#include #include #include #include #include #include #include #include #include #include #include "readloop-X.h" #include "readloop-cvt.h" extern const char *__progname; static const char *videodev = "viddev"; #define NFRAMES 64 #define MAXCMDLINE 1024 #define SKIPHISTN 64 typedef enum { EVT_NONE = 1, EVT_PIC, EVT_KEY, EVT_DSP, } EVTYPE; typedef struct ievent IEVENT; struct ievent { EVTYPE t; union { unsigned char pic; unsigned char key; unsigned char dsp; } ; } ; static int vfd; static unsigned char *framebuf; static unsigned int framelen; static int rpipe; static int dpipe; static unsigned char *bnv; static int ifd; static struct termios old_tio; static struct termios new_tio; static char icmdbuf[MAXCMDLINE]; static int icmdlen; static int icmdcurs; static char icmddbuf[MAXCMDLINE]; static int icmddlen; static int icmddcurs; static char icmdobuf[MAXCMDLINE+1]; static int icmdolen; static int icmdocurs; static unsigned char icmdpref; static unsigned char twirler; static const unsigned char twirl[4] = "|/-\\"; static int twirlx; static CVTPARAMS cpar; static int displaying; static int skipped; static int skiphist[SKIPHISTN]; static int skiphisthand; static double daskip; static double daskipf; static void open_video(void) { vfd = open(videodev,O_RDWR,0); if (vfd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,videodev,strerror(errno)); exit(1); } } static void size_frame(void) { int lastlen; int goodcount; int n; int goodlen; framelen = 1000; lastlen = 0; framebuf = 0; while (1) { if (framelen != lastlen) { goodcount = 0; free(framebuf); framebuf = malloc(framelen+1); lastlen = framelen; } n = read(vfd,framebuf,framelen+1); if (n < 0) { fprintf(stderr,"%s: %s: read: %s\n",__progname,videodev,strerror(errno)); exit(1); } if (n == 0) { fprintf(stderr,"%s: %s: read EOF\n",__progname,videodev); exit(1); } if (n > framelen) { framelen <<= 1; } else { if ((goodcount == 0) || (goodlen != n)) { goodlen = n; goodcount = 1; } else { goodcount ++; } if (goodcount > 4) break; } } framelen = n; printf("framelen %d\n",n); } static void setup_frame(void) { void *mmrv; free(framebuf); mmrv = mmap(0,(framelen*NFRAMES)+1,PROT_READ|PROT_WRITE,MAP_ANON|MAP_SHARED,-1,0); if (mmrv == MAP_FAILED) { fprintf(stderr,"%s: buffer mmap (%u): %s\n",__progname,(unsigned int)(framelen*NFRAMES),strerror(errno)); exit(1); } framebuf = mmrv; bzero(framebuf,framelen*NFRAMES); bnv = malloc(NFRAMES); } #define wrt(fd,buf,len) wrt_(#fd "," #buf "," #len,(fd),(buf),len) static void wrt_(const char *s, int fd, const void *buf, int len) { int n; n = write(fd,buf,len); if (n < 0) { fprintf(stderr,"write(%s): %s\n",s,strerror(errno)); exit(1); } if (n != len) { fprintf(stderr,"write(%s): wrote %d not %d\n",s,n,len); exit(1); } } static void reader_main(void) { unsigned char bno; int nr; nr = -1; while (1) { if (nr < 0) nr = read(rpipe,&bno,1); if (nr < 0) { fprintf(stderr,"%s: reader pipe read: %s\n",__progname,strerror(errno)); exit(1); } if (nr == 0) exit(0); if (nr != 1) { fprintf(stderr,"%s: impossible reader pipe read (%d)\n",__progname,nr); exit(1); } nr = read(vfd,framebuf+(bno*framelen),framelen+1); if (nr == framelen) { wrt(rpipe,&bno,1); nr = -1; } else if (nr < 0) { fprintf(stderr,"%s: %s: read: %s\n",__progname,videodev,strerror(errno)); exit(1); } else if (nr == 0) { fprintf(stderr,"%s: %s: read EOF\n",__progname,videodev); exit(1); } else { // fprintf(stderr,"%s: %s: read %d, expecting %d\n",__progname,videodev,nr,framelen); nr = 1; } } } static void fork_reader(void) { int p[2]; int pid; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&p[0]) < 0) { fprintf(stderr,"%s: socketpair: %s\n",__progname,strerror(errno)); exit(1); } fflush(0); pid = fork(); if (pid == 0) { close(p[0]); rpipe = p[1]; reader_main(); _exit(0); } close(p[1]); rpipe = p[0]; // fcntl(rpipe,F_SETFL,fcntl(rpipe,F_GETFL,0)|O_NONBLOCK); } static void displayer_main(void) { unsigned char c; CVTPARAMS p; static void dpr(void *buf, int len) { int nr; nr = recv(dpipe,buf,len,MSG_WAITALL); if (nr == len) { return; } else if (nr < 0) { fprintf(stderr,"%s: displayer pipe read: %s\n",__progname,strerror(errno)); } else if (nr == 0) { fprintf(stderr,"%s: displayer pipe read EOF\n",__progname); } else if (nr < len) { fprintf(stderr,"%s: displayer pipe short read (%d, wanted %d)\n",__progname,nr,len); } else { fprintf(stderr,"%s: impossible displayer pipe read: %d\n",__progname,nr); } exit(1); } setup_X(); while (1) { dpr(&c,1); switch (c) { case 'f': dpr(&c,1); display_frame(framebuf+(c*framelen)); wrt(dpipe,&c,1); break; case 'p': dpr(&p,sizeof(p)); reset_cvt(&p); break; default: fprintf(stderr,"%s: bad displayer pipe command %02x\n",__progname,c); exit(1); break; } } } static void send_cpar(void) { wrt(dpipe,"p",1); wrt(dpipe,&cpar,sizeof(cpar)); } static void fork_displayer(void) { int p[2]; int pid; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&p[0]) < 0) { fprintf(stderr,"%s: socketpair: %s\n",__progname,strerror(errno)); exit(1); } fflush(0); pid = fork(); if (pid == 0) { close(rpipe); close(p[0]); dpipe = p[1]; displayer_main(); _exit(0); } close(p[1]); dpipe = p[0]; // fcntl(dpipe,F_SETFL,fcntl(dpipe,F_GETFL,0)|O_NONBLOCK); displaying = -1; cpar.Kr = .299; cpar.Kb = .114; cpar.minY = 0; cpar.maxY = 255; cpar.maxCC = 127; send_cpar(); } static void queue_buffers(void) { int i; for (i=NFRAMES-1;i>=0;i--) bnv[i] = i; wrt(rpipe,bnv,NFRAMES); } static IEVENT get_ievent(void) { struct pollfd pfds[3]; int n; IEVENT ev; pfds[0].fd = ifd; pfds[0].events = POLLIN | POLLRDNORM; pfds[1].fd = rpipe; pfds[1].events = POLLIN | POLLRDNORM; pfds[2].fd = dpipe; pfds[2].events = POLLIN | POLLRDNORM; n = poll(&pfds[0],3,INFTIM); if (n < 0) { if (errno != EINTR) { fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } ev.t = EVT_NONE; } else if (pfds[0].revents & (POLLIN|POLLRDNORM|POLLERR|POLLHUP|POLLNVAL)) { n = read(ifd,bnv,1); if (n < 0) { fprintf(stderr,"%s: input read: %s\n",__progname,strerror(errno)); exit(1); } if (n == 0) { fprintf(stderr,"%s: input read EOF\n",__progname); exit(1); } ev.t = EVT_KEY; ev.key = bnv[0]; } else if (pfds[1].revents & (POLLIN|POLLRDNORM|POLLERR|POLLHUP|POLLNVAL)) { n = read(rpipe,bnv,NFRAMES); if (n < 0) { fprintf(stderr,"%s: frame pipe read: %s\n",__progname,strerror(errno)); exit(1); } if (n == 0) { fprintf(stderr,"%s: frame pipe read EOF\n",__progname); exit(1); } if (n > 1) { /* int i; printf("[drop"); for (i=0;i= icmdlen) break; dsave(icmdbuf[i]); } if (j > icmdolen) { if (d1 < 0) d1 = icmdolen; d2 = j - 1; } else if (j < icmdolen) { if (d1 < 0) d1 = j; d2 = icmdolen - 1; } icmddlen = j; #if 0 printf("\n"); printf("icmd: len=%d curs=%d buf=%.*s\n",icmdlen,icmdcurs,icmdlen,&icmdbuf[0]); printf("icmdd: len=%d curs=%d buf=%.*s\n",icmddlen,icmddcurs,icmddlen,&icmddbuf[0]); printf("icmdo: len=%d curs=%d buf=%.*s\n",icmdolen,icmdocurs,icmdolen,&icmdobuf[0]); printf("d1=%d d2=%d\n",d1,d2); #endif i = icmdocurs; if (d2 >= 0) { for (;i>d1;i--) putc('\b',stdout); for (;i0;i--) putc(' ',stdout); } i = d2 + 1; } for (;i>icmddcurs;i--) putc('\b',stdout); for (;i=0;i--) s += skiphist[i]; printf("last %d, last%d %g, da %g, daf %g\n",skiphist[skiphisthand],SKIPHISTN,s/(double)SKIPHISTN,daskip,daskipf); } static void icmd_process(void) { twirler = '\0'; icmd_update(); printf("\n"); icmdolen = 0; icmdocurs = 0; if (icmdlen < 1) return; if (icmdbuf[0] == '?') { print_info(); } else if ((icmdlen > 2) && !strncasecmp(&icmdbuf[0],"kr",2)) { icmdbuf[icmdlen] = '\0'; cpar.Kr = atof(&icmdbuf[2]); send_cpar(); } else if ((icmdlen > 2) && !strncasecmp(&icmdbuf[0],"kb",2)) { icmdbuf[icmdlen] = '\0'; cpar.Kb = atof(&icmdbuf[2]); send_cpar(); } else if ((icmdlen > 3) && !strncasecmp(&icmdbuf[0],"daf",3)) { icmdbuf[icmdlen] = '\0'; daskipf = atof(&icmdbuf[3]); } else if ((icmdlen > 4) && !strncasecmp(&icmdbuf[0],"miny",4)) { icmdbuf[icmdlen] = '\0'; cpar.minY = atoi(&icmdbuf[4]); send_cpar(); } else if ((icmdlen > 4) && !strncasecmp(&icmdbuf[0],"maxy",4)) { icmdbuf[icmdlen] = '\0'; cpar.maxY = atoi(&icmdbuf[4]); send_cpar(); } else if ((icmdlen > 5) && !strncasecmp(&icmdbuf[0],"maxcc",5)) { icmdbuf[icmdlen] = '\0'; cpar.maxCC = atoi(&icmdbuf[5]); send_cpar(); } else { printf("huh?\n"); } icmdlen = 0; icmdcurs = 0; } static void icmd_del(int at, int n) { if ((at < 0) || (n < 0) || (at+n > icmdlen)) abort(); if (n < 1) return; if (at+n < icmdlen) bcopy(&icmdbuf[at+n],&icmdbuf[at],icmdlen-(at+n)); if (icmdcurs > at+n) { icmdcurs -= n; } else if (icmdcurs > at) { icmdcurs = at; } icmdlen -= n; } static int wordchar(unsigned char ch) { static const unsigned char test[32] = { 0x00, 0x00, 0x00, 0x00, /* C0 controls */ 0x00, 0x00, 0xff, 0x03, /* space - ? */ 0xfe, 0xff, 0xff, 0x07, /* @ - _ */ 0xfe, 0xff, 0xff, 0x07, /* ` - DEL */ 0x00, 0x00, 0x00, 0x00, /* C1 controls */ 0x00, 0x00, 0x00, 0x00, /* NBSP - ¿ */ 0xff, 0xff, 0x7f, 0xff, /* À - ß */ 0xff, 0xff, 0x7f, 0xff, /* à - ÿ */ }; return((test[ch>>3]>>(ch&7))&1); } static void icmd_back_word(void) { while ( (icmdcurs > 0) && !( !wordchar(icmdbuf[icmdcurs-1]) && wordchar(icmdbuf[icmdcurs]) ) ) icmdcurs --; } static void icmd_forw_word(void) { while ( (icmdcurs < icmdlen) && !( !wordchar(icmdbuf[icmdcurs+1]) || wordchar(icmdbuf[icmdcurs]) ) ) icmdcurs ++; } static void icmd_key(unsigned char k) { int i; switch (icmdpref) { default: abort(); break; case 0: switch (k) { case 0x01: /* ^A */ icmdcurs = 0; break; case 0x02: /* ^B */ if (icmdcurs > 0) icmdcurs --; break; case 0x04: /* ^D */ if (icmdcurs < icmdlen) icmd_del(icmdcurs,1); break; case 0x05: /* ^E */ icmdcurs = icmdlen; break; case 0x06: /* ^F */ if (icmdcurs < icmdlen) icmdcurs ++; break; case 0x08: /* ^H */ case 0x7f: /* DEL */ if (icmdcurs > 0) icmd_del(icmdcurs-1,1); break; case 0x0a: /* ^J */ case 0x0d: /* ^M */ icmd_process(); break; case 0x0b: /* ^K */ icmdlen = icmdcurs; break; case 0x14: /* ^T */ if (icmdcurs >= 2) { unsigned char t; t = icmdbuf[icmdcurs-2]; icmdbuf[icmdcurs-2] = icmdbuf[icmdcurs-1]; icmdbuf[icmdcurs-1] = t; } break; case ' ' ... '~': case (unsigned char)' ' ... (unsigned char)'ÿ': if (icmdlen < MAXCMDLINE) { if (icmdcurs <= icmdlen) { bcopy(&icmdbuf[icmdcurs],&icmdbuf[icmdcurs+1],icmdlen-icmdcurs); icmdbuf[icmdcurs] = k; icmdcurs ++; icmdlen ++; } } break; case 0x1b: /* ESC */ icmdpref = 0x1b; break; } break; case 0x1b: icmdpref = 0; switch (k) { case 'b': icmd_back_word(); break; case 'd': i = icmdcurs; icmd_forw_word(); icmd_del(i,icmdcurs-i); break; case 'f': icmd_forw_word(); break; case 'h': i = icmdcurs; icmd_back_word(); icmd_del(icmdcurs,i-icmdcurs); break; } break; } } static void init_stats(void) { int i; skipped = 0; for (i=SKIPHISTN-1;i>=0;i--) skiphist[i] = 0; skiphisthand = 0; daskip = 0; daskipf = .99; } static void update_skip_stats(void) { if (skiphisthand) skiphisthand --; else skiphisthand = SKIPHISTN - 1; skiphist[skiphisthand] = skipped; daskip = (daskip * daskipf) + (skipped * (1 - daskipf)); skipped = 0; } int main(void); int main(void) { open_video(); size_frame(); setup_frame(); fork_reader(); fork_displayer(); queue_buffers(); init_cmdline(); init_stats(); while (1) { IEVENT ev; ev = get_ievent(); switch (ev.t) { case EVT_NONE: break; case EVT_PIC: if (displaying < 0) { /*printf("[show %d]\n",ev.pic);fflush(0);*/ wrt(dpipe,"f",1); wrt(dpipe,&ev.pic,1); displaying = ev.pic; } else { /*printf("[skip %d]\n",ev.pic);fflush(0);*/ return_frame(ev.pic); skipped ++; } twirlx = (twirlx + 1) & 3; twirler = twirl[twirlx]; icmd_update(); break; case EVT_KEY: icmd_key(ev.key); icmd_update(); break; case EVT_DSP: displaying = -1; /*printf("[ret %d]\n",ev.pic);fflush(0);*/ return_frame(ev.dsp); update_skip_stats(); break; default: abort(); break; } } return(0); }