seed r226 - in trunk: examples extensions libseed tests



Author: racarr
Date: Mon Nov 10 13:00:08 2008
New Revision: 226
URL: http://svn.gnome.org/viewvc/seed?rev=226&view=rev

Log:
Implement the ability to define new signals on objects, add
GObject.TYPE_* through an import-hook js. Add return values from signals.
Add signal.emit().


Added:
   trunk/extensions/GObject.js
   trunk/tests/gtype-signal.js   (contents, props changed)
   trunk/tests/gtype-signal2.js   (contents, props changed)
Modified:
   trunk/examples/pango.js
   trunk/extensions/Makefile.am
   trunk/libseed/seed-closure.h
   trunk/libseed/seed-gtype.c
   trunk/libseed/seed-signals.c
   trunk/tests/Makefile.am

Modified: trunk/examples/pango.js
==============================================================================
--- trunk/examples/pango.js	(original)
+++ trunk/examples/pango.js	Mon Nov 10 13:00:08 2008
@@ -107,7 +107,7 @@
 	dy = evt.get_y() - actor.y;
 	
 	select_actor(actor);
-	
+
 	dragging = true;
 	
 	return true;

Added: trunk/extensions/GObject.js
==============================================================================
--- (empty file)
+++ trunk/extensions/GObject.js	Mon Nov 10 13:00:08 2008
@@ -0,0 +1,30 @@
+(function()
+ {
+     var types = [{name:"NONE", fundamental: 1},
+		  {name:"INTERFACE", fundamental: 2},
+		  {name:"CHAR", fundamental: 3},
+		  {name:"UCHAR", fundamental: 4},
+		  {name:"BOOLEAN", fundamental: 5},
+		  {name:"INT", fundamental: 6},
+		  {name:"UINT", fundamental: 7},
+		  {name:"LONG", fundamental: 8},
+		  {name:"ULONG", fundamental: 9},
+		  {name:"INT64", fundamental: 10},
+		  {name:"UINT64", fundamental: 11},
+		  {name:"ENUM", fundamental: 12},
+		  {name:"FLAGS", fundamental: 13},
+		  {name:"FLOAT", fundamental: 14},
+		  {name:"DOUBLE", fundamental: 15},
+		  {name:"STRING", fundamental: 16},
+		  {name:"POINTER", fundamental: 17},
+		  {name:"BOXED", fundamental: 18},
+		  {name:"PARAM", fundamental: 19},
+		  {name:"OBJECT", fundamental: 20}];
+     
+     for (var i = 0; i < types.length; i++)
+	 {
+
+	     GObject["TYPE_"+types[i].name] = types[i].fundamental << 2;
+	 }
+
+ }).apply();

Modified: trunk/extensions/Makefile.am
==============================================================================
--- trunk/extensions/Makefile.am	(original)
+++ trunk/extensions/Makefile.am	Mon Nov 10 13:00:08 2008
@@ -1,4 +1,4 @@
-EXTRA_DIST= Gio.js Seed.js Gtk.js
+EXTRA_DIST= Gio.js Seed.js Gtk.js GObject.js
 
 extensiondir=$(datadir)/seed
-extension_DATA = Gio.js Seed.js Gtk.js
+extension_DATA = Gio.js Seed.js Gtk.js GObject.js

Modified: trunk/libseed/seed-closure.h
==============================================================================
--- trunk/libseed/seed-closure.h	(original)
+++ trunk/libseed/seed-closure.h	Mon Nov 10 13:00:08 2008
@@ -30,6 +30,8 @@
     JSObjectRef this;
 
     JSValueRef user_data;
+    
+    GType return_type;
 } SeedClosure;
 
 typedef struct _SeedNativeClosure {

Modified: trunk/libseed/seed-gtype.c
==============================================================================
--- trunk/libseed/seed-gtype.c	(original)
+++ trunk/libseed/seed-gtype.c	Mon Nov 10 13:00:08 2008
@@ -24,6 +24,292 @@
 
 JSClassRef seed_gtype_class;
 
+/* From pygobject */
+static ffi_type *
+g_value_to_ffi_type (const GValue *gvalue, gpointer *value)
+{
+    ffi_type *rettype = NULL;
+    GType type = g_type_fundamental (G_VALUE_TYPE (gvalue));
+    g_assert (type != G_TYPE_INVALID);
+
+    switch (type) {
+    case G_TYPE_BOOLEAN:
+    case G_TYPE_CHAR:
+    case G_TYPE_INT:
+	rettype = &ffi_type_sint;
+	*value = (gpointer)&(gvalue->data[0].v_int);
+	break;
+    case G_TYPE_UCHAR:
+    case G_TYPE_UINT:
+	rettype = &ffi_type_uint;
+	*value = (gpointer)&(gvalue->data[0].v_uint);
+	break;
+    case G_TYPE_STRING:
+    case G_TYPE_OBJECT:
+    case G_TYPE_BOXED:
+    case G_TYPE_POINTER:
+	rettype = &ffi_type_pointer;
+	*value = (gpointer)&(gvalue->data[0].v_pointer);
+	break;
+    case G_TYPE_FLOAT:
+	rettype = &ffi_type_float;
+	*value = (gpointer)&(gvalue->data[0].v_float);
+	break;
+    case G_TYPE_DOUBLE:
+	rettype = &ffi_type_double;
+	*value = (gpointer)&(gvalue->data[0].v_double);
+	break;
+    case G_TYPE_LONG:
+	rettype = &ffi_type_slong;
+	*value = (gpointer)&(gvalue->data[0].v_long);
+	break;
+    case G_TYPE_ULONG:
+	rettype = &ffi_type_ulong;
+	*value = (gpointer)&(gvalue->data[0].v_ulong);
+	break;
+    case G_TYPE_INT64:
+	rettype = &ffi_type_sint64;
+	*value = (gpointer)&(gvalue->data[0].v_int64);
+	break;
+    case G_TYPE_UINT64:
+	rettype = &ffi_type_uint64;
+	*value = (gpointer)&(gvalue->data[0].v_uint64);
+	break;
+    default:
+	rettype = &ffi_type_pointer;
+	*value = NULL;
+	g_warning ("Unsupported fundamental type: %s", g_type_name (type));
+	break;
+    }
+    return rettype;
+}
+
+/* From pygobject */
+static void
+g_value_from_ffi_type (GValue *gvalue, gpointer *value)
+{
+    switch (g_type_fundamental (G_VALUE_TYPE (gvalue))) {
+    case G_TYPE_INT:
+	g_value_set_int (gvalue, *(gint*)value);
+	break;
+    case G_TYPE_FLOAT:
+	g_value_set_float (gvalue, *(gfloat*)value);
+	break;
+    case G_TYPE_DOUBLE:
+	g_value_set_double (gvalue, *(gdouble*)value);
+	break;
+    case G_TYPE_BOOLEAN:
+	g_value_set_boolean (gvalue, *(gboolean*)value);
+	break;
+    case G_TYPE_STRING:
+	g_value_set_string (gvalue, *(gchar**)value);
+	break;
+    case G_TYPE_CHAR:
+	g_value_set_char (gvalue, *(gchar*)value);
+	break;
+    case G_TYPE_UCHAR:
+	g_value_set_uchar (gvalue, *(guchar*)value);
+	break;
+    case G_TYPE_UINT:
+	g_value_set_uint (gvalue, *(guint*)value);
+	break;
+    case G_TYPE_POINTER:
+	g_value_set_pointer (gvalue, *(gpointer*)value);
+	break;
+    case G_TYPE_LONG:
+	g_value_set_long (gvalue, *(glong*)value);
+	break;
+    case G_TYPE_ULONG:
+	g_value_set_ulong (gvalue, *(gulong*)value);
+	break;
+    case G_TYPE_INT64:
+	g_value_set_int64 (gvalue, *(gint64*)value);
+	break;
+    case G_TYPE_UINT64:
+	g_value_set_uint64 (gvalue, *(guint64*)value);
+	break;
+    case G_TYPE_BOXED:
+	g_value_set_boxed (gvalue, *(gpointer*)value);
+	break;
+    default:
+	g_warning ("Unsupported fundamental type: %s",
+		   g_type_name (g_type_fundamental (G_VALUE_TYPE (gvalue))));
+    }
+
+}
+
+/* from pygobject */
+void
+g_cclosure_marshal_generic_ffi (GClosure *closure,
+				GValue *return_gvalue,
+				guint n_param_values,
+				const GValue *param_values,
+				gpointer invocation_hint,
+				gpointer marshal_data)
+{
+    ffi_type *rtype;
+    void *rvalue;
+    int n_args;
+    ffi_type **atypes;
+    void **args;
+    int i;
+    ffi_cif cif;
+    GCClosure *cc = (GCClosure*) closure;
+
+    if (return_gvalue && G_VALUE_TYPE (return_gvalue)) 
+    {
+	rtype = g_value_to_ffi_type (return_gvalue, &rvalue);
+    }
+    else 
+    {
+	rtype = &ffi_type_void;
+    }
+
+    rvalue = g_alloca (MAX (rtype->size, sizeof (ffi_arg)));
+  
+    n_args = n_param_values + 1;
+    atypes = g_alloca (sizeof (ffi_type *) * n_args);
+    args =  g_alloca (sizeof (gpointer) * n_args);
+
+    if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+	atypes[n_args-1] = g_value_to_ffi_type (param_values + 0,  
+						&args[n_args-1]);
+	atypes[0] = &ffi_type_pointer;
+	args[0] = &closure->data;
+    }
+    else
+    {
+	atypes[0] = g_value_to_ffi_type (param_values + 0, &args[0]);
+	atypes[n_args-1] = &ffi_type_pointer;
+	args[n_args-1] = &closure->data;
+    }
+
+    for (i = 1; i < n_args - 1; i++)
+	atypes[i] = g_value_to_ffi_type (param_values + i, &args[i]);
+
+    if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_args, rtype, atypes) != FFI_OK)
+	return;
+
+    ffi_call (&cif, marshal_data ? marshal_data : cc->callback, rvalue, args);
+
+    if (return_gvalue && G_VALUE_TYPE (return_gvalue))
+	g_value_from_ffi_type (return_gvalue, rvalue);
+}
+
+static JSValueRef
+seed_gsignal_method_invoked(JSContextRef ctx,
+			    JSObjectRef function,
+			    JSObjectRef thisObject,
+			    size_t argumentCount,
+			    const JSValueRef arguments[],
+			    JSValueRef * exception)
+{
+    // TODO: class_closure, and accumlator. Not useful until we have structs.
+    JSValueRef jsname, jstype, jsflags, jsreturn_type, jsparams, ret;
+    GType itype, return_type;
+    guint n_params = 0;
+    GType * param_types = 0;
+    gchar * name;
+    guint signal_id;
+    GSignalFlags flags;
+
+    /* Sanity check */
+    if (argumentCount != 1)
+    {
+	gchar * mes = g_strdup_printf("Signal constructor expected 1 argument",
+				      " got %d \n", argumentCount);
+	seed_make_exception(exception, "ArgumentError", mes);
+	g_free(mes);
+	return (JSObjectRef)JSValueMakeNull(eng->context);
+    }
+    if (JSValueIsNull(eng->context, arguments[0]) || 
+	!JSValueIsObject(eng->context, arguments[0]))
+    {
+	seed_make_exception(exception, "ArgumentError",
+			    "Signal constructor expected object"
+			    " as first argument");
+ 	return (JSObjectRef)JSValueMakeNull(eng->context);
+    }
+
+
+    /* Signal name */
+    jsname = seed_value_get_property((JSObjectRef)arguments[0], "name");
+    /* seed_value_to_string can handle non strings, however the kind
+     * of strings we want as a signal name are rather small, so make sure
+     * we have an actual string */
+    if (JSValueIsNull(eng->context, jsname) || 
+	!JSValueIsString(eng->context, jsname))
+    {
+	seed_make_exception(exception, "ArgumentError",
+			    "Signal definition needs name property");
+	return (JSObjectRef)JSValueMakeNull(eng->context);
+    }
+    name = seed_value_to_string(jsname, exception);
+    
+    /* Type to install on. Comes from class. */
+    jstype = seed_value_get_property(thisObject, "type");
+    itype = seed_value_to_int(jstype, exception);
+    
+    /* Signal flags */
+    jsflags = seed_value_get_property((JSObjectRef)arguments[0], "flags");
+    if (JSValueIsNull(eng->context, jsflags) || 
+	!JSValueIsNumber(eng->context, jsflags))
+	flags = G_SIGNAL_RUN_FIRST;
+    else
+	flags = seed_value_to_long(jsflags, exception);
+    
+    
+    /* Return type */
+    jsreturn_type = seed_value_get_property((JSObjectRef)arguments[0],
+					    "return_type");
+    if (JSValueIsNull(eng->context, jsreturn_type) || 
+	!JSValueIsNumber(eng->context, jsreturn_type))
+	return_type = G_TYPE_NONE;
+    else
+	return_type = seed_value_to_int(jsreturn_type, exception);
+
+    /* Number of params and types */
+    jsparams = seed_value_get_property((JSObjectRef)arguments[0],
+				       "parameters");
+    if (!JSValueIsNull(eng->context, jsparams) &&
+	JSValueIsObject(eng->context, jsparams))
+    {
+	n_params = seed_value_to_int
+	    (seed_value_get_property(jsparams, "length"), exception);
+	if (n_params > 0)
+	{
+	    guint i;
+	    
+	    param_types = g_new0(GType, n_params);
+	    for (i = 0; i < n_params; i++)
+	    {
+		JSValueRef ptype = 
+		    JSObjectGetPropertyAtIndex(eng->context,
+					       (JSObjectRef)jsparams, 
+					       i,
+					       exception);
+		
+		param_types[i] = seed_value_to_int(ptype, exception);
+	    }
+	}
+    }   
+    
+    signal_id = g_signal_newv(name, itype,
+			      flags, 0, 0, 0, 
+			      g_cclosure_marshal_generic_ffi,
+			      return_type,
+			      n_params,
+			      param_types);
+    
+    g_free(name);
+    g_free(param_types);
+    
+    return (JSValueRef)seed_value_from_uint(signal_id, exception);
+}
+
+
+
 static void
 seed_handle_class_init_closure(ffi_cif * cif,
 			       void *result, void **args, void *userdata)
@@ -37,6 +323,14 @@
     jsargs[0] = seed_make_struct(*(gpointer *) args[0], 0);
     jsargs[1] = seed_gobject_get_prototype_for_gtype(type);
 
+    // TODO: 
+    // Should probably have a custom type for class, and have it auto convert.
+    seed_value_set_property((JSObjectRef)jsargs[0], 
+			    "type", seed_value_from_int(type, 0));
+    seed_create_function("install_signal",
+			 &seed_gsignal_method_invoked,
+			 (JSObjectRef)jsargs[0]);
+
     JSObjectCallAsFunction(eng->context, function, 0, 2, jsargs, 0);
     if (exception)
     {
@@ -211,15 +505,14 @@
 
 void seed_gtype_init(void)
 {
-    JSClassDefinition def = kJSClassDefinitionEmpty;
+    JSClassDefinition gtype_def = kJSClassDefinitionEmpty;
     JSObjectRef gtype_constructor;
 
-    def.callAsConstructor = seed_gtype_constructor_invoked;
-    seed_gtype_class = JSClassCreate(&def);
+    gtype_def.callAsConstructor = seed_gtype_constructor_invoked;
+    seed_gtype_class = JSClassCreate(&gtype_def);
     JSClassRetain(seed_gtype_class);
 
     gtype_constructor = JSObjectMake(eng->context, seed_gtype_class, 0);
 
     seed_value_set_property(eng->global, "GType", gtype_constructor);
-
 }

Modified: trunk/libseed/seed-signals.c
==============================================================================
--- trunk/libseed/seed-signals.c	(original)
+++ trunk/libseed/seed-signals.c	Mon Nov 10 13:00:08 2008
@@ -111,6 +111,7 @@
 {
     SeedClosure *seed_closure = (SeedClosure *) closure;
     JSValueRef *args, exception = 0;
+    JSValueRef ret = 0;
     gint i;
 
     args = g_newa(JSValueRef, n_param_values + 1);
@@ -131,10 +132,16 @@
     else
 	args[i] = JSValueMakeNull(eng->context);
 
-    JSObjectCallAsFunction(eng->context, seed_closure->function,
+    ret = JSObjectCallAsFunction(eng->context, seed_closure->function,
 			   seed_closure->this,
 			   n_param_values + 1, args, &exception);
 
+    if (ret)
+    {
+	seed_gvalue_from_seed_value(ret, seed_closure->return_type,
+				    return_value, &exception);
+
+    }
     if (exception)
     {
 	gchar *mes = seed_exception_to_string(exception);
@@ -145,6 +152,61 @@
 }
 
 static JSValueRef
+seed_gobject_signal_emit(JSContextRef ctx,
+			    JSObjectRef function,
+			    JSObjectRef thisObject,
+			    size_t argumentCount,
+			    const JSValueRef arguments[],
+			    JSValueRef * exception)
+{
+    JSValueRef ret;
+    GValue * params;
+    GValue ret_value = {0};
+    GSignalQuery query;
+
+    signal_privates * privates;
+    guint i;
+
+    privates = JSObjectGetPrivate(thisObject);
+
+    g_signal_query(privates->signal_id,
+		   &query);
+    
+    if (argumentCount != query.n_params)
+    {
+	gchar * mes = g_strdup_printf("Signal: %s for type %s expected %d "
+				      "arguments, got %d",
+				      query.signal_name,
+				      g_type_name(query.itype),
+				      query.n_params,
+				      argumentCount);
+	seed_make_exception(exception, "ArgumentError", mes);
+	g_free(mes);
+	return JSValueMakeNull(eng->context);
+    }
+
+       
+    params = g_new0(GValue, argumentCount+1);
+   
+    g_value_init(&params[0], G_TYPE_OBJECT);
+    g_value_set_object(&params[0], privates->object);
+    for (i = 0; i < argumentCount; i++)
+	seed_gvalue_from_seed_value(arguments[i], 
+				    query.param_types[i],
+				    &params[i+1], exception);
+    
+    g_signal_emitv(params, privates->signal_id, 0, &ret_value);
+    
+    for (i = 0; i < argumentCount; i++)
+	g_value_unset(&params[i]);
+    g_free(params);
+    
+    ret = seed_value_from_gvalue(&ret_value, exception);
+    
+    return ret;    
+}
+
+static JSValueRef
 seed_gobject_signal_connect(JSContextRef ctx,
 			    JSObjectRef function,
 			    JSObjectRef thisObject,
@@ -152,6 +214,7 @@
 			    const JSValueRef arguments[],
 			    JSValueRef * exception)
 {
+    GSignalQuery query;
     signal_privates *privates;
     GClosure *closure;
 
@@ -170,6 +233,8 @@
 	g_free(mes);
 	return JSValueMakeNull(eng->context);
     }
+    
+    g_signal_query(privates->signal_id, &query);
 
     closure = g_closure_new_simple(sizeof(SeedClosure), 0);
     g_closure_set_marshal(closure, seed_signal_marshal_func);
@@ -177,6 +242,7 @@
     ((SeedClosure *) closure)->function = (JSObjectRef) arguments[0];
     ((SeedClosure *) closure)->object =
 	g_object_get_data(privates->object, "js-ref");
+    ((SeedClosure *) closure)->return_type = query.return_type;
     if (argumentCount >= 2 && !JSValueIsNull(eng->context, arguments[1]))
     {
 	JSValueProtect(eng->context, (JSObjectRef) arguments[1]);
@@ -199,9 +265,9 @@
 }
 
 JSStaticFunction signal_static_functions[] =
-    { {"connect", seed_gobject_signal_connect, 0}
-,
-{0, 0, 0}
+{ {"connect", seed_gobject_signal_connect, 0},
+  {"emit", seed_gobject_signal_emit, 0},
+  {0, 0, 0}
 };
 
 JSClassDefinition gobject_signal_def = {

Modified: trunk/tests/Makefile.am
==============================================================================
--- trunk/tests/Makefile.am	(original)
+++ trunk/tests/Makefile.am	Mon Nov 10 13:00:08 2008
@@ -3,8 +3,11 @@
     compare.js \
     constructor-args.js \
     signal-this.js \
+    gtype-signal.js \
     signal-userdata.js \
+    gtype-signal-exception.js \
     out-test.js \
+    gtype-signal2.js \
     signal-expects.js \
     gtype.js \
     json.js \

Added: trunk/tests/gtype-signal.js
==============================================================================
--- (empty file)
+++ trunk/tests/gtype-signal.js	Mon Nov 10 13:00:08 2008
@@ -0,0 +1,34 @@
+#!/usr/bin/env seed
+// Returns: 0
+// STDIN:
+// STDOUT:Hello\nGoodbye
+// STDERR:
+
+Seed.import_namespace("Gtk");
+Gtk.init(null, null);
+
+HelloWindowType = {       
+    parent: Gtk.Window,
+    name: "HelloWindow",
+    class_init: function(klass, prototype)
+    {
+	var HelloSignalDefinition = {name: "hello"};
+	var GoodbyeSignalDefinition = {name: "goodbye"};
+	
+	
+	hello_signal_id = klass.install_signal(HelloSignalDefinition);
+	goodbye_signal_id = klass.install_signal(GoodbyeSignalDefinition);
+    },
+    instance_init: function(klass)
+    {
+    }};
+
+HelloWindow = new GType(HelloWindowType);
+w = new HelloWindow();
+
+w.signal.hello.connect(function(){Seed.print("Hello")});
+w.signal.goodbye.connect(function(){Seed.print("Goodbye")});
+
+w.signal.hello.emit();
+w.signal.goodbye.emit();
+	  

Added: trunk/tests/gtype-signal2.js
==============================================================================
--- (empty file)
+++ trunk/tests/gtype-signal2.js	Mon Nov 10 13:00:08 2008
@@ -0,0 +1,32 @@
+#!/usr/bin/env seed
+// Returns: 0
+// STDIN:
+// STDOUT:2 Weathermen
+// STDERR:
+
+Seed.import_namespace("GObject");
+Seed.import_namespace("Gtk");
+Gtk.init(null, null);
+
+HelloWindowType = {       
+    parent: Gtk.Window,
+    name: "HelloWindow",
+    class_init: function(klass, prototype)
+    {
+	var HelloSignalDefinition = {name: "hello",
+				     parameters: [GObject.TYPE_INT,
+						  GObject.TYPE_STRING]};
+	
+	hello_signal_id = klass.install_signal(HelloSignalDefinition);
+
+    },
+}
+
+HelloWindow = new GType(HelloWindowType);
+w = new HelloWindow();
+
+w.signal.hello.connect(function(object, number, string)
+		       {Seed.print(number+ " " + string)});
+
+w.signal.hello.emit(2, "Weathermen");
+	  



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