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

commands.c

#define _GNU_SOURCE

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <math.h>
#include <stdarg.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/time.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include "grab-ng.h"

#include "capture.h"
#include "commands.h"
#include "writefile.h"
#include "channel.h"
#include "webcam.h"
#include "frequencies.h"
#include "sound.h"

/* ----------------------------------------------------------------------- */

/* feedback for the user */
void (*update_title)(char *message);
void (*display_message)(char *message);
void (*rec_status)(char *message);
#if TT
void (*vtx_message)(struct TEXTELEM *tt);
#endif
#ifdef HAVE_ZVBI
void (*vtx_subtitle)(struct vbi_page *pg, struct vbi_rect *rect);
#endif

/* for updating GUI elements / whatever */
void (*attr_notify)(struct ng_attribute *attr, int val);
void (*volume_notify)(void);
void (*freqtab_notify)(void);
void (*setfreqtab_notify)(void);
void (*setstation_notify)(void);

/* gets called _before_ channel switches */
void (*channel_switch_hook)(void);

/* capture overlay/grab/off */
void (*set_capture_hook)(int old, int new, int tmp_switch);

/* toggle fullscreen */
void (*fullscreen_hook)(void);
void (*exit_hook)(void);
void (*capture_get_hook)(void);
void (*capture_rel_hook)(void);
void (*movie_hook)(int argc, char **argv);

int debug;
int do_overlay;
char *snapbase = "snap";
int have_shmem;

unsigned int cur_tv_width,cur_tv_height;
int cur_movie,cur_attrs[256];

/* current hardware driver */
const struct ng_vid_driver  *drv;
void                        *h_drv;
int                          f_drv;

struct ng_attribute         *attrs = NULL;


/* ----------------------------------------------------------------------- */

static int setfreqtab_handler(char *name, int argc, char **argv);
static int setstation_handler(char *name, int argc, char **argv);
static int setchannel_handler(char *name, int argc, char **argv);

static int capture_handler(char *name, int argc, char **argv);
static int volume_handler(char *name, int argc, char **argv);
static int attr_handler(char *name, int argc, char **argv);
static int show_handler(char *name, int argc, char **argv);
static int list_handler(char *name, int argc, char **argv);
static int dattr_handler(char *name, int argc, char **argv);

static int snap_handler(char *name, int argc, char **argv);
static int webcam_handler(char *name, int argc, char **argv);
static int movie_handler(char *name, int argc, char **argv);
static int fullscreen_handler(char *name, int argc, char **argv);
static int msg_handler(char *name, int argc, char **argv);
static int showtime_handler(char *name, int argc, char **argv);
static int vdr_handler(char *name, int argc, char **argv);
#if TT
static int vtx_handler(char *name, int argc, char **argv);
#endif
static int exit_handler(char *name, int argc, char **argv);

static int keypad_handler(char *name, int argc, char **argv);

static struct COMMANDS {
    char  *name;
    int    min_args;
    int   (*handler)(char *name, int argc, char **argv);
} commands[] = {
    { "setstation", 0, setstation_handler },
    { "setchannel", 0, setchannel_handler },
    { "setfreq",    1, setchannel_handler },
    { "setfreqtab", 1, setfreqtab_handler },

    { "capture",    1, capture_handler    },

    { "setnorm",    1, attr_handler       },
    { "setinput",   1, attr_handler       },
    { "setattr",    1, attr_handler       },
    { "color",      0, attr_handler       },
    { "hue",        0, attr_handler       },
    { "bright",     0, attr_handler       },
    { "contrast",   0, attr_handler       },
    { "show",       0, show_handler       },
    { "list",       0, list_handler       },

    { "volume",     0, volume_handler     },
    { "attr",       0, dattr_handler      },

    { "snap",       0, snap_handler       },
    { "webcam",     1, webcam_handler     },
    { "movie",      1, movie_handler      },
    { "fullscreen", 0, fullscreen_handler },
    { "msg",        1, msg_handler        },
#if 0
    { "vtx",        0, vtx_handler        },
#endif
    { "message",    0, msg_handler        },
    { "exit",       0, exit_handler       },
    { "quit",       0, exit_handler       },
    { "bye",        0, exit_handler       },

    { "keypad",     1, keypad_handler     },
    { "showtime",   0, showtime_handler   },
    { "vdr",        1, vdr_handler        },

    { NULL, 0, NULL }
};

static int cur_dattr = 0;
static int dattr[] = {
    ATTR_ID_VOLUME,
    ATTR_ID_BRIGHT,
    ATTR_ID_CONTRAST,
    ATTR_ID_COLOR,
    ATTR_ID_HUE
};
#define NUM_DATTR (sizeof(dattr)/sizeof(char*))

static int keypad_state = -1;

/* ----------------------------------------------------------------------- */

void add_attrs(struct ng_attribute *new)
{
    struct ng_attribute *all;
    int nold,nnew;

    if (attrs)
      for (nold = 0; attrs[nold].name != NULL; nold++)
          ;
    else
      nold = 0;
    for (nnew = 0; new[nnew].name != NULL; nnew++)
      ;
    all = malloc(sizeof(struct ng_attribute) * (nold + nnew + 1));
    memset(all,0,sizeof(struct ng_attribute) * (nold + nnew + 1));
    memcpy(all,new,sizeof(struct ng_attribute)*nnew);
    if (attrs) {
      memcpy(all+nnew,attrs,sizeof(struct ng_attribute)*nold);
      free(attrs);
    }
    attrs = all;

#if 0
    {
      int i;
      fprintf(stderr,"  <attr>\n");
      for (i = 0; attrs[i].name != NULL; i++) {
          fprintf(stderr,"  attr[%p]: %s \n",
                attrs[i].handle,attrs[i].name);
      }
      fprintf(stderr,"  </attr>\n");
    }
#endif
}

void init_overlay(void)
{
    do_va_cmd(2,"setfreqtab",(-1 != chantab)
            ? chanlist_names[chantab].str : "europe-west");

    cur_capture = -1;
    switch (defaults.capture) {
    case CAPTURE_ON:
    case CAPTURE_OVERLAY:
      do_va_cmd(2,"capture","overlay");
      break;
    case CAPTURE_GRABDISPLAY:
      do_va_cmd(2,"capture","grabdisplay");
      break;
    default:
      do_va_cmd(2,"capture","off");
      break;
    }
}

/* ----------------------------------------------------------------------- */

int
do_va_cmd(int argc, ...)
{
    va_list ap;
    int  i;
    char *argv[32];
    
    va_start(ap,argc);
    for (i = 0; i < argc; i++)
      argv[i] = va_arg(ap,char*);
    argv[i] = NULL;
    va_end (ap);
    return do_command(argc,argv);
}

int
do_command(int argc, char **argv)
{
    int i;
    
    if (argc == 0) {
      fprintf(stderr,"do_command: no argument\n");
      return -1;
    }
    if (debug) {
      fprintf(stderr,"cmd:");
      for (i = 0; i < argc; i++) {
          fprintf(stderr," \"%s\"",argv[i]);
      }
      fprintf(stderr,"\n");
    }

    for (i = 0; commands[i].name != NULL; i++)
      if (0 == strcasecmp(commands[i].name,argv[0]))
          break;
    if (commands[i].name == NULL) {
      fprintf(stderr,"no handler for %s\n",argv[0]);
      return -1;
    }
    if (argc-1 < commands[i].min_args) {
      fprintf(stderr,"no enough args for %s\n",argv[0]);
      return -1;
    } else {
      return commands[i].handler(argv[0],argc-1,argv+1);
    }
}

char**
split_cmdline(char *line, int *count)
{
    static char cmdline[1024];
    static char *argv[32];
    int  argc,i;

    strcpy(cmdline,line);
    for (argc=0, i=0; argc<31;) {
      argv[argc++] = cmdline+i;
      while (cmdline[i] != ' ' &&
             cmdline[i] != '\t' &&
             cmdline[i] != '\0')
          i++;
      if (cmdline[i] == '\0')
          break;
      cmdline[i++] = '\0';
      while (cmdline[i] == ' ' ||
             cmdline[i] == '\t')
          i++;
      if (cmdline[i] == '\0')
          break;
    }
    argv[argc] = NULL;

    *count = argc;
    return argv;
}

/* ----------------------------------------------------------------------- */

/* sharing code does'nt work well for this one ... */
static void
set_capture(int capture, int tmp_switch)
{
    static int last_on = CAPTURE_OVERLAY;

    if (set_capture_hook) {
      if (capture == CAPTURE_ON)
          capture = last_on;
      
      if (capture == CAPTURE_OVERLAY) {
          /* can we do overlay ?? */
          if (!(f_drv & CAN_OVERLAY))
            capture = CAPTURE_GRABDISPLAY;
          if (!do_overlay)
            capture = CAPTURE_GRABDISPLAY;
      }

      if (cur_capture != capture) {
          set_capture_hook(cur_capture,capture,tmp_switch);
          cur_capture = capture;
      }
      
      if (cur_capture != CAPTURE_OFF)
          last_on = cur_capture;
    }
}

static void
set_attr(struct ng_attribute *attr, int val)
{
    if (NULL == attr)
      return;

    attr->write(attr,val);
    cur_attrs[attr->id] = val;
    if (attr_notify)
      attr_notify(attr,val);
}

static void
set_volume(void)
{
    struct ng_attribute *attr;
    
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_VOLUME)))
      attr->write(attr,cur_attrs[ATTR_ID_VOLUME]);
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_MUTE)))
      attr->write(attr,cur_attrs[ATTR_ID_MUTE]);

    if (volume_notify)
      volume_notify();
}

static void
set_freqtab(int j)
{
    freq_newtab(j);
    
    /* cur_channel might be invalid (>chancount) right now */
    cur_channel = -1;
    /* this is valid for (struct CHANNEL*)->channel too    */
    calc_frequencies();

    if (freqtab_notify)
      freqtab_notify();
}

static void
set_title(void)
{
    static char  title[256];
    const char *norm;

    keypad_state = -1;
    if (update_title) {
      if (-1 != cur_sender) {
          sprintf(title,"%s",channels[cur_sender]->name);
      } else if (-1 != cur_channel) {
          sprintf(title,"channel %s",chanlist[cur_channel].name);
          if (cur_fine != 0)
            sprintf(title+strlen(title)," (%d)",cur_fine);
          norm = ng_attr_getstr(ng_attr_byid(attrs,ATTR_ID_NORM),
                          cur_attrs[ATTR_ID_NORM]);
          sprintf(title+strlen(title)," (%s/%s)",
                norm ? norm : "???", chanlists[chantab].name);
      } else {
          sprintf(title,"%.3f MHz",cur_freq/16.0);
      }
      update_title(title);
    }
}

static void
set_msg_int(struct ng_attribute *attr, int val)
{
    static char  title[256];
    
    if (display_message) {
      sprintf(title,"%s: %d%%",attr->name,
            ng_attr_int2percent(attr,val));
      display_message(title);
    }
}

static void
set_msg_bool(const char *name, int val)
{
    static char  title[256];
    
    if (display_message) {
      sprintf(title,"%s: %s",name, val ? "on" : "off");
      display_message(title);
    }
}

static void
set_msg_str(char *name, char *val)
{
    static char  title[256];
    
    if (display_message) {
      sprintf(title,"%s: %s",name,val);
      display_message(title);
    }
}

/* ----------------------------------------------------------------------- */

static int update_int(struct ng_attribute *attr, int old, char *new)
{
    int value = old;
    int step;

    step = (attr->max - attr->min) * 3 / 100;
    if (step == 0)
      step = 1;
    
    if (0 == strcasecmp(new,"inc"))
        value += step;
    else if (0 == strcasecmp(new,"dec"))
      value -= step;
    else if (0 == strncasecmp(new,"+=",2))
      value += ng_attr_parse_int(attr,new+2);
    else if (0 == strncasecmp(new,"-=",2))
      value -= ng_attr_parse_int(attr,new+2);
    else if (isdigit(new[0]) || '+' == new[0] || '-' == new[0])
      value = ng_attr_parse_int(attr,new);
    else
      fprintf(stderr,"update_int: can't parse %s\n",new);

    if (value < attr->min)
      value = attr->min;
    if (value > attr->max)
      value = attr->max;
    return value;
}

/* ----------------------------------------------------------------------- */

void
attr_init(void)
{
    struct ng_attribute *attr;
    int val;
    
    for (attr = attrs; attr != NULL && attr->name != NULL; attr++) {
      if (attr->id == ATTR_ID_VOLUME ||
          attr->id == ATTR_ID_MUTE)
          continue;
      val = attr->read(attr);
      if (attr_notify)
          attr_notify(attr,val);
      cur_attrs[attr->id] = val;
    }
    if (-1 == defaults.color &&
      NULL != ng_attr_byid(attrs,ATTR_ID_COLOR))
      defaults.color = cur_attrs[ATTR_ID_COLOR];
    if (-1 == defaults.bright &&
      NULL != ng_attr_byid(attrs,ATTR_ID_BRIGHT))
      defaults.bright = cur_attrs[ATTR_ID_BRIGHT];
    if (-1 == defaults.hue &&
      NULL != ng_attr_byid(attrs,ATTR_ID_HUE))
      defaults.hue = cur_attrs[ATTR_ID_HUE];
    if (-1 == defaults.contrast &&
      NULL != ng_attr_byid(attrs,ATTR_ID_CONTRAST))
      defaults.contrast = cur_attrs[ATTR_ID_CONTRAST];
}

void
audio_init(void)
{
    struct ng_attribute *attr;

    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_VOLUME)))
      cur_attrs[ATTR_ID_VOLUME] = attr->read(attr);
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_MUTE)))
      cur_attrs[ATTR_ID_MUTE] = attr->read(attr);
    if (volume_notify)
      volume_notify();
}

void
audio_on(void)
{
    struct ng_attribute *attr,*list;

    list = drv->list_attrs(h_drv);
    attr = ng_attr_byid(list,ATTR_ID_MUTE);
    if (NULL != attr)
      attr->write(attr,0);
}

void
audio_off(void)
{
    struct ng_attribute *attr,*list;

    list = drv->list_attrs(h_drv);
    attr = ng_attr_byid(list,ATTR_ID_MUTE);
    if (NULL != attr)
      attr->write(attr,1);
}

void
set_defaults(void)
{
    struct ng_attribute *attr;

    /* image parameters */
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_COLOR)))
      set_attr(attr,defaults.color);
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_BRIGHT)))
      set_attr(attr,defaults.bright);
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_HUE)))
      set_attr(attr,defaults.hue);
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_CONTRAST)))
      set_attr(attr,defaults.contrast);
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_INPUT)))
      set_attr(attr,defaults.input);
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_NORM)))
      set_attr(attr,defaults.norm);
    set_capture(defaults.capture,0);

    cur_channel  = defaults.channel;
    cur_fine     = defaults.fine;
    cur_freq     = defaults.freq;
    if (f_drv & CAN_TUNE)
      drv->setfreq(h_drv,defaults.freq);
}

/* ----------------------------------------------------------------------- */

#ifndef HAVE_STRCASESTR
static char* strcasestr(char *haystack, char *needle)
{
    int hlen = strlen(haystack);
    int nlen = strlen(needle);
    int offset;

    for (offset = 0; offset <= hlen - nlen; offset++)
      if (0 == strncasecmp(haystack+offset,needle,nlen))
          return haystack+offset;
    return NULL;
}
#endif

static int setstation_handler(char *name, int argc, char **argv)
{
    struct ng_attribute *attr,*mute;
    int i;

    if (0 == argc) {
      set_title();
      return 0;
    }
    
    if (cur_movie) {
      if (display_message)
          display_message("grabber busy");
      return -1;
    }

    if (count && 0 == strcasecmp(argv[0],"next")) {
      i = (cur_sender+1) % count;
    } else if (count && 0 == strcasecmp(argv[0],"prev")) {
      i = (cur_sender+count-1) % count;
    } else if (count && 0 == strcasecmp(argv[0],"back")) {
      if (-1 == last_sender)
          return -1;
      i = last_sender;
    } else {
      /* search the configured channels first... */
      for (i = 0; i < count; i++)
          if (0 == strcasecmp(channels[i]->name,argv[0]))
            break;
      /* ... next try substring matches ... */
      if (i == count)
          for (i = 0; i < count; i++)
            if (NULL != strcasestr(channels[i]->name,argv[0]))
                break;
      /* ... next try using the argument as index ... */
      if (i == count)
          if (isdigit(argv[0][0]))
            i = atoi(argv[0]);
      if (i == count) {
          /* ... sorry folks */
          fprintf(stderr,"station \"%s\" not found\n",argv[0]);
          return -1;
      }
    }

    /* ok ?? */
    if (i < 0 || i >= count)
      return -1;
    
    /* switch ... */
    if (channel_switch_hook)
      channel_switch_hook();
    set_capture(CAPTURE_OFF,1);

    last_sender = cur_sender;
    cur_sender = i;

    mute = ng_attr_byid(attrs,ATTR_ID_MUTE);
    if (mute && !cur_attrs[ATTR_ID_MUTE])
      mute->write(mute,1);

    /* image parameters */
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_COLOR)))
      set_attr(attr,channels[i]->color);
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_BRIGHT)))
      set_attr(attr,channels[i]->bright);
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_HUE)))
      set_attr(attr,channels[i]->hue);
    if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_CONTRAST)))
      set_attr(attr,channels[i]->contrast);

    /* input / norm */
    if (cur_attrs[ATTR_ID_INPUT] != channels[i]->input)
      if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_INPUT)))
          set_attr(attr,channels[i]->input);
    if (cur_attrs[ATTR_ID_NORM] != channels[i]->norm)
      if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_NORM)))
          set_attr(attr,channels[i]->norm);
    
    /* station */
    cur_channel  = channels[i]->channel;
    cur_fine     = channels[i]->fine;
    cur_freq     = channels[i]->freq;
    if (f_drv & CAN_TUNE)
      drv->setfreq(h_drv,channels[i]->freq);
    set_capture(channels[i]->capture,0);
    
    set_title();
    if (setstation_notify)
      setstation_notify();

    if (mute && !cur_attrs[ATTR_ID_MUTE]) {
      usleep(20000);
      mute->write(mute,0);
    }
    return 0;
}

static int setchannel_handler(char *name, int argc, char **argv)
{
    struct ng_attribute *mute;
    int c,i;
    
    if (0 == argc) {
      set_title();
      return 0;
    }

    if (cur_movie) {
      if (display_message)
          display_message("grabber busy");
      return -1;
    }

    if (0 == strcasecmp("setfreq",name)) {
      cur_freq = (unsigned long)(atof(argv[0])*16);
      cur_sender = -1;
      cur_channel = -1;
      cur_fine = 0;
    } else {
      if (0 == strcasecmp(argv[0],"next")) {
          cur_channel = (cur_channel+1) % chancount;
          cur_fine = defaults.fine;
      } else if (0 == strcasecmp(argv[0],"prev")) {
          cur_channel = (cur_channel+chancount-1) % chancount;
          cur_fine = defaults.fine;
      } else if (0 == strcasecmp(argv[0],"fine_up")) {
          cur_fine++;
      } else if (0 == strcasecmp(argv[0],"fine_down")) {
          cur_fine--;
      } else {
          if (-1 != (c = lookup_channel(argv[0]))) {
            cur_channel = c;
            cur_fine = defaults.fine;
          }
      }
      
      if (0 != strncmp(argv[0],"fine",4)) {
          /* look if there is a known station on that channel */
          for (i = 0; i < count; i++) {
            if (cur_channel == channels[i]->channel) {
                char *argv[2];
                argv[0] = channels[i]->name;
                argv[1] = NULL;
                return setstation_handler("", argc, argv);
            }
          }
      }
      cur_sender  = -1;
      if (-1 != cur_channel)
          cur_freq = get_freq(cur_channel)+cur_fine;
      else {
          cur_freq += cur_fine;
          cur_fine = 0;
      }
    }
    
    if (channel_switch_hook)
      channel_switch_hook();
    set_capture(CAPTURE_OFF,1);

    mute = ng_attr_byid(attrs,ATTR_ID_MUTE);
    if (mute && !cur_attrs[ATTR_ID_MUTE])
      mute->write(mute,1);

    if (f_drv & CAN_TUNE)
      drv->setfreq(h_drv,cur_freq);
    set_capture(defaults.capture,0);

    set_title();
    if (setstation_notify)
      setstation_notify();

    if (mute && !cur_attrs[ATTR_ID_MUTE]) {
      usleep(20000);
      mute->write(mute,0);
    }
    return 0;
}

/* ----------------------------------------------------------------------- */

static void
print_choices(char *name, char *value, struct STRTAB *tab)
{
    int i;
    
    fprintf(stderr,"unknown %s: '%s' (available: ",name,value);
    for (i = 0; tab[i].str != NULL; i++)
      fprintf(stderr,"%s'%s'", (0 == i) ? "" : ", ", tab[i].str);
    fprintf(stderr,")\n");
}

static int setfreqtab_handler(char *name, int argc, char **argv)
{
    int i;

    i = str_to_int(argv[0],chanlist_names);
    if (i != -1)
      set_freqtab(i);
    else
      print_choices("freqtab",argv[0],chanlist_names);
    return 0;
}

static int capture_handler(char *name, int argc, char **argv)
{
    int i;

    if (0 == strcasecmp(argv[0],"toggle")) {
      i = (cur_capture == CAPTURE_OFF) ? CAPTURE_ON : CAPTURE_OFF;
    } else {
      i = str_to_int(argv[0],captab);
    }
    if (i != -1)
      set_capture(i,0);
    return 0;
}

/* ----------------------------------------------------------------------- */

static int volume_handler(char *name, int argc, char **argv)
{
    struct ng_attribute *vol = ng_attr_byid(attrs,ATTR_ID_VOLUME);

    if (0 == argc)
      goto display;
    
    if (0 == strcasecmp(argv[0],"mute")) {
      /* mute on/off/toggle */
      if (argc > 1) {
          switch (str_to_int(argv[1],booltab)) {
          case 0:  cur_attrs[ATTR_ID_MUTE] = 0; break;
          case 1:  cur_attrs[ATTR_ID_MUTE] = 1; break;
          default: cur_attrs[ATTR_ID_MUTE] = !cur_attrs[ATTR_ID_MUTE]; break;
          }
      } else {
          cur_attrs[ATTR_ID_MUTE] = !cur_attrs[ATTR_ID_MUTE];
      }
    } else {
      /* volume */
      if (NULL != vol) {
          cur_attrs[ATTR_ID_VOLUME] = vol->read(vol);
          cur_attrs[ATTR_ID_VOLUME] =
            update_int(vol,cur_attrs[ATTR_ID_VOLUME],argv[0]);
      }
    }
    set_volume();

 display:
    if (cur_attrs[ATTR_ID_MUTE])
      set_msg_str("volume","muted");
    else {
      if (vol)
          set_msg_int(vol,cur_attrs[ATTR_ID_VOLUME]);
      else
          set_msg_str("volume","unmuted");
    }
    return 0;
}

static int attr_handler(char *name, int argc, char **argv)
{
    struct ng_attribute *attr;
    int val,arg=0;

    if (0 == strcasecmp(name,"setnorm")) {
      attr = ng_attr_byname(attrs,"norm");

    } else if (0 == strcasecmp(name,"setinput")) {
      attr = ng_attr_byname(attrs,"input");

    } else if (0 == strcasecmp(name,"setattr") &&
             argc > 0) {
      attr = ng_attr_byname(attrs,argv[arg++]);

    } else {
      attr = ng_attr_byname(attrs,name);
    }

    if (NULL == attr) {
      fprintf(stderr,"cmd: %s: attribute not found\nvalid choices are:",
            (arg > 0) ? argv[0] : name);
      for (attr = attrs; attr->name != NULL; attr++)
          fprintf(stderr,"%s \"%s\"",
                (attr != attrs) ? "," : "", attr->name);
      fprintf(stderr,"\n");
      return -1;
    }

    if (!cur_movie && capture_get_hook)
      capture_get_hook();
    switch (attr->type) {
    case ATTR_TYPE_CHOICE:
      if (argc > arg) {
          if (0 == strcasecmp("next", argv[arg])) {
            val = cur_attrs[attr->id];
            val++;
            if (NULL == attr->choices[val].str)
                val = 0;
          } else {
            val = ng_attr_getint(attr, argv[arg]);
          }
          if (-1 == val) {
            fprintf(stderr,"invalid value for %s: %s\n",attr->name,argv[arg]);
            ng_attr_listchoices(attr);
          } else {
            set_attr(attr,val);
          }
      }
      break;
    case ATTR_TYPE_INTEGER:
      if (argc > arg) {
          cur_attrs[attr->id] = attr->read(attr);
          val = update_int(attr,cur_attrs[attr->id],argv[arg]);
          set_attr(attr,val);
      }
      set_msg_int(attr,cur_attrs[attr->id]);
      break;
    case ATTR_TYPE_BOOL:
      if (argc > arg) {
          val = str_to_int(argv[arg],booltab);
          if (-1 == val) {
            if (0 == strcasecmp(argv[arg],"toggle"))
                val = !cur_attrs[attr->id];
          }
          set_attr(attr,val);
      }
      set_msg_bool(attr->name,cur_attrs[attr->id]);
      break;
    }
    if (!cur_movie && capture_rel_hook)
      capture_rel_hook();
    return 0;
}

static int show_handler(char *name, int argc, char **argv)
{
    struct ng_attribute *attr;
    char *n[2] = { NULL, NULL };
    int val;

    if (0 == argc) {
      for (attr = attrs; attr->name != NULL; attr++) {
          n[0] = (char*)attr->name;
          show_handler("show", 1, n);
      }
      return 0;
    }

    attr = ng_attr_byname(attrs,argv[0]);
    if (NULL == attr) {
      fprintf(stderr,"fixme: 404 %s\n",argv[0]);
      return 0;
    }
    val = cur_attrs[attr->id];
    switch (attr->type) {
    case ATTR_TYPE_CHOICE:
      printf("%s: %s\n", attr->name, ng_attr_getstr(attr,val));
      break;
    case ATTR_TYPE_INTEGER:
      printf("%s: %d\n", attr->name, val);
      break;
    case ATTR_TYPE_BOOL:
      printf("%s: %s\n", attr->name, val ? "on" : "off");
      break;
    }
    return 0;
}

static int list_handler(char *name, int argc, char **argv)
{
    struct ng_attribute *attr;
    int val,i;

    printf("%-10.10s | type   | %-7.7s | %-7.7s | %s\n",
         "attribute","current","default","comment");
    printf("-----------+--------+---------+--------"
         "-+-------------------------------------\n");
    for (attr = attrs; attr->name != NULL; attr++) {
      val = cur_attrs[attr->id];
      switch (attr->type) {
      case ATTR_TYPE_CHOICE:
          printf("%-10.10s | choice | %-7.7s | %-7.7s |",
               attr->name,
               ng_attr_getstr(attr,val),
               ng_attr_getstr(attr,attr->defval));
          for (i = 0; attr->choices[i].str != NULL; i++)
            printf(" %s",attr->choices[i].str);
          printf("\n");
          break;
      case ATTR_TYPE_INTEGER:
          printf("%-10.10s | int    | %7d | %7d | range is %d => %d\n",
               attr->name, val, attr->defval,
               attr->min, attr->max);
          break;
      case ATTR_TYPE_BOOL:
          printf("%-10.10s | bool   | %-7.7s | %-7.7s |\n",
               attr->name,
               val ? "on" : "off",
               attr->defval ? "on" : "off");
          break;
      }
    }
    return 0;
}

static int dattr_handler(char *name, int argc, char **argv)
{
    struct ng_attribute *attr = NULL;
    unsigned int i;
    
    if (argc > 0 && 0 == strcasecmp(argv[0],"next")) {
      for (i = 0; i < NUM_DATTR; i++) {
          cur_dattr++;
          cur_dattr %= NUM_DATTR;
          attr = ng_attr_byid(attrs,dattr[cur_dattr]);
          if (NULL != attr)
            break;
      }
      if (NULL == attr)
          return 0;
      argc = 0;
    }
    if (NULL == attr)
      attr = ng_attr_byid(attrs,dattr[cur_dattr]);
    if (NULL == attr)
      return 0;
    return attr_handler((char*)attr->name,argc,argv);
}

/* ----------------------------------------------------------------------- */

static int snap_handler(char *hname, int argc, char **argv)
{
    char message[512];
    char *tmpfilename = NULL;
    char *filename = NULL;
    char *name;
    int   jpeg = 0;
    int   ret = 0;
    struct ng_video_fmt fmt;
    struct ng_video_buf *buf = NULL;

    if (!(f_drv & CAN_CAPTURE)) {
      fprintf(stderr,"grabbing: not supported [try -noxv switch?]\n");
      return -1;
    }

    if (cur_movie) {
      if (display_message)
          display_message("grabber busy");
      return -1;
    }

    if (capture_get_hook)
      capture_get_hook();

    /* format */
    if (argc > 0) {
      if (0 == strcasecmp(argv[0],"jpeg"))
          jpeg = 1;
      if (0 == strcasecmp(argv[0],"ppm"))
          jpeg = 0;
    }

    /* size */
    memset(&fmt,0,sizeof(fmt));
    fmt.fmtid  = VIDEO_RGB24;
    fmt.width  = 2048;
    fmt.height = 1572;
    if (argc > 1) {
      if (0 == strcasecmp(argv[1],"full")) {
          /* nothing */
      } else if (0 == strcasecmp(argv[1],"win")) {
          fmt.width  = cur_tv_width;
          fmt.height = cur_tv_height;
      } else if (2 == sscanf(argv[1],"%dx%d",&fmt.width,&fmt.height)) {
          /* nothing */
      } else {
          return -1;
      }
    }

    /* filename */
    if (argc > 2)
      filename = argv[2];
    
    if (NULL == (buf = ng_grabber_get_image(&fmt))) {
      if (display_message)
          display_message("grabbing failed");
      ret = -1;
      goto done;
    }
    buf = ng_filter_single(cur_filter,buf);

    if (NULL == filename) {
      if (-1 != cur_sender) {
          name = channels[cur_sender]->name;
      } else if (-1 != cur_channel) {
          name = chanlist[cur_channel].name;
      } else {
          name = "unknown";
      }
      filename = snap_filename(snapbase, name, jpeg ? "jpeg" : "ppm");
    }
    tmpfilename = malloc(strlen(filename)+8);
    sprintf(tmpfilename,"%s.$$$",filename);

    if (jpeg) {
      if (-1 == write_jpeg(tmpfilename, buf, ng_jpeg_quality, 0)) {
          sprintf(message,"open %s: %s\n",tmpfilename,strerror(errno));
      } else {
          sprintf(message,"saved jpeg: %s",filename);
      }
    } else {
      if (-1 == write_ppm(tmpfilename, buf)) {
          sprintf(message,"open %s: %s\n",tmpfilename,strerror(errno));
      } else {
          sprintf(message,"saved ppm: %s",filename);
      }
    }
    unlink(filename);
    if (-1 == link(tmpfilename,filename)) {
      fprintf(stderr,"link(%s,%s): %s\n",
            tmpfilename,filename,strerror(errno));
      goto done;
    }
    unlink(tmpfilename);
    if (display_message)
      display_message(message);

done:
    if (tmpfilename)
      free(tmpfilename);
    if (NULL != buf)
      ng_release_video_buf(buf);
    if (capture_rel_hook)
      capture_rel_hook();
    return ret;
}

static int webcam_handler(char *hname, int argc, char **argv)
{
    struct ng_video_fmt fmt;
    struct ng_video_buf *buf;

    if (webcam)
      free(webcam);
    webcam = strdup(argv[0]);

    /* if either avi recording or grabdisplay is active, we do
       /not/ stop capture and switch the video format.  The next
       capture will send a copy of the frame to the webcam thread
       and it has to deal with it as-is */
    if (cur_movie)
      return 0;
    if (cur_capture == CAPTURE_GRABDISPLAY)
      return 0;

    /* if no capture is running we can switch to RGB first to make
       the webcam happy */
    if (capture_get_hook)
      capture_get_hook();
    memset(&fmt,0,sizeof(fmt));
    fmt.fmtid  = VIDEO_RGB24;
    fmt.width  = cur_tv_width;
    fmt.height = cur_tv_height;
    buf = ng_grabber_get_image(&fmt);
    if (buf)
      ng_release_video_buf(buf);
    if (capture_rel_hook)
      capture_rel_hook();
    return 0;
}

static int movie_handler(char *name, int argc, char **argv)
{
    if (!movie_hook)
      return 0;
    movie_hook(argc,argv);
    return 0;
}

static int
fullscreen_handler(char *name, int argc, char **argv)
{
    if (fullscreen_hook)
      fullscreen_hook();
    return 0;
}

static int
msg_handler(char *name, int argc, char **argv)
{
    if (display_message)
      display_message(argv[0]);
    return 0;
}

static int
showtime_handler(char *name, int argc, char **argv)
{
    char timestr[6];
    struct tm *times;
    time_t timet;

    timet = time(NULL);
    times = localtime(&timet);
    strftime(timestr, 6, "%k:%M", times);
    if (display_message)
      display_message(timestr);
    return 0;
}

static int
exit_handler(char *name, int argc, char **argv)
{
    if (exit_hook)
      exit_hook();
    return 0;
}

/* ----------------------------------------------------------------------- */

static char *strfamily(int family)
{
    switch (family) {
    case PF_INET6: return "ipv6";
    case PF_INET:  return "ipv4";
    case PF_UNIX:  return "unix";
    }
    return "????";
}

static int
tcp_connect(struct addrinfo *ai, char *host, char *serv)
{
    struct addrinfo *res,*e;
    char uhost[INET6_ADDRSTRLEN+1];
    char userv[33];
    int sock,rc,opt=1;

    ai->ai_flags = AI_CANONNAME;
    if (debug)
      fprintf(stderr,"tcp: lookup %s:%s ... ",host,serv);
    if (0 != (rc = getaddrinfo(host, serv, ai, &res))) {
      fprintf(stderr,"tcp: getaddrinfo (%s:%s): %s\n",
            host,serv,gai_strerror(rc));
      return -1;
    }
    if (debug)
      fprintf(stderr,"ok\n");
    for (e = res; e != NULL; e = e->ai_next) {
      if (0 != getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
                       uhost,INET6_ADDRSTRLEN,userv,32,
                       NI_NUMERICHOST | NI_NUMERICSERV)) {
          fprintf(stderr,"tcp: getnameinfo (peer): oops\n");
          continue;
      }
      if (debug)
          fprintf(stderr,"tcp: trying %s (%s:%s) ... ",
                strfamily(e->ai_family),uhost,userv);
      if (-1 == (sock = socket(e->ai_family, e->ai_socktype,
                         e->ai_protocol))) {
          fprintf(stderr,"tcp: socket: %s\n",strerror(errno));
          continue;
      }
        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
      if (-1 == connect(sock,e->ai_addr,e->ai_addrlen)) {
          fprintf(stderr,"tcp: connect: %s\n",strerror(errno));
          close(sock);
          continue;
      }
      if (debug)
          fprintf(stderr,"ok\n");
      fcntl(sock,F_SETFL,O_NONBLOCK);
      fcntl(sock,F_SETFD,FD_CLOEXEC);
      return sock;
    }
    return -1;
}

static int tcp_readbuf(int sock, int timeout, char *dest, char dlen)
{
    struct timeval tv;
    fd_set set;
    int rc;

 again:
    FD_ZERO(&set);
    FD_SET(sock,&set);
    tv.tv_sec  = timeout;
    tv.tv_usec = 0;
    rc = select(sock+1,&set,NULL,NULL,&tv);
    if (-1 == rc && EINTR == errno)
      goto again;
    if (-1 == rc) {
      if (debug)
          perror("tcp: select");
      return -1;
    }
    if (0 == rc) {
      if (debug)
          fprintf(stderr,"tcp: select timeout\n");
      return -1;
    }
    rc = read(sock,dest,dlen-1);
    if (-1 == rc) {
      if (debug)
          perror("tcp: read");
      return -1;
    }
    dest[rc] = 0;
    return rc;
}

static int vdr_sock = -1;

static int
vdr_handler(char *name, int argc, char **argv)
{
    char line[80];
    struct addrinfo ask;
    int i,rc;
    unsigned int l,len;

 reconnect:
    if (-1 == vdr_sock) {
      memset(&ask,0,sizeof(ask));
      ask.ai_family = PF_UNSPEC;
      ask.ai_socktype = SOCK_STREAM;
      vdr_sock = tcp_connect(&ask,"localhost","2001");
      if (-1 == vdr_sock)
          return -1;
      if (debug)
          fprintf(stderr,"vdr: connected\n");

      /* skip greeting line */
      if (-1 == tcp_readbuf(vdr_sock,3,line,sizeof(line)))
          goto oops;
      if (debug)
          fprintf(stderr,"vdr: << %s",line);
    }

    /* send command */
    line[0] = 0;
    for (i = 0, len = 0; i < argc; i++) {
      l = strlen(argv[i]);
      if (len+l+4 > sizeof(line))
          break;
      if (len) {
          strcpy(line+len," ");
          len++;
      }
      strcpy(line+len,argv[i]);
      len += l;
    }
    strcpy(line+len,"\r\n");
    len += 2;
    if (len != (rc = write(vdr_sock,line,len))) {
      if (-1 == rc  &&  EPIPE == errno) {
          if (debug)
            fprintf(stderr,"tcp: write: broken pipe, trying reconnect\n");
          close(vdr_sock);
          vdr_sock = -1;
          goto reconnect;
      }
      if (debug)
          perror("tcp: write");
      goto oops;
    }
    if (debug)
      fprintf(stderr,"vdr: >> %s",line);
    
    /* skip answer */
    if (-1 == tcp_readbuf(vdr_sock,3,line,sizeof(line)))
      goto oops;
    if (debug)
      fprintf(stderr,"vdr: << %s",line);

#if 0
    /* play nicely and close the handle -- vdr can handle only one
     * connection at the same time.  Drawback is that it increases
     * latencies ... */
    close(vdr_sock);
    vdr_sock = -1;
#endif
    return 0;

oops:
    close(vdr_sock);
    vdr_sock = -1;
    return -1;
}

/* ----------------------------------------------------------------------- */

#if TT
static struct TEXTELEM*
parse_vtx(int lines, char **text)
{
    static char *names[8] = { "black", "red", "green", "yellow",
                        "blue", "magenta", "cyan", "white" };
    static struct TEXTELEM tt[VTX_COUNT];
    int i,n,t,ansi;
    char *ansi_fg,*ansi_bg;

    /* parse */
    t = 0;
    memset(tt,0,sizeof(tt));
    for (i = 0; i < lines; i++) {
      tt[t].line = i;
      ansi_fg = NULL; ansi_bg = NULL;
      for (n = 0; text[i][n] != 0;) {
          if (text[i][n] == '\033') {
            if (tt[t].len) {
                t++;
                if (VTX_COUNT == t)
                  return tt;
            }
            n++;
            if (text[i][n] == '[') {
                /* ANSI color tty sequences */
                n++;
                for (ansi=1;ansi;) {
                  switch (text[i][n]) {
                  case '3':
                      n++;
                      if (text[i][n] >= '0' && text[i][n] < '8') {
                        ansi_fg  = names[text[i][n]-'0'];
                        n++;
                      }
                      break;
                  case '4':
                      n++;
                      if (text[i][n] >= '0' && text[i][n] < '8') {
                        ansi_bg  = names[text[i][n]-'0'];
                        n++;
                      }
                      break;
                  case '1':
                  case ';':
                      n++;
                      break;
                  case 'm':
                      n++;
                      /* ok, commit */
                      ansi=0;
                      tt[t].fg = ansi_fg;
                      tt[t].bg = ansi_bg;
                      break;
                  default:
                      /* error */
                      ansi=0;
                  }
                }
            } else {
                /* old way: ESC fg bg */
                if (text[i][n] >= '0' && text[i][n] < '8') {
                  tt[t].fg  = names[text[i][n]-'0'];
                  n++;
                }
                if (text[i][n] >= '0' && text[i][n] < '8') {
                  tt[t].bg  = names[text[i][n]-'0'];
                  n++;
                }
            }
            tt[t].line = i;
          } else {
            tt[t].str[tt[t].len++] = text[i][n];
            n++;
            if (tt[t].len >= VTX_LEN-1) {
                t++;
                if (VTX_COUNT == t)
                  return tt;
                tt[t].line = i;
            }
          }
      }
      if (tt[t].len) {
          t++;
          if (VTX_COUNT == t)
            break;
      }
    }
    return tt;
}

static int
vtx_handler(char *name, int argc, char **argv)
{
    struct TEXTELEM *tt;

    if (vtx_message) {
      if (argc) {
          tt = parse_vtx(argc,argv);
          vtx_message(tt);
      } else {
          vtx_message(NULL);
      }
    }
    return 0;
}
#endif

/* ----------------------------------------------------------------------- */

#define CH_MAX (keypad_ntsc ? 99 : count)

static int
keypad_handler(char *name, int argc, char **argv)
{
    int n = atoi(argv[0])%10;
    char msg[8],ch[8];

    if (debug)
      fprintf(stderr,"keypad: key %d\n",n);
    if (-1 == keypad_state) {
      if ((keypad_partial   &&  n > 0 && n <= CH_MAX) ||
          (!keypad_partial  &&  n > 0 && n <= CH_MAX && 10*n > CH_MAX)) {
          if (keypad_ntsc) {
            sprintf(ch,"%d",n);
            do_va_cmd(2,"setchannel",ch,NULL);
          } else
            do_va_cmd(2,"setstation",channels[n-1]->name,NULL);
      }
      if (n >= 0 && 10*n <= CH_MAX) {
          if (debug)
            fprintf(stderr,"keypad: hang: %d\n",n);
          keypad_state = n;
          if (display_message) {
            sprintf(msg,"%d_",n);
            display_message(msg);
          }
      }
    } else {
      if ((n+keypad_state*10) <= CH_MAX)
          n += keypad_state*10;
      keypad_state = -1;
      if (debug)
          fprintf(stderr,"keypad: ok: %d\n",n);
      if (n > 0 && n <= CH_MAX) {
          if (keypad_ntsc) {
            sprintf(ch,"%d",n);
            do_va_cmd(2,"setchannel",ch,NULL);
          } else
            do_va_cmd(2,"setstation",channels[n-1]->name,NULL);
      }
    }
    return 0;
}

void
keypad_timeout(void)
{
    if (debug)
      fprintf(stderr,"keypad: timeout\n");
    if (keypad_state == cur_sender+1)
      set_title();
    keypad_state = -1;
}

Generated by  Doxygen 1.6.0   Back to index