Index: ChangeLog
===================================================================
--- ChangeLog	(revision 4930)
+++ ChangeLog	(revision 4934)
@@ -6,4 +6,5 @@
 	* Some cleanup of roarclients to fix ckport warnings.
 	* Fixed warning on FreeBSD about tempfile in ast2roar (Closes: #47)
+	* Added support for role based handling in roard (Closes: #49)
 
 v. 0.4beta5 - Tue Apr 26 2011 08:42 CEST
Index: roard/include/rolestack.h
===================================================================
--- roard/include/rolestack.h	(revision 4932)
+++ roard/include/rolestack.h	(revision 4934)
@@ -29,4 +29,29 @@
 #include <roaraudio.h>
 
+enum rs_action {
+ RS_ERROR = -1,
+ RS_MIX = 0,
+ RS_KICK,
+ RS_MUTE,
+ RS_PAUSE
+};
+
+struct rolestack {
+ int index;
+ int role;
+ enum rs_action action;
+};
+
+void rolestack_init(void);
+void print_rolestack(void);
+
+const struct rolestack * rolestack_get_role(int role);
+int rolestack_push(const struct rolestack * role);
+
+enum rs_action rolestack_str2action(const char * str);
+const char * rolestack_action2str(enum rs_action action);
+
+const struct rolestack * rolestack_parse(const char * str);
+
 #endif
 
Index: roard/roard.c
===================================================================
--- roard/roard.c	(revision 4924)
+++ roard/roard.c	(revision 4934)
@@ -248,4 +248,10 @@
         "                         a flag as default or remove it from the default\n"
        );
+ printf("\nStream Options:\n\n");
+ printf(
+        " --list-rolestack      - Show the current role stack\n"
+        " --rolestack-push R:A  - Add a rule for role R with action A on top\n"
+        "                         of the role stack.\n"
+       );
 
  printf("\nOutput Options:\n\n");
@@ -1332,4 +1338,5 @@
  struct roar_audio_info sa, max_sa;
  struct roard_config config;
+ const struct rolestack * rolestack;
 #ifdef ROAR_HAVE_FORK
  int    daemon       = 0;
@@ -1461,4 +1468,6 @@
 #endif
 
+ rolestack_init();
+
  if ( auth_init() == -1 ) {
   ROAR_ERR("Can not init auth subsystem!");
@@ -1712,4 +1721,17 @@
     return 1;
    }
+
+  } else if ( strcmp(k, "--list-rolestack") == 0 ) {
+   print_rolestack();
+   return 0;
+  } else if ( strcmp(k, "--rolestack-push") == 0 ) {
+   _CKHAVEARGS(1);
+  if ( (rolestack = rolestack_parse(argv[++i])) == NULL ) {
+   ROAR_ERR("Can not parse rolestack request: %s", roar_error2str(roar_error));
+  } else {
+   if ( rolestack_push(rolestack) == -1 ) {
+    ROAR_ERR("Can not push request to rolestack: %s", roar_error2str(roar_error));
+   }
+  }
 
   } else if ( strcmp(k, "--list-aiprofiles") == 0 ) {
Index: roard/rolestack.c
===================================================================
--- roard/rolestack.c	(revision 4932)
+++ roard/rolestack.c	(revision 4934)
@@ -26,3 +26,127 @@
 #include "roard.h"
 
+#define STACKSIZE 16
+
+static struct rolestack g_rolestack[STACKSIZE];
+
+void rolestack_init(void) {
+ int i;
+
+ for (i = 0; i < STACKSIZE; i++) {
+  g_rolestack[i].index  = i;
+  g_rolestack[i].role   = ROAR_ROLE_UNKNOWN;
+  g_rolestack[i].action = RS_MIX;
+ }
+
+ g_rolestack[0].role = ROAR_ROLE_NONE;
+}
+
+void print_rolestack(void) {
+ int i;
+
+ printf("Index Role             Action\n");
+ printf("-----------------------------\n");
+ for (i = 0; i < STACKSIZE && g_rolestack[i].role != ROAR_ROLE_UNKNOWN; i++) {
+  printf("%-5i %-16s %s\n", g_rolestack[i].index,
+          roar_role2str(g_rolestack[i].role), rolestack_action2str(g_rolestack[i].action));
+ }
+}
+
+const struct rolestack * rolestack_get_role(int role) {
+ int i;
+
+ ROAR_DBG("rolestack_get_role(role=%i) = ?", role);
+
+ if ( role == -1 )
+  return &(g_rolestack[0]);
+
+ for (i = STACKSIZE - 1; i >= 0; i--)
+  if ( g_rolestack[i].role == role )
+   return &(g_rolestack[i]);
+
+ return &(g_rolestack[0]);
+}
+
+int rolestack_push(const struct rolestack * role) {
+ int i;
+
+ for (i = 0; i < STACKSIZE; i++) {
+  if ( g_rolestack[i].role == ROAR_ROLE_UNKNOWN ) {
+   memcpy(&(g_rolestack[i]), role, sizeof(g_rolestack[i]));
+   g_rolestack[i].index = i;
+   return 0;
+  }
+ }
+
+ roar_err_set(ROAR_ERROR_NOMEM);
+ return -1;
+}
+
+enum rs_action rolestack_str2action(const char * str) {
+ if ( str == NULL ) {
+  roar_err_set(ROAR_ERROR_FAULT);
+  return -1;
+ }
+
+ if (!strcasecmp(str, "mix")) {
+  return RS_MIX;
+ } else if (!strcasecmp(str, "kick")) {
+  return RS_KICK;
+ } else if (!strcasecmp(str, "mute")) {
+  return RS_MUTE;
+ } else if (!strcasecmp(str, "pause")) {
+  return RS_PAUSE;
+ }
+
+ return RS_ERROR;
+}
+
+const char * rolestack_action2str(enum rs_action action) {
+ switch (action) {
+  case RS_ERROR: return "(error)"; break;
+  case RS_MIX:   return "mix";     break;
+  case RS_MUTE:  return "mute";    break;
+  case RS_PAUSE: return "pause";   break;
+  case RS_KICK:  return "kick";    break;
+ }
+
+ return "(unknown)";
+}
+
+const struct rolestack * rolestack_parse(const char * str) {
+ static struct rolestack ret;
+ char * role, * action;
+
+ if ( str == NULL ) {
+  roar_err_set(ROAR_ERROR_FAULT);
+  return NULL;
+ }
+
+ role = roar_mm_strdup(str);
+ if ( role == NULL )
+  return NULL;
+
+ if ( (action = strstr(role, ":")) == NULL ) {
+  roar_mm_free(role);
+  roar_err_set(ROAR_ERROR_BADMSG);
+  return NULL;
+ }
+
+ *action = 0;
+  action++;
+
+ ret.index  = -1;
+ ret.role   = roar_str2role(role);
+ ret.action = rolestack_str2action(action);
+
+ roar_mm_free(role);
+
+ if ( ret.role == -1 || ret.action == RS_ERROR ) {
+  roar_err_set(ROAR_ERROR_BADMSG);
+  return NULL;
+ }
+
+ return &ret;
+}
+
 //ll
Index: roard/streams.c
===================================================================
--- roard/streams.c	(revision 4896)
+++ roard/streams.c	(revision 4934)
@@ -33,4 +33,8 @@
 int streams_recsource_id = -1;
 
+static const struct rolestack * streams_rolestack = NULL;
+static int streams_role_num     =  0;
+
+
 static void _streams_change_state(struct roar_stream_server * s, const int new, const char * func) {
  register int id  = ROAR_STREAM(s)->id;
@@ -122,4 +126,6 @@
 int streams_init (void) {
  int i;
+
+ streams_rolestack = rolestack_get_role(-1); // get default
 
  for (i = 0; i < ROAR_STREAMS_MAX; i++)
@@ -263,4 +269,6 @@
  roar_notify_core_emit_snoargs(ROAR_OE_BASICS_DELETE, -1, id, ROAR_OT_STREAM);
 
+ streams_set_role(id, -1);
+
  prim = s->primary;
 
@@ -513,6 +521,22 @@
 }
 
+static int streams_set_role_handle(int id) {
+   switch (streams_rolestack->action) {
+    case RS_ERROR: /* nothing to do */; break;
+    case RS_MIX:   /* nothing to do */; break;
+    case RS_KICK:  streams_delete(id);  break;
+    case RS_MUTE:  streams_set_flag(id, ROAR_FLAG_MUTE);  break;
+    case RS_PAUSE: streams_set_flag(id, ROAR_FLAG_PAUSE); break;
+   }
+ return 0;
+}
+
 int streams_set_role   (int id, int role) {
  struct roar_stream_server * ss;
+ const struct rolestack * rs;
+ int old_role;
+ int i;
+
+ ROAR_DBG("streams_set_role(id=%i, role=%i) = ?", id, role);
 
  _CHECK_SID(id);
@@ -521,6 +545,80 @@
   return -1;
 
+ old_role = ss->role;
  ss->role = role;
 
+ ROAR_DBG("streams_set_role(id=%i, role=%i) = ?", id, role);
+
+ if ( ROAR_STREAM(ss)->dir != ROAR_DIR_PLAY )
+  return 0;
+
+ ROAR_DBG("streams_set_role(id=%i, role=%i) = ?", id, role);
+
+ if ( old_role == role )
+  return 0;
+
+ if ( old_role != role && old_role != -1 && role != -1 ) {
+  streams_set_role(id, -1);
+  streams_set_role(id, role);
+ }
+
+ ROAR_DBG("streams_set_role(id=%i, role=%i) = ?", id, role);
+
+ if ( ss->role != -1 && ss->role == streams_rolestack->role ) {
+  ROAR_DBG("streams_set_role(id=%i, role=%i) = ?", id, role);
+
+  streams_role_num--;
+  if ( streams_role_num == 0 ) {
+   streams_rolestack = rolestack_get_role(-1); // get default
+   for (i = 0; i < ROAR_STREAMS_MAX; i++) {
+    if ( (ss = g_streams[i]) == NULL )
+     continue;
+    if ( ROAR_STREAM(ss)->dir != ROAR_DIR_PLAY )
+     continue;
+    if ( ss->role == -1 )
+     continue;
+    rs = rolestack_get_role(ss->role);
+    if ( rs->index > streams_rolestack->role ) {
+     streams_rolestack = rs;
+    }
+   }
+   for (i = 0; i < ROAR_STREAMS_MAX; i++) {
+    if ( (ss = g_streams[i]) == NULL )
+     continue;
+    if ( ROAR_STREAM(ss)->dir != ROAR_DIR_PLAY )
+     continue;
+    if ( ss->role == streams_rolestack->role )
+     streams_role_num++;
+   }
+  }
+ } else if ( role != -1 ) {
+  ROAR_DBG("streams_set_role(id=%i, role=%i) = ?", id, role);
+
+  rs = rolestack_get_role(role);
+  ROAR_DBG("streams_set_role(id=%i, role=%i): rs->index=%i, streams_rolestack->index=%i", id, role, rs->index, streams_rolestack->index);
+  if ( rs->index == streams_rolestack->index ) {
+   ROAR_DBG("streams_set_role(id=%i, role=%i): rs->index=%i, streams_rolestack->index=%i", id, role, rs->index, streams_rolestack->index);
+   streams_role_num++;
+  } else if ( rs->index > streams_rolestack->index ) {
+   ROAR_DBG("streams_set_role(id=%i, role=%i): rs=%p{.index=%i, .action=%s}", id, role, rs, rs->index, rolestack_action2str(rs->action));
+   streams_role_num  = 1;
+   streams_rolestack = rs;
+   for (i = 0; i < ROAR_STREAMS_MAX; i++) {
+    if ( (ss = g_streams[i]) == NULL )
+     continue;
+    if ( ROAR_STREAM(ss)->dir != ROAR_DIR_PLAY )
+     continue;
+    rs = rolestack_get_role(ss->role);
+    ROAR_DBG("streams_set_role(id=%i, role=%i): i=%i, rs->index=%i, streams_rolestack->index=%i", id, role, i, rs->index, streams_rolestack->index);
+    if ( rs->index < streams_rolestack->index )
+     streams_set_role_handle(i);
+   }
+  } else {
+   ROAR_DBG("streams_set_role(id=%i, role=%i): streams_rolestack->action=%s", id, role, rolestack_action2str(streams_rolestack->action));
+   streams_set_role_handle(id);
+  }
+ }
+
+ ROAR_DBG("streams_set_role(id=%i, role=%i) = 0", id, role);
  return 0;
 }
