[Previous] [Next] [Up] [Contents] [Feedback]

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);
}


[Previous] [Next] [Up] [Contents] [Feedback]