view dbus-d/dsrc/org/freedesktop/dbus/DBus.d @ 6:963d271f7c25

disabled event stuff, to have working example
author Frank Benoit <benoit@tionex.de>
date Sun, 28 Oct 2007 19:35:38 +0100
parents 7c2c75740370
children 44c987465a20
line wrap: on
line source

/*
 * Copyright (C) 2007  Frank Benoit
 *
 * Licensed under the Academic Free License version 2.1
 * 
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
module org.freedesktop.dbus.DBus;

import tango.io.Stdout;
import tango.core.Exception;
import tango.core.Thread;
import tango.core.Traits;
import tango.text.convert.Integer;
import tango.text.Util;

public import tango.stdc.stringz : fromUtf8z;
//public import tango.core.Tuple;
//public import org.freedesktop.dbus.DBusInterface;

import org.freedesktop.dbus.c.Connection;
import org.freedesktop.dbus.c.Errors;
import org.freedesktop.dbus.c.Message;
import org.freedesktop.dbus.c.Bus;
import org.freedesktop.dbus.c.PendingCall;
import org.freedesktop.dbus.c.Shared;
import org.freedesktop.dbus.c.Protocol;
import org.freedesktop.dbus.c.Types;
import org.freedesktop.dbus.c.Memory : dbus_free;
import org.freedesktop.dbus.Struct;
import org.freedesktop.dbus.Variant;

//version=CD;

typedef char[] DBusObjPath;
typedef char[] DBusSignature;

private void notImplemented( char[] msg = null ){
    throw new TracedException( "not yet implemented " ~ msg );
}

private void ensure( bool cond, char[][] msg ... ){
    if( !cond ){
        char[200] buf;
        char[] formattedMsg = layout( buf, msg );
        throw new TracedException( "ensure failed: "~formattedMsg );
    }
}

private void ensureDBus( dbus_bool_t cond ){
    if( !cond ){
        throw new TracedException( "ensureDBus" );
    }
}


alias DBusHandlerResult
    function(
        DBusConnection* conn,
        DBusMessage* message,
        void* user_data )
    DBusHandler;


char[]     [ ClassInfo ] registeredIntrospectionData;
DBusHandler[ ClassInfo ] registeredHandlers;

public void registerIntrospectionData( ClassInfo ci, char[] data ){
    registeredIntrospectionData[ ci ] = data;
}
public void registerHandler( ClassInfo ci, DBusHandler handler ){
    registeredHandlers[ ci ] = handler;
}

public class DBusObjectImpl {
	static uint sInstCounter = 0;
	char[] instName;
	this(){
		//Stdout.formatln( "DBusObject ctor" );
		int cnt;
		synchronized{
			cnt = sInstCounter++;
		}
		instName = '/' ~ replace( this.classinfo.name.dup, '.', '/' ) ~ '/'  ~ .toUtf8( cnt );
		//Stdout.formatln( "DBusObject instname={}", instName );
	}

	public char[] getDBusInstanceName(){
		return instName;
	}

    public char[] Introspect(){
        char[] res = "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
        "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
        "<node>\n";
        bool[ ClassInfo ] all;
        void appendInterfaces( Interface[] intfs ){
            foreach( intf; intfs ){
                if( intf.classinfo.interfaces.length > 0 ){
                    appendInterfaces( intf.classinfo.interfaces );
                }
                all[ intf.classinfo ] = true;
            }
        }
        appendInterfaces( this.classinfo.interfaces );
        foreach( intf; all.keys ){
            char[]* intro = intf in registeredIntrospectionData;
            if( intro !is null ){
                res ~= *intro;
            }
        }
        DBusObjectImpl[] childs = getChildNodes();
        foreach( child; childs ){
            res ~= "  <node name=\""~child.instName~"\"/>\n";
        }
        res ~= "</node>\n";
        //Stdout.formatln( "DBus.d:{} Introspect: {}", __LINE__, res );
        return res;
    }

    public DBusObjectImpl[] getChildNodes(){
        return null;
    }

    private static extern(C) DBusHandlerResult VTableMessageFunction(
        DBusConnection* conn,
        DBusMessage* message,
        void* user_data )
    {
        char[METHOD_SIG_MAXLENGTH] buf;
        DBusObjectImpl o = cast(DBusObjectImpl)cast(Object)user_data;

        // find all interfaces ...
        bool[ ClassInfo ] all;
        void findAllInterfaces( Interface[] intfs ){
            foreach( intf; intfs ){
                findAllInterfaces( intf.classinfo.interfaces );
                all[ intf.classinfo ] = true;
            }
        }
        ClassInfo oci = o.classinfo;
        do{
            findAllInterfaces( oci.interfaces );
            oci = oci.base;
        } while( oci !is null );

        // call all existing handlers until message handled
        foreach( intf; all.keys ){
            DBusHandler* handler = intf in registeredHandlers;
            if( handler !is null ){
                DBusHandlerResult res =
                    (*handler)( conn, message, user_data );
                if( res == DBusHandlerResult.DBUS_HANDLER_RESULT_HANDLED ){
                    return DBusHandlerResult.DBUS_HANDLER_RESULT_HANDLED;
                }
            }
        }
        return DBusHandlerResult.DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

    private static extern(C) void VTableUnregisterFunction(
        DBusConnection  *connection,
        void* user_data )
    {
        DBusObjectImpl o = cast(DBusObjectImpl)cast(Object)user_data;
        objectLocker.remove( o );
    }

    private static DBusObjectPathVTable dbusObjectPathVTable = {
        message_function    : & VTableMessageFunction,
        unregister_function : & VTableUnregisterFunction
    };

 	public void   registerDBusObject( DBusConnection * conn ){
 		ensureDBus( dbus_connection_register_object_path(
         		conn,
         		XtoUtf8z( getDBusInstanceName() ),
         		& dbusObjectPathVTable,
 	        	cast(void*)cast(Object)this ));
 	    objectLocker[ this ] = true;
 	}

}

bool[ DBusObjectImpl ] objectLocker;


class DBusConnectionImpl {
	DBusConnection* conn;
	
	public this( char[] addr ){
		DBusError err;
		int trial = 0;
		while( true ){
			trial++;
			dbus_error_init(&err);

			scope(exit) 
				if( dbus_error_is_set(&err) ){
					dbus_error_free(&err);
				}
			
			conn = dbus_connection_open_private( XtoUtf8z(addr), & err );
			
			if( !dbus_error_is_set(&err) && conn !is null ){
				return;
			}
			if( trial > 10 ){
				if( dbus_error_is_set(&err) ){
					Stdout.formatln( "Connect Error ({})\n", fromUtf8z( err.message)); 
					throw new Exception("");
				}
				if( conn is null ){
					throw new IOException("connecting not successful");
				}
			}
			else{
				Thread.sleep( 0.1 );
			}
		}
	}
	
/+	public DBusInterface getRemoteObject( char[] path ){
		return null;
	}+/
	public void mainLoop(){
		//Stdout.formatln( "   D mainloop starting" );
		while (dbus_connection_read_write_dispatch (conn, -1)){
			//Stdout.formatln( "   D mainloop" );
     			// empty loop body
		}
		//Stdout.formatln( "   D mainloop stop" );
		return;

		DBusError err;
		dbus_error_init(&err);
		//CBus.dbus_bus_request_name(conn, XtoUtf8z("test.method.caller"), DBUS_NAME_FLAG_REPLACE_EXISTING , &err);
		if( dbus_error_is_set(&err) ){
			Stdout.formatln( "Name Error ({})\n", fromUtf8z( err.message)); 
      			dbus_error_free(&err);
      			throw new Exception("");
		}
	}
	public void disconnect(){
		if( conn !is null ){
			dbus_connection_close( conn );
		}
	}
}

class DirectConnection : DBusConnectionImpl {
	public this(  char[] addr  ){
		super( addr );
	}
}



class DBusExecutionException : TracedException {
	public this( char[] msg ){
		super( msg );
	}
}

template isDBusSimpleType(T){
    const bool isDBusSimpleType =
        is( T == bool   ) ||
        is( T == byte   ) ||
        is( T == short  ) ||
        is( T == ushort ) ||
        is( T == int    ) ||
        is( T == uint   ) ||
        is( T == long   ) ||
        is( T == ulong  ) ||
        is( T == double );
}


private void getCallValue( T )( DBusMessageIter* iter, out T t ){
    version(CD) pragma( msg, "getCallValue: "~T.stringof );

    void testType( int type ){
        int expected = dbus_message_iter_get_arg_type( iter );
        ensure( expected == type, "getCallType(%0) type does not match %1 '%2' !=  %3 '%4'", T.stringof, toUtf8(type), [cast(char)type], toUtf8(expected), [cast(char)expected] );
    }

    static if( false ){}
    else static if( isAssocArrayType!(T) ){
        version(CD) pragma( msg, "getCallValue: assic "~typeof(T).stringof );
        testType( DBUS_TYPE_ARRAY );
        DBusMessageIter sub;
        dbus_message_iter_recurse( iter, &sub );
        dbus_message_iter_next( iter );

        typeof(T.keys  [0]) key;
        typeof(T.values[0]) value;
        while( dbus_message_iter_get_arg_type( &sub ) != DBUS_TYPE_INVALID ){
            ensure( dbus_message_iter_get_arg_type( &sub ) == DBUS_TYPE_DICT_ENTRY, "getCallType type does not match for Assoc" );
            DBusMessageIter entry;
            dbus_message_iter_recurse( &sub, &entry );
            dbus_message_iter_next( &sub );

            getCallValue( &entry, key );
            getCallValue( &entry, value );
            t[ key ] = value;
        }
    }
    else static if( is( T == char[] )){ // char[] nicht als dyn array, sondern als String
        testType( DBUS_TYPE_STRING );
        char* ptr;
        dbus_message_iter_get_basic( iter, & ptr );
        dbus_message_iter_next( iter );
        t = fromUtf8z( ptr );
    }
    else static if( isDynamicArrayType!(T) ){
        testType( DBUS_TYPE_ARRAY );
        DBusMessageIter sub;
        dbus_message_iter_recurse( iter, &sub );
        dbus_message_iter_next( iter );
        static if( is( typeof(T[0]) == bool )) {
            version(CD) pragma( msg, "getCallValue: basic type "~typeof(T[0]).stringof );
            if( dbus_message_iter_get_arg_type( &sub ) != DBUS_TYPE_INVALID ){
                // special processing because of dbus bools are 4bytes vs. D bool is 1byte.
                dbus_bool_t* ptr;
                int n_elements;
                dbus_message_iter_get_fixed_array( & sub, &ptr, & n_elements );
                t.length = n_elements;
                foreach( idx, v; ptr[ 0 .. n_elements ] ){
                    t[ idx ] = ( v != 0 );
                }
            }
        }
        else static if( isDBusSimpleType!( typeof(T[0]) )) {
            version(CD) pragma( msg, "getCallValue: basic type "~typeof(T[0]).stringof );
            if( dbus_message_iter_get_arg_type( &sub ) != DBUS_TYPE_INVALID ){
                typeof(T[0])* ptr;
                int n_elements;
                dbus_message_iter_get_fixed_array( & sub, &ptr, & n_elements );
                t = ptr[ 0 .. n_elements ].dup;
            }
        }
        else{
            version(CD) pragma( msg, "getCallValue: composed type "~typeof(T[0]).stringof );
            uint idx = 0;
            while( dbus_message_iter_get_arg_type( &sub ) != DBUS_TYPE_INVALID ){
                t.length = t.length +1;
                getCallValue( &sub, t[idx] );
                // dbus_message_iter_next( &sub ) : is done in getCallValue for element.
                idx++;
            }
        }
    }
    else static if( isDBusSimpleType!(T) ) {
        testType( DBusTypeIdFromDType!(T) );
        dbus_message_iter_get_basic( iter, & t );
        dbus_message_iter_next( iter );
    }
    else static if( is( T : DBusObjectImpl )){
        testType( DBusTypeIdFromDType!(T) );
        static assert( false );
    }
    else static if( is( T == DBusVariant ) ){
        DBusMessageIter sub;
        dbus_message_iter_recurse( iter, &sub );
        dbus_message_iter_next( iter );
        char* sig = dbus_message_iter_get_signature( &sub );
        scope(exit) dbus_free( sig );
        char[] sigText = fromUtf8z( sig );
        t = createDBusVariant( &sub, sigText );
    }
    else static if( is( T == Struct!(typeof(T.t)) ) ){
        // Stdout.formatln( "DBus getCallValue {}", __LINE__ );
        DBusMessageIter sub;
        dbus_message_iter_recurse( iter, &sub );
        dbus_message_iter_next( iter );
        foreach( idx, el; t.t ){
            getCallValue( & sub, t.t[idx] );
        }
    }
    else{
        pragma( msg, "getCallValue: "~typeof(t).stringof );
        static assert(false, typeof(t).stringof );
    }
}

public Struct!(T) getCallValues( T... )( DBusMessage* message ){
    version(CD) pragma( msg, "getCallValues: "~T.stringof );
    Struct!(T) res;
    DBusMessageIter args;
    dbus_message_iter_init( message, &args);
    foreach( idx, t; T ){
        getCallValue( &args, res.t[idx] );
    }
    return res;
}


private void _appendMsgParam( DBusMessageIter* iter, int type, void* ptr ){
	if ( !dbus_message_iter_append_basic( iter, type, ptr ) ) {
		throw new TracedException( "Cannot append argument to iterator" );
	}
}


void appendMsgParam( DBusConnection* conn, DBusMessageIter* iter, bool value ){
    dbus_bool_t val = value;
    _appendMsgParam( iter, DBUS_TYPE_BOOLEAN, &val );
}
void appendMsgParam( DBusConnection* conn, DBusMessageIter* iter, byte value ){
    dbus_bool_t val = value;
    _appendMsgParam( iter, DBUS_TYPE_BYTE, &val );
}
void appendMsgParam( DBusConnection* conn, DBusMessageIter* iter, short value ){
    dbus_int16_t val = value;
    _appendMsgParam( iter, DBUS_TYPE_INT16, &val );
}
void appendMsgParam( DBusConnection* conn, DBusMessageIter* iter, ushort value ){
    dbus_uint16_t val = value;
    _appendMsgParam( iter, DBUS_TYPE_UINT16, &val );
}
void appendMsgParam( DBusConnection* conn, DBusMessageIter* iter, int value ){
    dbus_int32_t val = value;
    _appendMsgParam( iter, DBUS_TYPE_INT32, &val );
}
void appendMsgParam( DBusConnection* conn, DBusMessageIter* iter, uint value ){
    dbus_int32_t val = value;
    _appendMsgParam( iter, DBUS_TYPE_UINT32, &val );
}
void appendMsgParam( DBusConnection* conn, DBusMessageIter* iter, long value ){
    dbus_int64_t val = value;
    _appendMsgParam( iter, DBUS_TYPE_INT64, &val );
}
void appendMsgParam( DBusConnection* conn, DBusMessageIter* iter, ulong value ){
    dbus_uint64_t val = value;
    _appendMsgParam( iter, DBUS_TYPE_UINT64, &val );
}
void appendMsgParam( DBusConnection* conn, DBusMessageIter* iter, double value ){
    double val = value;
    _appendMsgParam( iter, DBUS_TYPE_DOUBLE, &val );
}



private const char[] nullChar = "\0";
char* XtoUtf8z (char[] s) {
  if (s.length == 0)
    return nullChar.ptr;
  if (s[$-1] == '\0')
    return s.ptr;
  s ~= '\0';
  return s.ptr;
}

void appendMsgParam( DBusConnection* conn, DBusMessageIter* iter, char[] value ){
    //toUtf8z( value ); // toUtf8z return null for a null char[], but a valid ptr is needed.
    char* val = XtoUtf8z( value );
    _appendMsgParam( iter, DBUS_TYPE_STRING, &val );
}

void appendMsgParamObject( DBusConnection* conn, DBusMessageIter* iter, DBusObjectImpl value ){
    value.registerDBusObject( conn );
    char* path = XtoUtf8z( value.getDBusInstanceName());
    _appendMsgParam( iter, DBUS_TYPE_OBJECT_PATH, &path );
}

void appendMsgParamVariant( DBusConnection* conn, DBusMessageIter* iter, DBusVariant value ){
    DBusMessageIter sub;
    ensureDBus( dbus_message_iter_open_container( iter, DBUS_TYPE_VARIANT, XtoUtf8z( value.getSig() ), &sub ));
    if( DBusVariantValueBool v = cast(DBusVariantValueBool)value ){
        appendMsgParam( conn, &sub, v.value );
    }
    else if( DBusVariantValueByte v = cast(DBusVariantValueByte)value ){
        appendMsgParam( conn, &sub, v.value );
    }
    else if( DBusVariantValueShort v = cast(DBusVariantValueShort)value ){
        appendMsgParam( conn, &sub, v.value );
    }
    else if( DBusVariantValueUShort v = cast(DBusVariantValueUShort)value ){
        appendMsgParam( conn, &sub, v.value );
    }
    else if( DBusVariantValueInt v = cast(DBusVariantValueInt)value ){
        appendMsgParam( conn, &sub, v.value );
    }
    else if( DBusVariantValueUInt v = cast(DBusVariantValueUInt)value ){
        appendMsgParam( conn, &sub, v.value );
    }
    else if( DBusVariantValueLong v = cast(DBusVariantValueLong)value ){
        appendMsgParam( conn, &sub, v.value );
    }
    else if( DBusVariantValueULong v = cast(DBusVariantValueULong)value ){
        appendMsgParam( conn, &sub, v.value );
    }
    else if( DBusVariantValueDouble v = cast(DBusVariantValueDouble)value ){
        appendMsgParam( conn, &sub, v.value );
    }
    else if( DBusVariantValueString v = cast(DBusVariantValueString)value ){
        appendMsgParam( conn, &sub, v.value );
    }

    else if( DBusVariantStruct v = cast(DBusVariantStruct)value ){
        DBusMessageIter sub2;
        ensureDBus( dbus_message_iter_open_container( &sub, DBUS_TYPE_STRUCT, XtoUtf8z( v.getSig() ), &sub2 ));
        foreach( el; v.values ){
            appendMsgParamVariant( conn, &sub2, el );
        }
        ensureDBus( dbus_message_iter_close_container( &sub, &sub2 ));
    }
    else if( DBusVariantArray v = cast(DBusVariantArray)value ){
        DBusMessageIter sub2;
        ensureDBus( dbus_message_iter_open_container( &sub, DBUS_TYPE_ARRAY, XtoUtf8z( v.getSig() ), &sub2 ));
        foreach( el; v.values ){
            appendMsgParamVariant( conn, &sub2, el );
        }
        ensureDBus( dbus_message_iter_close_container( &sub, &sub2 ));
    }
    else if( DBusVariantMap v = cast(DBusVariantMap)value ){
        DBusMessageIter sub2;
        ensureDBus( dbus_message_iter_open_container( &sub, DBUS_TYPE_ARRAY, XtoUtf8z( v.getSig() ), &sub2 ));
        foreach( entry; v.values ){
            DBusMessageIter el;
            ensureDBus( dbus_message_iter_open_container( &sub2, DBUS_TYPE_DICT_ENTRY, null, &el ));
            appendMsgParamVariant( conn, &el, entry.key );
            appendMsgParamVariant( conn, &el, entry.value );
            ensureDBus( dbus_message_iter_close_container( &sub2, &el ));
        }
        ensureDBus( dbus_message_iter_close_container( &sub, &sub2 ));
    }
    else{
        Stdout.formatln( "appendMsgParamVariant not handled case for sig: {}", value.getSig() ).flush(); 
        ensure( false, "appendMsgParamVariant not handled case for sig: %0", value.getSig() );
    }
    ensureDBus( dbus_message_iter_close_container( iter, &sub ));
}

void appendMsgParamArray( T )( DBusConnection* conn, DBusMessageIter* iter, T[] value ){
    //pragma( msg, "appendMsgParamArray: "~typeof(T).stringof );
	DBusMessageIter sub;
    //Stderr.formatln( "DBus {} {}", __LINE__,createDBusSignature!(T) ).flush;
	ensureDBus( dbus_message_iter_open_container( iter, DBUS_TYPE_ARRAY, XtoUtf8z( createDBusSignature!(T)), &sub ));
	foreach( item; value ){
        appendItem!( typeof( item ))( conn, &sub, item );
	}
	ensureDBus( dbus_message_iter_close_container( iter, &sub ));
}

void appendMsgParamStruct( T... )( DBusConnection* conn, DBusMessageIter* iter, T t ){
    //pragma( msg, "appendMsgParamStruct: "~typeof(T).stringof );
    DBusMessageIter sub;
    ensureDBus( dbus_message_iter_open_container( iter, DBUS_TYPE_STRUCT, XtoUtf8z( createDBusSignature!(T)), &sub ));
    foreach( item; t ){
        appendItem( conn, &sub, item );
    }
    ensureDBus( dbus_message_iter_close_container( iter, &sub ));
}

void appendMsgParamAssoc( K, V )( DBusConnection* conn, DBusMessageIter* iter, V[K] map ){
    //pragma( msg, "appendMsgParamAssoc: key="~typeof(K).stringof~" value="~typeof(V).stringof );
    DBusMessageIter sub;
    ensureDBus( dbus_message_iter_open_container( iter, DBUS_TYPE_ARRAY,
        XtoUtf8z( createDBusSignature!(V[K])[1..$] ), // "{ss}" this is without the leading 'a' for the array.
        &sub ));
    foreach( key, value; map ){
        DBusMessageIter el;
        ensureDBus( dbus_message_iter_open_container( &sub, DBUS_TYPE_DICT_ENTRY, null, &el ));
        appendItem!( K )( conn, &el, key );
        appendItem!( V )( conn, &el, value );
        ensureDBus( dbus_message_iter_close_container( &sub, &el ));
    }
    ensureDBus( dbus_message_iter_close_container( iter, &sub ));
}

template createDBusSignature(T...){
    //pragma( msg, "createDBusSignature(T) : T="~typeof(T).stringof );
    static if( T.length > 1 ){
        //pragma( msg, "R.length>1" );
        const char[] createDBusSignature = createDBusSignature!(T[0]) ~ createDBusSignature!(T[1..$]);
    }
    else static if( isAssocArrayType!(T[0])   ){
        //pragma( msg, "createDBusSignature: key="~typeof(T.keys[0] ).stringof~" value="~typeof(T.values[0] ).stringof );
        const char[] createDBusSignature =
            DBUS_TYPE_ARRAY_AS_STRING ~
            DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING ~
            createDBusSignature!( typeof(T[0].keys[0] )) ~
            createDBusSignature!( typeof(T[0].values[0] )) ~
            DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
    }
    else static if( isDynamicArrayType!(T[0]) ){
        //pragma( msg, "createDBusSignature: arr="~typeof(T[0]).stringof );
        static if( is( T[0] == char[] ) ){
            const char[] createDBusSignature = DBUS_TYPE_STRING_AS_STRING;
        }
        else{
            const char[] createDBusSignature = DBUS_TYPE_ARRAY_AS_STRING ~ createDBusSignature!(typeof(T[0][0]));
        }
    }
	else static if( is( T[0] == bool        ) ){ const char[] createDBusSignature = DBUS_TYPE_BOOLEAN_AS_STRING; }
	else static if( is( T[0] == byte        ) ){ const char[] createDBusSignature = DBUS_TYPE_BYTE_AS_STRING;   }
	else static if( is( T[0] == short       ) ){ const char[] createDBusSignature = DBUS_TYPE_INT16_AS_STRING;  }
	else static if( is( T[0] == ushort      ) ){ const char[] createDBusSignature = DBUS_TYPE_UINT16_AS_STRING; }
	else static if( is( T[0] == int         ) ){ const char[] createDBusSignature = DBUS_TYPE_INT32_AS_STRING;  }
	else static if( is( T[0] == uint        ) ){ const char[] createDBusSignature = DBUS_TYPE_UINT32_AS_STRING; }
	else static if( is( T[0] == long        ) ){ const char[] createDBusSignature = DBUS_TYPE_INT64_AS_STRING;  }
	else static if( is( T[0] == ulong       ) ){ const char[] createDBusSignature = DBUS_TYPE_UINT64_AS_STRING; }
	else static if( is( T[0] == double      ) ){ const char[] createDBusSignature = DBUS_TYPE_DOUBLE_AS_STRING; }
    else static if( is( T[0] : DBusObjectImpl ) ){ const char[] createDBusSignature = DBUS_TYPE_OBJECT_PATH_AS_STRING; }
    else static if( is( T[0] == DBusVariant ) ){ const char[] createDBusSignature = DBUS_TYPE_VARIANT_AS_STRING; }
    else static if( is( T[0] == Struct!(typeof(T[0].t)) )){
        //pragma( msg, "createDBusSignature Struct: t="~typeof(T[0].t).stringof );
        const char[] createDBusSignature =
            DBUS_STRUCT_BEGIN_CHAR_AS_STRING ~
            createDBusSignature!( typeof(T[0].t) ) ~
            DBUS_STRUCT_END_CHAR_AS_STRING;
    }
	else {
        pragma( msg, "createDBusSignature(T) : T="~typeof(T).stringof );
        static assert( false );
    }
}

template DBusTypeIdFromDType(T){
    //pragma( msg, "DBusTypeIdFromDType: "~typeof(T).stringof );
    static if(false){}
    else static if( is( T == char[]      ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_STRING; }
    else static if( isDynamicArrayType!(T) ){ const int DBusTypeIdFromDType = DBUS_TYPE_ARRAY; }
    else static if( is( T == bool        ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_BOOLEAN; }
    else static if( is( T == byte        ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_BYTE; }
    else static if( is( T == short       ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_INT16; }
    else static if( is( T == ushort      ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_UINT16; }
    else static if( is( T == int         ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_INT32; }
    else static if( is( T == uint        ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_UINT32; }
    else static if( is( T == long        ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_INT64; }
    else static if( is( T == ulong       ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_UINT64; }
    else static if( is( T == double      ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_DOUBLE; }
    else static if( is( T : DBusObjectImpl    ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_OBJECT_PATH; }
    else static if( is( T == DBusVariant ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_OBJECT_PATH; }
    else static if( is( T == Struct!(typeof(T.t)) ) ){ const int DBusTypeIdFromDType = DBUS_TYPE_OBJECT_PATH; }
	else static assert(false);
}

void appendItem( T... )( DBusConnection* conn, DBusMessageIter * args, T t ){
    //pragma( msg, ">>>appendItem input: "~typeof(t).stringof );
    //Stderr.formatln( "DBus {} {}", __LINE__,T[0].stringof ).flush;
    static if( T.length > 1 ){
        //pragma( msg, "T.length>1" );
        appendItem(T[0])( conn, args, t[0] );
        appendItem(T[1..$])( conn, args, t[1..$] );
    }
    else static if( isAssocArrayType!(T[0]) ){
        //pragma( msg, ">>> appendItem: assoc" );
        //Stdout.formatln( "DBus {}", __LINE__ );
        appendMsgParamAssoc( conn, args, t[0] );
    }
    else static if( is( T[0] == char[] )){ // char[] nicht als dyn array, sondern als String
        //pragma( msg, ">>> appendItem: char[]" );
        appendMsgParam( conn, args, t[0]);
    }
    else static if( isDynamicArrayType!(T[0]) ){
        //pragma( msg, ">>> appendItem: []" );
        //Stdout.formatln( "DBus {}", __LINE__ );
        appendMsgParamArray( conn, args, t[0] );
    }
	else static if( isDBusSimpleType!( T[0] ) ) {
        //pragma( msg, ">>> appendItem: integral" );
		appendMsgParam( conn, args, t[0] );
	}
	else static if( is( T[0] : DBusObjectImpl )){
        //pragma( msg, ">>> appendItem: interface/class" );
		appendMsgParamObject( conn, args, cast(DBusObjectImpl)t[0]);
	}
    else static if( is( T[0] == DBusVariant ) ){
        appendMsgParamVariant( conn, args, t[0]);
    }
    else static if( is( T[0] == DBusObjPath ) ){
        //pragma( msg, ">>> appendItem: path" );
        Stdout.formatln( "DBus {}", __LINE__ );
        notImplemented();
    }
    else static if( is( T[0] == DBusSignature ) ){
        //pragma( msg, ">>> appendItem: sig" );
        Stdout.formatln( "DBus {}", __LINE__ );
        notImplemented();
    }
    else static if( is( T[0] == Struct!(typeof(T[0].t)) ) ){
        //pragma( msg, ">>> appendItem: struct" );
        appendMsgParamStruct( conn, args, t[0].t );
        //pragma( msg, "struct: "~typeof(t).stringof );
    }
	else{
        pragma( msg, "appendItem: "~typeof(t).stringof );
		static assert(false, typeof(t).stringof );
    }
}

void sendReply( DBusConnection* conn, DBusMessage* message ){

    Stdout.flush();
    Stderr.flush();

    DBusMessage* reply = dbus_message_new_method_return(message);
    scope(exit){
        dbus_message_unref(reply);
    }
    dbus_bool_t res = dbus_connection_send(conn, reply, null );
    dbus_connection_flush(conn);
    if( !res ){
        throw new TracedException( "Cannot send. Out of memory." );
    }
}

void sendReplyData( T... )( DBusConnection* conn, DBusMessage* message, Struct!(T) t ){

    Stdout.flush();
    Stderr.flush();

    //Stdout.formatln( "DBus {}", __LINE__ );
	DBusMessage* reply = dbus_message_new_method_return(message);
	scope(exit){
		dbus_message_unref(reply);
	}
    //Stdout.formatln( "DBus {}", __LINE__ );

	static if( t.t.length > 0 ){
		DBusMessageIter args;
		dbus_message_iter_init_append( reply, &args);
		foreach( t_item; t.t ){
        //Stdout.formatln( "DBus {}", __LINE__ );
        //pragma( msg, "sendReply t: "~typeof(t).stringof );
        //pragma( msg, "sendReply t_item: "~typeof(t_item).stringof );
			appendItem( conn, & args, t_item );
        //pragma( msg, "sendReply OK" );
		}
	}
    //Stdout.formatln( "DBus {}", __LINE__ );
	//Stdout.formatln( "reply sig : {}", fromUtf8z(dbus_message_get_signature(reply)));
	dbus_bool_t res = dbus_connection_send(conn, reply, null );
	dbus_connection_flush(conn);
	if( !res ){
		throw new TracedException( "Cannot send. Out of memory." );
	}
}

void sendException( DBusConnection* conn, DBusMessage* message, Exception e ){

    Stdout.flush();
    Stderr.flush();

    char* type;
    char[] msg = e.msg;
    if( IOException e2 = cast(IOException)e ){
        type = XtoUtf8z(DBUS_ERROR_IO_ERROR);
        msg = "IOException " ~ msg;
    }
    else{
        type = XtoUtf8z(DBUS_ERROR_FAILED);
    }
    if( TracedException te = cast(TracedException)e ){
        foreach( m; te ){
            msg ~= m ~ \n;
        }
    }

	DBusMessage* reply =
		dbus_message_new_error(message, type, XtoUtf8z("D Exception: " ~ msg) );
	scope(exit){
		dbus_message_unref(reply);
	}
	dbus_bool_t res = dbus_connection_send(conn, reply, null );
	dbus_connection_flush(conn);
	if( !res ){
		throw new TracedException( "Cannot send. Out of dbus-memory." );
	}
}

bool checkIntf( char[] intfName, DBusMessage* message ){
	char[] namedInterface = fromUtf8z( dbus_message_get_interface(message));
	if( namedInterface.length == 0 ){
		return true; // no interface named in call, so lets try
	}
	return namedInterface == intfName;
}

public const int METHOD_SIG_MAXLENGTH = 100;

char[] methodSignature( DBusMessage* message, char[] buf = null ){
	char[] name = fromUtf8z( dbus_message_get_member(message));
	if( buf.length < name.length ){
		buf.length = name.length;
	}
	buf[ 0 .. name.length ] = name;
	
	char[] sig = fromUtf8z( dbus_message_get_signature(message));
	int sz = name.length+sig.length+1;
	if( buf.length < sz ){
		buf.length= sz;
	}
    int type = dbus_message_get_type( message );
    switch( type ){
    case DBUS_MESSAGE_TYPE_METHOD_CALL:
        buf[name.length] = '|';
        break;
    case DBUS_MESSAGE_TYPE_SIGNAL:
        buf[name.length] = '>';
        break;
    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
    case DBUS_MESSAGE_TYPE_ERROR:
    case DBUS_MESSAGE_TYPE_INVALID:
    default:
        ensure( false, "methodSignature for unknown type %0", toUtf8(type) );
    }
	buf[name.length+1 .. sz ] = sig;
	return buf[0..sz];
}

char[] nextSingleCompleteTypeSig( char[] sigText, inout int ate = 0 ){
    if( sigText.length > ate || sigText.length == 0 ){
        return null;
    }
    char[] sig = sigText[ ate .. $ ];

    switch( sig[0] ){
    case 'y':
    case 'b':
    case 'n':
    case 'q':
    case 'i':
    case 'u':
    case 'x':
    case 't':
    case 'd':
    case 's':
    case 'o':
    case 'g':
    case 'v':
        ate = 1;
        return sig[0..1];
    default:
        ensure( sig.length > 1, "nextSingleCompleteTypeSig found %0 as a single type sig, which is unknown", sig[0..1] );
    }

    if( sig[0] == 'a' ){
        if( sig[ 1 ] == '{' ){
            int idx = 2;
            nextSingleCompleteTypeSig( sig, idx );
            nextSingleCompleteTypeSig( sig, idx );
            ensure( sig.length > idx, "nextSingleCompleteTypeSig 2" );
            ensure( sig[ idx ] == '}', "nextSingleCompleteTypeSig 3" );
            idx++;
            ate += idx;
            return sig[ 0 .. idx ];
        }
        else{
            int idx = 1;
            nextSingleCompleteTypeSig( sig, idx );
            ate += idx;
            return sig[ 0 .. idx ];
        }
    }
    else if( sig[0] == '(' ){
        int idx = 1;
        ensure( sig.length > idx, "nextSingleCompleteTypeSig 4" );
        while( sig[ idx ] != ')' ){
            nextSingleCompleteTypeSig( sig, idx );
            ensure( sig.length > idx, "nextSingleCompleteTypeSig 5" );
        }
        idx++;
        ate += idx;
        return sig[ 0 .. idx ];
    }
    assert(false);
}

DBusVariant createDBusVariant( DBusMessageIter * iter, char[] sigText ){
    if( sigText.length == 0 ){
        return null;
    }

    void getBasic( void* ptr ){
        dbus_message_iter_get_basic( iter, ptr );
        dbus_message_iter_next( iter );
    }

    switch( sigText[0] ){
    case 'y':
        {
            DBusVariantValueByte res = new DBusVariantValueByte();
            getBasic( & res.value );
            return res;
        }
    case 'b':
        {
            DBusVariantValueBool res = new DBusVariantValueBool();
            getBasic( & res.value );
            return res;
        }
    case 'n':
        {
            DBusVariantValueShort res = new DBusVariantValueShort();
            getBasic( & res.value );
            return res;
        }
    case 'q':
        {
            DBusVariantValueUShort res = new DBusVariantValueUShort();
            getBasic( & res.value );
            return res;
        }
    case 'i':
        {
            DBusVariantValueInt res = new DBusVariantValueInt();
            getBasic( & res.value );
            return res;
        }
    case 'u':
        {
            DBusVariantValueUInt res = new DBusVariantValueUInt();
            getBasic( & res.value );
            return res;
        }
    case 'x':
        {
            DBusVariantValueLong res = new DBusVariantValueLong();
            getBasic( & res.value );
            return res;
        }
    case 't':
        {
            DBusVariantValueULong res = new DBusVariantValueULong();
            getBasic( & res.value );
            return res;
        }
    case 'd':
        {
            DBusVariantValueDouble res = new DBusVariantValueDouble();
            getBasic( & res.value );
            return res;
        }
    case 's':
        {
            DBusVariantValueString res = new DBusVariantValueString();
            char* ptr;
            getBasic( & ptr );
            res.value = fromUtf8z( ptr );
            return res;
        }
    case 'v':
        {
            DBusVariantValueVariant res = new DBusVariantValueVariant();
            DBusMessageIter subIter;
            dbus_message_iter_recurse( iter, &subIter );
            dbus_message_iter_next( iter );
            res.value = createDBusVariant( & subIter, sigText[1..$] );
            return res;
        }
    case 'o': //"DBusObject"
        assert(false);
    case 'g': //"DBusSignature"
        assert(false);
    default:
        break;
    }
    if( sigText[0] == 'a' ){
        if( sigText.length > 1 && sigText[ 1 ] == '{' ){
            int idx = 2;
            char[] sigKey   = nextSingleCompleteTypeSig( sigText, idx );
            char[] sigValue = nextSingleCompleteTypeSig( sigText, idx );
            DBusVariantMap res = new DBusVariantMap( sigKey, sigValue );

            DBusMessageIter subIter;
            dbus_message_iter_recurse( iter, &subIter );
            dbus_message_iter_next( iter );

            while( dbus_message_iter_get_arg_type( &subIter ) != DBUS_TYPE_INVALID ){
                ensure( dbus_message_iter_get_arg_type( &subIter ) == DBUS_TYPE_DICT_ENTRY, "createDBusVariant 1" );
                DBusMessageIter entry;
                dbus_message_iter_recurse( &subIter, &entry );
                dbus_message_iter_next( &subIter );

                DBusVariantMapItem item;
                item.key   = createDBusVariant( & subIter, sigKey   );
                item.value = createDBusVariant( & subIter, sigValue );
                res.values ~= item;
            }
            return res;
        }
        else{
            int idx = 1;
            char[] sigElement = nextSingleCompleteTypeSig( sigText, idx );
            DBusVariantArray res = new DBusVariantArray( sigElement );

            DBusMessageIter subIter;
            dbus_message_iter_recurse( iter, &subIter );
            dbus_message_iter_next( iter );

            while( dbus_message_iter_get_arg_type( &subIter ) != DBUS_TYPE_INVALID ){
                DBusMessageIter entry;
                dbus_message_iter_recurse( &subIter, &entry );
                dbus_message_iter_next( &subIter );
                res.values ~= createDBusVariant( & subIter, sigElement );
            }
            return res;
        }
    }
    else if( sigText[0] == '(' ){

        DBusVariantStruct res = new DBusVariantStruct();

        int idx = 1;
        ensure( sigText.length > idx, "createDBusVariant 2" );

        DBusMessageIter subIter;
        dbus_message_iter_recurse( iter, &subIter );
        dbus_message_iter_next( iter );

        while( sigText[ idx ] != ')' ){
            char[] sigElement = nextSingleCompleteTypeSig( sigText, idx );
            ensure( sigText.length > idx, "createDBusVariant 3" );
            res.values ~= createDBusVariant( & subIter, sigElement );
        }
        return res;
    }
    assert(false);
}

T getVariantAsKnownType( T )( DBusVariant var ){
    T res;
    static if( false ){}
    else static if( isAssocArrayType!(T) ){
        version(CD) pragma( msg, "getVariantAsKnownType: assoc "~typeof(T).stringof );
        DBusVariantMap map = cast(DBusVariantMap)var;
        ensure( map !is null, "getVariantAsKnownType map is null" );
        typeof(T.keys  [0]) k;
        typeof(T.values[0]) v;
        foreach( value; map.values ){
            k = getVariantAsKnownType!( typeof(T.keys  [0]) )( value.key   );
            v = getVariantAsKnownType!( typeof(T.values[0]) )( value.value );
            res[ k ] = v;
        }
    }
    else static if( is( T == char[] )){ // char[] nicht als dyn array, sondern als String
        version(CD) pragma( msg, "getVariantAsKnownType: string "~typeof(T).stringof );
        DBusVariantValueString str = cast(DBusVariantValueString)var;
        ensure( str !is null, "getVariantAsKnownType string is null" );
        res = str.value;
    }
    else static if( isDynamicArrayType!(T) ){
        version(CD) pragma( msg, "getVariantAsKnownType: [] "~typeof(T).stringof );
        DBusVariantArray arr = cast(DBusVariantArray)var;
        ensure( arr !is null, "getVariantAsKnownType array is null" );
        res = new typeof(T[0])[ arr.values.length ];
        foreach( idx, value; arr.values ){
            res[ idx ] = getVariantAsKnownType!( typeof(T[0]) )( value );
        }
    }
    else static if( isDBusSimpleType!(T) ) {
        DBusVariantValue!(T) v = cast(DBusVariantValue!(T))var;
        ensure( v !is null, "getVariantAsKnownType value is null" );
        res = v.value;
    }
    else static if( is( T : DBusObjectImpl )){
        static assert( false );
    }
    else static if( is( T == DBusVariant ) ){
        res = var;
    }
    else static if( is( T == Struct!(typeof(T.t)) ) ){
        DBusVariantStruct v = cast(DBusVariantStruct)var;
        ensure( v !is null, "getVariantAsKnownType isnull" );
        foreach( idx, item; res.t ){
            res.t[idx] = getVariantAsKnownType!( typeof(item) )( v.values[idx] );
        }
    }
    else{
        pragma( msg, "appendItem: "~typeof(t).stringof );
        static assert(false, typeof(t).stringof );
    }
    return res;
}




void testTemplateCompilation(){
  getCallValues!( int[] )( null );
  getCallValues!( int[ char[] ] )( null );
  getCallValues!( DBusVariant[ DBusVariant ] )( null );
  getCallValues!( Struct!( int[], long, char[], Struct!( bool ) ) )( null );

  getVariantAsKnownType!( int[] )( null );
  getVariantAsKnownType!( int[ char[] ] )( null );
  getVariantAsKnownType!( DBusVariant[ DBusVariant ] )( null );
  getVariantAsKnownType!( Struct!( int[], long, char[], Struct!( bool ) ) )( null );
}


/** 
 * this function shall be used like so:
 * ---
 * mixin( createJavaImplementation( "MyClass", ["I1", "I2" ] ));
 * ---
 * This creates a simple standard implementation by generating code in this form:
 * ---
 * class MyClass : I1, I2 {
 *   mixin I1.JImpl!();
 *   mixin I2.JImpl!();
 * }
 * ---
 * The given array with interfaces must not be zero length
 */
char[] createJavaImplementation( char[] className, char[][] interfaces ){
	assert( interfaces.length > 0 );
    char[] res = "class "~className~ " ";
    bool first = true;
    foreach( intf; interfaces ){
   		res ~= first ? ": " : ", ";
       	first = false;
    	res ~= intf;
    }
	res ~= " {\n";
    foreach( intf; interfaces ){
    	res ~= "  mixin "~intf~"._StdJavaImpl!();\n";
    }
	res ~= "}\n";
	return res;
}

char[] ctfeReplace( char[] text, char s, char r ){
	char[] res;
	foreach( c; text ){
		res ~= ( c == s ) ? r : c;
	}
	return res;
}
char[] createDImplementation( char[] className, char[][] interfaces ){
	assert( interfaces.length > 0 );
    char[] res = "abstract class "~className~ " : DBusObjectImpl ";
    foreach( intf; interfaces ){
   		res ~= ", ";
       	res ~= intf;
    }
	res ~= " {\n";
	/+
    foreach( intf; interfaces ){
    	res ~= "  mixin "~intf~"._StdDImpl!() _StdDImpl_"~ctfeReplace( intf, '.', '_' )~";\n";
    }
	res ~= "  protected this(){\n";
    foreach( intf; interfaces ){
    	res ~= "    _StdDImpl_"~ ctfeReplace( intf, '.', '_' ) ~"._init();\n";
    }
	res ~= "  }\n";
	+/
	res ~= "}\n";
	return res;
}