4. Anbindung für das GTK
Die entscheidende Arbeit auf Java-Seite leisten die Klassen InputThread, Base (die Basisklasse aller grafischen Kompenenten) und RunLoop. Die einzige Klasse mit native-Methoden ist InputThread:
InputThread.java:
package gtk;
public class InputThread extends Thread
{
public static Channel chan = new Channel();
static { System.loadLibrary("native"); }
public static void sleep ()
{
try { Thread.sleep(20); } catch (InterruptedException e) {}
}
public native Object initWithArgs (String args[]);
protected native void processEvents ();
protected native Object doRequest (Object request);
public void run ()
{
for (;;)
{
Object req[];
processEvents();
while ((req = (Object[]) chan.available()) != null)
{
Object result = doRequest(req);
if (req[0] != null) ((Channel) req[0]).send(result);
}
sleep();
}
}
}
Base.java:
package gtk;
public class Base
{
protected static ThreadLocal chan = new ThreadLocal();
protected int gtk; /* need long on 64bit */
public static Channel chan()
{
Channel result = (Channel) chan.get();
if (result == null) chan.set(result = new Channel());
return result;
}
protected Base (Object none)
{
// System.err.println("creating " + this);
}
public int connect (String signal, Channel channel, Object message)
{
InputThread.chan.send(new Object[] {chan(), this, "Base_connect",
signal, channel, message});
return ((Integer) chan().recv()).intValue();
}
public int connect (String signal, ActionListener target)
{
return RunLoop.defaultRunLoop().connect(this, signal, target);
}
public int connect (String signal, Object target, String action)
throws NoSuchMethodException
{
return RunLoop.defaultRunLoop().connect(this, signal, target, action);
}
}
RunLoop.java:
package gtk;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
public class RunLoop
{
protected static ThreadLocal loop = new ThreadLocal();
protected Channel chan;
protected boolean running = true;
public static RunLoop defaultRunLoop ()
{
RunLoop result = (RunLoop) loop.get();
if (result == null) loop.set(result = new RunLoop());
return result;
}
// ----------------------------------------
public RunLoop ()
{
this(new Channel());
}
public RunLoop (Channel channel)
{
chan = channel;
}
// ----------------------------------------
public Channel getChannel () // unused
{
return chan;
}
public int connect (Base object, String signal, ActionListener target)
{
Object info[] = {object, target, null};
return object.connect(signal, chan, info);
}
public int connect (Base object, String signal, Object target,
String action) throws NoSuchMethodException
{
Object info[] = {object, target,
target.getClass().getMethod(action, new Class[] {Object.class})};
return object.connect(signal, chan, info);
}
public void run ()
{
while (running)
{
Object info[] = (Object[]) chan.recv();
try
{
if (info[2] == null)
((ActionListener) info[1]).actionPerformed(info[0]);
else
((Method) info[2]).invoke(info[1], new Object[] {info[0]});
}
catch (InvocationTargetException e)
{
System.err.println(this + ": " + e.getTargetException());
}
catch (Exception e)
{
System.err.println(this + ": " + e);
}
}
}
public void terminate ()
{
running = false;
}
}
Der wesentliche Teil von native.c:
JNIEXPORT void JNICALL
Java_gtk_InputThread_processEvents (JNIEnv *env, jobject self)
{
if (my_env == NULL) my_env = env;
while (gtk_events_pending())
gtk_main_iteration();
}
JNIEXPORT jobject JNICALL
Java_gtk_InputThread_doRequest (JNIEnv *env, jobject self, jobject array)
{
jstring fkt_name = ELEMENT(array, 2);
const char *name = (*env)->GetStringUTFChars(env, fkt_name, 0);
jobject result = find_entry(name)(env, array);
(*env)->ReleaseStringUTFChars(env, fkt_name, name);
return result;
}
/* ------------------------------------------------------------ */
struct SendInfo
{
jobject channel; /* channel to send info to */
jmethodID method; /* send method id */
jobject message; /* message to send */
};
static void forward_signal (GtkWidget *sender, struct SendInfo *info)
{
JNIEnv *env = (JNIEnv *) my_env;
/* DEBUG("-> forward_signal (info->channel=%p, signal=%p, class=%p, "
"method=%p)\n", info->channel, signal, class, info->method); */
(*env)->CallVoidMethod(env, info->channel, info->method, info->message);
/* DEBUG("<- forward_signal\n"); */
}
/* TODO: this should be static */
jobject Base_connect (JNIEnv *env, jobjectArray a)
{
/* ENTER("Base_connect"); */
jobject self = ELEMENT(a, 1);
jstring _signal = ELEMENT(a, 3);
jobject channel = ELEMENT(a, 4);
jobject message = ELEMENT(a, 5);
const char *signal = (*env)->GetStringUTFChars(env, _signal, 0);
struct SendInfo *info = g_new(struct SendInfo, 1);
gint sig_id;
/* FIXME: delete references when disconnecting */
info->channel = (*env)->NewGlobalRef(env, channel);
info->method = (*env)->GetMethodID(env, GET_CLASS(env, channel),
"send", "(Ljava/lang/Object;)V");
info->message = (*env)->NewGlobalRef(env, message);
if (info->method == 0) fprintf(stderr, "GetMethodID: send\n"), abort();
sig_id = gtk_signal_connect(GET_WIDGET(self), signal, forward_signal, info);
(*env)->ReleaseStringUTFChars(env, _signal, signal);
/* LEAVE("Base_connect"); */
return NEW_INT(sig_id);
}