Logo Search packages:      
Sourcecode: ladcca version File versions  Download package

project.c

/*
 *   LADCCA
 *    
 *   Copyright (C) 2002 Robert Ham <rah@bash.sh>
 *    
 *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#define _GNU_SOURCE

#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <assert.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <signal.h>

#include <jack/jack.h>
#include <alsa/asoundlib.h>
#include <libxml/tree.h>

#include <ladcca/ladcca.h>
#include <ladcca/internal_headers.h>

#include "project.h"
#include "client.h"
#include "store.h"
#include "jack_patch.h"
#include "alsa_patch.h"
#include "globals.h"
#include "server.h"
#include "jack_mgr.h"

#define ID_DIR      ".id"
#define CONFIG_DIR  ".config"
#define INFO_FILE   ".ladcca_info"
#define PROJECT_XML_VERSION "1.0"

project_t *
project_new (server_t * server)
{
  project_t * project;
  project = cca_malloc0 (sizeof (project_t));

  project->server = server;

  return project;
}

void
project_set_directory (project_t * project, const char * directory)
{
  set_string_property (project->directory, directory);
}

void
project_set_name (project_t * project, const char * name)
{
  set_string_property (project->name, name);
}

int 
project_name_exists (cca_list_t * projects, const char * name)
{
  for (; projects; projects = cca_list_next (projects))
    {
      if (strcmp (name, ((project_t *)projects->data)->name) == 0)
        return 1;
    }
  
  return 0;
}

client_t *
project_get_client_by_id (project_t * project, uuid_t id)
{
  cca_list_t * list;
  client_t * client;
  
  for (list = project->clients; list; list = cca_list_next (list))
    {
      client = (client_t *) list->data;

      if (uuid_compare (client->id, id) == 0)
        return client;
    }
  
  return NULL;
}

client_t *
project_get_lost_client_by_id (project_t * project, uuid_t id)
{
  cca_list_t * list;
  client_t * client;
  
  for (list = project->lost_clients; list; list = cca_list_next (list))
    {
      client = (client_t *) list->data;

      if (uuid_compare (client->id, id) == 0)
        return client;
    }
  
  return NULL;
}


/*************************************
 ************ operations *************
 *************************************/

void
project_new_client (project_t * project, client_t * client)
{
  if (uuid_is_null (client->id))
    client_generate_id (client);
  
  CCA_DEBUGARGS ("client on connection %ld now has id '%s'",
                 client->conn_id, client_get_id_str (client));
  
  if (CLIENT_CONFIG_DATA_SET (client))
    client_store_open (client, project_get_client_config_dir (project, client));
  
  project->clients = cca_list_append (project->clients, client);

  printf ("Added client %s of class %s to project %s\n",
          client_get_id_str (client), client->class, project->name);

  cca_create_dir (project_get_client_id_dir (project, client));

  server_notify_interfaces (project, client, CCA_Client_Add, NULL);
}


void
project_name_client (project_t * project, client_t * client, const char * name)
{
  cca_list_t * list;
  client_t * p_client;
  int err;
  int linked = 0;
  
  CCA_DEBUGARGS ("naming client '%s' with name '%s'",
                 client_get_id_str (client),
                 name);
  
  /* check the client doesn't already have the name */
  if (client->name && strcmp (name, client->name) == 0)
    {
      CCA_DEBUGARGS ("client '%s' already has name '%s'; not setting", client_get_id_str (client), name);
      return;
    }
  
  /* check the name doesn't already exist */
  for (list = project->clients; list; list = cca_list_next (list))
    {
      p_client = (client_t *) list->data;
      
      if (p_client->name && strcmp (p_client->name, name) == 0)
        {
          fprintf (stderr, "%s: a client '%s' already has the name '%s'; not setting name for client '%s'\n",
                   __FUNCTION__, client_get_id_str (p_client), name, client_get_id_str (client));
          return;
        }
    }
  
  if (client->name &&
      (CLIENT_CONFIG_DATA_SET (client) ||
       CLIENT_CONFIG_FILE     (client)))
    { /* link should already exist */
      char * old_link;

      old_link = cca_strdup (project_get_client_name_dir (project, client));
      
      client_set_name (client, name);
      
      CCA_DEBUGARGS ("moving old link '%s' to '%s'", old_link, project_get_client_name_dir (project, client));

      err = rename (old_link, project_get_client_name_dir (project, client));
      if (err == -1)
        {
          fprintf (stderr, "%s: could not rename name link for client '%s': %s\n",
                   __FUNCTION__, client_get_id_str (client),
                   strerror (errno));
        }
      else
        linked = 1;
      
      free (old_link);
    }
  
  if (!linked &&
      (CLIENT_CONFIG_DATA_SET (client) ||
       CLIENT_CONFIG_FILE     (client)))
    {
      char * id_dir;
      
      client_set_name (client, name);
      id_dir = cca_malloc (strlen (ID_DIR) + 1 + sizeof (char[37]) + 1);
      sprintf (id_dir, "%s/%s", ID_DIR, client_get_id_str (client));
      
      CCA_DEBUGARGS ("linking id dir '%s' to name dir '%s", id_dir,
                     project_get_client_name_dir (project, client));
                     
      err = symlink (id_dir, project_get_client_name_dir (project, client));
      if (err == -1)
         fprintf (stderr, "%s: could not create name link for client '%s': %s\n",
                  __FUNCTION__, client_get_id_str (client), strerror (errno));
      
      free (id_dir);
    }
  else
    client_set_name (client, name);

  printf ("Client %s set its name to '%s'\n", client_get_id_str (client), client->name);
  server_notify_interfaces (project, client, CCA_Client_Name, name);
}

void
project_resume_client (project_t * project, client_t * client, client_t * lost_client)
{
  cca_event_t * event;
  int err;
  

  /*
   * get all the necessary data from the lost client
   */
  client_set_name (client, lost_client->name);
  client->alsa_patches = lost_client->alsa_patches;
  lost_client->alsa_patches = NULL;
  client->jack_patches = lost_client->jack_patches;
  lost_client->jack_patches = NULL;
  client->flags = lost_client->flags;
  uuid_copy (client->id, lost_client->id);

  
  
  /*
   * kill the lost client
   */
  project->lost_clients = cca_list_remove (project->lost_clients, lost_client);
  client_destroy (lost_client);
  
  CCA_DEBUGARGS ("resuming client '%s'", client_get_id_str (client));
  
  if (client->name)
    {
      char * name;
      name = cca_strdup (client->name);
      client_set_name (client, NULL);
      project_name_client (project, client, name);
      free (name);
    }
  
  /*
   * resume the client
   */
  if (CLIENT_CONFIG_FILE     (client) ||
      CLIENT_CONFIG_DATA_SET (client))
    cca_create_dir (project_get_client_id_dir (project, client));
  
  /* tell the client to load its files */
  if (CLIENT_CONFIG_FILE (client) && CLIENT_SAVED (client))
    {
      event = cca_event_new_with_type (CCA_Restore_File);
      cca_event_set_string (event, project_get_client_id_dir (project, client));
      conn_mgr_send_client_cca_event (project->server->conn_mgr,
                                      client->conn_id, event);
    }

  if (CLIENT_CONFIG_DATA_SET (client))
    {
      err = client_store_open (client, project_get_client_config_dir (project, client));
      if (err)
        fprintf (stderr, "%s: could not open client's store; not restoring its data set\n", __FUNCTION__);
      else
      {
        if (CLIENT_SAVED (client))
          project_restore_data_set (project, client);
      }
    }

  project->clients = cca_list_append (project->clients, client);

  printf ("Resumed client %s of class %s in project %s\n",
        client_get_id_str (client), client->class, project->name);
  server_notify_interfaces (project, client, CCA_Client_Add, NULL);
}

void
project_add_client (project_t * project, client_t * client)
{
  cca_list_t * list;
  client_t * lost_client;
  int no_client_id;
  
  if (CLIENT_NO_AUTORESUME (client))
    {
      project_new_client (project, client);
      return;
    }
  
  no_client_id = uuid_is_null (client->id);
  
  CCA_DEBUGARGS ("attempting to resume client on connection %ld", client->conn_id);
  
  /* try and find a client we can resume */
  list = project->lost_clients;
  while (list)
    {
      lost_client = (client_t *) list->data;
      
#ifdef LADCCA_DEBUG
      {      
        char * lost_str;
        lost_str = cca_strdup (client_get_id_str (lost_client));
        CCA_DEBUGARGS ("checking client '%s','%s' against lost client '%s','%s'",
                       client_get_id_str (client),
                       client->class, lost_str, lost_client->class);
        free (lost_str);
      }
#endif /* LADCCA_DEBUG */

      if ( (no_client_id && strcmp (client->class, lost_client->class) == 0)
           || uuid_compare (client->id, lost_client->id) == 0)
        {
          CCA_DEBUGARGS ("resuming client '%s' of class '%s' with client on connection %ld",
                         client_get_id_str (client), lost_client->class, client->conn_id);
          project_resume_client (project, client, lost_client);
          return;
        }
      
      list = list->next;
    }
  
  CCA_DEBUGARGS ("could not resume client on connection %ld", client->conn_id);
  project_new_client (project, client);
}

/**************************************
 ************ store stuff *************
 **************************************/
 
const char *
project_get_client_id_dir (project_t * project, client_t * client)
{
  get_store_and_return_fqn (
    cca_get_fqn (project->directory, ID_DIR),
    client_get_id_str (client));
}


const char *
project_get_client_name_dir (project_t * project, client_t * client)
{
  get_store_and_return_fqn (project->directory, client->name);
}

const char *
project_get_client_config_dir (project_t * project, client_t * client)
{
  get_store_and_return_fqn (
    client->name
      ? project_get_client_name_dir (project, client)
      : project_get_client_id_dir (project, client),
    CONFIG_DIR);
}

const char *
project_get_client_file_dir (project_t * project, client_t * client)
{
  return client->name
           ? project_get_client_name_dir (project, client)
           : project_get_client_id_dir (project, client);
}

char *
escape_file_name (const char * fn)
{
  char * escfn;
  size_t escfn_size;
  size_t fn_size;
  ptrdiff_t * escchars = NULL;
  size_t escchars_size = 0;
  const char * ptr;
  unsigned int i, j;

  ptr = fn - 1;

  while ( (ptr = strpbrk (ptr + 1, " |&;()<>")) )
    {
      if (!escchars)
        {
          escchars_size = 1;
          escchars = malloc (escchars_size * sizeof (ptrdiff_t));
        }
      else
        {
          escchars_size++;
          escchars = realloc (escchars, escchars_size * sizeof (ptrdiff_t));
        }

      escchars[escchars_size - 1] = ptr - fn;
    }

  if (!escchars)
    return strdup (fn);
                                                                                                                                                       
  fn_size = strlen (fn);
  escfn_size = fn_size + escchars_size + 1;
  escfn = malloc (escfn_size);
  strncpy (escfn, fn, fn_size + 1);

  for (i = 0; i < escchars_size; i++)
    {
      for (j = escfn_size - 1; (escfn + j) > (escfn + escchars[i] + i); j--)
      escfn[j] = escfn[j - 1];

      *(escfn + escchars[i] + i) = '\\';
     }

    return escfn;
}

void
project_move (project_t * project, const char * new_dir)
{
  cca_list_t * list;
  client_t * client;
  char * cmd;
  char * esc_proj_dir, * esc_new_proj_dir;
  sighandler_t childexit_handler;

  int err;

  if (strcmp (new_dir, project->directory) == 0)
    return;
  
  /* close all the clients' stores */
  for (list = project->clients; list; list = cca_list_next (list))
    {
      client = (client_t *) list->data;

      client_store_close (client);

      /* FIXME: check for errors */
    }
  
  /* move the directory */
  esc_proj_dir = escape_file_name (project->directory);
  esc_new_proj_dir = escape_file_name (new_dir);
  cmd = malloc (strlen ("mv") + 1 +
            strlen (esc_proj_dir) + 1 +
            strlen (esc_new_proj_dir) + 1 +
            1);
  sprintf (cmd, "mv %s %s", esc_proj_dir, esc_new_proj_dir);

  childexit_handler = signal (SIGCHLD, SIG_IGN);
  err = system (cmd);
  if (err == -1 || WEXITSTATUS(err))
    {
      fprintf (stderr, "could not move project directory to %s\n", new_dir);
    }
  else
    {
      printf ("Project %s moved from %s to %s\n", project->name, project->directory, new_dir);
      project_set_directory (project, new_dir);
      server_notify_interfaces (project, client, CCA_Project_Dir, new_dir);
    }
  signal (SIGCHLD, childexit_handler);

  free (cmd);
  free (esc_proj_dir);
  free (esc_new_proj_dir);

  /* open all the clients' stores again */
  for (list = project->clients; list; list = cca_list_next (list))
    {
      client = (client_t *) list->data;

      client_store_open (client, project_get_client_config_dir (project, client));

      /* FIXME: check for errors */
    }
}

void
project_restore_data_set (project_t * project, client_t * client)
{
  
  if (CLIENT_CONFIG_DATA_SET (client))
    {
      char * key;
      cca_config_t * config;
      cca_list_t * list;
      cca_event_t * event;
      
      CCA_DEBUGARGS ("restoring data set for client '%s'", client_get_id_str (client));

      list = store_get_keys (client->store);

      /* send the event to the client */
      if (list)
        {
          event = cca_event_new_with_type (CCA_Restore_Data_Set);
          conn_mgr_send_client_cca_event (project->server->conn_mgr, client->conn_id, event);
        }
      
      for (; list; list = cca_list_next (list))
        {
          key = (char *) list->data;
          
          config = client_store_get_config (client, key);
          
          if (config)
            conn_mgr_send_client_cca_config (project->server->conn_mgr,
                                             client->conn_id, config);
        }
    }
}

void
project_save_clients (project_t * project)
{
  cca_list_t * list;
  client_t * client;
  cca_event_t * event;
  
  for (list = project->clients; list; list = cca_list_next (list))
    {
      client = (client_t *) list->data;
      
      if (CLIENT_CONFIG_FILE (client))
        {
        CCA_DEBUGARGS ("telling client %s to save files", client_get_identity (client));

          event = cca_event_new_with_type (CCA_Save_File);
          
          cca_event_set_string (event, project_get_client_file_dir (project, client));
          conn_mgr_send_client_cca_event (project->server->conn_mgr,
                                          client->conn_id, event);

        project->saves++;
      
        client->flags |= CCA_Saved;
        }
      
      if (CLIENT_CONFIG_DATA_SET (client))
        {
        CCA_DEBUGARGS ("telling client %s to save data set", client_get_identity (client));

          event = cca_event_new_with_type (CCA_Save_Data_Set);
          conn_mgr_send_client_cca_event (project->server->conn_mgr,
                                          client->conn_id, event);
        project->saves++;
      
        client->flags |= CCA_Saved;
        }
    }

  project->pending_saves = project->saves;
}

void
project_create_client_jack_patch_xml (project_t * project, client_t * client, xmlNodePtr clientxml)
{
  xmlNodePtr jack_patch_set;
  cca_list_t * patches, * node;
  jack_patch_t * patch;

  jack_mgr_lock (project->server->jack_mgr);
  patches = jack_mgr_get_client_patches (project->server->jack_mgr, client->id);
  jack_mgr_unlock (project->server->jack_mgr);

  if (!patches)
    return;

  jack_patch_set = xmlNewChild (clientxml, NULL, BAD_CAST "jack_patch_set", NULL);

  for (node = patches; node; node = cca_list_next (node))
    {
      patch = (jack_patch_t *) node->data;

      jack_patch_create_xml (patch, jack_patch_set);

      jack_patch_destroy (patch);
    }

  cca_list_free (patches);
}

void
project_create_client_alsa_patch_xml (project_t * project, client_t * client, xmlNodePtr clientxml)
{
  xmlNodePtr alsa_patch_set;
  cca_list_t * patches, * node;
  alsa_patch_t * patch;

  alsa_mgr_lock (project->server->alsa_mgr);
  patches = alsa_mgr_get_client_patches (project->server->alsa_mgr, client->id);
  alsa_mgr_unlock (project->server->alsa_mgr);

  if (!patches)
    return;

  alsa_patch_set = xmlNewChild (clientxml, NULL, BAD_CAST "alsa_patch_set", NULL);

  for (node = patches; node; node = cca_list_next (node))
    {
      patch = (alsa_patch_t *) node->data;

      alsa_patch_create_xml (patch, alsa_patch_set);

      alsa_patch_destroy (patch);
    }

  cca_list_free (patches);
}

static xmlDocPtr
project_create_xml (project_t * project)
{
  xmlDocPtr doc;
  xmlNodePtr ladcca_project, clientxml, arg_set;
  cca_list_t * clnode;
  client_t * client;
  char num[16];
  int i;

  doc = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION);

  /* dtd */
  xmlCreateIntSubset (doc, BAD_CAST "ladcca_project", NULL,
                      BAD_CAST "http://purge.bash.sh/~rah/ladcca-project-1.0.dtd");

  /* root node */
  ladcca_project = xmlNewDocNode(doc, NULL, BAD_CAST "ladcca_project", NULL);
  xmlAddChild ((xmlNodePtr)doc, ladcca_project);

  xmlNewChild (ladcca_project, NULL, BAD_CAST "version", BAD_CAST PROJECT_XML_VERSION);
  xmlNewChild (ladcca_project, NULL, BAD_CAST "name", BAD_CAST project->name);

  for (clnode = project->clients; clnode; clnode = cca_list_next (clnode))
    {
      client = (client_t *) clnode->data;

      clientxml = xmlNewChild (ladcca_project, NULL, BAD_CAST "client", NULL);

      xmlNewChild (clientxml, NULL, BAD_CAST "class", BAD_CAST client->class);
      xmlNewChild (clientxml, NULL, BAD_CAST "id", BAD_CAST client_get_id_str (client));
      sprintf (num, "%d", client->flags);
      xmlNewChild (clientxml, NULL, BAD_CAST "flags", BAD_CAST num);
      xmlNewChild (clientxml, NULL, BAD_CAST "working_directory", BAD_CAST client->working_dir);

      arg_set = xmlNewChild (clientxml, NULL, BAD_CAST "arg_set", NULL);
      for (i = 0; i < client->argc; i++)
      xmlNewChild (arg_set, NULL, BAD_CAST "arg", BAD_CAST client->argv[i]);

      if (client->jack_client_name)
        project_create_client_jack_patch_xml (project, client, clientxml);

      if (client->alsa_client_id)
        project_create_client_alsa_patch_xml (project, client, clientxml);
    }

  return doc;
}

static int
project_write_info (project_t * project)
{
  xmlDocPtr doc;
  const char * filename;
  int err;

  doc = project_create_xml (project);

  filename = cca_get_fqn (project->directory, INFO_FILE);

  err = xmlSaveFormatFile (filename, doc, 1);
  if (err == -1)
    {
      fprintf (stderr, "%s: could not save project xml to file %s: %s",
             __FUNCTION__, filename, strerror (errno));
    }
  else
    err = 0;

  return err;
}

void
project_clear_lost_clients (project_t * project)
{
  cca_list_t * list;
  client_t * client;
  const char * client_dir;
  
  for (list = project->lost_clients; list; list = cca_list_next (list))
    {
      client = (client_t *) list->data;
      
      client_dir = project_get_client_id_dir (project, client);
      if (cca_dir_exists (client_dir))
        cca_remove_dir (client_dir);
      
      client_destroy (client);
    }
    
  cca_list_free (project->lost_clients);
  project->lost_clients = NULL;
}

void
project_save (project_t * project)
{
  char num[16];
  int err;

  if (project->pending_saves)
    {
      CCA_DEBUG ("a save is in progress; cannot save at this time");
      return;
    }

  /* initialise the interfaces' progress display */
  sprintf (num, "%d", 0);
  server_notify_interfaces (project, NULL, CCA_Percentage, num);
  
  project_save_clients (project);
  
  err = project_write_info (project);
  if (err)
    {
      fprintf (stderr, "%s: error writing info file for project '%s'; aborting save\n",
               __FUNCTION__, project->name);
      return;
    }
  
  project_clear_lost_clients (project);
}

project_t *
project_new_from_xml (server_t * server, xmlDocPtr doc)
{
  xmlNodePtr projectnode, xmlnode;
  xmlChar * content;

  for (projectnode = doc->children; projectnode; projectnode = projectnode->next)
    if (projectnode->type == XML_ELEMENT_NODE && strcmp (CAST_BAD projectnode->name, "ladcca_project") == 0)
      break;

  if (!projectnode)
    {
      CCA_DEBUG ("no ladcca_project node in doc");
      return NULL;
    }

  project_t * project;
  project = project_new (server);

  for (xmlnode = projectnode->children; xmlnode; xmlnode = xmlnode->next)
    {
      if (strcmp (CAST_BAD xmlnode->name, "version") == 0)
      {
        /* FIXME: check version */
      }
      else if (strcmp (CAST_BAD xmlnode->name, "name") == 0)
      {
        content = xmlNodeGetContent (xmlnode);
        project_set_name (project, CAST_BAD content);
        xmlFree (content);
      }
      else if (strcmp (CAST_BAD xmlnode->name, "client") == 0)
      {
        client_t * client;

        client = client_new ();
        client_parse_xml (client, xmlnode);

        project->lost_clients = cca_list_append (project->lost_clients, client);
      }
    }

  if (!project->name)
    {
      project_destroy (project);
      return NULL;
    }

  return project;
}

project_t *
project_restore (server_t * server, const char * dir)
{
  cca_list_t * list;
  project_t * project;
  const char * filename;
  xmlDocPtr doc;
  
  CCA_DEBUGARGS ("attempting to restore project in dir '%s'", dir);
  
  /* check if we've already got it open */
  list = server->projects;
  while (list)
    {
      project = (project_t *) list->data;
      
      if (strcmp (project->directory, dir) == 0)
        {
          fprintf (stderr, "%s: cannot restore project from directory '%s': a project, '%s', is already active with this directory\n",
                   __FUNCTION__, dir, project->name);
          return NULL;
        }
      
      list = list->next;
    }
  
  filename = cca_get_fqn (dir, INFO_FILE);
  
  doc = xmlParseFile (filename);
  if (!doc)
    {
      fprintf (stderr, "%s: could not parse file %s\n", __FUNCTION__, filename);
      return NULL;
    }

  project = project_new_from_xml (server, doc);
  if (!project)
    {
      fprintf (stderr, "%s: could not recreate project from xml\n", __FUNCTION__);
      return NULL;
    }
    
  project_set_directory (project, dir);
  

#ifdef LADCCA_DEBUG
  {
    cca_list_t * client_list;
    client_t * client;
    cca_list_t * list;
    int i;
    
  
    CCA_DEBUG     ("resored project with:");
    CCA_DEBUGARGS ("  directory: '%s'", project->directory);
    CCA_DEBUGARGS ("  name:      '%s'", project->name);
    CCA_DEBUG     ("  clients:");
    for (client_list = project->lost_clients; client_list; client_list = client_list->next)
      {
        client = (client_t *) client_list->data;
        
        CCA_DEBUG     ("  ------");
        CCA_DEBUGARGS ("    id:          '%s'", client_get_id_str (client));
        CCA_DEBUGARGS ("    working dir: '%s'", client->working_dir);
        CCA_DEBUGARGS ("    flags:       %d",   client->flags);
      CCA_DEBUGARGS ("    argc:        %d",   client->argc);
      CCA_DEBUG     ("    args:");
      for (i = 0; i < client->argc; i++)
        {
          CCA_DEBUGARGS ("      %d: '%s'", i, client->argv[i]);
        }
        if (client->alsa_patches)
          {
            CCA_DEBUG ("    alsa patches:");
            for (list = client->alsa_patches; list; list = list->next)
              {
            CCA_DEBUGARGS ("      %s", alsa_patch_get_desc ((alsa_patch_t *) list->data));
              }
          }
        else
          CCA_DEBUG ("    no alsa patches");

        if (client->jack_patches)
          {
            CCA_DEBUG ("    jack patches:");
            for (list = client->jack_patches; list; list = list->next)
              {
                CCA_DEBUGARGS ("      %s", jack_patch_get_desc ((jack_patch_t *) list->data));
              }
          }
        else
          CCA_DEBUG ("    no jack patches");
      }
  }
#endif
  
  return project;
}

void
project_remove_client (project_t * project, client_t * client)
{
  conn_mgr_send_client_cca_event (project->server->conn_mgr, client->conn_id, cca_event_new_with_type (CCA_Quit));
}


void
project_lose_client (project_t * project, client_t * client,
                     cca_list_t * jack_patches, cca_list_t * alsa_patches)
{
  int i;

  CCA_DEBUGARGS ("losing client '%s' from project '%s'",
                 client_get_id_str (client),
                 project->name);
  project->clients = cca_list_remove (project->clients, client);

  if (CLIENT_CONFIG_DATA_SET (client))
    {
      store_t * store;
      store = client->store;
      
      if (store)
        {
          if (store_get_keys (store))
            store_write (store);
          else if (cca_dir_exists (store->dir))
            cca_remove_dir (store->dir);
        }
    }
  

  if (CLIENT_CONFIG_DATA_SET (client) ||
      CLIENT_CONFIG_FILE     (client))
    {
      const char * dir;
      dir = project_get_client_id_dir (project, client);
      
      if (cca_dir_exists (dir) && cca_dir_empty (dir))
        cca_remove_dir (dir);
      
      dir = cca_get_fqn (project->directory, ID_DIR);
      if (cca_dir_exists (dir) && cca_dir_empty (dir))
        cca_remove_dir (dir);
    
      if (client->name)
        unlink (project_get_client_name_dir (project, client));
      
    }
  
  client->jack_patches = cca_list_concat (client->jack_patches, jack_patches);
  client->alsa_patches = cca_list_concat (client->alsa_patches, alsa_patches);
  project->lost_clients = cca_list_append (project->lost_clients, client);

  /* don't need these any more */
  for (i = 0; i < client->argc; i++)
    free (client->argv[i]);
  free (client->argv);
  client->argc = 0;
  client->argv = NULL;

  printf ("Client %s removed from project %s\n", client_get_identity (client), project->name);
  server_notify_interfaces (project, client, CCA_Client_Remove, NULL);
}

void
project_destroy (project_t * project)
{
  cca_list_t * node;
  cca_list_t * patches, * pnode;
  client_t * client;
  cca_event_t * cca_event;
  server_event_t * server_event;

  for (node = project->clients; node; node = cca_list_next (node))
    {
      client = (client_t *) node->data;

      if (client->jack_client_name)
      {
        jack_mgr_lock (project->server->jack_mgr);
        patches = jack_mgr_remove_client (project->server->jack_mgr, client->id);
        jack_mgr_unlock (project->server->jack_mgr);

        for (pnode = patches; pnode; pnode = cca_list_next (pnode))
          jack_patch_destroy ((jack_patch_t *) pnode->data);
        cca_list_free (patches);
      }

      if (client->alsa_client_id)
      {
        alsa_mgr_lock (project->server->alsa_mgr);
        patches = alsa_mgr_remove_client (project->server->alsa_mgr, client->id);
        alsa_mgr_unlock (project->server->alsa_mgr);

        for (pnode = patches; pnode; pnode = cca_list_next (pnode))
          alsa_patch_destroy ((alsa_patch_t *) pnode->data);
        cca_list_free (patches);
      }


      /* remove the client name links */
      if (CLIENT_CONFIG_DATA_SET (client) || CLIENT_CONFIG_FILE (client))
      if (client->name)
        unlink (project_get_client_name_dir (project, client));

      cca_event = cca_event_new_with_type (CCA_Quit);
      conn_mgr_send_client_cca_event (project->server->conn_mgr, client->conn_id, cca_event);

      server_event = server_event_new ();
      server_event->type = Client_Disconnect;
      server_event->conn_id = client->conn_id;
      conn_mgr_send_client_event (project->server->conn_mgr, server_event);

      client_destroy (client);
    }
  cca_list_free (project->clients);


  for (node = project->lost_clients; node; node = cca_list_next (node))
    client_destroy ((client_t *)node->data);
  cca_list_free (project->lost_clients);

  printf ("Project %s removed\n", project->name);

  project_set_name (project, NULL);

  if (access (cca_get_fqn (project->directory, INFO_FILE), F_OK) != 0)
    cca_remove_dir (project->directory);

  project_set_directory (project, NULL);

  free (project);
}

static void
project_notify_percentage (project_t * project)
{
  char num[16];
  int percentage;

  percentage = ( ((float) (project->saves - project->pending_saves)) / ((float) project->saves)
             * 100.0);

  if (percentage > 100)
    percentage = 100;

  sprintf (num, "%d", percentage);
  server_notify_interfaces (project, NULL, CCA_Percentage, num);

  if (!project->pending_saves)
    {
      sprintf (num, "%d", 0);
      server_notify_interfaces (project, NULL, CCA_Percentage, num);

      project->saves = 0;
    }
}

void
project_file_complete     (project_t * project, client_t * client)
{
  project->pending_saves--;
  project_notify_percentage (project);
}


void
project_data_set_complete (project_t * project, client_t * client)
{
  int err;
  
  if (!CLIENT_CONFIG_DATA_SET (client))
    return;
  
  err = client_store_write (client);
  if (err)
    fprintf (stderr, "%s: could not write client '%s's data to disk!"
           "You should attempt to ascertain why, resolve the situation, and save the project again.\n",
             __FUNCTION__, client_get_id_str (client));

  project->pending_saves--;
  project_notify_percentage (project);
}


/* EOF */


Generated by  Doxygen 1.6.0   Back to index