#include #include #include #include #include #include #include #include #include #include #include #include #define UNUSED_ARG(x) x __attribute__((__unused__)) #include "cards.h" #define SLIDER_WIDTH 30 #define COLOR_XSIZE 10 #define COLOR_YSIZE 30 #define NCOL 32 #define MAXBACKS 64 #if NCOL > CARD_MAX_NCOL #undef NCOL #define NCOL CARD_MAX_NCOL #endif #if MAXBACKS > CARD_MAX_NBACKS #undef MAXBACKS #define MAXBACKS CARD_MAX_NBACKS #endif static int argc; static char **argv; typedef struct rgb RGB; typedef struct hsv HSV; typedef struct slider SLIDER; typedef struct slider_rgb_private SLIDER_RGB_PRIVATE; typedef struct slider_hsv_private SLIDER_HSV_PRIVATE; typedef struct button BUTTON; typedef struct area AREA; typedef struct areaband AREABAND; typedef struct debugbit DEBUGBIT; typedef struct interval INTERVAL; struct interval { int s; int e; } ; struct debugbit { const char *name; unsigned int bits; } ; struct rgb { unsigned short int r; unsigned short int g; unsigned short int b; } ; struct hsv { double h; double s; double v; } ; struct slider { void (*init)(SLIDER *); void (*expose)(SLIDER *, int, int, int, int, int); void (*reload)(SLIDER *); void (*set)(SLIDER *, int); void *private; SLIDER *link; Window win; int w; int h; } ; struct slider_rgb_private { int cx; unsigned short int (*getval)(int); void (*setval)(int, unsigned short int); unsigned short int val; } ; struct slider_hsv_private { double (*getval)(int); void (*bgpixel)(int, int, double *, void *); SLIDER_HSV_PRIVATE *p_h; SLIDER_HSV_PRIVATE *p_s; SLIDER_HSV_PRIVATE *p_v; Pixmap bg; Window linewin; double val; } ; struct button { const char *label; void (*clickfn)(BUTTON *); BUTTON *flink; BUTTON *blink; char *private; Window win; int textw; int winw; XCharStruct textsize; int pressed; int disabled; int highlit; } ; struct areaband { AREABAND *link; INTERVAL y; int nrects; INTERVAL *rects; } ; struct area { AREABAND *bands; } ; static XrmDatabase db; static const char *defaults = "\ *Font: fixed\n\ *Foreground: white\n\ *Background: black\n\ *BorderColor: white\n\ *BorderWidth: 1\n\ *BorderMargin: 1\n\ *GridWidth: 1\n\ *Mag: 8\n\ "; static char *displayname; static char *geometryspec; static char *visualstr; static char *fontname; static char *foreground; static char *background; static char *bordercstr; static char *borderwstr; static char *iborderwstr; static char *ibordercstr; static char *bordermstr; static char *maggridstr; static char *magstr; static char *deckfile; static const char *name = "xeditdeck"; static const char *iconname = "xeditdeck"; static unsigned int debugging; static const char *debugoutfn; static int debugoutappend; #define DEBUG_X 0x00000001 #define DEBUG_AREA 0x00000002 #define DEBUG_ARITH 0x00000004 #define DEBUG_BAND 0x00000008 #define DEBUG_SELECT 0x00000010 #define DEBUG(x) (debugging & DEBUG_##x) static DEBUGBIT debugbits[] = { { "x", DEBUG_X }, { "area", DEBUG_AREA }, { "arith", DEBUG_ARITH }, { "band", DEBUG_BAND }, { "select", DEBUG_SELECT }, { "all", ~0 }, { 0, 0 } }; static Display *disp; static Screen *scr; static int scrwidth; static int scrheight; static int depth; static Window rootwin; static XVisualInfo visinfo; static Colormap cmap; static int cmap_is_default; static int (*prev_error)(Display *, XErrorEvent *); static int (*prev_io_error)(Display *); static XFontStruct *font; static int baselineskip; static int margin = 1; static int borderwidth = 1; static int iborderwidth = 1; static int maggridwidth = 1; static int mag = 10; static XColor fgcolor; static XColor bgcolor; static XColor bdcolor; static XColor ibdcolor; static XColor pricolor[8]; #define PCX_R 1 #define PCX_G 2 #define PCX_B 4 static const char *prinames[8] = { "(black)", "(red)", "(green)", "(yellow)", "(blue)", "(magenta)", "(cyan)", "(white)" }; static int layout_w1; static int layout_w2; static int layout_w3; static int layout_w123; static int layout_h1; static int layout_h2; static int layout_h2a; static int layout_h3; static int layout_x1; static int layout_x2; static int layout_y1; static int layout_y1a; static int layout_y2; static Window topwin; static Window parentwin; static Window menuwin; static Window cardswin; static Window picwin; static Window colorswin_c; static Window colorswin_bw; static Window bwcolorwin; static Window demowin; static Window sliderparentwin; static Window sliderwin; static Window sliderlabelwin; static Window slider_rgb_win; static Window slider_hsv_win; /* warning: code knows SLST_NONE is 0 and the rest start at 1 */ #define SLST_NONE 0 #define SLST_RGB 1 #define SLST_HSV 2 #define SLST__N 3 static int slider_style; static const char *slst_names[SLST__N] = { "", "RGB", "HSV" }; static GC gc; static GC xorgc; static Pixmap grey50; static XTextProperty wn_prop; static XTextProperty in_prop; static XSizeHints normal_hints; static XWMHints wm_hints; static XClassHint class_hints; static char name_value_char[13]; static char name_suit_char[6]; static int suits[6] = { CARD_C, CARD_D, CARD_H, CARD_S, CARD_R, CARD_B }; static int suitinv[6] = { [CARD_C] = 0, [CARD_D] = 1, [CARD_H] = 2, [CARD_S] = 3, [CARD_R] = 4, [CARD_B] = 5 }; static int values[13] = { CARD_A, CARD_2, CARD_3, CARD_4, CARD_5, CARD_6, CARD_7, CARD_8, CARD_9, CARD_T, CARD_J, CARD_Q, CARD_K }; static int valueinv[13] = { [CARD_A] = 0, [CARD_2] = 1, [CARD_3] = 2, [CARD_4] = 3, [CARD_5] = 4, [CARD_6] = 5, [CARD_7] = 6, [CARD_8] = 7, [CARD_9] = 8, [CARD_T] = 9, [CARD_J] = 10, [CARD_Q] = 11, [CARD_K] = 12 }; static char card_xnames[CARD_XDECK-CARD_DECK][2] = { "jk" }; static char card_names[CARD_XDECK][2]; static char back_names[CARD_MAX_NBACKS][3]; static XCharStruct c_n_sz[CARD_XDECK]; static XCharStruct c_n_sz_max; static XCharStruct b_n_sz[CARD_MAX_NBACKS]; static XCharStruct b_n_sz_max; static int auto_180_card[CARD_XDECK]; static int *auto_180_back; static int c_cw; static int c_ch; static int c_bw; static int c_bh; static SLIDER *allsliders = 0; static RGB colors[NCOL]; static unsigned long int colpix[NCOL]; static int selcolor = -1; static int bw_selcolor = -1; static int c_selcolor = -1; #define CT_NONE 0 #define CT_CARD 1 #define CT_BACK 2 static int cur_type = CT_NONE; static int cur_num = 0; static int cur_colorp = -1; static unsigned char cur_image[CARD_XSIZE][CARD_YSIZE]; static BUTTON *allbuttons = 0; static int button_w; #define MODE_EDIT 1 #define MODE_SEL_A 2 #define MODE_SEL_B 3 #define MODE_PASTE 4 #define MODE_WAIT 5 static int mode = MODE_EDIT; static int sel_a_x; static int sel_a_y; static int sel_b_x; static int sel_b_y; static unsigned char sel_colorp; static unsigned char sel_image[CARD_XSIZE][CARD_YSIZE]; static int sel_w; static int sel_h; static int arithnrects = 0; static INTERVAL *arithrects = 0; static int xte_junk1; static int xte_junk2; static int xte_junk3; #define XTE_JUNK &xte_junk1,&xte_junk2,&xte_junk3 static void save_click(BUTTON *); static void sel_click(BUTTON *); static void paste_click(BUTTON *); static void rot180_click(BUTTON *); static void new_back_click(BUTTON *); static void del_back_click(BUTTON *); static void quit_click(BUTTON *); static BUTTON savebutton = { "Save", save_click }; static BUTTON selbutton = { "Select", sel_click }; static BUTTON pastebutton = { "Paste", paste_click }; static BUTTON rot180button = { "Auto 180°", rot180_click }; static BUTTON newbackbutton = { "New back", new_back_click }; static BUTTON delbackbutton = { "Delete back", del_back_click }; static BUTTON quitbutton = { "Quit", quit_click }; static BUTTON *buttons[] = { &savebutton, &selbutton, &pastebutton, &rot180button, &newbackbutton, &delbackbutton, &quitbutton }; #define NBUTTONS (sizeof(buttons)/sizeof(buttons[0])) static void init_slider_rgb(SLIDER *); static void expose_slider_rgb(SLIDER *, int, int, int, int, int); static void reload_slider_rgb(SLIDER *); static void set_slider_rgb(SLIDER *, int); static unsigned short int rgb_getval_r(int); static unsigned short int rgb_getval_g(int); static unsigned short int rgb_getval_b(int); static void rgb_setval_r(int, unsigned short int); static void rgb_setval_g(int, unsigned short int); static void rgb_setval_b(int, unsigned short int); static SLIDER_RGB_PRIVATE slider_rgb_priv_r = { PCX_R, rgb_getval_r, rgb_setval_r }; static SLIDER_RGB_PRIVATE slider_rgb_priv_g = { PCX_G, rgb_getval_g, rgb_setval_g }; static SLIDER_RGB_PRIVATE slider_rgb_priv_b = { PCX_B, rgb_getval_b, rgb_setval_b }; static SLIDER slider_rgb_r = { init_slider_rgb, expose_slider_rgb, reload_slider_rgb, set_slider_rgb, &slider_rgb_priv_r }; static SLIDER slider_rgb_g = { init_slider_rgb, expose_slider_rgb, reload_slider_rgb, set_slider_rgb, &slider_rgb_priv_g }; static SLIDER slider_rgb_b = { init_slider_rgb, expose_slider_rgb, reload_slider_rgb, set_slider_rgb, &slider_rgb_priv_b }; static void init_slider_hsv(SLIDER *); static void expose_slider_hsv(SLIDER *, int, int, int, int, int); static void reload_slider_hsv(SLIDER *); static void set_slider_hsv(SLIDER *, int); static double hsv_getval_h(int); static double hsv_getval_s(int); static double hsv_getval_v(int); static void hsv_bg_h(int, int, double *, void *); static void hsv_bg_s(int, int, double *, void *); static void hsv_bg_v(int, int, double *, void *); static SLIDER_HSV_PRIVATE slider_hsv_priv_h; static SLIDER_HSV_PRIVATE slider_hsv_priv_s; static SLIDER_HSV_PRIVATE slider_hsv_priv_v; static SLIDER_HSV_PRIVATE slider_hsv_priv_h = { hsv_getval_h, hsv_bg_h, &slider_hsv_priv_h, &slider_hsv_priv_s, &slider_hsv_priv_v }; static SLIDER_HSV_PRIVATE slider_hsv_priv_s = { hsv_getval_s, hsv_bg_s, &slider_hsv_priv_h, &slider_hsv_priv_s, &slider_hsv_priv_v }; static SLIDER_HSV_PRIVATE slider_hsv_priv_v = { hsv_getval_v, hsv_bg_v, &slider_hsv_priv_h, &slider_hsv_priv_s, &slider_hsv_priv_v }; static SLIDER slider_hsv_h = { init_slider_hsv, expose_slider_hsv, reload_slider_hsv, set_slider_hsv, &slider_hsv_priv_h }; static SLIDER slider_hsv_s = { init_slider_hsv, expose_slider_hsv, reload_slider_hsv, set_slider_hsv, &slider_hsv_priv_s }; static SLIDER slider_hsv_v = { init_slider_hsv, expose_slider_hsv, reload_slider_hsv, set_slider_hsv, &slider_hsv_priv_v }; __inline__ static int imax(int, int) __attribute__ ((__const__)); __inline__ static int imax(int a, int b) { return((a>b)?a:b); } __inline__ static int imin(int, int) __attribute__ ((__const__)); __inline__ static int imin(int a, int b) { return((a>= 1; for (w=0;m;w++,m>>=1) ; return(w); } static __inline__ int intervals_overlap(int, int, int, int) __attribute__ ((__const__)); static __inline__ int intervals_overlap(int x1, int w1, int x2, int w2) { return( (x1 < x2) ? (w1 > x2-x1) : (w2 > x1-x2) ); } static __inline__ int overlapping_interval(int, int, int, int, int *, int *); static __inline__ int overlapping_interval(int x1, int w1, int x2, int w2, int *xp, int *wp) { int x; int xx1; int xx2; int xx; if (! intervals_overlap(x1,w1,x2,w2)) return(0); x = (x1 > x2) ? x1 : x2; xx1 = x1 + w1; xx2 = x2 + w2; xx = (xx1 < xx2) ? xx1 : xx2; *xp = x; *wp = xx - x; return(1); } static void *deconst(const void *cvp) { void *vp; bcopy(&cvp,&vp,sizeof(void *)); return(vp); } 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",4)) { debugoutfn = s + 4; debugoutappend = 1; return(0); } if (*s == '!') { neg = 1; s ++; } else { neg = 0; } for (i=0;debugbits[i].name;i++) { if (!strcmp(s,debugbits[i].name)) break; } if (! debugbits[i].name) { fprintf(stderr,"%s: -debug keyword `%s' unknown\n",argv[0],s); return(1); } if (neg) { debugging &= ~debugbits[i].bits; } else { debugging |= debugbits[i].bits; } return(0); } static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: unrecognized argument `%s'\n",argv[0],*av); errs ++; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",argv[0],*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-display")) { WANTARG(); displayname = av[skip]; continue; } if (!strcmp(*av,"-geometry")) { WANTARG(); geometryspec = av[skip]; continue; } if (!strcmp(*av,"-visual")) { WANTARG(); visualstr = av[skip]; continue; } if (!strcmp(*av,"-font") || !strcmp(*av,"-fn")) { WANTARG(); fontname = av[skip]; continue; } if (!strcmp(*av,"-foreground") || !strcmp(*av,"-fg")) { WANTARG(); foreground = av[skip]; continue; } if (!strcmp(*av,"-background") || !strcmp(*av,"-bg")) { WANTARG(); background = av[skip]; continue; } if (!strcmp(*av,"-bordercolor") || !strcmp(*av,"-bd")) { WANTARG(); bordercstr = av[skip]; continue; } if (!strcmp(*av,"-borderwidth") || !strcmp(*av,"-bw")) { WANTARG(); borderwstr = av[skip]; continue; } if (!strcmp(*av,"-interiorborderwidth") || !strcmp(*av,"-ibw")) { WANTARG(); iborderwstr = av[skip]; continue; } if (!strcmp(*av,"-interiorbordercolor") || !strcmp(*av,"-ibd")) { WANTARG(); ibordercstr = av[skip]; continue; } if (!strcmp(*av,"-bordermargin") || !strcmp(*av,"-bm")) { WANTARG(); bordermstr = av[skip]; continue; } if (!strcmp(*av,"-maggridwidth") || !strcmp(*av,"-mgw")) { WANTARG(); maggridstr = av[skip]; continue; } if (!strcmp(*av,"-mag")) { WANTARG(); magstr = av[skip]; continue; } if (!strcmp(*av,"-deck")) { WANTARG(); deckfile = av[skip]; continue; } if (!strcmp(*av,"-name")) { WANTARG(); name = av[skip]; continue; } if (!strcmp(*av,"-iconname")) { WANTARG(); iconname = av[skip]; continue; } if (!strcmp(*av,"-debug")) { WANTARG(); errs += setdebug(av[skip]); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",argv[0],*av); errs ++; } if (errs) { exit(1); } } #if 0 static void store_cmap(void) { int cmap[256]; int imap[256]; int mapn; int c; static void do_image(unsigned char (*img)[CARD_YSIZE][CARD_XSIZE]) { int x; int y; unsigned char v; for (y=0;y NCOL) abort(); free(card_cmap); card_cmap = malloc(mapn*sizeof(card_cmap[0])); for (c=0;c NCOL) { fprintf(stderr,"%s: %s: pack uses too many colors (%d) for this program (max %d)\n",argv[0],deckfile,card_ncol,NCOL); exit(1); } if (card_savedeck(deckfile) < 0) { fprintf(stderr,"%s: can't write %s: %s\n",argv[0],deckfile,strerror(errno)); exit(1); } fetch_cmap(); } static int ioerror_handler(UNUSED_ARG(Display *d)) { (*prev_io_error)(d); exit(1); } static int error_handler(Display *d, XErrorEvent *e) { (*prev_error)(d,e); exit(1); } static void setup_error(void) { prev_error = XSetErrorHandler(error_handler); prev_io_error = XSetIOErrorHandler(ioerror_handler); } static int effective_depth(XVisualInfo *vi) { switch (vi->class) { case PseudoColor: return(vi->depth); break; case DirectColor: return( imax( imax( maskwidth(vi->red_mask), maskwidth(vi->green_mask) ), maskwidth(vi->blue_mask) ) ); break; } abort(); } static void setup_visual(void) { int defscrno; defscrno = XScreenNumberOfScreen(XDefaultScreenOfDisplay(disp)); if (visualstr) { XVisualInfo *xvi; int nvi; XVisualInfo template; long int mask; int i; int best; unsigned long int id; char *cp; char *ep; id = strtol(visualstr,&cp,0); if (! *cp) { template.visualid = (VisualID) id; mask = VisualIDMask; } else { if (!strncasecmp(visualstr,"pseudocolor",11)) { cp = visualstr + 11; template.class = PseudoColor; } else if (!strncasecmp(visualstr,"directcolor",11)) { cp = visualstr + 11; template.class = DirectColor; } else { fprintf(stderr,"%s: %s: invalid visual option\n",argv[0],visualstr); exit(1); } mask = VisualClassMask; if (*cp) { if (*cp != '/') { fprintf(stderr,"%s: %s: invalid visual option\n",argv[0],visualstr); exit(1); } cp ++; template.depth = strtol(cp,&ep,0); if ((ep == cp) || *ep) { fprintf(stderr,"%s: %s: invalid visual option\n",argv[0],visualstr); exit(1); } mask |= VisualDepthMask; } } xvi = XGetVisualInfo(disp,mask,&template,&nvi); if (xvi == 0) { fprintf(stderr,"%s: %s: no such visual found\n",argv[0],visualstr); exit(1); } if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo returned non-nil but zero count?\n",argv[0]); exit(1); } best = -1; for (i=0;i effective_depth(&xvi[best])) { best = i; continue; } if (effective_depth(&xvi[i]) < effective_depth(&xvi[best])) { continue; } if (xvi[i].visual == XDefaultVisualOfScreen(XScreenOfDisplay(disp,xvi[i].screen))) { best = i; continue; } } if (best < 0) { fprintf(stderr,"%s: %s: no usable visual found\n",argv[0],visualstr); exit(1); } visinfo = xvi[best]; } else { XVisualInfo *xvi; int nvi; int i; int best; xvi = XGetVisualInfo(disp,0,0,&nvi); if (xvi == 0) { fprintf(stderr,"%s: what? no visuals found\n",argv[0]); exit(1); } else if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo returned non-nil but zero count?\n",argv[0]); exit(1); } best = -1; for (i=0;i effective_depth(&xvi[best])) { best = i; continue; } if (effective_depth(&xvi[i]) < effective_depth(&xvi[best])) { continue; } if (xvi[i].visual == XDefaultVisualOfScreen(XScreenOfDisplay(disp,xvi[i].screen))) { best = i; continue; } } if (best < 0) { fprintf(stderr,"%s: %s: no usable visual found\n",argv[0],visualstr); exit(1); } visinfo = xvi[best]; } if (visinfo.screen != defscrno) { fprintf(stderr,"%s: using visual %ld on screen %ld\n",argv[0],(long int)visinfo.visualid,(long int)visinfo.screen); } } static void setup_db(void) { char *str; char *home; char hostname[256]; XrmDatabase db2; db = XrmGetStringDatabase(defaults); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } str = XScreenResourceString(scr); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } home = getenv("HOME"); if (home) { str = malloc(strlen(home)+1+10+1); sprintf(str,"%s/.Xdefaults",home); db2 = XrmGetFileDatabase(str); if (db2) { XrmMergeDatabases(db2,&db); } free(str); gethostname(&hostname[0],(sizeof(hostname)/sizeof(hostname[0]))-1); hostname[(sizeof(hostname)/sizeof(hostname[0]))-1] = '\0'; str = malloc(strlen(home)+1+11+strlen(&hostname[0])+1); sprintf(str,"%s/.Xdefaults-%s",home,&hostname[0]); db2 = XrmGetFileDatabase(str); if (db2) { XrmMergeDatabases(db2,&db); } free(str); } } static void setup_cmap(void) { if (visinfo.visual == XDefaultVisualOfScreen(scr)) { cmap = XDefaultColormapOfScreen(scr); cmap_is_default = 1; } else { cmap = XCreateColormap(disp,rootwin,visinfo.visual,AllocNone); cmap_is_default = 0; } } static void maybeset(char **strp, char *str) { if (str && !*strp) *strp = str; } static char *get_default_value(const char *name, const char *class) { char *type; XrmValue value; if (XrmGetResource(db,name,class,&type,&value) == False) return(0); return(value.addr); } static void setup_names(void) { int i; int j; int c; for (i=0;i<13;i++) { switch (i) { case CARD_A: name_value_char[i] = 'A'; break; case CARD_2: name_value_char[i] = '2'; break; case CARD_3: name_value_char[i] = '3'; break; case CARD_4: name_value_char[i] = '4'; break; case CARD_5: name_value_char[i] = '5'; break; case CARD_6: name_value_char[i] = '6'; break; case CARD_7: name_value_char[i] = '7'; break; case CARD_8: name_value_char[i] = '8'; break; case CARD_9: name_value_char[i] = '9'; break; case CARD_T: name_value_char[i] = 'T'; break; case CARD_J: name_value_char[i] = 'J'; break; case CARD_Q: name_value_char[i] = 'Q'; break; case CARD_K: name_value_char[i] = 'K'; break; } } for (i=0;i<6;i++) { switch (i) { case CARD_C: name_suit_char[i] = 'C'; break; case CARD_D: name_suit_char[i] = 'D'; break; case CARD_H: name_suit_char[i] = 'H'; break; case CARD_S: name_suit_char[i] = 'S'; break; case CARD_R: name_suit_char[i] = 'R'; break; case CARD_B: name_suit_char[i] = 'B'; break; } } for (i=0;i<6;i++) { for (j=0;j<13;j++) { c = CARD_MAKE(suits[i],values[j]); card_names[c][0] = name_value_char[j]; card_names[c][1] = name_suit_char[i]; } } for (i=CARD_DECK;i0;n--,v++) { if (v->lbearing < m.lbearing) m.lbearing = v->lbearing; if (v->rbearing > m.rbearing) m.rbearing = v->rbearing; if (v->width > m.width) m.width = v->width; if (v->ascent > m.ascent) m.ascent = v->ascent; if (v->descent > m.descent) m.descent = v->descent; } *mx = m; } static void setup_font(void) { int i; font = XLoadQueryFont(disp,fontname); if (font == 0) { font = XQueryFont(disp,XGContextFromGC(XDefaultGCOfScreen(scr))); if (font == 0) { fprintf(stderr,"%s: can't query the server default font, sorry\n",argv[0]); exit(1); } fprintf(stderr,"%s: can't load font %s, using server default\n",argv[0],fontname); } baselineskip = font->ascent + font->descent; for (i=0;iwin = XCreateWindow(disp,parent,x,y,w,h,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,s->win); s->w = w; s->h = h; s->link = allsliders; allsliders = s; } static void makebuttonwindow(BUTTON *b, Window parent, int x) { unsigned long int attrmask; XSetWindowAttributes attr; attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask; attrmask |= CWEventMask; b->win = XCreateWindow(disp,parent, x, margin, b->winw, baselineskip, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,b->win); } /* window parenting hierarchy is: (root window of screen, or window manager frame) topwin parentwin picwin cardswin demowin sliderlabelwin colorswin_c colorswin_bw bwcolorwin menuwin buttons[0..NBUTTONS]->win sliderparentwin sliderwin slider parent window, slider style 1 slider implementation window(s) (repeat for each slider style) window layout within parentwin is | <------------------------w123------------------------> | 0 <-----w1-----> x1 <---------w2---------> x2 <---w3---> +----------------+--------------------------+------------+ 0 | sliderlabelwin | colorswin_c/colorswin_bw | bwcolorwin | h1 +----------------+--------------------------+------------+ y1 --- | | | | ^ | | | cardswin | h2a | |sliderparentwin | picwin | | h2 | | +------------+ y1a | | | | demowin | v +----------------+--------------------------+------------+ y2 --- | menuwin | h3 +--------------------------------------------------------+ w* and h* values do not include iborderwidth on either side */ static void setup_window(void) { int x; int y; int w; int h; int iw; int ih; int bits; int i; unsigned long int attrmask; XSetWindowAttributes attr; unsigned long int gcvalmask; XGCValues gcval; layout_w1 = margin + SLIDER_WIDTH + margin + iborderwidth + margin + SLIDER_WIDTH + margin + iborderwidth + margin + SLIDER_WIDTH + margin; layout_w2 = imax( (CARD_XSIZE * mag) + maggridwidth, ((margin+COLOR_XSIZE+margin+iborderwidth)*NCOL) - iborderwidth ); layout_w3 = imax( (6 * (margin + c_n_sz_max.rbearing-c_n_sz_max.lbearing + margin)) + (margin + b_n_sz_max.rbearing-b_n_sz_max.lbearing + margin) + (7 * iborderwidth), CARD_XSIZE ); layout_h1 = margin + COLOR_YSIZE + margin; layout_h2 = imax( (CARD_YSIZE * mag) + maggridwidth, (13 * (margin + baselineskip + margin + iborderwidth)) - iborderwidth + CARD_YSIZE ); layout_h3 = margin + baselineskip + margin; layout_w123 = layout_w1 + iborderwidth + layout_w2 + iborderwidth + layout_w3; layout_x1 = layout_w1 + iborderwidth; layout_x2 = layout_x1 + layout_w2 + iborderwidth; layout_y1 = layout_h1 + iborderwidth; layout_h2a = layout_h2 - CARD_YSIZE; layout_y1a = layout_y1 + layout_h2a; layout_y2 = layout_y1 + layout_h2 + iborderwidth; iw = layout_x2 + layout_w3; ih = layout_y2 + layout_h3; w = iw + (2 * borderwidth); h = ih + (2 * borderwidth); x = (scrwidth - w) / 2; y = (scrheight - h) / 2; bits = XParseGeometry(geometryspec,&x,&y,&w,&h); if (bits & XNegative) x = scrwidth + x - w; if (bits & YNegative) y = scrheight + y - h; attrmask = 0; attr.border_pixel = bdcolor.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = StructureNotifyMask; attrmask |= CWEventMask; attr.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask; attrmask |= CWDontPropagate; attr.colormap = cmap; attrmask |= CWColormap; w -= 2 * borderwidth; h -= 2 * borderwidth; topwin = XCreateWindow(disp,rootwin,x,y,w,h,borderwidth,depth,InputOutput,visinfo.visual,attrmask,&attr); gcvalmask = 0; gcval.font = font->fid; gcvalmask |= GCFont; gcval.background = bgcolor.pixel; gcvalmask |= GCBackground; gc = XCreateGC(disp,topwin,gcvalmask,&gcval); gcvalmask = 0; gcval.font = font->fid; gcvalmask |= GCFont; gcval.foreground = fgcolor.pixel ^ bgcolor.pixel; gcvalmask |= GCForeground; gcval.function = GXxor; gcvalmask |= GCFunction; xorgc = XCreateGC(disp,topwin,gcvalmask,&gcval); grey50 = XCreatePixmap(disp,topwin,2,2,depth); XSetForeground(disp,gc,bgcolor.pixel); XDrawPoint(disp,grey50,gc,0,0); XDrawPoint(disp,grey50,gc,1,1); XSetForeground(disp,gc,fgcolor.pixel); XDrawPoint(disp,grey50,gc,0,1); XDrawPoint(disp,grey50,gc,1,0); XSetWindowBackgroundPixmap(disp,topwin,grey50); wn_prop.value = (unsigned char *) deconst(name); wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen(wn_prop.value); in_prop.value = (unsigned char *) deconst(iconname); in_prop.encoding = XA_STRING; in_prop.format = 8; in_prop.nitems = strlen(in_prop.value); normal_hints.flags = PMinSize | PResizeInc; normal_hints.x = x; normal_hints.y = y; normal_hints.flags |= (bits & (XValue|YValue)) ? USPosition : PPosition; normal_hints.width = w; normal_hints.height = h; normal_hints.flags |= (bits & (WidthValue|HeightValue)) ? USSize : PSize; normal_hints.min_width = w; normal_hints.min_height = h; normal_hints.width_inc = 1; normal_hints.height_inc = 1; wm_hints.flags = InputHint; wm_hints.input = False; class_hints.res_name = deconst(name); class_hints.res_class = deconst("Editor"); XSetWMProperties(disp,topwin,&wn_prop,&in_prop,argv,argc,&normal_hints,&wm_hints,&class_hints); attrmask = 0; attr.background_pixel = ibdcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = StructureNotifyMask; attrmask |= CWEventMask; parentwin = XCreateWindow(disp,topwin,0,0,iw,ih,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,parentwin); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ButtonPressMask | ExposureMask; attrmask |= CWEventMask; sliderlabelwin = XCreateWindow(disp,parentwin, 0, 0, layout_w1, layout_h1, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,sliderlabelwin); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ButtonPressMask | ExposureMask; attrmask |= CWEventMask; colorswin_c = XCreateWindow(disp,parentwin, layout_x1, 0, layout_w2, layout_h1, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ButtonPressMask | ExposureMask; attrmask |= CWEventMask; colorswin_bw = XCreateWindow(disp,parentwin, layout_x1, 0, layout_w2, layout_h1, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); cur_colorp = -1; attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ButtonPressMask | ExposureMask; attrmask |= CWEventMask; bwcolorwin = XCreateWindow(disp,parentwin, layout_x2, 0, layout_w3, layout_h1, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,bwcolorwin); attrmask = 0; attr.background_pixmap = grey50; attrmask |= CWBackPixmap; attr.backing_store = NotUseful; attrmask |= CWBackingStore; sliderparentwin = XCreateWindow(disp,parentwin, 0, layout_y1, layout_w1, layout_h2, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,sliderparentwin); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; sliderwin = XCreateWindow(disp,sliderparentwin, 0, 0, layout_w1, layout_h2, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,sliderwin); slider_rgb_win = create_slider_parent(); init_slider(&slider_rgb_r,slider_rgb_win, 0, 0, SLIDER_WIDTH, layout_h2); init_slider(&slider_rgb_g,slider_rgb_win, margin+SLIDER_WIDTH+margin+iborderwidth, 0, SLIDER_WIDTH, layout_h2); init_slider(&slider_rgb_b,slider_rgb_win, (margin+SLIDER_WIDTH+margin+iborderwidth)*2, 0, SLIDER_WIDTH, layout_h2); slider_hsv_win = create_slider_parent(); init_slider(&slider_hsv_h,slider_hsv_win, 0, 0, SLIDER_WIDTH, layout_h2); init_slider(&slider_hsv_s,slider_hsv_win, margin+SLIDER_WIDTH+margin+iborderwidth, 0, SLIDER_WIDTH, layout_h2); init_slider(&slider_hsv_v,slider_hsv_win, (margin+SLIDER_WIDTH+margin+iborderwidth)*2, 0, SLIDER_WIDTH, layout_h2); attrmask = 0; attr.background_pixmap = grey50; attrmask |= CWBackPixmap; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask | ExposureMask; attrmask |= CWEventMask; picwin = XCreateWindow(disp,parentwin, layout_x1, layout_y1, layout_w2, layout_h2, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,picwin); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ButtonPressMask | ExposureMask; attrmask |= CWEventMask; cardswin = XCreateWindow(disp,parentwin, layout_x2, layout_y1, layout_w3, layout_h2a, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,cardswin); attrmask = 0; attr.background_pixmap = grey50; attrmask |= CWBackPixmap; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask; attrmask |= CWEventMask; demowin = XCreateWindow(disp,parentwin, layout_x2, layout_y1a, layout_w3, CARD_YSIZE, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,demowin); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; menuwin = XCreateWindow(disp,parentwin, 0, layout_y2, layout_w123, layout_h3, 0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,menuwin); w = layout_w123 - button_w; x = 0; for (i=0;iwinw; } XMapRaised(disp,topwin); slider_style = SLST_NONE; } static void init_slider_rgb(UNUSED_ARG(SLIDER *s)) { } static void expose_slider_rgb(SLIDER *s, UNUSED_ARG(int x), UNUSED_ARG(int y), UNUSED_ARG(int w), UNUSED_ARG(int h), int count) { int ty; SLIDER_RGB_PRIVATE *p; if (count != 0) return; p = s->private; ty = ((65535-p->val) * (s->h-1)) / 65535; XSetForeground(disp,gc,pricolor[p->cx].pixel); XFillRectangle(disp,s->win,gc,0,ty,SLIDER_WIDTH,s->h-ty); } static unsigned short int rgb_getval_r(int x) { return(colors[x].r); } static unsigned short int rgb_getval_g(int x) { return(colors[x].g); } static unsigned short int rgb_getval_b(int x) { return(colors[x].b); } static void reload_slider_rgb(SLIDER *s) { SLIDER_RGB_PRIVATE *p; p = s->private; p->val = (selcolor >= 0) ? (*p->getval)(selcolor) : 0; if (slider_style == SLST_RGB) { XClearArea(disp,s->win,0,0,0,0,False); expose_slider_rgb(s,0,0,0,0,0); } } static void re_store_colors(int x, int n) { int i; XColor cols[NCOL]; if ((x < 0) || (n < 0) || (x+n > NCOL)) abort(); for (i=0;iprivate; if (y < 0) y = 0; if (y >= s->h) y = s->h - 1; p->val = 65535 - ((y*65535) / (s->h-1)); if (selcolor >= 0) { (*p->setval)(selcolor,p->val); } if (slider_style == SLST_RGB) { XClearArea(disp,s->win,0,0,0,0,False); expose_slider_rgb(s,0,0,0,0,0); } } static void init_slider_hsv(SLIDER *s) { SLIDER_HSV_PRIVATE *p; p = s->private; p->bg = None; p->linewin = None; } static unsigned char choose_color(double val[3], double *err) { static int bits[3] = { PCX_R, PCX_G, PCX_B }; unsigned char rv; int i; rv = 0; for (i=0;i<3;i++) { if (val[i] < .5) { err[i] = val[i]; } else { rv |= bits[i]; err[i] = val[i] - 1; } } return(rv); } static Pixmap dither_to_pixmap(int w, int h, void (*pixel)(int, int, double *, void *), void *arg) { double (*errs)[3]; double (*curerr)[3]; double (*preverr)[3]; double (*tmperr)[3]; double val[3]; unsigned char *dith; unsigned char *dr; unsigned char cx; int y; int x; int i; Pixmap rv; errs = malloc(w*2*sizeof(*errs)); dith = malloc(w*h); curerr = errs; preverr = errs + w; for (x=0;x0;x--) { (*pixel)(x,y,&val[0],arg); for (i=0;i<3;i++) { val[i] += (( preverr[x+1][i] + (5 * preverr[x][i]) + (3 * preverr[x-1][i]) + (7 * curerr[x+1][i]) ) / 16); } dr[x] = choose_color(&val[0],&curerr[x][0]); } (*pixel)(0,y,&val[0],arg); for (i=0;i<3;i++) val[i] += preverr[0][i]; dr[0] = choose_color(&val[0],&curerr[0][0]); } dr += w; } rv = XCreatePixmap(disp,topwin,w,h,depth); for (cx=0;cx<8;cx++) { i = 0; dr = dith; for (y=0;yprivate; if (p->bg != None) return; p->bg = dither_to_pixmap(s->w,s->h,p->bgpixel,s); XSetWindowBackgroundPixmap(disp,s->win,p->bg); XClearArea(disp,s->win,0,0,0,0,False); } static void expose_slider_hsv(SLIDER *s, UNUSED_ARG(int x), UNUSED_ARG(int y), UNUSED_ARG(int w), UNUSED_ARG(int h), UNUSED_ARG(int count)) { slider_hsv_private_setup_bg(s); } static void slider_hsv_private_setup_line(SLIDER *s) { SLIDER_HSV_PRIVATE *p; unsigned long int attrmask; XSetWindowAttributes attr; p = s->private; if (p->linewin != None) return; attrmask = 0; attr.background_pixel = fgcolor.pixel; attrmask |= CWBackPixel; attr.border_pixel = bgcolor.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; p->linewin = XCreateWindow(disp,s->win,-1,-3,SLIDER_WIDTH,1,1,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,p->linewin); } static void reload_slider_hsv(SLIDER *s) { SLIDER_HSV_PRIVATE *p; int vy; p = s->private; p->val = (selcolor >= 0) ? (*p->getval)(selcolor) : 0; if (slider_style == SLST_HSV) { vy = p->val * s->h; if (vy >= s->h) vy = s->h - 1; slider_hsv_private_setup_line(s); XMoveWindow(disp,p->linewin,-1,vy-1); } } static void hsv_to_rgb(const HSV *hsv, RGB *rgb) { double hid; int hi; double hf; unsigned short int rgbmin; unsigned short int rgbmid; unsigned short int rgbmax; hf = modf(hsv->h*6,&hid); hi = hid; rgbmax = .001 + (65535 * hsv->v); rgbmin = .001 + rgbmax - (rgbmax * hsv->s); rgbmid = .001 + rgbmax - (((hi&1)?(1-hf):hf) * (rgbmax - rgbmin)); switch (hi) { case 0: case 6: rgb->r = rgbmax; rgb->g = rgbmin; rgb->b = rgbmid; break; case 1: rgb->r = rgbmax; rgb->g = rgbmid; rgb->b = rgbmin; break; case 2: rgb->r = rgbmid; rgb->g = rgbmax; rgb->b = rgbmin; break; case 3: rgb->r = rgbmin; rgb->g = rgbmax; rgb->b = rgbmid; break; case 4: rgb->r = rgbmin; rgb->g = rgbmid; rgb->b = rgbmax; break; case 5: rgb->r = rgbmid; rgb->g = rgbmin; rgb->b = rgbmax; break; default: abort(); break; } } static void hsv_setval(int x, double h, double s, double v) { HSV hsv; hsv.h = h; hsv.s = s; hsv.v = v; hsv_to_rgb(&hsv,&colors[x]); re_store_colors(x,1); } static void set_slider_hsv(SLIDER *s, int y) { SLIDER_HSV_PRIVATE *p; p = s->private; if (y < 0) y = 0; if (y >= s->h) y = s->h - 1; p->val = y / (double)(s->h-1); if (selcolor >= 0) { hsv_setval(selcolor,p->p_h->val,p->p_s->val,p->p_v->val); } if (slider_style == SLST_HSV) { slider_hsv_private_setup_line(s); XMoveWindow(disp,p->linewin,-1,y-1); } } static void rgb_to_hsv(const RGB *rgb, HSV *hsv) { static RGB rgbcache = { 0, 0, 0 }; static HSV hsvcache = { 0, 0, 0 }; unsigned short int rgbmin; unsigned short int rgbmax; if ( (rgb->r == rgbcache.r) && (rgb->g == rgbcache.g) && (rgb->b == rgbcache.b) ) { *hsv = hsvcache; return; } rgbcache = *rgb; rgbmax = imax(rgbcache.r,imax(rgbcache.g,rgbcache.b)); rgbmin = imin(rgbcache.r,imin(rgbcache.g,rgbcache.b)); hsvcache.v = rgbmax / 65535.0; if (hsvcache.v == 0) { hsvcache.s = 0; } else { hsvcache.s = (rgbmax - rgbmin) / (double)rgbmax; } if (hsvcache.s == 0) { hsvcache.h = 0; } else { double tr; double tg; double tb; double h; tr = (rgbmax - rgbcache.r) / (double)(rgbmax - rgbmin); tg = (rgbmax - rgbcache.g) / (double)(rgbmax - rgbmin); tb = (rgbmax - rgbcache.b) / (double)(rgbmax - rgbmin); if (rgbmax == rgbcache.r) h = 1 + tb - tg; else if (rgbmax == rgbcache.g) h = 3 + tr - tb; else h = 5 + tg - tr; hsvcache.h = h / 6; } *hsv = hsvcache; } static double hsv_getval_h(int x) { HSV hsv; rgb_to_hsv(&colors[x],&hsv); return(hsv.h); } static double hsv_getval_s(int x) { HSV hsv; rgb_to_hsv(&colors[x],&hsv); return(hsv.s); } static double hsv_getval_v(int x) { HSV hsv; rgb_to_hsv(&colors[x],&hsv); return(hsv.v); } static void hsv_bg_h(UNUSED_ARG(int x), int y, double *pix, void *arg) { static int lasty = -1; static RGB lastrgb; if (y != lasty) { SLIDER *s; HSV hsv; s = arg; hsv.h = y / (double)s->h; hsv.s = 1; hsv.v = 1; hsv_to_rgb(&hsv,&lastrgb); lasty = y; } pix[0] = lastrgb.r / 65535.0; pix[1] = lastrgb.g / 65535.0; pix[2] = lastrgb.b / 65535.0; } static void hsv_bg_s(int x, int y, double *pix, void *arg) { SLIDER *s; HSV hsv; RGB rgb; s = arg; hsv.h = x / (double)s->w; hsv.s = y / (double)s->h; hsv.v = 1; hsv_to_rgb(&hsv,&rgb); pix[0] = rgb.r / 65535.0; pix[1] = rgb.g / 65535.0; pix[2] = rgb.b / 65535.0; } static void hsv_bg_v(UNUSED_ARG(int x), int y, double *pix, void *arg) { SLIDER *s; double v; s = arg; v = y / (double)s->h; pix[0] = v; pix[1] = v; pix[2] = v; } static void setup_colpix(void) { int i; XColor cols[NCOL]; if (! XAllocColorCells(disp,cmap,False,0,0,&colpix[0],NCOL)) { fprintf(stderr,"%s: can't allocate read/write colormap cells\n",argv[0]); exit(1); } for (i=0;i=0;i--) { XDrawRectangle(disp,w,gc,rx+i,i,margin+COLOR_XSIZE+margin-1-(2*i),margin+COLOR_YSIZE+margin-1-(2*i)); } } else { XFillRectangle(disp,w,gc,rx,0,margin+COLOR_XSIZE+margin,margin); XFillRectangle(disp,w,gc,rx,margin,margin,COLOR_YSIZE+margin); XFillRectangle(disp,w,gc,rx+margin+COLOR_XSIZE,margin,margin,COLOR_YSIZE+margin); XFillRectangle(disp,w,gc,rx+margin,margin+COLOR_YSIZE,COLOR_XSIZE,margin); } } static int map_over_sliders(int (*fn)(SLIDER *)) { SLIDER *s; int rv; for (s=allsliders;s;s=s->link) { rv = (*fn)(s); if (rv) return(rv); } return(0); } static int load_color_into_slider(SLIDER *s) { (*s->reload)(s); return(0); } static void select_color(int c) { if ((c < 0) || (c >= (cur_colorp?NCOL:2))) c = -1; if (c == selcolor) return; if (selcolor >= 0) { XSetForeground(disp,gc,bgcolor.pixel); draw_colors_margin_box(selcolor,None); } selcolor = c; if (selcolor >= 0) { XSetForeground(disp,gc,fgcolor.pixel); draw_colors_margin_box(selcolor,None); } map_over_sliders(load_color_into_slider); } static void expose_sliderlabelwin(UNUSED_ARG(int x), UNUSED_ARG(int y), UNUSED_ARG(int w), UNUSED_ARG(int h), int count) { int tx; int ty; const char *s; int sl; XCharStruct ext; if ((count != 0) || !cur_colorp) return; s = &slst_names[slider_style][0]; sl = strlen(s); XTextExtents(font,s,sl,XTE_JUNK,&ext); tx = ((layout_w1 - ext.width) / 2) - ext.lbearing; ty = ((COLOR_YSIZE - baselineskip) / 2) + font->ascent; XSetForeground(disp,gc,fgcolor.pixel); XDrawString(disp,sliderlabelwin,gc,tx,ty,s,sl); } static void set_slider_style(int st) { if (st == slider_style) return; switch (slider_style) { case SLST_RGB: XUnmapWindow(disp,slider_rgb_win); break; case SLST_HSV: XUnmapWindow(disp,slider_hsv_win); break; } slider_style = st; switch (slider_style) { case SLST_RGB: XMapWindow(disp,slider_rgb_win); break; case SLST_HSV: XMapWindow(disp,slider_hsv_win); break; } XClearArea(disp,sliderlabelwin,0,0,0,0,False); expose_sliderlabelwin(0,0,0,0,0); map_over_sliders(load_color_into_slider); } static void expose_colorswin_c(int x, int y, int w, int h, UNUSED_ARG(int count)) { int i; int any; int y0; int h0; y0 = y; h0 = h; if (y < margin) { h += margin - y; y = margin; } if (h > COLOR_YSIZE) h = COLOR_YSIZE; any = 0; for (i=0;i= 0) && intervals_overlap(x,w,selcolor*(margin+COLOR_XSIZE+margin+iborderwidth),margin+COLOR_YSIZE+margin) ) { XSetForeground(disp,gc,fgcolor.pixel); draw_colors_margin_box(selcolor,colorswin_c); } } static void expose_colorswin_bw(int x, int y, int w, int h, UNUSED_ARG(int count)) { int i; int y0; int h0; y0 = y; h0 = h; if (y < margin) { h += margin - y; y = margin; } if (h > COLOR_YSIZE) h = COLOR_YSIZE; if (intervals_overlap(x,w,margin+COLOR_XSIZE+margin,iborderwidth)) { XSetForeground(disp,gc,ibdcolor.pixel); XFillRectangle(disp,colorswin_bw,gc,margin+COLOR_XSIZE+margin,y,iborderwidth,h); } for (i=0;i<2;i++) { if (intervals_overlap(x,w,margin+(i*(margin+COLOR_XSIZE+margin+iborderwidth)),COLOR_XSIZE)) { XSetForeground(disp,gc,pricolor[i?(PCX_R|PCX_G|PCX_B):0].pixel); XFillRectangle(disp,colorswin_bw,gc,margin+(i*(margin+COLOR_XSIZE+margin+iborderwidth)),y,COLOR_XSIZE,h); } } if ( (selcolor >= 0) && intervals_overlap(x,w,selcolor*(margin+COLOR_XSIZE+margin+iborderwidth),margin+COLOR_XSIZE+margin) ) { XSetForeground(disp,gc,fgcolor.pixel); draw_colors_margin_box(selcolor,colorswin_bw); } } static void expose_bwcolorwin(UNUSED_ARG(int x), UNUSED_ARG(int y), UNUSED_ARG(int w), UNUSED_ARG(int h), int count) { int tx; int ty; const char *s; int sl; XCharStruct ext; if (count != 0) return; s = cur_colorp ? "Color" : "B/W"; sl = strlen(s); XTextExtents(font,s,sl,XTE_JUNK,&ext); tx = ((layout_w3 - ext.width) / 2) - ext.lbearing; ty = ((layout_h1 - baselineskip) / 2) + font->ascent; XSetForeground(disp,gc,fgcolor.pixel); XDrawString(disp,bwcolorwin,gc,tx,ty,s,sl); } static void expose_slider_parent_win(Window win, int x, int y, int w, int h, UNUSED_ARG(int count)) { int i; int lx; int any; any = 0; for (i=0;i<2;i++) { lx = (i*(iborderwidth+margin)) + ((i+1)*(SLIDER_WIDTH+margin)); if (intervals_overlap(x,w,lx,iborderwidth)) { if (! any) XSetForeground(disp,gc,ibdcolor.pixel); XFillRectangle(disp,win,gc,lx,y,iborderwidth,h); any ++; } } } static void redraw_picpix(int x, int y) { XSetForeground( disp, gc, cur_colorp ? colpix[cur_image[x][y]] : pricolor[cur_image[x][y]?(PCX_R|PCX_G|PCX_B):0].pixel ); XFillRectangle(disp,picwin,gc,(x*mag)+maggridwidth,(y*mag)+maggridwidth,mag-maggridwidth,mag-maggridwidth); } static void expose_picwin(int x, int y, int w, int h, UNUSED_ARG(int count)) { int x0; int y0; int x1; int y1; int cvec[NCOL]; int c; x0 = (x - maggridwidth) / mag; y0 = (y - maggridwidth) / mag; x1 = ((x+w-1-maggridwidth+mag)/mag) - 1; y1 = ((y+h-1-maggridwidth+mag)/mag) - 1; if (x0 < 0) x0 = 0; if (y0 < 0) y0 = 0; if (x1 >= CARD_XSIZE) x1 = CARD_XSIZE - 1; if (y1 >= CARD_YSIZE) y1 = CARD_YSIZE - 1; for (c=0;cascent,&card_names[c][0],2); XSetForeground(disp,gc,fgcolor.pixel); } else { XDrawString(disp,cardswin,gc,(c_cw*j)+margin-c_n_sz[c].lbearing,(c_ch*i)+margin+font->ascent,&card_names[c][0],2); } } } } } if (intervals_overlap(x,w,0,c_cw-iborderwidth)) { yy = c_ch * 13; for (i=0;iascent,&card_names[CARD_DECK+i][0],2); XSetForeground(disp,gc,fgcolor.pixel); } else { XDrawString(disp,cardswin,gc,margin-c_n_sz[CARD_DECK+i].lbearing,yy+(c_ch*i)+margin+font->ascent,&card_names[CARD_DECK+i][0],2); } } } } xx = (c_cw * 6) + iborderwidth; if (intervals_overlap(x,w,xx,c_bw-iborderwidth)) { for (i=0;iascent,&back_names[i][0],3); XSetForeground(disp,gc,fgcolor.pixel); } else { XDrawString(disp,cardswin,gc,xx+margin-b_n_sz[i].lbearing,(c_bh*i)+margin+font->ascent,&back_names[i][0],3); } } } } } static void expose_cardswin_for(int type, int num) { int x; int y; int w; int h; switch (type) { case CT_NONE: return; break; case CT_CARD: if (num >= CARD_DECK) { x = 0; y = ((num - CARD_DECK) + 13) * c_ch; } else { x = suitinv[CARD_SUIT(num)] * c_cw; y = valueinv[CARD_VALUE(num)] * c_ch; } w = c_cw - iborderwidth; h = c_ch - iborderwidth; break; case CT_BACK: x = 6 * c_cw; y = num * c_bh; w = c_bw + iborderwidth; h = c_bh; break; default: abort(); break; } XClearArea(disp,cardswin,x,y,w,h,False); expose_cardswin(x,y,w,h,0); } static void expose_demowin(int x, int y, int w, int h, UNUSED_ARG(int count)) { int x0; int x1; int y0; int y1; int c; int cvec[NCOL]; if ( overlapping_interval(0,CARD_XSIZE,x,w,&x,&w) && overlapping_interval(0,CARD_YSIZE,y,h,&y,&h) ) { x0 = x; y0 = y; x1 = x + w - 1; y1 = y + h - 1; for (c=0;cwin,0,0,0,0,False); XSetForeground(disp,gc,fgcolor.pixel); XDrawString(disp,(Drawable)b->win,gc,((b->winw-b->textw)/2)-b->textsize.lbearing,font->ascent,b->label,strlen(b->label)); if (b->pressed ^ b->highlit) XFillRectangle(disp,(Drawable)b->win,xorgc,0,0,b->winw,baselineskip); } static void doexpose(Window win, int x, int y, int w, int h, int count) { SLIDER *s; BUTTON *b; if (win == sliderlabelwin) { expose_sliderlabelwin(x,y,w,h,count); } if (win == colorswin_c) { expose_colorswin_c(x,y,w,h,count); } if (win == colorswin_bw) { expose_colorswin_bw(x,y,w,h,count); } if (win == bwcolorwin) { expose_bwcolorwin(x,y,w,h,count); } if ( (win == slider_rgb_win) || (win == slider_hsv_win) ) { expose_slider_parent_win(win,x,y,w,h,count); } if (win == picwin) { expose_picwin(x,y,w,h,count); } if (win == cardswin) { expose_cardswin(x,y,w,h,count); } if (win == demowin) { expose_demowin(x,y,w,h,count); } for (s=allsliders;s;s=s->link) { if (win == s->win) { (*s->expose)(s,x,y,w,h,count); } } if (count == 0) { for (b=allbuttons;b;b=b->flink) { if (win == b->win) { redrawbutton(b); } } } } static void set_pic_pixel(int x, int y) { static void set_it(int x, int y) { cur_image[x][y] = selcolor; redraw_picpix(x,y); expose_demowin(x,y,1,1,0); } if (selcolor < 0) return; set_it(x,y); if (rot180button.highlit) set_it(CARD_XSIZE-1-x,CARD_YSIZE-1-y); } static void store_color_image(unsigned char (*cp)[CARD_YSIZE][CARD_XSIZE]) { int x; int y; for (y=0;y>3] &=~ (0x80 >> (x&7)); } else { (*cp)[y][x>>3] |= 0x80 >> (x&7); } } } } static void fetch_color_image(unsigned char (*cp)[CARD_YSIZE][CARD_XSIZE]) { int x; int y; for (y=0;y>3] & (0x80>>(x&7))) ? 0 : 1; } } } static void store_cur_image(void) { switch (cur_type) { case CT_NONE: break; case CT_CARD: if (cur_colorp) store_color_image(&card_cards_color[cur_num]); else store_bw_image(&card_cards_bw[cur_num]); break; case CT_BACK: if (cur_colorp) store_color_image(&card_backs_color[cur_num]); else store_bw_image(&card_backs_bw[cur_num]); break; default: abort(); break; } } static void fetch_cur_image(void) { switch (cur_type) { case CT_NONE: bzero(&cur_image,sizeof(cur_image)); break; case CT_CARD: if (cur_colorp) fetch_color_image(&card_cards_color[cur_num]); else fetch_bw_image(&card_cards_bw[cur_num]); break; case CT_BACK: if (cur_colorp) fetch_color_image(&card_backs_color[cur_num]); else fetch_bw_image(&card_backs_bw[cur_num]); break; default: abort(); break; } } static void full_image_redraw(void) { expose_picwin(0,0,layout_w2,layout_h2,0); expose_demowin(0,0,CARD_XSIZE,CARD_YSIZE,0); } static void set_button_highlit(BUTTON *b, int hl) { hl = ! ! hl; if (hl == b->highlit) return; b->highlit = hl; if (! b->disabled) redrawbutton(b); } static void sel_b_expose(int clearfirst) { int x1; int y1; int x2; int y2; if (sel_a_x < sel_b_x) { x1 = sel_a_x * mag; x2 = sel_b_x * mag; } else { x1 = sel_b_x * mag; x2 = sel_a_x * mag; } if (sel_a_y < sel_b_y) { y1 = sel_a_y * mag; y2 = sel_b_y * mag; } else { y1 = sel_b_y * mag; y2 = sel_a_y * mag; } if (clearfirst) { XClearArea(disp,picwin, x1-1, y1-1, (x2-x1)+mag+maggridwidth+2, maggridwidth+2, False ); XClearArea(disp,picwin, x1-1, y1+maggridwidth+1, maggridwidth+2, (y2-y1)+mag-maggridwidth-2, False ); XClearArea(disp,picwin, x2+mag-1, y1+maggridwidth+1, maggridwidth+2, (y2-y1)+mag-maggridwidth-2, False ); XClearArea(disp,picwin, x1-1, y2+mag-1, (x2-x1)+mag+maggridwidth+2, maggridwidth+2, False ); } expose_picwin( x1-1, y1-1, (x2-x1)+mag+maggridwidth+2, maggridwidth+2, 3 ); expose_picwin( x1-1, y1+maggridwidth+1, maggridwidth+2, (y2-y1)+mag-maggridwidth-2, 2 ); expose_picwin( x2+mag-1, y1+maggridwidth+1, maggridwidth+2, (y2-y1)+mag-maggridwidth-2, 1 ); expose_picwin( x1-1, y2+mag-1, (x2-x1)+mag+maggridwidth+2, maggridwidth+2, 0 ); } static void sel_b_draw(void) { sel_b_expose(0); } static void sel_b_erase(void) { sel_b_expose(1); } static void dbg_printf(int, const char *, ...) __attribute__ ((__format__(__printf__,2,3))); static void dbg_printf(int bit, const char *fmt, ...) { va_list ap; static FILE *f = 0; if (bit && !(debugging & bit)) return; if (f == 0) { if (debugoutfn) f = fopen(debugoutfn,"w"); if (f == 0) f = stderr; } va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); fflush(f); } static AREA *new_area(void) { AREA *a; a = malloc(sizeof(AREA)); a->bands = 0; dbg_printf(DEBUG_AREA,"new_area() -> %p\n",(void *)a); return(a); } static AREA *new_rect_area(int x1, int y1, int x2, int y2) { AREA *a; AREABAND *b; if ((x2 < x1) || (y2 < y1)) abort(); a = malloc(sizeof(AREA)); if ((x2 == x1) || (y2 == y1)) { a->bands = 0; } else { b = malloc(sizeof(AREABAND)); a->bands = b; b->link = 0; b->y.s = y1; b->y.e = y2; b->nrects = 1; b->rects = malloc(sizeof(INTERVAL)); b->rects[0].s = x1; b->rects[0].e = x2; } dbg_printf(DEBUG_AREA,"new_rect_area(%d,%d,%d,%d) -> %p\n",x1,y1,x2,y2,(void *)a); return(a); } #if 0 static AREA *area_copy(AREA *old) { AREABAND *oldb; AREA *a; AREABAND *b; AREABAND **tail; a = malloc(sizeof(AREA)); tail = &a->bands; for (oldb=old->bands;oldb;oldb=oldb->link) { b = malloc(sizeof(AREABAND)); *b = *oldb; b->rects = malloc(b->nrects*sizeof(INTERVAL)); bcopy(oldb->rects,b->rects,b->nrects*sizeof(INTERVAL)); *tail = b; tail = &b->link; } *tail = 0; return(a); } #endif static AREABAND *band_free(AREABAND *b) { AREABAND *rv; rv = b->link; free(b->rects); free(b); return(rv); } static void free_area(AREA *a) { AREABAND *b; dbg_printf(DEBUG_AREA,"free_area(%p)\n",(void *)a); for (b=a->bands;b;b=band_free(b)) ; free(a); } static AREABAND *band_split(AREABAND *b, int y) { AREABAND *b2; b2 = malloc(sizeof(AREABAND)); b2->link = b->link; b->link = b2; b2->nrects = b->nrects; b2->rects = malloc(b2->nrects*sizeof(INTERVAL)); bcopy(b->rects,b2->rects,b2->nrects*sizeof(INTERVAL)); b2->y.s = y; b2->y.e = b->y.e; b->y.e = y; return(b); } static void ensure_arith_nrects(int n) { if (arithnrects < n) { free(arithrects); arithnrects = n; arithrects = malloc(n*sizeof(INTERVAL)); } } #define R1 b1->rects[i1] #define R2 b2->rects[i2] static AREABAND *band_add(AREABAND *b1, AREABAND *b2) { int n; int i1; int i2; AREABAND *rv; ensure_arith_nrects(b1->nrects+b2->nrects); i1 = 0; i2 = 0; n = 0; while ((i1 < b1->nrects) && (i2 < b2->nrects)) { dbg_printf(DEBUG_BAND,"band_add: b1=[%d..%d], b2=[%d..%d]\n",R1.s,R1.e,R2.s,R2.e); if (R2.s < R1.s) { AREABAND *tb; int ti; dbg_printf(DEBUG_BAND,"band_add: swapping\n"); tb = b1; b1 = b2; b2 = tb; ti = i1; i1 = i2; i2 = ti; } else if (R1.e < R2.s) { dbg_printf(DEBUG_BAND,"band_add: including b1\n"); arithrects[n] = R1; n ++; i1 ++; } else if (R1.e <= R2.e) { dbg_printf(DEBUG_BAND,"band_add: merging, moving b2 left edge\n"); R2.s = R1.s; i1 ++; } else { dbg_printf(DEBUG_BAND,"band_add: dropping covered b2\n"); i2 ++; } } if (i1 < b1->nrects) { dbg_printf(DEBUG_BAND,"band_add: appending remaining b1\n"); bcopy(b1->rects+i1,arithrects+n,(b1->nrects-i1)*sizeof(INTERVAL)); n += b1->nrects - i1; } if (i2 < b2->nrects) { dbg_printf(DEBUG_BAND,"band_add: appending remaining b2\n"); bcopy(b2->rects+i2,arithrects+n,(b2->nrects-i2)*sizeof(INTERVAL)); n += b2->nrects - i2; } rv = malloc(sizeof(AREABAND)); rv->y = b1->y; rv->nrects = n; rv->rects = malloc(n*sizeof(INTERVAL)); bcopy(arithrects,rv->rects,n*sizeof(INTERVAL)); return(rv); } static AREABAND *band_sub(AREABAND *b1, AREABAND *b2) { int n; int i1; int i2; AREABAND *rv; ensure_arith_nrects(b1->nrects+b2->nrects); i1 = 0; i2 = 0; n = 0; while ((i1 < b1->nrects) && (i2 < b2->nrects)) { dbg_printf(DEBUG_BAND,"band_sub: b1=[%d..%d], b2=[%d..%d]\n",R1.s,R1.e,R2.s,R2.e); if (R1.s < R2.s) { if (R1.e < R2.s) { dbg_printf(DEBUG_BAND,"band_sub: including b1\n"); arithrects[n] = R1; n ++; i1 ++; } else if (R1.e <= R2.e) { dbg_printf(DEBUG_BAND,"band_sub: including b1 up to b2\n"); arithrects[n].s = R1.s; arithrects[n].e = R2.s; n ++; i1 ++; } else { dbg_printf(DEBUG_BAND,"band_sub: including b1 fragment, moving left edge\n"); arithrects[n].s = R1.s; arithrects[n].e = R2.s; n ++; R1.s = R2.e; i2 ++; } } else { if (R2.e <= R1.s) { dbg_printf(DEBUG_BAND,"band_sub: dropping b2\n"); i2 ++; } else if (R2.e >= R1.e) { dbg_printf(DEBUG_BAND,"band_sub: dropping covered b1\n"); i1 ++; } else { dbg_printf(DEBUG_BAND,"band_sub: moving b1 left edge, dropping b2\n"); R1.s = R2.e; i2 ++; } } } if (i1 < b1->nrects) { dbg_printf(DEBUG_BAND,"band_sub: appending remaining b1\n"); bcopy(b1->rects+i1,arithrects+n,(b1->nrects-i1)*sizeof(INTERVAL)); n += b1->nrects - i1; } rv = malloc(sizeof(AREABAND)); rv->y = b1->y; rv->nrects = n; if (n) { rv->rects = malloc(n*sizeof(INTERVAL)); bcopy(arithrects,rv->rects,n*sizeof(INTERVAL)); } else { rv->rects = 0; } return(rv); } static AREABAND *band_xor(AREABAND *b1, AREABAND *b2) { int n; int i1; int i2; AREABAND *rv; ensure_arith_nrects(b1->nrects+b2->nrects); i1 = 0; i2 = 0; n = 0; while ((i1 < b1->nrects) && (i2 < b2->nrects)) { dbg_printf(DEBUG_BAND,"band_xor: b1=[%d..%d], b2=[%d..%d]\n",R1.s,R1.e,R2.s,R2.e); if (R1.s < R2.s) { if (R1.e < R2.s) { dbg_printf(DEBUG_BAND,"band_xor: including b1\n"); arithrects[n] = R1; n ++; i1 ++; } else if (R1.e == R2.s) { dbg_printf(DEBUG_BAND,"band_xor: merging exact neighbors\n"); R2.s = R1.s; i1 ++; } else { dbg_printf(DEBUG_BAND,"band_xor: including partial b1...\n"); arithrects[n].s = R1.s; arithrects[n].e = R2.s; n ++; if (R1.e < R2.e) { dbg_printf(DEBUG_BAND,"band_xor: ...moving b2 left edge\n"); R2.s = R1.e; i1 ++; } else if (R1.e == R2.e) { dbg_printf(DEBUG_BAND,"band_xor: ...skipping rest of b2\n"); i1 ++; i2 ++; } else { dbg_printf(DEBUG_BAND,"band_xor: ...removing b2 from b1\n"); R1.s = R2.e; i2 ++; } } } else if (R1.s == R2.s) { if (R1.e < R2.e) { dbg_printf(DEBUG_BAND,"band_xor: removing b1 from b2\n"); R2.s = R1.e; i1 ++; } else if (R1.e == R2.e) { dbg_printf(DEBUG_BAND,"band_xor: cancelling both\n"); i1 ++; i2 ++; } else { dbg_printf(DEBUG_BAND,"band_xor: removing b2 from b1\n"); R1.s = R2.e; i2 ++; } } else { AREABAND *bt; int it; dbg_printf(DEBUG_BAND,"band_xor: swapping\n"); bt = b1; b1 = b2; b2 = bt; it = i1; i1 = i2; i2 = it; } } if (i1 < b1->nrects) { dbg_printf(DEBUG_BAND,"band_xor: appending remaining band 1\n"); bcopy(b1->rects+i1,arithrects+n,(b1->nrects-i1)*sizeof(INTERVAL)); n += b1->nrects - i1; } if (i2 < b2->nrects) { dbg_printf(DEBUG_BAND,"band_xor: appending remaining band 2\n"); bcopy(b2->rects+i2,arithrects+n,(b2->nrects-i2)*sizeof(INTERVAL)); n += b2->nrects - i2; } rv = malloc(sizeof(AREABAND)); rv->y = b1->y; rv->nrects = n; if (n) { rv->rects = malloc(n*sizeof(INTERVAL)); bcopy(arithrects,rv->rects,n*sizeof(INTERVAL)); } else { rv->rects = 0; } return(rv); } #undef R1 #undef R2 static void collapse_adjacent_bands(AREABAND *b) { int i; AREABAND *b2; while (b && (b2=b->link)) { if ( (b->y.e == b2->y.s) && (b->nrects != b2->nrects) ) { for (i=b->nrects-1;i>=0;i--) { if ( (b->rects[i].s != b2->rects[i].s) || (b->rects[i].e != b2->rects[i].e) ) break; } if (i < 0) { dbg_printf(DEBUG_BAND,"collapse_adjacent_bands: merging [%d..%d..%d]\n",b->y.s,b->y.e,b2->y.e); b->y.e = b2->y.e; b->link = band_free(b2); continue; } } b = b->link; } } static AREABAND *band_include(AREABAND *b, AREABAND ***tp) { dbg_printf(DEBUG_BAND,"band_include\n"); **tp = b; *tp = &b->link; return(b->link); } static AREABAND *band_omit(AREABAND *b, UNUSED_ARG(AREABAND ***tp)) { dbg_printf(DEBUG_BAND,"band_omit\n"); return(band_free(b)); } static void area_arith( AREA *into, AREA *arg, AREABAND *(*bandfn)(AREABAND *, AREABAND *), AREABAND *(*intoband)(AREABAND *, AREABAND ***), AREABAND *(*argband)(AREABAND *, AREABAND ***) ) { AREABAND *b1; AREABAND *b2; AREABAND *b; AREABAND *root; AREABAND **tail; b1 = into->bands; b2 = arg->bands; into->bands = 0; arg->bands = 0; root = 0; tail = &root; while (b1 && b2) { dbg_printf(DEBUG_ARITH,"arith: b1 [%d..%d] b2 [%d..%d]\n",b1->y.s,b1->y.e,b2->y.s,b2->y.e); if (b1->y.s == b2->y.s) { if (b1->y.e == b2->y.e) { dbg_printf(DEBUG_ARITH,"arith: calling bandfn\n"); b = (*bandfn)(b1,b2); if (b->nrects == 0) { dbg_printf(DEBUG_ARITH,"arith: no rects, freeing band\n"); band_free(b); } else { dbg_printf(DEBUG_ARITH,"arith: appending resulting band\n"); *tail = b; tail = &b->link; } b1 = band_free(b1); b2 = band_free(b2); } else if (b1->y.e < b2->y.e) { dbg_printf(DEBUG_ARITH,"arith: splitting b2 (1)\n"); b2 = band_split(b2,b1->y.e); } else { dbg_printf(DEBUG_ARITH,"arith: splitting b1 (1)\n"); b1 = band_split(b1,b2->y.e); } } else if (b1->y.s < b2->y.s) { if (b1->y.e <= b2->y.s) { dbg_printf(DEBUG_ARITH,"arith: calling intoband\n"); b1 = (*intoband)(b1,&tail); } else { dbg_printf(DEBUG_ARITH,"arith: splitting b1 (2)\n"); b1 = band_split(b1,b2->y.s); } } else { if (b2->y.e <= b1->y.s) { dbg_printf(DEBUG_ARITH,"arith: calling argband\n"); b2 = (*argband)(b2,&tail); } else { dbg_printf(DEBUG_ARITH,"arith: splitting b2 (2)\n"); b2 = band_split(b2,b1->y.s); } } } while (b1) { dbg_printf(DEBUG_ARITH,"arith: cleanup intoband\n"); b1 = (*intoband)(b1,&tail); } while (b2) { dbg_printf(DEBUG_ARITH,"arith: cleanup argband\n"); b2 = (*argband)(b2,&tail); } *tail = 0; collapse_adjacent_bands(root); into->bands = root; dbg_printf(DEBUG_ARITH,"arith: returning\n"); } static int walk_area(AREA *a, int (*fn)(int, int, int, int, void *), void *arg) { AREABAND *b; int i; int rv; rv = 0; for (b=a->bands;b;b=b->link) { for (i=0;inrects;i++) { rv = (*fn)(b->rects[i].s,b->y.s,b->rects[i].e,b->y.e,arg); if (rv) return(rv); } } return(0); } static void dump_area(AREA *a) { AREABAND *b; int i; dbg_printf(0,"%p=",(void *)a); if (a->bands == 0) { dbg_printf(0,"[nil]"); return; } for (b=a->bands;b;b=b->link) { dbg_printf(0,"<(%d..%d)×",b->y.s,b->y.e); for (i=0;inrects;i++) { dbg_printf(0,"(%d..%d)",b->rects[i].s,b->rects[i].e); } dbg_printf(0,">"); if (b->link) dbg_printf(0,","); } } static void area_union(AREA *into, AREA *arg) { if (DEBUG(AREA)) { dbg_printf(0,"area_union: "); dump_area(into); dbg_printf(0," + "); dump_area(arg); dbg_printf(0,"\n"); } area_arith(into,arg,band_add,band_include,band_include); if (DEBUG(AREA)) { dbg_printf(0,"area_union: = "); dump_area(into); dbg_printf(0,"\n"); } } static void area_sub(AREA *into, AREA *arg) { if (DEBUG(AREA)) { dbg_printf(0,"area_sub: "); dump_area(into); dbg_printf(0," - "); dump_area(arg); dbg_printf(0,"\n"); } area_arith(into,arg,band_sub,band_include,band_omit); if (DEBUG(AREA)) { dbg_printf(0,"area_sub: = "); dump_area(into); dbg_printf(0,"\n"); } } static void area_xor(AREA *into, AREA *arg) { if (DEBUG(AREA)) { dbg_printf(0,"area_xor: "); dump_area(into); dbg_printf(0," ^ "); dump_area(arg); dbg_printf(0,"\n"); } area_arith(into,arg,band_xor,band_include,band_include); if (DEBUG(AREA)) { dbg_printf(0,"area_xor: = "); dump_area(into); dbg_printf(0,"\n"); } } static int clear_picwin_area(int x1, int y1, int x2, int y2, UNUSED_ARG(void *arg)) { if ((x2 <= x1) || (y2 <= y1)) abort(); XClearArea(disp,picwin,x1,y1,x2-x1,y2-y1,False); return(0); } static int expose_picwin_area(int x1, int y1, int x2, int y2, UNUSED_ARG(void *arg)) { if ((x2 <= x1) || (y2 <= y1)) abort(); expose_picwin(x1,y1,x2-x1,y2-y1,0); return(0); } static AREA *rectangle_difference( int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2 ) { AREA *a; AREA *t; a = new_area(); t = new_rect_area(ax1,ay1,ax2,ay2); area_union(a,t); free_area(t); t = new_rect_area(bx1,by1,bx2,by2); area_sub(a,t); free_area(t); return(a); } static void sel_b_animate(int x, int y) { int x1; int y1; int x2; int y2; int nx1; int ny1; int nx2; int ny2; AREA *new; AREA *exp; AREA *t; if ((x == sel_b_x) && (y == sel_b_y)) return; if (sel_a_x < sel_b_x) { x1 = sel_a_x * mag; x2 = sel_b_x * mag; } else { x1 = sel_b_x * mag; x2 = sel_a_x * mag; } if (sel_a_y < sel_b_y) { y1 = sel_a_y * mag; y2 = sel_b_y * mag; } else { y1 = sel_b_y * mag; y2 = sel_a_y * mag; } if (sel_a_x < x) { nx1 = sel_a_x * mag; nx2 = x * mag; } else { nx1 = x * mag; nx2 = sel_a_x * mag; } if (sel_a_y < y) { ny1 = sel_a_y * mag; ny2 = y * mag; } else { ny1 = y * mag; ny2 = sel_a_y * mag; } dbg_printf(DEBUG_AREA,"********\na=(%d,%d), old b=(%d,%d), new b=(%d,%d)\n", sel_a_x, sel_a_y, sel_b_x, sel_b_y, x, y); exp = rectangle_difference( x1-1, y1-1, x2+mag+maggridwidth+1, y2+mag+maggridwidth+1, x1+maggridwidth+1, y1+maggridwidth+1, x2+mag-1, y2+mag-1 ); new = rectangle_difference( nx1-1, ny1-1, nx2+mag+maggridwidth+1 ,ny2+mag+maggridwidth+1, nx1+maggridwidth+1, ny1+maggridwidth+1, nx2+mag-1, ny2+mag-1 ); area_xor(exp,new); free_area(new); if ( ((x1 != nx1) && (y1 == ny1)) || ((x1 == nx1) && (y1 != ny1)) ) { t = new_rect_area(x1-1,y1-1,x1+maggridwidth+1,y1+maggridwidth+1); area_union(exp,t); free_area(t); } if ( ((x2 != nx2) && (y1 == ny1)) || ((x2 == nx2) && (y1 != ny1)) ) { t = new_rect_area(x2+mag-1,y1-1,x2+mag+maggridwidth+1,y1+maggridwidth+1); area_union(exp,t); free_area(t); } if ( ((x1 != nx1) && (y2 == ny2)) || ((x1 == nx1) && (y2 != ny2)) ) { t = new_rect_area(x1-1,y2+mag-1,x1+maggridwidth+1,y2+mag+maggridwidth+1); area_union(exp,t); free_area(t); } if ( ((x2 != nx2) && (y2 == ny2)) || ((x2 == nx2) && (y2 != ny2)) ) { t = new_rect_area(x2+mag-1,y2+mag-1,x2+mag+maggridwidth+1,y2+mag+maggridwidth+1); area_union(exp,t); free_area(t); } if (DEBUG(AREA)) { dbg_printf(0,"final area: "); dump_area(exp); dbg_printf(0,"\n"); } sel_b_x = x; sel_b_y = y; walk_area(exp,clear_picwin_area,0); walk_area(exp,expose_picwin_area,0); free_area(exp); } static void get_all_picwin_motion(int allp) { XSelectInput(disp,picwin, ButtonPressMask | ButtonReleaseMask | ExposureMask | (allp?PointerMotionMask:ButtonMotionMask) ); } static void paste_draw(void) { sel_b_expose(0); } static void paste_erase(void) { sel_b_expose(1); } static void set_mode(int newmode) { int oldmode; if (newmode == mode) return; oldmode = mode; mode = newmode; switch (oldmode) { case MODE_SEL_A: set_button_highlit(&selbutton,0); break; case MODE_SEL_B: set_button_highlit(&selbutton,0); sel_b_erase(); break; case MODE_PASTE: set_button_highlit(&pastebutton,0); get_all_picwin_motion(0); paste_erase(); break; } switch (mode) { case MODE_SEL_A: set_button_highlit(&selbutton,1); break; case MODE_SEL_B: set_button_highlit(&selbutton,1); sel_b_draw(); break; case MODE_PASTE: set_button_highlit(&pastebutton,1); get_all_picwin_motion(1); sel_a_x = 0; sel_a_y = 0; sel_b_x = sel_w; sel_b_y = sel_h; paste_draw(); break; } } static void break_area_select(void) { if (mode == MODE_SEL_B) set_mode(MODE_EDIT); } static void set_colorp(int cp) { if (cp == cur_colorp) return; break_area_select(); switch (cur_colorp) { case 0: XUnmapWindow(disp,colorswin_bw); bw_selcolor = selcolor; break; case 1: XUnmapWindow(disp,colorswin_c); XUnmapWindow(disp,sliderwin); c_selcolor = selcolor; break; } switch (cp) { case 0: XMapWindow(disp,colorswin_bw); selcolor = bw_selcolor; break; case 1: XMapWindow(disp,colorswin_c); XMapWindow(disp,sliderwin); selcolor = c_selcolor; break; } store_cur_image(); cur_colorp = cp; fetch_cur_image(); full_image_redraw(); XClearArea(disp,bwcolorwin,0,0,0,0,False); expose_bwcolorwin(0,0,0,0,0); XClearArea(disp,sliderlabelwin,0,0,0,0,False); expose_sliderlabelwin(0,0,0,0,0); } static void set_button_enable(BUTTON *b, int enb) { int dsb; dsb = ! enb; if (dsb == b->disabled) return; b->disabled = dsb; if (dsb) XUnmapWindow(disp,b->win); else XMapWindow(disp,b->win); } static void select_card(int type, int num) { int prev_type; int prev_num; if ((type == cur_type) && (num == cur_num)) return; switch (cur_type) { case CT_CARD: auto_180_card[cur_num] = rot180button.highlit; break; case CT_BACK: auto_180_back[cur_num] = rot180button.highlit; break; } break_area_select(); prev_type = cur_type; prev_num = cur_num; store_cur_image(); cur_type = type; cur_num = num; fetch_cur_image(); full_image_redraw(); expose_cardswin_for(prev_type,prev_num); expose_cardswin_for(cur_type,cur_num); set_button_enable(&delbackbutton,type==CT_BACK); switch (cur_type) { case CT_CARD: set_button_highlit(&rot180button,auto_180_card[cur_num]); break; case CT_BACK: set_button_highlit(&rot180button,auto_180_back[cur_num]); break; } } static void pick_card(int x, int y) { if (x < c_cw*6) { if (((x+c_cw-margin)%c_cw) >= c_cw-margin-margin-iborderwidth) return; if (((y+c_ch-margin)%c_ch) >= c_ch-margin-margin-iborderwidth) return; x = (x - margin) / c_cw; y = (y - margin) / c_ch; if ((x >= 0) && (x < 6) && (y >= 0) && (y < 13)) { select_card(CT_CARD,CARD_MAKE(suits[x],values[y])); } else if ((x == 0) && (y >= 13) && (y < 13+CARD_XDECK-CARD_DECK)) { select_card(CT_CARD,CARD_DECK+y-13); } } else { x -= (c_cw*6) + iborderwidth + margin; if (x < 0) return; if (x > c_bw-margin-margin-iborderwidth) return; if (((y+c_bh-margin)%c_bh) >= c_bh-margin-margin-iborderwidth) return; y = (y - margin) / c_bh; if ((y < 0) || (y >= card_nbacks)) return; select_card(CT_BACK,y); } } static void buttonbutton(BUTTON *b, UNUSED_ARG(int bno), int downp) { if (b->disabled) return; if (downp) { b->pressed = 1; redrawbutton(b); } else { if (b->pressed) { b->pressed = 0; redrawbutton(b); (*b->clickfn)(b); } } } static void do_select(int x1, int y1, int x2, int y2) { int x; int y; if (x1 > x2) { x = x1; x1 = x2; x2 = x; } if (y1 > y2) { y = y1; y1 = y2; y2 = y; } sel_w = x2 + 1 - x1; sel_h = y2 + 1 - y1; sel_colorp = cur_colorp; for (x=0;x= CARD_XSIZE) x = CARD_XSIZE - sel_w; if (y < 0) y = 0; else if (y+sel_h >= CARD_YSIZE) y = CARD_YSIZE - sel_h; sel_a_x = x; sel_a_y = y; #define RX(x) (CARD_XSIZE-1-(x)) #define RY(y) (CARD_YSIZE-1-(y)) for (x=0;x= CARD_XSIZE) ax = CARD_XSIZE - sel_w; if (ay < 0) ay = 0; else if (ay+sel_h >= CARD_YSIZE) ay = CARD_YSIZE - sel_h; if ((ax == sel_a_x) && (ay == sel_a_y)) return; sel_a_x = ax; sel_a_y = ay; sel_b_x = sel_a_x + sel_w - 1; sel_b_y = sel_a_y + sel_h - 1; ax *= mag; ay *= mag; exp = rectangle_difference( oax-1, oay-1, oax+sw+maggridwidth+1, oay+sh+maggridwidth+1, oax+maggridwidth+1, oay+maggridwidth+1, oax+sw-1, oay+sh-1 ); t = rectangle_difference( ax-1, ay-1, ax+sw+maggridwidth+1, ay+sh+maggridwidth+1, ax+maggridwidth+1, ay+maggridwidth+1, ax+sw-1, ay+sh-1 ); /* Reducing overexposure in all cases of overlap is complicated; handling moving it so one edge's new position overlaps the old position of the other edge parallel to it, for example. We wimp out and expose the whole thing all the time. */ area_union(exp,t); free_area(t); walk_area(exp,clear_picwin_area,0); walk_area(exp,expose_picwin_area,0); free_area(exp); } static void picwin_button(int x, int y, int button, int downp) { switch (mode) { case MODE_EDIT: switch (button) { case Button1: set_pic_pixel(x,y); break; case Button2: select_color(cur_image[x][y]); break; } break; case MODE_SEL_A: if (downp) { if (button == Button1) { sel_a_x = x; sel_a_y = y; sel_b_x = x + sel_w - 1; sel_b_y = y + sel_h - 1; set_mode(MODE_SEL_B); } else { set_mode(MODE_EDIT); } } break; case MODE_SEL_B: if (button != Button1) { set_mode(MODE_EDIT); break; } if (DEBUG(SELECT)) { if (downp) { sel_b_animate(x,y); } } else { if (! downp) { do_select(sel_a_x,sel_a_y,x,y); set_mode(MODE_EDIT); } } break; case MODE_PASTE: if (button != Button1) { set_mode(MODE_EDIT); break; } if (downp) { do_paste(x,y); set_mode(MODE_WAIT); } break; case MODE_WAIT: if (! downp) set_mode(MODE_EDIT); break; } } static void picwin_motion(int x, int y) { switch (mode) { case MODE_EDIT: set_pic_pixel(x,y); break; case MODE_SEL_A: /* "can't happen" */ picwin_button(x,y,Button1,1); break; case MODE_SEL_B: if (! DEBUG(SELECT)) sel_b_animate(x,y); break; case MODE_PASTE: paste_animate(x,y); break; } } static void dopress(Window win, int x, int y, UNUSED_ARG(unsigned int state), unsigned int button, int downp) { SLIDER *s; BUTTON *b; if (win == picwin) { picwin_button((x-maggridwidth)/mag,(y-maggridwidth)/mag,button,downp); } if (win == colorswin_c) { if ((x % (COLOR_XSIZE+margin+iborderwidth+margin)) < COLOR_XSIZE) { select_color(x/(COLOR_XSIZE+margin+iborderwidth+margin)); } } if (win == colorswin_bw) { if ((x % (COLOR_XSIZE+margin+iborderwidth+margin)) < COLOR_XSIZE) { select_color(x/(COLOR_XSIZE+margin+iborderwidth+margin)); } } if (win == sliderlabelwin) { set_slider_style((slider_style%(SLST__N-1))+1); } if (win == bwcolorwin) { set_colorp(!cur_colorp); } if (win == cardswin) { pick_card(x,y); } for (s=allsliders;s;s=s->link) { if (win == s->win) { (*s->set)(s,y); } } for (b=allbuttons;b;b=b->flink) { if (win == b->win) { buttonbutton(b,button,downp); } } } static void domotion(Window win, int x, int y, UNUSED_ARG(unsigned int state)) { SLIDER *s; if (win == picwin) { picwin_motion((x-maggridwidth)/mag,(y-maggridwidth)/mag); } for (s=allsliders;s;s=s->link) { if (win == s->win) { (*s->set)(s,y); } } } static void handle_event(XEvent *e) { switch (e->type) { default: break; case Expose: /* XExposeEvent - xexpose */ doexpose( e->xexpose.window, e->xexpose.x, e->xexpose.y, e->xexpose.width, e->xexpose.height, e->xexpose.count ); break; case ButtonPress: /* XButtonPressedEvent - XButtonEvent - xbutton */ dopress( e->xbutton.window, e->xbutton.x, e->xbutton.y, e->xbutton.state, e->xbutton.button, 1 ); break; case ButtonRelease: /* XButtonReleasedEvent - XButtonEvent - xbutton */ dopress( e->xbutton.window, e->xbutton.x, e->xbutton.y, e->xbutton.state, e->xbutton.button, 0 ); break; case MotionNotify: /* XMotionEvent - xmotion */ domotion( e->xbutton.window, e->xbutton.x, e->xbutton.y, e->xbutton.state ); break; } } static void run(void) { XEvent e; while (1) { XNextEvent(disp,&e); handle_event(&e); } } static void initbutton(BUTTON *b) { XTextExtents(font,b->label,strlen(b->label),XTE_JUNK,&b->textsize); b->textw = b->textsize.rbearing - b->textsize.lbearing; b->winw = b->textw; b->pressed = 0; b->disabled = 0; b->highlit = 0; b->flink = allbuttons; b->blink = 0; if (b->flink) b->flink->blink = b; allbuttons = b; } static void setup_buttons(void) { int i; button_w = 0; for (i=0;itextw; } } static int image_is_symmetric(void) { int x; int y; for (x=(CARD_XSIZE/2)-1;x>=0;x--) { for (y=CARD_YSIZE-1;y>=0;y--) { if (cur_image[x][y] != cur_image[CARD_XSIZE-1-x][CARD_YSIZE-1-y]) return(0); } } #if CARD_XSIZE % 2 #define MX ((CARD_XSIZE-1)/2) for (y=(CARD_YSIZE-1)/2;y>=0;y--) { if (cur_image[MX][y] != cur_image[MX][CARD_YSIZE-1-y]) return(0); } #undef MX #endif return(1); } static int both_symmetric(int n) { cur_num = n; cur_colorp = 1; fetch_cur_image(); if (image_is_symmetric()) { cur_colorp = 0; fetch_cur_image(); if (image_is_symmetric()) return(1); } return(0); } static void check_r180(void) { int i; auto_180_back = malloc(card_nbacks*sizeof(*auto_180_back)); cur_type = CT_CARD; for (i=CARD_XDECK-1;i>=0;i--) auto_180_card[i] = both_symmetric(i); cur_type = CT_BACK; for (i=card_nbacks-1;i>=0;i--) auto_180_back[i] = both_symmetric(i); } static void save_click(UNUSED_ARG(BUTTON *b)) { store_cur_image(); store_cmap(); if (card_savedeck(deckfile) < 0) { XBell(disp,0); fprintf(stderr,"%s: can't write %s: %s\n",argv[0],deckfile,strerror(errno)); } } static void new_back_click(UNUSED_ARG(BUTTON *b)) { if (card_nbacks >= CARD_MAX_NBACKS) { XBell(disp,0); return; } card_nbacks ++; card_backs_color = realloc(card_backs_color,card_nbacks*sizeof(*card_backs_color)); card_backs_bw = realloc(card_backs_bw,card_nbacks*sizeof(*card_backs_bw)); auto_180_back = realloc(auto_180_back,card_nbacks*sizeof(*auto_180_back)); bzero(&card_backs_color[card_nbacks-1],sizeof(card_backs_color[0])); memset(&card_backs_bw[card_nbacks-1],0xff,sizeof(card_backs_bw[0])); auto_180_back[card_nbacks-1] = 0; select_card(CT_BACK,card_nbacks-1); #if 0 store_cur_image(); save_type = cur_type; save_num = cur_num; save_colp = cur_colorp; cur_type = CT_BACK; cur_num = card_nbacks - 1; bzero(&cur_image,sizeof(cur_image)); cur_colorp = 0; store_cur_image(); cur_colorp = 1; store_cur_image(); cur_type = save_type; cur_num = save_num; cur_colorp = save_colp; fetch_cur_image(); expose_cardswin_for(CT_BACK,card_nbacks-1); #endif } static void del_back_click(UNUSED_ARG(BUTTON *b)) { if ( (card_nbacks < 2) || (cur_type != CT_BACK) ) { XBell(disp,0); return; } break_area_select(); card_nbacks --; if (cur_num < card_nbacks) { bcopy(card_backs_color+cur_num+1,card_backs_color+cur_num,(card_nbacks-cur_num)*sizeof(card_backs_color[0])); bcopy(card_backs_bw+cur_num+1,card_backs_bw+cur_num,(card_nbacks-cur_num)*sizeof(card_backs_bw[0])); } else { cur_num = card_nbacks - 1; } fetch_cur_image(); full_image_redraw(); XClearArea( disp, cardswin, c_cw*6, card_nbacks*c_bh, c_bw+iborderwidth, c_bh+iborderwidth, False ); expose_cardswin_for(CT_BACK,cur_num); } static void quit_click(UNUSED_ARG(BUTTON *b)) { exit(0); } static void sel_click(UNUSED_ARG(BUTTON *b)) { if (mode == MODE_SEL_A) { set_mode(MODE_EDIT); } else { set_mode(MODE_SEL_A); } } static void paste_click(UNUSED_ARG(BUTTON *b)) { if (mode == MODE_PASTE) { set_mode(MODE_EDIT); } else { set_mode(MODE_PASTE); } } static void rot180_click(UNUSED_ARG(BUTTON *b)) { set_button_highlit(&rot180button,!rot180button.highlit); } int main(int, char **); int main(int ac, char **av) { saveargv(ac,av); handleargs(ac,av); setup_pack(); check_r180(); disp = XOpenDisplay(displayname); if (DEBUG(X)) XSynchronize(disp,True); setup_error(); setup_visual(); scr = XScreenOfDisplay(disp,visinfo.screen); scrwidth = XWidthOfScreen(scr); scrheight = XHeightOfScreen(scr); depth = visinfo.depth; rootwin = XRootWindowOfScreen(scr); setup_db(); setup_cmap(); maybeset(&geometryspec,get_default_value("xeditdeck.geometry","Editor.Geometry")); maybeset(&fontname,get_default_value("xeditdeck.font","Editor.Font")); maybeset(&foreground,get_default_value("xeditdeck.foreground","Editor.Foreground")); maybeset(&background,get_default_value("xeditdeck.background","Editor.Background")); maybeset(&bordercstr,get_default_value("xeditdeck.borderColor","Editor.BorderColor")); maybeset(&ibordercstr,get_default_value("xeditdeck.interiorBorderColor","Editor.BorderColor")); maybeset(&borderwstr,get_default_value("xeditdeck.borderWidth","Editor.BorderWidth")); maybeset(&iborderwstr,get_default_value("xeditdeck.interiorBorderWidth","Editor.BorderWidth")); maybeset(&bordermstr,get_default_value("xeditdeck.borderMargin","Editor.BorderMargin")); maybeset(&maggridstr,get_default_value("xeditdeck.gridWidth","Editor.GridWidth")); maybeset(&magstr,get_default_value("xeditdeck.mag","Editor.Mag")); setup_names(); setup_font(); setup_numbers(); setup_colors(); setup_buttons(); setup_window(); setup_colpix(); select_color(-1); set_colorp(1); set_slider_style(SLST_RGB); select_card(CT_CARD,CARD_MAKE(CARD_C,CARD_A)); run(); exit(0); }