Saturday, January 3, 2009

uinput jni interface

I needed to build a Java interface to the uinput kernel device, and as this was my first non-trivial jni code, I'll document it here. (This ties in with the wiigee library)

Note this requires the uipnut kernel mode to be loaded, and the running user to have write access to the uinput device.

First, write the Java code containing the native library references:

public class X11Input {
//Singleton
private static X11Input u;
public static X11Input getInstance() {
if(u == null) {
u = new X11Input();
}
return u;
}

//native methods
//private - called to destroy, and init native interface
private native void destroy();
private native void init();
//public functions
public native void mouseMove(int x, int y);
public native void keyPress(int key);
public native void keyRelease(int key);

//Load native library
static {
System.loadLibrary("X11Input");
}

//We add the Shutdown hook, and init the uinput interface in the contructor
public X11Input() {
init();
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
X11Input.getInstance().destroy();
System.out.println("Destroying the X11Input singleton jni");
}
});
}
}
Note the use of the addShutdownHook to ensure* the uinput device is correctly closed.

Next, using the compiled class and javah, generate a c header file:
javah -jni org.wiiboard.x11.X11Input

This is executed at the root of the bin directory, and will generate the file:
org_wiiboard_x11_X11Input.h

Next, write the c code that adds the workings behind the the functions:
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include
#include
#include "UinputTest.h"

/* Globals */
static int uinp_fd = -1;
struct uinput_user_dev uinp; // uInput device structure
struct input_event event; // Input device structure
/* Setup the uinput device */
int setup_uinput_device()
{
// Temporary variable
int i=0;
// Open the input device
uinp_fd = open("/dev/input/uinput", O_WRONLY | O_NDELAY);
if (uinp_fd == 0)
{
printf("Unable to open /dev/input/uinput\n");
return -1;
}
memset(&uinp,0,sizeof(uinp)); // Intialize the uInput device to NULL
strncpy(uinp.name, "PolyVision Touch Screen", UINPUT_MAX_NAME_SIZE);
uinp.id.version = 4;
uinp.id.bustype = BUS_USB;
// Setup the uinput device
ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY);
ioctl(uinp_fd, UI_SET_EVBIT, EV_REP);
ioctl(uinp_fd, UI_SET_EVBIT, EV_REL);
ioctl(uinp_fd, UI_SET_RELBIT, REL_X);
ioctl(uinp_fd, UI_SET_RELBIT, REL_Y);
for (i=0; i <> 0 && ( x != 0 || y != 0 )) {

if(x != 0) {
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_REL;
event.code = REL_X;
event.value = x;
write(uinp_fd, &event, sizeof(event));
}
if(y!=0) {
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_REL;
event.code = REL_Y;
event.value = y;
write(uinp_fd, &event, sizeof(event));
}

memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
}
}

JNIEXPORT void JNICALL
Java_org_wiiboard_x11_X11Input_keyPress(JNIEnv *env, jobject obj, jint key)
{
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_KEY;
event.code = key;
event.value = 1;
write(uinp_fd, &event, sizeof(event));

memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
}

JNIEXPORT void JNICALL
Java_org_wiiboard_x11_X11Input_keyRelease(JNIEnv *env, jobject obj, jint key)
{
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_KEY;
event.code = key;
event.value = 0;
write(uinp_fd, &event, sizeof(event));

memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
}

JNIEXPORT void JNICALL
Java_org_wiiboard_x11_X11Input_init(JNIEnv *env, jobject obj)
{
setup_uinput_device();
}

JNIEXPORT void JNICALL
Java_org_wiiboard_x11_X11Input_destroy(JNIEnv *env, jobject obj)
{
destroy_uinput_device();
}

Now compile:
gcc --share -o libX11Input.so -I/include/ -I/include/linux org_wiiboard_x11_X11Input.c /jre/lib/i386/server/libjvm.so

That will generate the libX11Input.so library.

Now you can run the java code with the java.lirary.path pointing to your new .so file's directory:
java -Djava.library.path=. X11InputTest

Wiigee on linux

Wiigee is a wiimote library written in Java that can learn and identify gestures. A demo application is available for download, and source is included in the jar file.

I have used wiigee under linux (ubuntu i386 and Kubuntu x64), and this is what I had to do to get it running.

First, to get it running, download the the bluecove libraries (I used version 2.1.0), and maven2.
Make sure libbluetooth and libbluetooth-dev are installed (and libstdc++.so.6 is linked too libstdc++.so on Kubuntu)

The bluecove MicroeditionConnector.java needs to have the L2CAP validation removed (apparently the wii mote uses non-standard ports)

export the JAVA_HOME variable (if its not alerady set), and execute:
mvn -Dmaven.test.skip=true -e
That will generate all the bluetooth libraries

Make the bluecove, and bluecove-gpl jars, and the libbluecove native binary lib avaiable to the wiigee library, and you're set.

Tuesday, December 23, 2008

Useful Commands

For network troubles getting files:
wget -c -t 0 -T 1
-> continue download with infinite re-tries and a 1 second timeout.

To allow ssh users to launch apps into the logged in users X sessoion:
netcat -l -s 127.0.0.1 -p 2020
-> start net cat in listen mode on the lo interface on port 2020
Ensure this is called in the gdm PreSession config script

Wednesday, December 10, 2008

Putty authorized keys

  1. Install Putty
  2. Generate the key
  3. Enter a pass phrase
  4. Select the generated public key and copy to the clipboard
  5. Save the private and public keys somehwere sensible
  6. ssh into the server that will hold the public key
  7. Make a .ssh directory in ~: mkdir .ssh
  8. Set access to .ssh : chmod 700 .ssh
  9. Enter the command: cat > .ssh/authorized_keys
  10. Paste the test from the clipboard
  11. Hit CTRL-D to exit
  12. Set the access on the authorized_keys file: chmod 644 .ssh/authorized_keys
  13. Start Pagent and add the new private key (passphrase entered above will be required)
Check that it worked by using putty to ssh in to the server.

How to install Ruby on Rails

For Ubuntu:
sudo apt-get install ruby ruby-dev postgres libpq-dev libopenssl-ruby
sudo gem install rake rails postgres ruby-debug-ide

To allow users to install gems:
chmod -R 777 /var/lib/gems/1.8

For Windows:
install ruby (using the one click installer!)
install postgres
gem install rake rails postgres-pr ruby-debug-ide

Points to note:
NetBeans 6.1 cvs doesn't work on windows - use 6.5 instead
Also, let NetBeans install the fast debugger (ruby-debug-ide)

Drag and drop in a text box

I have an application that needs to fire an onchange javascript event when data in one of 21 to 119 text boxes changes (yes, it sounds like a problem, but the UI works!). Sounds easy...

But it is possible to drag and drop text from one text box into any of the others.

This is the one situation where MS IE actually does a decent job - IE fires all the drag events (ondrop, ondragstart, ondragend, etc) where as all the other browsers (firefox, chrome, konqueror, opera, safari) do not!

So IE first...
ondrop set variable window.dragged to be the destination text box, and ondragend (fired on the source text box) calls the onchange function on both source and destination - see, easy!

Now the hard bit....
This is only an approximation, but seems to work well. (We'll see what the testers do with it!)
When onmousedown is fired, and there is text selected in the textbox make a note of the id of the text box (window.dragged) - this indicates that we may have a drag event,
Subsequently, when the onmouseout event is fired (that doesn't originate from the text box that was source for the onmousedown event), and window.dragged is set, call the onchange function on both source and destination.

Yes, its a hack, but it seems to work.

To summarise:
onmousedown fired at start of text selection
onmouseup fired at end of text selection
onmousedown fired at start of drag - there is text selected, so we set window.dragged to this.id
** user drags text to another field, and drops
onmouseout fired at source text box
** user moves mouse out of destination text box
onmouseout event fired on destination text box - signal to call onchange function on the source (window.dragged) and destination (this)

Possible improvements:
1. Maybe the onmouseout code should be in document.onmousedown (update: not required)
2. Save needs to re-check each - just to be sure.... (update: yes, to force update before form is submitted using enter key after drop)