Main.BluetoothKeyboard History
Hide minor edits - Show changes to output
Deleted line 190:
Deleted lines 192-193:
[+Further Exploration+]
Added lines 195-196:
[+Further Exploration+]
\\
\\
Changed lines 1-2 from:
[++User-space Java Bluetooth (JSR-82) Keyboard Driver++]
to:
[++User-Space Java Bluetooth (JSR-82) Keyboard Driver++]
Changed lines 13-17 from:
\\
Build your own [[http://www.matias.ca/halfkeyboard/|HalfKeyboard (tm)]].
to:
Changed lines 192-198 from:
@]
to:
@]
[+Further Exploration+]
\\
\\
Build your own [[http://www.matias.ca/halfkeyboard/|HalfKeyboard (tm)]].
[+Further Exploration+]
\\
\\
Build your own [[http://www.matias.ca/halfkeyboard/|HalfKeyboard (tm)]].
Changed lines 1-2 from:
[+User-space Java Bluetooth (JSR-82) Keyboard Driver+]
to:
[++User-space Java Bluetooth (JSR-82) Keyboard Driver++]
Changed lines 5-8 from:
[++Motivation++]
[++Requirements++]
[+
to:
[+Motivation+]
[+Requirements+]
[+Requirements+]
Changed lines 11-13 from:
[++Explanation++]
[++Further Exploration++]
[+
to:
[+Explanation+]
[+Further Exploration+]
\\
[+Further Exploration+]
\\
Added line 17:
Added lines 1-15:
[+User-space Java Bluetooth (JSR-82) Keyboard Driver+]
... stay tuned!
[++Motivation++]
[++Requirements++]
* JSR-82 implementation, e.g., [[http://bluecove.org/|BlueCove]]
[++Explanation++]
[++Further Exploration++]
Build your own [[http://www.matias.ca/halfkeyboard/|HalfKeyboard (tm)]].
... stay tuned!
[++Motivation++]
[++Requirements++]
* JSR-82 implementation, e.g., [[http://bluecove.org/|BlueCove]]
[++Explanation++]
[++Further Exploration++]
Build your own [[http://www.matias.ca/halfkeyboard/|HalfKeyboard (tm)]].
Changed line 164 from:
KeyEvent.VK_TAB, ' ', /* 42 - 44 */
to:
KeyEvent.VK_TAB, ' ', /* 42 - 44 */
Changed line 164 from:
KeyEvent.VK_TAB, ' ', /* 42 - 44 */
to:
KeyEvent.VK_TAB, ' ', /* 42 - 44 */
Changed line 163 from:
KeyEvent.VK_ENTER, 27, KeyEvent.VK_BACK_SPACE, /* 40 - 42 */
to:
KeyEvent.VK_ENTER, 27, KeyEvent.VK_BACK_SPACE, /* 40 - 42 */
Changed line 164 from:
KeyEvent.VK_TAB, ' ', /* 42 - 44 */
to:
KeyEvent.VK_TAB, ' ', /* 42 - 44 */
Changed line 166 from:
'.', '/', KeyEvent.VK_ENTER /* 55 - 57 */
to:
'.', '/', KeyEvent.VK_ENTER /* 55 - 57 */
Changed lines 165-166 from:
'-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',' /* 45 - 54 */
'.', '/', KeyEvent.VK_ENTER /* 55 - 57 */
'.', '/', KeyEvent.VK_ENTER
to:
'-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',', /* 45 - 54 */
'.', '/', KeyEvent.VK_ENTER /* 55 - 57 */
'.', '/', KeyEvent.VK_ENTER /* 55 - 57 */
Changed line 165 from:
'-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',', /* 45 - 54 */
to:
'-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',' /* 45 - 54 */
Changed lines 163-164 from:
KeyEvent.VK_ENTER, 27, KeyEvent.VK_BACK_SPACE, KeyEvent.VK_TAB, ' ',
/* 40 - 44 */
to:
KeyEvent.VK_ENTER, 27, KeyEvent.VK_BACK_SPACE, /* 40 - 42 */
KeyEvent.VK_TAB, ' ', /* 42 - 44 */
KeyEvent.VK_TAB, ' ', /* 42 - 44 */
Changed lines 94-95 from:
to:
boolean ctrl_pressed, boolean alt_pressed,
boolean command_pressed) throws AWTException {
boolean command_pressed) throws AWTException {
Changed lines 163-164 from:
KeyEvent.VK_ENTER, 27, KeyEvent.VK_BACK_SPACE, KeyEvent.VK_TAB, ' ', /* 40 - 44 */
to:
KeyEvent.VK_ENTER, 27, KeyEvent.VK_BACK_SPACE, KeyEvent.VK_TAB, ' ',
/* 40 - 44 */
/* 40 - 44 */
Changed lines 83-85 from:
injectKey(new_key, shift_pressed,
ctrl_pressed, alt_pressed, command_pressed);
to:
injectKey(new_key, shift_pressed, ctrl_pressed,
alt_pressed, command_pressed);
alt_pressed, command_pressed);
Changed lines 93-94 from:
void injectKey(int new_key, boolean shift_pressed, boolean ctrl_pressed, boolean alt_pressed,
boolean command_pressed) throws AWTException {
boolean command_pressed) throws AWTException {
to:
void injectKey(int new_key, boolean shift_pressed,
boolean ctrl_pressed, boolean alt_pressed,
boolean command_pressed) throws AWTException {
boolean ctrl_pressed, boolean alt_pressed,
boolean command_pressed) throws AWTException {
Changed lines 93-94 from:
void injectKey(int new_key, boolean shift_pressed, boolean ctrl_pressed,
boolean alt_pressed, boolean command_pressed) throws AWTException {
to:
void injectKey(int new_key, boolean shift_pressed, boolean ctrl_pressed, boolean alt_pressed,
boolean command_pressed) throws AWTException {
boolean command_pressed) throws AWTException {
Changed lines 149-150 from:
public static final int cursor_keys[] = { KeyEvent.VK_RIGHT, KeyEvent.VK_LEFT, KeyEvent.VK_DOWN, KeyEvent.VK_UP};
to:
public static final int cursor_keys[] = {
KeyEvent.VK_RIGHT,
KeyEvent.VK_LEFT,
KeyEvent.VK_DOWN,
KeyEvent.VK_UP
};
KeyEvent.VK_RIGHT,
KeyEvent.VK_LEFT,
KeyEvent.VK_DOWN,
KeyEvent.VK_UP
};
Changed lines 83-84 from:
injectKey(new_key, shift_pressed, ctrl_pressed, alt_pressed, command_pressed);
to:
injectKey(new_key, shift_pressed,
ctrl_pressed, alt_pressed, command_pressed);
ctrl_pressed, alt_pressed, command_pressed);
Changed line 26 from:
+ kbAddr + ":13;authenticate=true;");
to:
+ kbAddr + ":13;authenticate=true;");
Changed lines 43-44 from:
void handlePacket(byte[] hidReport, int packetLen) throws IOException, AWTException {
to:
void handlePacket(byte[] hidReport, int packetLen) throws
IOException,
AWTException {
IOException,
AWTException {
Changed line 15 from:
IOException{
to:
IOException {
Changed lines 25-26 from:
L2CAPConnection con = (L2CAPConnection) Connector.open("btl2cap://" + kbAddr + ":13;authenticate=true;");
to:
L2CAPConnection con = (L2CAPConnection) Connector.open("btl2cap://"
+ kbAddr + ":13;authenticate=true;");
+ kbAddr + ":13;authenticate=true;");
Changed lines 12-15 from:
public static void main(String[] args) throws AWTException, InterruptedException, IOException{
to:
public static void main(String[] args) throws
AWTException,
InterruptedException,
IOException{
AWTException,
InterruptedException,
IOException{
Changed lines 12-165 from:
new BluetoothKeyboard().go();
}
void go() throws IOException, AWTException {
// allow connections to PSM below
// connect
L2CAPConnection con = (L2CAPConnection) Connector.open("btl2cap://" + kbAddr + ":13;authenticate
System
// process reports
byte hidReport[]
// get packet from L2CAP connection
int packetLen = con
dumpBuffer(hidReport, packetLen);
// process event
handlePacket(hidReport, packetLen);
}
}
void handlePacket(byte[] hidReport, int packetLen) throws IOException, AWTException {
// decode keys, only
int modifier =
int nrKeys
nrKeys = NUM_KEYS;
}
// modifier keys
boolean ctrl_pressed
boolean shift_pressed
boolean alt_pressed
boolean command_pressed = (modifier & 0x88
// 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
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_
} else if (new_event >= 0x4f && new_event <= 0x52) {
new_key = cursor_keys[ new_event - 0x4f];
} else {
continue;
System.arraycopy(hidReport, 4, last_keyboard_state
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
}
if (ctrl_pressed) {
robot.keyPress(KeyEvent.VK_CONTROL);
}
if (alt_pressed) {
robot.keyPress(KeyEvent.VK_ALT);
}
if (command
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);
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
public static int keytable_us_none []
'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',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', /* 30 - 39 */
KeyEvent.VK_ENTER, 27, KeyEvent.VK_BACK_SPACE, KeyEvent.VK_TAB, ' ', /* 40 - 44 */
'-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',',
'.', '/', KeyEvent.VK
};
public static final int NUM_KEYS = 5;
Robot robot;
byte last
int last_modifier = 0;
// address of your keyboard
public
to:
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, KeyEvent.VK_TAB, ' ', /* 40 - 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";
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, KeyEvent.VK_TAB, ' ', /* 40 - 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";
Changed lines 12-15 from:
public static void main(String[] args) throws AWTException, InterruptedException, IOException{
to:
public static void main(String[] args) throws
AWTException,
InterruptedException,
IOException {
AWTException,
InterruptedException,
IOException {
Added lines 1-165:
[@
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, KeyEvent.VK_TAB, ' ', /* 40 - 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";
}
@]
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, KeyEvent.VK_TAB, ' ', /* 40 - 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";
}
@]