/*************************************************************************** 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 #include #include #include #include #include #include #include #include #include #if TDE_VERSION >= 0x030200 #include #endif #include #include #include #include #include #include #include #include #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") == 0) { TQMap temp((const TQMap&) *((TQMap*) 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") == 0) { TQMap temp; *stream >> temp; stack[i].s_voidp = new TQMap(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") { // Special case TQValueList as a TQDataStream marshaller // isn't in the Smoke runtime TQValueList valuelist; ds >> valuelist; _result = rb_ary_new(); for (TQValueListIterator 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") { // And special case this type too TQValueList propertyList; ds >> propertyList; _result = rb_ary_new(); for (TQValueListIterator it = propertyList.begin(); it != propertyList.end(); ++it) { rb_ary_push(_result, rb_str_new2((const char *) *it)); } } else if (replyType == "TQValueList") { // And special case this type too TQValueList propertyList; ds >> propertyList; _result = rb_ary_new(); for (TQValueListIterator it = propertyList.begin(); it != propertyList.end(); ++it) { rb_ary_push(_result, INT2NUM(*it)); } } else if (replyType == "TQMap") { // And another.. TQMap actionMap; ds >> actionMap; _result = rb_hash_new(); TQMap::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") == 0 && TYPE(result) == T_ARRAY ) { // Special case TQValueList as a TQDataStream marshaller // isn't in the Smoke runtime TQValueList 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") == 0 && TYPE(result) == T_ARRAY ) { // And special case this type too TQValueList 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") == 0 && TYPE(result) == T_HASH ) { // And another.. TQMap 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(((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(((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(((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(((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(((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 libqtruby.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"); } };