/*********************************************************************** * * Gtk+ wrappers for GNU Smalltalk * ***********************************************************************/ /*********************************************************************** * * Copyright 2001, 2003 Free Software Foundation, Inc. * Written by Paolo Bonzini, Norman Jordan, Mike S. Anderson. * * This file is part of GNU Smalltalk. * * GNU Smalltalk is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2, or (at your option) any later * version. * * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * GNU Smalltalk; see the file COPYING. If not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***********************************************************************/ #include "config.h" #include "gstpub.h" #include #include #include #include #include #include #include #ifdef STDC_HEADERS #include #include #endif #include "gst-gtk.h" #include "placer.h" typedef struct SmalltalkClosure { GClosure closure; OOP receiver; OOP selector; OOP data; OOP widget; } SmalltalkClosure; VMProxy *_blox_vm_proxy; static GQuark q_gst_object = 0; static int pending_quit_count = 0; static void blox_gtk_init (); /* Wait in the main event loop until there are no pending events. */ static void my_gtk_main_iteration (); /* Wait in the main event loop until there are no pending events. Never block unless BLOCKING is true. */ static void my_gtk_main_iteration_do (gboolean blocking); /* Answer whether we should leave the current event loop (if gtk_main_quit has been called). */ static gboolean should_quit (); /* Unref OBJ and detach it from the Smalltalk object that has represented it so far. */ static void free_oop_for_g_object (GObject *obj); /* If no Smalltalk object has represented OBJ so far, change OOP's class to be the correct one, ref the object, mark it as finalizable, and answer OOP; otherwise answer the pre-existing object. */ static OOP narrow_oop_for_g_object (GObject *obj, OOP oop); /* Answer a Smalltalk object that can represent OBJ. This is the same as narrow_oop_for_g_object, but creates a new OOP if no Smalltalk object has represented OBJ so far. */ static OOP get_oop_for_g_object (GObject *obj); /* Answer a Smalltalk object that can represent the boxed value OBJ. This needs to know the TYPE of the value because GBoxed values don't know their type. */ static OOP get_oop_for_g_boxed (gpointer obj, GType type); /* Store in a quark that OBJ is represented by the Smalltalk object OOP. */ static void associate_oop_to_g_object (GObject *obj, OOP oop); /* Convert the GValue, VAL, to a Smalltalk object. */ static OOP convert_g_value_to_oop (const GValue *val); /* Store the value represented by OOP into the GValue, VAL. */ static void fill_g_value_from_oop (GValue *val, OOP oop); /* Create a GClosure that invokes the selector, SELECTOR, on the given object. DATA is inserted as the second parameter (or is passed as the only one is the closure's arity is 0). */ static GClosure * create_smalltalk_closure(OOP receiver, OOP selector, OOP data); /* The finalization notifier for Smalltalk GClosures. Unregisters the receiver and user data for the CLOSURE. */ static void finalize_smalltalk_closure(gpointer data, GClosure *closure); /* The marshalling routine for Smalltalk GClosures. */ static void invoke_smalltalk_closure(GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); /* A wrapper around g_signal_connect_closure that looks up the selector and creates a Smalltalk GClosure. */ static int connect_signal (OOP widget, char *event_name, OOP receiver, OOP selector, OOP user_data); /* A wrapper around g_object_get_property that replaces GValues with OOPs. */ static OOP object_get_property (GObject *anObject, const char *aProperty); /* A wrapper around g_object_set_property that replaces GValues with OOPs. */ static void object_set_property (GObject *anObject, const char *aProperty, OOP aValue); /* A wrapper around gtk_container_child_get_property that replaces GValues with OOPs. */ static OOP container_get_child_property (GtkContainer *aParent, GtkWidget *anObject, const char *aProperty); /* A wrapper around gtk_container_child_set_property that replaces GValues with OOPs. */ static void container_set_child_property (GtkContainer *aParent, GtkWidget *anObject, const char *aProperty, OOP aValue); /* Some wrappers for GTK macros. */ static GdkWindow *widget_get_window(GtkWidget *widget); static int widget_get_flags(GtkWidget *widget); static void widget_set_flags(GtkWidget *widget, int flags); static void widget_unset_flags(GtkWidget *widget, int flags); /* GObject wrapper. */ void register_for_type (OOP oop, GType type) { _blox_vm_proxy->registerOOP (oop); g_type_set_qdata (type, q_gst_object, oop); } void free_oop_for_g_object (GObject *obj) { g_object_set_qdata (obj, q_gst_object, NULL); g_object_unref (obj); } void associate_oop_to_g_object (GObject *obj, OOP oop) { OOP class = g_type_get_qdata (G_OBJECT_TYPE (obj), q_gst_object); if (class) OOP_TO_OBJ (oop)->objClass = class; g_object_set_qdata (obj, q_gst_object, oop); g_object_ref (obj); _blox_vm_proxy->strMsgSend (oop, "addToBeFinalized", NULL); } OOP narrow_oop_for_g_object (GObject *obj, OOP oop) { OOP preexistingOOP; if (!(preexistingOOP = g_object_get_qdata (obj, q_gst_object))) { associate_oop_to_g_object (obj, oop); return oop; } else return preexistingOOP; } OOP get_oop_for_g_boxed (gpointer obj, GType type) { OOP oop = _blox_vm_proxy->cObjectToOOP(obj); OOP class = g_type_get_qdata (type, q_gst_object); if (class) OOP_TO_OBJ (oop)->objClass = class; return oop; } OOP get_oop_for_g_object (GObject *obj) { OOP oop; if (!(oop = g_object_get_qdata (obj, q_gst_object))) { /* We don't have a wrapper for it, so create it. Get the class from the object's type. */ oop = _blox_vm_proxy->cObjectToOOP(obj); associate_oop_to_g_object (obj, oop); } return oop; } /* SmalltalkClosure implementation. */ OOP convert_g_value_to_oop (const GValue *val) { GType type = G_VALUE_TYPE (val); GType fundamental; char v_char; gboolean v_boolean; gpointer v_ptr; long v_int; double v_float; if (G_TYPE_IS_FUNDAMENTAL (type)) fundamental = type; else fundamental = G_TYPE_FUNDAMENTAL (type); switch (fundamental) { case G_TYPE_CHAR: v_char = g_value_get_char (val); return _blox_vm_proxy->charToOOP(v_char); case G_TYPE_BOOLEAN: v_boolean = g_value_get_boolean (val); return _blox_vm_proxy->boolToOOP(v_boolean); case G_TYPE_UCHAR: v_int = g_value_get_uchar (val); return _blox_vm_proxy->intToOOP(v_int); case G_TYPE_INT: v_int = g_value_get_int (val); return _blox_vm_proxy->intToOOP(v_int); case G_TYPE_UINT: v_int = g_value_get_uint (val); return _blox_vm_proxy->intToOOP(v_int); case G_TYPE_LONG: v_int = g_value_get_long (val); return _blox_vm_proxy->intToOOP(v_int); case G_TYPE_ULONG: v_int = g_value_get_ulong (val); return _blox_vm_proxy->intToOOP(v_int); case G_TYPE_ENUM: v_int = g_value_get_enum (val); return _blox_vm_proxy->intToOOP(v_int); case G_TYPE_FLAGS: v_int = g_value_get_flags (val); return _blox_vm_proxy->intToOOP(v_int); case G_TYPE_FLOAT: v_float = g_value_get_float (val); return _blox_vm_proxy->floatToOOP(v_float); case G_TYPE_DOUBLE: v_float = g_value_get_double (val); return _blox_vm_proxy->floatToOOP(v_float); case G_TYPE_STRING: v_ptr = (gpointer) g_value_get_string (val); return _blox_vm_proxy->stringToOOP(v_ptr); case G_TYPE_POINTER: v_ptr = g_value_get_pointer (val); return _blox_vm_proxy->cObjectToOOP(v_ptr); case G_TYPE_BOXED: v_ptr = g_value_get_boxed (val); return get_oop_for_g_boxed (v_ptr, type); case G_TYPE_OBJECT: case G_TYPE_INTERFACE: v_ptr = g_value_get_object (val); if (fundamental == type || G_TYPE_CHECK_INSTANCE_TYPE (v_ptr, type)) return get_oop_for_g_object (v_ptr); default: return NULL; } } void fill_g_value_from_oop (GValue *return_value, OOP oop) { GType type = G_VALUE_TYPE (return_value); GType fundamental; char v_char; gboolean v_boolean; gpointer v_ptr; long v_int; double v_float; if (G_TYPE_IS_FUNDAMENTAL (type)) fundamental = type; else fundamental = G_TYPE_FUNDAMENTAL (type); switch (fundamental) { case G_TYPE_NONE: case G_TYPE_INVALID: break; case G_TYPE_CHAR: v_char = _blox_vm_proxy->OOPToChar(oop); g_value_set_char (return_value, v_char); break; case G_TYPE_BOOLEAN: v_boolean = _blox_vm_proxy->OOPToBool(oop); g_value_set_boolean (return_value, v_boolean); break; case G_TYPE_UCHAR: v_int = _blox_vm_proxy->OOPToInt(oop); g_value_set_uchar (return_value, v_int); break; case G_TYPE_INT: v_int = _blox_vm_proxy->OOPToInt(oop); g_value_set_int (return_value, v_int); break; case G_TYPE_UINT: v_int = _blox_vm_proxy->OOPToInt(oop); g_value_set_uint (return_value, v_int); break; case G_TYPE_LONG: v_int = _blox_vm_proxy->OOPToInt(oop); g_value_set_long (return_value, v_int); break; case G_TYPE_ULONG: v_int = _blox_vm_proxy->OOPToInt(oop); g_value_set_ulong (return_value, v_int); break; case G_TYPE_ENUM: v_int = _blox_vm_proxy->OOPToInt(oop); g_value_set_enum (return_value, v_int); break; case G_TYPE_FLAGS: v_int = _blox_vm_proxy->OOPToInt(oop); g_value_set_flags (return_value, v_int); break; case G_TYPE_FLOAT: v_float = _blox_vm_proxy->OOPToFloat(oop); g_value_set_float (return_value, v_float); break; case G_TYPE_DOUBLE: v_float = _blox_vm_proxy->OOPToFloat(oop); g_value_set_double (return_value, v_float); break; case G_TYPE_STRING: v_ptr = _blox_vm_proxy->OOPToString(oop); g_value_set_string_take_ownership (return_value, v_ptr); break; case G_TYPE_POINTER: v_ptr = _blox_vm_proxy->OOPToCObject(oop); g_value_set_pointer (return_value, v_ptr); break; case G_TYPE_BOXED: v_ptr = _blox_vm_proxy->OOPToCObject(oop); g_value_set_boxed (return_value, v_ptr); break; case G_TYPE_OBJECT: case G_TYPE_INTERFACE: v_ptr = _blox_vm_proxy->OOPToCObject(oop); g_value_set_object (return_value, v_ptr); break; default: fprintf (stderr, "Invalid type."); abort (); } } GClosure * create_smalltalk_closure(OOP receiver, OOP selector, OOP data) { GClosure *closure = g_closure_new_simple (sizeof (SmalltalkClosure), NULL); SmalltalkClosure *stc = (SmalltalkClosure *) closure; _blox_vm_proxy->registerOOP (receiver); _blox_vm_proxy->registerOOP (data); stc->receiver = receiver; stc->selector = selector; stc->data = data; g_closure_set_marshal (closure, invoke_smalltalk_closure); g_closure_add_finalize_notifier (closure, NULL, finalize_smalltalk_closure); return closure; } void finalize_smalltalk_closure(gpointer data, GClosure *closure) { SmalltalkClosure *stc = (SmalltalkClosure *) closure; _blox_vm_proxy->unregisterOOP (stc->receiver); _blox_vm_proxy->unregisterOOP (stc->data); } void invoke_smalltalk_closure(GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { OOP *args = alloca (sizeof (OOP) * (1 + n_param_values)); SmalltalkClosure *stc = (SmalltalkClosure *) closure; OOP resultOOP; int i; /* Maintain the Gtk order of parameters, even if we end up discarding the sender (first parameter, usually) most of the time */ for (i = 0; i < n_param_values; i++) { OOP oop = convert_g_value_to_oop (¶m_values[i]); if (!oop) { fprintf (stderr, "Invalid type, signal discarded.\n"); if (return_value->g_type == G_TYPE_NONE) return; else abort (); } args[i] = oop; } args[n_param_values] = stc->data; resultOOP = _blox_vm_proxy->nvmsgSend(stc->receiver, stc->selector, args, 1 + n_param_values); /* FIXME Need to init return_value's type? */ if (return_value) fill_g_value_from_oop (return_value, resultOOP); } /* Signal implementation. */ int connect_signal (OOP widget, char *event_name, OOP receiver, OOP selector, OOP user_data) { GtkWidget *cWidget = _blox_vm_proxy->OOPToCObject (widget); GClosure *closure; GSignalQuery qry; guint sig_id; OOP oop_sel_args; /* Check parameters */ if (!G_IS_OBJECT(cWidget)) { return(-1); } /* Invalid widget passed */ sig_id = g_signal_lookup(event_name, G_OBJECT_TYPE(G_OBJECT(cWidget))); if (sig_id = 0) { return(-2); } /* Invalid event name */ g_signal_query(sig_id, &qry); oop_sel_args = _blox_vm_proxy->strMsgSend(selector, "numArgs", NULL); if (oop_sel_args == _blox_vm_proxy->nilOOP) { return(-3); } /* Invalid selector */ /* Check the number of arguments in the selector against the number of arguments in the event callback */ /* We can return fewer arguments than are in the event, if the others aren't wanted, but we can't return more, and returning nilOOPs instead is not 100% satisfactory, so fail. */ if (_blox_vm_proxy->OOPToInt(oop_sel_args) > qry.n_params) { return(-4); } /* Receiver is assumed to be OK, no matter what it is */ /* Parameters OK, so carry on and connect the signal */ associate_oop_to_g_object (G_OBJECT (cWidget), widget); closure = create_smalltalk_closure (receiver, selector, user_data); g_signal_connect_closure (cWidget, event_name, closure, FALSE); return(0); } /* Event loop. */ void my_gtk_main_iteration () { while (gtk_events_pending ()) pending_quit_count += gtk_main_iteration_do (TRUE); } void my_gtk_main_iteration_do (gboolean blocking) { while (gtk_events_pending ()) pending_quit_count += gtk_main_iteration_do (blocking); } gboolean should_quit () { if (!pending_quit_count) return FALSE; pending_quit_count--; return TRUE; } /* Wrappers for macros. */ GdkWindow * widget_get_window(GtkWidget *widget) { return widget->window; } int widget_get_flags(GtkWidget *widget) { return GTK_WIDGET_FLAGS (widget); } void widget_set_flags(GtkWidget *widget, int flags) { GTK_WIDGET_SET_FLAGS (widget, flags); } void widget_unset_flags(GtkWidget *widget, int flags) { GTK_WIDGET_UNSET_FLAGS (widget, flags); } /* Wrappers for GValue users. */ OOP object_get_property(GObject *anObject, const char *aProperty) { GParamSpec *spec; GValue result = {0,}; GObject *obj; obj = G_OBJECT (anObject); spec = g_object_class_find_property (G_OBJECT_GET_CLASS(obj), aProperty); g_value_init (&result, spec->value_type); g_object_get_property (obj, aProperty, &result); return (convert_g_value_to_oop (&result)); } void object_set_property(GObject *anObject, const char *aProperty, OOP aValue) { GParamSpec *spec; GObject *obj; GValue value = {0,}; obj = G_OBJECT (anObject); spec = g_object_class_find_property (G_OBJECT_GET_CLASS(obj), aProperty); g_value_init (&value, spec->value_type); fill_g_value_from_oop (&value, aValue); g_object_set_property (obj, aProperty, &value); } OOP container_get_child_property(GtkContainer *aParent, GtkWidget *aChild, const char *aProperty) { GParamSpec *spec; GValue result = {0,}; g_return_val_if_fail (GTK_WIDGET (aParent) == gtk_widget_get_parent (GTK_WIDGET (aChild)), _blox_vm_proxy->nilOOP); spec = gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (aParent), aProperty); g_value_init(&result, spec->value_type); gtk_container_child_get_property(aParent, aChild, aProperty, &result); return (convert_g_value_to_oop (&result)); } void container_set_child_property(GtkContainer *aParent, GtkWidget *aChild, const char *aProperty, OOP aValue) { GParamSpec *spec; GValue value = {0,}; g_return_if_fail (GTK_WIDGET (aParent) == gtk_widget_get_parent (GTK_WIDGET (aChild))); spec = gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (aParent), aProperty); g_value_init (&value, spec->value_type); fill_g_value_from_oop (&value, aValue); gtk_container_child_set_property(aParent, aChild, aProperty, &value); } /* Initialization. */ void blox_gtk_init () { static int init = 0; int argc = 1; gchar *argvArray[] = { "gst", NULL }; gchar **argv = argvArray; if (init) return; init++; gtk_init (&argc, &argv); } /* Miscellaneous. */ int gtk_text_iter_sizeof () { return(sizeof(GtkTextIter)); } int gtk_tree_iter_sizeof () { return(sizeof(GtkTreeIter)); } OOP gtk_tree_model_get_oop(GtkTreeModel *model, GtkTreeIter *iter, int col) { GValue gval = { 0, }; OOP result; gtk_tree_model_get_value(model, iter, col, &gval); result = convert_g_value_to_oop(&gval); g_value_unset(&gval); return(result); } void gtk_list_store_set_oop(GtkListStore *store, GtkTreeIter *iter, int col, OOP value) { GValue gval = { 0, }; g_value_init (&gval, gtk_tree_model_get_column_type(GTK_TREE_MODEL(store), col)); fill_g_value_from_oop(&gval, value); gtk_list_store_set_value (store, iter, col, &gval); g_value_unset(&gval); } void gtk_tree_store_set_oop(GtkTreeStore *store, GtkTreeIter *iter, int col, OOP value) { GValue gval = { 0, }; g_value_init (&gval, gtk_tree_model_get_column_type(GTK_TREE_MODEL(store), col)); fill_g_value_from_oop(&gval, value); gtk_tree_store_set_value (store, iter, col, &gval); g_value_unset(&gval); } void gst_initModule (proxy) VMProxy *proxy; { q_gst_object = g_quark_from_string ("gst_object"); g_type_init (); _blox_vm_proxy = proxy; _blox_vm_proxy->defineCFunc ("bloxGtkInit", blox_gtk_init); _blox_vm_proxy->defineCFunc ("bloxGtkRegisterForType", register_for_type); _blox_vm_proxy->defineCFunc ("bloxGtkFreeGObjectOOP", free_oop_for_g_object); _blox_vm_proxy->defineCFunc ("bloxGtkNarrowGObjectOOP", narrow_oop_for_g_object); _blox_vm_proxy->defineCFunc ("bloxGtkConnectSignal", connect_signal); _blox_vm_proxy->defineCFunc ("bloxGtkMainIteration", my_gtk_main_iteration); _blox_vm_proxy->defineCFunc ("bloxGtkMainIterationDo", my_gtk_main_iteration_do); _blox_vm_proxy->defineCFunc ("bloxGtkShouldQuit", should_quit); _blox_vm_proxy->defineCFunc ("bloxGtkGetProperty", object_get_property); _blox_vm_proxy->defineCFunc ("bloxGtkSetProperty", object_set_property); _blox_vm_proxy->defineCFunc ("bloxGtkGetChildProperty", container_get_child_property); _blox_vm_proxy->defineCFunc ("bloxGtkSetChildProperty", container_set_child_property); _blox_vm_proxy->defineCFunc ("bloxGtkGetFlags", widget_get_flags); _blox_vm_proxy->defineCFunc ("bloxGtkSetFlags", widget_set_flags); _blox_vm_proxy->defineCFunc ("bloxGtkUnsetFlags", widget_unset_flags); _blox_vm_proxy->defineCFunc ("bloxGtkGetWindow", widget_get_window); _blox_vm_proxy->defineCFunc ("gtk_text_iter_sizeof", gtk_text_iter_sizeof); _blox_vm_proxy->defineCFunc ("gtk_tree_iter_sizeof", gtk_tree_iter_sizeof); _blox_vm_proxy->defineCFunc ("gtk_tree_model_get_oop", gtk_tree_model_get_oop); _blox_vm_proxy->defineCFunc ("gtk_list_store_set_oop", gtk_list_store_set_oop); _blox_vm_proxy->defineCFunc ("gtk_tree_store_set_oop", gtk_tree_store_set_oop); _blox_vm_proxy->defineCFunc ("gtk_placer_get_type", gtk_placer_get_type); _blox_vm_proxy->defineCFunc ("gtk_placer_new", gtk_placer_new); _blox_vm_proxy->defineCFunc ("gtk_placer_put", gtk_placer_put); _blox_vm_proxy->defineCFunc ("gtk_placer_move", gtk_placer_move); _blox_vm_proxy->defineCFunc ("gtk_placer_resize", gtk_placer_resize); _blox_vm_proxy->defineCFunc ("gtk_placer_move_rel", gtk_placer_move_rel); _blox_vm_proxy->defineCFunc ("gtk_placer_resize_rel", gtk_placer_resize_rel); _blox_vm_proxy->defineCFunc ("gtk_placer_set_has_window", gtk_placer_set_has_window); _blox_vm_proxy->defineCFunc ("gtk_placer_get_has_window", gtk_placer_get_has_window); }