TQt bindings for Perl
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
libtqt-perl/PerlTQt/TQt.xs

2198 lines
55 KiB

#include <stdio.h>
#include <ntqglobal.h>
#include <ntqstring.h>
#include <ntqapplication.h>
#include <ntqmetaobject.h>
#include <private/qucomextra_p.h>
#include "smoke.h"
#undef DEBUG
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_POSIX
#define __USE_POSIX
#endif
#ifndef __USE_XOPEN
#define __USE_XOPEN
#endif
#ifdef _BOOL
#define HAS_BOOL
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifndef TQT_VERSION_STR
#define TQT_VERSION_STR "Unknown"
#endif
#undef free
#undef malloc
#include "marshall.h"
#include "perltqt.h"
#include "smokeperl.h"
#ifndef IN_BYTES
#define IN_BYTES IN_BYTE
#endif
#ifndef IN_LOCALE
#define IN_LOCALE (PL_curcop->op_private & HINT_LOCALE)
#endif
extern Smoke *qt_Smoke;
extern void init_qt_Smoke();
int do_debug = qtdb_none;
HV *pointer_map = 0;
SV *sv_qapp = 0;
int object_count = 0;
void *_current_object = 0; // TODO: ask myself if this is stupid
bool temporary_virtual_function_success = false;
static TQAsciiDict<Smoke::Index> *methcache = 0;
static TQAsciiDict<Smoke::Index> *classcache = 0;
SV *sv_this = 0;
Smoke::Index _current_object_class = 0;
Smoke::Index _current_method = 0;
/*
* Type handling by moc is simple.
*
* If the type name matches /^(?:const\s+)?\Q$types\E&?$/, use the
* static_TQUType, where $types is join('|', qw(bool int double char* TQString);
*
* Everything else is passed as a pointer! There are types which aren't
* Smoke::tf_ptr but will have to be passed as a pointer. Make sure to keep
* track of what's what.
*/
/*
* Simply using typeids isn't enough for signals/slots. It will be possible
* to declare signals and slots which use arguments which can't all be
* found in a single smoke object. Instead, we need to store smoke => typeid
* pairs. We also need additional informatation, such as whether we're passing
* a pointer to the union element.
*/
enum MocArgumentType {
xmoc_ptr,
xmoc_bool,
xmoc_int,
xmoc_double,
xmoc_charstar,
xmoc_TQString
};
struct MocArgument {
// smoke object and associated typeid
SmokeType st;
MocArgumentType argType;
};
extern TypeHandler TQt_handlers[];
void install_handlers(TypeHandler *);
void *sv_to_ptr(SV *sv) { // ptr on success, null on fail
smokeperl_object *o = sv_obj_info(sv);
return o ? o->ptr : 0;
}
bool isTQObject(Smoke *smoke, Smoke::Index classId) {
if(!strcmp(smoke->classes[classId].className, "TQObject"))
return true;
for(Smoke::Index *p = smoke->inheritanceList + smoke->classes[classId].parents;
*p;
p++) {
if(isTQObject(smoke, *p))
return true;
}
return false;
}
int isDerivedFrom(Smoke *smoke, Smoke::Index classId, Smoke::Index baseId, int cnt) {
if(classId == baseId)
return cnt;
cnt++;
for(Smoke::Index *p = smoke->inheritanceList + smoke->classes[classId].parents;
*p;
p++) {
if(isDerivedFrom(smoke, *p, baseId, cnt) != -1)
return cnt;
}
return -1;
}
int isDerivedFrom(Smoke *smoke, const char *className, const char *baseClassName, int cnt) {
if(!smoke || !className || !baseClassName)
return -1;
Smoke::Index idClass = smoke->idClass(className);
Smoke::Index idBase = smoke->idClass(baseClassName);
return isDerivedFrom(smoke, idClass, idBase, cnt);
}
SV *getPointerObject(void *ptr) {
HV *hv = pointer_map;
SV *keysv = newSViv((IV)ptr);
STRLEN len;
char *key = SvPV(keysv, len);
SV **svp = hv_fetch(hv, key, len, 0);
if(!svp){
SvREFCNT_dec(keysv);
return 0;
}
if(!SvOK(*svp)){
hv_delete(hv, key, len, G_DISCARD);
SvREFCNT_dec(keysv);
return 0;
}
return *svp;
}
void unmapPointer(smokeperl_object *o, Smoke::Index classId, void *lastptr) {
HV *hv = pointer_map;
void *ptr = o->smoke->cast(o->ptr, o->classId, classId);
if(ptr != lastptr) {
lastptr = ptr;
SV *keysv = newSViv((IV)ptr);
STRLEN len;
char *key = SvPV(keysv, len);
if(hv_exists(hv, key, len))
hv_delete(hv, key, len, G_DISCARD);
SvREFCNT_dec(keysv);
}
for(Smoke::Index *i = o->smoke->inheritanceList + o->smoke->classes[classId].parents;
*i;
i++) {
unmapPointer(o, *i, lastptr);
}
}
// Store pointer in pointer_map hash : "pointer_to_TQt_object" => weak ref to associated Perl object
// Recurse to store it also as casted to its parent classes.
void mapPointer(SV *obj, smokeperl_object *o, HV *hv, Smoke::Index classId, void *lastptr) {
void *ptr = o->smoke->cast(o->ptr, o->classId, classId);
if(ptr != lastptr) {
lastptr = ptr;
SV *keysv = newSViv((IV)ptr);
STRLEN len;
char *key = SvPV(keysv, len);
SV *rv = newSVsv(obj);
sv_rvweaken(rv); // weak reference!
hv_store(hv, key, len, rv, 0);
SvREFCNT_dec(keysv);
}
for(Smoke::Index *i = o->smoke->inheritanceList + o->smoke->classes[classId].parents;
*i;
i++) {
mapPointer(obj, o, hv, *i, lastptr);
}
}
Marshall::HandlerFn getMarshallFn(const SmokeType &type);
class VirtualMethodReturnValue : public Marshall {
Smoke *_smoke;
Smoke::Index _method;
Smoke::Stack _stack;
SmokeType _st;
SV *_retval;
public:
const Smoke::Method &method() { return _smoke->methods[_method]; }
SmokeType type() { return _st; }
Marshall::Action action() { return Marshall::FromSV; }
Smoke::StackItem &item() { return _stack[0]; }
SV *var() { return _retval; }
void unsupported() {
croak("Cannot handle '%s' as return-type of virtual method %s::%s",
type().name(),
_smoke->className(method().classId),
_smoke->methodNames[method().name]);
}
Smoke *smoke() { return _smoke; }
void next() {}
bool cleanup() { return false; }
VirtualMethodReturnValue(Smoke *smoke, Smoke::Index meth, Smoke::Stack stack, SV *retval) :
_smoke(smoke), _method(meth), _stack(stack), _retval(retval) {
_st.set(_smoke, method().ret);
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
}
};
class VirtualMethodCall : public Marshall {
Smoke *_smoke;
Smoke::Index _method;
Smoke::Stack _stack;
GV *_gv;
int _cur;
Smoke::Index *_args;
SV **_sp;
bool _called;
SV *_savethis;
public:
SmokeType type() { return SmokeType(_smoke, _args[_cur]); }
Marshall::Action action() { return Marshall::ToSV; }
Smoke::StackItem &item() { return _stack[_cur + 1]; }
SV *var() { return _sp[_cur]; }
const Smoke::Method &method() { return _smoke->methods[_method]; }
void unsupported() {
croak("Cannot handle '%s' as argument of virtual method %s::%s",
type().name(),
_smoke->className(method().classId),
_smoke->methodNames[method().name]);
}
Smoke *smoke() { return _smoke; }
void callMethod() {
dSP;
if(_called) return;
_called = true;
SP = _sp + method().numArgs - 1;
PUTBACK;
int count = call_sv((SV*)GvCV(_gv), G_SCALAR);
SPAGAIN;
VirtualMethodReturnValue r(_smoke, _method, _stack, POPs);
PUTBACK;
FREETMPS;
LEAVE;
}
void next() {
int oldcur = _cur;
_cur++;
while(!_called && _cur < method().numArgs) {
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
_cur++;
}
callMethod();
_cur = oldcur;
}
bool cleanup() { return false; } // is this right?
VirtualMethodCall(Smoke *smoke, Smoke::Index meth, Smoke::Stack stack, SV *obj, GV *gv) :
_smoke(smoke), _method(meth), _stack(stack), _gv(gv), _cur(-1), _sp(0), _called(false) {
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, method().numArgs);
_savethis = sv_this;
sv_this = newSVsv(obj);
_sp = SP + 1;
for(int i = 0; i < method().numArgs; i++)
_sp[i] = sv_newmortal();
_args = _smoke->argumentList + method().args;
}
~VirtualMethodCall() {
SvREFCNT_dec(sv_this);
sv_this = _savethis;
}
};
class MethodReturnValue : public Marshall {
Smoke *_smoke;
Smoke::Index _method;
SV *_retval;
Smoke::Stack _stack;
public:
MethodReturnValue(Smoke *smoke, Smoke::Index method, Smoke::Stack stack, SV *retval) :
_smoke(smoke), _method(method), _retval(retval), _stack(stack) {
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
}
const Smoke::Method &method() { return _smoke->methods[_method]; }
SmokeType type() { return SmokeType(_smoke, method().ret); }
Marshall::Action action() { return Marshall::ToSV; }
Smoke::StackItem &item() { return _stack[0]; }
SV *var() { return _retval; }
void unsupported() {
croak("Cannot handle '%s' as return-type of %s::%s",
type().name(),
_smoke->className(method().classId),
_smoke->methodNames[method().name]);
}
Smoke *smoke() { return _smoke; }
void next() {}
bool cleanup() { return false; }
};
class MethodCall : public Marshall {
int _cur;
Smoke *_smoke;
Smoke::Stack _stack;
Smoke::Index _method;
Smoke::Index *_args;
SV **_sp;
int _items;
SV *_retval;
bool _called;
public:
MethodCall(Smoke *smoke, Smoke::Index method, SV **sp, int items) :
_smoke(smoke), _method(method), _sp(sp), _items(items), _cur(-1), _called(false) {
_args = _smoke->argumentList + _smoke->methods[_method].args;
_items = _smoke->methods[_method].numArgs;
_stack = new Smoke::StackItem[items + 1];
_retval = newSV(0);
}
~MethodCall() {
delete[] _stack;
SvREFCNT_dec(_retval);
}
SmokeType type() { return SmokeType(_smoke, _args[_cur]); }
Marshall::Action action() { return Marshall::FromSV; }
Smoke::StackItem &item() { return _stack[_cur + 1]; }
SV *var() {
if(_cur < 0) return _retval;
SvGETMAGIC(*(_sp + _cur));
return *(_sp + _cur);
}
inline const Smoke::Method &method() { return _smoke->methods[_method]; }
void unsupported() {
croak("Cannot handle '%s' as argument to %s::%s",
type().name(),
_smoke->className(method().classId),
_smoke->methodNames[method().name]);
}
Smoke *smoke() { return _smoke; }
inline void callMethod() {
if(_called) return;
_called = true;
Smoke::ClassFn fn = _smoke->classes[method().classId].classFn;
void *ptr = _smoke->cast(
_current_object,
_current_object_class,
method().classId
);
_items = -1;
(*fn)(method().method, ptr, _stack);
MethodReturnValue r(_smoke, _method, _stack, _retval);
}
void next() {
int oldcur = _cur;
_cur++;
while(!_called && _cur < _items) {
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
_cur++;
}
callMethod();
_cur = oldcur;
}
bool cleanup() { return true; }
};
class UnencapsulatedTQObject : public TQObject {
public:
TQConnectionList *public_receivers(int signal) const { return receivers(signal); }
void public_activate_signal(TQConnectionList *clist, TQUObject *o) { activate_signal(clist, o); }
};
class EmitSignal : public Marshall {
UnencapsulatedTQObject *_qobj;
int _id;
MocArgument *_args;
SV **_sp;
int _items;
int _cur;
Smoke::Stack _stack;
bool _called;
public:
EmitSignal(TQObject *qobj, int id, int items, MocArgument *args, SV **sp) :
_qobj((UnencapsulatedTQObject*)qobj), _id(id), _items(items), _args(args),
_sp(sp), _cur(-1), _called(false) {
_stack = new Smoke::StackItem[_items];
}
~EmitSignal() {
delete[] _stack;
}
const MocArgument &arg() { return _args[_cur]; }
SmokeType type() { return arg().st; }
Marshall::Action action() { return Marshall::FromSV; }
Smoke::StackItem &item() { return _stack[_cur]; }
SV *var() { return _sp[_cur]; }
void unsupported() {
croak("Cannot handle '%s' as signal argument", type().name());
}
Smoke *smoke() { return type().smoke(); }
void emitSignal() {
if(_called) return;
_called = true;
TQConnectionList *clist = _qobj->public_receivers(_id);
if(!clist) return;
TQUObject *o = new TQUObject[_items + 1];
for(int i = 0; i < _items; i++) {
TQUObject *po = o + i + 1;
Smoke::StackItem *si = _stack + i;
switch(_args[i].argType) {
case xmoc_bool:
static_TQUType_bool.set(po, si->s_bool);
break;
case xmoc_int:
static_TQUType_int.set(po, si->s_int);
break;
case xmoc_double:
static_QUType_double.set(po, si->s_double);
break;
case xmoc_charstar:
static_TQUType_charstar.set(po, (char*)si->s_voidp);
break;
case xmoc_TQString:
static_TQUType_TQString.set(po, *(TQString*)si->s_voidp);
break;
default:
{
const SmokeType &t = _args[i].st;
void *p;
switch(t.elem()) {
case Smoke::t_bool:
p = &si->s_bool;
break;
case Smoke::t_char:
p = &si->s_char;
break;
case Smoke::t_uchar:
p = &si->s_uchar;
break;
case Smoke::t_short:
p = &si->s_short;
break;
case Smoke::t_ushort:
p = &si->s_ushort;
break;
case Smoke::t_int:
p = &si->s_int;
break;
case Smoke::t_uint:
p = &si->s_uint;
break;
case Smoke::t_long:
p = &si->s_long;
break;
case Smoke::t_ulong:
p = &si->s_ulong;
break;
case Smoke::t_float:
p = &si->s_float;
break;
case Smoke::t_double:
p = &si->s_double;
break;
case Smoke::t_enum:
{
// allocate a new enum value
Smoke::EnumFn fn = SmokeClass(t).enumFn();
if(!fn) {
warn("Unknown enumeration %s\n", t.name());
p = new int((int)si->s_enum);
break;
}
Smoke::Index id = t.typeId();
(*fn)(Smoke::EnumNew, id, p, si->s_enum);
(*fn)(Smoke::EnumFromLong, id, p, si->s_enum);
// FIXME: MEMORY LEAK
}
break;
case Smoke::t_class:
case Smoke::t_voidp:
p = si->s_voidp;
break;
default:
p = 0;
break;
}
static_TQUType_ptr.set(po, p);
}
}
}
_qobj->public_activate_signal(clist, o);
delete[] o;
}
void next() {
int oldcur = _cur;
_cur++;
while(!_called && _cur < _items) {
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
_cur++;
}
emitSignal();
_cur = oldcur;
}
bool cleanup() { return true; }
};
class InvokeSlot : public Marshall {
TQObject *_qobj;
GV *_gv;
int _items;
MocArgument *_args;
TQUObject *_o;
int _cur;
bool _called;
SV **_sp;
Smoke::Stack _stack;
public:
const MocArgument &arg() { return _args[_cur]; }
SmokeType type() { return arg().st; }
Marshall::Action action() { return Marshall::ToSV; }
Smoke::StackItem &item() { return _stack[_cur]; }
SV *var() { return _sp[_cur]; }
Smoke *smoke() { return type().smoke(); }
bool cleanup() { return false; }
void unsupported() {
croak("Cannot handle '%s' as slot argument\n", type().name());
}
void copyArguments() {
for(int i = 0; i < _items; i++) {
TQUObject *o = _o + i + 1;
switch(_args[i].argType) {
case xmoc_bool:
_stack[i].s_bool = static_TQUType_bool.get(o);
break;
case xmoc_int:
_stack[i].s_int = static_TQUType_int.get(o);
break;
case xmoc_double:
_stack[i].s_double = static_QUType_double.get(o);
break;
case xmoc_charstar:
_stack[i].s_voidp = static_TQUType_charstar.get(o);
break;
case xmoc_TQString:
_stack[i].s_voidp = &static_TQUType_TQString.get(o);
break;
default: // case xmoc_ptr:
{
const SmokeType &t = _args[i].st;
void *p = static_TQUType_ptr.get(o);
switch(t.elem()) {
case Smoke::t_bool:
_stack[i].s_bool = *(bool*)p;
break;
case Smoke::t_char:
_stack[i].s_char = *(char*)p;
break;
case Smoke::t_uchar:
_stack[i].s_uchar = *(unsigned char*)p;
break;
case Smoke::t_short:
_stack[i].s_short = *(short*)p;
break;
case Smoke::t_ushort:
_stack[i].s_ushort = *(unsigned short*)p;
break;
case Smoke::t_int:
_stack[i].s_int = *(int*)p;
break;
case Smoke::t_uint:
_stack[i].s_uint = *(unsigned int*)p;
break;
case Smoke::t_long:
_stack[i].s_long = *(long*)p;
break;
case Smoke::t_ulong:
_stack[i].s_ulong = *(unsigned long*)p;
break;
case Smoke::t_float:
_stack[i].s_float = *(float*)p;
break;
case Smoke::t_double:
_stack[i].s_double = *(double*)p;
break;
case Smoke::t_enum:
{
Smoke::EnumFn fn = SmokeClass(t).enumFn();
if(!fn) {
warn("Unknown enumeration %s\n", t.name());
_stack[i].s_enum = *(int*)p;
break;
}
Smoke::Index id = t.typeId();
(*fn)(Smoke::EnumToLong, id, p, _stack[i].s_enum);
}
break;
case Smoke::t_class:
case Smoke::t_voidp:
_stack[i].s_voidp = p;
break;
}
}
}
}
}
void invokeSlot() {
dSP;
if(_called) return;
_called = true;
SP = _sp + _items - 1;
PUTBACK;
int count = call_sv((SV*)GvCV(_gv), G_SCALAR);
SPAGAIN;
SP -= count;
PUTBACK;
FREETMPS;
LEAVE;
}
void next() {
int oldcur = _cur;
_cur++;
while(!_called && _cur < _items) {
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
_cur++;
}
invokeSlot();
_cur = oldcur;
}
InvokeSlot(TQObject *qobj, GV *gv, int items, MocArgument *args, TQUObject *o) :
_qobj(qobj), _gv(gv), _items(items), _args(args), _o(o), _cur(-1), _called(false) {
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, items);
PUTBACK;
_sp = SP + 1;
for(int i = 0; i < _items; i++)
_sp[i] = sv_newmortal();
_stack = new Smoke::StackItem[_items];
copyArguments();
}
~InvokeSlot() {
delete[] _stack;
}
};
class TQtSmokeBinding : public SmokeBinding {
public:
TQtSmokeBinding(Smoke *s) : SmokeBinding(s) {}
void deleted(Smoke::Index classId, void *ptr) {
SV *obj = getPointerObject(ptr);
smokeperl_object *o = sv_obj_info(obj);
if(do_debug && (do_debug & qtdb_gc)) {
fprintf(stderr, "%p->~%s()\n", ptr, smoke->className(classId));
}
if(!o || !o->ptr) {
return;
}
unmapPointer(o, o->classId, 0);
o->ptr = 0;
}
bool callMethod(Smoke::Index method, void *ptr, Smoke::Stack args, bool isAbstract) {
SV *obj = getPointerObject(ptr);
smokeperl_object *o = sv_obj_info(obj);
if(do_debug && (do_debug & qtdb_virtual)) fprintf(stderr, "virtual %p->%s::%s() called\n", ptr,
smoke->classes[smoke->methods[method].classId].className,
smoke->methodNames[smoke->methods[method].name]
);
if(!o) {
if(!PL_dirty && (do_debug && (do_debug & qtdb_virtual)) ) // if not in global destruction
fprintf(stderr, "Cannot find object for virtual method\n");
return false;
}
HV *stash = SvSTASH(SvRV(obj));
if(*HvNAME(stash) == ' ')
stash = gv_stashpv(HvNAME(stash) + 1, TRUE);
const char *methodName = smoke->methodNames[smoke->methods[method].name];
GV *gv = gv_fetchmethod_autoload(stash, methodName, 0);
if(!gv) return false;
VirtualMethodCall c(smoke, method, args, obj, gv);
// exception variable, just temporary
temporary_virtual_function_success = true;
c.next();
bool ret = temporary_virtual_function_success;
temporary_virtual_function_success = true;
return ret;
}
char *className(Smoke::Index classId) {
const char *className = smoke->className(classId);
char *buf = new char[strlen(className) + 6];
strcpy(buf, " TQt::");
strcat(buf, className + 1);
return buf;
}
};
// ---------------- Helpers -------------------
SV *catArguments(SV** sp, int n)
{
SV* r=newSVpvf("");
for(int i = 0; i < n; i++) {
if(i) sv_catpv(r, ", ");
if(!SvOK(sp[i])) {
sv_catpv(r, "undef");
} else if(SvROK(sp[i])) {
smokeperl_object *o = sv_obj_info(sp[i]);
if(o)
sv_catpv(r, o->smoke->className(o->classId));
else
sv_catsv(r, sp[i]);
} else {
bool isString = SvPOK(sp[i]);
STRLEN len;
char *s = SvPV(sp[i], len);
if(isString) sv_catpv(r, "'");
sv_catpvn(r, s, len > 10 ? 10 : len);
if(len > 10) sv_catpv(r, "...");
if(isString) sv_catpv(r, "'");
}
}
return r;
}
Smoke::Index package_classid(const char *p)
{
Smoke::Index *item = classcache->find(p);
if(item)
return *item;
char *nisa = new char[strlen(p)+6];
strcpy(nisa, p);
strcat(nisa, "::ISA");
AV* isa=get_av(nisa, true);
delete[] nisa;
for(int i=0; i<=av_len(isa); i++) {
SV** np = av_fetch(isa, i, 0);
if(np) {
Smoke::Index ix = package_classid(SvPV_nolen(*np));
if(ix) {
classcache->insert(p, new Smoke::Index(ix));
return ix;
}
}
}
return (Smoke::Index) 0;
}
char *get_SVt(SV *sv)
{
char *r;
if(!SvOK(sv))
r = "u";
else if(SvIOK(sv))
r = "i";
else if(SvNOK(sv))
r = "n";
else if(SvPOK(sv))
r = "s";
else if(SvROK(sv)) {
smokeperl_object *o = sv_obj_info(sv);
if(!o) {
switch (SvTYPE(SvRV(sv))) {
case SVt_PVAV:
r = "a";
break;
// case SVt_PV:
// case SVt_PVMG:
// r = "p";
default:
r = "r";
}
}
else
r = (char*)o->smoke->className(o->classId);
}
else
r = "U";
return r;
}
SV *prettyPrintMethod(Smoke::Index id) {
SV *r = newSVpvf("");
Smoke::Method &meth = qt_Smoke->methods[id];
const char *tname = qt_Smoke->types[meth.ret].name;
if(meth.flags & Smoke::mf_static) sv_catpv(r, "static ");
sv_catpvf(r, "%s ", (tname ? tname:"void"));
sv_catpvf(r, "%s::%s(", qt_Smoke->classes[meth.classId].className, qt_Smoke->methodNames[meth.name]);
for(int i = 0; i < meth.numArgs; i++) {
if(i) sv_catpv(r, ", ");
tname = qt_Smoke->types[qt_Smoke->argumentList[meth.args+i]].name;
sv_catpv(r, (tname ? tname:"void"));
}
sv_catpv(r, ")");
if(meth.flags & Smoke::mf_const) sv_catpv(r, " const");
return r;
}
// --------------- Unary Keywords && Attributes ------------------
// implements unary 'this'
XS(XS_this) {
dXSARGS;
ST(0) = sv_this;
XSRETURN(1);
}
// implements unary attributes: 'foo' means 'this->{foo}'
XS(XS_attr) {
dXSARGS;
char *key = GvNAME(CvGV(cv));
U32 klen = strlen(key);
SV **svp = 0;
if(SvROK(sv_this) && SvTYPE(SvRV(sv_this)) == SVt_PVHV) {
HV *hv = (HV*)SvRV(sv_this);
svp = hv_fetch(hv, key, klen, 1);
}
if(svp) {
ST(0) = *svp;
XSRETURN(1);
}
XSRETURN_UNDEF;
}
// implements unary SUPER attribute: 'SUPER' means ${(CopSTASH)::_INTERNAL_STATIC_}{SUPER}
XS(XS_super) {
dXSARGS;
char *key = "SUPER";
U32 klen = strlen(key);
SV **svp = 0;
if(SvROK(sv_this) && SvTYPE(SvRV(sv_this)) == SVt_PVHV) {
HV *cs = (HV*)CopSTASH(PL_curcop);
if(!cs) XSRETURN_UNDEF;
svp = hv_fetch(cs, "_INTERNAL_STATIC_", 17, 0);
if(!svp) XSRETURN_UNDEF;
cs = GvHV((GV*)*svp);
if(!cs) XSRETURN_UNDEF;
svp = hv_fetch(cs, "SUPER", 5, 0);
}
if(svp) {
ST(0) = *svp;
XSRETURN(1);
}
XSRETURN_UNDEF;
}
//---------- XS Autoload (for all functions except fully qualified statics & enums) ---------
static inline bool isTQt(char *p) {
return (p[0] == 'Q' && p[1] && p[1] == 't' && ((p[2] && p[2] == ':') || !p[2]));
}
bool avoid_fetchmethod = false;
XS(XS_AUTOLOAD) {
// Err, XS autoload is borked. Lets try...
dXSARGS;
SV *sv = get_sv("TQt::AutoLoad::AUTOLOAD", TRUE);
char *package = SvPV_nolen(sv);
char *method = 0;
for(char *s = package; *s ; s++)
if(*s == ':') method = s;
if(!method) XSRETURN_NO;
*(method++ - 1) = 0; // sorry for showing off. :)
int withObject = (*package == ' ') ? 1 : 0;
int isSuper = 0;
if(withObject) {
package++;
if(*package == ' ') {
isSuper = 1;
char *super = new char[strlen(package) + 7];
package++;
strcpy(super, package);
strcat(super, "::SUPER");
package = super;
}
} else if( isTQt(package) )
avoid_fetchmethod = true;
HV *stash = gv_stashpv(package, TRUE);
if(do_debug && (do_debug & qtdb_autoload))
warn("In XS Autoload for %s::%s()\n", package, method);
// check for user-defined methods in the REAL stash; skip prefix
GV *gv = 0;
if(avoid_fetchmethod)
avoid_fetchmethod = false;
else
gv = gv_fetchmethod_autoload(stash, method, 0);
// If we've made it here, we need to set sv_this
if(gv) {
if(do_debug && (do_debug & qtdb_autoload))
warn("\tfound in %s's Perl stash\n", package);
// call the defined Perl method with new 'this'
SV *old_this;
if(withObject && !isSuper) {
old_this = sv_this;
sv_this = newSVsv(ST(0));
}
ENTER;
SAVETMPS;
PUSHMARK(SP - items + withObject);
PUTBACK;
int count = call_sv((SV*)GvCV(gv), G_SCALAR|G_EVAL);
SPAGAIN;
SV *ret = newSVsv(TOPs);
SP -= count;
PUTBACK;
FREETMPS;
LEAVE;
if(withObject && !isSuper) {
SvREFCNT_dec(sv_this);
sv_this = old_this;
}
else if(isSuper)
delete[] package;
if(SvTRUE(ERRSV))
croak("%s", SvPV_nolen(ERRSV));
ST(0) = sv_2mortal(ret);
XSRETURN(1);
}
else if(!strcmp(method, "DESTROY")) {
SV *old_this;
if(withObject && !isSuper) {
old_this = sv_this;
sv_this = newSVsv(ST(0));
}
smokeperl_object *o = sv_obj_info(sv_this);
if(!(o && o->ptr && (o->allocated || getPointerObject(o->ptr)))) {
if(isSuper)
delete[] package;
if(withObject && !isSuper) {
SvREFCNT_dec(sv_this);
sv_this = old_this;
}
XSRETURN_YES;
}
const char *key = "has been hidden";
U32 klen = 15;
SV **svp = 0;
if(SvROK(sv_this) && SvTYPE(SvRV(sv_this)) == SVt_PVHV) {
HV *hv = (HV*)SvRV(sv_this);
svp = hv_fetch(hv, key, klen, 0);
}
if(svp) {
if(isSuper)
delete[] package;
if(withObject && !isSuper) {
SvREFCNT_dec(sv_this);
sv_this = old_this;
}
XSRETURN_YES;
}
gv = gv_fetchmethod_autoload(stash, "ON_DESTROY", 0);
if( !gv )
croak( "Couldn't find ON_DESTROY method for %s=%p\n", package, o->ptr);
PUSHMARK(SP);
call_sv((SV*)GvCV(gv), G_SCALAR|G_NOARGS);
SPAGAIN;
int ret = POPi;
PUTBACK;
if(withObject && !isSuper) {
SvREFCNT_dec(sv_this);
sv_this = old_this;
}
if( do_debug && ret && (do_debug & qtdb_gc) )
fprintf(stderr, "Increasing refcount in DESTROY for %s=%p (still has a parent)\n", package, o->ptr);
} else {
if( items > 18 ) XSRETURN_NO; // current max number of args in TQt is 13.
// save the stack -- we'll need it
SV **savestack = new SV*[items+1];
SV *saveobj = ST(0);
SV *old_this;
Copy(SP - items + 1 + withObject, savestack, items-withObject, SV*);
// Get the classid (eventually converting SUPER to the right TQt class)
Smoke::Index cid = package_classid(package);
// Look in the cache
char *cname = (char*)qt_Smoke->className(cid);
int lcname = strlen(cname);
int lmethod = strlen(method);
char mcid[256];
strncpy(mcid, cname, lcname);
char *ptr = mcid + lcname;
*(ptr++) = ';';
strncpy(ptr, method, lmethod);
ptr += lmethod;
for(int i=withObject ; i<items ; i++)
{
*(ptr++) = ';';
char *t = get_SVt(ST(i));
int tlen = strlen(t);
strncpy(ptr, t, tlen );
ptr += tlen;
}
*ptr = 0;
Smoke::Index *rcid = methcache->find(mcid);
if(rcid) {
// Got a hit
_current_method = *rcid;
if(withObject && !isSuper) {
old_this = sv_this;
sv_this = newSVsv(ST(0));
}
}
else {
// Find the C++ method to call. I'll do that from Perl for now
ENTER;
SAVETMPS;
PUSHMARK(SP - items + withObject);
EXTEND(SP, 3);
PUSHs(sv_2mortal(newSViv((IV)cid)));
PUSHs(sv_2mortal(newSVpv(method, 0)));
PUSHs(sv_2mortal(newSVpv(package, 0)));
PUTBACK;
if(withObject && !isSuper) {
old_this = sv_this;
sv_this = newSVsv(saveobj);
}
call_pv("TQt::_internal::do_autoload", G_DISCARD|G_EVAL);
FREETMPS;
LEAVE;
// Restore sv_this on error, so that eval{ } works
if(SvTRUE(ERRSV)) {
if(withObject && !isSuper) {
SvREFCNT_dec(sv_this);
sv_this = old_this;
}
else if(isSuper)
delete[] package;
delete[] savestack;
croak("%s", SvPV_nolen(ERRSV));
}
// Success. Cache result.
methcache->insert(mcid, new Smoke::Index(_current_method));
}
// FIXME: I shouldn't have to set the current object
{
smokeperl_object *o = sv_obj_info(sv_this);
if(o && o->ptr) {
_current_object = o->ptr;
_current_object_class = o->classId;
} else {
_current_object = 0;
}
}
// honor debugging channels
if(do_debug && (do_debug & qtdb_calls)) {
warn("Calling method\t%s\n", SvPV_nolen(sv_2mortal(prettyPrintMethod(_current_method))));
if(do_debug & qtdb_verbose)
warn("with arguments (%s)\n", SvPV_nolen(sv_2mortal(catArguments(savestack, items-withObject))));
}
MethodCall c(qt_Smoke, _current_method, savestack, items-withObject);
c.next();
if(savestack)
delete[] savestack;
if(withObject && !isSuper) {
SvREFCNT_dec(sv_this);
sv_this = old_this;
}
else if(isSuper)
delete[] package;
SV *ret = c.var();
SvREFCNT_inc(ret);
ST(0) = sv_2mortal(ret);
XSRETURN(1);
}
if(isSuper)
delete[] package;
XSRETURN_YES;
}
//----------------- Sig/Slot ------------------
MocArgument *getmetainfo(GV *gv, const char *name, int &offset, int &index, int &argcnt) {
char *signalname = GvNAME(gv);
HV *stash = GvSTASH(gv);
// $meta = $stash->{META}
SV **svp = hv_fetch(stash, "META", 4, 0);
if(!svp) return 0;
HV *hv = GvHV((GV*)*svp);
if(!hv) return 0;
// $metaobject = $meta->{object}
// aka. Class->staticMetaObject
svp = hv_fetch(hv, "object", 6, 0);
if(!svp) return 0;
smokeperl_object *ometa = sv_obj_info(*svp);
if(!ometa) return 0;
TQMetaObject *metaobject = (TQMetaObject*)ometa->ptr;
offset = metaobject->signalOffset();
// $signals = $meta->{signal}
U32 len = strlen(name);
svp = hv_fetch(hv, name, len, 0);
if(!svp) return 0;
HV *signalshv = (HV*)SvRV(*svp);
// $signal = $signals->{$signalname}
len = strlen(signalname);
svp = hv_fetch(signalshv, signalname, len, 0);
if(!svp) return 0;
HV *signalhv = (HV*)SvRV(*svp);
// $index = $signal->{index}
svp = hv_fetch(signalhv, "index", 5, 0);
if(!svp) return 0;;
index = SvIV(*svp);
// $argcnt = $signal->{argcnt}
svp = hv_fetch(signalhv, "argcnt", 6, 0);
if(!svp) return 0;
argcnt = SvIV(*svp);
// $mocargs = $signal->{mocargs}
svp = hv_fetch(signalhv, "mocargs", 7, 0);
if(!svp) return 0;
MocArgument *args = (MocArgument*)SvIV(*svp);
return args;
}
MocArgument *getslotinfo(GV *gv, int id, char *&slotname, int &index, int &argcnt, bool isSignal = false) {
HV *stash = GvSTASH(gv);
// $meta = $stash->{META}
SV **svp = hv_fetch(stash, "META", 4, 0);
if(!svp) return 0;
HV *hv = GvHV((GV*)*svp);
if(!hv) return 0;
// $metaobject = $meta->{object}
// aka. Class->staticMetaObject
svp = hv_fetch(hv, "object", 6, 0);
if(!svp) return 0;
smokeperl_object *ometa = sv_obj_info(*svp);
if(!ometa) return 0;
TQMetaObject *metaobject = (TQMetaObject*)ometa->ptr;
int offset = isSignal ? metaobject->signalOffset() : metaobject->slotOffset();
index = id - offset; // where we at
// FIXME: make slot inheritance work
if(index < 0) return 0;
// $signals = $meta->{signal}
const char *key = isSignal ? "signals" : "slots";
svp = hv_fetch(hv, key, strlen(key), 0);
if(!svp) return 0;
AV *signalsav = (AV*)SvRV(*svp);
svp = av_fetch(signalsav, index, 0);
if(!svp) return 0;
HV *signalhv = (HV*)SvRV(*svp);
// $argcnt = $signal->{argcnt}
svp = hv_fetch(signalhv, "argcnt", 6, 0);
if(!svp) return 0;
argcnt = SvIV(*svp);
// $mocargs = $signal->{mocargs}
svp = hv_fetch(signalhv, "mocargs", 7, 0);
if(!svp) return 0;
MocArgument *args = (MocArgument*)SvIV(*svp);
svp = hv_fetch(signalhv, "name", 4, 0);
if(!svp) return 0;
slotname = SvPV_nolen(*svp);
return args;
}
XS(XS_signal) {
dXSARGS;
smokeperl_object *o = sv_obj_info(sv_this);
TQObject *qobj = (TQObject*)o->smoke->cast(
o->ptr,
o->classId,
o->smoke->idClass("TQObject")
);
if(qobj->signalsBlocked()) XSRETURN_UNDEF;
int offset;
int index;
int argcnt;
MocArgument *args;
args = getmetainfo(CvGV(cv), "signal", offset, index, argcnt);
if(!args) XSRETURN_UNDEF;
// Okay, we have the signal info. *whew*
if(items < argcnt)
croak("Insufficient arguments to emit signal");
EmitSignal signal(qobj, offset + index, argcnt, args, &ST(0));
signal.next();
XSRETURN_UNDEF;
}
XS(XS_qt_invoke) {
dXSARGS;
// Arguments: int id, TQUObject *o
int id = SvIV(ST(0));
TQUObject *_o = (TQUObject*)SvIV(SvRV(ST(1)));
smokeperl_object *o = sv_obj_info(sv_this);
TQObject *qobj = (TQObject*)o->smoke->cast(
o->ptr,
o->classId,
o->smoke->idClass("TQObject")
);
// Now, I need to find out if this means me
int index;
char *slotname;
int argcnt;
MocArgument *args;
bool isSignal = !strcmp(GvNAME(CvGV(cv)), "qt_emit");
args = getslotinfo(CvGV(cv), id, slotname, index, argcnt, isSignal);
if(!args) {
// throw an exception - evil style
temporary_virtual_function_success = false;
XSRETURN_UNDEF;
}
HV *stash = GvSTASH(CvGV(cv));
GV *gv = gv_fetchmethod_autoload(stash, slotname, 0);
if(!gv) XSRETURN_UNDEF;
InvokeSlot slot(qobj, gv, argcnt, args, _o);
slot.next();
XSRETURN_UNDEF;
}
// ------------------- Tied types ------------------------
MODULE = TQt PACKAGE = TQt::_internal::TQString
PROTOTYPES: DISABLE
SV*
FETCH(obj)
SV* obj
CODE:
if (!SvROK(obj))
croak("?");
IV tmp = SvIV((SV*)SvRV(obj));
TQString *s = (TQString*) tmp;
RETVAL = newSV(0);
if( s )
{
if(!(IN_BYTES))
{
sv_setpv_mg(RETVAL, (const char *)s->utf8());
SvUTF8_on(RETVAL);
}
else if(IN_LOCALE)
sv_setpv_mg(RETVAL, (const char *)s->local8Bit());
else
sv_setpv_mg(RETVAL, (const char *)s->latin1());
}
else
sv_setsv_mg(RETVAL, &PL_sv_undef);
OUTPUT:
RETVAL
void
STORE(obj,what)
SV* obj
SV* what
CODE:
if (!SvROK(obj))
croak("?");
IV tmp = SvIV((SV*)SvRV(obj));
TQString *s = (TQString*) tmp;
s->truncate(0);
if(SvOK(what)) {
if(SvUTF8(what))
s->append(TQString::fromUtf8(SvPV_nolen(what)));
else if(IN_LOCALE)
s->append(TQString::fromLocal8Bit(SvPV_nolen(what)));
else
s->append(TQString::fromLatin1(SvPV_nolen(what)));
}
void
DESTROY(obj)
SV* obj
CODE:
if (!SvROK(obj))
croak("?");
IV tmp = SvIV((SV*)SvRV(obj));
TQString *s = (TQString*) tmp;
delete s;
MODULE = TQt PACKAGE = TQt::_internal::TQByteArray
PROTOTYPES: DISABLE
SV*
FETCH(obj)
SV* obj
CODE:
if (!SvROK(obj))
croak("?");
IV tmp = SvIV((SV*)SvRV(obj));
TQByteArray *s = (TQByteArray*) tmp;
RETVAL = newSV(0);
if( s )
{
sv_setpvn_mg(RETVAL, s->data(), s->size());
}
else
sv_setsv_mg(RETVAL, &PL_sv_undef);
OUTPUT:
RETVAL
void
STORE(obj,what)
SV* obj
SV* what
CODE:
if (!SvROK(obj))
croak("?");
IV tmp = SvIV((SV*)SvRV(obj));
TQByteArray *s = (TQByteArray*) tmp;
if(SvOK(what)) {
STRLEN len;
char* tmp2 = SvPV(what, len);
s->resize(len);
Copy((void*)tmp2, (void*)s->data(), len, char);
} else
s->truncate(0);
void
DESTROY(obj)
SV* obj
CODE:
if (!SvROK(obj))
croak("?");
IV tmp = SvIV((SV*)SvRV(obj));
TQByteArray *s = (TQByteArray*) tmp;
delete s;
MODULE = TQt PACKAGE = TQt::_internal::TQRgbStar
PROTOTYPES: DISABLE
SV*
FETCH(obj)
SV* obj
CODE:
if (!SvROK(obj))
croak("?");
IV tmp = SvIV((SV*)SvRV(obj));
TQRgb *s = (TQRgb*) tmp;
AV* ar = newAV();
RETVAL = newRV_noinc((SV*)ar);
for(int i=0; s[i] ; i++)
{
SV *item = newSViv((IV)s[i]);
if(!av_store(ar, (I32)i, item))
SvREFCNT_dec( item );
}
OUTPUT:
RETVAL
void
STORE(obj,sv)
SV* obj
SV* sv
CODE:
if (!SvROK(obj))
croak("?");
IV tmp = SvIV((SV*)SvRV(obj));
TQRgb *s = (TQRgb*) tmp;
if(!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV ||
av_len((AV*)SvRV(sv)) < 0) {
s = new TQRgb[1];
s[0] = 0;
sv_setref_pv(obj, "TQt::_internal::TQRgbStar", (void*)s);
return;
}
AV *list = (AV*)SvRV(sv);
int count = av_len(list);
s = new TQRgb[count + 2];
int i;
for(i = 0; i <= count; i++) {
SV **item = av_fetch(list, i, 0);
if(!item || !SvOK(*item)) {
s[i] = 0;
continue;
}
s[i] = SvIV(*item);
}
s[i] = 0;
sv_setref_pv(obj, "TQt::_internal::TQRgbStar", (void*)s);
void
DESTROY(obj)
SV* obj
CODE:
if (!SvROK(obj))
croak("?");
IV tmp = SvIV((SV*)SvRV(obj));
TQRgb *s = (TQRgb*) tmp;
delete[] s;
# --------------- XSUBS for TQt::_internal::* helpers ----------------
MODULE = TQt PACKAGE = TQt::_internal
PROTOTYPES: DISABLE
void
getMethStat()
PPCODE:
XPUSHs(sv_2mortal(newSViv((int)methcache->size())));
XPUSHs(sv_2mortal(newSViv((int)methcache->count())));
void
getClassStat()
PPCODE:
XPUSHs(sv_2mortal(newSViv((int)classcache->size())));
XPUSHs(sv_2mortal(newSViv((int)classcache->count())));
void
getIsa(classId)
int classId
PPCODE:
Smoke::Index *parents =
qt_Smoke->inheritanceList +
qt_Smoke->classes[classId].parents;
while(*parents)
XPUSHs(sv_2mortal(newSVpv(qt_Smoke->classes[*parents++].className, 0)));
void
dontRecurse()
CODE:
avoid_fetchmethod = true;
void *
sv_to_ptr(sv)
SV* sv
void *
allocateMocArguments(count)
int count
CODE:
RETVAL = (void*)new MocArgument[count + 1];
OUTPUT:
RETVAL
void
setMocType(ptr, idx, name, static_type)
void *ptr
int idx
char *name
char *static_type
CODE:
Smoke::Index typeId = qt_Smoke->idType(name);
if(!typeId) XSRETURN_NO;
MocArgument *arg = (MocArgument*)ptr;
arg[idx].st.set(qt_Smoke, typeId);
if(!strcmp(static_type, "ptr"))
arg[idx].argType = xmoc_ptr;
else if(!strcmp(static_type, "bool"))
arg[idx].argType = xmoc_bool;
else if(!strcmp(static_type, "int"))
arg[idx].argType = xmoc_int;
else if(!strcmp(static_type, "double"))
arg[idx].argType = xmoc_double;
else if(!strcmp(static_type, "char*"))
arg[idx].argType = xmoc_charstar;
else if(!strcmp(static_type, "TQString"))
arg[idx].argType = xmoc_TQString;
XSRETURN_YES;
void
installsignal(name)
char *name
CODE:
char *file = __FILE__;
newXS(name, XS_signal, file);
void
installqt_invoke(name)
char *name
CODE:
char *file = __FILE__;
newXS(name, XS_qt_invoke, file);
void
setDebug(on)
int on
CODE:
do_debug = on;
int
debug()
CODE:
RETVAL = do_debug;
OUTPUT:
RETVAL
char *
getTypeNameOfArg(method, idx)
int method
int idx
CODE:
Smoke::Method &m = qt_Smoke->methods[method];
Smoke::Index *args = qt_Smoke->argumentList + m.args;
RETVAL = (char*)qt_Smoke->types[args[idx]].name;
OUTPUT:
RETVAL
int
classIsa(className, base)
char *className
char *base
CODE:
RETVAL = isDerivedFrom(qt_Smoke, className, base, 0);
OUTPUT:
RETVAL
void
insert_pclassid(p, ix)
char *p
int ix
CODE:
classcache->insert(p, new Smoke::Index((Smoke::Index)ix));
int
find_pclassid(p)
char *p
CODE:
Smoke::Index *r = classcache->find(p);
if(r)
RETVAL = (int)*r;
else
RETVAL = 0;
OUTPUT:
RETVAL
void
insert_mcid(mcid, ix)
char *mcid
int ix
CODE:
methcache->insert(mcid, new Smoke::Index((Smoke::Index)ix));
int
find_mcid(mcid)
char *mcid
CODE:
Smoke::Index *r = methcache->find(mcid);
if(r)
RETVAL = (int)*r;
else
RETVAL = 0;
OUTPUT:
RETVAL
char *
getSVt(sv)
SV *sv
CODE:
RETVAL=get_SVt(sv);
OUTPUT:
RETVAL
void *
make_TQUParameter(name, type, extra, inout)
char *name
char *type
SV *extra
int inout
CODE:
TQUParameter *p = new TQUParameter;
p->name = new char[strlen(name) + 1];
strcpy((char*)p->name, name);
if(!strcmp(type, "bool"))
p->type = &static_TQUType_bool;
else if(!strcmp(type, "int"))
p->type = &static_TQUType_int;
else if(!strcmp(type, "double"))
p->type = &static_QUType_double;
else if(!strcmp(type, "char*") || !strcmp(type, "const char*"))
p->type = &static_TQUType_charstar;
else if(!strcmp(type, "TQString") || !strcmp(type, "TQString&") ||
!strcmp(type, "const TQString") || !strcmp(type, "const TQString&"))
p->type = &static_TQUType_TQString;
else
p->type = &static_TQUType_ptr;
// Lacking support for several types. Evil.
p->inOut = inout;
p->typeExtra = 0;
RETVAL = (void*)p;
OUTPUT:
RETVAL
void *
make_TQMetaData(name, method)
char *name
void *method
CODE:
TQMetaData *m = new TQMetaData; // will be deleted
m->name = new char[strlen(name) + 1];
strcpy((char*)m->name, name);
m->method = (TQUMethod*)method;
m->access = TQMetaData::Public;
RETVAL = m;
OUTPUT:
RETVAL
void *
make_TQUMethod(name, params)
char *name
SV *params
CODE:
TQUMethod *m = new TQUMethod; // permanent memory allocation
m->name = new char[strlen(name) + 1]; // this too
strcpy((char*)m->name, name);
m->count = 0;
m->parameters = 0;
if(SvOK(params) && SvRV(params)) {
AV *av = (AV*)SvRV(params);
m->count = av_len(av) + 1;
if(m->count > 0) {
m->parameters = new TQUParameter[m->count];
for(int i = 0; i < m->count; i++) {
SV *sv = av_shift(av);
if(!SvOK(sv))
croak("Invalid paramater for TQUMethod\n");
TQUParameter *p = (TQUParameter*)SvIV(sv);
SvREFCNT_dec(sv);
((TQUParameter*)m->parameters)[i] = *p;
delete p;
}
} else
m->count = 0;
}
RETVAL = m;
OUTPUT:
RETVAL
void *
make_TQMetaData_tbl(list)
SV *list
CODE:
RETVAL = 0;
if(SvOK(list) && SvRV(list)) {
AV *av = (AV*)SvRV(list);
int count = av_len(av) + 1;
TQMetaData *m = new TQMetaData[count];
for(int i = 0; i < count; i++) {
SV *sv = av_shift(av);
if(!SvOK(sv))
croak("Invalid metadata\n");
TQMetaData *old = (TQMetaData*)SvIV(sv);
SvREFCNT_dec(sv);
m[i] = *old;
delete old;
}
RETVAL = (void*)m;
}
OUTPUT:
RETVAL
SV *
make_metaObject(className, parent, slot_tbl, slot_count, signal_tbl, signal_count)
char *className
SV *parent
void *slot_tbl
int slot_count
void *signal_tbl
int signal_count
CODE:
smokeperl_object *po = sv_obj_info(parent);
if(!po || !po->ptr) croak("Cannot create metaObject\n");
TQMetaObject *meta = TQMetaObject::new_metaobject(
className, (TQMetaObject*)po->ptr,
(const TQMetaData*)slot_tbl, slot_count, // slots
(const TQMetaData*)signal_tbl, signal_count, // signals
0, 0, // properties
0, 0, // enums
0, 0);
// this object-creation code is so, so wrong here
HV *hv = newHV();
SV *obj = newRV_noinc((SV*)hv);
smokeperl_object o;
o.smoke = qt_Smoke;
o.classId = qt_Smoke->idClass("TQMetaObject");
o.ptr = meta;
o.allocated = true;
sv_magic((SV*)hv, sv_qapp, '~', (char*)&o, sizeof(o));
MAGIC *mg = mg_find((SV*)hv, '~');
mg->mg_virtual = &vtbl_smoke;
char *buf = qt_Smoke->binding->className(o.classId);
sv_bless(obj, gv_stashpv(buf, TRUE));
delete[] buf;
RETVAL = obj;
OUTPUT:
RETVAL
void
dumpObjects()
CODE:
hv_iterinit(pointer_map);
HE *e;
while(e = hv_iternext(pointer_map)) {
STRLEN len;
SV *sv = HeVAL(e);
printf("key = %s, refcnt = %d, weak = %d, ref? %d\n", HePV(e, len), SvREFCNT(sv), SvWEAKREF(sv), SvROK(sv)?1:0);
if(SvRV(sv))
printf("REFCNT = %d\n", SvREFCNT(SvRV(sv)));
//SvREFCNT_dec(HeVAL(e));
//HeVAL(e) = &PL_sv_undef;
}
void
dangle(obj)
SV *obj
CODE:
if(SvRV(obj))
SvREFCNT_inc(SvRV(obj));
void
setAllocated(obj, b)
SV *obj
bool b
CODE:
smokeperl_object *o = sv_obj_info(obj);
if(o) {
o->allocated = b;
}
void
setqapp(obj)
SV *obj
CODE:
if(!obj || !SvROK(obj))
croak("Invalid TQt::Application object. Couldn't set TQt::app()\n");
sv_qapp = SvRV(obj);
void
setThis(obj)
SV *obj
CODE:
sv_setsv_mg(sv_this, obj);
void
deleteObject(obj)
SV *obj
CODE:
smokeperl_object *o = sv_obj_info(obj);
if(!o) { XSRETURN_EMPTY; }
TQObject *qobj = (TQObject*)o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQObject"));
delete qobj;
void
mapObject(obj)
SV *obj
CODE:
smokeperl_object *o = sv_obj_info(obj);
if(!o)
XSRETURN_EMPTY;
SmokeClass c( o->smoke, o->classId );
if(!c.hasVirtual() ) {
XSRETURN_EMPTY;
}
mapPointer(obj, o, pointer_map, o->classId, 0);
bool
isTQObject(obj)
SV *obj
CODE:
RETVAL = 0;
smokeperl_object *o = sv_obj_info(obj);
if(o && isTQObject(o->smoke, o->classId))
RETVAL = 1;
OUTPUT:
RETVAL
bool
isValidAllocatedPointer(obj)
SV *obj
CODE:
RETVAL = 0;
smokeperl_object *o = sv_obj_info(obj);
if(o && o->ptr && o->allocated)
RETVAL = 1;
OUTPUT:
RETVAL
SV*
findAllocatedObjectFor(obj)
SV *obj
CODE:
RETVAL = &PL_sv_undef;
smokeperl_object *o = sv_obj_info(obj);
SV *ret;
if(o && o->ptr && (ret = getPointerObject(o->ptr)))
RETVAL = ret;
OUTPUT:
RETVAL
SV *
getGV(cv)
SV *cv
CODE:
RETVAL = (SvROK(cv) && (SvTYPE(SvRV(cv))==SVt_PVCV) ?
SvREFCNT_inc(CvGV((CV*)SvRV(cv))) : &PL_sv_undef);
OUTPUT:
RETVAL
int
idClass(name)
char *name
CODE:
RETVAL = qt_Smoke->idClass(name);
OUTPUT:
RETVAL
int
idMethodName(name)
char *name
CODE:
RETVAL = qt_Smoke->idMethodName(name);
OUTPUT:
RETVAL
int
idMethod(idclass, idmethodname)
int idclass
int idmethodname
CODE:
RETVAL = qt_Smoke->idMethod(idclass, idmethodname);
OUTPUT:
RETVAL
void
findMethod(c, name)
char *c
char *name
PPCODE:
Smoke::Index meth = qt_Smoke->findMethod(c, name);
// printf("DAMNIT on %s::%s => %d\n", c, name, meth);
if(!meth) {
// empty list
} else if(meth > 0) {
Smoke::Index i = qt_Smoke->methodMaps[meth].method;
if(!i) { // shouldn't happen
croak("Corrupt method %s::%s", c, name);
} else if(i > 0) { // single match
PUSHs(sv_2mortal(newSViv(
(IV)qt_Smoke->methodMaps[meth].method
)));
} else { // multiple match
i = -i; // turn into ambiguousMethodList index
while(qt_Smoke->ambiguousMethodList[i]) {
PUSHs(sv_2mortal(newSViv(
(IV)qt_Smoke->ambiguousMethodList[i]
)));
i++;
}
}
}
void
findMethodFromIds(idclass, idmethodname)
int idclass
int idmethodname
PPCODE:
Smoke::Index meth = qt_Smoke->findMethod(idclass, idmethodname);
if(!meth) {
// empty list
} else if(meth > 0) {
Smoke::Index i = qt_Smoke->methodMaps[meth].method;
if(i >= 0) { // single match
PUSHs(sv_2mortal(newSViv((IV)i)));
} else { // multiple match
i = -i; // turn into ambiguousMethodList index
while(qt_Smoke->ambiguousMethodList[i]) {
PUSHs(sv_2mortal(newSViv(
(IV)qt_Smoke->ambiguousMethodList[i]
)));
i++;
}
}
}
# findAllMethods(classid [, startingWith]) : returns { "mungedName" => [index in methods, ...], ... }
HV*
findAllMethods(classid, ...)
SV* classid
CODE:
RETVAL=newHV();
if(SvIOK(classid)) {
Smoke::Index c = (Smoke::Index) SvIV(classid);
char * pat = 0L;
if(items > 1 && SvPOK(ST(1)))
pat = SvPV_nolen(ST(1));
Smoke::Index imax = qt_Smoke->numMethodMaps;
Smoke::Index imin = 0, icur = -1, methmin = 0, methmax = 0;
int icmp = -1;
while(imax >= imin) {
icur = (imin + imax) / 2;
icmp = qt_Smoke->leg(qt_Smoke->methodMaps[icur].classId, c);
if(!icmp) {
Smoke::Index pos = icur;
while(icur && qt_Smoke->methodMaps[icur-1].classId == c)
icur --;
methmin = icur;
icur = pos;
while(icur < imax && qt_Smoke->methodMaps[icur+1].classId == c)
icur ++;
methmax = icur;
break;
}
if (icmp > 0)
imax = icur - 1;
else
imin = icur + 1;
}
if(!icmp) {
for(Smoke::Index i=methmin ; i <= methmax ; i++) {
Smoke::Index m = qt_Smoke->methodMaps[i].name;
if(!pat || !strncmp(qt_Smoke->methodNames[m], pat, strlen(pat))) {
Smoke::Index ix= qt_Smoke->methodMaps[i].method;
AV* meths = newAV();
if(ix >= 0) { // single match
av_push(meths, newSViv((IV)ix));
} else { // multiple match
ix = -ix; // turn into ambiguousMethodList index
while(qt_Smoke->ambiguousMethodList[ix]) {
av_push(meths, newSViv((IV)qt_Smoke->ambiguousMethodList[ix]));
ix++;
}
}
hv_store(RETVAL, qt_Smoke->methodNames[m],strlen(qt_Smoke->methodNames[m]),newRV_inc((SV*)meths),0);
}
}
}
}
OUTPUT:
RETVAL
SV *
dumpCandidates(rmeths)
SV *rmeths
CODE:
if(SvROK(rmeths) && SvTYPE(SvRV(rmeths)) == SVt_PVAV) {
AV *methods = (AV*)SvRV(rmeths);
SV *errmsg = newSVpvf("");
for(int i = 0; i <= av_len(methods); i++) {
sv_catpv(errmsg, "\t");
IV id = SvIV(*(av_fetch(methods, i, 0)));
Smoke::Method &meth = qt_Smoke->methods[id];
const char *tname = qt_Smoke->types[meth.ret].name;
if(meth.flags & Smoke::mf_static) sv_catpv(errmsg, "static ");
sv_catpvf(errmsg, "%s ", (tname ? tname:"void"));
sv_catpvf(errmsg, "%s::%s(", qt_Smoke->classes[meth.classId].className, qt_Smoke->methodNames[meth.name]);
for(int i = 0; i < meth.numArgs; i++) {
if(i) sv_catpv(errmsg, ", ");
tname = qt_Smoke->types[qt_Smoke->argumentList[meth.args+i]].name;
sv_catpv(errmsg, (tname ? tname:"void"));
}
sv_catpv(errmsg, ")");
if(meth.flags & Smoke::mf_const) sv_catpv(errmsg, " const");
sv_catpv(errmsg, "\n");
}
RETVAL=errmsg;
}
else
RETVAL=newSVpvf("");
OUTPUT:
RETVAL
SV *
catArguments(r_args)
SV* r_args
CODE:
RETVAL=newSVpvf("");
if(SvROK(r_args) && SvTYPE(SvRV(r_args)) == SVt_PVAV) {
AV* args=(AV*)SvRV(r_args);
for(int i = 0; i <= av_len(args); i++) {
SV **arg=av_fetch(args, i, 0);
if(i) sv_catpv(RETVAL, ", ");
if(!arg || !SvOK(*arg)) {
sv_catpv(RETVAL, "undef");
} else if(SvROK(*arg)) {
smokeperl_object *o = sv_obj_info(*arg);
if(o)
sv_catpv(RETVAL, o->smoke->className(o->classId));
else
sv_catsv(RETVAL, *arg);
} else {
bool isString = SvPOK(*arg);
STRLEN len;
char *s = SvPV(*arg, len);
if(isString) sv_catpv(RETVAL, "'");
sv_catpvn(RETVAL, s, len > 10 ? 10 : len);
if(len > 10) sv_catpv(RETVAL, "...");
if(isString) sv_catpv(RETVAL, "'");
}
}
}
OUTPUT:
RETVAL
SV *
callMethod(...)
PPCODE:
if(_current_method) {
MethodCall c(qt_Smoke, _current_method, &ST(0), items);
c.next();
SV *ret = c.var();
SvREFCNT_inc(ret);
PUSHs(sv_2mortal(ret));
} else
PUSHs(sv_newmortal());
bool
isObject(obj)
SV *obj
CODE:
RETVAL = sv_to_ptr(obj) ? TRUE : FALSE;
OUTPUT:
RETVAL
void
setCurrentMethod(meth)
int meth
CODE:
// FIXME: damn, this is lame, and it doesn't handle ambiguous methods
_current_method = meth; //qt_Smoke->methodMaps[meth].method;
SV *
getClassList()
CODE:
AV *av = newAV();
for(int i = 1; i <= qt_Smoke->numClasses; i++) {
//printf("%s => %d\n", qt_Smoke->classes[i].className, i);
av_push(av, newSVpv(qt_Smoke->classes[i].className, 0));
// hv_store(hv, qt_Smoke->classes[i].className, 0, newSViv(i), 0);
}
RETVAL = newRV((SV*)av);
OUTPUT:
RETVAL
void
installthis(package)
char *package
CODE:
if(!package) XSRETURN_EMPTY;
char *name = new char[strlen(package) + 7];
char *file = __FILE__;
strcpy(name, package);
strcat(name, "::this");
// *{ $name } = sub () : lvalue;
CV *thissub = newXS(name, XS_this, file);
sv_setpv((SV*)thissub, ""); // sub this () : lvalue;
delete[] name;
void
installattribute(package, name)
char *package
char *name
CODE:
if(!package || !name) XSRETURN_EMPTY;
char *attr = new char[strlen(package) + strlen(name) + 3];
sprintf(attr, "%s::%s", package, name);
char *file = __FILE__;
// *{ $attr } = sub () : lvalue;
CV *attrsub = newXS(attr, XS_attr, file);
sv_setpv((SV*)attrsub, "");
CvLVALUE_on(attrsub);
CvNODEBUG_on(attrsub);
delete[] attr;
void
installsuper(package)
char *package
CODE:
if(!package) XSRETURN_EMPTY;
char *attr = new char[strlen(package) + 8];
sprintf(attr, "%s::SUPER", package);
char *file = __FILE__;
CV *attrsub = newXS(attr, XS_super, file);
sv_setpv((SV*)attrsub, "");
delete[] attr;
void
installautoload(package)
char *package
CODE:
if(!package) XSRETURN_EMPTY;
char *autoload = new char[strlen(package) + 11];
strcpy(autoload, package);
strcat(autoload, "::_UTOLOAD");
char *file = __FILE__;
// *{ $package."::AUTOLOAD" } = XS_AUTOLOAD
newXS(autoload, XS_AUTOLOAD, file);
delete[] autoload;
# ----------------- XSUBS for TQt:: -----------------
MODULE = TQt PACKAGE = TQt
SV *
this()
CODE:
RETVAL = newSVsv(sv_this);
OUTPUT:
RETVAL
SV *
app()
CODE:
RETVAL = newRV_inc(sv_qapp);
OUTPUT:
RETVAL
SV *
version()
CODE:
RETVAL = newSVpv(TQT_VERSION_STR,0);
OUTPUT:
RETVAL
BOOT:
init_qt_Smoke();
qt_Smoke->binding = new TQtSmokeBinding(qt_Smoke);
install_handlers(TQt_handlers);
pointer_map = newHV();
sv_this = newSV(0);
methcache = new TQAsciiDict<Smoke::Index>(1187);
classcache = new TQAsciiDict<Smoke::Index>(827);
methcache->setAutoDelete(1);
classcache->setAutoDelete(1);