#include "lib/clutter.h"
typedef struct
{
SV *cb;
const char *signature;
char ret_type;
char mode;
SV *userdata;
DCCallVM *cvm;
} Callback;
static char callback_handler(DCCallback *cb, DCArgs *args, DCValue *result, void *userdata) {
dTHX;
#ifdef USE_ITHREADS
PERL_SET_CONTEXT(my_perl);
#endif
char ret_type;
{
dSP;
int count;
Callback *container = ((Callback *)userdata);
SV *cb_sv = container->cb;
ENTER;
SAVETMPS;
PUSHMARK(SP);
mXPUSHs(sv_setref_pv(newSV(0), "Dyn::Callback", (DCpointer)cb));
mXPUSHs(sv_setref_pv(newSV(0), "Dyn::Callback::Args", (DCpointer)args));
mXPUSHs(sv_setref_pv(newSV(0), "Dyn::Callback::Value", (DCpointer)result));
if (SvOK(container->userdata))
mXPUSHs(SvREFCNT_inc(SvRV(container->userdata)));
else
mXPUSHs(newSV(0));
PUTBACK;
count = call_sv(cb_sv, ret_type == DC_SIGCHAR_VOID ? G_VOID : G_SCALAR);
SPAGAIN;
if (count != 1) croak("Expected a single return value to callback");
ret_type = (char)*POPp;
PUTBACK;
FREETMPS;
LEAVE;
}
return ret_type;
}
// clang-format off
MODULE = Dyn::Callback PACKAGE = Dyn::Callback
BOOT:
// clang-format on
#ifdef USE_ITHREADS
my_perl = (PerlInterpreter *)PERL_GET_CONTEXT;
#endif
// clang-format off
DCCallback *
dcbNewCallback(const char * signature, SV * funcptr, userdata)
PREINIT:
#ifdef USE_ITHREADS
PERL_SET_CONTEXT(my_perl);
#endif
dTHX;
INIT:
Callback * container;
CODE:
// clang-format on
{
container = (Callback *)safemalloc(sizeof(Callback));
if (!container) // OOM
XSRETURN_UNDEF;
container->cvm = dcNewCallVM(1024);
dcMode(container->cvm, DC_CALL_C_DEFAULT); // TODO: Use correct value according to signature
dcReset(container->cvm);
container->signature = signature;
container->cb = SvREFCNT_inc(funcptr);
container->userdata = newRV_inc(items > 2 ? ST(2) : newSV(0));
for (int i = 0; container->signature[i + 1] != '\0'; ++i) {
// warn("here at %s line %d.", __FILE__, __LINE__);
if (container->signature[i] == ')') {
container->ret_type = container->signature[i + 1];
break;
}
}
// warn("signature: %s at %s line %d.", signature, __FILE__, __LINE__);
RETVAL = dcbNewCallback(signature, callback_handler, (DCpointer)container);
}
// clang-format off
OUTPUT:
RETVAL
void
dcbInitCallback(DCCallback * pcb, const char * signature, DCCallbackHandler * funcptr, userdata)
PREINIT:
dTHX;
Callback * container;
#ifdef USE_ITHREADS
PERL_SET_CONTEXT(my_perl);
#endif
CODE:
// clang-format on
{
container = (Callback *)dcbGetUserData(pcb);
container->signature = signature;
container->cb = SvREFCNT_inc((SV *)funcptr);
container->userdata = items > 3 ? newRV_inc(ST(3)) : &PL_sv_undef;
for (int i = 0; container->signature[i + 1] != '\0'; ++i) {
// warn("here at %s line %d.", __FILE__, __LINE__);
if (container->signature[i] == ')') {
container->ret_type = container->signature[i + 1];
break;
}
}
dcbInitCallback(pcb, signature, callback_handler, (void *)container);
}
// clang-format off
void
dcbFreeCallback(DCCallback * pcb)
PREINIT:
dTHX;
#ifdef USE_ITHREADS
PERL_SET_CONTEXT(my_perl);
#endif
CODE:
{ // clang-format on
Callback *container = ((Callback *)dcbGetUserData(pcb));
SvREFCNT_dec(container->cb);
SvREFCNT_dec(container->userdata);
dcFree(container->cvm);
dcbFreeCallback(pcb);
safefree(container);
// TODO: Free SVs
// clang-format off
}
SV *
dcbGetUserData(DCCallback * pcb);
PREINIT:
dTHX;
#ifdef USE_ITHREADS
PERL_SET_CONTEXT(my_perl);
#endif
INIT:
RETVAL = (SV*) &PL_sv_undef;
CODE:
// clang-format on
{
Callback *container = ((Callback *)dcbGetUserData(pcb));
if (SvOK(container->userdata)) RETVAL = SvREFCNT_inc(SvRV(container->userdata));
// clang-format off
}
OUTPUT:
RETVAL
=pod
Less Perl, more C.
=cut
SV *
call(DCCallback * self, ... )
PREINIT:
dTHX;
#ifdef USE_ITHREADS
PERL_SET_CONTEXT(my_perl);
#endif
CODE:
// clang-format on
{
Callback *container = ((Callback *)dcbGetUserData(self));
const char *signature = container->signature;
// warn("Callback sig: %s", signature);
int done = 0;
dcReset(container->cvm); // Get it ready to call again
int tally = 0;
// warn("here at %s line %d.", __FILE__, __LINE__);
for (int i = 1; signature[i] != '\0'; ++i) {
// warn ("i: %d vs items: %d", i, items);
// if (i > items - 1) // TODO: Don't do this is signature is var_list, etc.
// croak("Incorrect number of arguments for callback. Expected %d but were handed %d.",
// i, items - 1);
tally++;
// warn("Checking char: %c", signature[i - 1]);
// warn("DC_SIGCHAR_CHAR == %c", DC_SIGCHAR_INT);
// warn("here at %s line %d.", __FILE__, __LINE__);
switch (signature[i - 1]) {
case DC_SIGCHAR_VOID:
// dcArgVoid(container->cvm, self);
tally--;
break;
case DC_SIGCHAR_BOOL:
dcArgBool(container->cvm, (bool)SvTRUE(ST(i)));
break;
case DC_SIGCHAR_CHAR:
dcArgChar(container->cvm, (char)(SvIOK(ST(i)) ? SvIV(ST(i)) : 0));
break;
case DC_SIGCHAR_UCHAR:
dcArgChar(container->cvm, (unsigned char)(SvUOK(ST(i)) ? SvIV(ST(i)) : 0));
break;
case DC_SIGCHAR_SHORT:
dcArgShort(container->cvm, (short)(SvIOK(ST(i)) ? SvIV(ST(i)) : 0));
break;
case DC_SIGCHAR_USHORT:
dcArgShort(container->cvm, (unsigned short)(SvUOK(ST(i)) ? SvIV(ST(i)) : 0));
break;
case DC_SIGCHAR_INT:
dcArgInt(container->cvm, (int)(SvIOK(ST(i)) ? SvIV(ST(i)) : 0));
break;
case DC_SIGCHAR_UINT:
dcArgInt(container->cvm, (unsigned int)(SvUOK(ST(i)) ? SvIV(ST(i)) : 0));
break;
case DC_SIGCHAR_LONG:
dcArgLong(container->cvm, (long)(SvIOK(ST(i)) ? SvIV(ST(i)) : 0));
break;
case DC_SIGCHAR_ULONG:
dcArgLong(container->cvm, (unsigned long)(SvUOK(ST(i)) ? SvIV(ST(i)) : 0));
break;
case DC_SIGCHAR_LONGLONG:
dcArgLongLong(container->cvm, (long long)(SvIOK(ST(i)) ? SvIV(ST(i)) : 0));
break;
case DC_SIGCHAR_ULONGLONG:
dcArgLongLong(container->cvm, (unsigned long long)(SvUOK(ST(i)) ? SvIV(ST(i)) : 0));
break;
case DC_SIGCHAR_FLOAT:
dcArgFloat(container->cvm, (float)(SvNIOK(ST(i)) ? SvNV(ST(i)) : 0.0f));
break;
case DC_SIGCHAR_DOUBLE:
dcArgDouble(container->cvm, (double)(SvNIOK(ST(i)) ? SvNV(ST(i)) : 0.0f));
break;
case DC_SIGCHAR_POINTER:
warn("Unhandled arg type [%c] at %s line %d.", container->ret_type, __FILE__, __LINE__);
break;
case DC_SIGCHAR_STRING:
dcArgPointer(container->cvm, SvPV_nolen(ST(i)));
break;
case DC_SIGCHAR_AGGREGATE:
warn("Unhandled arg type [%c] at %s line %d.", container->ret_type, __FILE__, __LINE__);
break;
case DC_SIGCHAR_ENDARG:
if (tally > items) // TODO: Don't do this is signature is var_list, etc.
croak("Not enough arguments for callback");
else if (tally < items)
croak("Too many arguments for callback");
done++;
break;
case '(':
break;
// skip it for now
default:
warn("Unhandled arg type [%c] at %s line %d.", container->ret_type, __FILE__, __LINE__);
break;
}
// warn("Done: %s", (done? "Yes": "No"));
if (done) break;
}
// warn("Return type: %c at %s line %d.", container->ret_type, __FILE__, __LINE__);
switch (container->ret_type) {
case DC_SIGCHAR_VOID:
dcCallVoid(container->cvm, self);
XSRETURN_UNDEF;
break;
case DC_SIGCHAR_BOOL:
RETVAL = newSViv(dcCallBool(container->cvm, self));
break; // TODO: Make this a real t/f
case DC_SIGCHAR_CHAR:
RETVAL = newSViv(dcCallChar(container->cvm, self));
break;
case DC_SIGCHAR_UCHAR:
RETVAL = newSVuv(dcCallChar(container->cvm, self));
break;
case DC_SIGCHAR_SHORT:
RETVAL = newSViv(dcCallShort(container->cvm, self));
break;
case DC_SIGCHAR_USHORT:
RETVAL = newSVuv(dcCallShort(container->cvm, self));
break;
case DC_SIGCHAR_INT:
RETVAL = newSViv(dcCallInt(container->cvm, self));
break;
case DC_SIGCHAR_UINT:
RETVAL = newSVuv(dcCallInt(container->cvm, self));
break;
case DC_SIGCHAR_LONG:
RETVAL = newSViv(dcCallLong(container->cvm, self));
break;
case DC_SIGCHAR_ULONG:
RETVAL = newSVuv(dcCallLong(container->cvm, self));
break;
case DC_SIGCHAR_LONGLONG:
RETVAL = newSViv(dcCallLongLong(container->cvm, self));
break;
case DC_SIGCHAR_ULONGLONG:
RETVAL = newSVuv(dcCallLongLong(container->cvm, self));
break;
case DC_SIGCHAR_FLOAT:
RETVAL = newSVnv(dcCallFloat(container->cvm, self));
break;
case DC_SIGCHAR_DOUBLE:
RETVAL = newSVnv(dcCallDouble(container->cvm, self));
break;
case DC_SIGCHAR_POINTER:
warn("Unhandled return type [%c] at %s line %d.", container->ret_type, __FILE__, __LINE__);
XSRETURN_UNDEF;
break;
case DC_SIGCHAR_STRING:
RETVAL = newSVpv((const char *)dcCallPointer(container->cvm, self), 0);
break;
case DC_SIGCHAR_AGGREGATE:
warn("Unhandled return type [%c] at %s line %d.", container->ret_type, __FILE__, __LINE__);
XSRETURN_UNDEF;
break;
default:
warn("Unhandled return type [%c] at %s line %d.", container->ret_type, __FILE__, __LINE__);
XSRETURN_UNDEF;
break;
}
}
// warn("here at %s line %d.", __FILE__, __LINE__);
// clang-format off
OUTPUT:
RETVAL
BOOT:
// clang-format on
{
export_function("Dyn::Callback", "dcbNewCallback", "default");
export_function("Dyn::Callback", "dcbInitCallback", "default");
export_function("Dyn::Callback", "dcbFreeCallback", "default");
export_function("Dyn::Callback", "dcbGetUserData", "default");
}
// clang-format off
INCLUDE: Callback/Args.xsh
INCLUDE: Callback/Value.xsh