Re: Java support in GTK/Gnome



On Thu, 3 Aug 2000, Oskar Liljeblad wrote:

> Hi
> 
> I'm working on gnome-gcj (http://gnome-gcj.sourceforge.net) which is a set
> of bindings for Gtk in Java/CNI. Currently, each Java objects contains a
> pointer to the corresponding Gtk object, and the Gtk object has an
> associated data containing the Java object reference. This causes problems
> when Gtk objects are created from within Gtk, and not by the user from
> Java. For example, the GtkDialog contains some default widgets - boxes
> and buttons. These are allocated in GtkDialog and thus can't be accessed
> from Java. (This can however be solved by automaticly creating a Java
> object for Gtk objects without Java peers.)
> 
> But a better solution (IMHO) is described by Per Bothner in his
> "A Design for Java Support in Gnome" document at
> http://www.bothner.com/~per/gnome-java.html. The solution is to merge GTK's
> object allocation with gcj's Java object allocation by modifying the
> function gtk_type_new (GTK 1.2.x) or g_type_create_instance (GTK/Glib 1.3.x).
> The GTK object is actually embedded in the Java object.

i just read through that document, and basically have a couple of issues
with it. unfortunately i don't have Per Bothner's email handle and only
got notified of this writeup through your email. i feel some more thoughtfull
discussion from both parts needs to happen before we come up with a good
solution. if you could forward this email, that'd be apprechiated.

on the signal part, dealing with interfaces ala:

public ButtonClickedListener
{
  public void clicked ();
}
public class Button extends Bin
{
  ...
  public native void addClickedListener(ButtonClickedListener listener);
  public native void removeClickedListener(ButtonClickedListener listener);
}
gnu::gtk::Button::addClickedListener(ButtonClickedListener* listener)
{
  gtk_signal_connect(GTK_OBJECT(this->peer),
                      "clicked",
                      GTK_SIGNAL_FUNC(button_click_cb),
                      listener)
}

will become quite cumbersome since you have the additonal overhead
of add<SIGNAL>Listener remove<SIGNAL>Listener for ever signal on all
objects, and you don't support dynamically adding signals to existing
classes this way.
moving to something that'd allow button.connect ("clicked", listener);
seems far more appealing.
especially since there is more than just add/remove you could do
with a connection, like temporarily blocking it which would additionally
give blockClickedListener() and unblockClickedListener().
besides that, there are obvious issues with object reference maintenance
by passing listener as an anonymous pointer (but that might be due to
the above excerpt being pseudo code), glib 1.3 will have sufficient
support for real objects as connect data.

as for the GObject -> Java object mapping (you really need to base your
bindings on glib 1.3's GObject instead of GtkObject), per first writes:

| There is a forwarding pointer from the gnu.gtk.Button to the corresponding
| GtkButton object, but no obvious way to go back, which may cause some
| performance loss. It also seems wastefull to allocate all these Java wrapper
| objects that don't actually do anything.

for the obvious way to go back, that'd be g_object_set_data() which also gives
finalization notification. but judging from your email you already figured
that ;)
as for having a java wrapper for every GObject, yes that'd be wastefull as
long as the java code doesn't actually reference the object, e.g. a child
of a GtkColorSelection.

though, then he writes (and that's the idea you seem to consider most
appealing):

| However, there is a trick we can use, inspired by C++ multiple inheritance:
| We embed the Gtk object in a Java Object.
[...]
| Note that a Gtk object has no fields that can be accessed from Java. All
| fields have to be accessed using methods that are written in
| native (C or C++) code. 
[...]
| When we create a Gtk instance, we now we actually want to do is create te
| Java wrapper instance. For example, we want gtk_button_new() to be
| equivalent to:
|  (GtkWidget*) ((char*) new gnu::gtk::Button() + JAVA_OFFSET)

which does add the extra memory overhead to each GObject unconditionally.


> We need something like this in glib/gobject/gtype.c:
> 
>   typedef GTypeInstance *(*GTypeMemoryAllocator) (GType type);
> 
>   static GTypeMemoryAllocator mem_alloc_callback = NULL;
> 
>   void
>   g_type_set_allocator(GTypeMemoryAllocator callback)
>   {
>       mem_alloc_callback = callback;
>   }
> 
>   GTypeInstance*
>   g_type_create_instance (GType type)
>   {
>      ...
>      if (mem_alloc_callback == NULL) 
>        instance = g_malloc0 (node->data->instance.instance_size);
>      else
>        instance = mem_alloc_callback (type);
>      ...
>   }
> 
> What the registered memory callback does is simply to allocate
> memory for the instance. Obviously, this callback which Java registers
> using  g_type_set_allocator needs to be able to figure out the name
> (or some unique persistent identifier) and superclasses of a GtkType.
> This information will be used to figure out what kind of Java object
> that need to be allocated.

the inheritance tree, name, interfaces an instantiatable type is conforming
to etc, can all be figured through the type systems API.

> This is just code to outline the solution. Obviously it needs to be made
> more solid, but the question right now is: Is this a reasonable thing to
> implement? Are there any better solutions that you know of? Would a patch
> implementing the above be accepted for GLib 1.3.x?


well, as for the acceptance, we'll certainly provide the necessary hooks
in glib to create java and othe language bindings, however they need to be
reasonably justified ;)

i don't think physical object embedding will provide you with any significant
advantages though. a much more viable alternative is probably to stick to
your java proxy approach and use something like (also pseudo code of course ;):

void
jproxy_gobject_died (gpointer data)
{
  JProxy *jproxy = data;
  
  jproxy->gobject = NULL;
}

JProxy*
gobject_get_jproxy (GObject *object)
{
  JProxy *jproxy = NULL;

  if (object)
    {
      jproxy = g_object_get_data (object, "java-proxy");
      
      if (!jproxy)
        {
          jproxy = jproxy_new_gobject (object);
      
          g_object_set_data_full (object, "java-proxy",
                                  jproxy, jproxy_gobject_died);
        }
    }
  return jproxy;
}

void
jproxy_finalize (JProxy *jproxy)
{
#if EITHER
  /* let jproxy_gobject_died() cleanup */
  g_object_set_data (object, "java-proxy", NULL);
#else /* OR */
  /* let's cleanup ourselves,
   * _steal_data resets to NULL without destroy notification
   */
  g_object_steal_data (object, "java-proxy");
  jproxy->gobject = NULL;
#endif
}

though in practice, you probably will want jproxy to hold a reference count
on jproxy->gobject to prevent it from premature death.

however, with keeping java object proxies and creating them on demand,
you don't pay unecessary memory penalties per GObject, and you could still
let GType do its clever tricks like allocating objects from memchunks.
also, at least at the object level, several language bindings can live
in the same adress space, say i have an application that provides GObject's
(not necessarily widgets) and want to provide scripting abilities for scheme
and java scripts that freak around with my objects. with language bindings
requiring physical embedding that's utterly impossible ;)

> 
> Oskar Liljeblad (osk@hem.passagen.se)
> 

---
ciaoTJ






[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]