cvs: php-gtk /demos/examples signals.php /main php_gtk.h phpg_closure.c phpg_gobject.c
| From: | Andrei Zmievski | Date: | Sat, 22 Sep 2007 20:23:09 +0000 |
| Subject: | cvs: php-gtk /demos/examples signals.php /main php_gtk.h phpg_closure.c phpg_gobject.c | ||
| Groups: | php.gtk.cvs | ||
| Request: | Send a blank email to php-gtk-cvs+get-2773@lists.php.net to get a copy of this message | ||
andrei Sat Sep 22 20:23:09 2007 UTC
Added files:
/php-gtk/demos/examples signals.php
Modified files:
/php-gtk/main php_gtk.h phpg_closure.c phpg_gobject.c
Log:
Custom signals support. See demos/examples/signals.php for an example.
http://cvs.php.net/viewvc.cgi/php-gtk/main/php_gtk.h?r1=1.149&r2=1.150&diff_format=u Index: php-gtk/main/php_gtk.h diff -u php-gtk/main/php_gtk.h:1.149 php-gtk/main/php_gtk.h:1.150 --- php-gtk/main/php_gtk.h:1.149 Wed Sep 19 13:38:46 2007 +++ php-gtk/main/php_gtk.h Sat Sep 22 20:23:09 2007 @@ -18,7 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* $Id: php_gtk.h,v 1.149 2007/09/19 13:38:46 auroraeosrose Exp $: */ +/* $Id: php_gtk.h,v 1.150 2007/09/22 20:23:09 andrei Exp $: */ #ifndef _PHP_GTK_H #define _PHP_GTK_H @@ -518,6 +518,7 @@ /* Closures */ PHP_GTK_API GClosure* phpg_closure_new(zval *callback, zval *user_args, int connect_type, zval *replace_object TSRMLS_DC); PHP_GTK_API void phpg_watch_closure(zval *obj, GClosure *closure TSRMLS_DC); +PHP_GTK_API GClosure* phpg_get_signal_class_closure(); PHP_GTK_API extern PHP_GTK_EXPORT_CE(gtype_ce); PHP_GTK_API extern PHP_GTK_EXPORT_CE(gobject_ce); http://cvs.php.net/viewvc.cgi/php-gtk/main/phpg_closure.c?r1=1.17&r2=1.18&diff_format=u Index: php-gtk/main/phpg_closure.c diff -u php-gtk/main/phpg_closure.c:1.17 php-gtk/main/phpg_closure.c:1.18 --- php-gtk/main/phpg_closure.c:1.17 Sun Sep 16 21:55:18 2007 +++ php-gtk/main/phpg_closure.c Sat Sep 22 20:23:09 2007 @@ -198,6 +198,129 @@ efree(cbd); } +static void phpg_signal_class_closure_marshal(GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + GObject *object; + GSignalInvocationHint *hint = (GSignalInvocationHint *)invocation_hint; + zval *php_object = NULL; + gchar *method_name, *tmp, *lc_method_name; + gsize method_name_len; + zval ***params = NULL; + zval *retval = NULL; + zval *item, z_method_name; + phpg_gboxed_t *boxed_item; + int i, k; + + phpg_return_if_fail(invocation_hint != NULL); + /* verify that first parameter is an object */ + object = g_value_get_object(¶m_values[0]); + phpg_return_if_fail(object != NULL && G_IS_OBJECT(object)); + + /* create wrapper for the object */ + phpg_gobject_new(&php_object, object TSRMLS_CC); + if (Z_TYPE_P(php_object) == IS_NULL) { + zval_ptr_dtor(&php_object); + return; + } + + method_name = g_strconcat("__do_", g_signal_name(hint->signal_id), NULL); + for (tmp = method_name; *tmp != '\0'; tmp++) { + if (*tmp == '-') *tmp = '_'; + } + + method_name_len = strlen(method_name); + lc_method_name = g_ascii_strdown(method_name, method_name_len); + if (!zend_hash_exists(&Z_OBJCE_P(php_object)->function_table, lc_method_name, method_name_len+1)) { + union _zend_function *func = NULL; + + g_free(lc_method_name); + + if (Z_OBJ_HT_P(php_object)->get_method != NULL + && (func = Z_OBJ_HT_P(php_object)->get_method(&php_object, method_name, method_name_len TSRMLS_CC)) != NULL) { + if (func->type == ZEND_INTERNAL_FUNCTION + && ((zend_internal_function*)func)->handler == zend_std_call_user_call + ) { + efree(((zend_internal_function*)func)->function_name); + efree(func); + zval_ptr_dtor(&php_object); + return; + } + } + zval_ptr_dtor(&php_object); + return; + } + g_free(lc_method_name); + + params = (zval ***)emalloc((n_param_values-1) * sizeof(zval **)); + for (i = 0; i < n_param_values-1; i++) { + params[i] = (zval **) emalloc(sizeof(zval *)); + *(params[i]) = NULL; + if (phpg_gvalue_to_zval(¶m_values[i+1], params[i], FALSE, TRUE TSRMLS_CC) != SUCCESS) { + goto err_class_closure_marshal; + } + } + + ZVAL_STRINGL(&z_method_name, method_name, method_name_len, 0); + call_user_function_ex(EG(function_table), &php_object, &z_method_name, + &retval, n_param_values-1, params, 0, NULL TSRMLS_CC); + + + for (k = 0; k < i; k++) { + item = *params[k]; + if (Z_TYPE_P(item) == IS_OBJECT && + instanceof_function(Z_OBJCE_P(item), gboxed_ce TSRMLS_CC) && + item->refcount > 1) { + boxed_item = phpg_gboxed_get(item TSRMLS_CC); + if (!boxed_item->free_on_destroy) { + boxed_item->boxed = g_boxed_copy(boxed_item->gtype, boxed_item->boxed); + boxed_item->free_on_destroy = TRUE; + } + } + } + + if (retval) { + if (return_value) { + if (phpg_gvalue_from_zval(return_value, &retval, TRUE TSRMLS_CC) == FAILURE) { + php_error(E_WARNING, "Could not convert return value of custom signal action '%s' to '%s'", + method_name, g_type_name(G_VALUE_TYPE(return_value))); + } + } + zval_ptr_dtor(&retval); + } + + i--; // adjust for cleanup + +err_class_closure_marshal: + for ( ; i >= 0; i--) { + zval_ptr_dtor(params[i]); + efree(params[i]); + } + efree(params); + g_free(method_name); + zval_ptr_dtor(&php_object); +} + + +PHP_GTK_API GClosure* phpg_get_signal_class_closure() +{ + static GClosure *closure = NULL; + + if (closure == NULL) { + closure = g_closure_new_simple(sizeof(GClosure), NULL); + g_closure_set_marshal(closure, phpg_signal_class_closure_marshal); + + g_closure_ref(closure); + g_closure_sink(closure); + } + + return closure; +} + #endif /* HAVE_PHP_GTK */ /* vim: set fdm=marker et sts=4: */ http://cvs.php.net/viewvc.cgi/php-gtk/main/phpg_gobject.c?r1=1.63&r2=1.64&diff_format=u Index: php-gtk/main/phpg_gobject.c diff -u php-gtk/main/phpg_gobject.c:1.63 php-gtk/main/phpg_gobject.c:1.64 --- php-gtk/main/phpg_gobject.c:1.63 Wed Sep 19 13:38:47 2007 +++ php-gtk/main/phpg_gobject.c Sat Sep 22 20:23:09 2007 @@ -400,6 +400,7 @@ obj = PHPG_GOBJECT(this_ptr); if (!g_signal_parse_name(signal, G_OBJECT_TYPE(obj), &signal_id, &detail, TRUE)) { php_error(E_WARNING, "%s(): unknown signal name '%s'", get_active_function_name(TSRMLS_C), signal); + if (extra) zval_ptr_dtor(&extra); return; } @@ -700,12 +701,14 @@ obj = PHPG_GOBJECT(this_ptr); if (!g_signal_parse_name(signal, G_OBJECT_TYPE(obj), &signal_id, &detail, TRUE)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "unknown signal name '%s'", signal); + if (extra) zval_ptr_dtor(&extra); return; } g_signal_query(signal_id, &query); if (extra && zend_hash_num_elements(Z_ARRVAL_P(extra)) != query.n_params) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d parameters needed for signal '%s', %d given", query.n_params, signal, zend_hash_num_elements(Z_ARRVAL_P(extra))); + zval_ptr_dtor(&extra); return; } @@ -724,7 +727,7 @@ if (phpg_gvalue_from_zval(¶ms[i], item, TRUE TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not convert value to %s for parameter %d", - g_type_name(G_VALUE_TYPE(¶ms[i])), i-1); + g_type_name(G_VALUE_TYPE(¶ms[i])), i); goto cleanup; } } @@ -939,7 +942,7 @@ phpg_gobject_new(&php_object, object TSRMLS_CC); phpg_paramspec_new(&php_pspec, pspec TSRMLS_CC); - zend_call_method_with_1_params(&php_object, Z_OBJCE_P(php_object), NULL, "do_get_property", &retval, php_pspec); + zend_call_method_with_1_params(&php_object, Z_OBJCE_P(php_object), NULL, "__get_gproperty", &retval, php_pspec); SEPARATE_ZVAL(&retval); if (retval) { @@ -971,7 +974,7 @@ phpg_gobject_new(&php_object, object TSRMLS_CC); phpg_paramspec_new(&php_pspec, pspec TSRMLS_CC); - zend_call_method_with_2_params(&php_object, Z_OBJCE_P(php_object), NULL, "do_set_property", &retval, php_pspec, php_value); + zend_call_method_with_2_params(&php_object, Z_OBJCE_P(php_object), NULL, "__set_gproperty", &retval, php_pspec, php_value); if (retval) { zval_ptr_dtor(&retval); @@ -1236,13 +1239,115 @@ return retval; } +static int phpg_override_signal(GType type, const char *signal_name) +{ + guint signal_id; + + signal_id = g_signal_lookup(signal_name, type); + if (!signal_id) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not find signal '%s'", signal_name); + return FAILURE; + } + + g_signal_override_class_closure(signal_id, type, phpg_get_signal_class_closure()); + return SUCCESS; +} + +static int phpg_create_signal(GType type, const char *signal_name, zval *data) +{ + long signal_flags; + zval *php_return_type; + zval *php_param_types; + GType return_type; + GType *param_types; + guint n_params, i, signal_id; + zval **item; + + if (!php_gtk_parse_args_hash(data, "lVa", &signal_flags, &php_return_type, &php_param_types)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "incorrect format for '%s' signal spec", signal_name); + return FAILURE; + } + + return_type = phpg_gtype_from_zval(php_return_type); + if (0 == return_type) { + return FAILURE; + } + + n_params = zend_hash_num_elements(Z_ARRVAL_P(php_param_types)); + param_types = safe_emalloc(n_params, sizeof(GType), 0); + for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(php_param_types)), i = 0; + zend_hash_get_current_data(Z_ARRVAL_P(php_param_types), (void**)&item) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(php_param_types)), i++) { + + param_types[i] = phpg_gtype_from_zval(*item); + if (0 == param_types[i]) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not get type of param #%d gor signal '%s'", i+1, signal_name); + efree(param_types); + return FAILURE; + } + } + + signal_id = g_signal_newv(signal_name, type, signal_flags, phpg_get_signal_class_closure(), + (GSignalAccumulator)0, NULL, (GSignalCMarshaller)0, + return_type, n_params, param_types); + efree(param_types); + + if (0 == signal_id) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not create signal '%s'", signal_name); + return FAILURE; + } + + return SUCCESS; +} + +static int phpg_register_signals(GType type, zval *signals) +{ + GObjectClass *oclass; + zval **data; + char *str_key; + uint str_key_len; + ulong num_key; + int retval = SUCCESS; + + oclass = g_type_class_ref(type); + if (!oclass) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "couuld not get a reference to type class"); + return FAILURE; + } + + for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(signals)); + zend_hash_get_current_data(Z_ARRVAL_P(signals), (void**)&data) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(signals))) { + + if (zend_hash_get_current_key_ex(Z_ARRVAL_P(signals), &str_key, &str_key_len, + &num_key, 0, NULL) != HASH_KEY_IS_STRING) { + continue; + } + + if (Z_TYPE_PP(data) == IS_NULL || + (Z_TYPE_PP(data) == IS_STRING && + !strcmp(Z_STRVAL_PP(data), "override"))) { + retval = phpg_override_signal(type, str_key); + } else { + retval = phpg_create_signal(type, str_key, *data); + } + + if (retval == FAILURE) { + break; + } + } + + g_type_class_unref(oclass); + return retval; +} + static PHP_METHOD(GObject, register_type) { zend_class_entry *class = gobject_ce; GType parent_type, new_type; GTypeQuery query; const char *type_name; - zval **prop_decls; + zval **prop_decls, **signal_decls; GTypeInfo type_info = { 0, /* class_size */ @@ -1307,6 +1412,16 @@ } /* register signals */ + if (zend_hash_find(&class->default_properties, "__gsignals", sizeof("__gsignals"), (void**)&signal_decls) == SUCCESS) { + if (Z_TYPE_PP(signal_decls) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "__gsignals variable has to be an array"); + return; + } + if (phpg_register_signals(new_type, *signal_decls TSRMLS_CC) == FAILURE) { + return; + } + zend_hash_del(&class->default_properties, "__gsignals", sizeof("__gsignals")); + } /*TODO interface registration */ #if 0 http://cvs.php.net/viewvc.cgi/php-gtk/demos/examples/signals.php?view=markup&rev=1.1 Index: php-gtk/demos/examples/signals.php +++ php-gtk/demos/examples/signals.php <?php class NewType extends GtkButton { public $__gsignals = array( 'clicked' => 'override', 'mashed' => array(GObject::SIGNAL_RUN_LAST, GObject::TYPE_BOOLEAN, array(GObject::TYPE_INT, GtkRequisition::gtype)), ); function __construct() { parent::__construct(); } function __do_clicked() { echo "do_clicked called\n"; } function __do_mashed($arg, $arg2) { echo "NewType: class closure for `mashed` called with arguments {$arg}, ", get_class($arg2), "\n"; } } class NewType2 extends NewType { function __do_mashed($arg, $arg2) { echo "NewType2: class closure for `mashed` called, chaining up to NewType\n"; NewType::__do_mashed($arg, $arg2); } } function my_handler($obj, $arg, $arg2, $ex) { echo "my_handler called with args {$arg}, ", get_class($arg2), " and extra {$ex}\n"; return true; } GObject::register_type('NewType'); GObject::register_type('NewType2'); $c = new NewType; $d = new NewType2; $r = $c->size_request(); $c->connect('mashed', 'my_handler', 99); $c->emit('clicked'); var_dump($c->emit('mashed', 42, $r)); var_dump($d->emit('mashed', 24, $r));
http://cvs.php.net/viewvc.cgi/php-gtk/main/php_gtk.h?r1=1.149&r2=1.150&diff_format=u Index: php-gtk/main/php_gtk.h diff -u php-gtk/main/php_gtk.h:1.149 php-gtk/main/php_gtk.h:1.150 --- php-gtk/main/php_gtk.h:1.149 Wed Sep 19 13:38:46 2007 +++ php-gtk/main/php_gtk.h Sat Sep 22 20:23:09 2007 @@ -18,7 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* $Id: php_gtk.h,v 1.149 2007/09/19 13:38:46 auroraeosrose Exp $: */ +/* $Id: php_gtk.h,v 1.150 2007/09/22 20:23:09 andrei Exp $: */ #ifndef _PHP_GTK_H #define _PHP_GTK_H @@ -518,6 +518,7 @@ /* Closures */ PHP_GTK_API GClosure* phpg_closure_new(zval *callback, zval *user_args, int connect_type, zval *replace_object TSRMLS_DC); PHP_GTK_API void phpg_watch_closure(zval *obj, GClosure *closure TSRMLS_DC); +PHP_GTK_API GClosure* phpg_get_signal_class_closure(); PHP_GTK_API extern PHP_GTK_EXPORT_CE(gtype_ce); PHP_GTK_API extern PHP_GTK_EXPORT_CE(gobject_ce); http://cvs.php.net/viewvc.cgi/php-gtk/main/phpg_closure.c?r1=1.17&r2=1.18&diff_format=u Index: php-gtk/main/phpg_closure.c diff -u php-gtk/main/phpg_closure.c:1.17 php-gtk/main/phpg_closure.c:1.18 --- php-gtk/main/phpg_closure.c:1.17 Sun Sep 16 21:55:18 2007 +++ php-gtk/main/phpg_closure.c Sat Sep 22 20:23:09 2007 @@ -198,6 +198,129 @@ efree(cbd); } +static void phpg_signal_class_closure_marshal(GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + GObject *object; + GSignalInvocationHint *hint = (GSignalInvocationHint *)invocation_hint; + zval *php_object = NULL; + gchar *method_name, *tmp, *lc_method_name; + gsize method_name_len; + zval ***params = NULL; + zval *retval = NULL; + zval *item, z_method_name; + phpg_gboxed_t *boxed_item; + int i, k; + + phpg_return_if_fail(invocation_hint != NULL); + /* verify that first parameter is an object */ + object = g_value_get_object(¶m_values[0]); + phpg_return_if_fail(object != NULL && G_IS_OBJECT(object)); + + /* create wrapper for the object */ + phpg_gobject_new(&php_object, object TSRMLS_CC); + if (Z_TYPE_P(php_object) == IS_NULL) { + zval_ptr_dtor(&php_object); + return; + } + + method_name = g_strconcat("__do_", g_signal_name(hint->signal_id), NULL); + for (tmp = method_name; *tmp != '\0'; tmp++) { + if (*tmp == '-') *tmp = '_'; + } + + method_name_len = strlen(method_name); + lc_method_name = g_ascii_strdown(method_name, method_name_len); + if (!zend_hash_exists(&Z_OBJCE_P(php_object)->function_table, lc_method_name, method_name_len+1)) { + union _zend_function *func = NULL; + + g_free(lc_method_name); + + if (Z_OBJ_HT_P(php_object)->get_method != NULL + && (func = Z_OBJ_HT_P(php_object)->get_method(&php_object, method_name, method_name_len TSRMLS_CC)) != NULL) { + if (func->type == ZEND_INTERNAL_FUNCTION + && ((zend_internal_function*)func)->handler == zend_std_call_user_call + ) { + efree(((zend_internal_function*)func)->function_name); + efree(func); + zval_ptr_dtor(&php_object); + return; + } + } + zval_ptr_dtor(&php_object); + return; + } + g_free(lc_method_name); + + params = (zval ***)emalloc((n_param_values-1) * sizeof(zval **)); + for (i = 0; i < n_param_values-1; i++) { + params[i] = (zval **) emalloc(sizeof(zval *)); + *(params[i]) = NULL; + if (phpg_gvalue_to_zval(¶m_values[i+1], params[i], FALSE, TRUE TSRMLS_CC) != SUCCESS) { + goto err_class_closure_marshal; + } + } + + ZVAL_STRINGL(&z_method_name, method_name, method_name_len, 0); + call_user_function_ex(EG(function_table), &php_object, &z_method_name, + &retval, n_param_values-1, params, 0, NULL TSRMLS_CC); + + + for (k = 0; k < i; k++) { + item = *params[k]; + if (Z_TYPE_P(item) == IS_OBJECT && + instanceof_function(Z_OBJCE_P(item), gboxed_ce TSRMLS_CC) && + item->refcount > 1) { + boxed_item = phpg_gboxed_get(item TSRMLS_CC); + if (!boxed_item->free_on_destroy) { + boxed_item->boxed = g_boxed_copy(boxed_item->gtype, boxed_item->boxed); + boxed_item->free_on_destroy = TRUE; + } + } + } + + if (retval) { + if (return_value) { + if (phpg_gvalue_from_zval(return_value, &retval, TRUE TSRMLS_CC) == FAILURE) { + php_error(E_WARNING, "Could not convert return value of custom signal action '%s' to '%s'", + method_name, g_type_name(G_VALUE_TYPE(return_value))); + } + } + zval_ptr_dtor(&retval); + } + + i--; // adjust for cleanup + +err_class_closure_marshal: + for ( ; i >= 0; i--) { + zval_ptr_dtor(params[i]); + efree(params[i]); + } + efree(params); + g_free(method_name); + zval_ptr_dtor(&php_object); +} + + +PHP_GTK_API GClosure* phpg_get_signal_class_closure() +{ + static GClosure *closure = NULL; + + if (closure == NULL) { + closure = g_closure_new_simple(sizeof(GClosure), NULL); + g_closure_set_marshal(closure, phpg_signal_class_closure_marshal); + + g_closure_ref(closure); + g_closure_sink(closure); + } + + return closure; +} + #endif /* HAVE_PHP_GTK */ /* vim: set fdm=marker et sts=4: */ http://cvs.php.net/viewvc.cgi/php-gtk/main/phpg_gobject.c?r1=1.63&r2=1.64&diff_format=u Index: php-gtk/main/phpg_gobject.c diff -u php-gtk/main/phpg_gobject.c:1.63 php-gtk/main/phpg_gobject.c:1.64 --- php-gtk/main/phpg_gobject.c:1.63 Wed Sep 19 13:38:47 2007 +++ php-gtk/main/phpg_gobject.c Sat Sep 22 20:23:09 2007 @@ -400,6 +400,7 @@ obj = PHPG_GOBJECT(this_ptr); if (!g_signal_parse_name(signal, G_OBJECT_TYPE(obj), &signal_id, &detail, TRUE)) { php_error(E_WARNING, "%s(): unknown signal name '%s'", get_active_function_name(TSRMLS_C), signal); + if (extra) zval_ptr_dtor(&extra); return; } @@ -700,12 +701,14 @@ obj = PHPG_GOBJECT(this_ptr); if (!g_signal_parse_name(signal, G_OBJECT_TYPE(obj), &signal_id, &detail, TRUE)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "unknown signal name '%s'", signal); + if (extra) zval_ptr_dtor(&extra); return; } g_signal_query(signal_id, &query); if (extra && zend_hash_num_elements(Z_ARRVAL_P(extra)) != query.n_params) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d parameters needed for signal '%s', %d given", query.n_params, signal, zend_hash_num_elements(Z_ARRVAL_P(extra))); + zval_ptr_dtor(&extra); return; } @@ -724,7 +727,7 @@ if (phpg_gvalue_from_zval(¶ms[i], item, TRUE TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not convert value to %s for parameter %d", - g_type_name(G_VALUE_TYPE(¶ms[i])), i-1); + g_type_name(G_VALUE_TYPE(¶ms[i])), i); goto cleanup; } } @@ -939,7 +942,7 @@ phpg_gobject_new(&php_object, object TSRMLS_CC); phpg_paramspec_new(&php_pspec, pspec TSRMLS_CC); - zend_call_method_with_1_params(&php_object, Z_OBJCE_P(php_object), NULL, "do_get_property", &retval, php_pspec); + zend_call_method_with_1_params(&php_object, Z_OBJCE_P(php_object), NULL, "__get_gproperty", &retval, php_pspec); SEPARATE_ZVAL(&retval); if (retval) { @@ -971,7 +974,7 @@ phpg_gobject_new(&php_object, object TSRMLS_CC); phpg_paramspec_new(&php_pspec, pspec TSRMLS_CC); - zend_call_method_with_2_params(&php_object, Z_OBJCE_P(php_object), NULL, "do_set_property", &retval, php_pspec, php_value); + zend_call_method_with_2_params(&php_object, Z_OBJCE_P(php_object), NULL, "__set_gproperty", &retval, php_pspec, php_value); if (retval) { zval_ptr_dtor(&retval); @@ -1236,13 +1239,115 @@ return retval; } +static int phpg_override_signal(GType type, const char *signal_name) +{ + guint signal_id; + + signal_id = g_signal_lookup(signal_name, type); + if (!signal_id) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not find signal '%s'", signal_name); + return FAILURE; + } + + g_signal_override_class_closure(signal_id, type, phpg_get_signal_class_closure()); + return SUCCESS; +} + +static int phpg_create_signal(GType type, const char *signal_name, zval *data) +{ + long signal_flags; + zval *php_return_type; + zval *php_param_types; + GType return_type; + GType *param_types; + guint n_params, i, signal_id; + zval **item; + + if (!php_gtk_parse_args_hash(data, "lVa", &signal_flags, &php_return_type, &php_param_types)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "incorrect format for '%s' signal spec", signal_name); + return FAILURE; + } + + return_type = phpg_gtype_from_zval(php_return_type); + if (0 == return_type) { + return FAILURE; + } + + n_params = zend_hash_num_elements(Z_ARRVAL_P(php_param_types)); + param_types = safe_emalloc(n_params, sizeof(GType), 0); + for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(php_param_types)), i = 0; + zend_hash_get_current_data(Z_ARRVAL_P(php_param_types), (void**)&item) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(php_param_types)), i++) { + + param_types[i] = phpg_gtype_from_zval(*item); + if (0 == param_types[i]) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not get type of param #%d gor signal '%s'", i+1, signal_name); + efree(param_types); + return FAILURE; + } + } + + signal_id = g_signal_newv(signal_name, type, signal_flags, phpg_get_signal_class_closure(), + (GSignalAccumulator)0, NULL, (GSignalCMarshaller)0, + return_type, n_params, param_types); + efree(param_types); + + if (0 == signal_id) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not create signal '%s'", signal_name); + return FAILURE; + } + + return SUCCESS; +} + +static int phpg_register_signals(GType type, zval *signals) +{ + GObjectClass *oclass; + zval **data; + char *str_key; + uint str_key_len; + ulong num_key; + int retval = SUCCESS; + + oclass = g_type_class_ref(type); + if (!oclass) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "couuld not get a reference to type class"); + return FAILURE; + } + + for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(signals)); + zend_hash_get_current_data(Z_ARRVAL_P(signals), (void**)&data) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(signals))) { + + if (zend_hash_get_current_key_ex(Z_ARRVAL_P(signals), &str_key, &str_key_len, + &num_key, 0, NULL) != HASH_KEY_IS_STRING) { + continue; + } + + if (Z_TYPE_PP(data) == IS_NULL || + (Z_TYPE_PP(data) == IS_STRING && + !strcmp(Z_STRVAL_PP(data), "override"))) { + retval = phpg_override_signal(type, str_key); + } else { + retval = phpg_create_signal(type, str_key, *data); + } + + if (retval == FAILURE) { + break; + } + } + + g_type_class_unref(oclass); + return retval; +} + static PHP_METHOD(GObject, register_type) { zend_class_entry *class = gobject_ce; GType parent_type, new_type; GTypeQuery query; const char *type_name; - zval **prop_decls; + zval **prop_decls, **signal_decls; GTypeInfo type_info = { 0, /* class_size */ @@ -1307,6 +1412,16 @@ } /* register signals */ + if (zend_hash_find(&class->default_properties, "__gsignals", sizeof("__gsignals"), (void**)&signal_decls) == SUCCESS) { + if (Z_TYPE_PP(signal_decls) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "__gsignals variable has to be an array"); + return; + } + if (phpg_register_signals(new_type, *signal_decls TSRMLS_CC) == FAILURE) { + return; + } + zend_hash_del(&class->default_properties, "__gsignals", sizeof("__gsignals")); + } /*TODO interface registration */ #if 0 http://cvs.php.net/viewvc.cgi/php-gtk/demos/examples/signals.php?view=markup&rev=1.1 Index: php-gtk/demos/examples/signals.php +++ php-gtk/demos/examples/signals.php <?php class NewType extends GtkButton { public $__gsignals = array( 'clicked' => 'override', 'mashed' => array(GObject::SIGNAL_RUN_LAST, GObject::TYPE_BOOLEAN, array(GObject::TYPE_INT, GtkRequisition::gtype)), ); function __construct() { parent::__construct(); } function __do_clicked() { echo "do_clicked called\n"; } function __do_mashed($arg, $arg2) { echo "NewType: class closure for `mashed` called with arguments {$arg}, ", get_class($arg2), "\n"; } } class NewType2 extends NewType { function __do_mashed($arg, $arg2) { echo "NewType2: class closure for `mashed` called, chaining up to NewType\n"; NewType::__do_mashed($arg, $arg2); } } function my_handler($obj, $arg, $arg2, $ex) { echo "my_handler called with args {$arg}, ", get_class($arg2), " and extra {$ex}\n"; return true; } GObject::register_type('NewType'); GObject::register_type('NewType2'); $c = new NewType; $d = new NewType2; $r = $c->size_request(); $c->connect('mashed', 'my_handler', 99); $c->emit('clicked'); var_dump($c->emit('mashed', 42, $r)); var_dump($d->emit('mashed', 24, $r));