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

xt.c

/*
 * common X11 stuff (mostly libXt level) moved here from main.c
 *
 *   (c) 1997-2003 Gerd Knorr <kraxel@bytesex.org>
 *
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>

#if defined(__linux__)
# include <sys/ioctl.h>
# include "videodev.h"
#endif

#include "config.h"

#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>
#include <X11/extensions/XShm.h>

#include "grab-ng.h"
#include "commands.h"
#include "sound.h"
#include "toolbox.h"
#include "xv.h"
#include "atoms.h"
#include "xt.h"
#include "x11.h"
#include "wmhooks.h"
#include "channel.h"
#include "capture.h"
#include "midictrl.h"
#include "lirc.h"
#include "joystick.h"
#include "vbi-data.h"
#include "blit.h"
#include "parseconfig.h"
#include "event.h"

/* jwz */
#include "remote.h"

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

XtAppContext      app_context;
Widget            app_shell, tv;
Widget            on_shell;
Display           *dpy;
int               stay_on_top = 0;

XVisualInfo       vinfo;
Colormap          colormap;

int               have_dga   = 0;
int               have_vm    = 0;
int               have_randr = 0;
int               fs = 0;

void              *movie_state;
int               movie_blit;

XtIntervalId      zap_timer,scan_timer;

#ifdef HAVE_LIBXXF86VM
int               vm_count;
XF86VidModeModeInfo **vm_modelines;
XF86VidModeModeLine vm_line;
int                 vm_dot;
#endif
#ifdef HAVE_LIBXINERAMA
XineramaScreenInfo *xinerama;
int                nxinerama;
#endif
#ifdef HAVE_LIBXRANDR
XRRScreenSize      *randr;
int                nrandr;
int                randr_evbase;
#endif

static Widget on_label;
static XtIntervalId title_timer, on_timer;
static char default_title[256] = "???";

static int zap_start,zap_fast;

/*--- args ----------------------------------------------------------------*/

struct ARGS args;

XtResource args_desc[] = {
    /* name, class, type, size, offset, default_type, default_addr */
    {
      /* Strings */
      "device",
      XtCString, XtRString, sizeof(char*),
      XtOffset(struct ARGS*,device),
      XtRString, NULL
    },{
      "driver",
      XtCString, XtRString, sizeof(char*),
      XtOffset(struct ARGS*,driver),
      XtRString, NULL
    },{
      "dspdev",
      XtCString, XtRString, sizeof(char*),
      XtOffset(struct ARGS*,dspdev),
      XtRString, NULL
    },{
      "vbidev",
      XtCString, XtRString, sizeof(char*),
      XtOffset(struct ARGS*,vbidev),
      XtRString, NULL
    },{
      "joydev",
      XtCString, XtRString, sizeof(char*),
      XtOffset(struct ARGS*,joydev),
      XtRString, NULL
    },{
      "basename",
      XtCString, XtRString, sizeof(char*),
      XtOffset(struct ARGS*,basename),
      XtRString, "snap"
    },{
      "conffile",
      XtCString, XtRString, sizeof(char*),
      XtOffset(struct ARGS*,conffile),
      XtRString, NULL
    },{
      /* Integer */
      "debug",
      XtCValue, XtRInt, sizeof(int),
      XtOffset(struct ARGS*,debug),
      XtRString, "0"
    },{
      "bpp",
      XtCValue, XtRInt, sizeof(int),
      XtOffset(struct ARGS*,bpp),
      XtRString, "0"
    },{
      "shift",
      XtCValue, XtRInt, sizeof(int),
      XtOffset(struct ARGS*,shift),
      XtRString, "0"
    },{
      "xvport",
      XtCValue, XtRInt, sizeof(int),
      XtOffset(struct ARGS*,xv_port),
      XtRString, "0"
    },{
      "parallel",
      XtCValue, XtRInt, sizeof(int),
      XtOffset(struct ARGS*,parallel),
      XtRString, "1"
    },{
      "bufcount",
      XtCValue, XtRInt, sizeof(int),
      XtOffset(struct ARGS*,bufcount),
      XtRString, "16"
    },{
      /* Boolean */
      "remote",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,remote),
      XtRString, "0"
    },{
      "readconfig",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,readconfig),
      XtRString, "1"
    },{
      "fullscreen",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,fullscreen),
      XtRString, "0"
    },{
      "fbdev",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,fbdev),
      XtRString, "0"
    },{
      "xv",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,xv),
      XtRString, "1"
    },{
      "xvVideo",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,xv_video),
      XtRString, "1"
    },{
      "xvImage",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,xv_image),
      XtRString, "1"
    },{
      "gl",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,gl),
      XtRString, "1"
    },{
      "vidmode",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,vidmode),
      XtRString, "1"
    },{
      "dga",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,dga),
      XtRString, "1"
    },{
      "randr",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,randr),
      XtRString, "1"
    },{
      "help",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,help),
      XtRString, "0"
    },{
      "hwscan",
      XtCBoolean, XtRBoolean, sizeof(int),
      XtOffset(struct ARGS*,hwscan),
      XtRString, "0"
    }
};

const int args_count = XtNumber(args_desc);

XrmOptionDescRec opt_desc[] = {
    { "-c",          "device",      XrmoptionSepArg, NULL },
    { "-device",     "device",      XrmoptionSepArg, NULL },
    { "-driver",     "driver",      XrmoptionSepArg, NULL },
    { "-C",          "dspdev",      XrmoptionSepArg, NULL },
    { "-dspdev",     "dspdev",      XrmoptionSepArg, NULL },
    { "-vbidev",     "vbidev",      XrmoptionSepArg, NULL },
    { "-joydev",     "joydev",      XrmoptionSepArg, NULL },
    { "-o",          "basename",    XrmoptionSepArg, NULL },
    { "-outfile",    "basename",    XrmoptionSepArg, NULL },
    { "-conffile",   "conffile",    XrmoptionSepArg, NULL },
    
    { "-v",          "debug",       XrmoptionSepArg, NULL },
    { "-debug",      "debug",       XrmoptionSepArg, NULL },
    { "-b",          "bpp",         XrmoptionSepArg, NULL },
    { "-bpp",        "bpp",         XrmoptionSepArg, NULL },
    { "-shift",      "shift",       XrmoptionSepArg, NULL },
    { "-xvport",     "xvport",      XrmoptionSepArg, NULL },
    { "-parallel",   "parallel",    XrmoptionSepArg, NULL },
    { "-bufcount",   "bufcount",    XrmoptionSepArg, NULL },
    
    { "-remote",     "remote",      XrmoptionNoArg,  "1" },
    { "-n",          "readconfig",  XrmoptionNoArg,  "0" },
    { "-noconf",     "readconfig",  XrmoptionNoArg,  "0" },
    { "-f",          "fullscreen",  XrmoptionNoArg,  "1" },
    { "-fullscreen", "fullscreen",  XrmoptionNoArg,  "1" },
    { "-hwscan",     "hwscan",      XrmoptionNoArg,  "1" },
    { "-fb",         "fbdev",       XrmoptionNoArg,  "1" },
    
    { "-xv",         "xv",          XrmoptionNoArg,  "1" },
    { "-noxv",       "xv",          XrmoptionNoArg,  "0" },
    { "-xv-video",   "xvVideo",     XrmoptionNoArg,  "1" },
    { "-noxv-video", "xvVideo",     XrmoptionNoArg,  "0" },
    { "-xv-image",   "xvImage",     XrmoptionNoArg,  "1" },
    { "-noxv-image", "xvImage",     XrmoptionNoArg,  "0" },
    { "-gl",         "gl",          XrmoptionNoArg,  "1" },
    { "-nogl",       "gl",          XrmoptionNoArg,  "0" },

    { "-vm",         "vidmode",     XrmoptionNoArg,  "1" },
    { "-novm",       "vidmode",     XrmoptionNoArg,  "0" },
    { "-dga",        "dga",         XrmoptionNoArg,  "1" },
    { "-nodga",      "dga",         XrmoptionNoArg,  "0" },
    { "-randr",      "randr",       XrmoptionNoArg,  "1" },
    { "-norandr",    "randr",       XrmoptionNoArg,  "0" },
    
    { "-h",          "help",        XrmoptionNoArg,  "1" },
    { "-help",       "help",        XrmoptionNoArg,  "1" },
    { "--help",      "help",        XrmoptionNoArg,  "1" },
};

const int opt_count = (sizeof(opt_desc)/sizeof(XrmOptionDescRec));
/*----------------------------------------------------------------------*/

Boolean
ExitWP(XtPointer client_data)
{
    /* exit if the application is idle,
     * i.e. all the DestroyCallback's are called.
     */
    exit(0);
}

void
ExitCB(Widget widget, XtPointer client_data, XtPointer calldata)
{
    audio_off();
    video_overlay(0);
    video_close();
    do_va_cmd(2,"capture", "off");
    if (fs)
      do_va_cmd(1,"fullscreen");
    XSync(dpy,False);
    drv->close(h_drv);
    XtAppAddWorkProc(app_context,ExitWP, NULL);
    XtDestroyWidget(app_shell);
}

void
do_exit(void)
{
    ExitCB(NULL,NULL,NULL);
}

void
CloseMainAction(Widget widget, XEvent *event,
            String *params, Cardinal *num_params)
{
    if (NULL != event && event->type == ClientMessage) {
      if (debug)
          fprintf(stderr,"CloseMainAction: received %s message\n",
                XGetAtomName(dpy,event->xclient.data.l[0]));
      if ((Atom)event->xclient.data.l[0] == WM_DELETE_WINDOW) {
          /* fall throuth -- popdown window */
      } else {
          /* whats this ?? */
          return;
      }
    }
    ExitCB(widget,NULL,NULL);
}

void
RemoteAction(Widget widget, XEvent * event,
           String * params, Cardinal * num_params)
{
    Atom            type;
    int             format, argc;
    unsigned int    i;
    char            *argv[32];
    unsigned long   nitems, bytesafter;
    unsigned char   *args = NULL;

    if (event->type == PropertyNotify) {
      if (debug > 1)
          fprintf(stderr,"PropertyNotify %s\n",
                XGetAtomName(dpy,event->xproperty.atom));
      if (event->xproperty.atom == _XAWTV_REMOTE &&
          Success == XGetWindowProperty(dpy,
                                event->xproperty.window,
                                event->xproperty.atom,
                                0, (65536 / sizeof(long)),
                                True, XA_STRING,
                                &type, &format, &nitems, &bytesafter,
                                &args) &&
          nitems != 0) {
          for (i = 0, argc = 0; i <= nitems; i += strlen(args + i) + 1) {
            if (i == nitems || args[i] == '\0') {
                argv[argc] = NULL;
                do_command(argc,argv);
                argc = 0;
              } else {
                argv[argc++] = args+i;
              }
          }
          XFree(args);
      }
    }
}

static void
zap_timeout(XtPointer client_data, XtIntervalId *id)
{
    static int muted = 0;

    if (zap_fast && !cur_attrs[ATTR_ID_MUTE]) {
      /* mute for fast channel scan */
      muted = 1;
      do_va_cmd(2,"volume","mute","on");
    }
    /* pixit(); */
    do_va_cmd(2,"setstation","next");
    if (cur_sender != zap_start) {
      zap_timer = XtAppAddTimeOut
          (app_context, zap_fast ? CAP_TIME : ZAP_TIME, zap_timeout,NULL);
    } else {
      if(muted) {
          /* unmute */
          muted = 0;
          do_va_cmd(2,"volume","mute","off");
      }
    }
}

void
ZapAction(Widget widget, XEvent *event,
        String *params, Cardinal *num_params)
{
    if (zap_timer) {
      XtRemoveTimeOut(zap_timer);
      zap_timer = 0;
#if 0
      strcpy(title,"channel hopping off");
      set_timer_title();
#endif
    } else {
      zap_start = (cur_sender == -1) ? 0 : cur_sender;
      zap_fast = 0;
      if (*num_params > 0) {
          if (0 == strcasecmp(params[0],"fast"))
            zap_fast = 1;
      }
      if (count)
          zap_timer = XtAppAddTimeOut
            (app_context, CAP_TIME, zap_timeout,NULL);
    }
}

static void
scan_timeout(XtPointer client_data, XtIntervalId *id)
{
    scan_timer = 0;
    
    /* check */
    if (!(f_drv & CAN_TUNE))
      return;
    if (drv->is_tuned(h_drv))
      return;

    do_va_cmd(2,"setchannel","next");
    scan_timer = XtAppAddTimeOut
      (app_context, SCAN_TIME, scan_timeout, NULL);
}

void
ScanAction(Widget widget, XEvent *event,
         String *params, Cardinal *num_params)
{
    if (channel_switch_hook)
      channel_switch_hook();
    do_va_cmd(2,"setchannel","next");
    scan_timer = XtAppAddTimeOut
      (app_context, SCAN_TIME, scan_timeout,NULL);
}

void
RatioAction(Widget widget, XEvent *event,
          String *params, Cardinal *num_params)
{
    int w,h;
    
    if (2 != *num_params)
      return;
    w = atoi(params[0]);
    h = atoi(params[1]);
    ng_ratio_x = w;
    ng_ratio_y = h;
    do_va_cmd(2,"capture","off");
    do_va_cmd(2,"capture","on");
}

/*--- onscreen display (fullscreen) --------------------------------------*/

void
create_onscreen(WidgetClass class)
{
    on_shell = XtVaCreateWidget("onscreen",transientShellWidgetClass,
                        app_shell,
                        XtNoverrideRedirect,True,
                        XtNvisual,vinfo.visual,
                        XtNcolormap,colormap,
                        XtNdepth,vinfo.depth,
                        NULL);
    on_label = XtVaCreateManagedWidget("label", class, on_shell,
                               NULL);
}

static void
popdown_onscreen(XtPointer client_data, XtIntervalId *id)
{
    if (debug)
      fprintf(stderr,"osd: hide\n");
    XtPopdown(on_shell);
    on_timer = 0;
}

static void
display_onscreen(char *title)
{
    static int first = 1;
    Dimension x,y;

    if (!fs)
      return;
    if (!use_osd)
      return;

    if (debug)
      fprintf(stderr,"osd: show (%s)\n",title);
    XtVaGetValues(app_shell,XtNx,&x,XtNy,&y,NULL);
    XtVaSetValues(on_shell,XtNx,x+osd_x,XtNy,y+osd_y,NULL);
    toolkit_set_label(on_label,title);
    XtPopup(on_shell, XtGrabNone);
    if (wm_stay_on_top && stay_on_top > 0)
      wm_stay_on_top(dpy,XtWindow(on_shell),1);
    if (on_timer)
      XtRemoveTimeOut(on_timer);
    on_timer = XtAppAddTimeOut
      (app_context, ONSCREEN_TIME, popdown_onscreen,NULL);

    if (first) {
      first = 0;
      XDefineCursor(dpy, XtWindow(on_shell), no_ptr);
      XDefineCursor(dpy, XtWindow(on_label), no_ptr);
    }
}

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

Boolean
rec_work(XtPointer client_data)
{
    struct ng_video_buf *buf;

    if (movie_blit) {
      buf = NULL;
      movie_grab_put_video(movie_state, &buf);
      if (buf)
          video_gd_blitframe(&vh,buf);
    } else {
      movie_grab_put_video(movie_state, NULL);
    }
    return False;
}

void
exec_done(int signal)
{
    int pid,stat;

    if (debug)
      fprintf(stderr,"got sigchild\n");
    pid = waitpid(-1,&stat,WUNTRACED|WNOHANG);
    if (debug) {
      if (-1 == pid) {
          perror("waitpid");
      } else if (0 == pid) {
          fprintf(stderr,"oops: got sigchild and waitpid returns 0 ???\n");
      } else if (WIFEXITED(stat)){
          fprintf(stderr,"[%d]: normal exit (%d)\n",pid,WEXITSTATUS(stat));
      } else if (WIFSIGNALED(stat)){
          fprintf(stderr,"[%d]: %s\n",pid,strsignal(WTERMSIG(stat)));
      } else if (WIFSTOPPED(stat)){
          fprintf(stderr,"[%d]: %s\n",pid,strsignal(WSTOPSIG(stat)));
      }
    }
}

static void
exec_output(XtPointer data, int *fd, XtInputId * iproc)
{
    char buffer[81];
    int len;

    switch (len = read(*fd,buffer,80)) {
    case -1: /* error */
      perror("read pipe");
      /* fall */
    case 0:  /* EOF */
      close(*fd);
      XtRemoveInput(*iproc);
      break;
    default: /* got some bytes */
      buffer[len] = 0;
      fprintf(stderr,"%s",buffer);
      break;
    }
}

int
exec_x11(char **argv)
{
    int p[2],pid,i;

    if (debug) {
      fprintf(stderr,"exec: \"%s\"",argv[0]);
      for (i = 1; argv[i] != NULL; i++)
          fprintf(stderr,", \"%s\"",argv[i]);
      fprintf(stderr,"\n");
    }
    pipe(p);
    switch (pid = fork()) {
    case -1:
      perror("fork");
      return -1;
    case 0:
      /* child */
      dup2(p[1],1);
      dup2(p[1],2);
      close(p[0]);
      close(p[1]);
      close(ConnectionNumber(dpy));
      execvp(argv[0],argv);
      fprintf(stderr,"exec %s: %s\n",argv[0],strerror(errno));
      exit(1);
    default:
      /* parent */
      close(p[1]);
      XtAppAddInput(app_context, p[0], (XtPointer) XtInputReadMask,
                  exec_output, NULL);
      break;
    }
    return pid;
}

void
exec_player(char *moviefile)
{
    //static char *command = "xanim +f +Sr +Ze -Zr";
    static char *command = "pia";
    char *cmd;
    char **argv;
    int  argc;
    
    /* go! */
    cmd = malloc(strlen(command)+strlen(moviefile)+5);
    sprintf(cmd,"%s %s",command,moviefile);
    argv = split_cmdline(cmd,&argc);
    exec_x11(argv);
}

void
LaunchAction(Widget widget, XEvent *event,
          String *params, Cardinal *num_params)
{
    char **argv;
    int  i,argc;
    
    if (*num_params != 1)
      return;
    for (i = 0; i < nlaunch; i++) {
      if (0 == strcasecmp(params[0],launch[i].name))
          break;
    }
    if (i == nlaunch)
      return;

    argv = split_cmdline(launch[i].cmdline,&argc);

    switch (fork()) {
    case -1:
      perror("fork");
      break;
    case 0:
      if (debug) {
          fprintf(stderr,"[%d]: exec ",getpid());
          for (i = 0; i < argc; i++) {
            fprintf(stderr,"\"%s\" ",argv[i]);
          }
          fprintf(stderr,"\n");
      }
      execvp(argv[0],argv);
      fprintf(stderr,"execvp %s: %s",argv[0],strerror(errno));
      exit(1);
      break;
    default:
      break;
    }
}

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

XtSignalId sig_id;

static void termsig_handler(XtPointer data, XtSignalId *id)
{
    ExitCB(NULL,NULL,NULL);
}

static void
termsig(int signal)
{
    if (debug)
      fprintf(stderr,"received signal %d [%s]\n",signal,strsignal(signal));
    XtNoticeSignal(sig_id);
}

static void
segfault(int signal)
{
    fprintf(stderr,"[pid=%d] segfault catched, aborting\n",getpid());
    abort();
}

void
xt_siginit(void)
{
    struct sigaction act,old;

    memset(&act,0,sizeof(act));
    sigemptyset(&act.sa_mask);
    act.sa_handler  = exec_done;
    sigaction(SIGCHLD,&act,&old);

    sig_id = XtAppAddSignal(app_context,termsig_handler,NULL);
    act.sa_handler  = termsig;
    sigaction(SIGINT,&act,&old);
    sigaction(SIGTERM,&act,&old);

    act.sa_handler  = SIG_IGN;
    sigaction(SIGPIPE,&act,&old);
    
    if (debug) {
      act.sa_handler  = segfault;
      sigaction(SIGSEGV,&act,&old);
      fprintf(stderr,"main thread [pid=%d]\n",getpid());
    }
}

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

static XtIntervalId xscreensaver_timer;

static void
xscreensaver_timefunc(XtPointer clientData, XtIntervalId *id)
{
    static int first = 1;
    int status;
    char *err;

    if (debug)
      fprintf(stderr,"xscreensaver_timefunc\n");
    xscreensaver_timer = 0;
    if (first) {
      xscreensaver_init(dpy);
      first = 0;
    }
    status = xscreensaver_command(dpy,XA_DEACTIVATE,0,debug,&err);
    if (0 != status) {
      if (debug)
          fprintf(stderr,"xscreensaver_command: %s\n",err);
      return;
    }
    xscreensaver_timer = XtAppAddTimeOut(app_context,60000,
                               xscreensaver_timefunc,NULL);
}

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

#ifdef HAVE_LIBXXF86VM
static void
vidmode_timer(XtPointer clientData, XtIntervalId *id)
{
    do_va_cmd(2,"capture", "on");
}

static void
set_vidmode(XF86VidModeModeInfo *mode)
{
    if (CAPTURE_OVERLAY == cur_capture) {
      do_va_cmd(2,"capture", "off");
      XtAppAddTimeOut(app_context,VIDMODE_DELAY,vidmode_timer,NULL);
    }
    /* usleep(VIDMODE_DELAY*1000); */
    if (debug)
      fprintf(stderr,"switching mode: %d  %d %d %d %d  %d %d %d %d  %d\n",
            mode->dotclock,
            mode->hdisplay,
            mode->hsyncstart,
            mode->hsyncend,
            mode->htotal,
            mode->vdisplay,
            mode->vsyncstart,
            mode->vsyncend,
            mode->vtotal,
            mode->flags);
    XF86VidModeSwitchToMode(dpy,XDefaultScreen(dpy),mode);
    XSync(dpy,False);
}

static void
do_vidmode_modeswitch(int fs_state, int *vp_width, int *vp_height)
{
    static int                  vm_switched;
    static XF86VidModeModeInfo  *vm_current    = NULL;
    static XF86VidModeModeInfo  *vm_fullscreen = NULL;
    int i;
    
    if (fs_state) {
      /* enter fullscreen mode */
      XF86VidModeGetModeLine(dpy,XDefaultScreen(dpy),&vm_dot,&vm_line);
      XF86VidModeGetAllModeLines(dpy,XDefaultScreen(dpy),
                           &vm_count,&vm_modelines);
      vm_fullscreen = NULL;
      for (i = 0; i < vm_count; i++) {
          if (fs_width  == vm_modelines[i]->hdisplay &&
            fs_height == vm_modelines[i]->vdisplay &&
            vm_fullscreen == NULL)
            vm_fullscreen = vm_modelines[i];
          if (vm_line.hdisplay == vm_modelines[i]->hdisplay &&
            vm_line.vdisplay == vm_modelines[i]->vdisplay &&
            vm_current == NULL)
            vm_current = vm_modelines[i];
      }
      if (debug) {
          fprintf(stderr,"vm: current=%dx%d",
                vm_current->hdisplay,vm_current->vdisplay);
          if (vm_fullscreen)
            fprintf(stderr,"fullscreen=%dx%d",
                  vm_fullscreen->hdisplay,vm_fullscreen->vdisplay);
          fprintf(stderr,"\n");
      }
      if (vm_current && vm_fullscreen &&
          vm_fullscreen->hdisplay != vm_current->hdisplay &&
          vm_fullscreen->vdisplay != vm_current->vdisplay) {
          set_vidmode(vm_fullscreen);
          vm_switched = 1;
          *vp_width   = vm_fullscreen->hdisplay;
          *vp_height  = vm_fullscreen->vdisplay;
      } else {
          vm_switched = 0;
          *vp_width   = vm_current->hdisplay;
          *vp_height  = vm_current->vdisplay;
      }
    } else {
      /* leave fullscreen mode */
      if (vm_switched) {
          set_vidmode(vm_current);
          vm_switched = 0;
      }
    }
}
#endif

#ifdef HAVE_LIBXRANDR
static void
do_randr_modeswitch(int fs_state, int *vp_width, int *vp_height)
{
    static SizeID normal;
    Window root = RootWindow(dpy, DefaultScreen(dpy));
    XRRScreenConfiguration *sc;
    Rotation rotation;
    SizeID current, new, i;
    
    sc = XRRGetScreenInfo(dpy, root);
    current = XRRConfigCurrentConfiguration(sc, &rotation);
    new = current;
    if (fs_state) {
      /* enter fullscreen mode */
      normal = current;
      new = current;
      for (i = 0; i < nrandr; i++) {
          if (randr[i].width  == fs_width &&
            randr[i].height == fs_height) {
            new = i;
            break;
          }
      }
    } else {
      /* leave fullscreen mode */
      new = normal;
    }

    if (new != current) {
      /* switch mode */
      if (debug)
          fprintf(stderr, "randr: switch to %dx%d\n",
                randr[new].width, randr[new].height);
      XRRSetScreenConfig(dpy, sc, root, new, rotation, CurrentTime);
    }
    XRRFreeScreenConfigInfo(sc);

    /* FIXME: change swidth / sheight instead */
    *vp_width  = randr[new].width;
    *vp_height = randr[new].height;
}
#endif

static void
do_modeswitch(int fs_state, int *vp_width, int *vp_height)
{
    *vp_width  = swidth;
    *vp_height = sheight;

#ifdef HAVE_LIBXXF86VM
    if (have_vm)
      do_vidmode_modeswitch(fs_state,vp_width,vp_height);
#endif
#ifdef HAVE_LIBXRANDR
    if (!have_vm && have_randr)
      do_randr_modeswitch(fs_state,vp_width,vp_height);
#endif
}

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

Boolean
MyResize(XtPointer client_data)
{
    /* needed for program-triggered resizes (fullscreen mode) */
    video_new_size();
    return TRUE;
}

static void
do_screensaver(int fs_state)
{
    static int timeout,interval,prefer_blanking,allow_exposures;
#ifdef HAVE_LIBXDPMS
    static BOOL dpms_on;
    CARD16 dpms_state;
    int dpms_dummy;
#endif

    if (fs_state) {
      /* fullscreen on -- disable screensaver */
      XGetScreenSaver(dpy,&timeout,&interval,
                  &prefer_blanking,&allow_exposures);
      XSetScreenSaver(dpy,0,0,DefaultBlanking,DefaultExposures);
#ifdef HAVE_LIBXDPMS
      if ((DPMSQueryExtension(dpy, &dpms_dummy, &dpms_dummy)) && 
          (DPMSCapable(dpy))) {
          DPMSInfo(dpy, &dpms_state, &dpms_on);
            DPMSDisable(dpy); 
      }
#endif
      xscreensaver_timer = XtAppAddTimeOut(app_context,60000,
                                   xscreensaver_timefunc,NULL);
    } else {
      /* fullscreen off -- enable screensaver */
      XSetScreenSaver(dpy,timeout,interval,prefer_blanking,allow_exposures);
#ifdef HAVE_LIBXDPMS
      if ((DPMSQueryExtension(dpy, &dpms_dummy, &dpms_dummy)) && 
          (DPMSCapable(dpy)) && (dpms_on)) {
            DPMSEnable(dpy);
      }
#endif
      if (xscreensaver_timer)
          XtRemoveTimeOut(xscreensaver_timer);
    }
}

void
do_fullscreen(void)
{
    static Dimension x,y,w,h;
    static int rpx,rpy;
    static int warp_pointer;

    Window root,child;
    int    wpx,wpy,mask;
    unsigned int vp_width, vp_height;

    if (use_wm_fullscreen && wm_fullscreen) {
      /* full service for us, next to nothing to do */
      fs = !fs;
      if (debug)
          fprintf(stderr,"fullscreen %s via netwm\n", fs ? "on" : "off");

      do_modeswitch(fs,&vp_width,&vp_height);
      XSync(dpy,False);
      wm_fullscreen(dpy,XtWindow(app_shell),fs);

      if (0 == fs  &&  on_timer) {
          XtPopdown(on_shell);
          XtRemoveTimeOut(on_timer);
          on_timer = 0;
      }
      do_screensaver(fs);
      return;
    }

    if (fs) {
      if (debug)
          fprintf(stderr,"turning fs off (%dx%d+%d+%d)\n",w,h,x,y);
      do_modeswitch(0,&vp_width,&vp_height);
      
      if (on_timer) {
          XtPopdown(on_shell);
          XtRemoveTimeOut(on_timer);
          on_timer = 0;
      }
      
      XtVaSetValues(app_shell,
                  XtNwidthInc, WIDTH_INC,
                  XtNheightInc,HEIGHT_INC,
                  XtNx,        x + fs_xoff,
                  XtNy,        y + fs_yoff,
                  XtNwidth,    w,
                  XtNheight,   h,
                  NULL);

      do_screensaver(0);
      if (warp_pointer)
          XWarpPointer(dpy, None, RootWindowOfScreen(XtScreen(tv)),
                   0, 0, 0, 0, rpx, rpy);
      fs = 0;
    } else {
      int vp_x, vp_y;

      if (debug)
          fprintf(stderr,"turning fs on\n");
      XQueryPointer(dpy, RootWindowOfScreen(XtScreen(tv)),
                  &root, &child, &rpx, &rpy, &wpx, &wpy, &mask);

      vp_x = 0;
      vp_y = 0;
      do_modeswitch(1,&vp_width,&vp_height);
      if (vp_width < sheight || vp_width < swidth) {
          /* move viewpoint, make sure the pointer is in there */
          warp_pointer = 1;
          XWarpPointer(dpy, None, RootWindowOfScreen(XtScreen(tv)),
                   0, 0, 0, 0, vp_width/2, vp_height/2);
#ifdef HAVE_LIBXXF86VM
          XF86VidModeSetViewPort(dpy,XDefaultScreen(dpy),0,0);
#endif
      }
      XtVaGetValues(app_shell,
                  XtNx,          &x,
                  XtNy,          &y,
                  XtNwidth,      &w,
                  XtNheight,     &h,
                  NULL);

#ifdef HAVE_LIBXINERAMA
      if (nxinerama) {
          /* check which physical screen we are visible on */
          int i;
          for (i = 0; i < nxinerama; i++) {
            if (x >= xinerama[i].x_org &&
                y >= xinerama[i].y_org && 
                x <  xinerama[i].x_org + xinerama[i].width &&
                y <  xinerama[i].y_org + xinerama[i].height) {
                vp_x      = xinerama[i].x_org;
                vp_y      = xinerama[i].y_org;
                vp_width  = xinerama[i].width;
                vp_height = xinerama[i].height;
                break;
            }
          }
      }
#endif
      if (debug)
          fprintf(stderr,"viewport: %dx%d+%d+%d\n",
                vp_width,vp_height,vp_x,vp_y);

      XtVaSetValues(app_shell,
                  XtNwidthInc,   1,
                  XtNheightInc,  1,
                  NULL);
      XtVaSetValues(app_shell,
                  XtNx,          (vp_x & 0xfffc) + fs_xoff,
                  XtNy,          vp_y            + fs_yoff,
                  XtNwidth,      vp_width,
                  XtNheight,     vp_height,
                  NULL);

        XRaiseWindow(dpy, XtWindow(app_shell));
      do_screensaver(1);
      if (warp_pointer)
          XWarpPointer(dpy, None, XtWindow(tv), 0, 0, 0, 0, 30, 15);
      fs = 1;
    }
    XtAppAddWorkProc (app_context, MyResize, NULL);
}

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

static void
title_timeout(XtPointer client_data, XtIntervalId *id)
{
    keypad_timeout();
    XtVaSetValues(app_shell,XtNtitle,default_title,NULL);
    title_timer = 0;
}

void
new_title(char *txt)
{
    strcpy(default_title,txt);
    XtVaSetValues(app_shell,
              XtNtitle,default_title,
              XtNiconName,default_title,
              NULL);
    display_onscreen(default_title);

    if (title_timer) {
      XtRemoveTimeOut(title_timer);
      title_timer = 0;
    }
}

void
new_message(char *txt)
{
    XtVaSetValues(app_shell,XtNtitle,txt,NULL);
    display_onscreen(txt);
    if (title_timer)
      XtRemoveTimeOut(title_timer);
    title_timer = XtAppAddTimeOut
      (app_context, TITLE_TIME, title_timeout,NULL);
}

/*
 * mode = -1: check mode (just update the title)
 * mode =  0: set autodetect (and read back result)
 * mode >  0: set some mode
 */
void
change_audio(int mode)
{
    struct ng_attribute *attr;
    const char *mname;
    char label[64];

    attr = ng_attr_byid(attrs,ATTR_ID_AUDIO_MODE);
    if (NULL == attr)
      return;

    if (-1 != mode)
      attr->write(attr,mode);
    if (-1 == mode || 0 == mode)
      mode = attr->read(attr);

    mname = ng_attr_getstr(attr,mode);
    if (NULL == mname)
      mname = "???";

    if (attr_notify)
      attr_notify(attr,mode);

    sprintf(label,"%s (%s)",default_title,mname);
    XtVaSetValues(app_shell,XtNtitle,label,NULL);
}

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

void
CommandAction(Widget widget, XEvent *event,
            String *params, Cardinal *num_params)
{
    do_command(*num_params,params);
}

void
set_property(int freq, char *channel, char *name)
{
    int  len;
    char line[80];

    len  = sprintf(line,"%.3f",(float)freq/16)+1;
    len += sprintf(line+len,"%s",channel ? channel : "?") +1;
    len += sprintf(line+len,"%s",name    ? name    : "?") +1;
    XChangeProperty(dpy, XtWindow(app_shell),
                    _XAWTV_STATION, XA_STRING,
                    8, PropModeReplace,
                    line, len);
}

void command_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    struct DO_CMD *cmd = clientdata;
    do_command(cmd->argc,cmd->argv);
}

void tv_expose_event(Widget widget, XtPointer client_data,
                 XEvent *event, Boolean *d)
{
    static GC  gc;
    XGCValues  values;

    switch(event->type) {
    case Expose:
      if (debug)
          fprintf(stderr,"expose count=%d\n",
                event->xexpose.count);
      if (0 == event->xexpose.count && CAPTURE_OVERLAY == cur_capture) {
          if (f_drv & NEEDS_CHROMAKEY) {
            Dimension win_width, win_height;
            if (debug)
                fprintf(stderr,"expose: chromakey [%dx%d]\n",
                      cur_tv_width, cur_tv_height);
            if (0 == gc) {
                XColor color;
                color.red   = (ng_chromakey & 0x00ff0000) >> 8;
                color.green = (ng_chromakey & 0x0000ff00);
                color.blue  = (ng_chromakey & 0x000000ff) << 8;
                XAllocColor(dpy,colormap,&color);
                values.foreground = color.pixel;
                gc = XCreateGC(dpy, XtWindow(widget), GCForeground,
                           &values);
            }
            /* draw background for chroma keying */
            XtVaGetValues(widget, XtNwidth, &win_width,
                        XtNheight, &win_height, NULL);
            XFillRectangle(dpy,XtWindow(widget),gc,
                         (win_width  - cur_tv_width)  >> 1,
                         (win_height - cur_tv_height) >> 1,
                         cur_tv_width, cur_tv_height);
          }
          if (have_xv) {
            if (debug)
                fprintf(stderr,"expose: xv reblit\n");
            video_new_size();
          }
      }
      break;
    }
}

void
FilterAction(Widget widget, XEvent *event,
            String *params, Cardinal *num_params)
{
    struct list_head *item;
    struct ng_filter *filter;

    cur_filter = NULL;
    if (0 == *num_params)
      return;
    list_for_each(item,&ng_filters) {
      filter = list_entry(item, struct ng_filter, list);
      if (0 == strcasecmp(filter->name,params[0])) {
          cur_filter = filter;
          break;
      }
    }
}

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

void
xfree_dga_init(Display *dpy)
{
#ifdef HAVE_LIBXXF86DGA
    int  flags,foo,bar,ma,mi;

    if (!do_overlay)
      return;
    
    if (args.dga) {
      if (XF86DGAQueryExtension(dpy,&foo,&bar)) {
          XF86DGAQueryDirectVideo(dpy,XDefaultScreen(dpy),&flags);
          if (flags & XF86DGADirectPresent) {
            XF86DGAQueryVersion(dpy,&ma,&mi);
            if (debug)
                fprintf(stderr,"DGA version %d.%d\n",ma,mi);
            have_dga = 1;
          }
      }
    }
#endif
}

void
xfree_vm_init(Display *dpy)
{
#ifdef HAVE_LIBXXF86VM
    int  foo,bar,i,ma,mi;

    if (!do_overlay)
      return;

    if (args.vidmode) {
      if (XF86VidModeQueryExtension(dpy,&foo,&bar)) {
          XF86VidModeQueryVersion(dpy,&ma,&mi);
          if (debug)
            fprintf(stderr,"VidMode  version %d.%d\n",ma,mi);
          have_vm = 1;
          XF86VidModeGetAllModeLines(dpy,XDefaultScreen(dpy),
                               &vm_count,&vm_modelines);
          if (debug) {
            fprintf(stderr,"  available video mode(s):");
            for (i = 0; i < vm_count; i++) {
                fprintf(stderr," %dx%d",
                      vm_modelines[i]->hdisplay,
                      vm_modelines[i]->vdisplay);
            }         
            fprintf(stderr,"\n");
          }
      }
    }
#endif
}

void
xfree_randr_init(Display *dpy)
{
#ifdef HAVE_LIBXRANDR
    int bar,i;
    
    if (args.randr) {
      if (XRRQueryExtension(dpy,&randr_evbase,&bar)) {
          randr = XRRSizes(dpy,DefaultScreen(dpy),&nrandr);
          if (nrandr > 0) {
            have_randr = 1;
            if (debug) {
                fprintf(stderr,"xrandr:");
                for (i = 0; i < nrandr; i++) {
                  fprintf(stderr, " %dx%d",
                        randr[i].width, randr[i].height);
                }
                fprintf(stderr,"\n");
            }
          }
      }
    }
#endif
}

void
xfree_xinerama_init(Display *dpy)
{
#ifdef HAVE_LIBXINERAMA
    int foo,bar,i;
    
    if (XineramaQueryExtension(dpy,&foo,&bar) &&
      XineramaIsActive(dpy)) {
      xinerama = XineramaQueryScreens(dpy,&nxinerama);
      for (i = 0; i < nxinerama; i++) {
          fprintf(stderr,"xinerama %d: %dx%d+%d+%d\n",
                xinerama[i].screen_number,
                xinerama[i].width,
                xinerama[i].height,
                xinerama[i].x_org,
                xinerama[i].y_org);
      }
    }
#endif
}

void
grabber_init()
{
    struct ng_video_fmt screen;
    void *base = NULL;

    memset(&screen,0,sizeof(screen));
#ifdef HAVE_LIBXXF86DGA
    if (have_dga) {
      int bar,fred;
      XF86DGAGetVideoLL(dpy,XDefaultScreen(dpy),(void*)&base,
                    &screen.bytesperline,&bar,&fred);
    }
#endif
    if (!do_overlay) {
      if (debug)
          fprintf(stderr,"x11: remote display (overlay disabled)\n");
      drv = ng_vid_open(args.device, args.driver, NULL, base, &h_drv);
    } else {
      screen.width  = XtScreen(app_shell)->width;
      screen.height = XtScreen(app_shell)->height;
      screen.fmtid  = x11_dpy_fmtid;
      screen.bytesperline *= ng_vfmt_to_depth[x11_dpy_fmtid]/8;
      if (debug)
          fprintf(stderr,"x11: %dx%d, %d bit/pixel, %d byte/scanline%s%s\n",
                screen.width,screen.height,
                ng_vfmt_to_depth[screen.fmtid],
                screen.bytesperline,
                have_dga ? ", DGA"     : "",
                have_vm  ? ", VidMode" : "");
      drv = ng_vid_open(args.device, args.driver, &screen, base, &h_drv);
    }
    if (NULL == drv) {
      fprintf(stderr,"no video grabber device available\n");
      exit(1);
    }
    f_drv = drv->capabilities(h_drv);
    add_attrs(drv->list_attrs(h_drv));
}

void
grabber_scan(void)
{
    const struct ng_vid_driver  *driver;
    void *handle;
    struct stat st;
    int n,i,fh,flags;

    for (i = 0; ng_dev.video_scan[i] != NULL; i++) {
      if (-1 == lstat(ng_dev.video_scan[i],&st)) {
          if (ENOENT == errno)
            continue;
          fprintf(stderr,"%s: %s\n",ng_dev.video_scan[i],strerror(errno));
          continue;
      }
      fh = open(ng_dev.video_scan[i],O_RDWR);
      if (-1 == fh) {
          if (ENODEV == errno)
            continue;
          fprintf(stderr,"%s: %s\n",ng_dev.video_scan[i],strerror(errno));
          continue;
      }
      close(fh);

      driver = ng_vid_open(ng_dev.video_scan[i], args.driver,
                       NULL, NULL, &handle);
      if (NULL == driver) {
          fprintf(stderr,"%s: initialization failed\n",ng_dev.video_scan[i]);
          continue;
      }
      flags = driver->capabilities(handle);
      n = fprintf(stderr,"%s: OK",ng_dev.video_scan[i]);
      fprintf(stderr,"%*s[ -device %s ]\n",40-n,"",ng_dev.video_scan[i]);
      fprintf(stderr,"    type : %s\n",driver->name);
      if (driver->get_devname)
          fprintf(stderr,"    name : %s\n",driver->get_devname(handle));
      fprintf(stderr,"    flags: %s %s %s %s\n",
            (flags & CAN_OVERLAY)     ? "overlay"   : "",
            (flags & CAN_CAPTURE)     ? "capture"   : "",
            (flags & CAN_TUNE)        ? "tuner"     : "",
            (flags & NEEDS_CHROMAKEY) ? "chromakey" : "");
      driver->close(handle);
      fprintf(stderr,"\n");
    }
    exit(0);
}

void
x11_check_remote()
{
#if defined(HAVE_GETNAMEINFO) && defined(HAVE_SOCKADDR_STORAGE)
    int fd = ConnectionNumber(dpy);
    struct sockaddr_storage ss;
    char me[INET6_ADDRSTRLEN+1];
    char peer[INET6_ADDRSTRLEN+1];
    char port[17];
    int length;

    if (debug)
      fprintf(stderr, "check if the X-Server is local ... ");
    
    /* me */
    length = sizeof(ss);
    if (-1 == getsockname(fd,(struct sockaddr*)&ss,&length)) {
      perror("getsockname");
      return;
    }
    if (debug)
      fprintf(stderr,"*");

    /* catch unix sockets on FreeBSD */
    if (0 == length || ss.ss_family == AF_UNIX) {
      if (debug)
          fprintf(stderr, " ok (unix socket)\n");
      return;
    }
    
    getnameinfo((struct sockaddr*)&ss,length,
            me,INET6_ADDRSTRLEN,port,16,
            NI_NUMERICHOST | NI_NUMERICSERV);
    if (debug)
      fprintf(stderr,"*");

    /* peer */
    length = sizeof(ss);
    if (-1 == getpeername(fd,(struct sockaddr*)&ss,&length)) {
      perror("getsockname");
      return;
    }
    if (debug)
      fprintf(stderr,"*");

    getnameinfo((struct sockaddr*)&ss,length,
            peer,INET6_ADDRSTRLEN,port,16,
            NI_NUMERICHOST | NI_NUMERICSERV);
    if (debug)
      fprintf(stderr,"*");

    if (debug)
      fprintf(stderr," ok\nx11 socket: me=%s, server=%s\n",me,peer);
    if (0 != strcmp(me,peer))
      /* different hosts => assume remote display */
      do_overlay = 0;
#endif
    return;
}

void x11_misc_init(Display *dpy)
{
    fcntl(ConnectionNumber(dpy),F_SETFD,FD_CLOEXEC);
}

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

void
visual_init(char *n1, char *n2)
{
    Visual         *visual;
    XVisualInfo    *vinfo_list;
    int            n;

    /* look for a useful visual */
    visual = x11_find_visual(XtDisplay(app_shell));
    vinfo.visualid = XVisualIDFromVisual(visual);
    vinfo_list = XGetVisualInfo(dpy, VisualIDMask, &vinfo, &n);
    vinfo = vinfo_list[0];
    XFree(vinfo_list);
    if (visual != DefaultVisualOfScreen(XtScreen(app_shell))) {
      fprintf(stderr,"switching visual (0x%lx)\n",vinfo.visualid);
      colormap = XCreateColormap(dpy,RootWindowOfScreen(XtScreen(app_shell)),
                           vinfo.visual,AllocNone);
      XtDestroyWidget(app_shell);
      app_shell = XtVaAppCreateShell(n1,n2,
                               applicationShellWidgetClass, dpy,
                               XtNvisual,vinfo.visual,
                               XtNcolormap,colormap,
                               XtNdepth, vinfo.depth,
                               NULL);
    } else {
      colormap = DefaultColormapOfScreen(XtScreen(app_shell));
    }
    x11_init_visual(XtDisplay(app_shell),&vinfo);
}

void
v4lconf_init()
{
    if (!do_overlay)
      return;

    strcpy(ng_v4l_conf,"v4l-conf");
    if (!args.debug)
      strcat(ng_v4l_conf," -q");
    if (args.fbdev)
      strcat(ng_v4l_conf," -f");
    if (args.shift)
      sprintf(ng_v4l_conf+strlen(ng_v4l_conf)," -s %d",args.shift);
    if (args.bpp)
      sprintf(ng_v4l_conf+strlen(ng_v4l_conf)," -b %d",args.bpp);
    if (args.device)
      sprintf(ng_v4l_conf+strlen(ng_v4l_conf)," -c %s",args.device);
}

static void
usage(void)
{
    fprintf(stderr,
          "\n"
          "usage: xawtv [ options ] [ station ]\n"
          "options:\n"
          "  -h  -help           print this text\n"
          "  -v  -debug n        debug level n, n = [0..2]\n"
          "      -remote         assume remote display\n"
          "  -n  -noconf         don't read the config file\n"
          "  -m  -nomouse        startup with mouse pointer disabled\n"
          "  -f  -fullscreen     startup in fullscreen mode\n"
#ifdef HAVE_LIBXXF86DGA
          "      -(no)dga        enable/disable DGA extention\n"
#endif
#ifdef HAVE_LIBXXF86VM
          "      -(no)vm         enable/disable VidMode extention\n"
#endif
#ifdef HAVE_LIBXRANDR
          "      -(no)randr      enable/disable Xrandr extention\n"
#endif
#ifdef HAVE_LIBXV
          "      -(no)xv         enable/disable Xvideo extention altogether\n"
          "      -(no)xv-video   enable/disable Xvideo extention (for video only,\n"
          "                      i.e. XvPutVideo() calls)\n"
          "      -(no)xv-image   enable/disable Xvideo extention (for image scaling\n"
          "                      only, i.e. XvPutImage() calls)\n"
#endif
#ifdef HAVE_GL
          "      -(no)gl         enable/disable OpenGL\n"
#endif
          "  -b  -bpp n          color depth of the display is n (n=24,32)\n"
          "  -o  -outfile file   filename base for snapshots\n"
          "  -c  -device file    use <file> as video4linux device\n"
          "  -C  -dspdev file    use <file> as audio (oss) device\n"
          "      -vbidev file    use <file> as vbi device\n"
          "      -joydev file    use <file> as joystick device\n"
          "      -shift x        shift display by x bytes\n"
          "      -fb             let fb (not X) set up v4l device\n"
          "      -parallel n     use n compression threads\n"
          "      -bufcount n     use n video buffers\n"
          "      -hwscan         print a list of available devices.\n"
          "station:\n"
          "  this is one of the stations listed in $HOME/.xawtv\n"
          "\n"
          "Check the manual page for a more detailed description.\n"
          "\n"
          "--\n"
          "Gerd Knorr <kraxel@bytesex.org>\n");
}

void
hello_world(char *name)
{
    struct utsname uts;

    if (0 == geteuid() && 0 != getuid()) {
      fprintf(stderr,"%s *must not* be installed suid root\n",name);
      exit(1);
    }
    
    uname(&uts);
    fprintf(stderr,"This is %s-%s, running on %s/%s (%s)\n",
          name,VERSION,uts.sysname,uts.machine,uts.release);
}

void
handle_cmdline_args(void)
{
    XtGetApplicationResources(app_shell,&args,
                        args_desc,args_count,
                        NULL,0);
    if (args.help) {
      usage();
      exit(0);
    }
    snapbase = args.basename;
    debug    = args.debug;
    ng_debug = args.debug;

    if (0 == args.xv) {
      args.xv_video = 0;
      args.xv_image = 0;
    }
    if (NULL == args.dspdev)
      args.dspdev = ng_dev.dsp;
    if (NULL == args.vbidev)
      args.vbidev = ng_dev.vbi;
    if (NULL == args.device) {
      args.device = ng_dev.video;
    } else {
      args.xv_video = 0;
    }
    if (0 != args.xv_port)
      args.xv_video = 1;
}

int
x11_ctrl_alt_backspace(Display *dpy)
{
    fprintf(stderr,"game over\n");
    if (debug)
      abort();
    audio_off();
    video_overlay(0);
    video_close();
    drv->close(h_drv);
    exit(0);
}

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

static int mouse_visible;
static XtIntervalId mouse_timer;

static void
mouse_timeout(XtPointer clientData, XtIntervalId *id)
{
    Widget widget = clientData;
    if (debug > 1)
      fprintf(stderr,"xt: pointer hide\n");
    if (XtWindow(widget))
      XDefineCursor(dpy, XtWindow(widget), no_ptr);
    mouse_visible = 0;
    mouse_timer = 0;
}

void
mouse_event(Widget widget, XtPointer client_data, XEvent *event, Boolean *d)
{
    if (!mouse_visible) {
      if (debug > 1)
          fprintf(stderr,"xt: pointer show\n");
      if (XtWindow(widget))
          XDefineCursor(dpy, XtWindow(widget), left_ptr);
      mouse_visible = 1;
    }
    if (mouse_timer)
      XtRemoveTimeOut(mouse_timer);
    mouse_timer = XtAppAddTimeOut(app_context, 1000, mouse_timeout,widget);
}

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

#if TT
static char *vtx_colors[8] = { "black", "red", "green", "yellow",
                         "blue", "magenta", "cyan", "white" };
#endif

#ifdef HAVE_ZVBI
static XtInputId x11_vbi_input;
static struct vbi_state *x11_vbi;
static int x11_vbi_page;
char x11_vbi_station[64];

#if 0
static struct TEXTELEM*
vtx_to_tt(struct vt_page *vtp)
{
    static struct TEXTELEM tt[VTX_COUNT];
    int t,x,y,color,lcolor;
    struct fmt_page pg[1];
    struct fmt_char l[W+2];
#define L (l+1)
    
    t = 0;
    fmt_page(fmt,pg,vtp);
    memset(tt,0,sizeof(tt));
    for (y = 0; y < H; y++) {
      for (x = 0; x < W; x++) {
          struct fmt_char c = pg->data[y][x];
          switch (c.ch) {
          case 0x00:
          case 0xa0:
            c.ch = ' ';
            break;
          case 0x7f:
            c.ch = '*';
            break;
          case BAD_CHAR:
            c.ch = '?';
            break;
          default:
            if (c.attr & EA_GRAPHIC)
                c.ch = '#';
            break;
          }
          L[x] = c;
      }
      
      /* delay fg and attr changes as far as possible */
      for (x = 0; x < W; ++x)
          if (L[x].ch == ' ') {
            L[x].fg = L[x-1].fg;
            L[x].attr = L[x-1].attr;
          }
      
      /* move fg and attr changes to prev bg change point */
      for (x = W-1; x >= 0; x--)
          if (L[x].ch == ' ' && L[x].bg == L[x+1].bg) {
            L[x].fg = L[x+1].fg;
            L[x].attr = L[x+1].attr;
          }
      
      /* now render the line */
      lcolor = -1;
      tt[t].line = y;
      tt[t].len  = 0;
      for (x = 0; x < W; x++) {
          color = (L[x].fg&0x0f) * 10 + (L[x].bg&0x0f);
          if (color != lcolor) {
            if (-1 != lcolor)
                if (tt[t].len) {
                  t++;
                  tt[t].line = y;
                }
            lcolor = color;
          }
          tt[t].str[tt[t].len++] = L[x].ch;
          tt[t].fg = vtx_colors[L[x].fg&0x0f];
          tt[t].bg = vtx_colors[L[x].bg&0x0f];
      }
      if (tt[t].len)
          t++;
    }
    return tt;
}

#define EMPTY_VBI_LINE "                                        "
static struct TEXTELEM*
tt_pick_subtitle(struct TEXTELEM *tt)
{
    int i;

    /* skip header line */
    while (tt->len && 0 == tt->line)
      tt++;
    /* rm empty lines (from top) */
    while (tt->len &&
         0 == strcmp(tt->str,EMPTY_VBI_LINE))
      tt++;

    if (tt->len) {
      /* seek to end */
      for (i = 0; tt[i].len != 0; i++)
          ;
      /* rm empty lines (from bottom) */
      while (0 == strcmp(tt[i-1].str,EMPTY_VBI_LINE))
          i--;
      tt[i].len = 0;
    }
    
    return tt;
}

static void
dump_tt(struct TEXTELEM *tt)
{
    int i,lastline = 0;

    lastline = tt[0].line;
    for (i = 0; tt[i].len > 0; i++) {
      if (tt[i].line != lastline) {
          lastline = tt[i].line;
          fprintf(stderr,"\n");
      }
      fprintf(stderr,"[%s,%s,%d]%s",
            tt[i].fg ? tt[i].fg : "def",
            tt[i].bg ? tt[i].bg : "def",
            tt[i].line,tt[i].str);
    }
    fprintf(stderr,"\n");
}
#endif

static void
x11_vbi_event(struct vbi_event *ev, void *user)
{
    struct vbi_page pg;
    struct vbi_rect rect;
    
    switch (ev->type) {
    case VBI_EVENT_NETWORK:
      strcpy(x11_vbi_station,ev->ev.network.name);
      break;
    case VBI_EVENT_TTX_PAGE:
      if (ev->ev.ttx_page.pgno == x11_vbi_page) {
          if (debug)
            fprintf(stderr,"got vtx page %03x\n",ev->ev.ttx_page.pgno);
          if (vtx_subtitle) {
            vbi_fetch_vt_page(x11_vbi->dec,&pg,
                          ev->ev.ttx_page.pgno,
                          ev->ev.ttx_page.subno,
                          VBI_WST_LEVEL_1p5,25,1);
            vbi_find_subtitle(&pg,&rect);
            if (25 == rect.y1)
                vtx_subtitle(NULL,NULL);
            else
                vtx_subtitle(&pg,&rect);
          }
      }
      break;
    }
}

static void x11_vbi_data(XtPointer data, int *fd, XtInputId *iproc)
{
    struct vbi_state *vbi = data;
    vbi_hasdata(vbi);
}

int
x11_vbi_start(char *device)
{
    if (NULL != x11_vbi)
      return 0;

    if (NULL == device)
      device = ng_dev.vbi;

    x11_vbi = vbi_open(device, debug, 0);
    if (NULL == x11_vbi) {
      fprintf(stderr,"open %s: %s\n",device,strerror(errno));
      return -1;
    }
    vbi_event_handler_add(x11_vbi->dec,~0,x11_vbi_event,x11_vbi);
    x11_vbi_input = XtAppAddInput(app_context,x11_vbi->fd,
                          (XtPointer) XtInputReadMask,
                          x11_vbi_data,x11_vbi);
    if (debug)
      fprintf(stderr,"x11_vbi_start\n");
    return 0;
}

/* try to use the vbi device to see whenever we have a station or not.
 * failing that, fallback to the libng grabber driver. */
int
x11_vbi_tuned(void)
{
#if defined(__linux__)
    struct video_tuner tuner;

    if (NULL == x11_vbi)
      return drv->is_tuned(h_drv);
    memset(&tuner,0,sizeof(tuner));
    if (-1 != ioctl(x11_vbi->fd,VIDIOCGTUNER,&tuner))
      return tuner.signal ? 1 : 0;
#endif
    return drv->is_tuned(h_drv);
}

void
x11_vbi_stop(void)
{
    if (NULL == x11_vbi)
      return;
    XtRemoveInput(x11_vbi_input);
    vbi_event_handler_remove(x11_vbi->dec,x11_vbi_event);
    vbi_close(x11_vbi);
    x11_vbi = NULL;
    if (debug)
      fprintf(stderr,"x11_vbi_stop\n");
}

void
VtxAction(Widget widget, XEvent *event,
        String *params, Cardinal *num_params)
{
    if (0 == *num_params)
      return;
    if (0 == strcasecmp(params[0],"start")) {
      if (2 >= *num_params)
          sscanf(params[1],"%x",&x11_vbi_page);
      if (debug)
          fprintf(stderr,"subtitles page: %x\n",x11_vbi_page);
      x11_vbi_start(args.vbidev);
    }
    if (0 == strcasecmp(params[0],"stop")) {
      x11_vbi_page = 0;
      x11_vbi_stop();
#if TT
      if (vtx_message)
          vtx_message(NULL);
#endif
#ifdef HAVE_ZVBI
      if (vtx_subtitle)
          vtx_subtitle(NULL,NULL);
#endif
    }
}
#endif

/* ---------------------------------------------------------------------- */
/* control via lirc / midi / joystick                                     */

static int xt_lirc;

static void
xt_lirc_data(XtPointer data, int *fd, XtInputId *iproc)
{
    if (debug)
      fprintf(stderr,"lirc_input triggered\n");
    if (-1 == lirc_tv_havedata()) {
      fprintf(stderr,"lirc: connection lost\n");
      XtRemoveInput(*iproc);
      close(*fd);
    }
}

int xt_lirc_init(void)
{
    if (-1 != (xt_lirc = lirc_tv_init()))
      XtAppAddInput(app_context,xt_lirc,(XtPointer)XtInputReadMask,
                  xt_lirc_data,NULL);
    return 0;
}

#ifdef HAVE_ALSA
static struct midi_handle xt_midi;

static void
xt_midi_data(XtPointer data, int *fd, XtInputId *iproc)
{
    midi_read(&xt_midi);
    midi_translate(&xt_midi);
}
#endif

int xt_midi_init(char *dev)
{
    if (NULL == dev)
      return -1;

#ifdef HAVE_ALSA
    memset(&xt_midi,0,sizeof(xt_midi));
    if (-1 == midi_open(&xt_midi, "xawtv"))
      return -1;
    midi_connect(&xt_midi,dev);
    XtAppAddInput(app_context,xt_midi.fd,(XtPointer) XtInputReadMask,
              xt_midi_data,NULL);
    return 0;
#else
    fprintf(stderr,"midi: not compiled in, sorry\n");
    return -1;
#endif
}

static int xt_joystick;

static void
xt_joystick_data(XtPointer data, int *fd, XtInputId *iproc)
{
    joystick_tv_havedata(xt_joystick);
}

int xt_joystick_init(void)
{
    if (-1 != (xt_joystick = joystick_tv_init(args.joydev)))
      XtAppAddInput(app_context,xt_joystick,(XtPointer)XtInputReadMask,
                  xt_joystick_data,NULL);
    return 0;
}

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

void xt_kbd_init(Widget tv)
{
    char **list,key[32],str[128];

    list = cfg_list_entries("eventmap");
    if (NULL == list)
      return;
    
    for (; *list != NULL; list++) {
      if (1 != sscanf(*list,"kbd-key-%31s",key))
          continue;
      sprintf(str,"<Key>%s: Event(%s)",key,*list);
      XtOverrideTranslations(tv,XtParseTranslationTable(str));
    }
}

void
EventAction(Widget widget, XEvent *event,
          String *params, Cardinal *num_params)
{
    if (0 != *num_params)
      event_dispatch(params[0]);
}

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

Cursor left_ptr;
Cursor menu_ptr;
Cursor qu_ptr;
Cursor no_ptr;

Pixmap bm_yes;
Pixmap bm_no;

static unsigned char bm_yes_data[] = {
    /* -------- -------- */  0x00,
    /* -------- -------- */  0x00,
    /* ------xx xx------ */  0x18,                 
    /* ----xxxx xxxx---- */  0x3c,
    /* ----xxxx xxxx---- */  0x3c,
    /* ------xx xx------ */  0x18,
    /* -------- -------- */  0x00,
    /* -------- -------- */  0x00
};

static unsigned char bm_no_data[] = { 0,0,0,0, 0,0,0,0 };

void
create_pointers(Widget app_shell)
{
    XColor white,red,dummy;
    Screen *scr;
    
    left_ptr = XCreateFontCursor(dpy,XC_left_ptr);
    menu_ptr = XCreateFontCursor(dpy,XC_right_ptr);
    qu_ptr   = XCreateFontCursor(dpy,XC_question_arrow);
    scr = DefaultScreenOfDisplay(dpy);
    if (vinfo.depth > 1) {
      if (XAllocNamedColor(dpy,colormap,"white",&white,&dummy) &&
          XAllocNamedColor(dpy,colormap,"red",&red,&dummy)) {
          XRecolorCursor(dpy,left_ptr,&red,&white);
          XRecolorCursor(dpy,menu_ptr,&red,&white);
          XRecolorCursor(dpy,qu_ptr,&red,&white);
      } 
    }
}

void
create_bitmaps(Widget app_shell)
{
    XColor black, dummy;

    bm_yes = XCreateBitmapFromData(dpy, XtWindow(app_shell),
                           bm_yes_data, 8,8);
    bm_no = XCreateBitmapFromData(dpy, XtWindow(app_shell),
                          bm_no_data, 8,8);

    XAllocNamedColor(dpy,colormap,"black",&black,&dummy);
    no_ptr = XCreatePixmapCursor(dpy, bm_no, bm_no,
                         &black, &black,
                         0, 0);
}

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

int xt_handle_pending(Display *dpy)
{
    XEvent event;

    if (debug)
      fprintf(stderr,"xt: handle_pending:  start ...\n");
    XFlush(dpy);
    while (True == XCheckMaskEvent(dpy, ~0, &event))
      XtDispatchEvent(&event);
    if (debug)
      fprintf(stderr,"xt: handle_pending:  ... done\n");
    return 0;
}

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

int xt_vm_randr_input_init(Display *dpy)
{
    /* vidmode / randr */
    if (debug)
      fprintf(stderr,"xt: checking for randr extention ...\n");
    xfree_randr_init(dpy);
#if 0
    if (fs_width && fs_height && !args.vidmode) {
      if (debug)
          fprintf(stderr,"fullscreen mode configured (%dx%d), "
                "VidMode extention enabled\n",fs_width,fs_height);
      args.vidmode = 1;
    }
#endif
    if (debug)
      fprintf(stderr,"xt: checking for vidmode extention ...\n");
    xfree_vm_init(dpy);
    
    /* input */
    if (debug)
      fprintf(stderr,"xt: checking for lirc ...\n");
    xt_lirc_init();
    if (debug)
      fprintf(stderr,"xt: checking for joystick ...\n");
    xt_joystick_init();
    if (debug)
      fprintf(stderr,"xt: checking for midi ...\n");
    xt_midi_init(midi);
    if (debug)
      fprintf(stderr,"xt: adding kbd hooks ...\n");
    xt_kbd_init(tv);

    return 0;
}

int xt_main_loop()
{
    XEvent event;
    
    if (debug)
      fprintf(stderr,"xt: enter main event loop... \n");
    signal(SIGHUP,SIG_IGN); /* don't really need a tty ... */

    for (;;) {
      if (XtAppGetExitFlag(app_context))
          break;
      XtAppNextEvent(app_context, &event);
      if (True == XtDispatchEvent(&event))
          continue;
    }
    return 0;
}

Generated by  Doxygen 1.6.0   Back to index