Tutorial 7: Key Listener

This how to will extend the library with a simple key listener.

The key listener will return the event type (pressed, released, typed), the source of the event and the keycode of the pressed key.

For adding a key listener to this library we need a new Java class. The class must implemend the java.awt.event.KeyListener interface. A key listener is not responsible for only one event, but for three events: key pressed, released and typed. So we need some constants to tell the programmer which event was triggered. Then we need a native method to call back our library, which has a function pointer as addional parameter beside the information we want to return. Now we can implement the key listener methods.

package at.fhv.sgr.guilib.listeners;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class CBKeyListener implements KeyListener {
   public final static int CB_KEY_PRESSED = 1;
   public final static int CB_KEY_RELEASED = 2;
   public final static int CB_KEY_TYPED = 3;
   
   private native void cbKeyListener(long funcptr, int event, Object source, int keycode);
   private long m_FuncPointr;
   
   public CBKeyListener(long ptr) {
      m_FuncPointr = ptr;
   }
   
   protected void callback(KeyEvent e, int event) {
      cbKeyListener(m_FuncPointr, event, e.getSource(), e.getKeyCode());
   }
   
   public void keyTyped(KeyEvent e) {
      callback(e, CB_KEY_TYPED);
   }

   public void keyPressed(KeyEvent e) {
      callback(e, CB_KEY_PRESSED);
   }

   public void keyReleased(KeyEvent e) {
      callback(e, CB_KEY_RELEASED);
   }

}

The next step we have to do is to extend the c library. Therefor we extend the "listeners.h" and "listeners.c" files of the c library.

First we extend the "listeners.h" file by adding the same constants as we had in the Java class and the function deklarations for adding and removing a key listener:

...
#define CB_KEY_PRESSED 1
#define CB_KEY_RELEASED 2
#define CB_KEY_TYPED 3
...
typedef jobject KeyListener
...
KeyListener addKeyListener(jobject obj, void (*func)(jint, Component, jint));
void removeKeyListener(jobject obj, KeyListener kl);
...

After that we extend the "listeners.c" file. To understand the following lines of code you have to know how these callback listeners work. First of all one native function is registered per class per native method, so we can not directly register the programmers functions. Therefor we register a function which then calls back the programmers function. This function will be called cbKeyListener in this example. The addKeyListener function returns a jobject. This jobject is important, because the programmer will need it to unregister the listener. This function registers the cbKeyListener function only once in our CBKeyListener class. After that a new instance of the CBKeyListener class is created and added to the event source. In the removeKeyListener the listener is removed from the event source and its global reference is deleted, so it can be garbage collected.

...
#define CLS_CBKEYLISTENER "at/fhv/sgr/guilib/listeners/CBKeyListener"
...
void cbKeyListener(JNIEnv* env, jobject jobj, jlong ptr, jint event, jobject source, jint keycode) {
   void (*func)(jint, jobject, jint) = (void (*)(jint, jobject, jint))ptr;
   func(event, source, keycode);
}
 
KeyListener addKeyListener(jobject obj, void (*func)(jint, Component, jint)) {
   static jclass cbkeylist = NULL;
   jobject kl = NULL;

   if (cbkeylist == NULL) {
      JNINativeMethod nm;

      nm.name = "cbKeyListener";  method name 
      nm.signature = "(JILjava/lang/Object;I)V";  method signature 
      nm.fnPtr = (void*)&cbKeyListener;  pointer of function to register 

      cbkeylist = findClass(CLS_CBKEYLISTENER);
      registerNatives(cbkeylist, &nm, 1);
   }

   kl = newObjectBySignature(cbkeylist, "(J)V", (jlong) func);

   callVoidMethodByName(obj, "addKeyListener", "(Ljava/awt/event/KeyListener;)V", kl);

   exception();

   return kl;
}

void removeKeyListener(jobject obj, KeyListener kl) {
   callVoidMethodByName(obj, "removeKeyListener", "(Ljava/awt/event/KeyListener;)V", kl);
   deleteGlobalReference(kl);

   exception();
}

How did we get the method signature? Well there is a tool in Sun's JDK called javap. When you execute it with the following arguments:

javap -s -p at.fhv.sgr.guilib.listeners.CBKeyListener

you will get this result:

Compiled from "CBKeyListener.java"
public class at.fhv.sgr.guilib.listeners.CBKeyListener extends java.lang.Object
implements java.awt.event.KeyListener{
public static final int CB_KEY_PRESSED;
  Signature: I
public static final int CB_KEY_RELEASED;
  Signature: I
public static final int CB_KEY_TYPED;
  Signature: I
private long m_FuncPointr;
  Signature: J
private native void cbKeyListener(long, int, java.lang.Object, int);
  Signature: (JILjava/lang/Object;I)V
public at.fhv.sgr.guilib.listeners.CBKeyListener(long);
  Signature: (J)V
protected void callback(java.awt.event.KeyEvent, int);
  Signature: (Ljava/awt/event/KeyEvent;I)V
public void keyTyped(java.awt.event.KeyEvent);
  Signature: (Ljava/awt/event/KeyEvent;)V
public void keyPressed(java.awt.event.KeyEvent);
  Signature: (Ljava/awt/event/KeyEvent;)V
public void keyReleased(java.awt.event.KeyEvent);
  Signature: (Ljava/awt/event/KeyEvent;)V
}

So the last thing you have to do is to extend the component, you want to add a key listener to, with the functions to add them. If you don't know how to do that, have a look at the Frame or the Button.


Generated on Sat Nov 19 14:11:18 2005 for GrubC by  doxygen 1.4.4