/* * Shows a PPM file. * * By default, looks for a TrueColor visual; this can be explicitly * specified with -true. With -q, uses a PseudoColor visual and does * successive color approximations a la ppmq. With -cube, allocates a * color cube of the specified size and uses that instead of a * TrueColor visual. -dither causes it to do a Floyd-Steinberg-style * dither; with -q, this happens only when it finally runs out of * colors. -map causes it to assume the input contains few enough * colors they'll all fit in a PseudoColor map, and to construct the * map and use it. * * -pad causes it to pad the input file with all-black pixels, if the * data area is too short. * * -gamma can be given to specify a gamma correction factor. * * -accept makes it listen for connections on the specified port and * accept a pnm file, holding a new picture, on each such connection. * The port number can also be prefixed with addr/ to specify an * address to listen on. -accept can be given multiple times, and the * listening sockets accumulate. * * -ownmap makes each window created have its own colormap, even if the * default visual is in use. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UNUSED_ARG(x) x __attribute__((__unused__)) extern const char *__progname; static XrmDatabase db; static const char *defaults = "\ *Foreground: white\n\ *Background: black\n\ *BorderColor: white\n\ *BorderWidth: 1\n\ *Name: xshowppm\n\ *IconName: xshowppm\n\ "; #define SBWIDTH 10 static char *displayname; static char *visualstr; static int synch = 0; #define STYLE_TRUECOLOR 1 #define STYLE_PPMQ 2 #define STYLE_MAPPED 3 #define STYLE_COLORCUBE 4 #define STYLE_SMALLCUBE 5 #define STYLE_MINCUBE 6 static int style = STYLE_TRUECOLOR; static int dodither = 0; static int quiet = 0; static char *cubearg; static int debug = 0; static int nodisplay = 0; static int wantfork = 0; static const char *resname = "xshowppm"; static const char *resclass = "XShowPPM"; static char *xrmstr = 0; static int argc; static char **argv; static Display *disp; static Screen *scr; static int width; static int height; static Window rootwin; static Colormap defcmap; static XVisualInfo visinfo; static int bits_per_pixel; static int scanline_pad; static int (*preverr)(Display *, XErrorEvent *); static int (*prevIOerr)(Display *); static struct { const char *name; Atom a; } atoms[] = { { "WM_PROTOCOLS" }, #define A_WM_PROTOCOLS 0 { "WM_DELETE_WINDOW" }, #define A_WM_DELETE_WINDOW 1 { 0 } }; static int rshift; static int gshift; static int bshift; static int rmul; static int gmul; static int bmul; static int bpp; static int xfd; static int xpx; static int xselcnt = 0; typedef enum { SBD_X = 1, SBD_Y, } SBDIM; typedef enum { RS_NOTREADING = 1, RS_COMMMAGIC, RS_HDRCOMM, RS_COMMAND, RS_IFILTER, RS_ERRDRAIN, RS_FILTERED, RS_MAG_D, RS_MAG_P, RS_MAG_GIF, RS_MAG_JPEG, RS_MAG_TIFF_LE, RS_MAG_TIFF_BE, RS_MAG_PNG, RS_GET_X, RS_GET_Y, RS_GET_MAXVAL, RS_GET_BPL, RS_PIX_INIT, RS_PIX_R, RS_PIX_G, RS_PIX_B, RS_PIX_D, } READSTATE; typedef enum { NS_PRE = 1, NS_COMM, NS_DIGS, } NUMSTATE; typedef struct cell CELL; typedef struct pic PIC; typedef struct group GROUP; typedef struct watch WATCH; typedef struct acc ACC; typedef struct crunchops CRUNCHOPS; typedef struct cp_dither CP_DITHER; typedef struct cp_loadimg CP_LOADIMG; typedef struct scrollbars SCROLLBARS; typedef struct scrollbar SCROLLBAR; typedef struct strlist STRLIST; struct strlist { STRLIST *link; char *ptr; int len; } ; struct acc { ACC *link; int fd; const char *txt; int px; } ; struct watch { WATCH *flink; WATCH *blink; PIC *p; int ng; GROUP **gv; } ; struct group { GROUP *link; const char *name; int nmembers; PIC **members; unsigned int nodelete : 1; } ; struct cell { int index; unsigned char min[3]; unsigned char max[3]; unsigned char range[3][2]; int hist[3][256]; double mean[3]; double var[3]; int npix; } ; struct crunchops { void (*start)(PIC *, va_list); void (*step)(PIC *); void (*raised)(PIC *); void (*abort)(PIC *); } ; struct scrollbar { unsigned int flags; #define SBF_HAVE 0x00000001 #define SBF_WINS 0x00000002 #define SBF_WANT 0x00000004 int base; /* loc of bar end */ int width; /* size of bar and of visible image */ int full; /* size of whole image */ int othersize; /* other-dimension width as if no bar */ int thumbsize; /* size of thumb */ int thumbloc; /* location of thumb */ int voff; /* scrolled-to offset */ Window win[2]; Window thumb[2]; } ; struct scrollbars { SCROLLBAR x; SCROLLBAR y; } ; struct pic { PIC *flink; PIC *blink; PIC *crunchflink; PIC *crunchblink; char *optline; int optlen; char *abovestr; char *belowstr; char *geometryspec; char *replace; char *foreground; char *background; char *bordercstr; char *borderwstr; char *xname; char *cname; char *iconname; char *vmdir; unsigned int maxcolors; char *gammastr; double gammafactor; unsigned long int *cubepix; unsigned long int *cubepsetv; unsigned int ncubepixset; #define CUBEPIXISSET(p,n) \ ((p->cubepsetv[((unsigned int)(n))/32]>>(((unsigned int)(n))%32))&1) #define CUBEPIXSET(p,n) \ (p->cubepsetv[((unsigned int)(n))/32]|=1<<(((unsigned int)(n))%32)) #define CUBEPIXCLR(p,n) \ (p->cubepsetv[((unsigned int)(n))/32]&=~(1<<(((unsigned int)(n))%32))) int *cuberv; int *cubegv; int *cubebv; int cubenr; int cubeng; int cubenb; int cuberm; int cubegm; int cubebm; unsigned int padflag : 1; unsigned int pleaseclose : 1; unsigned int pushed : 1; unsigned int ateof : 1; unsigned int ngriped : 1; unsigned int preexpose : 1; unsigned int vm_rpic : 1; unsigned int vm_mpic : 1; unsigned int vm_dpic : 1; unsigned int didcmd : 1; unsigned int defxname : 1; unsigned int ifilter : 1; unsigned int ifilt_eof_ci : 1; unsigned int ifilt_eof_e : 1; unsigned int ownmap : 1; int filter_ci; int fpxci; unsigned char *fcbuf; int fcfill; int fcbp; int filter_co; int fpxco; int filter_e; int fpxe; int filter_ec; int nmagics; const char **magics; int *maglens; char *magbuf; int magfill; int push; READSTATE readstate; unsigned int imgtype; #define T_TYPE 0x00000008 #define T_TS 0x00000007 #define T_T_PNM 0x00000000 #define T_PBM 0x00000001 #define T_PGM 0x00000002 #define T_PPM 0x00000003 #define T_PNM 0x00000003 #define T_RAW 0x00000004 #define T_T_DD 0x00000008 #define T_TRUE 0x00000001 int maxval; int pix_x; int pixleft; unsigned char *pp; int pix_r; int pix_g; NUMSTATE nstate; int nv; char *hcbuf; int hclen; int hchave; unsigned char *ibuf; #define IBUFSIZE 10240 int imax; int ifill; int ibp; int infd; int px; CRUNCHOPS *crunchops; void *crunchpriv; FILE *outf; Window topwin; Window clipwin; Window win; SCROLLBARS sb; int topw; int toph; GC wingc; Colormap wincmap; XColor fgcolor; XColor bgcolor; XColor bdcolor; int borderwidth; int availy; CELL *cells; int ncells; XColor *xcols; int xsize; int ysize; unsigned char *rpic; /* raw */ unsigned char *mpic; /* mapped */ unsigned char *dpic; /* display */ int bpl; int inputlimit[3][3]; XImage *ximg; STRLIST *ifilters; } ; struct cp_dither { PIC *pic; CP_DITHER *flink; CP_DITHER *blink; unsigned int mask; double (*ditherr)[3]; double (*lerr)[3]; double (*cerr)[3]; unsigned long int (*pixel)(PIC *, double *, double *); void (*done)(PIC *); int y; } ; struct cp_loadimg { unsigned char *iptr; unsigned char *obase; int y; unsigned long int (*pixelfn)(PIC *, unsigned char *); void (*done)(PIC *); } ; static ACC *accs = 0; static char **ifiles = 0; static int nifiles = 0; static PIC initpic; static PIC *pics_h; static PIC *pics_t; static PIC *crunchpics_h; static PIC *crunchpics_t; static CP_DITHER *dither_h; static CP_DITHER *dither_t; static unsigned int crunchcycle; static GROUP *groups; static GROUP *group_all; static GROUP *group_dithering; static WATCH *watch_h; static WATCH *watch_t; static int anykids; static int sb_min_w; static int sb_min_h; #define round_up(x,y) ((((x)+(y)-1)/(y))*(y)) static char *deconst_(int dummy, ...) { va_list ap; char *rv; va_start(ap,dummy); rv = va_arg(ap,char *); va_end(ap); return(rv); } static char *deconst(const char *s) { return(deconst_(0,s)); } static void saveargv(int ac, char **av) { int i; int nc; char *abuf; argc = ac; argv = malloc((ac+1)*sizeof(char *)); nc = 1; for (i=0;i 0) { buf = mmap(0,nb,PROT_READ,MAP_SHARED,fd,0); if (buf == (void *)-1) { fprintf(stderr,"%s: mmap: %s\n",__progname,strerror(errno)); return(0); } return(buf); } return(0); } static void unmap_file(void *data, unsigned int nb) { munmap(data,nb); } static void vm_free(PIC *p, int vmflag, void *mem) { if (! p->vmdir) { free(mem); return; } if (! mem) return; mem = ((char *)mem) - HDRBYTES; if (! vmflag) { free(mem); return; } unmap_file(mem,*(unsigned int *)mem); } static void *vm_malloc(PIC *p, unsigned int nb) { void *mem; if (! p->vmdir) return(malloc(nb)); nb += HDRBYTES; mem = malloc(nb); *(unsigned int *)mem = nb; return(((char *)mem)+HDRBYTES); } static int vm_finalize(PIC *p, unsigned char **dpp) { char *fntmp; int fd; struct timeval tv; static int serial = 1; char *mem; unsigned int nb; if (! p->vmdir) return(0); if (! *dpp) return(0); gettimeofday(&tv,0); fntmp = malloc(strlen(p->vmdir)+1+8+1+5+1+8+1+8+1+9+1); sprintf(fntmp,"%s/xshowppm.%05d.%08x.%08x.%09d",p->vmdir,getpid(),(unsigned int)tv.tv_sec,(unsigned int)tv.tv_usec,serial++); unlink(fntmp); fd = open(fntmp,O_RDWR|O_CREAT,0600); if (fd < 0) { fprintf(stderr,"%s: can't create %s: %s\n",__progname,fntmp,strerror(errno)); return(0); } unlink(fntmp); free(fntmp); nb = *(unsigned int *)((*dpp)-HDRBYTES); write(fd,(*dpp)-HDRBYTES,nb); mem = map_file(fd,nb); close(fd); if (mem == 0) return(0); free((*dpp)-HDRBYTES); *dpp = mem + HDRBYTES; return(1); } static void init_sb(SCROLLBAR *sb) { sb->flags = 0; sb->base = -1; sb->width = -1; sb->full = -1; sb->othersize = -1; sb->thumbsize = -1; sb->thumbloc = -1; sb->voff = -1; } static void initinitpic(void) { initpic.optline = 0; initpic.optlen = 0; initpic.abovestr = 0; initpic.belowstr = 0; initpic.geometryspec = 0; initpic.replace = 0; initpic.foreground = 0; initpic.background = 0; initpic.bordercstr = 0; initpic.borderwstr = 0; initpic.xname = 0; initpic.cname = 0; initpic.iconname = 0; initpic.vmdir = 0; initpic.maxcolors = ~0U; initpic.gammastr = 0; initpic.gammafactor = 1; initpic.cubepix = 0; initpic.cubepsetv = 0; /* ncubepixset needs no init here */ initpic.cuberv = 0; initpic.cubegv = 0; initpic.cubebv = 0; /* cubenr needs no init here */ /* cubeng needs no init here */ /* cubenb needs no init here */ /* cuberm needs no init here */ /* cubegm needs no init here */ /* cubebm needs no init here */ initpic.padflag = 0; initpic.pleaseclose = 0; initpic.pushed = 0; /* ateof needs no init here */ initpic.ngriped = 0; initpic.preexpose = 1; initpic.vm_rpic = 0; initpic.vm_mpic = 0; initpic.vm_dpic = 0; initpic.didcmd = 0; /* defxname needs no init here */ initpic.ifilter = 0; /* ifilt_eof_ci needs no init here */ /* ifilt_eof_e needs no init here */ initpic.ownmap = 0; /* filter_ci needs no init here */ /* fpxci needs no init here */ /* fcbuf needs no init here */ /* fcfill needs no init here */ /* fcbp needs no init here */ /* filter_co needs no init here */ /* fpxco needs no init here */ initpic.filter_e = -1; /* fpxe needs no init here */ initpic.filter_ec = -1; initpic.nmagics = -1; initpic.magics = 0; initpic.maglens = 0; initpic.magbuf = 0; /* magfill needs no init here */ /* push needs no init here */ initpic.readstate = RS_NOTREADING; /* imgtype needs no init here */ /* maxval needs no init here */ /* pix_x needs no init here */ /* pixleft needs no init here */ /* pp needs no init here */ /* pix_r needs no init here */ /* pix_g needs no init here */ initpic.nstate = NS_PRE; /* nv needs no init here */ initpic.hcbuf = 0; initpic.hclen = 0; initpic.hchave = 0; initpic.ibuf = 0; /* imax needs no init here */ /* ifill needs no init here */ /* ibp needs no init here */ initpic.infd = -1; initpic.px = -1; initpic.crunchops = 0; /* crunchpriv needs no init here */ initpic.outf = 0; initpic.topwin = None; /* clipwin needs no init here */ initpic.win = None; init_sb(&initpic.sb.x); init_sb(&initpic.sb.y); initpic.topw = 0; initpic.toph = 0; /* wingc needs no init here */ /* wincmap needs no init here */ /* fgcolor needs no init here */ /* bgcolor needs no init here */ /* bdcolor needs no init here */ /* borderwidth needs no init here */ initpic.availy = -1; initpic.cells = 0; /* ncells needs no init here */ initpic.xcols = 0; /* xsize needs no init here */ /* ysize needs no init here */ initpic.rpic = 0; initpic.mpic = 0; initpic.dpic = 0; /* bpl needs no init here */ /* inputlimit needs no init here */ initpic.ximg = 0; } static void pic_link_h(PIC *p) { p->flink = pics_h; p->blink = 0; if (pics_h) pics_h->blink = p; else pics_t = p; pics_h = p; } static void pic_unlink(PIC *p) { if (p->blink) p->blink->flink = p->flink; else pics_h = p->flink; if (p->flink) p->flink->blink = p->blink; else pics_t = p->blink; memset(&p->flink,0xd1,sizeof(p->flink)); memset(&p->blink,0xd1,sizeof(p->blink)); } static char *strdup0(const char *s) { return(s?strdup(s):0); } static PIC *newpic(int ifd, int ofd) { PIC *p; p = malloc(sizeof(PIC)); *p = initpic; p->geometryspec = strdup0(initpic.geometryspec); p->foreground = strdup0(initpic.foreground); p->background = strdup0(initpic.background); p->bordercstr = strdup0(initpic.bordercstr); p->borderwstr = strdup0(initpic.borderwstr); p->vmdir = strdup0(initpic.vmdir); p->xname = strdup0(initpic.xname); p->iconname = strdup0(initpic.iconname); p->gammastr = strdup0(initpic.gammastr); p->defxname = 1; p->infd = ifd; p->ateof = 0; p->ibuf = malloc(IBUFSIZE); p->imax = 1; p->ifill = 0; p->ibp = 0; p->outf = (ofd < 0) ? 0 : fdopen(dup(ofd),"w"); p->readstate = RS_COMMMAGIC; pic_link_h(p); return(p); } static void pprintf(PIC *p, const char *fmt, ...) __attribute__((format(printf,2,3))); static void pprintf(PIC *p, const char *fmt, ...) { va_list ap; va_start(ap,fmt); if (p && p->outf) { vfprintf(p->outf,fmt,ap); fflush(p->outf); if (ferror(p->outf)) p->pleaseclose = 1; } else { vfprintf(stderr,fmt,ap); } va_end(ap); } static void notify_group_chg(GROUP *g, int how, PIC *p) #define NGC_ADD 1 #define NGC_DEL 2 { WATCH *w; int i; int j; for (w=watch_h;w;w=w->flink) { for (i=w->ng-1;i>=0;i--) { if (w->gv[i] == g) { pprintf(w->p,"%s: ",g->name); switch (how) { case NGC_ADD: pprintf(w->p,"+"); break; case NGC_DEL: pprintf(w->p,"-"); break; default: abort(); break; } pprintf(w->p,"%s: %d:",p->cname,g->nmembers); for (j=0;jnmembers;j++) pprintf(w->p," %s",g->members[j]->cname); pprintf(w->p,"\n"); } } } } static int del_from_group(GROUP *g, PIC *p) { int i; int n; for (i=g->nmembers-1;i>=0;i--) { if (g->members[i] == p) { n ++; if (i < g->nmembers-1) bcopy(&g->members[i+1],&g->members[i],(g->nmembers-1-i)*sizeof(PIC *)); g->nmembers --; notify_group_chg(g,NGC_DEL,p); } } return(n); } static void del_from_all_groups(PIC *p) { GROUP *g; for (g=groups;g;g=g->link) del_from_group(g,p); } static void freewatch(WATCH *w) { free(w->gv); free(w); } static void watch_link_h(WATCH *w) { w->flink = watch_h; w->blink = 0; if (watch_h) watch_h->blink = w; else watch_t = w; watch_h = w; } static void watch_unlink(WATCH *w) { if (w->blink) w->blink->flink = w->flink; else watch_h = w->flink; if (w->flink) w->flink->blink = w->blink; else watch_t = w->blink; memset(&w->flink,0xd1,sizeof(w->flink)); memset(&w->blink,0xd1,sizeof(w->blink)); } static void freepic(PIC *p) { WATCH *w; WATCH *w2; for (w=watch_h;w;w=w2) { w2 = w->flink; if (w->p == p) { watch_unlink(w); freewatch(w); } } del_from_all_groups(p); pic_unlink(p); if (p->cname != p->xname) free(p->cname); if (p->ifilter) { if (p->filter_ci >= 0) close(p->filter_ci); free(p->fcbuf); if (p->filter_co >= 0) close(p->filter_co); } if (p->filter_e >= 0) close(p->filter_e); if (p->filter_ec >= 0) close(p->filter_ec); free(p->optline); free(p->abovestr); free(p->belowstr); free(p->geometryspec); free(p->replace); free(p->foreground); free(p->background); free(p->bordercstr); free(p->borderwstr); free(p->xname); free(p->iconname); free(p->vmdir); free(p->gammastr); free(p->cubepix); free(p->cubepsetv); free(p->cuberv); free(p->cubegv); free(p->cubebv); free(p->magics); free(p->maglens); free(p->magbuf); free(p->hcbuf); free(p->ibuf); if (p->infd >= 0) close(p->infd); if (p->outf) fclose(p->outf); free(p->cells); free(p->xcols); vm_free(p,p->vm_rpic,p->rpic); vm_free(p,p->vm_mpic,p->mpic); vm_free(p,p->vm_dpic,p->dpic); if (p->ximg) { p->ximg->data = malloc(1); XDestroyImage(p->ximg); } while (p->ifilters) { STRLIST *s; s = p->ifilters; p->ifilters = s->link; free(s->ptr); free(s); } free(p); } static int addaccport(const char *arg) { struct addrinfo hints; struct addrinfo *ai0; struct addrinfo *ai; ACC *a; int s; int se; char *txt; char hn[NI_MAXHOST]; char pn[NI_MAXSERV]; char *slash; hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; slash = index(arg,'/'); if (slash) { char *t; t = malloc((slash-arg)+1); bcopy(arg,t,slash-arg); t[slash-arg] = '\0'; if (getaddrinfo(t,slash+1,&hints,&ai0)) { fprintf(stderr,"%s: %s: %s\n",__progname,arg,gai_strerror(errno)); return(1); } free(t); } else { if (getaddrinfo(0,arg,&hints,&ai0)) { fprintf(stderr,"%s: %s: %s\n",__progname,arg,gai_strerror(errno)); return(1); } } se = 0; for (ai=ai0;ai;ai=ai->ai_next) { s = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (s < 0) { if (se == 0) se = errno; continue; } se = -1; if (getnameinfo(ai->ai_addr,ai->ai_addrlen,&hn[0],NI_MAXHOST,&pn[0],NI_MAXSERV,NI_NUMERICHOST|NI_NUMERICSERV)) { fprintf(stderr,"%s: can't get printable form: %s\n",__progname,strerror(errno)); return(1); } if (bind(s,ai->ai_addr,ai->ai_addrlen) < 0) { fprintf(stderr,"%s: bind to %s/%s: %s\n",__progname,&hn[0],&pn[0],strerror(errno)); return(1); } listen(s,10); asprintf(&txt,"%s/%s",&hn[0],&pn[0]); a = malloc(sizeof(ACC)); a->fd = s; a->txt = txt; a->link = accs; accs = a; } if (se > 0) { fprintf(stderr,"%s: socket: %s\n",__progname,strerror(se)); return(1); } freeaddrinfo(ai0); return(0); } static void setstr(char **vp, const char *s) { free(*vp); *vp = strdup0(s); } static int handleargs(int ac, char **av, PIC *ap) { int skip; int errs; PIC *p; p = ap ? ap : &initpic; skip = 0; errs = 0; for (;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { if (ap) { pprintf(ap,"%s: can't specify filenames in an input picture\n",__progname); errs ++; } else { ifiles = realloc(ifiles,(nifiles+1)*sizeof(*ifiles)); ifiles[nifiles] = *av; nifiles ++; } continue; } if (0) { needarg:; pprintf(ap,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } if (0) { nopic:; pprintf(ap,"%s: can't specify %s in an input picture\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) #define NOPIC() do { if (ap) goto nopic; } while (0) if (!strcmp(*av,"-display")) { WANTARG(); NOPIC(); displayname = av[skip]; continue; } if (!strcmp(*av,"-fork")) { NOPIC(); wantfork = 1; continue; } if (!strcmp(*av,"-geometry")) { WANTARG(); setstr(&p->geometryspec,av[skip]); continue; } if (!strcmp(*av,"-foreground") || !strcmp(*av,"-fg")) { WANTARG(); setstr(&p->foreground,av[skip]); continue; } if (!strcmp(*av,"-background") || !strcmp(*av,"-bg")) { WANTARG(); setstr(&p->background,av[skip]); continue; } if (!strcmp(*av,"-bordercolor") || !strcmp(*av,"-bd")) { WANTARG(); setstr(&p->bordercstr,av[skip]); continue; } if (!strcmp(*av,"-borderwidth") || !strcmp(*av,"-bw")) { WANTARG(); setstr(&p->borderwstr,av[skip]); continue; } if (!strcmp(*av,"-vmdir")) { WANTARG(); setstr(&p->vmdir,av[skip]); continue; } if (!strcmp(*av,"-visual")) { WANTARG(); NOPIC(); visualstr = av[skip]; continue; } if (!strcmp(*av,"-name")) { WANTARG(); setstr(&p->xname,av[skip]); p->defxname = 0; continue; } if (!strcmp(*av,"-iconname")) { WANTARG(); setstr(&p->iconname,av[skip]); continue; } if (!strcmp(*av,"-resname")) { WANTARG(); NOPIC(); resname = av[skip]; continue; } if (!strcmp(*av,"-resclass")) { WANTARG(); NOPIC(); resclass = av[skip]; continue; } if (!strcmp(*av,"-xrm")) { WANTARG(); NOPIC(); strappend(&xrmstr,"\n"); strappend(&xrmstr,av[skip]); continue; } if (!strcmp(*av,"-sync")) { NOPIC(); synch = 1; continue; } if (!strcmp(*av,"-accept")) { WANTARG(); NOPIC(); errs += addaccport(av[skip]); continue; } if (!strcmp(*av,"-nodisplay")) { NOPIC(); nodisplay = 1; continue; } if (!strcmp(*av,"-quiet")) { NOPIC(); quiet = 1; continue; } if (!strcmp(*av,"-true")) { NOPIC(); style = STYLE_TRUECOLOR; continue; } if (!strcmp(*av,"-q")) { NOPIC(); style = STYLE_PPMQ; continue; } if (!strcmp(*av,"-map")) { NOPIC(); style = STYLE_MAPPED; continue; } if (!strcmp(*av,"-cube")) { WANTARG(); NOPIC(); cubearg = av[skip]; style = STYLE_COLORCUBE; continue; } if (!strcmp(*av,"-smallcube")) { WANTARG(); NOPIC(); cubearg = av[skip]; style = STYLE_SMALLCUBE; continue; } if (!strcmp(*av,"-mincube")) { WANTARG(); NOPIC(); cubearg = av[skip]; style = STYLE_MINCUBE; continue; } if (!strcmp(*av,"-colors") || !strcmp(*av,"-colours")) { WANTARG(); p->maxcolors = atoi(av[skip]); continue; } if (!strcmp(*av,"-gamma")) { WANTARG(); setstr(&p->gammastr,av[skip]); continue; } if (!strcmp(*av,"-dither")) { NOPIC(); dodither = 1; continue; } if (!strcmp(*av,"-debug")) { NOPIC(); debug = 1; continue; } if (!strcmp(*av,"-pad")) { p->padflag = 1; continue; } if (!strcmp(*av,"-ownmap")) { p->ownmap = 1; continue; } if (!strcmp(*av,"-above")) { WANTARG(); setstr(&p->abovestr,av[skip]); setstr(&p->belowstr,0); continue; } if (!strcmp(*av,"-below")) { WANTARG(); setstr(&p->abovestr,0); setstr(&p->belowstr,av[skip]); continue; } if (!strcmp(*av,"-replace")) { WANTARG(); setstr(&p->replace,av[skip]); continue; } #undef WANTARG #undef NOPIC pprintf(ap,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } return(errs); } static void maybeset(char **strp, char *str) { if (str && !*strp) *strp = strdup(str); } static void setup_db(void) { char *str; XrmDatabase db2; db = XrmGetStringDatabase(defaults); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } #if defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 5) str = XScreenResourceString(scr); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } #endif if (xrmstr) { db2 = XrmGetStringDatabase(xrmstr); XrmMergeDatabases(db2,&db); } } static char *get_default_value(const char *name, const char *class) { char *type; XrmValue value; int nl; int cl; static int buflen = -1; static int rnlen = 0; static int rclen = 0; static char *nbuf; static char *cbuf; nl = strlen(name); cl = strlen(class); if ((nl+1+rnlen >= buflen) || (cl+1+rclen >= buflen)) { if (buflen < 0) { rnlen = strlen(resname); rclen = strlen(resclass); buflen = 10; nbuf = malloc(buflen); cbuf = malloc(buflen); } if (buflen <= nl+1+rnlen) buflen = nl+1+rnlen + 1; if (buflen <= cl+1+rclen) buflen = cl+1+rclen + 1; nbuf = realloc(nbuf,buflen); cbuf = realloc(cbuf,buflen); } sprintf(nbuf,"%s.%s",resname,name); sprintf(cbuf,"%s.%s",resclass,class); if (XrmGetResource(db,nbuf,cbuf,&type,&value) == False) return(0); return(value.addr); } static void setup_atoms(void) { int i; for (i=0;atoms[i].name;i++) atoms[i].a = XInternAtom(disp,atoms[i].name,False); } static void setup_visual(void) { XVisualInfo *xvi; int nvi; XVisualInfo template; int class; const char *classname; switch (style) { case STYLE_TRUECOLOR: class = TrueColor; classname = "TrueColor"; break; case STYLE_COLORCUBE: case STYLE_SMALLCUBE: case STYLE_MINCUBE: case STYLE_PPMQ: case STYLE_MAPPED: class = PseudoColor; classname = "PseudoColor"; break; default: abort(); } if (visualstr) { unsigned long int id; char *cp; id = strtol(visualstr,&cp,0); if (*cp) { fprintf(stderr,"%s: %s: invalid visual option\n",__progname,visualstr); exit(1); } template.visualid = (VisualID) id; xvi = XGetVisualInfo(disp,VisualIDMask,&template,&nvi); if (xvi == 0) { fprintf(stderr,"%s: no such visual found\n",__progname); exit(1); } if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo returned non-nil but zero count?\n",__progname); exit(1); } visinfo = xvi[0]; if (visinfo.class != class) { fprintf(stderr,"%s: visual %s isn't %s\n",__progname,visualstr,classname); exit(1); } if (visinfo.screen != XDefaultScreen(disp)) { fprintf(stderr,"%s: warning: visual %s is on screen %d\n",__progname,visualstr,(int)visinfo.screen); } } else { int best_onscr; int best; int i; template.class = class; xvi = XGetVisualInfo(disp,VisualClassMask,&template,&nvi); best = -1; best_onscr = -1; for (i=0;i xvi[best_onscr].depth) || ( (xvi[i].depth == xvi[best_onscr].depth) && (xvi[i].visual == XDefaultVisual(disp,xvi[i].screen)) ) ) { best_onscr = i; } } if ( (best < 0) || (xvi[i].depth > xvi[best].depth) || ( (xvi[i].depth == xvi[best].depth) && (xvi[i].visual == XDefaultVisual(disp,xvi[i].screen)) ) ) { best = i; } } if (best < 0) { fprintf(stderr,"%s: no %s visual available\n",__progname,classname); exit(1); } if (best_onscr < 0) { fprintf(stderr,"%s: no %s visual on screen %d, using screen %d\n",__progname,classname,XDefaultScreen(disp),xvi[best].screen); } else { best = best_onscr; } visinfo = xvi[best]; } } static void setup_cmap(PIC *p) { if ((visinfo.visual == XDefaultVisual(disp,visinfo.screen)) && !p->ownmap) { p->wincmap = defcmap; } else { p->wincmap = XCreateColormap(disp,rootwin,visinfo.visual,AllocNone); } } static void setup_color(PIC *p, char *str, XColor *col) { if (XParseColor(disp,p->wincmap,str,col) == 0) { fprintf(stderr,"%s: bad color `%s'\n",__progname,str); exit(1); } if (XAllocColor(disp,p->wincmap,col) == 0) { fprintf(stderr,"%s: can't allocate cell for color `%s'\n",__progname,str); exit(1); } } static void setup_colors(PIC *p) { setup_color(p,p->foreground,&p->fgcolor); setup_color(p,p->background,&p->bgcolor); setup_color(p,p->bordercstr,&p->bdcolor); } static void setup_cube(PIC *p, int style) { int nr; int ng; int nb; int ncc; char *sp; char *sp2; int ir; int ig; int ib; int i; if (0) { badarg:; fprintf(stderr,"%s: invalid -cube argument `%s'\n",__progname,cubearg); fprintf(stderr,"%s: syntax is: -cube \n",__progname); fprintf(stderr,"%s: or: -cube //\n",__progname); exit(1); } nr = strtol(cubearg,&sp,0); if (sp == cubearg) goto badarg; if ((*sp == ',') || (*sp == '/')) { ng = strtol(sp+1,&sp2,0); if (sp2 == sp+1) goto badarg; if ((*sp == ',') || (*sp == '/')) { nb = strtol(sp2+1,&sp,0); if (sp == sp2+1) goto badarg; } else { goto badarg; } } else { ng = nr; nb = nr; } ncc = nr * ng * nb; p->cubepix = malloc(ncc*sizeof(*p->cubepix)); if (p->cubepix == 0) { fprintf(stderr,"%s: can't malloc space for color cube\n",__progname); exit(1); } p->cubenr = nr; p->cubeng = ng; p->cubenb = nb; p->cuberm = ng * nb; p->cubegm = nb; p->cubebm = 1; if (debug) printf("cuben=(%d,%d,%d) cubem=(%d,%d,%d)\n",p->cubenr,p->cubeng,p->cubenb,p->cuberm,p->cubegm,p->cubebm); if (style != STYLE_SMALLCUBE) { for (i=0;i<3;i++) { p->inputlimit[i][0] = 0; p->inputlimit[i][1] = 255; } } for (i=0;i<3;i++) p->inputlimit[i][2] = p->inputlimit[i][1] - p->inputlimit[i][0]; #define il p->inputlimit #define FOO(inx,x) \ p->cube##x##v = malloc(n##x*sizeof(*p->cube##x##v)); \ for (i=0;icube##x##v[i] = rint(65535*pow( \ (il[inx][0]+(il[inx][2]*i/(double)(n##x-1))) / 255.0, \ p->gammafactor)); \ } FOO(0,r) FOO(1,g) FOO(2,b) #undef FOO #undef il if (style == STYLE_MINCUBE) { p->cubepsetv = malloc(((ncc+31)>>5)*sizeof(unsigned long int)); bzero(p->cubepsetv,((ncc+31)>>5)*sizeof(unsigned long int)); p->ncubepixset = 0; } else { i = 0; /* wish there were some way to do multiple read-only allocations in a single request - this is horribly inefficient, at one roundtrip per cell in the cube! */ for (ir=0;ircuberv[ir]; xc.green = p->cubegv[ig]; xc.blue = p->cubebv[ib]; if (XAllocColor(disp,p->wincmap,&xc) == 0) { fprintf(stderr,"%s: can't allocate cell for cube color (%d,%d,%d)\n",__progname,ir,ig,ib); exit(1); } p->cubepix[i] = xc.pixel; if (debug) printf("cubepix[%d=(%d,%d,%d)] = %lu=(%hu,%hu,%hu)\n",i,ir,ig,ib,xc.pixel,(unsigned short int)xc.red,(unsigned short int)xc.green,(unsigned short int)xc.blue); i ++; } } } } } static int compute_color_mul(unsigned long int mask) { if (mask & 1) return(256 ); /* xxxxxxxx */ if (mask & 2) return(256 +2); /* xxxxxxx0 */ if (mask & 4) return(256 +4 ); /* xxxxxx00 */ if (mask & 8) return(256 +8 ); /* xxxxx000 */ if (mask & 16) return(256 +16 ); /* xxxx0000 */ if (mask & 32) return(256 +32 +4 ); /* xxx00000 */ if (mask & 64) return(256 +64 +16 +4 ); /* xx000000 */ return(256+128+64+32+16+8+4+2); /* x0000000 */ } static void setup_numbers_g(void) { int i; int npv; unsigned long int uli; XPixmapFormatValues *pv; pv = XListPixmapFormats(disp,&npv); for (i=0;i= npv) { fprintf(stderr,"%s: can't find a pixmap format for depth %d\n",__progname,visinfo.depth); exit(1); } if (pv[i].bits_per_pixel < 8) { fprintf(stderr,"%s: can't deal with bits_per_pixel < 8\n",__progname); exit(1); } if (style == STYLE_TRUECOLOR) { #define FOO(mask,shift,mul) \ uli = visinfo.mask; \ if (uli & ~0xff) for (shift=0;uli&~0xff;uli>>=1,shift++); \ else if (! (uli & 0x80)) for (shift=0;!(uli&0x80);uli<<=1,shift--);\ else shift = 0; \ mul = compute_color_mul(uli); FOO(red_mask,rshift,rmul) FOO(green_mask,gshift,gmul) FOO(blue_mask,bshift,bmul) #undef FOO if (debug) printf("masks (%#lx,%#lx,%#lx), shifts (%d,%d,%d), mul (%d,%d,%d)\n",visinfo.red_mask,visinfo.green_mask,visinfo.blue_mask,rshift,gshift,bshift,rmul,gmul,bmul); } bpp = pv[i].bits_per_pixel / 8; if (pv[i].bits_per_pixel != bpp*8) { fprintf(stderr,"%s: strange bits-per-pixel value %d\n",__progname,(int)pv[i].bits_per_pixel); exit(1); } bits_per_pixel = pv[i].bits_per_pixel; scanline_pad = pv[i].scanline_pad; } static void setup_numbers_p(PIC *p) { if (p->borderwstr) p->borderwidth = atoi(p->borderwstr); if (p->gammastr) p->gammafactor = 1 / atof(p->gammastr); } static void setup_image(PIC *p) { switch (p->imgtype & T_TYPE) { case T_T_PNM: p->bpl = round_up(p->xsize*bits_per_pixel,scanline_pad) / 8; p->dpic = vm_malloc(p,p->bpl*p->ysize); if (p->dpic == 0) { pprintf(p,"%s: can't allocate space for image\n",__progname); p->ximg = 0; p->pleaseclose = 1; return; } break; case T_T_DD: break; } p->ximg = XCreateImage(disp,visinfo.visual,visinfo.depth,ZPixmap,0,p->dpic,p->xsize,p->ysize,scanline_pad,p->bpl); } static void ensure_sb_wins(SCROLLBAR *sb, PIC *p, int grav) { XSetWindowAttributes attr; unsigned long int attrmask; if (sb->flags & SBF_WINS) return; attrmask = 0; attr.background_pixel = p->bgcolor.pixel; attrmask |= CWBackPixel; attr.event_mask = ButtonPressMask; attrmask |= CWEventMask; sb->win[0] = XCreateWindow(disp,p->topwin,0,0,SBWIDTH,SBWIDTH,0,visinfo.depth,InputOutput,visinfo.visual,attrmask,&attr); attr.win_gravity = grav; attrmask |= CWWinGravity; sb->win[1] = XCreateWindow(disp,p->topwin,0,0,SBWIDTH,SBWIDTH,0,visinfo.depth,InputOutput,visinfo.visual,attrmask,&attr); attrmask = 0; attr.background_pixel = p->fgcolor.pixel; attrmask |= CWBackPixel; sb->thumb[0] = XCreateWindow(disp,sb->win[0],1,1,SBWIDTH-2,SBWIDTH-2,0,visinfo.depth,InputOutput,visinfo.visual,attrmask,&attr); sb->thumb[1] = XCreateWindow(disp,sb->win[1],1,1,SBWIDTH-2,SBWIDTH-2,0,visinfo.depth,InputOutput,visinfo.visual,attrmask,&attr); XMapWindow(disp,sb->thumb[0]); XMapWindow(disp,sb->thumb[1]); sb->flags |= SBF_WINS; } static void scrollbarbutton(PIC *p, SBDIM dim, int at) { SCROLLBAR *sb; int voff; int thumbloc; XWindowChanges chg; unsigned int chgmask; int *vp; switch (dim) { case SBD_X: sb = &p->sb.x; chgmask = CWX; vp = &chg.x; break; case SBD_Y: sb = &p->sb.y; chgmask = CWY; vp = &chg.y; break; default: abort(); break; } voff = (((at - (sb->thumbsize/2)) * (sb->full-sb->width)) + ((sb->width-sb->thumbsize)/2)) / (sb->width-sb->thumbsize); if (voff < 0) voff = 0; else if (voff+sb->width > sb->full) voff = sb->full - sb->width; thumbloc = ((voff * (sb->width-sb->thumbsize)) + ((sb->full-sb->width)/2)) / (sb->full-sb->width); if (sb->voff != voff) { sb->voff = voff; *vp = - voff; XConfigureWindow(disp,p->win,chgmask,&chg); sb->voff = voff; } if (thumbloc != sb->thumbloc) { *vp = thumbloc; XConfigureWindow(disp,sb->thumb[0],chgmask,&chg); XConfigureWindow(disp,sb->thumb[1],chgmask,&chg); sb->thumbloc = thumbloc; } } /* * |------------------------------------| <- full image (full) * |----------| <- visible portion - also bar size (width) * |--| <- thumb (sb->thumbsize) * ^--^ thumb display location (sb->thumbloc) * ^-----^ scrolled-to location (sb->voff) */ static void reconfig_sb(SCROLLBAR *sb, int base, int width, int full, int othersize, SBDIM dim) { XWindowChanges chgw[2]; unsigned int chgwmask[2]; XWindowChanges chgt; unsigned int chgtmask; int thumbsize; int thumbloc; int voff; switch (dim) { case SBD_X: case SBD_Y: break; default: abort(); break; } chgwmask[0] = 0; chgwmask[1] = 0; chgtmask = 0; if (othersize != sb->othersize) { if (dim == SBD_X) { chgwmask[1] |= CWY; chgw[1].y = othersize - SBWIDTH; } else { chgwmask[1] |= CWX; chgw[1].x = othersize - SBWIDTH; } } if (base != sb->base) { if (dim == SBD_X) { chgwmask[0] |= CWX; chgw[0].x = base; chgwmask[1] |= CWX; chgw[1].x = base; } else { chgwmask[0] |= CWY; chgw[0].y = base; chgwmask[1] |= CWY; chgw[1].y = base; } } if (width != sb->width) { if (dim == SBD_X) { chgwmask[0] |= CWWidth; chgw[0].width = width; chgwmask[1] |= CWWidth; chgw[1].width = width; } else { chgwmask[0] |= CWHeight; chgw[0].height = width; chgwmask[1] |= CWHeight; chgw[1].height = width; } } thumbsize = (width * width) / full; voff = (sb->voff + (sb->width/2)) - (width/2); if (voff < 0) voff = 0; else if (voff+width > full) voff = full - width; thumbloc = ((voff * (width-thumbsize)) + ((full-width)/2)) / (full-width); if (thumbsize != sb->thumbsize) { if (dim == SBD_X) { chgtmask |= CWWidth; chgt.width = thumbsize; } else { chgtmask |= CWHeight; chgt.height = thumbsize; } } if (thumbloc != sb->thumbloc) { if (dim == SBD_X) { chgtmask |= CWX; chgt.x = thumbloc; } else { chgtmask |= CWY; chgt.y = thumbloc; } } if (chgwmask[0]) XConfigureWindow(disp,sb->win[0],chgwmask[0],&chgw[0]); if (chgwmask[1]) XConfigureWindow(disp,sb->win[1],chgwmask[1],&chgw[1]); if (chgtmask) { XConfigureWindow(disp,sb->thumb[0],chgtmask,&chgt); XConfigureWindow(disp,sb->thumb[1],chgtmask,&chgt); } sb->base = base; sb->width = width; sb->full = full; sb->othersize = othersize; sb->thumbsize = thumbsize; sb->thumbloc = thumbloc; sb->voff = voff; } static void map_sb_wins(SCROLLBAR *sb) { XMapWindow(disp,sb->win[0]); XMapWindow(disp,sb->win[1]); } static void unmap_sb_wins(SCROLLBAR *sb) { XUnmapWindow(disp,sb->win[0]); XUnmapWindow(disp,sb->win[1]); } static void resize(PIC *p, int topw, int toph) { int fw; int fh; int bx; int by; int ox; int oy; int loop; if ((topw == p->topw) && (toph == p->toph)) return; bx = 0; by = 0; fw = topw; fh = toph; p->sb.x.flags &= ~SBF_WANT; p->sb.y.flags &= ~SBF_WANT; do { loop = 0; if (!(p->sb.x.flags & SBF_WANT) && (fw < p->xsize) && (topw >= (3*SBWIDTH))) { by = SBWIDTH; fh = toph - (2 * SBWIDTH); p->sb.x.flags |= SBF_WANT; loop = 1; } if (!(p->sb.y.flags & SBF_WANT) && (fh < p->ysize) && (toph >= (3*SBWIDTH))) { bx = SBWIDTH; fw = topw - (2 * SBWIDTH); p->sb.y.flags |= SBF_WANT; loop = 1; } } while (loop); if ((p->sb.x.flags & (SBF_HAVE|SBF_WANT)) == SBF_HAVE) { unmap_sb_wins(&p->sb.x); p->sb.x.flags &= ~SBF_HAVE; } if ((p->sb.y.flags & (SBF_HAVE|SBF_WANT)) == SBF_HAVE) { unmap_sb_wins(&p->sb.y); p->sb.y.flags &= ~SBF_HAVE; } if (p->sb.x.flags & SBF_WANT) { ensure_sb_wins(&p->sb.x,p,SouthWestGravity); reconfig_sb(&p->sb.x,bx,fw,p->xsize,toph,SBD_X); ox = - p->sb.x.voff; } else { ox = (fw - p->xsize) / 2; } if (p->sb.y.flags & SBF_WANT) { ensure_sb_wins(&p->sb.y,p,NorthEastGravity); reconfig_sb(&p->sb.y,by,fh,p->ysize,topw,SBD_Y); oy = - p->sb.y.voff; } else { oy = (fh - p->ysize) / 2; } if ((p->sb.x.flags & (SBF_HAVE|SBF_WANT)) == SBF_WANT) { map_sb_wins(&p->sb.x); p->sb.x.flags |= SBF_HAVE; } if ((p->sb.y.flags & (SBF_HAVE|SBF_WANT)) == SBF_WANT) { map_sb_wins(&p->sb.y); p->sb.y.flags |= SBF_HAVE; } XMoveResizeWindow(disp,p->clipwin,bx,by,fw,fh); XMoveWindow(disp,p->win,ox,oy); p->topw = topw; p->toph = toph; } static void redisplay(PIC *p, int x, int y, int w, int h, UNUSED_ARG(int count)) { if (p->preexpose) return; if (y > p->availy) return; if (y+h > p->availy) h = p->availy + 1 - y; XPutImage(disp,p->win,p->wingc,p->ximg,x,y,x,y,w,h); } static void buttonpressed(PIC *p, unsigned int button, unsigned int state) { XWindowChanges ch; switch (button) { case Button1: ch.stack_mode = Above; XReconfigureWMWindow(disp,p->topwin,XScreenNumberOfScreen(scr),CWStackMode,&ch); if (p->crunchops) (*p->crunchops->raised)(p); break; case Button2: if (state & Button3Mask) { p->pleaseclose = 1; return; } ch.stack_mode = Above; XReconfigureWMWindow(disp,p->topwin,XScreenNumberOfScreen(scr),CWStackMode,&ch); break; case Button3: if (state & Button2Mask) { p->pleaseclose = 1; return; } ch.stack_mode = Below; XReconfigureWMWindow(disp,p->topwin,XScreenNumberOfScreen(scr),CWStackMode,&ch); break; } } static void handle_event(XEvent *e) { PIC *p; switch (e->type) { default: break; case ConfigureNotify: /* XConfigureEvent - xconfigure */ for (p=pics_h;p;p=p->flink) { if (e->xconfigure.window == p->topwin) { resize(p,e->xconfigure.width,e->xconfigure.height); } } break; case Expose: /* XExposeEvent - xexpose */ for (p=pics_h;p;p=p->flink) { if (e->xexpose.window == p->win) { p->preexpose = 0; redisplay(p,e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height,e->xexpose.count); } } break; case ButtonPress: /* XButtonPressedEvent - XButtonEvent - xbutton */ for (p=pics_h;p;p=p->flink) { if (e->xbutton.window == p->topwin) { buttonpressed(p,e->xbutton.button,e->xbutton.state); } if ( (p->sb.x.flags & SBF_HAVE) && ( (e->xbutton.window == p->sb.x.win[0]) || (e->xbutton.window == p->sb.x.win[1]) ) ) { scrollbarbutton(p,SBD_X,e->xbutton.x); } if ( (p->sb.y.flags & SBF_HAVE) && ( (e->xbutton.window == p->sb.y.win[0]) || (e->xbutton.window == p->sb.y.win[1]) ) ) { scrollbarbutton(p,SBD_Y,e->xbutton.y); } } break; case ClientMessage: /* XClientMessageEvent - xclient */ for (p=pics_h;p;p=p->flink) { if ( (e->xclient.window == p->topwin) && (e->xclient.message_type == atoms[A_WM_PROTOCOLS].a) && (e->xclient.format == 32) ) { if (e->xclient.data.l[0] == atoms[A_WM_DELETE_WINDOW].a) { p->pleaseclose = 1; } } } break; } } static void checkX(void) { XEvent e; while (XEventsQueued(disp,QueuedAfterFlush) > 0) { XNextEvent(disp,&e); handle_event(&e); } } static void crunch_link_h(PIC *p) { p->crunchblink = 0; p->crunchflink = crunchpics_h; if (crunchpics_h) crunchpics_h->crunchblink = p; else crunchpics_t = p; crunchpics_h = p; } static void crunch_unlink(PIC *p) { if (p->crunchflink) p->crunchflink->crunchblink = p->crunchblink; else crunchpics_t = p->crunchblink; if (p->crunchblink) p->crunchblink->crunchflink = p->crunchflink; else crunchpics_h = p->crunchflink; memset(&p->crunchflink,0xd1,sizeof(p->crunchflink)); memset(&p->crunchblink,0xd1,sizeof(p->crunchblink)); } static void start_crunching(PIC *p, CRUNCHOPS *ops, ...) { va_list ap; va_start(ap,ops); if (p->crunchops) abort(); crunch_link_h(p); p->crunchops = ops; (*ops->start)(p,ap); va_end(ap); } static void stop_crunching(PIC *p) { p->crunchops = 0; crunch_unlink(p); } static void closepic(PIC *p) { unsigned long int *pv; int i; if (p->crunchops) { crunch_unlink(p); (*p->crunchops->abort)(p); } if (p->topwin != None) { XDestroyWindow(disp,p->topwin); XFreeGC(disp,p->wingc); if (p->wincmap != defcmap) XFreeColormap(disp,p->wincmap); switch (style) { case STYLE_TRUECOLOR: break; case STYLE_PPMQ: case STYLE_MAPPED: pv = malloc(p->ncells*sizeof(*pv)); for (i=0;incells;i++) pv[i] = p->xcols[i].pixel; XFreeColors(disp,p->wincmap,pv,p->ncells,0); free(pv); break; case STYLE_COLORCUBE: case STYLE_SMALLCUBE: XFreeColors(disp,p->wincmap,p->cubepix,p->cubenr*p->cubeng*p->cubenb,0); break; case STYLE_MINCUBE: { int x; int ncc; ncc = p->cubenr * p->cubeng * p->cubenb; pv = malloc(p->ncubepixset*sizeof(*pv)); x = 0; for (i=0;icubepix[i]; if (x != p->ncubepixset) { fprintf(stderr,"%s: MINCUBE free bug: %d != %d\n",__progname,x,p->ncubepixset); } XFreeColors(disp,p->wincmap,pv,x,0); free(pv); } break; } } freepic(p); } static int checkclose(PIC *skip) { PIC *p; PIC *p2; int n; n = 0; for (p=pics_h;p;p=p2) { p2 = p->flink; if (p == skip) continue; if (p->pleaseclose) { closepic(p); n ++; } } return(n); } static void crunch_loadimg_start(PIC *p, va_list ap) { CP_LOADIMG *v; v = malloc(sizeof(CP_LOADIMG)); v->iptr = p->rpic; v->obase = p->dpic; p->availy = 0; v->y = 0; v->pixelfn = va_arg(ap,__typeof__(v->pixelfn)); v->done = va_arg(ap,__typeof__(v->done)); p->crunchpriv = v; } static void crunch_loadimg_step(PIC *p) { CP_LOADIMG *v; unsigned char *iptr; unsigned char *obase; unsigned char *optr; int y; unsigned long int uli; int i; int x; static unsigned int lastcycle; /* if (crunchcycle == lastcycle) return; */ lastcycle = crunchcycle; v = p->crunchpriv; iptr = v->iptr; obase = v->obase; y = v->y; #define PRELOOP \ optr = obase; for (x=0;xxsize;x++) \ { uli = (*v->pixelfn)(p,iptr); iptr += 3; #define POSTLOOP \ } obase += p->bpl; p->availy = y + 1; \ redisplay(p,0,y,p->xsize,1,0); if (p->ximg->byte_order == LSBFirst) { PRELOOP for (i=0;i>= 8; } POSTLOOP } else { PRELOOP for (i=(bpp-1)*8;i>=0;i-=8) { *optr++ = (uli >> i) & 0xff; } POSTLOOP } #undef PRELOOP #undef POSTLOOP y ++; if (y < p->ysize) { v->iptr = iptr; v->obase = obase; v->y = y; } else { stop_crunching(p); (*v->done)(p); free(v); } } static void crunch_loadimg_raised(UNUSED_ARG(PIC *p)) { } static void crunch_loadimg_abort(PIC *p) { free(p->crunchpriv); } static CRUNCHOPS crunchops_loadimg = { &crunch_loadimg_start, &crunch_loadimg_step, &crunch_loadimg_raised, &crunch_loadimg_abort }; static void load_image( PIC *p, unsigned long int (*pixelfn)(PIC *, unsigned char *), void (*done)(PIC *) ) { start_crunching(p,&crunchops_loadimg,pixelfn,done); } static unsigned long int load_pixel_TrueColor(UNUSED_ARG(PIC *p), unsigned char *ip) { #define SHIFT(val,n) (((n)<0)?((val)>>-(n)):((val)<<(n))) return( (SHIFT((unsigned long int)ip[0],rshift) & visinfo.red_mask ) | (SHIFT((unsigned long int)ip[1],gshift) & visinfo.green_mask) | (SHIFT((unsigned long int)ip[2],bshift) & visinfo.blue_mask ) ); #undef SHIFT } __inline__ static int ndiv(unsigned int, unsigned int) __attribute__((const)); static int ndiv(unsigned int num, unsigned int den) { return(rint(num/(double)den)); } static unsigned long int load_pixel_cube(PIC *p, unsigned char *ip) { #define il p->inputlimit return( p->cubepix[ (ndiv((ip[0]-il[0][0])*(p->cubenr-1),il[0][2]) * p->cuberm) + (ndiv((ip[1]-il[1][0])*(p->cubeng-1),il[1][2]) * p->cubegm) + (ndiv((ip[2]-il[2][0])*(p->cubenb-1),il[2][2]) * p->cubebm) ]); #undef il } static void write_pixel(PIC *p, unsigned long int pv, unsigned char *optr) { int i; if (p->ximg->byte_order == LSBFirst) { for (i=0;i>= 8; } } else { for (i=(bpp-1)*8;i>=0;i-=8) { *optr++ = (pv >> i) & 0xff; } } } static void add_to_group(GROUP *g, PIC *p) { g->members = realloc(g->members,(g->nmembers+1)*sizeof(PIC *)); g->members[g->nmembers++] = p; notify_group_chg(g,NGC_ADD,p); } static void clear_gaps(PIC *p) { int y; unsigned char *obase; obase = p->dpic; for (y=p->ysize;y>0;y--) { bzero(obase+(p->xsize*bpp),p->bpl-(p->xsize*bpp)); obase += p->bpl; } } static void finalize_pics(PIC *p) { vm_free(p,p->vm_rpic,p->rpic); p->rpic = 0; #if 0 p->vm_mpic = vm_finalize(p,&p->mpic); #else vm_free(p,p->vm_mpic,p->mpic); p->mpic = 0; #endif if (bpp*p->xsize != p->bpl) clear_gaps(p); p->vm_dpic = vm_finalize(p,&p->dpic); if (p->ximg) p->ximg->data = p->dpic; } static void remask_dithers(void) { CP_DITHER *v; unsigned int m; m = 0; for (v=dither_h;v;v=v->flink) { v->mask = m; m = (m << 1) | 1; } } static void cp_dither_link_h(CP_DITHER *v) { v->flink = dither_h; v->blink = 0; if (dither_h) dither_h->blink = v; else dither_t = v; dither_h = v; } static void cp_dither_unlink(CP_DITHER *v) { if (v->blink) v->blink->flink = v->flink; else dither_h = v->flink; if (v->flink) v->flink->blink = v->blink; else dither_t = v->blink; memset(&v->flink,0xd1,sizeof(v->flink)); memset(&v->blink,0xd1,sizeof(v->blink)); } static void crunch_dither_start(PIC *p, va_list ap) { int x; CP_DITHER *v; v = malloc(sizeof(CP_DITHER)); v->pic = p; v->ditherr = malloc(2*p->xsize*sizeof(*v->ditherr)); v->lerr = v->ditherr; v->cerr = v->lerr + p->xsize; v->pixel = va_arg(ap,__typeof__(v->pixel)); v->done = va_arg(ap,__typeof__(v->done)); if (p->availy < 0) p->availy = 0; v->y = 0; for (x=0;xxsize;x++) { v->lerr[x][0] = 0; v->lerr[x][1] = 0; v->lerr[x][2] = 0; } if (group_dithering) add_to_group(group_dithering,p); cp_dither_link_h(v); remask_dithers(); p->crunchpriv = v; } static void crunch_dither_step(PIC *p) { CP_DITHER *v; unsigned char (*iptr)[3]; unsigned char *obase; double (*lerr)[3]; double (*cerr)[3]; unsigned char *optr; int x; int i; double pv[3]; unsigned long int (*pixelfn)(PIC *, double *, double *); unsigned long int uli; v = p->crunchpriv; if (crunchcycle & v->mask) return; iptr = ((unsigned char (*)[3]) p->rpic) + (v->y * p->xsize); obase = p->dpic + (v->y * p->bpl); pixelfn = v->pixel; lerr = v->lerr; cerr = v->cerr; if (debug) printf("dither row %d: ",v->y); if (v->y & 1) { if (debug) printf("left->right\n"); optr = obase; for (i=0;i<3;i++) pv[i] = iptr[0][i]; uli = (*pixelfn)(p,&pv[0],&cerr[0][0]); if (debug) printf("x=0 in=(%d,%d,%d) pv=(%g,%g,%g) pix=%lu cerr=(%g,%g,%g)\n",iptr[0][0],iptr[0][1],iptr[0][2],pv[0],pv[1],pv[2],uli,cerr[0][0],cerr[0][1],cerr[0][2]); write_pixel(p,uli,optr); optr += bpp; for (x=2;xxsize;x++) { for (i=0;i<3;i++) { pv[i] = iptr[x-1][i] + (( lerr[x-2][i] + (5 * lerr[x-1][i]) + (3 * lerr[x][i]) + (7 * cerr[x-2][i]) ) / 16); } uli = (*pixelfn)(p,&pv[0],&cerr[x-1][0]); if (debug) printf("x=%d in=(%d,%d,%d) pv=(%g,%g,%g) pix=%lu cerr=(%g,%g,%g)\n",x-1,iptr[x-1][0],iptr[x-1][1],iptr[x-1][2],pv[0],pv[1],pv[2],uli,cerr[x-1][0],cerr[x-1][1],cerr[x-1][2]); write_pixel(p,uli,optr); optr += bpp; } for (i=0;i<3;i++) pv[i] = iptr[p->xsize-1][i]; uli = (*pixelfn)(p,&pv[0],&cerr[p->xsize-1][0]); if (debug) printf("x=%d in=(%d,%d,%d) pv=(%g,%g,%g) pix=%lu cerr=(%g,%g,%g)\n",p->xsize-1,iptr[p->xsize-1][0],iptr[p->xsize-1][1],iptr[p->xsize-1][2],pv[0],pv[1],pv[2],uli,cerr[p->xsize-1][0],cerr[p->xsize-1][1],cerr[p->xsize-1][2]); write_pixel(p,uli,optr); } else { if (debug) printf("right->left\n"); optr = obase + (bpp * p->xsize); for (i=0;i<3;i++) pv[i] = iptr[p->xsize-1][i]; uli = (*pixelfn)(p,&pv[0],&cerr[p->xsize-1][0]); if (debug) printf("x=%d in=(%d,%d,%d) pv=(%g,%g,%g) pix=%lu cerr=(%g,%g,%g)\n",p->xsize-1,iptr[p->xsize-1][0],iptr[p->xsize-1][1],iptr[p->xsize-1][2],pv[0],pv[1],pv[2],uli,cerr[p->xsize-1][0],cerr[p->xsize-1][1],cerr[p->xsize-1][2]); optr -= bpp; write_pixel(p,uli,optr); for (x=p->xsize-2;x>0;x--) { for (i=0;i<3;i++) { pv[i] = iptr[x][i] + (( lerr[x-1][i] + (5 * lerr[x][i]) + (3 * lerr[x+1][i]) + (7 * cerr[x+1][i]) ) / 16); } uli = (*pixelfn)(p,&pv[0],&cerr[x][0]); if (debug) printf("x=%d in=(%d,%d,%d) pv=(%g,%g,%g) pix=%lu cerr=(%g,%g,%g)\n",x,iptr[x][0],iptr[x][1],iptr[x][2],pv[0],pv[1],pv[2],uli,cerr[x][0],cerr[x][1],cerr[x][2]); optr -= bpp; write_pixel(p,uli,optr); } for (i=0;i<3;i++) pv[i] = iptr[0][i]; uli = (*pixelfn)(p,&pv[0],&cerr[0][0]); if (debug) printf("x=0 in=(%d,%d,%d) pv=(%g,%g,%g) pix=%lu cerr=(%g,%g,%g)\n",iptr[0][0],iptr[0][1],iptr[0][2],pv[0],pv[1],pv[2],uli,cerr[0][0],cerr[0][1],cerr[0][2]); optr -= bpp; write_pixel(p,uli,optr); } redisplay(p,0,v->y,p->xsize,1,0); v->y ++; if (p->availy < v->y) p->availy = v->y; v->lerr = cerr; v->cerr = lerr; if (v->y >= p->ysize) { stop_crunching(p); cp_dither_unlink(v); remask_dithers(); free(v->ditherr); if (v->done) (*v->done)(p); free(v); finalize_pics(p); if (group_dithering) del_from_group(group_dithering,p); } } static void crunch_dither_raised(PIC *p) { CP_DITHER *v; v = p->crunchpriv; cp_dither_unlink(v); cp_dither_link_h(v); remask_dithers(); } static void crunch_dither_abort(PIC *p) { CP_DITHER *v; v = p->crunchpriv; cp_dither_unlink(v); free(v->ditherr); free(v); remask_dithers(); } static CRUNCHOPS crunchops_dither = { &crunch_dither_start, &crunch_dither_step, &crunch_dither_raised, &crunch_dither_abort }; static void dither_image(PIC *p, unsigned long int (*pixelfn)(PIC *, double *, double *), void (*donefn)(PIC *)) { start_crunching(p,&crunchops_dither,pixelfn,donefn); } static unsigned long int dither_pixel_TrueColor(UNUSED_ARG(PIC *p), double *rgb, double *err) { unsigned long int pix; unsigned long int pp; int irgb; #define SHIFT(val,n) (((n)<0)?((val)>>-(n)):((val)<<(n))) pix = 0; #define FOO(inx,short,long_) \ irgb = rint(rgb[inx]); \ if (irgb < 0) irgb = 0; \ else if (irgb > 255) irgb = 255; \ pp = SHIFT((unsigned long int)irgb,short##shift) & \ visinfo.long_##_mask; \ err[inx] = rgb[inx] - ((SHIFT(pp,-short##shift) * short##mul) >> 8);\ pix |= pp; FOO(0,r,red) FOO(1,g,green) FOO(2,b,blue) #undef FOO #undef SHIFT return(pix); } static unsigned long int dither_pixel_cube(PIC *p, double *rgb, double *err) { int pix; int pp; int irgb; pix = 0; #define il p->inputlimit #define FOO(inx,x) \ irgb = rint(rgb[inx]); \ if (irgb < il[inx][0]) irgb = il[inx][0]; \ else if (irgb > il[inx][1]) irgb = il[inx][1]; \ pp = ndiv((irgb-il[inx][0])*(p->cuben##x-1),il[inx][2]); \ err[inx] = rgb[inx] - (((pp * il[inx][2]) + il[inx][0]) / (double)(p->cuben##x-1));\ pix += pp * p->cube##x##m; FOO(0,r) FOO(1,g) FOO(2,b) #undef FOO #undef il return(p->cubepix[pix]); } static void setmincubepix(PIC *p, int ir, int ig, int ib, int pix) { XColor xc; xc.red = p->cuberv[ir]; xc.green = p->cubegv[ig]; xc.blue = p->cubebv[ib]; if (XAllocColor(disp,p->wincmap,&xc) == 0) { fprintf(stderr,"%s: can't allocate cell for cube color (%d,%d,%d)\n",__progname,ir,ig,ib); exit(1); } p->cubepix[pix] = xc.pixel; if (debug) printf("cubepix[%d=(%d,%d,%d)] = %lu=(%hu,%hu,%hu)\n",pix,ir,ig,ib,xc.pixel,(unsigned short int)xc.red,(unsigned short int)xc.green,(unsigned short int)xc.blue); CUBEPIXSET(p,pix); p->ncubepixset ++; } static unsigned long int load_pixel_mincube(PIC *p, unsigned char *ip) { int ir; int ig; int ib; int pix; #define il p->inputlimit ir = ndiv((ip[0]-il[0][0])*(p->cubenr-1),il[0][2]); ig = ndiv((ip[1]-il[1][0])*(p->cubeng-1),il[1][2]); ib = ndiv((ip[2]-il[2][0])*(p->cubenb-1),il[2][2]); #undef il pix = (ir * p->cuberm) + (ig * p->cubegm) + (ib * p->cubebm); if (! CUBEPIXISSET(p,pix)) setmincubepix(p,ir,ig,ib,pix); return(p->cubepix[pix]); } static unsigned long int dither_pixel_mincube(PIC *p, double *rgb, double *err) { int pix; int irgb; int ir; int ig; int ib; #define il p->inputlimit #define FOO(inx,x) \ irgb = rint(rgb[inx]); \ if (irgb < il[inx][0]) irgb = il[inx][0]; \ else if (irgb > il[inx][1]) irgb = il[inx][1]; \ i##x = ndiv((irgb-il[inx][0])*(p->cuben##x-1),il[inx][2]); \ err[inx] = rgb[inx] - ( ((i##x * il[inx][2]) + il[inx][0]) / \ (double)(p->cuben##x-1) ); FOO(0,r) FOO(1,g) FOO(2,b) #undef FOO #undef il pix = (ir * p->cuberm) + (ig * p->cubegm) + (ib * p->cubebm); if (! CUBEPIXISSET(p,pix)) setmincubepix(p,ir,ig,ib,pix); return(p->cubepix[pix]); } __inline__ static double sq(double) __attribute__ ((const)); __inline__ static double sq(double x) { return(x*x); } static unsigned long int dither_pixel_ppmq(PIC *p, double *rgb, double *err) { int best; double bestdist; double dist; int i; CELL *c; best = -1; bestdist = 0; for (i=0;incells;i++) { c = &p->cells[i]; dist = (.299 * sq(c->mean[0]-rgb[0])) + (.587 * sq(c->mean[1]-rgb[1])) + (.114 * sq(c->mean[2]-rgb[2])); if ((best < 0) || (dist < bestdist)) { best = i; bestdist = dist; } } c = &p->cells[best]; err[0] = rgb[0] - c->mean[0]; err[1] = rgb[1] - c->mean[1]; err[2] = rgb[2] - c->mean[2]; return(p->xcols[c->index].pixel); } static void load_image_PseudoColor(PIC *p) { unsigned char *iptr; unsigned char *obase; unsigned char *optr; int x; int y; int i; unsigned long int uli; int r; int r0; iptr = p->mpic; obase = p->dpic; r0 = 3000 / p->xsize; if (r0 < 1) r0 = 1; r = r0; if (p->ximg->byte_order == LSBFirst) { for (y=0;yysize;y++) { optr = obase; for (x=0;xxsize;x++) { uli = p->xcols[*iptr++].pixel; for (i=0;i>= 8; } } obase += p->bpl; } } else { for (y=0;yysize;y++) { optr = obase; for (x=0;xxsize;x++) { uli = p->xcols[*iptr++].pixel; for (i=(bpp-1)*8;i>=0;i-=8) { *optr++ = (uli >> i) & 0xff; } } obase += p->bpl; } } p->availy = p->ysize; } static PIC *pic_by_name(const char *name) { PIC *p; for (p=pics_h;p;p=p->flink) { if ((p->topwin != None) && !strcmp(p->cname,name)) return(p); } return(0); } static const char *process_geometry_spec(const char *spec, int w, int h) { int i; int j; int nw; int nh; int wl; int hl; int l; static char *rv = 0; char tc; if (! spec) return(0); nw = 0; nh = 0; for (i=0;spec[i];i++) { switch (spec[i]) { case 'W': nw ++; break; case 'H': nh ++; break; } } if ((nw == 0) && (nh == 0)) return(spec); wl = nw ? snprintf(&tc,1,"%d",w) : 1; hl = nh ? snprintf(&tc,1,"%d",h) : 1; free(rv); l = i + (nw * (wl - 1)) + (nh * (hl - 1)); rv = malloc(l+1); j = 0; for (i=0;spec[i];i++) { switch (spec[i]) { case 'W': sprintf(rv+j,"%d",w); j += wl; break; case 'H': sprintf(rv+j,"%d",h); j += hl; break; default: rv[j++] = spec[i]; break; } } if (j != l) abort(); rv[j] = '\0'; return(rv); } static void setup_windows(PIC *p) { int x; int y; int w; int h; int bits; unsigned long int attrmask; unsigned long int gcvalmask; XGCValues gcval; XSetWindowAttributes attr; XTextProperty wn_prop; XTextProperty in_prop; XWMHints *wm_hints; XSizeHints *normal_hints; XClassHint *class_hints; PIC *repp; const char *gt; if (p->replace) { repp = pic_by_name(p->replace); if (repp) { p->topwin = repp->topwin; p->clipwin = repp->clipwin; p->win = repp->win; p->sb = repp->sb; p->wingc = repp->wingc; repp->topwin = None; repp->win = None; repp->wingc = None; repp->sb.x.flags = 0; repp->sb.y.flags = 0; repp->pleaseclose = 1; XClearArea(disp,p->win,0,0,0,0,True); return; } pprintf(p,"%s: -replace: no picture `%s'\n",__progname,p->replace); } wm_hints = XAllocWMHints(); normal_hints = XAllocSizeHints(); class_hints = XAllocClassHint(); w = p->xsize; h = p->ysize; if ((sb_min_w > 0) && (sb_min_w < w)) { w = sb_min_w; if ((sb_min_h > 0) && (sb_min_h < h)) { h = sb_min_h; } else if ((sb_min_h <= 0) || (sb_min_h >= h+(2*SBWIDTH))) { h += 2 * SBWIDTH; } } else if ((sb_min_h > 0) && (sb_min_h < h)) { h = sb_min_h; if ((sb_min_w <= 0) || (sb_min_w >= w+(2*SBWIDTH))) { w += 2 * SBWIDTH; } } if ((sb_min_h > 0) && (sb_min_h < h)) h = sb_min_h; x = (width - (w+(2*p->borderwidth))) / 2; y = (height - (h+(2*p->borderwidth))) / 2; wm_hints->flags = InputHint; wm_hints->input = True; normal_hints->min_width = 1; normal_hints->min_height = 1; normal_hints->width_inc = 1; normal_hints->height_inc = 1; normal_hints->base_width = w; normal_hints->base_height = h; normal_hints->flags = PMinSize | PBaseSize | PResizeInc; class_hints->res_name = deconst(resname); class_hints->res_class = deconst(resclass); gt = process_geometry_spec(p->geometryspec,p->xsize,p->ysize); /* pity we can't use XWMGeometry here, but it doesn't even come close to what we want. */ // bits = XWMGeometry(disp,XScreenNumberOfScreen(scr),gt,(char *)0,borderwidth,normal_hints,&x,&y,&w,&h,&normal_hints->win_gravity); bits = XParseGeometry(gt,&x,&y,&w,&h); if (bits & XNegative) x = width + x - w - (2 * p->borderwidth); if (bits & YNegative) y = height + y - h - (2 * p->borderwidth); normal_hints->x = x; normal_hints->y = y; normal_hints->width = w; normal_hints->height = h; normal_hints->win_gravity = (bits & XNegative) ? (bits & YNegative) ? SouthEastGravity : NorthEastGravity : (bits & YNegative) ? SouthWestGravity : NorthWestGravity ; normal_hints->flags |= PWinGravity; if (bits & (XValue|YValue)) normal_hints->flags |= USPosition; normal_hints->flags |= (bits & (WidthValue|HeightValue)) ? USSize : PSize; attrmask = 0; attr.background_pixel = p->bgcolor.pixel; attrmask |= CWBackPixel; attr.border_pixel = p->bdcolor.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = StructureNotifyMask | ButtonPressMask; attrmask |= CWEventMask; attr.colormap = p->wincmap; attrmask |= CWColormap; p->topwin = XCreateWindow(disp,rootwin,x,y,w,h,p->borderwidth,visinfo.depth,InputOutput,visinfo.visual,attrmask,&attr); wn_prop.value = (unsigned char *) p->cname; wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen((char *)wn_prop.value); in_prop.value = (unsigned char *) p->iconname; in_prop.encoding = XA_STRING; in_prop.format = 8; in_prop.nitems = strlen((char *)in_prop.value); XSetWMProperties(disp,p->topwin,&wn_prop,&in_prop,argv,argc,normal_hints,wm_hints,class_hints); attrmask = 0; attr.background_pixel = p->bgcolor.pixel; attrmask |= CWBackPixel; attr.border_pixel = p->bdcolor.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.colormap = p->wincmap; attrmask |= CWColormap; p->clipwin = XCreateWindow(disp,p->topwin,0,0,w,h,0,visinfo.depth,InputOutput,CopyFromParent,attrmask,&attr); XMapRaised(disp,p->clipwin); attrmask = 0; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask; attrmask |= CWEventMask; p->win = XCreateWindow(disp,p->clipwin,0,0,p->xsize,p->ysize,0,visinfo.depth,InputOutput,CopyFromParent,attrmask,&attr); XMapRaised(disp,p->win); gcvalmask = 0; gcval.foreground = p->fgcolor.pixel; gcvalmask |= GCForeground; gcval.background = p->bgcolor.pixel; gcvalmask |= GCBackground; p->wingc = XCreateGC(disp,p->win,gcvalmask,&gcval); { Atom protocols[1]; protocols[0] = atoms[A_WM_DELETE_WINDOW].a; XSetWMProtocols(disp,p->topwin,&protocols[0],1); } if (p->abovestr) { PIC *ap; ap = pic_by_name(p->abovestr); if (ap) { XWindowChanges chg; chg.stack_mode = Above; chg.sibling = ap->topwin; XReconfigureWMWindow(disp,p->topwin,XScreenNumberOfScreen(scr),CWSibling|CWStackMode,&chg); } else { pprintf(p,"%s: -above: no picture `%s'\n",__progname,p->abovestr); } } else if (p->belowstr) { PIC *ap; ap = pic_by_name(p->belowstr); if (ap) { XWindowChanges chg; chg.stack_mode = Below; chg.sibling = ap->topwin; XReconfigureWMWindow(disp,p->topwin,XScreenNumberOfScreen(scr),CWSibling|CWStackMode,&chg); } else { pprintf(p,"%s: -below: no picture `%s'\n",__progname,p->belowstr); } } else { XRaiseWindow(disp,p->topwin); } XMapWindow(disp,p->topwin); resize(p,w,h); XFree((char *)wm_hints); XFree((char *)normal_hints); XFree((char *)class_hints); } static int handleX(void) { XEvent e; if (XQLength(disp) > 0) { do { XNextEvent(disp,&e); handle_event(&e); } while (XQLength(disp) > 0); xselcnt = 0; return(1); } return(0); } static int waitkids(void) { pid_t kid; if (! anykids) return(0); kid = wait4(-1,0,WNOHANG,0); if ((kid < 0) && (errno == ECHILD)) { anykids = 0; return(1); } return(0); } static void testXread(void) { int bpend; ioctl(xfd,FIONREAD,&bpend); if (bpend == 0) XSync(disp,False); XEventsQueued(disp,QueuedAfterReading); if (xselcnt++ > 600) exit(1); } #define G_EOF (-1) #define G_BLOCK (-2) static int get(PIC *p) { if (p->pushed) { p->pushed = 0; return(p->push); } if (p->ateof) return(G_EOF); if (p->ibp >= p->ifill) return(G_BLOCK); return(p->ibuf[p->ibp++]); } static void unget(PIC *p, int v) { p->push = v; p->pushed = 1; } static void noinput(PIC *p, const char *why) { if (why) pprintf(p,"%s: can't read input picture: %s\n",__progname,why); p->readstate = RS_ERRDRAIN; p->pleaseclose = 1; } static void argcrack(char *str, int len, int *cp, char ***vp) { int c; char **v; int *vo; int i; int n; int q; int b; char *ip; char *op; static void beginarg(void) { if (n < 0) { vo = realloc(vo,(c+1)*sizeof(int)); vo[c++] = op - str; n = 0; } } static void endarg(void) { if (n >= 0) { *op++ = '\0'; n = -1; } } static void copychar(void) { beginarg(); if (op != ip) *op++ = *ip; n ++; } c = 0; n = -1; q = 0; b = 0; vo = 0; ip = str; op = ip; for (i=len;i>=0;i--,ip++) { if (b) { b = 0; copychar(); continue; } if (*ip == '\\') { b = 1; continue; } if (q) { if (*ip == q) q = 0; else copychar(); continue; } if ((*ip == '\'') || (*ip == '"')) { beginarg(); q = *ip; continue; } if (!*ip || isspace((unsigned char)*ip)) endarg(); else copychar(); } endarg(); v = malloc((c+1)*sizeof(char *)); for (i=0;ioptline) return(0); argcrack(p->optline,p->optlen,&c,&v); rv = handleargs(c,v,p); free(p->optline); p->optline = 0; p->optlen = 0; free(v); return(rv); } static void ngripe(PIC *p, unsigned char ch) { if (! p->ngriped) { pprintf(p,"%s: warning: ignoring non-digit character 0x%02x in picture\n",__progname,ch); p->ngriped = 1; } } #define RN_BLOCK 1 #define RN_EOF 2 #define RN_DONE 3 static int readn(PIC *p, int paddable) { int v; while (1) { v = get(p); if (v == G_BLOCK) return(RN_BLOCK); if (v == G_EOF) { if (p->padflag && paddable) { p->nv = 0; return(RN_DONE); } else { return(RN_EOF); } } switch (p->nstate) { case NS_PRE: if (v == '#') { p->nstate = NS_COMM; break; } if (isascii(v) && isspace(v)) break; if (! (isascii(v) && isdigit(v))) { ngripe(p,v); break; } p->nv = v - '0'; p->nstate = NS_DIGS; break; case NS_COMM: if (v == '\n') p->nstate = NS_PRE; break; case NS_DIGS: if (isascii(v) && isdigit(v)) { p->nv = (p->nv * 10) + (v - '0'); } else { unget(p,v); p->nstate = NS_PRE; return(RN_DONE); } break; } } } static void readclose(PIC *p) { if (p->outf) fclose(p->outf); close(p->infd); p->outf = 0; p->infd = -1; free(p->ibuf); p->ibuf = 0; if (p->ifilter) { if (p->filter_ci >= 0) close(p->filter_ci); free(p->fcbuf); p->fcbuf = 0; if (p->filter_co >= 0) close(p->filter_co); p->ifilter = 0; } if (p->filter_e >= 0) { close(p->filter_e); p->filter_e = -1; } if (p->filter_ec >= 0) { close(p->filter_ec); p->filter_ec = -1; } } static void cellhist(CELL *c) { int i; int j; if (c->npix == 0) { c->mean[0] = 0; c->mean[1] = 0; c->mean[2] = 0; c->var[0] = 0; c->var[1] = 0; c->var[2] = 0; c->range[0][0] = 0; c->range[0][1] = 0; c->range[1][0] = 0; c->range[1][1] = 0; c->range[2][0] = 0; c->range[2][1] = 0; } else { for (j=0;j<3;j++) { register double sum; double mean; double var; int hv; int rmin; int rmax; rmin = 255; rmax = 0; sum = 0; for (i=0;i<256;i++) { hv = c->hist[j][i]; sum += hv * i; if (hv) { if (i < rmin) rmin = i; if (i > rmax) rmax = i; } } c->range[j][0] = rmin; c->range[j][1] = rmax; mean = sum / c->npix; c->mean[j] = mean; sum = 0; for (i=0;i<256;i++) sum += c->hist[j][i] * (i - mean) * (i - mean); var = sum / c->npix; c->var[j] = var; } } #if 0 fprintf(stderr,"cell %d has %d pixel(s), (%d..%d)X(%d..%d)X(%d..%d) range (%d..%d)X(%d..%d)X(%d..%d)\n", c->index, c->npix, c->min[0],c->max[0], c->min[1],c->max[1], c->min[2],c->max[2], c->range[0][0],c->range[0][1], c->range[1][0],c->range[1][1], c->range[2][0],c->range[2][1] ); #endif } static void scancell(PIC *p, CELL *c) { unsigned char *pp; unsigned char *mp; int i; for (i=0;i<256;i++) { c->hist[0][i] = 0; c->hist[1][i] = 0; c->hist[2][i] = 0; } c->npix = 0; pp = p->rpic; mp = p->mpic; for (i=p->xsize*p->ysize;i>0;i--) { if ( (pp[0] >= c->min[0]) && (pp[0] <= c->max[0]) && (pp[1] >= c->min[1]) && (pp[1] <= c->max[1]) && (pp[2] >= c->min[2]) && (pp[2] <= c->max[2]) ) { c->npix ++; c->hist[0][pp[0]] ++; c->hist[1][pp[1]] ++; c->hist[2][pp[2]] ++; *mp = c->index; } pp += 3; mp ++; } cellhist(c); } static void setcolor(PIC *p, CELL *c) { p->xcols[c->index].red = rint(65535*pow(c->mean[0]/255.0,p->gammafactor)); p->xcols[c->index].green = rint(65535*pow(c->mean[1]/255.0,p->gammafactor)); p->xcols[c->index].blue = rint(65535*pow(c->mean[2]/255.0,p->gammafactor)); p->xcols[c->index].flags = DoRed | DoGreen | DoBlue; XStoreColor(disp,p->wincmap,&p->xcols[c->index]); } static int splitacell(PIC *p) { int i; int j; int maxr; int maxi; int r; int maxj; CELL *old; CELL *c; unsigned char *mp; unsigned char *pp; maxi = 0; /* XXX shut up "might be used uninitialized" */ maxj = 0; /* XXX shut up "might be used uninitialized" */ maxr = 0; for (i=0;incells;i++) { c = &p->cells[i]; for (j=0;j<3;j++) { r = (c->range[j][1] - c->range[j][0]) * c->npix; if (r > maxr) { maxr = r; maxi = i; maxj = j; } } } if (maxr == 0) return(-1); old = &p->cells[maxi]; j = old->mean[maxj]; #if 0 fprintf(stderr,"ppmq: %d\r",ncells); fflush(stderr); #endif #if 0 fprintf(stderr,"splitting cell %d along %c at %g\n",maxi,"rgb"[maxj],old->mean[maxj]); #endif c = &p->cells[p->ncells]; *c = *old; c->index = p->ncells; c->min[maxj] = j+1; old->max[maxj] = j; p->ncells ++; for (i=0;i<256;i++) { c->hist[0][i] = 0; c->hist[1][i] = 0; c->hist[2][i] = 0; } c->npix = 0; pp = p->rpic; mp = p->mpic; for (i=p->xsize*p->ysize;i>0;i--) { if ((i & 0xff) == 0) { checkX(); checkclose(p); if (p->pleaseclose) break; } if ((*mp == maxi) && (pp[maxj] > j)) { c->npix ++; old->npix --; c->hist[0][pp[0]] ++; c->hist[1][pp[1]] ++; c->hist[2][pp[2]] ++; old->hist[0][pp[0]] --; old->hist[1][pp[1]] --; old->hist[2][pp[2]] --; *mp = c->index; } pp += 3; mp ++; } cellhist(old); cellhist(c); return(old->index); } static void colorcrunch(PIC *p) { int csz; int i; csz = visinfo.colormap_size; if (csz > 256) csz = 256; p->cells = malloc(csz*sizeof(CELL)); p->xcols = malloc(csz*sizeof(XColor)); p->ncells = 1; p->cells[0].index = 0; p->cells[0].min[0] = 0; p->cells[0].min[1] = 0; p->cells[0].min[2] = 0; p->cells[0].max[0] = 255; p->cells[0].max[1] = 255; p->cells[0].max[2] = 255; if (! XAllocColorCells(disp,p->wincmap,False,0,0,&p->xcols[0].pixel,1)) { fprintf(stderr,"%s: can't allocate even one r/w colormap cell\n",__progname); exit(1); } p->xcols[0].red = 0; p->xcols[0].green = 0; p->xcols[0].blue = 0; p->xcols[0].flags = DoRed | DoGreen | DoBlue; XStoreColor(disp,p->wincmap,&p->xcols[0]); bzero(p->mpic,p->xsize*p->ysize); load_image_PseudoColor(p); redisplay(p,0,0,p->xsize,p->ysize,0); scancell(p,&p->cells[0]); setcolor(p,&p->cells[0]); while (1) { if (p->ncells >= p->maxcolors) { if (! quiet) printf("%s: limit reached at %d\n",__progname,p->ncells); break; } if (p->ncells >= csz) { if (! quiet) printf("%s: full at %d\n",__progname,p->ncells); break; } if (! XAllocColorCells(disp,p->wincmap,False,0,0,&p->xcols[p->ncells].pixel,1)) { if (! quiet) printf("%s: can't allocate another color at %d\n",__progname,p->ncells); break; } i = splitacell(p); if (i < 0) { if (! quiet) printf("%s: no more colors in picture at %d\n",__progname,p->ncells); XFreeColors(disp,p->wincmap,&p->xcols[p->ncells].pixel,1,0); break; } p->xcols[p->ncells-1].red = p->xcols[i].red ; p->xcols[p->ncells-1].green = p->xcols[i].green; p->xcols[p->ncells-1].blue = p->xcols[i].blue ; p->xcols[p->ncells-1].flags = DoRed | DoGreen | DoBlue; XStoreColor(disp,p->wincmap,&p->xcols[p->ncells-1]); load_image_PseudoColor(p); redisplay(p,0,0,p->xsize,p->ysize,0); setcolor(p,&p->cells[i]); setcolor(p,&p->cells[p->ncells-1]); if (! quiet) { printf("%d\r",p->ncells); fflush(stdout); } XFlush(disp); } } static void findmap(PIC *p) { int nc; unsigned long int *cols; unsigned long int *pixs; int y; int x; unsigned char *iptr; unsigned char *mptr; unsigned long int col; int l; int h; int m; p->xcols = malloc(sizeof(*p->xcols)); cols = malloc(sizeof(*cols)); pixs = malloc(sizeof(*pixs)); nc = 0; iptr = p->rpic; mptr = p->mpic; for (y=0;yysize;y++) { for (x=0;xxsize;x++) { col = (iptr[0] << 16) | (iptr[1] << 8) | iptr[2]; iptr += 3; l = -1; h = nc; m = 0; if (nc > 0) { while (h-l > 1) { m = (h + l) >> 1; if (cols[m] <= col) { l = m; } if (cols[m] >= col) { h = m; } } } if (l != h) { m = nc++; p->xcols = realloc(p->xcols,nc*sizeof(*p->xcols)); cols = realloc(cols,nc*sizeof(*cols)); pixs = realloc(pixs,nc*sizeof(*pixs)); if (h < m) { bcopy(&cols[h],&cols[h+1],(m-h)*sizeof(*cols)); bcopy(&pixs[h],&pixs[h+1],(m-h)*sizeof(*pixs)); } cols[h] = col; pixs[h] = m; p->xcols[m].red = rint(65535*pow(((col>>16)&0xff)/255.0,p->gammafactor)); p->xcols[m].green = rint(65535*pow(((col>> 8)&0xff)/255.0,p->gammafactor)); p->xcols[m].blue = rint(65535*pow(((col )&0xff)/255.0,p->gammafactor)); if (XAllocColor(disp,p->wincmap,&p->xcols[m]) == 0) { fprintf(stderr,"%s: can't allocate cell for color #%d = (%d,%d,%d)\n",__progname,nc,(int)(col>>16)&0xff,(int)(col>>8)&0xff,(int)(col)&0xff); exit(1); } } *mptr++ = pixs[h]; } } p->ncells = nc; if (! quiet) printf("Total %d colors\n",nc); } static void print_mincube_usage(PIC *p) { if (quiet) return; printf("# colors used: %d.\n",p->ncubepixset); } static void setup_strings(PIC *p) { if (p->defxname) { asprintf(&p->cname,"(%p)",(void *)p); } else { p->cname = p->xname; } } static void load_image_done_dither_TrueColor(PIC *p) { dither_image(p,&dither_pixel_TrueColor,0); } static void load_image_done_dither_cube(PIC *p) { dither_image(p,&dither_pixel_cube,0); } static void load_image_done_mincube(PIC *p) { print_mincube_usage(p); finalize_pics(p); } static void picsetup(PIC *p) { setup_numbers_p(p); setup_cmap(p); setup_colors(p); setup_image(p); if (p->pleaseclose) return; setup_strings(p); setup_windows(p); switch (p->imgtype & T_TYPE) { case T_T_PNM: switch (style) { case STYLE_TRUECOLOR: load_image(p,&load_pixel_TrueColor,dodither?&load_image_done_dither_TrueColor:&finalize_pics); break; case STYLE_PPMQ: colorcrunch(p); if (dodither) dither_image(p,&dither_pixel_ppmq,0); break; case STYLE_MAPPED: findmap(p); load_image_PseudoColor(p); redisplay(p,0,0,p->xsize,p->ysize,0); break; case STYLE_COLORCUBE: case STYLE_SMALLCUBE: setup_cube(p,style); load_image(p,&load_pixel_cube,dodither?&load_image_done_dither_cube:&finalize_pics); break; case STYLE_MINCUBE: setup_cube(p,style); if (dodither) { dither_image(p,&dither_pixel_mincube,&print_mincube_usage); } else { load_image(p,&load_pixel_mincube,&load_image_done_mincube); } break; } break; case T_T_DD: p->availy = p->ysize; break; } if (group_all) add_to_group(group_all,p); } static void maybe_inputlimit(PIC *p) { unsigned char *pp; int n; int rmin; int rmax; int gmin; int gmax; int bmin; int bmax; unsigned char v; if (style != STYLE_SMALLCUBE) return; pp = p->rpic; rmin = 255; rmax = 0; gmin = 255; gmax = 0; bmin = 255; bmax = 0; for (n=p->xsize*p->ysize;n>0;n--) { v = *pp++; if (v < rmin) rmin = v; if (v > rmax) rmax = v; v = *pp++; if (v < gmin) gmin = v; if (v > gmax) gmax = v; v = *pp++; if (v < bmin) bmin = v; if (v > bmax) bmax = v; } p->inputlimit[0][0] = rmin; p->inputlimit[0][1] = rmax; p->inputlimit[1][0] = gmin; p->inputlimit[1][1] = gmax; p->inputlimit[2][0] = bmin; p->inputlimit[2][1] = bmax; } static void readfinish(PIC *p) { p->pp = 0; p->readstate = RS_ERRDRAIN; maybe_inputlimit(p); p->vm_rpic = vm_finalize(p,&p->rpic); picsetup(p); } static void supply_pixel(PIC *p, int r, int g, int b) { if (! p->pp) return; if (p->maxval != 255) { p->pp[0] = (r * 255) / p->maxval; p->pp[1] = (g * 255) / p->maxval; p->pp[2] = (b * 255) / p->maxval; } else { p->pp[0] = r; p->pp[1] = g; p->pp[2] = b; } p->pp += 3; if (--p->pixleft < 1) readfinish(p); } static void bcopy_pgmtopbm(const void *from, void *to, int npix) { const unsigned char *fp; unsigned char *tp; unsigned char c; fp = from; tp = to; for (;npix>0;npix--) { c = *fp++; *tp++ = c; *tp++ = c; *tp++ = c; } } static GROUP *findgroup(const char *name, GROUP ***ptr) { GROUP **gp; GROUP *g; for (gp=&groups;(g=*gp);gp=&g->link) { if (!strcmp(g->name,name)) { if (ptr) *ptr = gp; return(g); } } return(0); } static void group_expand(PIC *p, const char *tag, int *npp, const char ***nvp, char **qvp) #define np (*npp) #define nv (*nvp) #define qv (*qvp) { int va; int i; int j; GROUP *g; GROUP zg; va = np; i = 0; zg.nmembers = 0; zg.members = 0; while (i < np) { if ((nv[i][0] == '@') && !qv[i]) { g = findgroup(nv[i]+1,0); if (! g) { pprintf(p,"%s %s: no group `%s'\n",__progname,tag,nv[i]+1); g = &zg; } if (np+g->nmembers > va) { va = np + g->nmembers; nv = realloc(nv,va*sizeof(*nv)); qv = realloc(qv,va*sizeof(*qv)); } switch (g->nmembers) { case 0: if (i < np-1) { bcopy(&nv[i+1],&nv[i],(np-1-i)*sizeof(*nv)); bcopy(&qv[i+1],&qv[i],(np-1-i)*sizeof(*qv)); } np --; break; case 1: nv[i] = g->members[0]->cname; qv[i] = 1; i ++; break; default: if (i < np-1) { bcopy(&nv[i+1],&nv[i+g->nmembers],(np-1-i)*sizeof(*nv)); bcopy(&qv[i+1],&qv[i+g->nmembers],(np-1-i)*sizeof(*qv)); } np += g->nmembers - 1; for (j=0;jnmembers;j++) { nv[i] = g->members[j]->cname; qv[i] = 1; i ++; } break; } } else { i ++; } } } #undef np #undef nv #undef qv static int picvec(PIC *p, const char *tag, int mincnt, int *npp, const char ***nvp, char **qvp, PIC ***pvp) #define np (*npp) #define nv (*nvp) #define qv (*qvp) #define pv (*pvp) { int errs; int i; pv = 0; if ((np == 1) && !qv[0] && !strcmp(nv[0],"*")) { PIC *t; free(nv); free(qv); np = 0; for (t=pics_h;t;t=t->flink) if (t->topwin != None) np ++; nv = malloc(np*sizeof(const char *)); qv = malloc(np); pv = malloc(np*sizeof(PIC *)); i = 0; for (t=pics_h;t;t=t->flink) { if (t->topwin != None) { nv[i] = t->cname; pv[i] = t; qv[i] = 1; i ++; } } } else { group_expand(p,tag,&np,&nv,&qv); } if (np < mincnt) { pprintf(p,"%s %s: too few pictures (min %d)\n",__progname,tag,mincnt); return(1); } errs = 0; if (! pv) { pv = malloc(np*sizeof(PIC *)); for (i=0;i 0) { skip --; continue; } if (0) { needarg:; pprintf(p,"%s stripe: %s needs a following argument\n",__progname,v[i]); errs ++; continue; } if (v[i][0] != '-') { nv[np] = v[i]; qv[np] = 0; np ++; } #define WANTARG() do { if (i+(++skip) >= c) goto needarg; } while (0) else if (!strcmp(v[i],"-help")) { pprintf(p,"%s stripe: flags are:\n",__progname); pprintf(p," -pic picname - specifies picture name\n"); pprintf(p," -{x,y,width,height} value - specifies space to stripe over\n"); pprintf(p," -[wh][0f] - specifies 0-or-full width-or-height\n"); pprintf(p," -corners - equivalent to -w0 -h0\n"); pprintf(p," -reverse - reverses order of pictures\n"); pprintf(p," -/ - specifies LL-to-UR stripe (default)\n"); pprintf(p," -\\ - specifies UL-to-LR stripe\n"); pprintf(p," -{ul,ur,ll,lr} - specifies which corner to stripe\n"); errs ++; } else if (!strcmp(v[i],"-pic")) { WANTARG(); nv[np] = v[i+skip]; qv[np] = 1; np ++; } else if (!strcmp(v[i],"-x")) { WANTARG(); if (v[i+skip][0] == '-') { x0 = atoi(v[i+skip]+1); flags |= F_XNEG; } else { x0 = atoi(v[i+skip]); flags &= ~F_XNEG; } } else if (!strcmp(v[i],"-y")) { WANTARG(); if (v[i+skip][0] == '-') { y0 = atoi(v[i+skip]+1); flags |= F_YNEG; } else { y0 = atoi(v[i+skip]); flags &= ~F_YNEG; } } else if (!strcmp(v[i],"-width")) { WANTARG(); w0 = atoi(v[i+skip]); } else if (!strcmp(v[i],"-height")) { WANTARG(); h0 = atoi(v[i+skip]); } else if (!strcmp(v[i],"-geometry")) { int bits; int x; int y; int w; int h; WANTARG(); bits = XParseGeometry(v[i+skip],&x,&y,&w,&h); if (bits & XValue) { x0 = x; if (bits & XNegative) flags |= F_XNEG; else flags &= ~F_XNEG; } if (bits & YValue) { y0 = y; if (bits & YNegative) flags |= F_YNEG; else flags &= ~F_YNEG; } if (bits & WidthValue) w0 = w; if (bits & HeightValue) h0 = h; } else if (!strcmp(v[i],"-w0")) { flags |= F_ZWIDTH; } else if (!strcmp(v[i],"-h0")) { flags |= F_ZHEIGHT; } else if (!strcmp(v[i],"-corners")) { flags |= F_ZWIDTH | F_ZHEIGHT; } else if (!strcmp(v[i],"-wf")) { flags &= ~F_ZWIDTH; } else if (!strcmp(v[i],"-hf")) { flags &= ~F_ZHEIGHT; } else if (!strcmp(v[i],"-reverse")) { flags ^= F_REVERSE; } else if (!strcmp(v[i],"-/")) { flags &= ~F_ULTORR; } else if (!strcmp(v[i],"-\\")) { flags |= F_ULTORR; } else if (!strcmp(v[i],"-ul")) { flags = (flags & ~F_CORNER) | F_C_UL; } else if (!strcmp(v[i],"-ur")) { flags = (flags & ~F_CORNER) | F_C_UR; } else if (!strcmp(v[i],"-ll")) { flags = (flags & ~F_CORNER) | F_C_LL; } else if (!strcmp(v[i],"-lr")) { flags = (flags & ~F_CORNER) | F_C_LR; } else { pprintf(p,"%s stripe: unrecognized option %s (-help for help)\n",__progname,v[i]); errs ++; } #undef WANTARG } errs += picvec(p,"stripe",2,&np,&nv,&qv,&pv); if (errs) { free(nv); free(qv); free(pv); return; } if (flags & F_REVERSE) { for (i=(np/2)-1;i>=0;i--) { PIC *t; t = pv[i]; pv[i] = pv[np-1-i]; pv[np-1-i] = t; } } if (flags & F_XNEG) x0 = width + x0 - w0; if (flags & F_YNEG) y0 = height + y0 - h0; xv = malloc(2*np*sizeof(int)); yv = xv + np; if (flags & F_ZWIDTH) { bestnum = w0; bestden = np - 1; } else { bestnum = -1; for (pass=0;pass<2;pass++) { for (i=0;itopw + (2 * pv[i]->borderwidth)); if ((pass == 0) && (num < 0)) continue; den = (flags & F_OWIDTH) ? np-1-i : i; if ((den != 0) && ((bestnum < 0) || ((num*bestden) < (bestnum*den)))) { bestnum = num; bestden = den; } } if (bestnum >= 0) break; } } for (i=0;itopw + (2 * pv[i]->borderwidth)); } else { xv[i] = x0 + ((i * bestnum) / bestden); } } if (flags & F_ZHEIGHT) { bestnum = h0; bestden = np - 1; } else { bestnum = -1; for (pass=0;pass<2;pass++) { if (flags & F_ULTORR) { for (i=0;itoph + (2 * pv[i]->borderwidth)); if ((pass == 0) && (num < 0)) continue; den = (flags & F_OHEIGHT) ? np-1-i : i; if ((den != 0) && ((bestnum < 0) || ((num*bestden) < (bestnum*den)))) { bestnum = num; bestden = den; } } } else { for (i=np-1;i>=0;i--) { num = h0 - (pv[i]->toph + (2 * pv[i]->borderwidth)); if ((pass == 0) && (num < 0)) continue; den = (flags & F_OHEIGHT) ? i : np-1-i; if ((den != 0) && ((bestnum < 0) || ((num*bestden) < (bestnum*den)))) { bestnum = num; bestden = den; } } } if (bestnum >= 0) break; } } if (flags & F_ULTORR) { for (i=0;itoph + (2 * pv[i]->borderwidth)); } else { yv[i] = y0 + ((i * bestnum) / bestden); } } } else { for (i=0;itoph + (2 * pv[i]->borderwidth)); } else { yv[i] = y0 + (((np-1-i) * bestnum) / bestden); } } } for (i=0;itopwin,XScreenNumberOfScreen(scr),CWX|CWY,&chg); } free(xv); free(pv); free(nv); free(qv); #undef F_ZWIDTH #undef F_ZHEIGHT #undef F_REVERSE #undef F_ULTORR #undef F_OWIDTH #undef F_OHEIGHT #undef F_CORNER #undef F_C_UL #undef F_C_UR #undef F_C_LL #undef F_C_LR } static void cmd_raise(PIC *p, int c, char **v) { int i; int np; const char **nv; PIC **pv; char *qv; int skip; int errs; unsigned int flags; #define F_REVERSE 0x00000001 #define F_RELATIVE 0x00000002 nv = malloc(c*sizeof(const char *)); qv = malloc(c); np = 0; skip = 0; errs = 0; flags = 0; for (i=0;i 0) { skip --; continue; } if (0) { needarg:; pprintf(p,"%s raise: %s needs a following argument\n",__progname,v[i]); errs ++; continue; } if (v[i][0] != '-') { nv[np] = v[i]; qv[np] = 0; np ++; } #define WANTARG() do { if (i+(++skip) >= c) goto needarg; } while (0) else if (!strcmp(v[i],"-pic")) { WANTARG(); nv[np] = v[i+skip]; qv[np] = 1; np ++; } else if (!strcmp(v[i],"-reverse")) { flags ^= F_REVERSE; } else if (!strcmp(v[i],"-relative")) { flags |= F_RELATIVE; } else if (!strcmp(v[i],"-absolute")) { flags &= ~F_RELATIVE; } else { pprintf(p,"%s raise: unrecognized option %s\n",__progname,v[i]); errs ++; } #undef WANTARG } errs += picvec(p,"raise",0,&np,&nv,&qv,&pv); if (errs) { free(nv); free(qv); free(pv); return; } if (flags & F_REVERSE) { for (i=(np/2)-1;i>=0;i--) { PIC *t; t = pv[i]; pv[i] = pv[np-1-i]; pv[np-1-i] = t; } } if (np > 0) { XWindowChanges chg; if (! (flags & F_RELATIVE)) { chg.stack_mode = Above; XReconfigureWMWindow(disp,pv[np-1]->topwin,XScreenNumberOfScreen(scr),CWStackMode,&chg); } for (i=np-2;i>=0;i--) { chg.stack_mode = Below; chg.sibling = pv[i+1]->topwin; XReconfigureWMWindow(disp,pv[i]->topwin,XScreenNumberOfScreen(scr),CWSibling|CWStackMode,&chg); } } free(pv); free(nv); free(qv); #undef F_REVERSE #undef F_RELATIVE } static void cmd_draise(PIC *p, int c, char **v) { int i; int np; const char **nv; PIC **pv; char *qv; int skip; int errs; unsigned int flags; #define F_REVERSE 0x00000001 nv = malloc(c*sizeof(const char *)); qv = malloc(c); np = 0; skip = 0; errs = 0; for (i=0;i 0) { skip --; continue; } if (0) { needarg:; pprintf(p,"%s draise: %s needs a following argument\n",__progname,v[i]); errs ++; continue; } if (v[i][0] != '-') { nv[np] = v[i]; qv[np] = 0; np ++; } #define WANTARG() do { if (i+(++skip) >= c) goto needarg; } while (0) else if (!strcmp(v[i],"-pic")) { WANTARG(); nv[np] = v[i+skip]; qv[np] = 1; np ++; } else if (!strcmp(v[i],"-reverse")) { flags ^= F_REVERSE; } else { pprintf(p,"%s draise: unrecognized option %s\n",__progname,v[i]); errs ++; } #undef WANTARG } errs += picvec(p,"draise",0,&np,&nv,&qv,&pv); if (errs) { free(nv); free(qv); free(pv); return; } if (flags & F_REVERSE) { for (i=(np/2)-1;i>=0;i--) { PIC *t; t = pv[i]; pv[i] = pv[np-1-i]; pv[np-1-i] = t; } } for (i=0;icrunchops) (*pv[i]->crunchops->raised)(pv[i]); } free(pv); free(nv); free(qv); #undef F_REVERSE } static void freegroup(GROUP *g) { WATCH *w; WATCH *w2; int i; for (w=watch_h;w;w=w2) { w2 = w->flink; for (i=w->ng-1;i>=0;i--) { if (w->gv[i] == g) { if (i < w->ng-1) bcopy(&w->gv[i+1],&w->gv[i],(w->ng-1-i)*sizeof(GROUP *)); w->ng --; break; } } if (w->ng == 0) { watch_unlink(w); freewatch(w); } } free(g->members); free(g); } static void cmd_group_list(PIC *p, int c, char **v) { int i; GROUP *g; static void showgroup(GROUP *g) { int i; pprintf(p,"%s: %d:",g->name,g->nmembers); for (i=0;inmembers;i++) pprintf(p," %s",g->members[i]->cname); pprintf(p,"\n"); } if (c == 0) { for (g=groups;g;g=g->link) showgroup(g); } else { for (i=0;inodelete) { pprintf(p,"%s group del: group %s is internal - can't delete\n",__progname,v[i]); } else { *gp = g->link; freegroup(g); } } } static GROUP *newgroup(const char *name) { GROUP *g; g = malloc(sizeof(GROUP)+strlen(name)+1); strcpy((char *)(g+1),name); g->name = (char *)(g+1); g->nmembers = 0; g->members = 0; g->nodelete = 0; g->link = groups; groups = g; return(g); } static void cmd_group_set(PIC *p, int c, char **v) { GROUP *g; PIC *t; int i; if (c < 1) { pprintf(p,"Usage: group set [ ...]\n"); return; } g = findgroup(v[0],0); if (g == 0) g = newgroup(v[0]); if (g->nmembers > 0) { free(g->members); g->members = 0; g->nmembers = 0; } for (i=1;inmembers-1;i>=0;i--) if (g->members[i] == p) return(1); return(0); } static void cmd_group_add(PIC *p, int c, char **v) { GROUP *g; PIC *t; int i; if (c < 1) { pprintf(p,"Usage: group add [ ...]\n"); return; } g = findgroup(v[0],0); if (! g) { pprintf(p,"%s group add: no group `%s'\n",__progname,v[0]); return; } for (i=1;i [ ...]\n"); return; } g = findgroup(v[0],0); if (! g) { pprintf(p,"%s group remove: no group `%s'\n",__progname,v[0]); return; } for (i=1;ing = c; w->gv = malloc(c*sizeof(GROUP *)); w->p = p; for (i=0;igv); free(w); return; } w->gv[i] = g; } watch_link_h(w); } static void cmd_group_count(PIC *p, int c, char **v) { int i; GROUP *g; static void showgroup(GROUP *g) { pprintf(p,"%s: %d\n",g->name,g->nmembers); } if (c == 0) { for (g=groups;g;g=g->link) showgroup(g); } else { for (i=0;i [args...]\n"); pprintf(p," s are: list del set add remove touch watch\n"); return; } if (!strcmp(v[0],"list")) cmd_group_list(p,c-1,v+1); else if (!strcmp(v[0],"del")) cmd_group_del(p,c-1,v+1); else if (!strcmp(v[0],"set")) cmd_group_set(p,c-1,v+1); else if (!strcmp(v[0],"add")) cmd_group_add(p,c-1,v+1); else if (!strcmp(v[0],"remove")) cmd_group_remove(p,c-1,v+1); else if (!strcmp(v[0],"touch")) cmd_group_touch(p,c-1,v+1); else if (!strcmp(v[0],"watch")) cmd_group_watch(p,c-1,v+1); else if (!strcmp(v[0],"count")) cmd_group_count(p,c-1,v+1); else { pprintf(p,"%s group: unknown subcommand `%s'\n",__progname,v[0]); } } static void cmd_close(PIC *p, int c, char **v) { int i; int np; const char **nv; PIC **pv; char *qv; int skip; int errs; nv = malloc(c*sizeof(const char *)); qv = malloc(c); np = 0; skip = 0; errs = 0; for (i=0;i 0) { skip --; continue; } if (0) { needarg:; pprintf(p,"%s close: %s needs a following argument\n",__progname,v[i]); errs ++; continue; } if (v[i][0] != '-') { nv[np] = v[i]; qv[np] = 0; np ++; } #define WANTARG() do { if (i+(++skip) >= c) goto needarg; } while (0) else if (!strcmp(v[i],"-pic")) { WANTARG(); nv[np] = v[i+skip]; qv[np] = 1; np ++; } else { pprintf(p,"%s close: unrecognized option %s\n",__progname,v[i]); errs ++; } #undef WANTARG } errs += picvec(p,"close",0,&np,&nv,&qv,&pv); if (errs) { free(nv); free(qv); free(pv); return; } for (i=0;ipleaseclose = 1; free(pv); free(nv); free(qv); } static void cmd_dump(PIC *p, int c, char **v) { PIC *dp; if (c != 1) { pprintf(p,"%s dump: must give exactly one picture name\n",__progname); return; } dp = pic_by_name(v[0]); if (! dp) { pprintf(p,"%s dump: no picture `%s'\n",__progname,v[0]); return; } switch (style) { case STYLE_TRUECOLOR: pprintf(p,"Dt\n%d %d %d\n",dp->xsize,dp->ysize,dp->bpl); fwrite(dp->dpic,1,dp->bpl*dp->ysize,p->outf); fflush(p->outf); break; } } static void cmd_wait(PIC *p, int c, char **v __attribute__((__unused__))) { if (c != 0) { pprintf(p,"%s wait: takes no args\n",__progname); return; } p->readstate = RS_NOTREADING; } static void do_cmd(PIC *p, char *cmd, int len) { int c; char **v; void (*fn)(PIC *, int, char **); argcrack(cmd,len,&c,&v); if (c >= 1) { if (!strcmp(v[0],"stripe")) fn = cmd_stripe; else if (!strcmp(v[0],"raise")) fn = cmd_raise; else if (!strcmp(v[0],"draise")) fn = cmd_draise; else if (!strcmp(v[0],"group")) fn = cmd_group; else if (!strcmp(v[0],"close")) fn = cmd_close; else if (!strcmp(v[0],"dump")) fn = cmd_dump; else if (!strcmp(v[0],"wait")) fn = cmd_wait; else { pprintf(p,"%s: unrecognized command `%s'\n",__progname,v[0]); fn = 0; } if (fn) (*fn)(p,c-1,v+1); } free(v); } #define GM_FAIL (-1) #define GM_EOF (-2) #define GM_BLOCK (-3) static int getmagic(PIC *p, int preflen, int n, ...) { va_list ap; int i; int c; if (p->nmagics < 0) { int maxlen; p->nmagics = n; p->magics = malloc(n*sizeof(const char *)); p->maglens = malloc(n*sizeof(int)); va_start(ap,n); maxlen = -1; for (i=n-1;i>=0;i--) { p->magics[i] = va_arg(ap,const char *); p->maglens[i] = va_arg(ap,int); if (p->maglens[i] > maxlen) maxlen = p->maglens[i]; } va_end(ap); if (preflen > 0) { for (i=n-1;i>0;i--) { if (bcmp(p->magics[i],p->magics[0],preflen)) abort(); } } p->magbuf = malloc(maxlen); p->magfill = preflen; bcopy(p->magics[0],p->magbuf,preflen); } while (1) { c = get(p); if (c == G_BLOCK) return(GM_BLOCK); if (c == EOF) return(GM_EOF); p->magbuf[p->magfill++] = c; c = 0; for (i=p->nmagics-1;i>=0;i--) { if ( (p->magfill <= p->maglens[i]) && !bcmp(p->magbuf,p->magics[i],p->magfill) ) { if (p->magfill == p->maglens[i]) return(i); c ++; } } if (c < 1) return(GM_FAIL); } } static void magicdone(PIC *p) { free(p->magics); p->magics = 0; free(p->maglens); p->maglens = 0; free(p->magbuf); p->magbuf = 0; p->nmagics = -1; } static void close_fds(void) { ACC *a; PIC *p; close(xfd); for (a=accs;a;a=a->link) close(a->fd); for (p=pics_h;p;p=p->flink) { if (p->ifilter) { if (p->filter_ci >= 0) close(p->filter_ci); if (p->filter_co >= 0) close(p->filter_co); } if (p->filter_e >= 0) close(p->filter_e); if (p->filter_ec >= 0) close(p->filter_ec); if (p->infd >= 0) close(p->infd); if (p->outf) fclose(p->outf); } } static void setnonblock(int fd) { fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); } static int forkfilter(PIC *p, const char *pgm, ...) { pid_t kid; int pi[2]; int po[2]; int e; va_list ap; int na; int i; if (p->filter_e < 0) { int pe[2]; if (pipe(&pe[0]) < 0) return(-1); p->filter_e = pe[0]; p->filter_ec = pe[1]; setnonblock(p->filter_e); } if (pipe(&po[0]) < 0) return(-1); if (! p->ifilter) { if (pipe(&pi[0]) < 0) { e = errno; close(po[0]); close(po[1]); errno = e; return(-1); } } fflush(0); kid = fork(); if (kid < 0) { e = errno; if (! p->ifilter) { close(pi[0]); close(pi[1]); } close(po[0]); close(po[1]); errno = e; return(-1); } if (kid > 0) { close(po[1]); if (p->ifilter) { close(p->infd); if (p->fcbp < p->fcfill) abort(); } else { close(pi[0]); p->filter_ci = p->infd; p->filter_co = pi[1]; setnonblock(p->filter_co); p->fcbuf = p->ibuf; p->fcfill = p->ifill; p->fcbp = p->ibp; } p->infd = po[0]; p->ibuf = malloc(IBUFSIZE); p->ifill = 0; p->ibp = 0; p->ifilter = 1; p->fpxci = -1; p->fpxco = -1; p->fpxe = -1; p->ifilt_eof_ci = 0; p->ifilt_eof_e = 0; write(pi[1],p->magbuf,p->magfill); magicdone(p); p->readstate = RS_FILTERED; anykids = 1; return(0); } e = p->filter_ec; p->filter_ec = -1; i = p->infd; p->infd = -1; close_fds(); if (p->ifilter) { if (i != 0) { dup2(i,0); close(i); } } else { close(pi[1]); if (pi[0] != 0) { dup2(pi[0],0); close(pi[0]); } } close(po[0]); if (po[1] != 1) { dup2(po[1],1); close(po[1]); } if (e != 2) { dup2(e,2); close(e); } va_start(ap,pgm); na = 0; while (va_arg(ap,const char *)) na ++; va_end(ap); { const char *av[na+1]; int i; const char *arg; va_start(ap,pgm); i = 0; while (1) { arg = va_arg(ap,const char *); if (! arg) break; if (i >= na) abort(); av[i++] = arg; } va_end(ap); if (i != na) abort(); av[i] = 0; execvp(pgm,(const void *)&av[0]); } fprintf(stderr,"%s: can't exec %s: %s\n",__progname,pgm,strerror(errno)); exit(1); } static int process_ifilters(PIC *p) { STRLIST *s; STRLIST *l; l = p->ifilters; p->ifilters = 0; while (l) { s = l; l = l->link; s->link = p->ifilters; p->ifilters = s; } while (p->ifilters) { s = p->ifilters; p->ifilters = s->link; if (forkfilter(p,"sh","sh","-c",s->ptr,(const char *)0) < 0) { pprintf(p,"can't fork input filter %s: %s",s->ptr,strerror(errno)); noinput(p,0); free(s->ptr); free(s); return(0); } free(s->ptr); free(s); } if (p->filter_ec >= 0) { close(p->filter_ec); p->filter_ec = -1; } return(0); } static void converter(PIC *p, const char *pgm) { if (forkfilter(p,pgm,pgm,(const char *)0) < 0) { pprintf(p,"can't fork %s: %s",pgm,strerror(errno)); noinput(p,0); } process_ifilters(p); } static void push_ifilter(PIC *p, const char *s, int len) { STRLIST *sl; static char *n = 0; static char *c = 0; char *t; XrmValue v; while ((len > 0) && isspace((unsigned char)*s)) { len --; s ++; } free(n); free(c); asprintf(&n,"%s.filter.%.*s",resname,len,s); asprintf(&c,"%s.Filter.%.*s",resclass,len,s); if (XrmGetResource(db,n,c,&t,&v) == False) { pprintf(p,"unknown filter `%.*s'\n",len,s); return; } sl = malloc(sizeof(STRLIST)); sl->ptr = strdup((void *)v.addr); sl->len = strlen((void *)v.addr); sl->link = p->ifilters; p->ifilters = sl; } static const char *readstate_str(READSTATE rs) { static char *bad = 0; switch (rs) { case RS_NOTREADING: return("NOTREADING"); break; case RS_COMMMAGIC: return("COMMMAGIC"); break; case RS_HDRCOMM: return("HDRCOMM"); break; case RS_COMMAND: return("COMMAND"); break; case RS_IFILTER: return("IFILTER"); break; case RS_ERRDRAIN: return("ERRDRAIN"); break; case RS_FILTERED: return("FILTERED"); break; case RS_MAG_D: return("MAG_D"); break; case RS_MAG_P: return("MAG_P"); break; case RS_MAG_GIF: return("MAG_GIF"); break; case RS_MAG_JPEG: return("MAG_JPEG"); break; case RS_MAG_TIFF_LE: return("MAG_TIFF_LE"); break; case RS_MAG_TIFF_BE: return("MAG_TIFF_BE"); break; case RS_MAG_PNG: return("MAG_PNG"); break; case RS_GET_X: return("GET_X"); break; case RS_GET_Y: return("GET_Y"); break; case RS_GET_MAXVAL: return("GET_MAXVAL"); break; case RS_GET_BPL: return("GET_BPL"); break; case RS_PIX_INIT: return("PIX_INIT"); break; case RS_PIX_R: return("PIX_R"); break; case RS_PIX_G: return("PIX_G"); break; case RS_PIX_B: return("PIX_B"); break; case RS_PIX_D: return("PIX_D"); break; } free(bad); asprintf(&bad,"?%d",(int)rs); return(bad); } static void readsome(PIC *p) { int n; int v; if (p->ibp < p->ifill) { pprintf(p,"Warning: readsome: ibp=%d ifill=%d state=%s\n",p->ibp,p->ifill,readstate_str(p->readstate)); } n = (p->imax < 0) ? IBUFSIZE : p->imax; p->ifill = read(p->infd,&p->ibuf[0],n); p->ibp = 0; if (p->ifill < 0) fprintf(stderr,"%s: picture read error: %s\n",__progname,strerror(errno)); if (p->ifill <= 0) p->ateof = 1; v = 0; /* gcc whines about "might be used uninitialized"; I think it shouldn't, but it's cheap and easy to shut it up. */ while (1) { switch <"readstate"> (p->readstate) { case RS_NOTREADING: readclose(p); return; break; case RS_ERRDRAIN: return; break; case RS_COMMMAGIC: switch <"cmc"> (get(p)) { case <"readstate"> RS_FILTERED: switch (get(p)) { case G_BLOCK: case <"cmc"> G_BLOCK: return; break; case G_EOF: case <"cmc"> G_EOF: noinput(p,p->didcmd?0:"EOF looking for magic number"); break; case 'D': case <"cmc"> 'D': p->readstate = RS_MAG_D; p->imax = -1; p->imgtype = T_T_DD; if (process_optline(p) || process_ifilters(p)) noinput(p,0); break; case 'P': case <"cmc"> 'P': p->readstate = RS_MAG_P; p->imax = -1; p->imgtype = T_T_PNM; if (process_optline(p) || process_ifilters(p)) noinput(p,0); break; default: noinput(p,"bad magic number 2"); break; default <"cmc">: noinput(p,"bad magic number 1"); break; } break; case '$': p->readstate = RS_COMMAND; p->hclen = 0; break; case '#': p->readstate = RS_HDRCOMM; p->hclen = 0; break; case '|': p->readstate = RS_IFILTER; p->hclen = 0; break; case 'G': p->readstate = RS_MAG_GIF; break; case 0xff: p->readstate = RS_MAG_JPEG; break; case 0x89: p->readstate = RS_MAG_PNG; break; case 0x4d: p->readstate = RS_MAG_TIFF_BE; break; case 0x49: p->readstate = RS_MAG_TIFF_LE; break; } break; case RS_HDRCOMM: case RS_COMMAND: case RS_IFILTER: v = get(p); switch (v) { case G_BLOCK: return; break; case G_EOF: noinput(p,"EOF inside header comment"); break; case '\n': switch (p->readstate) { case RS_HDRCOMM: p->optline = realloc(p->optline,p->optlen+1+p->hclen+1); p->optline[p->optlen] = '\n'; bcopy(p->hcbuf,p->optline+p->optlen+1,p->hclen); p->optlen += 1 + p->hclen; p->optline[p->optlen] = '\0'; break; case RS_COMMAND: p->hcbuf[p->hclen] = '\0'; do_cmd(p,p->hcbuf,p->hclen); p->didcmd = 1; break; case RS_IFILTER: p->hcbuf[p->hclen] = '\0'; push_ifilter(p,p->hcbuf,p->hclen); break; default: abort(); break; } p->readstate = RS_COMMMAGIC; break; default: if (p->hclen >= p->hchave) p->hcbuf = realloc(p->hcbuf,(p->hchave=p->hclen+8)+1); p->hcbuf[p->hclen++] = v; break; } break; case RS_MAG_D: switch (get(p)) { case G_BLOCK: return; break; case G_EOF: noinput(p,"EOF in magic number"); break; case 't': p->imgtype |= T_TRUE; p->readstate = RS_GET_X; break; default: noinput(p,"bad magic number (D)"); break; } break; case RS_MAG_P: switch (get(p)) { case G_BLOCK: return; break; case G_EOF: noinput(p,"EOF in magic number"); break; case '1': p->imgtype |= T_PBM; p->readstate = RS_GET_X; break; case '2': p->imgtype |= T_PGM; p->readstate = RS_GET_X; break; case '3': p->imgtype |= T_PPM; p->readstate = RS_GET_X; break; case '4': p->imgtype |= T_PBM|T_RAW; p->readstate = RS_GET_X; break; case '5': p->imgtype |= T_PGM|T_RAW; p->readstate = RS_GET_X; break; case '6': p->imgtype |= T_PPM|T_RAW; p->readstate = RS_GET_X; break; default: noinput(p,"bad magic number (P)"); break; } break; case RS_MAG_GIF: switch (getmagic(p,1,2,"GIF87a",6,"GIF89a",6)) { case GM_BLOCK: return; break; case GM_EOF: noinput(p,"EOF in magic number"); break; case GM_FAIL: noinput(p,"bad magic number (GIF)"); break; default: converter(p,"giftopnm"); break; } break; case RS_MAG_JPEG: switch (getmagic(p,1,1,"\xff\xd8",2)) { case GM_BLOCK: return; break; case GM_EOF: noinput(p,"EOF in magic number"); break; case GM_FAIL: noinput(p,"bad magic number (jpeg)"); break; default: converter(p,"djpeg"); break; } break; case RS_MAG_PNG: switch (getmagic(p,1,1,"\x89PNG\r\n\x1a\n",8)) { case GM_BLOCK: return; break; case GM_EOF: noinput(p,"EOF in magic number"); break; case GM_FAIL: noinput(p,"bad magic number (PNG)"); break; default: converter(p,"pngtopnm"); break; } break; case RS_MAG_TIFF_LE: { const char *magic; magic = "\x49\x49"; if (0) { case RS_MAG_TIFF_BE: magic = "\x4d\x4d"; } switch (getmagic(p,1,1,magic,2)) { case GM_BLOCK: return; break; case GM_EOF: noinput(p,"EOF in magic number"); break; case GM_FAIL: noinput(p,"bad magic number (tiff)"); break; default: pprintf(p,"TIFF requires seekability"); noinput(p,0); break; } } break; case RS_GET_X: switch (readn(p,0)) { case RN_BLOCK: return; break; case RN_EOF: noinput(p,"EOF looking for X size"); break; case RN_DONE: p->xsize = p->nv; p->readstate = RS_GET_Y; break; } break; case RS_GET_Y: switch (readn(p,0)) { case RN_BLOCK: return; break; case RN_EOF: noinput(p,"EOF looking for Y size"); break; case RN_DONE: p->ysize = p->nv; switch (p->imgtype & T_TYPE) { case T_T_PNM: p->readstate = ((p->imgtype & T_PNM) == T_PBM) ? RS_PIX_INIT : RS_GET_MAXVAL; break; case T_T_DD: p->readstate = RS_GET_BPL; break; } break; } break; case RS_GET_MAXVAL: switch (readn(p,0)) { case RN_BLOCK: return; break; case RN_EOF: noinput(p,"EOF looking for maxval"); break; case RN_DONE: p->maxval = p->nv; p->readstate = RS_PIX_INIT; break; } break; case RS_GET_BPL: switch (readn(p,0)) { case RN_BLOCK: return; break; case RN_EOF: noinput(p,"EOF looking for bpl"); break; case RN_DONE: p->bpl = p->nv; p->readstate = RS_PIX_INIT; break; } break; case RS_PIX_INIT: { int n; switch (p->imgtype & T_TYPE) { case T_T_PNM: if (p->imgtype & T_RAW) { n = get(p); if (n == G_BLOCK) return; if (! (isascii(n) && isspace(n))) unget(p,n); } if ((p->imgtype & T_PNM) == T_PBM) p->maxval = 255; n = p->xsize * p->ysize; p->rpic = vm_malloc(p,3*n); if (! p->rpic) { noinput(p,"can't malloc space for image"); return; } switch (style) { case STYLE_PPMQ: case STYLE_MAPPED: p->mpic = vm_malloc(p,n); if (! p->mpic) { noinput(p,"can't malloc space for image"); return; } break; } p->readstate = RS_PIX_R; p->pp = p->rpic; p->pix_x = 0; p->pixleft = p->xsize * p->ysize; break; case T_T_DD: n = get(p); if (n == G_BLOCK) return; if (! (isascii(n) && isspace(n))) unget(p,n); switch (p->imgtype & T_TS) { case T_TRUE: if (style != STYLE_TRUECOLOR) { noinput(p,"truecolor picture needs -true"); return; } break; } n = p->bpl * p->ysize; p->dpic = vm_malloc(p,n); if (! p->dpic) { noinput(p,"can't malloc space for image"); return; } p->readstate = RS_PIX_D; p->pp = p->dpic; p->pixleft = n; break; } } break; case RS_PIX_R: if (p->ateof && p->padflag && !p->pushed) { bzero(p->pp,p->pixleft*3); readfinish(p); } else if (p->pixleft == 0) { readfinish(p); } else { switch (p->imgtype) { case T_PBM: switch (p->nstate) { case NS_PRE: v = get(p); switch (v) { case G_BLOCK: return; break; case G_EOF: if (! p->padflag) { noinput(p,"EOF while reading data"); return; } v = 0; break; case '0': v = 0; break; case '1': v = 255; break; case '#': p->nstate = NS_COMM; v = -1; break; default: if (! (isascii(v) && isspace(v))) ngripe(p,v); v = -1; break; } break; case NS_COMM: v = -1; switch (get(p)) { case G_BLOCK: return; break; case G_EOF: if (! p->padflag) { noinput(p,"EOF while reading data"); return; } v = 0; break; case '\n': p->nstate = NS_PRE; break; } break; default: p->nstate = NS_PRE; v = -1; break; } switch (v) { case 0: case 255: supply_pixel(p,v,v,v); break; } break; case T_PBM|T_RAW: { int i; v = get(p); switch (v) { case G_BLOCK: return; break; case G_EOF: if (! p->padflag) { noinput(p,"EOF while reading data"); return; } v = 0; break; } for (i=8;i>0;i--) { if (v & 0x80) supply_pixel(p,255,255,255); else supply_pixel(p,0,0,0); if (++p->pix_x >= p->xsize) { p->pix_x = 0; break; } v <<= 1; } } break; case T_PGM: switch (readn(p,1)) { case RN_BLOCK: return; break; case RN_EOF: noinput(p,"EOF while reading data"); break; case RN_DONE: supply_pixel(p,p->nv,p->nv,p->nv); break; } break; case T_PGM|T_RAW: if ( (p->maxval == 255) && (p->ibp < p->ifill) && !p->ateof && !p->pushed && (p->ifill-p->ibp <= p->pixleft) ) { int n; n = p->ifill - p->ibp; bcopy_pgmtopbm(&p->ibuf[p->ibp],p->pp,n); p->pixleft -= n; p->ibp = p->ifill; p->pp += n * 3; } else { v = get(p); switch (v) { case G_BLOCK: return; break; case G_EOF: if (! p->padflag) { noinput(p,"EOF while reading data"); return; } v = 0; break; } supply_pixel(p,v,v,v); } break; case T_PPM: v = readn(p,1); switch (v) { case RN_BLOCK: return; break; case RN_EOF: noinput(p,"EOF while reading data"); break; case RN_DONE: p->pix_r = p->nv; p->readstate = RS_PIX_G; break; } break; case T_PPM|T_RAW: { int n; n = (p->ifill - p->ibp) / 3; if ( (n > 0) && (p->maxval == 255) && !p->ateof && !p->pushed && (n <= p->pixleft) ) { bcopy(&p->ibuf[p->ibp],p->pp,n*3); p->pixleft -= n; p->ibp += n * 3; p->pp += n * 3; } else { v = get(p); switch (v) { case G_BLOCK: return; break; case G_EOF: if (! p->padflag) { noinput(p,"EOF while reading data"); return; } supply_pixel(p,0,0,0); break; default: p->pix_r = v; p->readstate = RS_PIX_G; break; } } } break; } } break; case RS_PIX_G: if (p->imgtype & T_RAW) { v = get(p); switch (v) { case G_BLOCK: return; break; case G_EOF: if (! p->padflag) { noinput(p,"EOF while reading data"); return; } v = 0; break; } p->pix_g = v; p->readstate = RS_PIX_B; } else { v = readn(p,1); switch (v) { case RN_BLOCK: return; break; case RN_EOF: noinput(p,"EOF while reading data"); break; case RN_DONE: p->pix_g = p->nv; p->readstate = RS_PIX_B; break; } } break; case RS_PIX_B: if (p->imgtype & T_RAW) { v = get(p); switch (v) { case G_BLOCK: return; break; case G_EOF: if (! p->padflag) { noinput(p,"EOF while reading data"); return; } v = 0; break; } p->readstate = RS_PIX_R; supply_pixel(p,p->pix_r,p->pix_g,v); } else { v = readn(p,1); switch (v) { case RN_BLOCK: return; break; case RN_EOF: noinput(p,"EOF while reading data"); break; case RN_DONE: p->readstate = RS_PIX_R; supply_pixel(p,p->pix_r,p->pix_g,p->nv); break; } } break; case RS_PIX_D: if (p->ateof && p->padflag && !p->pushed) { bzero(p->pp,p->pixleft); readfinish(p); } else if (p->pixleft == 0) { readfinish(p); } else if ( (p->ibp < p->ifill) && !p->ateof && !p->pushed && (p->ifill-p->ibp <= p->pixleft) ) { int n; n = p->ifill - p->ibp; bcopy(&p->ibuf[p->ibp],p->pp,n); p->pixleft -= n; p->ibp = p->ifill; p->pp += n; } else { v = get(p); switch (v) { case G_BLOCK: return; break; case G_EOF: if (! p->padflag) { noinput(p,"EOF while reading data"); return; } v = 0; break; } *p->pp++ = v; p->pixleft --; } break; } } } static void accept_image(ACC *a) { int s; struct sockaddr_storage from; socklen_t fromlen; PIC *p; fromlen = sizeof(from); s = accept(a->fd,(struct sockaddr *)&from,&fromlen); if (s < 0) { fprintf(stderr,"%s: accept %s: %s\n",__progname,a->txt,strerror(errno)); exit(1); } p = newpic(s,s); } static void filter_set_eof_ci(PIC *p) { if (p->ifilt_eof_ci) return; p->ifilt_eof_ci = 1; close(p->filter_ci); p->filter_ci = -1; if (p->filter_co >= 0) { close(p->filter_co); p->filter_co = -1; } } static void filter_copy_write(PIC *p) { int n; n = write(p->filter_co,p->fcbuf+p->fcbp,p->fcfill-p->fcbp); if (n < 0) { if (errno == EWOULDBLOCK) return; filter_set_eof_ci(p); return; } p->fcbp += n; } static void filter_copy_read(PIC *p) { int n; n = read(p->filter_ci,p->fcbuf,IBUFSIZE); if (n <= 0) { filter_set_eof_ci(p); return; } p->fcfill = n; p->fcbp = 0; filter_copy_write(p); } static void filter_copy_err(PIC *p) { char buf[512]; int n; n = read(p->filter_e,&buf[0],sizeof(buf)); if (n <= 0) { p->ifilt_eof_e = 1; return; } fwrite(&buf[0],1,n,p->outf?:stderr); } static void run(void) { int prv; struct pollfd *pfds; int pfda; int pfdn; PIC *p; int tmo; int nrd; ACC *a; static void initpfds(void) { pfdn = 0; } static int addpfd(int fd) { if (pfdn >= pfda) pfds = realloc(pfds,(pfda=pfdn+8)*sizeof(struct pollfd)); pfds[pfdn].fd = fd; pfds[pfdn].events = POLLIN | POLLRDNORM; return(pfdn++); } static int addwpfd(int fd) { if (pfdn >= pfda) pfds = realloc(pfds,(pfda=pfdn+8)*sizeof(struct pollfd)); pfds[pfdn].fd = fd; pfds[pfdn].events = POLLOUT | POLLWRNORM; return(pfdn++); } static __inline__ int pollrd(int x) { return(pfds[x].revents & (POLLIN|POLLRDNORM)); } static __inline__ int pollwr(int x) { return(pfds[x].revents & (POLLOUT|POLLWRNORM)); } pfds = 0; pfda = 0; while (pics_h || accs) { XFlush(disp); if (debug) fflush(stdout); if (checkclose(0)) continue; if (handleX()) continue; if (waitkids()) continue; if (xselcnt > 0) poll(0,0,100); initpfds(); xpx = addpfd(xfd); for (a=accs;a;a=a->link) a->px = addpfd(a->fd); tmo = INFTIM; for (p=pics_h;p;p=p->flink) { switch (p->readstate) { case RS_NOTREADING: p->px = -1; break; case RS_ERRDRAIN: p->px = -1; if (p->ifilt_eof_e || !p->ifilter) { p->readstate = RS_NOTREADING; readclose(p); } break; default: p->px = addpfd(p->infd); break; } if (p->ifilter) { if (!p->ifilt_eof_ci && (p->fcbp >= p->fcfill)) { p->fpxci = addpfd(p->filter_ci); } else { p->fpxci = -1; } if (p->fcbp < p->fcfill) { p->fpxco = addwpfd(p->filter_co); } else { p->fpxco = -1; } if (!p->ifilt_eof_e) { p->fpxe = addpfd(p->filter_e); } else { p->fpxe = -1; } } } if (crunchpics_h) tmo = 0; prv = poll(pfds,pfdn,tmo); if (prv < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } if (pollrd(xpx)) testXread(); for (a=accs;a;a=a->link) if (pollrd(a->px)) accept_image(a); nrd = 0; for (p=pics_h;p;p=p->flink) { if ((p->px >= 0) && pollrd(p->px)) { readsome(p); nrd ++; } if (p->ifilter) { if ((p->fpxci >= 0) && pollrd(p->fpxci)) filter_copy_read(p); if ((p->fpxco >= 0) && pollwr(p->fpxco)) filter_copy_write(p); if ((p->fpxe >= 0) && pollrd(p->fpxe)) filter_copy_err(p); } } if ((nrd == 0) && crunchpics_h) { PIC *p2; crunchcycle ++; for (p=crunchpics_h;p;p=p2) { p2 = p->crunchflink; (*p->crunchops->step)(p); } } } } static void setup_groups(void) { group_all = newgroup("ALL"); group_all->nodelete = 1; if (dodither) { group_dithering = newgroup("DITHERING"); group_dithering->nodelete = 1; } } static void init(void) { pics_h = 0; pics_t = 0; crunchpics_h = 0; crunchpics_t = 0; dither_h = 0; dither_t = 0; groups = 0; watch_h = 0; watch_t = 0; anykids = 0; } static Display *open_display(const char *disp) { Display *rv; rv = XOpenDisplay(disp); if (rv == 0) { fprintf(stderr,"%s: can't open display %s\n",__progname,XDisplayName(disp)); exit(1); } xfd = XConnectionNumber(rv); return(rv); } static int err(Display *d, XErrorEvent *ee) { return((*preverr)(d,ee)); } static int ioerr(Display *d) { return((*prevIOerr)(d)); } static void fork_and_exit(void) { pid_t kid; kid = fork(); if (kid < 0) { fprintf(stderr,"%s: fork: %s\n",__progname,strerror(errno)); exit(1); } if (kid == 0) return; exit(0); } static void setup_sb_minima(const char *str) { double pct; long int xv; long int yv; char *eptr; char *eptr2; if (! str) { sb_min_w = -1; sb_min_h = -1; return; } pct = strtod(str,&eptr); if ((eptr != str) && !strcmp(eptr,"%")) { sb_min_w = width * pct / 100; sb_min_h = height * pct / 100; return; } if (*str == 'x') { yv = strtol(str+1,&eptr,0); if ((eptr != str+1) && !*eptr && (yv > 0)) { sb_min_h = yv; if (sb_min_h == yv) return; } } else { xv = strtol(str,&eptr,0); if ((eptr[0] == 'x') && (xv > 0)) { sb_min_w = xv; if (sb_min_w == xv) { if (eptr[1]) { yv = strtol(eptr+1,&eptr2,0); if (!*eptr2 && (yv > 0)) { sb_min_h = yv; if (sb_min_h == yv) return; } } else { return; } } } } fprintf(stderr,"%s: invalid scrollbar min spec `%s'\n",__progname,str); sb_min_w = -1; sb_min_h = -1; } int main(int, char **); int main(int ac, char **av) { PIC *p; saveargv(ac,av); initinitpic(); if (handleargs(ac-1,av+1,0)) exit(1); if (! nodisplay) { XrmInitialize(); disp = open_display(displayname); if (synch) XSynchronize(disp,True); preverr = XSetErrorHandler(err); prevIOerr = XSetIOErrorHandler(ioerr); setup_atoms(); setup_visual(); scr = XScreenOfDisplay(disp,visinfo.screen); width = XWidthOfScreen(scr); height = XHeightOfScreen(scr); rootwin = XRootWindowOfScreen(scr); defcmap = XDefaultColormapOfScreen(scr); setup_db(); maybeset(&initpic.geometryspec,get_default_value("geometry","Geometry")); maybeset(&initpic.foreground,get_default_value("foreground","Foreground")); maybeset(&initpic.background,get_default_value("background","Background")); maybeset(&initpic.bordercstr,get_default_value("borderColor","BorderColor")); maybeset(&initpic.borderwstr,get_default_value("borderWidth","BorderWidth")); maybeset(&initpic.vmdir,get_default_value("vmDirectory","TempDir")); maybeset(&initpic.xname,get_default_value("name","Name")); maybeset(&initpic.iconname,get_default_value("iconName","IconName")); maybeset(&initpic.gammastr,get_default_value("gamma","Gamma")); setup_sb_minima(get_default_value("maxSize","MaxSize")); setup_numbers_g(); } if (wantfork) fork_and_exit(); init(); if (accs) { setup_groups(); signal(SIGPIPE,SIG_IGN); /* get EPIPE instead */ } if ((nifiles == 0) && !accs) { p = newpic(dup(0),-1); } else { if (nifiles > 0) { int i; for (i=0;i