chiark / gitweb /
Merge branch 'mdw/gstdecode'
[disorder] / scripts / protocol
index f2a0c80e3a49736671a75703496558e1e1e57149..f2f9a3d5275bb8cabcc990189ddb6cfff9588026 100755 (executable)
@@ -1,7 +1,7 @@
 #! /usr/bin/perl -w
 #
 # This file is part of DisOrder.
 #! /usr/bin/perl -w
 #
 # This file is part of DisOrder.
-# Copyright (C) 2010-11 Richard Kettlewell
+# Copyright (C) 2010-11, 13 Richard Kettlewell
 #
 # 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
 #
 # 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
@@ -45,6 +45,55 @@ use strict;
 
 our @h = ();
 our @c = ();
 
 our @h = ();
 our @c = ();
+our @ah = ();
+our @ac = ();
+our @missing = ();
+
+# Mapping of return type sequences to eclient callbacks
+our @eclient_return = (
+    ["no_response" => []],
+    ["string_response" => ["string"]],
+    ["string_response" => ["string-raw"]],
+    ["integer_response" => ["integer"]],
+    ["integer_response" => ["boolean"]],
+    ["time_response" => ["time"]],
+    ["pair_integer_response" => ["integer", "integer"]],
+    ["queue_response" => ["queue"]],
+    ["playing_response" => ["queue-one"]],
+    ["list_response" => ["body"]],
+    );
+
+# eclient_response_matces(RETURNS, VARIANT)
+#
+# Return true if VARIANT matches RETURNS
+sub eclient_response_matches {
+    my $returns = shift;
+    my $variant = shift;
+    my $types = $variant->[1];
+    if(scalar @$returns != scalar @$types) { return 0; }
+    for my $n (0 .. $#$returns) {
+       my $return = $returns->[$n];
+       my $type = $return->[0];
+       if($type ne $types->[$n]) { return 0; }
+    }
+    return 1;
+}
+
+# find_eclient_type(RETURNS)
+#
+# Find the result type for an eclient call
+sub find_eclient_response {
+    my $returns = shift;
+    if(!defined $returns) {
+       $returns = [];
+    }
+    for my $variant (@eclient_return) {
+       if(eclient_response_matches($returns, $variant)) {
+           return $variant->[0];
+       }
+    }
+    return undef;
+}
 
 # Write(PATH, LINES)
 #
 
 # Write(PATH, LINES)
 #
@@ -145,6 +194,7 @@ sub c_param_docs {
 sub c_return_docs {
     my $returns = shift;
     return () unless defined $returns;
 sub c_return_docs {
     my $returns = shift;
     return () unless defined $returns;
+    my @docs = ();
     for my $return (@$returns) {
         my $type = $return->[0];
         my $name = $return->[1];
     for my $return (@$returns) {
         my $type = $return->[0];
         my $name = $return->[1];
@@ -154,20 +204,25 @@ sub c_return_docs {
            or $type eq 'integer'
            or $type eq 'time'
            or $type eq 'boolean') {
            or $type eq 'integer'
            or $type eq 'time'
            or $type eq 'boolean') {
-            return (" * \@param ${name}p $descr\n");
+            push(@docs,
+                " * \@param ${name}p $descr\n");
         } elsif($type eq 'list' or $type eq 'body') {
         } elsif($type eq 'list' or $type eq 'body') {
-            return (" * \@param ${name}p $descr\n",
-                    " * \@param n${name}p Number of elements in ${name}p\n");
+            push(@docs,
+                " * \@param ${name}p $descr\n",
+                " * \@param n${name}p Number of elements in ${name}p\n");
         } elsif($type eq 'pair-list') {
         } elsif($type eq 'pair-list') {
-            return (" * \@param ${name}p $descr\n");
+            push(@docs,
+                " * \@param ${name}p $descr\n");
         } elsif($type eq 'queue' or $type eq 'queue-one') {
         } elsif($type eq 'queue' or $type eq 'queue-one') {
-            return (" * \@param ${name}p $descr\n");
+            push(@docs,
+                " * \@param ${name}p $descr\n");
         } elsif($type eq 'user') {
         } elsif($type eq 'user') {
-            return ();
+           # nothing
         } else {
             die "$0: c_return_docs: unknown type '$type'\n";
         }
     }
         } else {
             die "$0: c_return_docs: unknown type '$type'\n";
         }
     }
+    return @docs;
 }
 
 # simple(CMD, SUMMARY, DETAIL,
 }
 
 # simple(CMD, SUMMARY, DETAIL,
@@ -193,6 +248,22 @@ sub simple {
         $cmdc =~ s/-/_/g;
     }
     print STDERR "Processing $cmd... ";
         $cmdc =~ s/-/_/g;
     }
     print STDERR "Processing $cmd... ";
+    # C argument types
+    my @cargs = ();
+    for my $arg (@$args) {
+        if($arg->[0] eq 'body' or $arg->[0] eq 'list') {
+            push(@cargs, "disorder__$arg->[0]", $arg->[1], "n$arg->[1]");
+        } elsif($arg->[0] eq 'string') {
+            push(@cargs, $arg->[1]);
+        } elsif($arg->[0] eq 'integer'
+               or $arg->[0] eq 'time') {
+            push(@cargs, "disorder__$arg->[0]", "$arg->[1]");
+        } elsif($arg->[0] eq 'literal') {
+            push(@cargs, "\"$arg->[1]\"");
+        } else {
+            die "$0: unsupported arg type '$arg->[0]' for '$cmd'\n";
+        }
+    }
     # Synchronous C API
     print STDERR "H ";
     push(@h, "/** \@brief $summary\n",
     # Synchronous C API
     print STDERR "H ";
     push(@h, "/** \@brief $summary\n",
@@ -215,26 +286,6 @@ sub simple {
                    map(c_in_decl($_), @$args),
                     map(c_out_decl($_), @$returns)),
         ") {\n");
                    map(c_in_decl($_), @$args),
                     map(c_out_decl($_), @$returns)),
         ") {\n");
-    my @cargs = ();
-    for my $arg (@$args) {
-        if($arg->[0] eq 'body' or $arg->[0] eq 'list') {
-            push(@cargs, "disorder_$arg->[0]", $arg->[1], "n$arg->[1]");
-        } elsif($arg->[0] eq 'string') {
-            push(@cargs, $arg->[1]);
-        } elsif($arg->[0] eq 'integer') {
-            push(@cargs, "buf_$arg->[1]");
-            push(@c, "  char buf_$arg->[1]\[16];\n",
-                 "  byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%ld\", $arg->[1]);\n");
-        } elsif($arg->[0] eq 'time') {
-            push(@cargs, "buf_$arg->[1]");
-            push(@c, "  char buf_$arg->[1]\[16];\n",
-                 "  byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%lld\", (long long)$arg->[1]);\n");
-        } elsif($arg->[0] eq 'literal') {
-            push(@cargs, "\"$arg->[1]\"");
-        } else {
-            die "$0: unsupported arg type '$arg->[0]' for '$cmd'\n";
-        }
-    }
     if(!defined $returns or scalar @$returns == 0) {
         # Simple case
        push(@c, "  return disorder_simple(",
     if(!defined $returns or scalar @$returns == 0) {
         # Simple case
        push(@c, "  return disorder_simple(",
@@ -345,7 +396,49 @@ sub simple {
     push(@c, "}\n\n");
 
     # Asynchronous C API
     push(@c, "}\n\n");
 
     # Asynchronous C API
-    # TODO
+    my $variant = find_eclient_response($returns);
+    if(defined $variant) {
+       print STDERR "AH ";
+       push(@ah,
+            "/** \@brief $summary\n",
+            " *\n",
+            " * $detail\n",
+            " *\n",
+            " * \@param c Client\n",
+            " * \@param completed Called upon completion\n",
+            c_param_docs($args),
+            " * \@param v Passed to \@p completed\n",
+            " * \@return 0 if the command was queued successfuly, non-0 on error\n",
+            " */\n",
+            "int disorder_eclient_$cmdc(",
+            join(", ", "disorder_eclient *c",
+                 "disorder_eclient_$variant *completed",
+                 map(c_in_decl($_), @$args),
+                 "void *v"),
+            ");\n\n");
+
+       print STDERR "AC ";
+       push(@ac,
+            "int disorder_eclient_$cmdc(",
+            join(", ", "disorder_eclient *c",
+                 "disorder_eclient_$variant *completed",
+                 map(c_in_decl($_), @$args),
+                 "void *v"),
+            ") {\n");
+       push(@ac, "  return simple(",
+            join(", ", 
+                 "c",
+                 "${variant}_opcallback",
+                 "(void (*)())completed",
+                 "v",
+                 "\"$cmd\"",
+                 @cargs,
+                 "(char *)0"),
+            ");\n");
+       push(@ac, "}\n\n");
+    } else {
+       push(@missing, "disorder_eclient_$cmdc");
+    }
 
     # Python API
     # TODO
 
     # Python API
     # TODO
@@ -387,9 +480,33 @@ our @gpl = ("/*\n",
 push(@h, @generated, @gpl,
      "#ifndef CLIENT_STUBS_H\n",
      "#define CLIENT_STUBS_H\n",
 push(@h, @generated, @gpl,
      "#ifndef CLIENT_STUBS_H\n",
      "#define CLIENT_STUBS_H\n",
+     "/** \@file lib/client-stubs.h\n",
+     " * \@brief Generated client API\n",
+     " *\n",
+     " * Don't include this file directly - use \@ref lib/client.h instead.\n",
+     " */\n",
      "\n");
 
 push(@c, @generated, @gpl,
      "\n");
 
 push(@c, @generated, @gpl,
+     "/** \@file lib/client-stubs.c\n",
+     " * \@brief Generated client API implementation\n",
+     " */\n",
+     "\n");
+
+push(@ah, @generated, @gpl,
+     "#ifndef ECLIENT_STUBS_H\n",
+     "#define ECLIENT_STUBS_H\n",
+     "/** \@file lib/client-stubs.h\n",
+     " * \@brief Generated asynchronous client API\n",
+     " *\n",
+     " * Don't include this file directly - use \@ref lib/eclient.h instead.\n",
+     " */\n",
+     "\n");
+
+push(@ac, @generated, @gpl,
+     "/** \@file lib/client-stubs.c\n",
+     " * \@brief Generated asynchronous client API implementation\n",
+     " */\n",
      "\n");
 
 # The protocol ----------------------------------------------------------------
      "\n");
 
 # The protocol ----------------------------------------------------------------
@@ -684,6 +801,17 @@ simple("rtp-address",
        [["string", "address", "Where to store hostname or address"],
         ["string", "port", "Where to store service name or port number"]]);
 
        [["string", "address", "Where to store hostname or address"],
         ["string", "port", "Where to store service name or port number"]]);
 
+simple("rtp-cancel",
+       "Cancel RTP stream",
+       "",
+       []);
+
+simple("rtp-request",
+       "Request a unicast RTP stream",
+       "",
+       [["string", "address", "Destination address"],
+        ["string", "port", "Destination port number"]]);
+
 simple("scratch",
        "Terminate the playing track.",
        "Requires one of the 'scratch mine', 'scratch random' or 'scratch any' rights depending on how the track came to be added to the queue.",
 simple("scratch",
        "Terminate the playing track.",
        "Requires one of the 'scratch mine', 'scratch random' or 'scratch any' rights depending on how the track came to be added to the queue.",
@@ -816,7 +944,17 @@ simple(["volume", "get_volume"],
 
 push(@h, "#endif\n");
 
 
 push(@h, "#endif\n");
 
+push(@ah, "#endif\n");
+
 # Write it all out ------------------------------------------------------------
 
 Write("lib/client-stubs.h", \@h);
 Write("lib/client-stubs.c", \@c);
 # Write it all out ------------------------------------------------------------
 
 Write("lib/client-stubs.h", \@h);
 Write("lib/client-stubs.c", \@c);
+
+Write("lib/eclient-stubs.h", \@ah);
+Write("lib/eclient-stubs.c", \@ac);
+
+if(scalar @missing) {
+  print "Missing:\n";
+  print map("  $_\n", @missing);
+}