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.
tdebindings/korundum/rubylib/korundum/Korundum.cpp

1194 lines
34 KiB

/***************************************************************************
Korundum.cpp - Runtime for KDE services, DCOP etc
-------------------
begin : Sun Sep 28 2003
copyright : (C) 2003-2004 by Richard Dale
email : Richard_Dale@tipitina.demon.co.uk
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <tqobject.h>
#include <tqstringlist.h>
#include <tqmap.h>
#include <tqdatastream.h>
#include <tdeversion.h>
#include <dcopclient.h>
#include <dcopobject.h>
#include <dcopref.h>
#include <tdeapplication.h>
#include <kurl.h>
#if TDE_VERSION >= 0x030200
#include <tdeconfigskeleton.h>
#endif
#include <tdeio/global.h>
#include <tdeparts/browserextension.h>
#include <kde_terminal_interface.h>
#include <ruby.h>
#include <marshall.h>
#include <qtruby.h>
#include <smokeruby.h>
#include <smoke.h>
#define HAVE_STRLCAT_PROTO 1
#define HAVE_STRLCPY_PROTO 1
#include "config.h"
#ifndef HAVE_RUBY_1_9
#define RARRAY_LEN(x) (RARRAY(x)->len)
#define RSTRING_LEN(x) (RSTRING(x)->len)
#define rb_str_catf_1 rb_str_catf
#endif
extern "C" {
extern VALUE qt_internal_module;
extern VALUE tdeconfigskeleton_class;
extern VALUE tdeconfigskeleton_itemenum_choice_class;
extern VALUE tdeio_udsatom_class;
extern VALUE konsole_part_class;
extern VALUE set_obj_info(const char * className, smokeruby_object * o);
extern void set_kde_resolve_classname(const char * (*kde_resolve_classname) (Smoke*, int, void *));
extern const char * kde_resolve_classname(Smoke* smoke, int classId, void * ptr);
};
extern TypeHandler KDE_handlers[];
extern void install_handlers(TypeHandler *);
extern Smoke *qt_Smoke;
static VALUE kde_internal_module;
Marshall::HandlerFn getMarshallFn(const SmokeType &type);
/*
Copy items from the stack to the stream, each item has a corresponding description in the
args array of MocArguments. Used for marshalling the args to DCOP calls and sends, emitting
DCOP signals, and converting the return value of a DCOP slot to a stream.
*/
static void
smokeStackToStream(Marshall *m, Smoke::Stack stack, TQDataStream* stream, int items, MocArgument* args)
{
for(int i = 0; i < items; i++) {
switch(args[i].argType) {
case xmoc_bool:
*stream << stack[i].s_bool;
break;
case xmoc_int:
*stream << stack[i].s_int;
break;
case xmoc_double:
*stream << stack[i].s_double;
break;
case xmoc_charstar:
*stream << (char *) stack[i].s_voidp;
break;
case xmoc_QString:
{
TQString temp((const TQString&) *((TQString *) stack[i].s_voidp));
*stream << temp;
}
break;
default:
{
const SmokeType &t = args[i].st;
switch(t.elem()) {
case Smoke::t_bool:
*stream << stack[i].s_bool;
break;
case Smoke::t_char:
*stream << stack[i].s_char;
break;
case Smoke::t_uchar:
*stream << stack[i].s_uchar;
break;
case Smoke::t_short:
*stream << stack[i].s_short;
break;
case Smoke::t_ushort:
*stream << stack[i].s_ushort;
break;
case Smoke::t_int:
*stream << stack[i].s_int;
break;
case Smoke::t_uint:
*stream << stack[i].s_uint;
break;
case Smoke::t_long:
*stream << stack[i].s_long;
break;
case Smoke::t_ulong:
*stream << stack[i].s_ulong;
break;
case Smoke::t_float:
*stream << stack[i].s_float;
break;
case Smoke::t_double:
*stream << stack[i].s_double;
break;
case Smoke::t_enum:
m->unsupported();
break;
case Smoke::t_class:
case Smoke::t_voidp:
{
// Special case any types which are in the Smoke runtime, but
// don't have TQDataStream '<<' methods
if (strcmp(t.name(), "TQCString") == 0) {
TQCString temp((const TQCString&) *((TQCString *) stack[i].s_voidp));
*stream << temp;
break;
} else if (strcmp(t.name(), "QCStringList") == 0) {
QCStringList temp((const QCStringList&) *((QCStringList *) stack[i].s_voidp));
*stream << temp;
break;
} else if (strcmp(t.name(), "TQStringList") == 0) {
TQStringList temp((const TQStringList&) *((TQStringList *) stack[i].s_voidp));
*stream << temp;
break;
} else if (strcmp(t.name(), "KURL::List") == 0) {
KURL::List temp((const KURL::List&) *((KURL::List *) stack[i].s_voidp));
*stream << temp;
break;
} else if (strcmp(t.name(), "TQMap<TQCString,DCOPRef>") == 0) {
TQMap<TQCString,DCOPRef> temp((const TQMap<TQCString,DCOPRef>&) *((TQMap<TQCString,DCOPRef>*) stack[i].s_voidp));
*stream << temp;
break;
}
// Look for methods of the form: TQDataStream & operator<<(TQDataStream&, const MyClass&)
Smoke::Index meth = t.smoke()->findMethod("TQGlobalSpace", "operator<<##");
Smoke::Index ix;
if (meth > 0) {
ix = t.smoke()->methodMaps[meth].method;
ix = -ix; // turn into ambiguousMethodList index
while (t.smoke()->ambiguousMethodList[ix]) {
Smoke::Method &method = t.smoke()->methods[t.smoke()->ambiguousMethodList[ix]];
TQString refType("const ");
refType += t.name();
refType += "&";
if ( strcmp( "TQDataStream&",
t.smoke()->types[t.smoke()->argumentList[method.args+0]].name ) == 0
&& strcmp( refType.latin1(),
t.smoke()->types[t.smoke()->argumentList[method.args+1]].name ) == 0 )
{
Smoke::ClassFn fn = t.smoke()->classes[method.classId].classFn;
Smoke::StackItem local_stack[3];
local_stack[1].s_voidp = stream;
local_stack[2].s_voidp = stack[i].s_voidp;
// Call the TQDataStream marshaller write method
// with the instance to be marshalled
(*fn)(method.method, 0, local_stack);
break;
}
ix++;
}
}
}
break;
default:
break;
}
}
}
}
return;
}
/*
Copy items from the stream to the stack, each item has a corresponding description in the
args array of MocArguments. Used for marshalling the arguments to a DCOP slot invocation,
and for converting a dcop reply to a ruby value.
*/
static void
smokeStackFromStream(Marshall *m, Smoke::Stack stack, TQDataStream* stream, int items, MocArgument* args)
{
for(int i = 0; i < items; i++) {
switch(args[i].argType) {
case xmoc_bool:
{
*stream >> stack[i].s_bool;
break;
}
case xmoc_int:
{
*stream >> stack[i].s_int;
break;
}
case xmoc_double:
*stream >> stack[i].s_double;
break;
case xmoc_charstar:
*stream >> (char *&) stack[i].s_voidp;
break;
case xmoc_QString:
{
TQString temp;
*stream >> temp;
stack[i].s_voidp = new TQString(temp);
}
break;
default: // case xmoc_ptr:
{
const SmokeType &t = args[i].st;
switch(t.elem()) {
case Smoke::t_bool:
{
*stream >> stack[i].s_bool;
break;
}
case Smoke::t_char:
{
*stream >> stack[i].s_char;
break;
}
case Smoke::t_uchar:
{
*stream >> stack[i].s_uchar;
break;
}
case Smoke::t_short:
{
*stream >> stack[i].s_short;
break;
}
case Smoke::t_ushort:
{
*stream >> stack[i].s_ushort;
break;
}
case Smoke::t_int:
{
*stream >> stack[i].s_int;
break;
}
case Smoke::t_uint:
{
*stream >> stack[i].s_uint;
break;
}
case Smoke::t_long:
{
*stream >> stack[i].s_long;
break;
}
case Smoke::t_ulong:
{
*stream >> stack[i].s_ulong;
break;
}
case Smoke::t_float:
*stream >> stack[i].s_float;
break;
case Smoke::t_double:
*stream >> stack[i].s_double;
break;
case Smoke::t_enum:
m->unsupported();
break;
case Smoke::t_class:
case Smoke::t_voidp:
{
// Special case any types which are in the Smoke runtime, but
// don't have TQDataStream '>>' methods
if (strcmp(t.name(), "TQCString") == 0) {
TQCString temp;
*stream >> temp;
stack[i].s_voidp = new TQCString(temp);
break;
} else if (strcmp(t.name(), "QCStringList") == 0) {
QCStringList temp;
*stream >> temp;
stack[i].s_voidp = new QCStringList(temp);
break;
} else if (strcmp(t.name(), "TQStringList") == 0) {
TQStringList temp;
*stream >> temp;
stack[i].s_voidp = new TQStringList(temp);
break;
} else if (strcmp(t.name(), "KURL::List") == 0) {
KURL::List temp;
*stream >> temp;
stack[i].s_voidp = new KURL::List(temp);
break;
} else if (strcmp(t.name(), "TQMap<TQCString,DCOPRef>") == 0) {
TQMap<TQCString,DCOPRef> temp;
*stream >> temp;
stack[i].s_voidp = new TQMap<TQCString,DCOPRef>(temp);
break;
}
// First construct an instance to read the TQDataStream into,
// so look for a no args constructor
Smoke::Index ctorId = t.smoke()->idMethodName(t.name());
Smoke::Index ctorMeth = t.smoke()->findMethod(t.classId(), ctorId);
Smoke::Index ctor = t.smoke()->methodMaps[ctorMeth].method;
if(ctor < 1) {
stack[i].s_voidp = 0;
m->unsupported();
break; // Ambiguous or non-existent method, shouldn't happen with a no arg constructor
}
// Okay, ctor is the constructor. Time to call it.
Smoke::StackItem ctor_stack[1];
ctor_stack[0].s_voidp = 0;
Smoke::ClassFn classfn = t.smoke()->classes[t.classId()].classFn;
(*classfn)(t.smoke()->methods[ctor].method, 0, ctor_stack);
stack[i].s_voidp = ctor_stack[0].s_voidp;
// Look for methods of the form: TQDataStream & operator>>(TQDataStream&, MyClass&)
Smoke::Index meth = t.smoke()->findMethod("TQGlobalSpace", "operator>>##");
Smoke::Index ix;
if (meth > 0) {
ix = t.smoke()->methodMaps[meth].method;
ix = -ix; // turn into ambiguousMethodList index
while (t.smoke()->ambiguousMethodList[ix]) {
Smoke::Method &method = t.smoke()->methods[t.smoke()->ambiguousMethodList[ix]];
TQString refType(t.name());
refType += "&";
if ( strcmp( "TQDataStream&",
t.smoke()->types[t.smoke()->argumentList[method.args+0]].name ) == 0
&& strcmp( refType.latin1(),
t.smoke()->types[t.smoke()->argumentList[method.args+1]].name ) == 0 )
{
Smoke::ClassFn fn = t.smoke()->classes[method.classId].classFn;
Smoke::StackItem local_stack[3];
local_stack[1].s_voidp = stream;
local_stack[2].s_voidp = stack[i].s_voidp;
// Call the TQDataStream marshaller read method
// on the instance to be marshalled
(*fn)(method.method, 0, local_stack);
break;
}
ix++;
}
}
}
break;
}
}
}
}
}
/*
Converts a TQByteArray returned from a DCOP call to a ruby value.
*/
class DCOPReturn : public Marshall {
MocArgument * _replyType;
Smoke::Stack _stack;
VALUE * _result;
public:
DCOPReturn(TQDataStream & retval, VALUE * result, VALUE replyType)
{
_result = result;
VALUE temp = rb_funcall(qt_internal_module, rb_intern("getMocArguments"), 1, replyType);
Data_Get_Struct(rb_ary_entry(temp, 1), MocArgument, _replyType);
_stack = new Smoke::StackItem[1];
smokeStackFromStream(this, _stack, &retval, 1, _replyType);
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
}
SmokeType type() {
return _replyType[0].st;
}
Marshall::Action action() { return Marshall::ToVALUE; }
Smoke::StackItem &item() { return _stack[0]; }
VALUE * var() {
return _result;
}
void unsupported()
{
rb_raise(rb_eArgError, "Cannot handle '%s' as DCOP return-type", type().name());
}
Smoke *smoke() { return type().smoke(); }
void next() {}
bool cleanup() { return false; }
~DCOPReturn()
{
delete[] _stack;
}
};
class DCOPCall : public Marshall {
VALUE _obj;
TQCString & _remFun;
int _items;
VALUE *_sp;
TQByteArray *_data;
TQDataStream *_stream;
int _id;
MocArgument *_args;
bool _useEventLoop;
int _timeout;
int _cur;
Smoke::Stack _stack;
VALUE _result;
bool _called;
public:
DCOPCall(VALUE obj, TQCString & remFun, int items, VALUE *sp, VALUE args, bool useEventLoop, int timeout) :
_obj(obj), _remFun(remFun), _items(items), _sp(sp),
_useEventLoop(useEventLoop), _timeout(timeout), _cur(-1), _called(false)
{
_data = new TQByteArray();
_stream = new TQDataStream(*_data, IO_WriteOnly);
Data_Get_Struct(rb_ary_entry(args, 1), MocArgument, _args);
_stack = new Smoke::StackItem[_items];
_result = Qnil;
}
~DCOPCall()
{
delete[] _stack;
delete _data;
delete _stream;
}
const MocArgument &arg() { return _args[_cur]; }
SmokeType type() { return arg().st; }
Marshall::Action action() { return Marshall::FromVALUE; }
Smoke::StackItem &item() { return _stack[_cur]; }
VALUE * var() {
if(_cur < 0) return &_result;
return _sp + _cur;
}
void unsupported()
{
rb_raise(rb_eArgError, "Cannot handle '%s' as a DCOP call argument", type().name());
}
Smoke *smoke() { return type().smoke(); }
void dcopCall()
{
if(_called) return;
_called = true;
smokeStackToStream(this, _stack, _stream, _items, _args);
smokeruby_object *o = value_obj_info(_obj);
DCOPRef * dcopRef = (DCOPRef *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("DCOPRef"));
DCOPClient* dc = dcopRef->dcopClient();
TQCString replyType;
TQByteArray dataReceived;
#if TDE_VERSION >= 0x030200
bool ok = dc->call(dcopRef->app(), dcopRef->obj(), _remFun, *_data, replyType, dataReceived, _useEventLoop, _timeout);
#else
bool ok = dc->call(dcopRef->app(), dcopRef->obj(), _remFun, *_data, replyType, dataReceived, _useEventLoop);
#endif
if (!ok) {
// Note that a failed dcop call returns 'nil', not 'false'
_result = Qnil;
return;
} else if (replyType == "void" || replyType == "ASYNC") {
_result = Qtrue;
return;
}
TQDataStream ds(dataReceived, IO_ReadOnly);
if (replyType == "TQValueList<DCOPRef>") {
// Special case TQValueList<DCOPRef> as a TQDataStream marshaller
// isn't in the Smoke runtime
TQValueList<DCOPRef> valuelist;
ds >> valuelist;
_result = rb_ary_new();
for (TQValueListIterator<DCOPRef> it = valuelist.begin(); it != valuelist.end(); ++it) {
void *p = new DCOPRef(*it);
VALUE obj = getPointerObject(p);
if (obj == Qnil) {
smokeruby_object * o = ALLOC(smokeruby_object);
o->classId = qt_Smoke->idClass("DCOPRef");
o->smoke = qt_Smoke;
o->ptr = p;
o->allocated = true;
obj = set_obj_info("KDE::DCOPRef", o);
}
rb_ary_push(_result, obj);
}
} else if (replyType == "TQValueList<TQCString>") {
// And special case this type too
TQValueList<TQCString> propertyList;
ds >> propertyList;
_result = rb_ary_new();
for (TQValueListIterator<TQCString> it = propertyList.begin(); it != propertyList.end(); ++it) {
rb_ary_push(_result, rb_str_new2((const char *) *it));
}
} else if (replyType == "TQValueList<int>") {
// And special case this type too
TQValueList<int> propertyList;
ds >> propertyList;
_result = rb_ary_new();
for (TQValueListIterator<int> it = propertyList.begin(); it != propertyList.end(); ++it) {
rb_ary_push(_result, INT2NUM(*it));
}
} else if (replyType == "TQMap<TQString,DCOPRef>") {
// And another..
TQMap<TQString,DCOPRef> actionMap;
ds >> actionMap;
_result = rb_hash_new();
TQMap<TQString,DCOPRef>::Iterator it;
for (it = actionMap.begin(); it != actionMap.end(); ++it) {
void *p = new DCOPRef(it.data());
VALUE obj = getPointerObject(p);
if (obj == Qnil) {
smokeruby_object * o = ALLOC(smokeruby_object);
o->classId = qt_Smoke->idClass("DCOPRef");
o->smoke = qt_Smoke;
o->ptr = p;
o->allocated = true;
obj = set_obj_info("KDE::DCOPRef", o);
}
rb_hash_aset(_result, rb_str_new2(it.key().latin1()), obj);
}
} else {
DCOPReturn dcopReturn(ds, &_result, rb_str_new2((const char *) replyType));
}
}
void next()
{
int oldcur = _cur;
_cur++;
while(!_called && _cur < _items) {
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
_cur++;
}
dcopCall();
_cur = oldcur;
}
bool cleanup() { return true; }
};
class DCOPSend : public Marshall {
VALUE _obj;
TQCString & _remFun;
TQByteArray *_data;
TQDataStream *_stream;
int _id;
MocArgument *_args;
int _items;
VALUE *_sp;
int _cur;
VALUE * _result;
Smoke::Stack _stack;
bool _called;
public:
DCOPSend(VALUE obj, TQCString & remFun, int items, VALUE *sp, VALUE args, VALUE * result) :
_obj(obj), _remFun(remFun), _items(items), _sp(sp), _cur(-1), _result(result), _called(false)
{
_data = new TQByteArray();
_stream = new TQDataStream(*_data, IO_WriteOnly);
Data_Get_Struct(rb_ary_entry(args, 1), MocArgument, _args);
_stack = new Smoke::StackItem[_items];
}
~DCOPSend()
{
delete[] _stack;
delete _data;
delete _stream;
}
const MocArgument &arg() { return _args[_cur]; }
SmokeType type() { return arg().st; }
Marshall::Action action() { return Marshall::FromVALUE; }
Smoke::StackItem &item() { return _stack[_cur]; }
VALUE * var() { return _sp + _cur; }
void unsupported()
{
rb_raise(rb_eArgError, "Cannot handle '%s' as a DCOP send argument", type().name());
}
Smoke *smoke() { return type().smoke(); }
void dcopSend()
{
if(_called) return;
_called = true;
smokeStackToStream(this, _stack, _stream, _items, _args);
smokeruby_object *o = value_obj_info(_obj);
DCOPRef * dcopRef = (DCOPRef *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("DCOPRef"));
DCOPClient* dc = dcopRef->dcopClient();
bool ok = dc->send(dcopRef->app(), dcopRef->obj(), _remFun, *_data);
*_result = (ok ? Qtrue : Qfalse);
}
void next()
{
int oldcur = _cur;
_cur++;
while(!_called && _cur < _items) {
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
_cur++;
}
dcopSend();
_cur = oldcur;
}
bool cleanup() { return true; }
};
class EmitDCOPSignal : public Marshall {
VALUE _obj;
const char * _signalName;
TQByteArray *_data;
TQDataStream *_stream;
int _id;
MocArgument *_args;
VALUE *_sp;
int _items;
int _cur;
Smoke::Stack _stack;
bool _called;
public:
EmitDCOPSignal(VALUE obj, const char * signalName, int items, VALUE *sp, VALUE args) :
_obj(obj), _signalName(signalName), _sp(sp), _items(items), _cur(-1), _called(false)
{
_data = new TQByteArray();
_stream = new TQDataStream(*_data, IO_WriteOnly);
Data_Get_Struct(rb_ary_entry(args, 1), MocArgument, _args);
_stack = new Smoke::StackItem[_items];
}
~EmitDCOPSignal()
{
delete[] _stack;
delete _stream;
delete _data;
}
const MocArgument &arg() { return _args[_cur]; }
SmokeType type() { return arg().st; }
Marshall::Action action() { return Marshall::FromVALUE; }
Smoke::StackItem &item() { return _stack[_cur]; }
VALUE * var() { return _sp + _cur; }
void unsupported()
{
rb_raise(rb_eArgError, "Cannot handle '%s' as a DCOP signal argument", type().name());
}
Smoke *smoke() { return type().smoke(); }
void emitSignal()
{
if(_called) return;
_called = true;
smokeStackToStream(this, _stack, _stream, _items, _args);
smokeruby_object *o = value_obj_info(_obj);
DCOPObject * dcopObject = (DCOPObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("DCOPObject"));
dcopObject->emitDCOPSignal(_signalName, *_data);
}
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; }
};
/*
Converts a ruby value returned by a DCOP slot invocation to a TQByteArray
*/
class DCOPReplyValue : public Marshall {
MocArgument * _replyType;
Smoke::Stack _stack;
VALUE * _result;
public:
DCOPReplyValue(TQByteArray & retval, VALUE * result, VALUE replyType)
{
TQDataStream _retval(retval, IO_WriteOnly);
_result = result;
Data_Get_Struct(rb_ary_entry(replyType, 1), MocArgument, _replyType);
_stack = new Smoke::StackItem[1];
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
smokeStackToStream(this, _stack, &_retval, 1, _replyType);
}
SmokeType type() {
return _replyType[0].st;
}
Marshall::Action action() { return Marshall::FromVALUE; }
Smoke::StackItem &item() { return _stack[0]; }
VALUE * var() {
return _result;
}
void unsupported()
{
rb_raise(rb_eArgError, "Cannot handle '%s' as DCOP reply-type", type().name());
}
Smoke *smoke() { return type().smoke(); }
void next() {}
bool cleanup() { return false; }
~DCOPReplyValue() {
delete[] _stack;
}
};
class InvokeDCOPSlot : public Marshall {
VALUE _obj;
ID _slotname;
int _items;
MocArgument * _args;
TQDataStream * _stream;
const char * _replyTypeName;
VALUE _replyType;
TQByteArray * _retval;
int _cur;
bool _called;
VALUE * _sp;
Smoke::Stack _stack;
public:
const MocArgument &arg() { return _args[_cur]; }
SmokeType type() { return arg().st; }
Marshall::Action action() { return Marshall::ToVALUE; }
Smoke::StackItem &item() { return _stack[_cur]; }
VALUE * var() { return _sp + _cur; }
Smoke *smoke() { return type().smoke(); }
bool cleanup() { return false; }
void unsupported()
{
rb_raise(rb_eArgError, "Cannot handle '%s' as DCOP slot argument\n", type().name());
}
void copyArguments()
{
smokeStackFromStream(this, _stack, _stream, _items, _args);
return;
}
void invokeSlot()
{
if (_called) {
return;
}
_called = true;
VALUE result = rb_funcall2(_obj, _slotname, _items, _sp);
if ( strcmp(_replyTypeName, "TQValueList<DCOPRef>") == 0
&& TYPE(result) == T_ARRAY )
{
// Special case TQValueList<DCOPRef> as a TQDataStream marshaller
// isn't in the Smoke runtime
TQValueList<DCOPRef> windowList;
for (long i = 0; i < RARRAY_LEN(result); i++) {
VALUE item = rb_ary_entry(result, i);
smokeruby_object *o = value_obj_info(item);
if( !o || !o->ptr)
continue;
void * ptr = o->ptr;
ptr = o->smoke->cast(ptr, o->classId, o->smoke->idClass("DCOPRef"));
windowList.append((DCOPRef)*(DCOPRef*)ptr);
}
TQDataStream retval(*_retval, IO_WriteOnly);
retval << windowList;
} else if ( strcmp(_replyTypeName, "TQValueList<TQCString>") == 0
&& TYPE(result) == T_ARRAY )
{
// And special case this type too
TQValueList<TQCString> propertyList;
for (long i = 0; i < RARRAY_LEN(result); i++) {
VALUE item = rb_ary_entry(result, i);
propertyList.append(TQCString(StringValuePtr(item)));
}
TQDataStream retval(*_retval, IO_WriteOnly);
retval << propertyList;
} else if ( strcmp(_replyTypeName, "TQMap<TQString,DCOPRef>") == 0
&& TYPE(result) == T_HASH )
{
// And another..
TQMap<TQString,DCOPRef> actionMap;
// Convert the ruby hash to an array of key/value arrays
VALUE temp = rb_funcall(result, rb_intern("to_a"), 0);
for (long i = 0; i < RARRAY_LEN(temp); i++) {
VALUE action = rb_ary_entry(rb_ary_entry(temp, i), 0);
VALUE item = rb_ary_entry(rb_ary_entry(temp, i), 1);
smokeruby_object *o = value_obj_info(item);
if( !o || !o->ptr)
continue;
void * ptr = o->ptr;
ptr = o->smoke->cast(ptr, o->classId, o->smoke->idClass("DCOPRef"));
actionMap[TQString(StringValuePtr(action))] = (DCOPRef)*(DCOPRef*)ptr;
}
TQDataStream retval(*_retval, IO_WriteOnly);
retval << actionMap;
} else if (_replyType != Qnil) {
DCOPReplyValue dcopReply(*_retval, &result, _replyType);
}
}
void next()
{
int oldcur = _cur;
_cur++;
while(!_called && _cur < _items) {
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
_cur++;
}
invokeSlot();
_cur = oldcur;
}
InvokeDCOPSlot(VALUE obj, ID slotname, VALUE args, TQByteArray& data, VALUE replyTypeName, VALUE replyType, TQByteArray& returnValue) :
_obj(obj), _slotname(slotname), _replyType(replyType), _cur(-1), _called(false)
{
_replyTypeName = StringValuePtr(replyTypeName);
_items = NUM2INT(rb_ary_entry(args, 0));
_stream = new TQDataStream(data, IO_ReadOnly);
_retval = &returnValue;
Data_Get_Struct(rb_ary_entry(args, 1), MocArgument, _args);
_sp = ALLOC_N(VALUE, _items);
_stack = new Smoke::StackItem[_items];
copyArguments();
}
~InvokeDCOPSlot() {
delete[] _stack;
delete _stream;
for(int i=0;i<_items;++i) {
free(_sp++);
}
}
};
extern "C" {
extern void Init_qtruby();
extern void set_new_kde(VALUE (*new_kde) (int, VALUE *, VALUE));
extern void set_tdeconfigskeletonitem_immutable(VALUE (*tdeconfigskeletonitem_immutable) (VALUE));
extern void set_kde_resolve_classname(const char * (*kde_resolve_classname) (Smoke*, int, void *));
extern const char * kde_resolve_classname(Smoke* smoke, int classId, void * ptr);
extern VALUE new_qt(int argc, VALUE * argv, VALUE klass);
extern VALUE qt_module;
extern VALUE qt_internal_module;
extern VALUE qt_base_class;
extern VALUE kde_module;
extern VALUE tdeio_module;
extern VALUE tdeparts_module;
extern VALUE tdehtml_module;
VALUE
getdcopinfo(VALUE self, TQString & signalname)
{
VALUE member = rb_funcall( kde_internal_module,
rb_intern("fullSignalName"),
2, self, rb_str_new2(signalname) );
signalname.setLatin1(StringValuePtr(member));
return rb_funcall( qt_internal_module,
rb_intern("getMocArguments"),
1, member );
}
VALUE
k_dcop_signal(int argc, VALUE * argv, VALUE self)
{
VALUE dcopObject = rb_funcall(kde_module, rb_intern("createDCOPObject"), 1, self);
TQString signalname(rb_id2name(rb_frame_this_func()));
VALUE args = getdcopinfo(self, signalname);
if(args == Qnil) return Qfalse;
EmitDCOPSignal signal(dcopObject, signalname.latin1(), argc, argv, args);
signal.next();
return Qtrue;
}
static VALUE
dcop_functions(VALUE self)
{
VALUE dcopObject = rb_funcall(kde_module, rb_intern("createDCOPObject"), 1, self);
return rb_funcall(dcopObject, rb_intern("functions"), 0);
}
static VALUE
dcop_interfaces(VALUE self)
{
VALUE dcopObject = rb_funcall(kde_module, rb_intern("createDCOPObject"), 1, self);
return rb_funcall(dcopObject, rb_intern("interfaces"), 0);
}
static VALUE
dcop_connect_signal(VALUE self, VALUE sender, VALUE senderObj, VALUE signal, VALUE slot, VALUE volatile_connect)
{
VALUE dcopObject = rb_funcall(kde_module, rb_intern("createDCOPObject"), 1, self);
return rb_funcall(dcopObject, rb_intern("connectDCOPSignal"), 5, sender, senderObj, signal, slot, volatile_connect);
}
static VALUE
dcop_disconnect_signal(VALUE self, VALUE sender, VALUE senderObj, VALUE signal, VALUE slot)
{
VALUE dcopObject = rb_funcall(kde_module, rb_intern("createDCOPObject"), 1, self);
return rb_funcall(dcopObject, rb_intern("disconnectDCOPSignal"), 4, sender, senderObj, signal, slot);
}
static VALUE
dcop_process(VALUE /*self*/, VALUE target, VALUE slotname, VALUE args, VALUE data, VALUE replyTypeName, VALUE replyType, VALUE replyData)
{
smokeruby_object *o = value_obj_info(data);
if (o == 0 || o->ptr == 0) {
return Qfalse;
}
TQByteArray * dataArray = (TQByteArray*) o->ptr;
o = value_obj_info(replyData);
if (o == 0 || o->ptr == 0) {
return Qfalse;
}
TQByteArray * replyArray = (TQByteArray*) o->ptr;
InvokeDCOPSlot dcopSlot(target, rb_intern(StringValuePtr(slotname)), args, *dataArray, replyTypeName, replyType, *replyArray);
dcopSlot.next();
return Qtrue;
}
static VALUE
dcop_call(int argc, VALUE * argv, VALUE /*self*/)
{
TQCString fun(StringValuePtr(argv[1]));
VALUE args = argv[2];
bool useEventLoop = (argv[argc-2] == Qtrue ? true : false);
int timeout = NUM2INT(argv[argc-1]);
DCOPCall dcopCall(argv[0], fun, argc-5, argv+3, args, useEventLoop, timeout);
dcopCall.next();
return *(dcopCall.var());
}
static VALUE
dcop_send(int argc, VALUE * argv, VALUE /*self*/)
{
TQCString fun(StringValuePtr(argv[1]));
VALUE args = argv[2];
VALUE result = Qnil;
DCOPSend dcopSend(argv[0], fun, argc-3, argv+3, args, &result);
dcopSend.next();
return result;
}
static VALUE
new_kde(int argc, VALUE * argv, VALUE klass)
{
VALUE instance = new_qt(argc, argv, klass);
if (rb_funcall(kde_module, rb_intern("hasDCOPSignals"), 1, klass) == Qtrue) {
VALUE signalNames = rb_funcall(kde_module, rb_intern("getDCOPSignalNames"), 1, klass);
for (long index = 0; index < RARRAY_LEN(signalNames); index++) {
VALUE signal = rb_ary_entry(signalNames, index);
rb_define_method(klass, StringValuePtr(signal), (VALUE (*) (...)) k_dcop_signal, -1);
}
}
if ( rb_funcall(kde_module, rb_intern("hasDCOPSlots"), 1, klass) == Qtrue
|| rb_funcall(kde_module, rb_intern("hasDCOPSignals"), 1, klass) == Qtrue )
{
VALUE dcop_object = rb_funcall(kde_module, rb_intern("createDCOPObject"), 1, instance);
if (dcop_object != Qnil) {
rb_define_method(klass, "interfaces", (VALUE (*) (...)) dcop_interfaces, 0);
rb_define_method(klass, "functions", (VALUE (*) (...)) dcop_functions, 0);
rb_define_method(klass, "connectDCOPSignal", (VALUE (*) (...)) dcop_connect_signal, 5);
rb_define_method(klass, "disconnectDCOPSignal", (VALUE (*) (...)) dcop_disconnect_signal, 4);
}
}
return instance;
}
#if TDE_VERSION >= 0x030200
static VALUE
tdeconfigskeletonitem_immutable(VALUE self)
{
smokeruby_object *o = value_obj_info(self);
TDEConfigSkeletonItem * item = (TDEConfigSkeletonItem *) o->ptr;
return item->isImmutable() ? Qtrue : Qfalse;
}
static VALUE
config_additem(int argc, VALUE * argv, VALUE self)
{
smokeruby_object *o = value_obj_info(self);
TDEConfigSkeleton * config = (TDEConfigSkeleton *) o->ptr;
if (argc < 1 || argc > 2) {
rb_raise(rb_eArgError, "wrong number of arguments(%d for 2)\n", argc);
}
if (TYPE(argv[0]) != T_DATA) {
rb_raise(rb_eArgError, "wrong argument type, expected KDE::ConfigSkeletonItem\n", argc);
}
smokeruby_object *c = value_obj_info(argv[0]);
TDEConfigSkeletonItem * item = (TDEConfigSkeletonItem *) c->ptr;
if (argc == 1) {
config->addItem(item);
} else {
config->addItem(item, TQString(StringValuePtr(argv[1])));
}
return self;
}
#endif
static VALUE
konsole_part_startprogram(VALUE self, VALUE value_program, VALUE value_args)
{
smokeruby_object * o = value_obj_info(self);
TerminalInterface * t = static_cast<TerminalInterface*>(((KParts::ReadOnlyPart*) o->ptr)->tqt_cast("TerminalInterface"));
TQStrList *args = new TQStrList;
if (value_args != Qnil) {
for (long i = 0; i < RARRAY_LEN(value_args); i++) {
VALUE item = rb_ary_entry(value_args, i);
args->append(TQString::fromLatin1(StringValuePtr(item), RSTRING_LEN(item)));
}
}
t->startProgram(TQString::fromLatin1(StringValuePtr(value_program)), args);
return self;
}
static VALUE
konsole_part_showshellindir(VALUE self, VALUE value_dir)
{
smokeruby_object * o = value_obj_info(self);
TerminalInterface * t = static_cast<TerminalInterface*>(((KParts::ReadOnlyPart*) o->ptr)->tqt_cast("TerminalInterface"));
t->showShellInDir(StringValuePtr(value_dir));
return self;
}
static VALUE
konsole_part_sendinput(VALUE self, VALUE value_text)
{
smokeruby_object * o = value_obj_info(self);
TerminalInterface * t = static_cast<TerminalInterface*>(((KParts::ReadOnlyPart*) o->ptr)->tqt_cast("TerminalInterface"));
t->sendInput(StringValuePtr(value_text));
return self;
}
#if TDE_VERSION >= 0x030500
static VALUE
konsole_part_setautostartshell(VALUE self, VALUE enabled)
{
smokeruby_object * o = value_obj_info(self);
ExtTerminalInterface * t = static_cast<ExtTerminalInterface*>(((KParts::ReadOnlyPart*) o->ptr)->tqt_cast("ExtTerminalInterface"));
t->setAutoStartShell(enabled == Qtrue);
return self;
}
static VALUE
konsole_part_setautodestroy(VALUE self, VALUE enabled)
{
smokeruby_object * o = value_obj_info(self);
ExtTerminalInterface * t = static_cast<ExtTerminalInterface*>(((KParts::ReadOnlyPart*) o->ptr)->tqt_cast("ExtTerminalInterface"));
t->setAutoDestroy(enabled == Qtrue);
return self;
}
#endif
void
Init_korundum()
{
if (qt_internal_module != Qnil) {
rb_fatal("require 'Korundum' must not follow require 'Qt'\n");
return;
}
set_new_kde(new_kde);
#if TDE_VERSION >= 0x030200
set_tdeconfigskeletonitem_immutable(tdeconfigskeletonitem_immutable);
#endif
set_kde_resolve_classname(kde_resolve_classname);
// The Qt extension is linked against libsmoketqt.so, but Korundum links against
// libsmoketde.so only. Specifying both a 'require Qt' and a 'require Korundum',
// would give a link error (see the rb_fatal() error above).
// So call the Init_qtruby() initialization function explicitely, not via 'require Qt'
// (Qt.o is linked into libtqtruby.so, as well as the Qt.so extension).
Init_qtruby();
install_handlers(KDE_handlers);
kde_internal_module = rb_define_module_under(kde_module, "Internal");
rb_define_singleton_method(kde_module, "dcop_process", (VALUE (*) (...)) dcop_process, 7);
rb_define_singleton_method(kde_module, "dcop_call", (VALUE (*) (...)) dcop_call, -1);
rb_define_singleton_method(kde_module, "dcop_send", (VALUE (*) (...)) dcop_send, -1);
#if TDE_VERSION >= 0x030200
rb_define_method(tdeconfigskeleton_class, "addItem", (VALUE (*) (...)) config_additem, -1);
#endif
rb_define_method(konsole_part_class, "startProgram", (VALUE (*) (...)) konsole_part_startprogram, 2);
rb_define_method(konsole_part_class, "showShellInDir", (VALUE (*) (...)) konsole_part_showshellindir, 1);
rb_define_method(konsole_part_class, "sendInput", (VALUE (*) (...)) konsole_part_sendinput, 1);
#if TDE_VERSION >= 0x030500
rb_define_method(konsole_part_class, "setAutoStartShell", (VALUE (*) (...)) konsole_part_setautostartshell, 1);
rb_define_method(konsole_part_class, "autoStartShell=", (VALUE (*) (...)) konsole_part_setautostartshell, 1);
rb_define_method(konsole_part_class, "setAutoDestroy", (VALUE (*) (...)) konsole_part_setautodestroy, 1);
rb_define_method(konsole_part_class, "autoDestroy=", (VALUE (*) (...)) konsole_part_setautodestroy, 1);
#endif
rb_require("KDE/korundum.rb");
}
};