#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include static XrmDatabase db; static const char *defaults = "\ *Foreground: white\n\ *Background: black\n\ *BorderColor: white\n\ *BorderWidth: 1\n\ *BorderMargin: 1\n\ *MenuMargin: 25\n\ *Name: xbme\n\ *IconName: xbme\n\ *MagFactor: 16\n\ "; static char *displayname; static char *geometryspec; static char *foreground; static char *background; static char *bordercstr; static char *borderwstr; static char *bordermstr; static char *menumstr; static char *magfacstr; static char *name; static char *iconname; static char *fontname; static int synch; static int debugdraw; static int argc; static char **argv; static Display *disp; static Screen *scr; static int width; static int height; static int depth; static Window rootwin; static Colormap defcmap; static Visual *visual; static int (*preverr)(Display *, XErrorEvent *); static int (*prevIOerr)(Display *); static XColor fgcolor; static XColor bgcolor; static XColor bdcolor; typedef struct xy XY; typedef struct tpl TPL; typedef struct menu MENU; typedef struct menuent MENUENT; typedef struct magscroll MAGSCROLL; typedef struct fpxc FPXC; typedef struct ranges RANGES; typedef struct selection SELECTION; struct selection { char *name; ROP_AREA *area; int w; int h; } ; struct ranges { int n; int a; int (*v)[2]; } ; #define RANGES_INIT { 0, 0, 0 } struct fpxc { int x; int d; } ; struct xy { int x; int y; } ; struct tpl { const char *name; unsigned long int eventmask; int (*ctl)(TPL *, int, ...); #define TPL_CTL_EVENT 1 #define TPL_CTL_INIT 2 #define TPL_CTL_RESIZE 3 TPL *link; int flags; #define TPL_F_UP 0x00000001 #define TPL_F_CREATED 0x00000002 Window top; Window win; Cursor curcurs; int winw; int winh; } ; struct menuent { const char *text; void (*handler)(Time, void *, int, void (*)(void)); int flags; #define ME_F_UNSELECTABLE 0x00000001 #define ME_F_DIRTY 0x00000002 void *arg_ptr; int arg_int; void (*arg_fxn)(void); char *textfree; MENU *menu; int textx; int texty; XCharStruct textbound; } ; typedef void MENU_FN_TYPE(Time, void *, int, void (*)(void)); #define MENU_FN_DECL(name) static MENU_FN_TYPE name; #define MENU_FN_DEFN(name) static void name(Time t __attribute__((__unused__)), void *arg_v __attribute__((__unused__)), int arg_i __attribute__((__unused__)), void (*arg_fn)(void) __attribute__((__unused__))) #define ENTRY_BLANK { "", 0, ME_F_UNSELECTABLE, 0, 0, 0, 0 } #define ENTRY_TEXT(s) { (s), 0, ME_F_UNSELECTABLE, 0, 0, 0, 0 } #define ENTRY_VOID(s,fn) { (s), (fn), 0, 0, 0, 0, 0 } #define ENTRY_PTR(s,fn,arg) { (s), (fn), 0, (arg), 0, 0, 0 } #define ENTRY_INT(s,fn,arg) { (s), (fn), 0, 0, (arg), 0, 0 } #define ENTRY_FXN(s,fn,arg) { (s), (fn), 0, 0, 0, (void (*)(void))(arg), 0 } #define ENTRY_ALL(s,fn,p,i,f) { (s), (fn), 0, (p), (i), (void (*)(void))(f), 0 } struct menu { MENUENT *entries; int nentries; int flags; #define MENU_F_DIDINIT 0x00000001 int w; int h; } ; #define MENU_INIT(ents) &ents[0], sizeof(ents)/sizeof(ents[0]), 0 struct magscroll { MAGSCROLL *link; unsigned long int serial; int dx; int dy; } ; static TPL *tpls; #define EACH_TPL(var) var=tpls;var;var=var->link static Pixmap gray_50; static GC fwdgc; static GC revgc; static GC xorgc; static GC bitgc; static int pan_ctl(TPL *, int, ...); static int fullsize_ctl(TPL *, int, ...); static int fatbits_ctl(TPL *, int, ...); static TPL pan = { "panner", ExposureMask | ButtonPressMask | PointerMotionMask | PointerMotionHintMask, pan_ctl }; static TPL fullsize = { "image", ExposureMask | ButtonPressMask | PointerMotionMask | PointerMotionHintMask, fullsize_ctl }; static TPL fatbits = { "fatbits", ExposureMask | ButtonPressMask | ButtonMotionMask, fatbits_ctl }; static Colormap wincmap; static Cursor arrowcurs; static Cursor xhaircurs; static Cursor boxgrabcurs[3][3]; static Window boxgrabtop; static Window boxgrabwin[3][3]; static void (*boxgrabdone)(int, int) = 0; static int margin; static int menu_margin; static int borderwidth; static XFontStruct *font; static int deffont; static int baselineskip; static int ininit = 1; static char *filename; static FILE *filef; static int rawbits; static int xsize; static int ysize; static int magfactor; static int tmpsize; static int tmpoff; static ROP_AREA *bits; static Pixmap pan_pixmap; static ROP_AREA *pan_bw_data; static unsigned char *pan_grey_data; static int loading_pan; static int panload_x; static int panload_y; static double *panload_err_1 = 0; static double *panload_err_2; static int pan_xpos; static int pan_ypos; static int pan_picw; static int pan_pich; static int pan_px; static int pan_py; static int pan_pw; static int pan_ph; static int pan_dragging = 0; static int pan_dox; static int pan_doy; static int mag_dragging = 0; static int mag_dox; static int mag_doy; static int mag_lastx; static int mag_lasty; static int mag_lastb; static char *mag_cur; static char *mag_want; #define MB_PIX 0x01 #define MB_TMP 0x02 #define MB_HL 0x04 #define MB_VL 0x08 static int mag_dirty; static Pixmap fat_pix[4]; /* one for each possible mb value */ static int mag_x; static int mag_y; static int mag_w; static int mag_h; static MAGSCROLL *mag_scroll; static MAGSCROLL **mag_scroll_tail; static int ms_sum_dx; static int ms_sum_dy; static SELECTION sel; static int nselections; static int aselections; static SELECTION **selections; static void (*selection_op)(int); static unsigned int selections_gen; static unsigned int selections_menu_gen; #define SELR_SR 1 /* solid rectangle */ #define SELR_HR 2 /* hollow rectangle */ static int sel_render = SELR_SR; static int sel_render1; static int sel_render1_set = 0; static XY sel_lock = { x:-1, y:-1 }; static int *fp_y0; static int *fp_y1; static int (**fp_slabs)[2]; /* [lineno][y-y0][0|1] */ static int *fp_prev = 0; static int *fp_next = 0; static int fp_np_a; static FPXC *fp_xc = 0; static int fp_xca = 0; static int fp_xcn; static RANGES fp_xr = RANGES_INIT; static int (*sc_v)[2] = 0; static int sc_y0; static int sc_y1; static int fatbits_alu = ROP_SRC; static int draw_alu = ROP_SET; static int copy_alu = ROP_SRC; static int copy1_alu; static int copy1_alu_set = 0; static int paste_disp_alu(int, int); static int paste_disp_resalu(int, int); static int (*pdisp_fn)(int, int) = paste_disp_resalu; static int (*pdisp1_fn)(int, int); static int pdisp_alu = ROP_SRC; static int pdisp1_alu; static int pdisp_set_alu; static int pdisp1_set = 0; static int pdisp1_setting; static int pdsalu_xor; static int (*pdsalu_fn)(int, int); static Window menu_top; static Window menu_win; static int curitem; static int drawn_curitem; static int menu_wantquery; static int items_dirty; static MENU *curmenu; #define CP_INIT 1 #define CP_ABORT 2 #define CP_CHOSEN 3 #define CP_MOVE 4 #define CP_HIDEFAT 5 #define CP_DRAWFAT 6 #define CP_HIDEFULL 7 #define CP_DRAWFULL 8 #define CP_RBUTTON 9 static void (*choose_pt)(int, ...); MENU_FN_DECL(any_menu_new_menu); static int *op_menu_var; static void (*op_menu_fn)(void); MENU_FN_DECL(op_menu_setop); static MENUENT full_op_menu_ents[] = { ENTRY_TEXT("Choose an operation"), ENTRY_BLANK, ENTRY_INT("Clear",op_menu_setop,ROP_CLR), ENTRY_INT("NOR",op_menu_setop,ROP_SRC_NOR_DST), ENTRY_INT("src & ~dst",op_menu_setop,ROP_SRC_AND_NDST), ENTRY_INT("Negate dst",op_menu_setop,ROP_NDST), ENTRY_INT("~src & dst",op_menu_setop,ROP_NSRC_AND_DST), ENTRY_INT("Negated copy",op_menu_setop,ROP_NSRC), ENTRY_INT("XOR",op_menu_setop,ROP_SRC_XOR_DST), ENTRY_INT("NAND",op_menu_setop,ROP_SRC_NAND_DST), ENTRY_INT("AND",op_menu_setop,ROP_SRC_AND_DST), ENTRY_INT("XNOR",op_menu_setop,ROP_SRC_NXOR_DST), ENTRY_INT("Copy",op_menu_setop,ROP_SRC), ENTRY_INT("src | ~dst",op_menu_setop,ROP_SRC_OR_NDST), ENTRY_INT("Noop",op_menu_setop,ROP_DST), ENTRY_INT("~src | dst",op_menu_setop,ROP_NSRC_OR_DST), ENTRY_INT("OR",op_menu_setop,ROP_SRC_OR_DST), ENTRY_INT("Set",op_menu_setop,ROP_SET) }; static MENU full_op_menu = { MENU_INIT(full_op_menu_ents) }; static MENUENT half_op_menu_ents[] = { ENTRY_TEXT("Choose an operation"), ENTRY_BLANK, ENTRY_INT("Clear",op_menu_setop,ROP_CLR), ENTRY_INT("Flip",op_menu_setop,ROP_NDST), ENTRY_INT("Noop",op_menu_setop,ROP_DST), ENTRY_INT("Set",op_menu_setop,ROP_SET) }; static MENU half_op_menu = { MENU_INIT(half_op_menu_ents) }; MENU_FN_DECL(choose_op_menu_buttons); MENU_FN_DECL(choose_op_menu_draw); MENU_FN_DECL(choose_op_menu_copy); static MENUENT choose_op_menu_ents[] = { ENTRY_TEXT("Change which operation"), ENTRY_BLANK, ENTRY_VOID("Buttons",choose_op_menu_buttons), ENTRY_VOID("Drawing",choose_op_menu_draw), ENTRY_VOID("Copy",choose_op_menu_copy) }; static MENU choose_op_menu = { MENU_INIT(choose_op_menu_ents) }; MENU_FN_DECL(main_menu_quit); MENU_FN_DECL(main_menu_update_panner); MENU_FN_DECL(main_menu_select); MENU_FN_DECL(main_menu_paste); MENU_FN_DECL(main_menu_selections); MENU_FN_DECL(main_menu_twoop); MENU_FN_DECL(main_menu_fillpoly); MENU_FN_DECL(main_menu_pixcleanup); MENU_FN_DECL(main_menu_save); static void mmhr__draw(int); static void mmsr__draw(int); static void mml__draw(int); static void mmhcr__draw(int); static void mmhcb__draw(int); static void mmheb__draw(int); static void mmscr__draw(int); static void mmscb__draw(int); static void mmseb__draw(int); static MENUENT main_menu_ents[] = { ENTRY_VOID("Quit",main_menu_quit), ENTRY_BLANK, ENTRY_VOID("Update panner",main_menu_update_panner), #define MME_UPDATE_PANNER 2 ENTRY_BLANK, ENTRY_PTR("Choose operation",any_menu_new_menu,&choose_op_menu), ENTRY_BLANK, ENTRY_VOID("Select",main_menu_select), ENTRY_VOID("Paste",main_menu_paste), ENTRY_BLANK, ENTRY_VOID("Selections",main_menu_selections), ENTRY_BLANK, ENTRY_FXN("Hollow rectangle",main_menu_twoop,mmhr__draw), ENTRY_FXN("Solid rectangle",main_menu_twoop,mmsr__draw), ENTRY_FXN("Line",main_menu_twoop,mml__draw), ENTRY_VOID("Filled Polygon",main_menu_fillpoly), ENTRY_FXN("Outline circle from radius",main_menu_twoop,mmhcr__draw), ENTRY_FXN("Outline circle from box",main_menu_twoop,mmhcb__draw), ENTRY_FXN("Outline ellipse from box",main_menu_twoop,mmheb__draw), ENTRY_FXN("Solid circle from radius",main_menu_twoop,mmscr__draw), ENTRY_FXN("Solid circle from box",main_menu_twoop,mmscb__draw), ENTRY_FXN("Solid ellipse from box",main_menu_twoop,mmseb__draw), ENTRY_BLANK, ENTRY_VOID("Isolated pixel cleanup",main_menu_pixcleanup), ENTRY_BLANK, ENTRY_VOID("Save",main_menu_save) }; static MENU main_menu = { MENU_INIT(main_menu_ents) }; MENU_FN_DECL(selections_menu_save); MENU_FN_DECL(selections_menu_replace); MENU_FN_DECL(selections_menu_drop); #define selections_menu_select main_menu_select #define selections_menu_paste main_menu_paste static MENUENT selections_menu_head[] = { ENTRY_VOID("Save",selections_menu_save), ENTRY_VOID("Replace",selections_menu_replace), ENTRY_VOID("Drop",selections_menu_drop), ENTRY_BLANK, ENTRY_VOID("Select",selections_menu_select), ENTRY_VOID("Paste",selections_menu_paste), ENTRY_BLANK }; #define SELECTIONS_MENU_HEAD_N (sizeof(selections_menu_head) / sizeof(selections_menu_head[0])) static MENU selections_menu_with_head = { 0, 0 }; static MENU selections_menu_no_head = { 0, 0 }; MENU_FN_DECL(polygon_menu_abort); MENU_FN_DECL(polygon_menu_done); MENU_FN_DECL(polygon_menu_del_last); #define FP_EO 1 #define FP_NZ 2 static MENUENT polygon_menu_ents[] = { ENTRY_TEXT("Filled polygon menu"), ENTRY_BLANK, ENTRY_VOID("Delete last point",polygon_menu_del_last), ENTRY_BLANK, ENTRY_VOID("Abort",polygon_menu_abort), ENTRY_BLANK, ENTRY_INT("Done (even-odd)",polygon_menu_done,FP_EO), ENTRY_INT("Done (non-zero)",polygon_menu_done,FP_NZ) }; static MENU polygon_menu = { MENU_INIT(polygon_menu_ents) }; MENU_FN_DECL(paste_flip_menu_rotate); MENU_FN_DECL(paste_flip_menu_reflect_x); MENU_FN_DECL(paste_flip_menu_reflect_y); MENU_FN_DECL(paste_flip_menu_reflect_xy); MENU_FN_DECL(paste_flip_menu_reflect_nxy); static MENUENT paste_flip_menu_ents[] = { ENTRY_TEXT("Paste flip menu"), ENTRY_BLANK, ENTRY_INT("Rotate clockwise",paste_flip_menu_rotate,1), ENTRY_INT("Rotate 180°",paste_flip_menu_rotate,2), ENTRY_INT("Rotate counterclockwise",paste_flip_menu_rotate,3), ENTRY_BLANK, ENTRY_VOID("Reflect about |",paste_flip_menu_reflect_x), ENTRY_VOID("Reflect about -",paste_flip_menu_reflect_y), ENTRY_VOID("Reflect about \\",paste_flip_menu_reflect_xy), ENTRY_VOID("Reflect about /",paste_flip_menu_reflect_nxy) }; static MENU paste_flip_menu = { MENU_INIT(paste_flip_menu_ents) }; MENU_FN_DECL(paste_display_set_alu); MENU_FN_DECL(paste_display_set_hr); static MENUENT paste_display_menu_ents[] = { ENTRY_TEXT("Paste display menu"), ENTRY_BLANK, ENTRY_ALL("Display as ALU(paste-result,dst) xor dst",paste_display_set_alu,0,ROP_DST,paste_disp_resalu), ENTRY_ALL("Display as ALU(paste-result,dst)",paste_display_set_alu,0,0,paste_disp_resalu), ENTRY_ALL("Display as ALU(paste-src,dst) xor dst",paste_display_set_alu,0,ROP_DST,paste_disp_alu), ENTRY_ALL("Display as ALU(paste-src,dst)",paste_display_set_alu,0,0,paste_disp_alu), ENTRY_VOID("Display as hollow rectangle",paste_display_set_hr) }; static MENU paste_display_menu = { MENU_INIT(paste_display_menu_ents) }; MENU_FN_DECL(paste_menu_abort); MENU_FN_DECL(paste_menu_reanchor); MENU_FN_DECL(paste_menu_setop_one); MENU_FN_DECL(paste_menu_setop_all); MENU_FN_DECL(paste_menu_setdisp_one); MENU_FN_DECL(paste_menu_setdisp_all); static MENUENT paste_menu_ents[] = { ENTRY_TEXT("Paste menu"), ENTRY_BLANK, ENTRY_PTR("Reflect/rotate",any_menu_new_menu,&paste_flip_menu), ENTRY_BLANK, ENTRY_VOID("Change display (once)",paste_menu_setdisp_one), ENTRY_VOID("Change display (sticky)",paste_menu_setdisp_all), ENTRY_BLANK, ENTRY_VOID("Re-anchor",paste_menu_reanchor), ENTRY_VOID("Change operation (once)",paste_menu_setop_one), ENTRY_VOID("Change operation (sticky)",paste_menu_setop_all), ENTRY_BLANK, ENTRY_VOID("Abort paste",paste_menu_abort) }; static MENU paste_menu = { MENU_INIT(paste_menu_ents) }; MENU_FN_DECL(select_menu_abort); MENU_FN_DECL(select_menu_regrab); MENU_FN_DECL(select_menu_display_one); MENU_FN_DECL(select_menu_display_all); static MENUENT select_menu_ents[] = { ENTRY_TEXT("Select menu"), ENTRY_BLANK, ENTRY_VOID("Pick corner/side to move",select_menu_regrab), ENTRY_BLANK, ENTRY_INT("Display as solid rectangle (once)",select_menu_display_one,SELR_SR), ENTRY_INT("Display as solid rectangle (sticky)",select_menu_display_all,SELR_SR), ENTRY_BLANK, ENTRY_INT("Display as hollow rectangle (once)",select_menu_display_one,SELR_HR), ENTRY_INT("Display as hollow rectangle (sticky)",select_menu_display_all,SELR_HR), ENTRY_BLANK, ENTRY_VOID("Abort select",select_menu_abort) }; static MENU select_menu = { MENU_INIT(select_menu_ents) }; static int junk_direction; static int junk_ascent; static int junk_descent; #define XTE_JUNK &junk_direction,&junk_ascent,&junk_descent #if 0 /* You need to provide alloc_once(), which allocates memory that will never be freed for the life of the program. The reason for not simply using malloc() is that on many machines malloc() rounds up to a power of two, which is very wasteful when allocating space for multi-meg pictures. If your system has a malloc() that's smart about big requests, you can just have alloc_once call malloc; some other systems can use sbrk. Note that alloc_once is expected to complain and exit if the allocation fails, so you can't just #define it to malloc or sbrk. */ #define NEED_ALLOC_ONCE #if defined(NEED_ALLOC_ONCE) && defined(__NetBSD__) #undef NEED_ALLOC_ONCE #include static void *alloc_once(int nb) { static int pgsize = 0; char *rv; if (pgsize == 0) pgsize = getpagesize(); rv = mmap(0,nb,PROT_READ|PROT_WRITE,MAP_ANON|MAP_PRIVATE,-1,0); if (rv == (char *)-1) { fprintf(stderr,"%s: can't allocate %d bytes (can't map anonymous memory): %s\n",__progname,nb,strerror(errno)); exit(1); } return(rv); } #endif #if defined(NEED_ALLOC_ONCE) && defined(sun) /* well, SunOS 4.1 and 4.1.1, at least */ #undef NEED_ALLOC_ONCE #include #include static void *alloc_once(int nb) { static int zfd = -1; static int pgsize; char *rv; if (zfd < 0) { zfd = open("/dev/zero",O_RDONLY,0); if (zfd < 0) { fprintf(stderr,"%s: can't open /dev/zero: %s\n",__progname,strerror(errno)); exit(1); } pgsize = getpagesize(); } rv = mmap((caddr_t)0,nb,PROT_READ|PROT_WRITE,MAP_PRIVATE,zfd,0); if (rv == (char *)-1) { fprintf(stderr,"%s: can't allocate %d bytes (can't map /dev/zero): %s\n",__progname,nb,strerror(errno)); exit(1); } return(rv); } #endif #if defined(NEED_ALLOC_ONCE) && defined(NeXT) #undef NEED_ALLOC_ONCE #include #include /* contrary to doc, isn't enough */ static void *alloc_once(int nb) { kern_return_t rv; vm_address_t addr; rv = vm_allocate(task_self(),&addr,nb,1); if (rv != KERN_SUCCESS) { fprintf(stderr,"%s: can't vm_allocate %d bytes: %s\n",__progname,nb,mach_error_string(rv)); exit(1); } return((void *)addr); } #endif #ifdef NEED_ALLOC_ONCE #error Must implement alloc_once() #endif #endif static __inline__ int min(int a, int b) { return((ab)?a:b); } static __inline__ XY xysub(XY a, XY b) { return((XY){x:a.x-b.x,y:a.y-b.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)); } #undef abs #define abs bmeabs inline static unsigned int abs(int) __attribute__((__const__)); inline static unsigned int abs(int x) { if (x < 0) return(-x); else return(x); } #if 0 static const inline int gcd(int a, int b) { if (a < 0) a = -a; if (b < 0) b = -b; while (b) { int t; t = a % b; a = b; b = t; } return(a); } #endif inline static unsigned int isqrt(unsigned int) __attribute__((__const__)); inline static unsigned int isqrt(unsigned int n) { unsigned int i; unsigned int j; if (n < 1) return(0); for (i=n,j=1;i>1;i>>=2,j<<=1) ; do { i = j; j = ((n / i) + i) / 2; } while ((j != i) && (j != i-1)); if (j*j > n) j --; if (j*(j+1) < n) j ++; return(j); } #if 0 static int rnd(void) { static int didinit = 0; if (! didinit) { struct timeval tv; gettimeofday(&tv,(struct timezone *)0); srandom(tv.tv_sec+tv.tv_usec+getpid()); didinit = 1; } return(random()); } #endif static void xysize(int bigw, int bigh, int maxw, int maxh, int *wp, int *hp) { if ((bigw <= maxw) && (bigh <= maxh)) { *wp = bigw; *hp = bigh; return; } if (bigw*maxh > bigh*maxw) { *wp = maxw; *hp = ((bigh * maxw) + bigw - 1) / bigw; } else { *wp = ((bigw * maxh) + bigh - 1) / bigh; *hp = maxh; } } static int rect_overlap(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { if ( (x2+w2 <= x1) || (y2+h2 <= y1) || (x1+w1 <= x2) || (y1+h1 <= y2) ) return(0); return(1); } static int rect_intersect(int *xp, int *yp, int *wp, int *hp, int x, int y, int w, int h) { if ( (x+w <= *xp) || (y+h <= *yp) || (*xp+*wp <= x) || (*yp+*hp <= y) ) return(0); if (x+w < *xp+*wp) *wp = x + w - *xp; if (y+h < *yp+*hp) *hp = y + h - *yp; if (x > *xp) { *wp = *xp + *wp - x; *xp = x; } if (y > *yp) { *hp = *yp + *hp - y; *yp = y; } return(1); } static int point_in_rect(int px, int py, int x, int y, int w, int h) { return( (px >= x) && (py >= y) && (px < x+w) && (py < y+h) ); } static void panic_(const char *, int, const char *, const char *, ...) __attribute__((__format__(__printf__,4,5),__noreturn__)); static void panic_(const char *file, int line, const char *fn, const char *fmt, ...) { va_list ap; fprintf(stderr,"PANIC (%s(), \"%s\", line %d): ",fn,file,line); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); abort(); } #define panic(fmt,args...) panic_(__FILE__,__LINE__,__FUNCTION__,fmt , ##args) static Time cur_time(void) { static Window w = None; XEvent e; if (w == None) { XSetWindowAttributes attr; attr.event_mask = PropertyChangeMask; w = XCreateWindow(disp,rootwin,0,0,1,1,0,0,InputOnly,CopyFromParent,CWEventMask,&attr); } XChangeProperty(disp,w,XA_PRIMARY,XA_PRIMARY,8,PropModeAppend,0,0); XWindowEvent(disp,w,PropertyChangeMask,&e); return(e.xproperty.time); } static void saveargv(int ac, char **av) { int i; int nc; char *abuf; argc = ac; argv = (char **) malloc((ac+1)*sizeof(char *)); nc = 1; for (i=0;i 0) { skip --; continue; } if (**av != '-') { errs += specify_file(av[skip]); continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*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,"-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,"-bordermargin") || !strcmp(*av,"-bm")) { WANTARG(); bordermstr = av[skip]; continue; } if (!strcmp(*av,"-menumargin") || !strcmp(*av,"-mm")) { WANTARG(); menumstr = av[skip]; continue; } if (!strcmp(*av,"-magfactor") || !strcmp(*av,"-magf") || !strcmp(*av,"-mf")) { WANTARG(); magfacstr = av[skip]; continue; } if (!strcmp(*av,"-name")) { WANTARG(); name = av[skip]; continue; } if (!strcmp(*av,"-iconname")) { WANTARG(); iconname = av[skip]; continue; } if (!strcmp(*av,"-font") || !strcmp(*av,"-fn")) { WANTARG(); fontname = av[skip]; continue; } if (!strcmp(*av,"-sync")) { synch = 1; continue; } if (!strcmp(*av,"-debugdraw")) { debugdraw = 1; continue; } if (!strcmp(*av,"-file")) { WANTARG(); errs += specify_file(av[skip]); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) { exit(1); } } static void menu_popdown(void) { XDestroyWindow(disp,menu_top); curmenu = 0; } static void set_curitem_xy(int x, int y) { if ( (x < 0) || (y < 0) || (x >= curmenu->w) || (y >= curmenu->h) ) { curitem = -1; #define M (menu_margin+borderwidth+margin) if ( (x < -M) || (y < -M) || (x >= curmenu->w+M) || (y >= curmenu->h+M) ) { menu_popdown(); } #undef M } else { curitem = y / baselineskip; } } static void menu_choose_curitem(XEvent *e) { MENUENT *me; if (curitem < 0) { menu_popdown(); return; } me = &curmenu->entries[curitem]; if (me->flags & ME_F_UNSELECTABLE) { menu_popdown(); return; } menu_popdown(); (*me->handler)(e->xbutton.time,me->arg_ptr,me->arg_int,me->arg_fxn); } static int menu_event(XEvent *e) { int rv; rv = 0; switch (e->type) { case Expose: if (e->xexpose.window == menu_win) { int i; int y1; int y2; MENUENT *me; y1 = e->xexpose.y; y2 = e->xexpose.y + e->xexpose.height - 1; for (i=0;inentries;i++) { me = &curmenu->entries[i]; if ( (y1 < me->texty+me->textbound.descent) && (y2 >= me->texty-me->textbound.ascent) ) { me->flags |= ME_F_DIRTY; items_dirty = 1; } } rv = 1; } break; case MotionNotify: if (e->xmotion.window == menu_win) { menu_wantquery = 1; rv = 1; } break; case EnterNotify: if (e->xcrossing.window == menu_win) { set_curitem_xy(e->xcrossing.x,e->xcrossing.y); rv = 1; } break; case LeaveNotify: if (e->xcrossing.window == menu_win) { if (! e->xcrossing.same_screen) { menu_popdown(); } else { set_curitem_xy(e->xcrossing.x,e->xcrossing.y); } rv = 1; } break; case ButtonPress: if (e->xbutton.window == menu_win) { if (! e->xbutton.same_screen) { menu_popdown(); } else { set_curitem_xy(e->xbutton.x,e->xbutton.y); if (curmenu) menu_choose_curitem(e); } rv = 1; } break; } return(rv); } static void menu_query(void) { Window ret_root; Window ret_child; int ret_rootx; int ret_rooty; int ret_winx; int ret_winy; unsigned int ret_mask; menu_wantquery = 0; if (! curmenu) return; if (XQueryPointer(disp,menu_win,&ret_root,&ret_child,&ret_rootx,&ret_rooty,&ret_winx,&ret_winy,&ret_mask) != True) { menu_popdown(); return; } set_curitem_xy(ret_winx,ret_winy); } static void init_menu(MENU *m) { int i; int maxw; int y; MENUENT *me; y = 0; maxw = 0; for (i=0;inentries;i++) { me = &m->entries[i]; me->menu = m; me->texty = y + font->ascent; y += baselineskip; XTextExtents(font,me->text,strlen(me->text),XTE_JUNK,&me->textbound); if (me->textbound.width > maxw) maxw = me->textbound.width; } m->w = maxw; m->h = y; for (i=0;inentries;i++) { me = &m->entries[i]; me->textx = (maxw - me->textbound.width) / 2; } m->flags |= MENU_F_DIDINIT; } static void redraw_curitem(void) { if (drawn_curitem >= 0) curmenu->entries[drawn_curitem].flags |= ME_F_DIRTY; if (curitem >= 0) curmenu->entries[curitem].flags |= ME_F_DIRTY; items_dirty = 1; drawn_curitem = curitem; } static void redraw_item(int n) { GC *gcp; if ((n == curitem) && !(curmenu->entries[n].flags & ME_F_UNSELECTABLE)) { gcp = &revgc; } else { gcp = &fwdgc; } XDrawImageString(disp,menu_win,*gcp,curmenu->entries[n].textx,curmenu->entries[n].texty,curmenu->entries[n].text,strlen(curmenu->entries[n].text)); curmenu->entries[n].flags &= ~ME_F_DIRTY; } static void redraw_dirty_items(void) { int i; for (i=0;inentries;i++) { if (curmenu->entries[i].flags & ME_F_DIRTY) { redraw_item(i); } } items_dirty = 0; } static void menu_choose(MENU *m, Time time) { int x; int y; int w; int h; Window ret_root; Window ret_child; int ret_rootx; int ret_rooty; int ret_winx; int ret_winy; unsigned int ret_mask; unsigned long int attrmask; XSetWindowAttributes attr; if (curmenu) panic("recursive call to menu_choose"); if (time == CurrentTime) time = cur_time(); if (XGrabPointer(disp,rootwin,False,ButtonPressMask|ButtonReleaseMask|PointerMotionMask|PointerMotionHintMask|EnterWindowMask|LeaveWindowMask,GrabModeSync,GrabModeAsync,None,None,time) != GrabSuccess) { curmenu = 0; return; } if (XQueryPointer(disp,rootwin,&ret_root,&ret_child,&ret_rootx,&ret_rooty,&ret_winx,&ret_winy,&ret_mask) != True) { XUngrabPointer(disp,CurrentTime); curmenu = 0; return; } if (! (m->flags & MENU_F_DIDINIT)) init_menu(m); curmenu = m; w = m->w + (2 * margin) + (2 * borderwidth); h = m->h + (2 * margin) + (2 * borderwidth); x = ret_rootx - (w/2); y = ret_rooty - (h/2); if (x+w > width) x = width - w; if (y+h > height) y = height - h; if (x < 0) x = 0; if (y < 0) y = 0; w -= 2 * borderwidth; h -= 2 * borderwidth; attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.border_pixel = bdcolor.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = 0; attrmask |= CWEventMask; attr.override_redirect = True; attrmask |= CWOverrideRedirect; attr.colormap = wincmap; attrmask |= CWColormap; attr.cursor = arrowcurs; attrmask |= CWCursor; menu_top = XCreateWindow(disp,rootwin,x,y,w,h,borderwidth,depth,InputOutput,CopyFromParent,attrmask,&attr); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask|ButtonPressMask|ButtonReleaseMask|EnterWindowMask|LeaveWindowMask; attrmask |= CWEventMask; menu_win = XCreateWindow(disp,menu_top,margin,margin,m->w,m->h,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,menu_win); XMapRaised(disp,menu_top); if (XGrabPointer(disp,menu_win,False,ButtonPressMask|ButtonReleaseMask|PointerMotionMask|PointerMotionHintMask|EnterWindowMask|LeaveWindowMask,GrabModeSync,GrabModeAsync,None,None,time) != GrabSuccess) { XUngrabPointer(disp,CurrentTime); XDestroyWindow(disp,menu_top); curmenu = 0; return; } XAllowEvents(disp,AsyncPointer,time); drawn_curitem = -1; menu_wantquery = 1; items_dirty = 1; set_curitem_xy(ret_rootx-x,ret_rooty-y); } static void notpbm(const char *why) { fprintf(stderr,"%s: %s is not a PBM file (%s)\n",__progname,filename,why); exit(1); } static void shortfile(void) { fprintf(stderr,"%s: warning: premature EOF on %s\n",__progname,filename); } static int pnmgetc(FILE *f) { int c; c = getc(f); if (c == EOF) return(EOF); if (c == '#') { while (1) { c = getc(f); if (c == EOF) return(EOF); if (c == '\n') break; } } return(c); } static int pnmgetnum(FILE *f) { int v; int c; do c = pnmgetc(f); while (isspace(c)); if (c == EOF) notpbm("EOF in header"); if (! isdigit(c)) notpbm("non-digit in number"); v = c - '0'; while (1) { c = pnmgetc(f); if (c == EOF) notpbm("EOF in header"); if (! isdigit(c)) break; v = (10 * v) + (c - '0'); } if (! isspace(c)) notpbm("non-digit in number"); return(v); } static void readhdr(void) { if (getc(filef) != 'P') notpbm("bad magic number"); switch (getc(filef)) { case '1': rawbits = 0; break; case '4': rawbits = 1; break; case '2': case '5': notpbm("looks like a PGM file"); break; case '3': case '6': notpbm("looks like a PPM file"); break; default: notpbm("non-PBM magic number"); break; } xsize = pnmgetnum(filef); ysize = pnmgetnum(filef); if ((xsize <= 0) || (ysize <= 0)) notpbm("invalid size"); } static void padshort(int x, int y) { if (x > 0) { rop_rop(0,0,0,xsize-x,1,bits,x,y,ROP_CLR); y ++; } if (y < ysize) rop_rop(0,0,0,xsize,ysize-y,bits,0,y,ROP_CLR); } static void setup_picture(void) { int rb; if (! filename) { fprintf(stderr,"%s: you must give a filename\n",__progname); exit(1); } filef = fopen(filename,"r"); if (filef == 0) { fprintf(stderr,"%s: can't read %s\n",__progname,filename); exit(1); } readhdr(); bits = rop_create(xsize,ysize); rb = (xsize + 7) >> 3; if (rawbits) { int n; int y; unsigned char row[rb]; for (y=0;y>= 1; } *bitp++ = ibuf; /* no test - know there's at least one pixel per row */ rop_loadrow(bits,y,&row[0]); } out:; } fclose(filef); } static void maybeset(char **strp, char *str) { if (str && !*strp) *strp = str; } static void setup_db(void) { char *str; char *home; XrmDatabase db2; db = XrmGetStringDatabase(defaults); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } else { 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); } } } 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_color(char *str, XColor *col) { if (XParseColor(disp,wincmap,str,col) == 0) { fprintf(stderr,"%s: bad color `%s'\n",__progname,str); exit(1); } while (1) { if (XAllocColor(disp,wincmap,col) == 0) { if (wincmap != defcmap) { fprintf(stderr,"%s: can't allocate colormap cell for color `%s'\n",__progname,str); exit(1); } wincmap = XCopyColormapAndFree(disp,wincmap); continue; } break; } } static void setup_font(void) { deffont = 0; font = fontname ? XLoadQueryFont(disp,fontname) : 0; if (! font) { if (fontname) fprintf(stderr,"%s: can't load font %s, using server default\n",__progname,fontname); font = XQueryFont(disp,XGContextFromGC(XDefaultGCOfScreen(scr))); deffont = 1; } baselineskip = font->ascent + font->descent; } static void setup_colors(void) { wincmap = defcmap; setup_color(foreground,&fgcolor); setup_color(background,&bgcolor); setup_color(bordercstr,&bdcolor); } static void setup_numbers(void) { if (bordermstr) margin = atoi(bordermstr); if (menumstr) menu_margin = atoi(menumstr); if (borderwstr) borderwidth = atoi(borderwstr); if (magfacstr) magfactor = atoi(magfacstr); } static void setup_fat_pix(void) { int i; int tmpb; tmpb = (magfactor / 3) - 1; if (tmpb < 1) tmpb = 1; tmpsize = magfactor - 3 - (2 * tmpb); tmpoff = 2 + tmpb; for (i=0;i<(sizeof(fat_pix)/sizeof(fat_pix[0]));i++) { fat_pix[i] = XCreatePixmap(disp,rootwin,magfactor,magfactor,depth); XFillRectangle(disp,fat_pix[i],revgc,0,0,magfactor,magfactor); } for (i=0;i<(sizeof(fat_pix)/sizeof(fat_pix[0]));i++) { XDrawLine(disp,fat_pix[i],xorgc,0,0,magfactor-1,0); XDrawLine(disp,fat_pix[i],xorgc,0,1,0,magfactor-1); if (i & MB_PIX) XFillRectangle(disp,fat_pix[i],xorgc,2,2,magfactor-3,magfactor-3); if (i & MB_TMP) XFillRectangle(disp,fat_pix[i],xorgc,tmpoff,tmpoff,tmpsize,tmpsize); } } static void setup_cursor(void) { Pixmap pic; Pixmap mask; GC gc; unsigned int siz; unsigned int h; int x; int y; /* Unfortunately writing the shapes[][] initializer naturally here means subscripting it as [y][x] instead of [x][y] below.... */ static unsigned int shapes[3][3] = { { XC_top_left_corner , XC_top_side , XC_top_right_corner }, { XC_left_side , 0 , XC_right_side }, { XC_bottom_left_corner, XC_bottom_side, XC_bottom_right_corner } }; for (y=0;y<3;y++) { for (x=0;x<3;x++) { if ((x == 1) && (y == 1)) { boxgrabcurs[1][1] = None; } else { Cursor c; c = XCreateFontCursor(disp,shapes[y][x]); XRecolorCursor(disp,c,&fgcolor,&bgcolor); boxgrabcurs[x][y] = c; } } } arrowcurs = XCreateFontCursor(disp,XC_left_ptr); XRecolorCursor(disp,arrowcurs,&fgcolor,&bgcolor); /* wish we could count on XC_crosshair to have a decent mask! */ XQueryBestSize(disp,CursorShape,rootwin,width,height,&siz,&h); if (h < siz) siz = h; if (width/40 < siz) siz = width / 40; if (height/40 < siz) siz = height / 40; if (! (siz & 1)) siz --; pic = XCreatePixmap(disp,rootwin,siz,siz,1); mask = XCreatePixmap(disp,rootwin,siz,siz,1); gc = XCreateGC(disp,pic,0L,(XGCValues *)0); XFillRectangle(disp,mask,gc,0,0,siz,siz); XSetForeground(disp,gc,1L); #ifdef DIAGONAL_XHAIR XDrawLine(disp,mask,gc,0,0,siz-1,siz-1); XDrawLine(disp,mask,gc,0,1,siz-2,siz-1); XDrawLine(disp,mask,gc,1,0,siz-1,siz-2); XDrawLine(disp,mask,gc,0,siz-1,siz-1,0); XDrawLine(disp,mask,gc,0,siz-2,siz-2,0); XDrawLine(disp,mask,gc,1,siz-1,siz-1,1); #else XFillRectangle(disp,mask,gc,0,(siz/2)-1,siz,3); XFillRectangle(disp,mask,gc,(siz/2)-1,0,3,siz); #endif XFillRectangle(disp,mask,gc,(siz/2)-4,(siz/2)-4,9,9); XSetForeground(disp,gc,0L); XFillRectangle(disp,pic,gc,0,0,siz,siz); XFillRectangle(disp,mask,gc,(siz/2)-1,(siz/2)-1,3,3); XSetForeground(disp,gc,1L); #ifdef DIAGONAL_XHAIR XDrawLine(disp,pic,gc,1,1,siz-2,siz-2); XDrawLine(disp,pic,gc,1,siz-2,siz-2,1); #else XDrawLine(disp,pic,gc,1,siz/2,siz-2,siz/2); XDrawLine(disp,pic,gc,siz/2,1,siz/2,siz-2); #endif XDrawRectangle(disp,pic,gc,(siz/2)-3,(siz/2)-3,6,6); XSetForeground(disp,gc,0L); XFillRectangle(disp,pic,gc,(siz/2)-2,(siz/2)-2,5,5); xhaircurs = XCreatePixmapCursor(disp,pic,mask,&fgcolor,&bgcolor,siz/2,siz/2); XFreePixmap(disp,pic); XFreePixmap(disp,mask); XFreeGC(disp,gc); } static void init_tpl(TPL *t) { t->flags = 0; t->link = tpls; t->curcurs = arrowcurs; tpls = t; } static void setup_gray(void) { GC gc; gray_50 = XCreatePixmap(disp,rootwin,2,2,depth); gc = XCreateGC(disp,gray_50,0L,(XGCValues *)0); XSetForeground(disp,gc,fgcolor.pixel); XDrawPoint(disp,gray_50,gc,0,0); XDrawPoint(disp,gray_50,gc,1,1); XSetForeground(disp,gc,bgcolor.pixel); XDrawPoint(disp,gray_50,gc,0,1); XDrawPoint(disp,gray_50,gc,1,0); XFreeGC(disp,gc); } static void setup_tpl(TPL *tpl, int w, int h, int x, int y) { unsigned long int attrmask; char *nbuf; XSetWindowAttributes attr; XTextProperty wn_prop; XTextProperty in_prop; XSizeHints normal_hints; XWMHints wm_hints; XClassHint class_hints; attrmask = 0; attr.background_pixmap = gray_50; attrmask |= CWBackPixmap; attr.border_pixel = bdcolor.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = StructureNotifyMask; attrmask |= CWEventMask; attr.colormap = wincmap; attrmask |= CWColormap; attr.cursor = tpl->curcurs; attrmask |= CWCursor; tpl->top = XCreateWindow(disp,rootwin,x-borderwidth,y-borderwidth,w,h,borderwidth,depth,InputOutput,CopyFromParent,attrmask,&attr); nbuf = malloc(strlen(name)+1+strlen(tpl->name)+1); sprintf(nbuf,"%s %s",name,tpl->name); wn_prop.value = (unsigned char *) nbuf; wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen((char *)wn_prop.value); nbuf = malloc(strlen(iconname)+1+strlen(tpl->name)+1); sprintf(nbuf,"%s %s",iconname,tpl->name); in_prop.value = (unsigned char *) nbuf; in_prop.encoding = XA_STRING; in_prop.format = 8; in_prop.nitems = strlen((char *)in_prop.value); normal_hints.flags = PMinSize | PResizeInc; normal_hints.width = w; normal_hints.height = h; normal_hints.flags |= PSize; normal_hints.min_width = 1; normal_hints.min_height = 1; normal_hints.width_inc = 1; normal_hints.height_inc = 1; wm_hints.flags = InputHint; wm_hints.input = False; class_hints.res_name = deconst("xbme"); class_hints.res_class = deconst("Editor"); XSetWMProperties(disp,tpl->top,&wn_prop,&in_prop,argv,argc,&normal_hints,&wm_hints,&class_hints); free((char *)wn_prop.value); free((char *)in_prop.value); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = tpl->eventmask; attrmask |= CWEventMask; tpl->win = XCreateWindow(disp,tpl->top,0,0,1,1,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapRaised(disp,tpl->win); tpl->flags |= TPL_F_CREATED; (*tpl->ctl)(tpl,TPL_CTL_INIT,w,h); XMapRaised(disp,tpl->top); tpl->flags |= TPL_F_UP; } static void set_tpl_cursor(TPL *t, Cursor c) { t->curcurs = c; if (t->flags & TPL_F_CREATED) XDefineCursor(disp,t->top,c); } static void ensure_tpl(TPL *t, int w, int h, int x, int y) { if (! (t->flags & TPL_F_CREATED)) { setup_tpl(t,w,h,x,y); } if (! (t->flags & TPL_F_UP)) { XMapWindow(disp,t->top); t->flags |= TPL_F_UP; } } static void remove_tpl(TPL *t) { if (t->flags & TPL_F_UP) { XWithdrawWindow(disp,t->top,XScreenNumberOfScreen(scr)); t->flags &= ~TPL_F_UP; } } static void ensure_pan(void) { int w; int h; xysize(xsize,ysize,width/4,height/4,&w,&h); ensure_tpl(&pan,w,h,width-w-borderwidth,height-h-borderwidth); } static GC setup_gc(int fxn, unsigned long int fg, unsigned long int bg) { unsigned long int gcvalmask; XGCValues gcval; gcvalmask = 0; if (fxn != GXcopy) { gcval.function = fxn; gcvalmask |= GCFunction; } if (fg != 0) { gcval.foreground = fg; gcvalmask |= GCForeground; } if (bg != 1) { gcval.background = bg; gcvalmask |= GCBackground; } if (! deffont) { gcval.font = font->fid; gcvalmask |= GCFont; } return(XCreateGC(disp,rootwin,gcvalmask,&gcval)); } static void setup_windows(void) { unsigned long int gcvalmask; XGCValues gcval; int x; int y; int w; int h; setup_gray(); init_tpl(&pan); init_tpl(&fullsize); init_tpl(&fatbits); if ((xsize <= (2*width)/3) && (ysize <= (2*height)/3)) { setup_tpl(&fullsize,xsize,ysize,(width-xsize)/2,(height-ysize)/2); } else { w = (xsize < (2*width)/3) ? xsize : (width/2); h = (ysize < (2*height)/3) ? ysize : (height/2); x = (width - w) / 3; y = (height - h) / 2; setup_tpl(&fullsize,w,h,x,y); ensure_pan(); } w = width / (4 * magfactor); h = height / (4 * magfactor); if (w > xsize) w = xsize; if (h > ysize) h = ysize; mag_x = 0; mag_y = 0; mag_w = w; mag_h = h; w = (w * magfactor) + 1; h = (h * magfactor) + 1; setup_tpl(&fatbits,w,h,width-w-borderwidth,(height-h-borderwidth)/2); fwdgc = setup_gc(GXcopy,fgcolor.pixel,bgcolor.pixel); revgc = setup_gc(GXcopy,bgcolor.pixel,fgcolor.pixel); xorgc = setup_gc(GXxor,fgcolor.pixel^bgcolor.pixel,0); gcvalmask = 0; if (! deffont) { gcval.font = font->fid; gcvalmask |= GCFont; } gcval.foreground = 1; gcvalmask |= GCForeground; gcval.background = 0; gcvalmask |= GCBackground; { Pixmap t; t = XCreatePixmap(disp,rootwin,1,1,1); bitgc = XCreateGC(disp,t,gcvalmask,&gcval); XFreePixmap(disp,t); } } static void setup_boxgrab(void) { unsigned long int attrmask; XSetWindowAttributes attr; int x; int y; Window w; attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.border_pixel = bdcolor.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = 0; attrmask |= CWEventMask; attr.do_not_propagate_mask = ButtonPressMask|ButtonReleaseMask; attrmask |= CWDontPropagate; attr.override_redirect = True; attrmask |= CWOverrideRedirect; attr.colormap = wincmap; attrmask |= CWColormap; attr.cursor = arrowcurs; attrmask |= CWCursor; boxgrabtop = XCreateWindow(disp,rootwin,0,0,120,120,borderwidth,depth,InputOutput,CopyFromParent,attrmask,&attr); for (x=0;x<3;x++) { for (y=0;y<3;y++) { if ((x == 1) && (y == 1)) { boxgrabwin[1][1] = boxgrabtop; continue; } attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ButtonPressMask|ButtonReleaseMask; attrmask |= CWEventMask; attr.cursor = boxgrabcurs[x][y]; attrmask |= CWCursor; w = XCreateWindow(disp,boxgrabtop,x*40,y*40,40,40,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapRaised(disp,w); boxgrabwin[x][y] = w; } } } static void setup_sel(void) { sel.name = 0; sel.area = 0; nselections = 0; aselections = 0; selections = 0; selections_gen = 1; selections_menu_gen = 0; } static void boxgrab_popdown(void) { XUnmapWindow(disp,boxgrabtop); } static void boxgrab_calldone(int x, int y) { void (*fn)(int, int); fn = boxgrabdone; boxgrabdone = 0; (*fn)(x,y); } static int boxgrab_event(XEvent *e) { int rv; int x; int y; rv = 0; switch (e->type) { case ButtonPress: if (e->xbutton.window != boxgrabtop) return(0); if ( (e->xbutton.subwindow == boxgrabtop) || (e->xbutton.button == Button3) || !e->xbutton.same_screen ) { boxgrab_popdown(); boxgrab_calldone(0,0); } else { for (x=0;x<3;x++) { for (y=0;y<3;y++) { if (e->xbutton.subwindow == boxgrabwin[x][y]) { boxgrab_popdown(); boxgrab_calldone(x-1,y-1); rv = 1; } } } } break; } return(rv); } static void handle_event(XEvent *e) { TPL *t; switch (e->type) { case ConfigureNotify: /* XConfigureEvent - xconfigure */ for (EACH_TPL(t)) { if ((t->flags & TPL_F_CREATED) && (e->xconfigure.window == t->top)) { (*t->ctl)(t,TPL_CTL_RESIZE,e->xconfigure.width,e->xconfigure.height); break; } } break; default: if (curmenu && menu_event(e)) break; if (boxgrabdone && boxgrab_event(e)) break; for (EACH_TPL(t)) if ((t->flags & TPL_F_CREATED) && (*t->ctl)(t,TPL_CTL_EVENT,e)) break; break; } } static void flip_pan_rect(int x, int y, int w, int h) { if (ininit) return; if (pan.flags & TPL_F_UP) { XFillRectangle(disp,pan.win,xorgc,x,y,w,h); } } static void redraw_pan(int x, int y, int w, int h) { XCopyPlane(disp,pan_pixmap,pan.win,fwdgc,x,y,w,h,x,y,1L); if (rect_intersect(&x,&y,&w,&h,pan_px,pan_py,pan_pw,pan_ph)) { flip_pan_rect(x,y,w,h); } } static void update_pan_bitmap(int x, int y, int w, int h) { static XImage *xi = 0; if (! xi) xi = XCreateImage(disp,visual,1,XYBitmap,0,0,1,1,8,1); XSetFunction(disp,bitgc,GXcopy); XSetForeground(disp,bitgc,1L); XSetBackground(disp,bitgc,0L); rop_xputimage(disp,pan_pixmap,bitgc,xi,pan_bw_data,x,y,x,y,w,h); if (pan.flags & TPL_F_UP) redraw_pan(x,y,w,h); } static void load_pan_fast_line(void) { int x; int y; int ymap; int pixel; y = panload_y; ymap = (y * ysize) / pan_pich; for (x=0;x= pan_pich) { loading_pan ++; if (panload_err_1) { free((char *)(panload_err_1-1)); free((char *)(panload_err_2-1)); } panload_err_1 = 1 + (double *) malloc((pan_picw+2)*sizeof(double)); panload_err_2 = 1 + (double *) malloc((pan_picw+2)*sizeof(double)); for (x=(-1);x<=pan_picw;x++) panload_err_1[x] = 0; panload_x = 0; panload_y = 0; } } static void load_pan_slow_pixel(void) { int x1; int x2; int y1; int y2; int nl; int nr; int nt; int nb; int n; int x; int y; #if 0 unsigned long int suml; unsigned long int sumh; #define INCSUM(x) (((suml+=(x))&0x80000000)?((suml&=~0x80000000),(sumh++)):0) #endif unsigned long int sum; #define INCSUM(x) (sum+=(x)) sum = 0; x = panload_x * xsize; x1 = (x + pan_picw - 1) / pan_picw; nl = x % pan_picw; x = ((panload_x + 1) * xsize) - 1; x2 = x / pan_picw; nr = x % pan_picw; y = panload_y * ysize; y1 = (y + pan_pich - 1) / pan_pich; nt = y % pan_pich; y = ((panload_y + 1) * ysize) - 1; y2 = y / pan_pich; nb = y % pan_pich; if (nt > 0) nt = pan_pich - nt; if (nl > 0) nl = pan_picw - nl; if (nt > 0) { y1 --; if ((nl > 0) && rop_getpixel(bits,x1-1,y1)) INCSUM(nt*nl); if ((nr > 0) && rop_getpixel(bits,x2,y1)) INCSUM(nt*nr); nt *= pan_picw; for (x=x1;x 0) { if ((nl > 0) && rop_getpixel(bits,x1-1,y2)) INCSUM(nb*nl); if ((nr > 0) && rop_getpixel(bits,x2,y2)) INCSUM(nb*nr); nb *= pan_picw; for (x=x1;x 0) && rop_getpixel(bits,x1-1,y)) INCSUM(nl); if ((nr > 0) && rop_getpixel(bits,x2,y)) INCSUM(nr); for (x=x1;x>= 1; n >>= 1; } pan_grey_data[(panload_y*pan_picw)+panload_x] = (sum << 8) / n; panload_x ++; if (panload_x >= pan_picw) { unsigned char *dp; int dx; int ex; double v; double *etemp; for (x=-1;x<=pan_picw;x++) panload_err_2[x] = 0; dp = &pan_grey_data[panload_y*pan_picw]; if (panload_y & 1) { dx = -1; x = pan_picw - 1; ex = -1; } else { dx = 1; x = 0; ex = pan_picw; } for (;x!=ex;x+=dx) { v = dp[x] + panload_err_1[x]; if (v >= 128) { rop_putpixel(pan_bw_data,x,panload_y,1); v -= 255; } else { rop_putpixel(pan_bw_data,x,panload_y,0); } panload_err_1[x+dx] += (v * 7) / 16; panload_err_2[x-dx] += (v * 3) / 16; panload_err_2[x ] += (v * 5) / 16; panload_err_2[x+dx] += (v ) / 16; } etemp = panload_err_1; panload_err_1 = panload_err_2; panload_err_2 = etemp; update_pan_bitmap(0,panload_y,pan_picw,1); panload_x = 0; panload_y ++; if (panload_y >= pan_pich) loading_pan ++; } } static void load_one_pan(void) { switch (loading_pan) { case 1: load_pan_fast_line(); break; case 2: load_pan_slow_pixel(); break; default: loading_pan = 0; break; } } static void clean_mag(void) { int x; int y; int i; char *mcp; char *mwp; mcp = mag_cur; for (y=0;y 0) { XNextEvent(disp,&e); handle_event(&e); } XFlush(disp); if (curmenu && menu_wantquery) { menu_query(); } else if (curmenu && (curitem != drawn_curitem)) { redraw_curitem(); } else if (curmenu && items_dirty) { redraw_dirty_items(); } else if (mag_dirty) { mag_dirty = 0; clean_mag(); } else if (loading_pan) { fd_set fds; struct timeval tv; FD_ZERO(&fds); FD_SET(xfd,&fds); tv.tv_sec = 0; tv.tv_usec = 0; select(FD_SETSIZE,&fds,0,0,&tv); if (! FD_ISSET(xfd,&fds)) { load_one_pan(); } } else { XNextEvent(disp,&e); handle_event(&e); } } } static void reload_pan(void) { loading_pan = 1; panload_y = 0; } static void flip_mag_rect(int x, int y, int w, int h) { if (ininit) return; XFillRectangle(disp,fullsize.win,xorgc,x,y,w,h); } static void redraw_fullsize(int x, int y, int w, int h) { static XImage *xi = 0; if (! xi) xi = XCreateImage(disp,visual,1,XYBitmap,0,0,1,1,8,1); if (choose_pt) (*choose_pt)(CP_HIDEFULL); rop_xputimage(disp,fullsize.win,fwdgc,xi,bits,x,y,x,y,w,h); if (rect_intersect(&x,&y,&w,&h,mag_x,mag_y,mag_w,mag_h)) flip_mag_rect(x,y,w,h); if (choose_pt) (*choose_pt)(CP_DRAWFULL); } /* Really should do something like keep a sorted list, adding points and maintaining the sort, collapsing whenever possible, so that aside from list size limits order doesn't matter. This would avoid the "two rects" problem, wherein drawing something like xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (which can occur rather easily when drawing solid rectangles with "flip") doesn't cause it to do many lines, instead of a few rects, for the pieces in the middle. But a full region package is probably overkill, especially since things like large circles involve many very small areas, for which a simple point queue behaves about as well as anything will. And, of course, there's always the (hard) question of finding an optimal decomposition of an arbitrary shape into rectangles. */ typedef struct qpt QPT; typedef struct ptq PTQ; struct qpt { unsigned char type; #define QPT_PT 2 #define QPT_LINE 3 #define QPT_RECT 4 union { XY pt; struct { XY e1; XY e2; } line; struct { XY c1; XY c2; } rect; } u; } ; #define QSZ 256 struct ptq { unsigned int setp; unsigned int n; const char *tag; unsigned int tpts; XY d; XY e1; XY e2; QPT q[QSZ]; } ; static PTQ qset = { 1, 0, "set", 0 }; static PTQ qclr = { 0, 0, "clr", 0 }; static void q_flush_(PTQ *q) { QPT *qp; GC gc; int i; if (q->n < 1) return; gc = q->setp ? fwdgc : revgc; qp = &q->q[0]; for (i=q->n;i>0;i--,qp++) { switch (qp->type) { default: panic("bad type %d in q",qp->type); break; case QPT_PT: if (debugdraw) { fprintf(stderr,"%s pt %d %d\n",q->tag,qp->u.pt.x,qp->u.pt.y); getchar(); } XDrawPoint(disp,fullsize.win,gc,qp->u.pt.x,qp->u.pt.y); break; case QPT_LINE: if (debugdraw) { fprintf(stderr,"%s line %d,%d - %d,%d\n",q->tag,qp->u.line.e1.x,qp->u.line.e1.y,qp->u.line.e2.x,qp->u.line.e2.y); getchar(); } XDrawLine(disp,fullsize.win,gc,qp->u.line.e1.x,qp->u.line.e1.y,qp->u.line.e2.x,qp->u.line.e2.y); break; case QPT_RECT: if (debugdraw) { fprintf(stderr,"%s rect %d,%d - %d,%d\n",q->tag,qp->u.rect.c1.x,qp->u.rect.c1.y,qp->u.rect.c2.x,qp->u.rect.c2.y); getchar(); } XFillRectangle(disp,fullsize.win,gc,qp->u.rect.c1.x,qp->u.rect.c1.y,qp->u.rect.c2.x+1-qp->u.rect.c1.x,qp->u.rect.c2.y+1-qp->u.rect.c1.y); break; } } q->n = 0; } static void q_pt(int x, int y, PTQ *q) { QPT *qp; switch (q->tpts) { case 0: /* nothing there yet */ q->tpts = 1; q->e1.x = x; q->e1.y = y; break; case 1: /* one point there - is the new point adjacent? */ if ((abs(q->e1.x-x) > 1) || (abs(q->e1.y-y) > 1)) { /* no - send the previous point up, do a new one */ if (q->n >= QSZ) q_flush_(q); qp = &q->q[q->n++]; qp->type = QPT_PT; qp->u.pt = q->e1; q->e1.x = x; q->e1.y = y; } else { /* yes - start a line */ q->tpts = 2; q->e2.x = x; q->e2.y = y; q->d.x = x - q->e1.x; q->d.y = y - q->e1.y; } break; default: /* two or more points there - does the new point fit on either end of the existing line? */ if ((q->e2.x+q->d.x == x) && (q->e2.y+q->d.y == y)) { /* yes - fits after the end */ q->tpts ++; q->e2.x = x; q->e2.y = y; } else if ((q->e1.x-q->d.x == x) && (q->e1.y-q->d.y == y)) { /* yes - fits before the beginning */ q->tpts ++; q->e1.x = x; q->e1.y = y; } else { /* no - we have to send up the line and start a new one */ if ( (q->e1.x > q->e2.x) || ( (q->e1.x == q->e2.x) && (q->e1.y > q->e2.y) ) ) { /* this is to make sure that orthogonal lines always have e1 to the left of, or above, e2 */ XY t; t = q->e1; q->e1 = q->e2; q->e2 = t; } if (q->n > 0) { /* already something queued - can we collapse? */ qp = &q->q[q->n-1]; switch (qp->type) { case QPT_RECT: if (q->d.x == 0) { if ( (qp->u.rect.c1.y == q->e1.y) && (qp->u.rect.c2.y == q->e2.y) ) { if (qp->u.rect.c1.x-1 == q->e1.x) { qp->u.rect.c1.x --; goto addlinedone; } if (qp->u.rect.c2.x+1 == q->e1.x) { qp->u.rect.c2.x ++; goto addlinedone; } } } else if (q->d.y == 0) { if ( (qp->u.rect.c1.x == q->e1.x) && (qp->u.rect.c2.x == q->e2.x) ) { if (qp->u.rect.c1.y-1 == q->e1.y) { qp->u.rect.c1.y --; goto addlinedone; } if (qp->u.rect.c2.y+1 == q->e1.y) { qp->u.rect.c2.y ++; goto addlinedone; } } } break; case QPT_LINE: if (q->d.x == 0) { if ( (qp->u.line.e1.x == qp->u.line.e2.x) && (qp->u.line.e1.y == q->e1.y) && (qp->u.line.e2.y == q->e2.y) ) { if (qp->u.line.e1.x+1 == q->e1.x) { qp->type = QPT_RECT; qp->u.rect.c1.x = q->e1.x - 1; qp->u.rect.c1.y = q->e1.y; qp->u.rect.c2 = q->e2; goto addlinedone; } else if (qp->u.line.e1.x-1 == q->e1.x) { qp->type = QPT_RECT; qp->u.rect.c1 = q->e1; qp->u.rect.c2.x = q->e1.x + 1; qp->u.rect.c2.y = q->e2.y; goto addlinedone; } } } else if (q->d.y == 0) { if ( (qp->u.line.e1.y == qp->u.line.e2.y) && (qp->u.line.e1.x == q->e1.x) && (qp->u.line.e2.x == q->e2.x) ) { if (qp->u.line.e1.y+1 == q->e1.y) { qp->type = QPT_RECT; qp->u.rect.c1.x = q->e1.x; qp->u.rect.c1.y = q->e1.y - 1; qp->u.rect.c2 = q->e2; goto addlinedone; } else if (qp->u.line.e1.y-1 == q->e1.y) { qp->type = QPT_RECT; qp->u.rect.c1 = q->e1; qp->u.rect.c2.x = q->e2.x; qp->u.rect.c2.y = q->e1.y + 1; goto addlinedone; } } } break; } } if (q->n >= QSZ) q_flush_(q); qp = &q->q[q->n++]; qp->type = QPT_LINE; qp->u.line.e1 = q->e1; qp->u.line.e2 = q->e2; addlinedone:; q->tpts = 1; q->e1.x = x; q->e1.y = y; } break; } } static void q_flush(PTQ *q) { switch (q->tpts) { case 0: break; default: q_pt(q->e1.x+2,q->e1.y+3,q); break; } q->tpts = 0; q_flush_(q); } static void update_fsbits(int x, int y, int w, int h) { int i; int j; int v; for (i=0;i xsize) xp = xsize - fullsize.winw; if (xp < 0) xp = 0; if (yp+fullsize.winh > ysize) yp = ysize - fullsize.winh; if (yp < 0) yp = 0; set_pan_xypos(xp,yp); } static void pan_draglimit(void) { if (pan_px+pan_pw > pan_picw) pan_px = pan_picw - pan_pw; if (pan_px < 0) pan_px = 0; if (pan_py+pan_ph > pan_pich) pan_py = pan_pich - pan_ph; if (pan_py < 0) pan_py = 0; } static void init_pan_pic(void) { pan_pixmap = XCreatePixmap(disp,pan.win,1,1,1); pan_grey_data = malloc(1); pan_bw_data = rop_create(1,1); pan_picw = -1; pan_pich = -1; } static void resize_pan_pic(int w, int h) { if ((w != pan_picw) || (h != pan_pich)) { XFreePixmap(disp,pan_pixmap); rop_free(pan_bw_data); free(pan_grey_data); flip_pan_rect(pan_px,pan_py,pan_pw,pan_ph); pan_pixmap = XCreatePixmap(disp,pan.win,w,h,1); pan_picw = w; pan_pich = h; pan_grey_data = malloc(h*w); pan_bw_data = rop_create(w,h); to_pan_p(); flip_pan_rect(pan_px,pan_py,pan_pw,pan_ph); reload_pan(); } } static int pan_ctl(TPL *t, int op, ...) { va_list ap; int rv; rv = 0; va_start(ap,op); switch (op) { case TPL_CTL_EVENT: { XEvent *e; e = va_arg(ap,XEvent *); switch (e->type) { case Expose: if (e->xexpose.window == t->win) { redraw_pan(e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height); rv = 1; } break; case ButtonPress: if (e->xbutton.window == t->win) { if (pan_dragging) { pan_dragging = 0; flip_pan_rect(pan_px,pan_py,pan_pw,pan_ph); pan_px = e->xbutton.x - pan_dox; pan_py = e->xbutton.y - pan_doy; pan_draglimit(); from_pan_p(); to_pan_p(); flip_pan_rect(pan_px,pan_py,pan_pw,pan_ph); redraw_fullsize(0,0,fullsize.winw,fullsize.winh); } else if (point_in_rect(e->xbutton.x,e->xbutton.y,pan_px,pan_py,pan_pw,pan_ph)) { pan_dragging = 1; pan_dox = e->xbutton.x - pan_px; pan_doy = e->xbutton.y - pan_py; } rv = 1; } break; case MotionNotify: if (e->xmotion.window == t->win) { if (pan_dragging) { Window root_ret; Window child_ret; int rootx_ret; int rooty_ret; int winx_ret; int winy_ret; unsigned int mask_ret; if (XQueryPointer(disp,t->win,&root_ret,&child_ret,&rootx_ret,&rooty_ret,&winx_ret,&winy_ret,&mask_ret) == True) { flip_pan_rect(pan_px,pan_py,pan_pw,pan_ph); pan_px = winx_ret - pan_dox; pan_py = winy_ret - pan_doy; pan_draglimit(); flip_pan_rect(pan_px,pan_py,pan_pw,pan_ph); } } rv = 1; } break; } } break; case TPL_CTL_INIT: { int w; int h; w = va_arg(ap,int); h = va_arg(ap,int); init_pan_pic(); t->winw = 0; t->winh = 0; (*pan.ctl)(t,TPL_CTL_RESIZE,w,h); } break; case TPL_CTL_RESIZE: { int w; int h; w = va_arg(ap,int); h = va_arg(ap,int); if ((w != t->winw) || (h != t->winh)) { int winw; int winh; int winx; int winy; t->winw = w; t->winh = h; xysize(xsize,ysize,w,h,&winw,&winh); winx = (w - winw) / 2; winy = (h - winh) / 2; XMoveResizeWindow(disp,t->win,winx,winy,winw,winh); resize_pan_pic(winw,winh); } } break; } va_end(ap); return(rv); } static void reset_mag_bufs(void) { free(mag_cur); free(mag_want); mag_cur = malloc(mag_w*mag_h); mag_want = malloc(mag_w*mag_h); bzero(mag_cur,mag_w*mag_h); bzero(mag_want,mag_w*mag_h); } static void clear_mag_cur(int x, int y, int w, int h) { if (! rect_intersect(&x,&y,&w,&h,0,0,mag_w,mag_h)) return; if ((x == 0) && (w == mag_w)) { bzero(mag_cur+(y*mag_w),mag_w*h); } else { char *mcp; mcp = mag_cur + (y * mag_w) + x; for (;h>0;h--) { bzero(mcp,w); mcp += mag_w; } } } static void scroll_mag_cur(int dx, int dy) { int fy; int ty; int ny; int ey; int fx; int tx; int nx; if (dy >= 0) { fy = mag_h - 1 - dy; ty = mag_h - 1; ny = mag_h - dy; ey = dy; dy = -1; } else { fy = - dy; ty = 0; ny = mag_h + dy; ey = - dy; dy = 1; } if (dx >= 0) { fx = 0; tx = dx; nx = mag_w - dx; } else { fx = - dx; tx = 0; nx = mag_w + dx; } for (;ny>0;ny--) { bcopy(mag_cur+(fy*mag_w)+fx,mag_cur+(ty*mag_w)+tx,nx); if (dx < 0) bzero(mag_cur+(ty*mag_w)+nx,-dx); else bzero(mag_cur+(ty*mag_w),dx); fy += dy; ty += dy; } for (;ey>0;ey--) { bzero(mag_cur+(ty*mag_w),mag_w); ty += dy; } mag_dirty = 1; } static void reload_mag_want(void) { int x; int y; char *mwp; mwp = mag_want; for (y=0;y= sizeof(fat_pix)/sizeof(fat_pix[0]))) { panic(something); } switch (v) { case 0: if (erase) { XClearArea(disp,fatbits.win,(x*magfactor)+2,(y*magfactor)+2,magfactor-3,magfactor-3,False); } break; case MB_PIX: XFillRectangle(disp,fatbits.win,fwdgc,(x*magfactor)+2,(y*magfactor)+2,magfactor-3,magfactor-3); break; case MB_TMP: if (erase) { XCopyArea(disp,fat_pix[v],fatbits.win,fwdgc,0,0,magfactor,magfactor,x*magfactor,y*magfactor); } else { XFillRectangle(disp,fatbits.win,fwdgc,(x*magfactor)+tmpoff,(y*magfactor)+tmpoff,tmpsize,tmpsize); } break; case MB_PIX|MB_TMP: XCopyArea(disp,fat_pix[v],fatbits.win,fwdgc,0,0,magfactor,magfactor,x*magfactor,y*magfactor); break; default: panic(something); break; } } #endif static void redraw_fatbits(int x, int y, int w, int h) { int x0; int x1; int y0; int y1; x0 = x / magfactor; x1 = (x+w-1) / magfactor; y0 = y / magfactor; y1 = (y+h-1) / magfactor; clear_mag_cur(x0,y0,x1+1-x0,y1+1-y0); mag_dirty = 1; } static void set_fatbits_temp(int x, int y, int w, int h, int v) { int x0; int w0; char *mwp; if ((w < 1) || (h < 1)) return; if (! rect_intersect(&x,&y,&w,&h,mag_x,mag_y,mag_w,mag_h)) return; x -= mag_x; y -= mag_y; x0 = x; w0 = w; for (;h>0;y++,h--) { mwp = mag_want + (y * mag_w) + x0; for (x=x0,w=w0;w>0;x++,w--) { if (v) *mwp++ |= MB_TMP; else *mwp++ &= ~MB_TMP; } } mag_dirty = 1; } static void set_fatbits_pixel(int x, int y, int v) { if ((x < mag_x) || (y < mag_y) || (x >= mag_x+mag_w) || (y >= mag_y+mag_h)) return; x -= mag_x; y -= mag_y; if (v) mag_want[(y*mag_w)+x] |= MB_TMP; else mag_want[(y*mag_w)+x] &= ~MB_TMP; mag_dirty = 1; } static void (*w_l_fn)(XY); static void w_l_o0(XY p) { (*w_l_fn)((XY){x: p.x,y: p.y}); } static void w_l_o1(XY p) { (*w_l_fn)((XY){x: p.y,y: p.x}); } static void w_l_o2(XY p) { (*w_l_fn)((XY){x:-p.y,y: p.x}); } static void w_l_o3(XY p) { (*w_l_fn)((XY){x:-p.x,y: p.y}); } static void w_l_o4(XY p) { (*w_l_fn)((XY){x:-p.x,y:-p.y}); } static void w_l_o5(XY p) { (*w_l_fn)((XY){x:-p.y,y:-p.x}); } static void w_l_o6(XY p) { (*w_l_fn)((XY){x: p.y,y:-p.x}); } static void w_l_o7(XY p) { (*w_l_fn)((XY){x: p.x,y:-p.y}); } static void walk_line(XY p1, XY p2, void (*fn)(XY)) { XY d; void (*call)(XY); int syn; XY p; int acc; d.x = abs(p2.x-p1.x); d.y = abs(p2.y-p1.y); if (!d.x && !d.y) { (*fn)(p1); return; } w_l_fn = fn; if (p1.x <= p2.x) if (p1.y <= p2.y) if (d.y <= d.x) syn = 0; else syn = 1; else if (d.y <= d.x) syn = 2; else syn = 3; else if (p1.y <= p2.y) if (d.y <= d.x) syn = 4; else syn = 5; else if (d.y <= d.x) syn = 6; else syn = 7; if (syn & 4) { p1.x = - p1.x; p2.x = - p2.x; } if (syn & 2) { p1.y = - p1.y; p2.y = - p2.y; } #define SWAP(v1,v2) do { int t; t = v1; v1 = v2; v2 = t; } while (0) if (syn & 1) { SWAP(d.x,d.y); SWAP(p1.x,p1.y); SWAP(p2.x,p2.y); } #undef SWAP call = ((void (*[])(XY)) { w_l_o0, w_l_o1, w_l_o7, w_l_o6, w_l_o3, w_l_o2, w_l_o4, w_l_o5 })[syn]; p.y = p1.y; acc = d.x / 2; for (p.x=p1.x;p.x<=p2.x;p.x++) { (*call)(p); acc += d.y; if (acc >= d.x) { acc -= d.x; p.y ++; } } } static void walk_circle(int cx, int cy, int radsq, void (*fn)(XY)) { int x; int y; int err; x = (isqrt(radsq) | (cx & 1)) + 2; y = cy & 1; err = (x * x) + (y * y) - radsq; while (abs(err) >= abs(err-(4*x)+4)) { err -= (4 * x) - 4; x -= 2; } do { (*fn)((XY){x:(cx+x)/2,y:(cy+y)/2}); (*fn)((XY){x:(cx-x)/2,y:(cy+y)/2}); if (y) { (*fn)((XY){x:(cx+x)/2,y:(cy-y)/2}); (*fn)((XY){x:(cx-x)/2,y:(cy-y)/2}); } if (x != y) { (*fn)((XY){x:(cx+y)/2,y:(cy+x)/2}); (*fn)((XY){x:(cx+y)/2,y:(cy-x)/2}); if (y) { (*fn)((XY){x:(cx-y)/2,y:(cy+x)/2}); (*fn)((XY){x:(cx-y)/2,y:(cy-x)/2}); } } err += (y * 4) + 4; y += 2; if (abs(err) >= abs(err-(4*x)+4)) { err -= (4 * x) - 4; x -= 2; } } while (y <= x); } static void walk_ellipse(int cx, int cy, int rx, int ry, void (*fn)(XY)) { int x; int y; int err; int e1; int e2; int e3; if (ry == 0) { for (x=-rx;x<=rx;x+=2) (*fn)((XY){x:(cx+x)/2,y:cy/2}); return; } if (rx == 0) { for (y=-ry;y<=ry;y+=2) (*fn)((XY){x:cx/2,y:(cy+y)/2}); return; } x = (rx | (cx & 1)) + 2; y = cy & 1; rx *= rx; ry *= ry; err = (ry * x * x) + (rx * y * y) - (rx * ry); while (abs(err) >= abs(err+(ry*4*(1-x)))) { err += ry * 4 * (1 - x); x -= 2; } while (1) { (*fn)((XY){x:(cx+x)/2,y:(cy+y)/2}); if (x) (*fn)((XY){x:(cx-x)/2,y:(cy+y)/2}); if (y) { (*fn)((XY){x:(cx+x)/2,y:(cy-y)/2}); if (x) (*fn)((XY){x:(cx-x)/2,y:(cy-y)/2}); } if ((x == 0) && (err >= 0)) break; e1 = err + (rx * 4 * (y + 1)); e3 = err + (ry * 4 * (1 - x)); e2 = e1 + e3 - err; e1 = abs(e1); e2 = abs(e2); e3 = abs(e3); if ((e1 >= e2) || (e1 >= e3)) { err += ry * 4 * (1 - x); x -= 2; } if ((e3 >= e2) || (e3 >= e1)) { err += rx * 4 * (y + 1); y += 2; } if (x < 0) break; } } static void start_choose(void (*fn)(int, ...)) { choose_pt = fn; (*choose_pt)(CP_INIT); set_tpl_cursor(&fullsize,xhaircurs); set_tpl_cursor(&fatbits,xhaircurs); XSelectInput(disp,fatbits.win,fatbits.eventmask|PointerMotionMask|PointerMotionHintMask); } static void restart_choose(void (*fn)(int, ...)) { choose_pt = fn; set_tpl_cursor(&fullsize,xhaircurs); set_tpl_cursor(&fatbits,xhaircurs); XSelectInput(disp,fatbits.win,fatbits.eventmask|PointerMotionMask|PointerMotionHintMask); } static void end_choose(void) { choose_pt = 0; set_tpl_cursor(&fullsize,arrowcurs); set_tpl_cursor(&fatbits,arrowcurs); XSelectInput(disp,fatbits.win,fatbits.eventmask); } static void abort_choose(void) { (*choose_pt)(CP_ABORT); end_choose(); } static void mag_xylimit(void) { if (mag_x < 0) mag_x = 0; else if (mag_x+mag_w > xsize) mag_x = xsize - mag_w; if (mag_y < 0) mag_y = 0; else if (mag_y+mag_h > ysize) mag_y = ysize - mag_h; } static void add_mag_scroll(int dx, int dy) { MAGSCROLL *ms; ms = malloc(sizeof(MAGSCROLL)); ms->serial = XNextRequest(disp); ms->dx = dx; ms->dy = dy; *mag_scroll_tail = ms; mag_scroll_tail = &ms->link; ms->link = 0; ms_sum_dx += dx; ms_sum_dy += dy; } static void move_mag_rect(int x, int y) { int dx; int dy; dx = mag_x - x; dy = mag_y - y; if (dx || dy) { dx = mag_x; dy = mag_y; if (choose_pt) (*choose_pt)(CP_HIDEFAT); flip_mag_rect(mag_x,mag_y,mag_w,mag_h); mag_x = x; mag_y = y; mag_xylimit(); flip_mag_rect(mag_x,mag_y,mag_w,mag_h); dx -= mag_x; dy -= mag_y; if (dx || dy) { if ((abs(dx) >= mag_w) || (abs(dy) >= mag_h)) { XClearArea(disp,fatbits.win,0,0,0,0,False); reload_mag_want(); redraw_fatbits(0,0,fatbits.winw,fatbits.winh); } else { add_mag_scroll(dx*magfactor,dy*magfactor); XCopyArea(disp,fatbits.win,fatbits.win,fwdgc,-dx*magfactor,-dy*magfactor,(mag_w*magfactor)+1,(mag_h*magfactor)+1,0,0); reload_mag_want(); scroll_mag_cur(dx,dy); } } if (choose_pt) (*choose_pt)(CP_DRAWFAT); } } static void remove_pan(void) { remove_tpl(&pan); } static void call_main_menu(Time time) { if (pan.flags & TPL_F_UP) { main_menu.entries[MME_UPDATE_PANNER].flags &= ~ME_F_UNSELECTABLE; } else { main_menu.entries[MME_UPDATE_PANNER].flags |= ME_F_UNSELECTABLE; } menu_choose(&main_menu,time); } static int fullsize_ctl(TPL *t, int op, ...) { va_list ap; int rv; rv = 0; va_start(ap,op); switch (op) { case TPL_CTL_EVENT: { XEvent *e; e = va_arg(ap,XEvent *); switch (e->type) { case Expose: if (e->xexpose.window == t->win) { redraw_fullsize(e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height); rv = 1; } break; case ButtonPress: if (e->xbutton.window == t->win) { if (e->xbutton.button == Button3) { if (choose_pt) { (*choose_pt)(CP_RBUTTON,e); } else { call_main_menu(e->xbutton.time); } } else if ((e->xbutton.button == Button2) || !choose_pt) { XUngrabPointer(disp,e->xbutton.time); if (mag_dragging) { mag_dragging = 0; move_mag_rect(e->xbutton.x-mag_dox,e->xbutton.y-mag_doy); } else if (point_in_rect(e->xbutton.x,e->xbutton.y,mag_x,mag_y,mag_w,mag_h)) { mag_dragging = 1; mag_dox = e->xbutton.x - mag_x; mag_doy = e->xbutton.y - mag_y; } else { move_mag_rect(e->xbutton.x-(mag_w/2),e->xbutton.y-(mag_h/2)); } } else { XUngrabPointer(disp,e->xbutton.time); (*choose_pt)(CP_CHOSEN,e->xbutton.x,e->xbutton.y); } rv = 1; } break; case MotionNotify: if (e->xmotion.window == t->win) { if (mag_dragging || choose_pt) { Window root_ret; Window child_ret; int rootx_ret; int rooty_ret; int winx_ret; int winy_ret; unsigned int mask_ret; if (XQueryPointer(disp,t->win,&root_ret,&child_ret,&rootx_ret,&rooty_ret,&winx_ret,&winy_ret,&mask_ret) == True) { if (mag_dragging) { move_mag_rect(winx_ret-mag_dox,winy_ret-mag_doy); } if (choose_pt) { (*choose_pt)(CP_MOVE,winx_ret,winy_ret); } } } rv = 1; } break; } } break; case TPL_CTL_INIT: { int w; int h; w = va_arg(ap,int); h = va_arg(ap,int); XResizeWindow(disp,t->win,xsize,ysize); XGrabButton(disp,AnyButton,AnyModifier,t->win,False,ButtonPressMask|ButtonReleaseMask|PointerMotionMask|PointerMotionHintMask,GrabModeSync,GrabModeAsync,None,None); pan_xpos = 0; pan_ypos = 0; t->winw = 0; t->winh = 0; (*fullsize.ctl)(t,TPL_CTL_RESIZE,w,h); } break; case TPL_CTL_RESIZE: { int w; int h; int xpos; int ypos; w = va_arg(ap,int); h = va_arg(ap,int); if ((w != t->winw) || (h != t->winh)) { t->winw = w; t->winh = h; xpos = pan_xpos; ypos = pan_ypos; if (w >= xsize) { xpos = (xsize - w) / 2; } else if (xpos < 0) { xpos = 0; } else if (xpos+w > xsize) { xpos = xsize - w; } if (h >= ysize) { ypos = (ysize - h) / 2; } else if (pan_ypos < 0) { ypos = 0; } else if (ypos+h > ysize) { ypos = ysize - h; } set_pan_xypos(xpos,ypos); flip_pan_rect(pan_px,pan_py,pan_pw,pan_ph); to_pan_p(); flip_pan_rect(pan_px,pan_py,pan_pw,pan_ph); if ((w < xsize) || (h < ysize)) { ensure_pan(); } else { remove_pan(); } } } break; } va_end(ap); return(rv); } static void pop_mag_scroll(unsigned long int s) { while (mag_scroll && ((long int)(mag_scroll->serial-s) <= 0)) { MAGSCROLL *ms; ms_sum_dx -= mag_scroll->dx; ms_sum_dy -= mag_scroll->dy; ms = mag_scroll->link; free(mag_scroll); mag_scroll = ms; } if (! mag_scroll) { mag_scroll_tail = &mag_scroll; if (ms_sum_dx || ms_sum_dy) panic("leftover sum in pop_mag_scroll"); } } static void scrolled_mag_redraw(int x, int y, int w, int h) { redraw_fatbits(x+ms_sum_dx,y+ms_sum_dy,w,h); } static void update_fatbits(int x, int y, int w, int h) { int i; int j; if (x < mag_x) { w -= mag_x - x; x = mag_x; } if (y < mag_y) { h -= mag_y - y; y = mag_y; } if (x+w > mag_x+mag_w) w = mag_x + mag_w - x; if (y+h > mag_y+mag_h) h = mag_y + mag_h - y; if ((w < 1) || (h < 1)) return; for (i=0;i= mag_w) x = mag_w - 1; if (y < 0) y = 0; else if (y >= mag_h) y = mag_h - 1; x += mag_x; y += mag_y; #undef x #undef y } static void fatbits_motion(int x, int y) { int p; int v; fatwin_to_pos(&x,&y); if ((x == mag_lastx) && (y == mag_lasty) && !choose_pt) return; mag_lastx = x; mag_lasty = y; if (choose_pt) { Window root_ret; Window child_ret; int rootx_ret; int rooty_ret; int winx_ret; int winy_ret; unsigned int mask_ret; if (XQueryPointer(disp,fatbits.win,&root_ret,&child_ret,&rootx_ret,&rooty_ret,&winx_ret,&winy_ret,&mask_ret) == True) { fatwin_to_pos(&winx_ret,&winy_ret); (*choose_pt)(CP_MOVE,winx_ret,winy_ret); } } else { p = rop_getpixel(bits,x,y); switch (mag_lastb) { case Button1: v = rop_ropbit(fatbits_alu,1,p); break; case Button2: v = rop_ropbit(fatbits_alu,0,p); break; default: v = p; break; } if (v != p) { rop_putpixel(bits,x,y,v); update_fatbits(x,y,1,1); update_fsbits(x,y,1,1); drawing_flush(); } } } static void fatbits_button(int x, int y, unsigned int button) { if (choose_pt) { x /= magfactor; y /= magfactor; if (x < 0) x = 0; else if (x >= mag_w) x = mag_w - 1; if (y < 0) y = 0; else if (y >= mag_h) y = mag_h - 1; (*choose_pt)(CP_CHOSEN,x+mag_x,y+mag_y); } else { mag_lastx = -1; mag_lasty = -1; mag_lastb = button; fatbits_motion(x,y); } } static int fatbits_ctl(TPL *t, int op, ...) { va_list ap; int rv; rv = 0; va_start(ap,op); switch (op) { case TPL_CTL_EVENT: { XEvent *e; e = va_arg(ap,XEvent *); switch (e->type) { case Expose: if (e->xexpose.window == t->win) { pop_mag_scroll(e->xexpose.serial); /* paranoia */ scrolled_mag_redraw(e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height); rv = 1; } break; case GraphicsExpose: if (e->xgraphicsexpose.drawable == (Drawable)t->win) { pop_mag_scroll(e->xgraphicsexpose.serial); scrolled_mag_redraw(e->xgraphicsexpose.x,e->xgraphicsexpose.y,e->xgraphicsexpose.width,e->xgraphicsexpose.height); rv = 1; } break; case NoExpose: if (e->xnoexpose.drawable == (Drawable)t->win) { pop_mag_scroll(e->xnoexpose.serial); rv = 1; } break; case ButtonPress: if (e->xbutton.window == t->win) { switch (e->xbutton.button) { case Button1: case Button2: XUngrabPointer(disp,e->xbutton.time); fatbits_button(e->xbutton.x,e->xbutton.y,e->xbutton.button); break; case Button3: if (choose_pt) { (*choose_pt)(CP_RBUTTON,e); } else { call_main_menu(e->xbutton.time); } break; default: XUngrabPointer(disp,e->xbutton.time); mag_lastb = (Button1|Button2|Button3|Button4|Button5)+1; break; } rv = 1; } break; case MotionNotify: if (e->xmotion.window == t->win) { fatbits_motion(e->xmotion.x,e->xmotion.y); rv = 1; } break; } } break; case TPL_CTL_INIT: { int w; int h; mag_scroll = 0; mag_scroll_tail = &mag_scroll; ms_sum_dx = 0; ms_sum_dy = 0; w = va_arg(ap,int); h = va_arg(ap,int); mag_w = -1; mag_h = -1; mag_cur = malloc(1); mag_want = malloc(1); XGrabButton(disp,AnyButton,AnyModifier,t->win,False,ButtonPressMask|ButtonReleaseMask|PointerMotionMask|PointerMotionHintMask,GrabModeSync,GrabModeAsync,None,None); (*fatbits.ctl)(t,TPL_CTL_RESIZE,w,h); } break; case TPL_CTL_RESIZE: { int w; int h; w = va_arg(ap,int); h = va_arg(ap,int); if ((w != t->winw) || (h != t->winh)) { int mx; int my; int mw; int mh; t->winw = w; t->winh = h; mw = (w - 1) / magfactor; mh = (h - 1) / magfactor; if (mw > xsize) mw = xsize; if (mh > ysize) mh = ysize; if ((mw != mag_w) || (mh != mag_h)) { mx = mag_x; my = mag_y; if (mx+mw > xsize) mx = xsize - mw; if (my+mh > ysize) my = ysize - mh; if (choose_pt) (*choose_pt)(CP_HIDEFAT); flip_mag_rect(mag_x,mag_y,mag_w,mag_h); mag_x = mx; mag_y = my; mag_w = mw; mag_h = mh; flip_mag_rect(mag_x,mag_y,mag_w,mag_h); mw = (mw * magfactor) + 1; mh = (mh * magfactor) + 1; XMoveResizeWindow(disp,fatbits.win,(w-mw)/2,(h-mh)/2,mw,mh); XClearArea(disp,fatbits.win,0,0,0,0,False); reset_mag_bufs(); reload_mag_want(); if (! ininit) redraw_fatbits(0,0,fatbits.winw,fatbits.winh); if (choose_pt) (*choose_pt)(CP_DRAWFAT); } else { XMoveWindow(disp,fatbits.win,(w-((mw*magfactor)+1))/2,(h-((mh*magfactor)+1))/2); } } } break; } va_end(ap); return(rv); } static void perform_rectangle(int x, int y, int w, int h) { if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } if (x+w > xsize) w = xsize - x; if (y+h > ysize) h = ysize - y; if ((w < 1) || (h < 1)) return; rop_rop(0,0,0,w,h,bits,x,y,draw_alu); update_fatbits(x,y,w,h); update_fsbits(x,y,w,h); } static void perform_one_pixel(XY p) { perform_rectangle(p.x,p.y,1,1); } static void sel_select(int x1, int y1, int x2, int y2) { int t; if (sel.area) rop_free(sel.area); if (x1 > x2) { t = x1; x1 = x2; x2 = t; } if (y1 > y2) { t = y1; y1 = y2; y2 = t; } sel.w = (x2 - x1) + 1; sel.h = (y2 - y1) + 1; sel.area = rop_create(sel.w,sel.h); rop_rop(bits,x1,y1,sel.w,sel.h,sel.area,0,0,ROP_SRC); } static void sel_paste(int x, int y) { int fx; int fy; int fw; int fh; if (! sel.area) return; rop_rop(sel.area,0,0,sel.w,sel.h,bits,x,y,copy1_alu); redraw_fullsize(x,y,sel.w,sel.h); fx = mag_x; fy = mag_y; fw = mag_w; fh = mag_h; if (rect_intersect(&fx,&fy,&fw,&fh,x,y,sel.w,sel.h)) { reload_mag_want(); mag_dirty = 1; } } static void sel_init(void) { #if 0 int i; int j; sel_pm = XCreatePixmap(disp,rootwin,sel_w,sel_h,1); XSetFunction(disp,bitgc,GXcopy); XSetForeground(disp,bitgc,0L); XFillRectangle(disp,sel_pm,bitgc,0,0,sel_w,sel_h); XSetForeground(disp,bitgc,1L); for (i=0;i 1) { n = 1; while (1) { m = n << 1; if (m >= ysize) { bcopy(&sc_v[0],&sc_v[n],(ysize-n)*sizeof(sc_v[0])); break; } else { bcopy(&sc_v[0],&sc_v[n],n*sizeof(sc_v[0])); n = m; } } } } static void solid_convex_record(XY p) { if ((p.y < 0) || (p.y >= ysize) || (p.x < 0) || (p.x >= xsize)) return; if (p.y < sc_y0) sc_y0 = p.y; if (p.y > sc_y1) sc_y1 = p.y; if (p.x < sc_v[p.y][0]) sc_v[p.y][0] = p.x; if (p.x > sc_v[p.y][1]) sc_v[p.y][1] = p.x; } static void solid_convex_fill(void (*fn)(int, int, int)) { int y; for (y=sc_y0;y<=sc_y1;y++) if (sc_v[y][0] <= sc_v[y][1]) (*fn)(y,sc_v[y][0],sc_v[y][1]); } static void mmsel__draw_(int kind, int x, int y) { switch (kind & DK__OP) { default: panic("bad op %#x",kind); break; case DK_DRAW: case DK_ERASE: if (kind & DK_FULLSIZE) { XFillRectangle(disp,fullsize.win,xorgc,x,y,sel.w,sel.h); #if 0 XCopyPlane(disp,sel_pm,fullsize.win,xorgc,0,0,sel.w,sel.h,x,y,1); #endif } if (kind & DK_FATBITS) { int xx; int yy; int w; int h; int val; xx = x; yy = y; w = sel.w; h = sel.h; val = ((kind & DK__OP) == DK_DRAW); if (rect_intersect(&xx,&yy,&w,&h,mag_x,mag_y,mag_w,mag_h)) { int i; int j; if (! pdisp1_set) { pdisp1_alu = pdisp_alu; pdisp1_fn = pdisp_fn; } for (i=0;i 1) if (w > 1) XDrawRectangle(disp,fullsize.win,xorgc,x,y,w-1,h-1); else XDrawLine(disp,fullsize.win,xorgc,x,y,x,y+h-1); else if (w > 1) XDrawLine(disp,fullsize.win,xorgc,x,y,x+w-1,y); else XDrawPoint(disp,fullsize.win,xorgc,x,y); } if (kind & DK_FATBITS) { int val; val = ((kind & DK__OP) == DK_DRAW); if (rect_overlap(x,y,w,h,mag_x,mag_y,mag_w,mag_h)) { set_fatbits_temp(x,y,w,1,val); if (h > 1) { set_fatbits_temp(x,y+h-1,w,1,val); set_fatbits_temp(x,y+1,1,h-2,val); if (w > 1) set_fatbits_temp(x+w-1,y+1,1,h-2,val); } } } break; case DK_PERFORM: perform_rectangle(x,y,w,1); if (h > 1) perform_rectangle(x,y+h-1,w,1); if (h > 2) { perform_rectangle(x,y+1,1,h-2); if (w > 1) perform_rectangle(x+w-1,y+1,1,h-2); } drawing_flush(); break; } } static void mmhr__draw(int kind) { mmhr__draw_(kind,mm_c_p1.x,mm_c_p1.y,mm_c_last.x,mm_c_last.y); } static void mmsr__draw_(int kind, int x1, int y1, int x2, int y2) { int x; int y; int w; int h; w = x2 - x1; if (w < 0) { w = - w; x = x2; } else { x = x1; } h = y2 - y1; if (h < 0) { h = - h; y = y2; } else { y = y1; } w ++; h ++; switch (kind & DK__OP) { default: panic("bad op %#x",kind); break; case DK_DRAW: case DK_ERASE: if (kind & DK_FULLSIZE) { XFillRectangle(disp,fullsize.win,xorgc,x,y,w,h); } if (kind & DK_FATBITS) { int val; val = ((kind & DK__OP) == DK_DRAW); if (rect_overlap(x,y,w,h,mag_x,mag_y,mag_w,mag_h)) { set_fatbits_temp(x,y,w,h,val); } } break; case DK_PERFORM: perform_rectangle(x,y,w,h); drawing_flush(); break; } } static void mmsr__draw(int kind) { mmsr__draw_(kind,mm_c_p1.x,mm_c_p1.y,mm_c_last.x,mm_c_last.y); } #if 0 static void mmr__draw(int kind) { switch (kind) { case 0: case 1: XFillRectangle(disp,fullsize.win,xorgc,mm_c_lastx,mm_c_lasty,mm_c_w,mm_c_h); /* fall through */ case 2: case 3: kind &= 1; if (rect_overlap(mm_c_lastx,mm_c_lasty,mm_c_w,mm_c_h,mag_x,mag_y,mag_w,mag_h)) { set_fatbits_temp(mm_c_lastx,mm_c_lasty,mm_c_w,mm_c_h,kind); } break; case 4: { int i; int j; for (i=mm_c_w-1;i>=0;i--) { for (j=mm_c_h-1;j>=0;j--) { perform_one_pixel(mm_c_lastx+i,mm_c_lasty+j); } } drawing_flush(); } break; } } #endif static void mml__draw(int kind) { int x; int y; int w; int h; w = mm_c_last.x - mm_c_p1.x; if (w < 0) { w = - w; x = mm_c_last.x; } else { x = mm_c_p1.x; } h = mm_c_last.y - mm_c_p1.y; if (h < 0) { h = - h; y = mm_c_last.y; } else { y = mm_c_p1.y; } w ++; h ++; switch (kind & DK__OP) { default: panic("bad op %#x",kind); break; case DK_DRAW: case DK_ERASE: if (kind & DK_FULLSIZE) { XDrawLine(disp,fullsize.win,xorgc,mm_c_p1.x,mm_c_p1.y,mm_c_last.x,mm_c_last.y); } if (kind & DK_FATBITS) { if (rect_overlap(x,y,w,h,mag_x,mag_y,mag_w,mag_h)) { mm_c_draw_val = ((kind & DK__OP) == DK_DRAW); walk_line(mm_c_p1,mm_c_last,mm_c_draw_pixel); } } break; case DK_PERFORM: walk_line(mm_c_p1,mm_c_last,perform_one_pixel); drawing_flush(); break; } } static void mmhcr__draw(int kind) { int x; int y; int r2; int r; x = mm_c_last.x - mm_c_p1.x; y = mm_c_last.y - mm_c_p1.y; r2 = (x * x) + (y * y); r = isqrt(r2); switch (kind & DK__OP) { default: panic("bad op %#x",kind); break; case DK_DRAW: case DK_ERASE: if (kind & DK_FULLSIZE) { XDrawArc(disp,fullsize.win,xorgc,mm_c_p1.x-r,mm_c_p1.y-r,r*2,r*2,0,360*64); } if (kind & DK_FATBITS) { if (rect_overlap(mm_c_p1.x-r-1,mm_c_p1.y-r-1,(r+1)*2,(r+1)*2,mag_x,mag_y,mag_w,mag_h)) { mm_c_draw_val = ((kind & DK__OP) == DK_DRAW); walk_circle(2*mm_c_p1.x,2*mm_c_p1.y,4*r2,mm_c_draw_pixel); } } break; case DK_PERFORM: walk_circle(2*mm_c_p1.x,2*mm_c_p1.y,4*r2,perform_one_pixel); drawing_flush(); break; } } static void mmscr__draw(int kind) { int x; int y; int r2; int r; x = mm_c_last.x - mm_c_p1.x; y = mm_c_last.y - mm_c_p1.y; r2 = (x * x) + (y * y); r = isqrt(r2); switch (kind & DK__OP) { default: panic("bad op %#x",kind); break; case DK_DRAW: case DK_ERASE: if (kind & DK_FULLSIZE) { XFillArc(disp,fullsize.win,xorgc,mm_c_p1.x-r,mm_c_p1.y-r,r*2,r*2,0,360*64); } if (kind & DK_FATBITS) { if (rect_overlap(mm_c_p1.x-r-1,mm_c_p1.y-r-1,(r+1)*2,(r+1)*2,mag_x,mag_y,mag_w,mag_h)) { mm_c_draw_val = ((kind & DK__OP) == DK_DRAW); solid_convex_init(); walk_circle(2*mm_c_p1.x,2*mm_c_p1.y,4*r2,solid_convex_record); solid_convex_fill(set_fatbits_row); } } break; case DK_PERFORM: solid_convex_init(); walk_circle(2*mm_c_p1.x,2*mm_c_p1.y,4*r2,solid_convex_record); solid_convex_fill(perform_row); drawing_flush(); break; } } static void mmhcb__draw(int kind) { int x; int y; int w; int h; w = mm_c_last.x - mm_c_p1.x; if (w < 0) { w = - w; x = mm_c_last.x; } else { x = mm_c_p1.x; } h = mm_c_last.y - mm_c_p1.y; if (h < 0) { h = - h; y = mm_c_last.y; } else { y = mm_c_p1.y; } w ++; h ++; if (h > w) { y += (h - w) / 2; h = w; } else if (w > h) { x += (w - h) / 2; w = h; } switch (kind & DK__OP) { default: panic("bad op %#x",kind); break; case DK_DRAW: case DK_ERASE: if (kind & DK_FULLSIZE) { XDrawArc(disp,fullsize.win,xorgc,x,y,w,h,0,360*64); } if (kind & DK_FATBITS) { if (rect_overlap(x,y,w,h,mag_x,mag_y,mag_w,mag_h)) { mm_c_draw_val = ((kind & DK__OP) == DK_DRAW); walk_circle(x+x+w-1,y+y+w-1,(w-1)*(w-1),mm_c_draw_pixel); } } break; case DK_PERFORM: walk_circle(x+x+w-1,y+y+w-1,(w-1)*(w-1),perform_one_pixel); drawing_flush(); break; } } static void mmscb__draw(int kind) { int x; int y; int w; int h; w = mm_c_last.x - mm_c_p1.x; if (w < 0) { w = - w; x = mm_c_last.x; } else { x = mm_c_p1.x; } h = mm_c_last.y - mm_c_p1.y; if (h < 0) { h = - h; y = mm_c_last.y; } else { y = mm_c_p1.y; } w ++; h ++; if (h > w) { y += (h - w) / 2; h = w; } else if (w > h) { x += (w - h) / 2; w = h; } switch (kind & DK__OP) { default: panic("bad op %#x",kind); break; case DK_DRAW: case DK_ERASE: if (kind & DK_FULLSIZE) { XFillArc(disp,fullsize.win,xorgc,x,y,w,h,0,360*64); } if (kind & DK_FATBITS) { if (rect_overlap(x,y,w,h,mag_x,mag_y,mag_w,mag_h)) { mm_c_draw_val = ((kind & DK__OP) == DK_DRAW); solid_convex_init(); walk_circle(x+x+w-1,y+y+w-1,(w-1)*(w-1),solid_convex_record); solid_convex_fill(set_fatbits_row); } } break; case DK_PERFORM: solid_convex_init(); walk_circle(x+x+w-1,y+y+w-1,(w-1)*(w-1),solid_convex_record); solid_convex_fill(perform_row); drawing_flush(); break; } } static void mmheb__draw(int kind) { int x; int y; int w; int h; w = mm_c_last.x - mm_c_p1.x; if (w < 0) { w = - w; x = mm_c_last.x; } else { x = mm_c_p1.x; } h = mm_c_last.y - mm_c_p1.y; if (h < 0) { h = - h; y = mm_c_last.y; } else { y = mm_c_p1.y; } w ++; h ++; switch (kind & DK__OP) { default: panic("bad op %#x",kind); break; case DK_DRAW: case DK_ERASE: if (kind & DK_FULLSIZE) { XDrawArc(disp,fullsize.win,xorgc,x,y,w,h,0,360*64); } if (kind & DK_FATBITS) { if (rect_overlap(x,y,w,h,mag_x,mag_y,mag_w,mag_h)) { mm_c_draw_val = ((kind & DK__OP) == DK_DRAW); walk_ellipse(x+x+w-1,y+y+h-1,w-1,h-1,mm_c_draw_pixel); } } break; case DK_PERFORM: walk_ellipse(x+x+w-1,y+y+h-1,w-1,h-1,perform_one_pixel); drawing_flush(); break; } } static void mmseb__draw(int kind) { int x; int y; int w; int h; w = mm_c_last.x - mm_c_p1.x; if (w < 0) { w = - w; x = mm_c_last.x; } else { x = mm_c_p1.x; } h = mm_c_last.y - mm_c_p1.y; if (h < 0) { h = - h; y = mm_c_last.y; } else { y = mm_c_p1.y; } w ++; h ++; switch (kind & DK__OP) { default: panic("bad op %#x",kind); break; case DK_DRAW: case DK_ERASE: if (kind & DK_FULLSIZE) { XFillArc(disp,fullsize.win,xorgc,x,y,w,h,0,360*64); } if (kind & DK_FATBITS) { if (rect_overlap(x,y,w,h,mag_x,mag_y,mag_w,mag_h)) { mm_c_draw_val = ((kind & DK__OP) == DK_DRAW); solid_convex_init(); walk_ellipse(x+x+w-1,y+y+h-1,w-1,h-1,solid_convex_record); solid_convex_fill(set_fatbits_row); } } break; case DK_PERFORM: solid_convex_init(); walk_ellipse(x+x+w-1,y+y+h-1,w-1,h-1,solid_convex_record); solid_convex_fill(perform_row); drawing_flush(); break; } } static void mms_clear_lock(void) { sel_lock.x = -1; sel_lock.y = -1; } static void mms_apply_lock(void) { if (sel_lock.x >= 0) mm_c_last.x = sel_lock.x; if (sel_lock.y >= 0) mm_c_last.y = sel_lock.y; } static void mms__draw(int kind) { mms_apply_lock(); switch (sel_render1_set?sel_render1:sel_render) { default: panic("bad sel redition"); break; case SELR_SR: mmsr__draw_(kind,mm_c_p1.x,mm_c_p1.y,mm_c_last.x,mm_c_last.y); break; case SELR_HR: mmhr__draw_(kind,mm_c_p1.x,mm_c_p1.y,mm_c_last.x,mm_c_last.y); break; } } static void do_boxgrab(void (*fn)(int, int), Time time) { int x; int y; Window ret_root; Window ret_child; int ret_rootx; int ret_rooty; int ret_winx; int ret_winy; unsigned int ret_mask; if (boxgrabdone) panic("recursive boxgrab"); if (time == CurrentTime) time = cur_time(); if (XGrabPointer(disp,rootwin,False,ButtonPressMask|ButtonReleaseMask|PointerMotionMask|PointerMotionHintMask|EnterWindowMask|LeaveWindowMask,GrabModeSync,GrabModeAsync,None,None,time) != GrabSuccess) { boxgrabdone = 0; return; } if (XQueryPointer(disp,rootwin,&ret_root,&ret_child,&ret_rootx,&ret_rooty,&ret_winx,&ret_winy,&ret_mask) != True) { XUngrabPointer(disp,CurrentTime); boxgrabdone = 0; return; } boxgrabdone = fn; x = ret_rootx - 60; y = ret_rooty - 60; if (x+120 > width) x = width - 120; if (y+120 > height) y = height - 120; if (x < 0) x = 0; if (y < 0) y = 0; x -= borderwidth; y -= borderwidth; XMoveWindow(disp,boxgrabtop,x,y); XMapRaised(disp,boxgrabtop); if (XGrabPointer(disp,boxgrabtop,False,ButtonPressMask|ButtonReleaseMask|PointerMotionMask|PointerMotionHintMask|EnterWindowMask|LeaveWindowMask,GrabModeSync,GrabModeAsync,boxgrabtop,None,time) != GrabSuccess) { XUnmapWindow(disp,boxgrabtop); XUngrabPointer(disp,CurrentTime); boxgrabdone = 0; return; } XAllowEvents(disp,AsyncPointer,time); } static void select_menu_regrab_boxgrab(int x, int y) { int t; #define SWAP(a,b) do { t=a; a=b; b=t; } while (0) if ((x == 0) && (y == 0)) return; sel_lock = (XY){x:-1,y:-1}; if (x < 0) { if (mm_c_last.x > mm_c_p1.x) SWAP(mm_c_last.x,mm_c_p1.x); } else if (x > 0) { if (mm_c_last.x < mm_c_p1.x) SWAP(mm_c_last.x,mm_c_p1.x); } else { sel_lock.x = mm_c_last.x; } if (y < 0) { if (mm_c_last.y > mm_c_p1.y) SWAP(mm_c_last.y,mm_c_p1.y); } else if (y > 0) { if (mm_c_last.y < mm_c_p1.y) SWAP(mm_c_last.y,mm_c_p1.y); } else { sel_lock.y = mm_c_last.y; } #undef SWAP } MENU_FN_DEFN(select_menu_regrab) { do_boxgrab(select_menu_regrab_boxgrab,t); } MENU_FN_DEFN(select_menu_display_one) { mms__draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); sel_render1 = arg_i; sel_render1_set = 1; mms__draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); } MENU_FN_DEFN(select_menu_display_all) { mms__draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); sel_render = arg_i; sel_render1_set = 0; mms__draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); } MENU_FN_DEFN(select_menu_abort) { abort_choose(); } static void mms__ctl(int op, ...) { va_list ap; mms_apply_lock(); va_start(ap,op); switch (op) { default: panic("bad op %d",op); break; case MMC2_DONE: sel_select(mm_c_p1.x,mm_c_p1.y,mm_c_last.x,mm_c_last.y); break; case MMC2_RBUTTON: { XEvent *e; e = va_arg(ap,XEvent *); menu_choose(&select_menu,e->xbutton.time); } break; } va_end(ap); } static void mmp__choose(int op, ...) { va_list ap; int x; int y; XEvent *e; va_start(ap,op); switch (op) { default: case CP_INIT: sel_init(); mm_c_last = (XY){x:0,y:0}; mm_c_off = (XY){x:0,y:0}; mmsel__draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); copy1_alu_set = 0; copy1_alu = copy_alu; /* choose_move0(); */ break; case CP_ABORT: mmsel__draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); sel_done(); break; case CP_CHOSEN: mmsel__draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); x = va_arg(ap,int) + mm_c_off.x; y = va_arg(ap,int) + mm_c_off.y; end_choose(); sel_paste(x,y); sel_done(); break; case CP_MOVE: mmsel__draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); mm_c_last.x = va_arg(ap,int) + mm_c_off.x; mm_c_last.y = va_arg(ap,int) + mm_c_off.y; mmsel__draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); break; case CP_HIDEFAT: mmsel__draw(DK_ERASE|DK_FATBITS); break; case CP_DRAWFAT: mmsel__draw(DK_DRAW|DK_FATBITS); break; case CP_HIDEFULL: mmsel__draw(DK_ERASE|DK_FULLSIZE); break; case CP_DRAWFULL: mmsel__draw(DK_DRAW|DK_FULLSIZE); break; case CP_RBUTTON: e = va_arg(ap,XEvent *); menu_choose(&paste_menu,e->xbutton.time); break; } va_end(ap); } static void mmp__reanchor(int op, ...) { va_list ap; XY p; XEvent *e; va_start(ap,op); switch (op) { case CP_CHOSEN: p.x = va_arg(ap,int); p.y = va_arg(ap,int); mm_c_off = xysub(mm_c_last,p); restart_choose(mmp__choose); break; case CP_HIDEFAT: mmsel__draw(DK_ERASE|DK_FATBITS); break; case CP_DRAWFAT: mmsel__draw(DK_DRAW|DK_FATBITS); break; case CP_HIDEFULL: mmsel__draw(DK_ERASE|DK_FULLSIZE); break; case CP_DRAWFULL: mmsel__draw(DK_DRAW|DK_FULLSIZE); break; case CP_RBUTTON: e = va_arg(ap,XEvent *); XUngrabPointer(disp,e->xbutton.time); restart_choose(mmp__choose); break; } va_end(ap); } static void mmc__two_2(int op, ...) { va_list ap; XEvent *e; va_start(ap,op); switch (op) { case CP_INIT: mm_c_last = mm_c_p1; (*mm_c_twodraw)(DK_DRAW|DK_FULLSIZE|DK_FATBITS); break; case CP_ABORT: (*mm_c_twodraw)(DK_ERASE|DK_FULLSIZE|DK_FATBITS); break; case CP_CHOSEN: (*mm_c_twodraw)(DK_ERASE|DK_FULLSIZE|DK_FATBITS); mm_c_last.x = va_arg(ap,int); mm_c_last.y = va_arg(ap,int); end_choose(); if (mm_c_twoctl) (*mm_c_twoctl)(MMC2_DONE); else (*mm_c_twodraw)(DK_PERFORM); break; case CP_MOVE: (*mm_c_twodraw)(DK_ERASE|DK_FULLSIZE|DK_FATBITS); mm_c_last.x = va_arg(ap,int); mm_c_last.y = va_arg(ap,int); (*mm_c_twodraw)(DK_DRAW|DK_FULLSIZE|DK_FATBITS); break; case CP_HIDEFAT: (*mm_c_twodraw)(DK_ERASE|DK_FATBITS); break; case CP_DRAWFAT: (*mm_c_twodraw)(DK_DRAW|DK_FATBITS); break; case CP_HIDEFULL: (*mm_c_twodraw)(DK_ERASE|DK_FULLSIZE); break; case CP_DRAWFULL: (*mm_c_twodraw)(DK_DRAW|DK_FULLSIZE); break; case CP_RBUTTON: e = va_arg(ap,XEvent *); if (mm_c_twoctl) { (*mm_c_twoctl)(MMC2_RBUTTON,e); } else { XUngrabPointer(disp,e->xbutton.time); abort_choose(); } break; } va_end(ap); } static void mmc__two_1(int op, ...) { va_list ap; XEvent *e; va_start(ap,op); switch (op) { case CP_CHOSEN: mm_c_p1.x = va_arg(ap,int); mm_c_p1.y = va_arg(ap,int); end_choose(); start_choose(mmc__two_2); break; case CP_MOVE: break; case CP_RBUTTON: e = va_arg(ap,XEvent *); XUngrabPointer(disp,e->xbutton.time); abort_choose(); break; } va_end(ap); } static void choose_two(void (*draw)(int), void (*ctl)(int, ...)) { mm_c_twodraw = draw; mm_c_twoctl = ctl; start_choose(mmc__two_1); } MENU_FN_DEFN(any_menu_new_menu) { menu_choose(arg_v,t); } MENU_FN_DEFN(op_menu_setop) { *op_menu_var = arg_i; if (op_menu_fn) (*op_menu_fn)(); } MENU_FN_DEFN(choose_op_menu_buttons) { op_menu_var = &fatbits_alu; menu_choose(&full_op_menu,t); } MENU_FN_DEFN(choose_op_menu_draw) { op_menu_var = &draw_alu; menu_choose(&half_op_menu,t); } MENU_FN_DEFN(choose_op_menu_copy) { op_menu_var = ©_alu; menu_choose(&full_op_menu,t); } MENU_FN_DEFN(paste_menu_abort) { abort_choose(); } MENU_FN_DEFN(paste_menu_reanchor) { end_choose(); start_choose(mmp__reanchor); } static void paste_set_copy1(void) { op_menu_fn = 0; copy1_alu_set = 1; } MENU_FN_DEFN(paste_menu_setop_one) { op_menu_var = ©1_alu; op_menu_fn = paste_set_copy1; menu_choose(&full_op_menu,t); } static void paste_clear_copy1(void) { op_menu_fn = 0; copy1_alu = copy_alu; copy1_alu_set = 0; } MENU_FN_DEFN(paste_menu_setop_all) { op_menu_var = ©_alu; op_menu_fn = paste_clear_copy1; menu_choose(&full_op_menu,t); } static void paste_display_use_alu(void) { mmsel__draw(DK_ERASE|DK_FATBITS); op_menu_fn = 0; #if 0 /* This likely gets miscompiled - the similar construct in paste_display_set_hr definitely does. */ (pdisp1_setting ? pdisp1_fn : pdisp_fn) = paste_disp_alu; (pdisp1_setting ? pdisp1_alu : pdisp_alu) = pdisp_set_alu; #else if (pdisp1_setting) { pdisp1_fn = pdsalu_fn; pdisp1_alu = pdisp_set_alu ^ pdsalu_xor; } else { pdisp_fn = pdsalu_fn; pdisp_alu = pdisp_set_alu ^ pdsalu_xor; } #endif pdisp1_set = pdisp1_setting; mmsel__draw(DK_DRAW|DK_FATBITS); } MENU_FN_DEFN(paste_display_set_alu) { op_menu_var = &pdisp_set_alu; op_menu_fn = paste_display_use_alu; pdsalu_xor = arg_i; pdsalu_fn = (int (*)(int, int)) arg_fn; menu_choose(&full_op_menu,t); } MENU_FN_DEFN(paste_display_set_hr) { mmsel__draw(DK_ERASE|DK_FATBITS); #if 0 /* This gets miscompiled! */ (pdisp1_setting ? pdisp1_fn : pdisp_fn) = paste_disp_hr; #else if (pdisp1_setting) pdisp1_fn = paste_disp_hr; else pdisp_fn = paste_disp_hr; #endif pdisp1_set = pdisp1_setting; mmsel__draw(DK_DRAW|DK_FATBITS); } MENU_FN_DEFN(paste_menu_setdisp_one) { pdisp1_setting = 1; menu_choose(&paste_display_menu,t); } MENU_FN_DEFN(paste_menu_setdisp_all) { pdisp1_setting = 0; menu_choose(&paste_display_menu,t); } static void area_flip_x(ROP_AREA *a, ROP_AREA *t1, ROP_AREA *t2, ROP_AREA *t3) { int aw; int ah; int maskoff; int w; int halfw; aw = rop_w(a); ah = rop_h(a); rop_rop(0,0,0,aw,ah,t3,0,0,ROP_SET); w = aw; maskoff = aw; while (w > 1) { halfw = w >> 1; rop_rop(t3,0,0,aw,ah,t3,halfw,0,ROP_DST&~ROP_SRC); if (w-halfw != halfw) rop_rop(t3,0,0,aw,ah,t3,w-halfw,0,ROP_DST&~ROP_SRC); rop_rop(t3,0,0,aw,ah,t3,maskoff,0,ROP_DST|ROP_SRC); rop_rop(a,0,0,aw,ah,t1,0,0,ROP_SRC); rop_rop(t3,0,0,aw,ah,t1,0,0,ROP_DST&ROP_SRC); rop_rop(a,w-halfw,0,aw,ah,t2,0,0,ROP_SRC); rop_rop(t3,0,0,aw,ah,t2,0,0,ROP_DST&ROP_SRC); rop_rop(t3,0,0,aw,ah,a,0,0,ROP_DST&~ROP_SRC); rop_rop(t3,0,0,aw,ah,a,w-halfw,0,ROP_DST&~ROP_SRC); rop_rop(t1,0,0,aw-(w-halfw),ah,a,w-halfw,0,ROP_DST|ROP_SRC); rop_rop(t2,0,0,aw-(w-halfw),ah,a,0,0,ROP_DST|ROP_SRC); maskoff = w - halfw; w = halfw; } } static void area_flip_y(ROP_AREA *a, ROP_AREA *t1, ROP_AREA *t2, ROP_AREA *t3) { int aw; int ah; int maskoff; int h; int halfh; aw = rop_w(a); ah = rop_h(a); rop_rop(0,0,0,aw,ah,t3,0,0,ROP_SET); h = ah; maskoff = ah; while (h > 1) { halfh = h >> 1; rop_rop(t3,0,0,aw,ah,t3,0,halfh,ROP_DST&~ROP_SRC); if (h-halfh != halfh) rop_rop(t3,0,0,aw,ah,t3,0,h-halfh,ROP_DST&~ROP_SRC); rop_rop(t3,0,0,aw,ah,t3,0,maskoff,ROP_DST|ROP_SRC); rop_rop(a,0,0,aw,ah,t1,0,0,ROP_SRC); rop_rop(t3,0,0,aw,ah,t1,0,0,ROP_DST&ROP_SRC); rop_rop(a,0,h-halfh,aw,ah,t2,0,0,ROP_SRC); rop_rop(t3,0,0,aw,ah,t2,0,0,ROP_DST&ROP_SRC); rop_rop(t3,0,0,aw,ah,a,0,0,ROP_DST&~ROP_SRC); rop_rop(t3,0,0,aw,ah,a,0,h-halfh,ROP_DST&~ROP_SRC); rop_rop(t1,0,0,aw,ah-(h-halfh),a,0,h-halfh,ROP_DST|ROP_SRC); rop_rop(t2,0,0,aw,ah-(h-halfh),a,0,0,ROP_DST|ROP_SRC); maskoff = h - halfh; h = halfh; } } MENU_FN_DEFN(paste_flip_menu_reflect_x) { ROP_AREA *tmp; ROP_AREA *tmp2; ROP_AREA *mask; mmsel__draw(DK_ERASE|DK_FATBITS); tmp = rop_create(sel.w,sel.h); tmp2 = rop_create(sel.w,sel.h); mask = rop_create(sel.w,sel.h); area_flip_x(sel.area,tmp,tmp2,mask); rop_free(tmp); rop_free(tmp2); rop_free(mask); mmsel__draw(DK_DRAW|DK_FATBITS); } MENU_FN_DEFN(paste_flip_menu_reflect_y) { ROP_AREA *tmp; ROP_AREA *tmp2; ROP_AREA *mask; mmsel__draw(DK_ERASE|DK_FATBITS); tmp = rop_create(sel.w,sel.h); tmp2 = rop_create(sel.w,sel.h); mask = rop_create(sel.w,sel.h); area_flip_y(sel.area,tmp,tmp2,mask); rop_free(tmp); rop_free(tmp2); rop_free(mask); mmsel__draw(DK_DRAW|DK_FATBITS); } MENU_FN_DEFN(paste_flip_menu_reflect_xy) { int o; ROP_AREA *a; mmsel__draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); o = mm_c_off.x; mm_c_off.x = mm_c_off.y; mm_c_off.y = o; a = rop_transpose(sel.area,0,ROP_TR_XY); rop_free(sel.area); sel.w = rop_w(a); sel.h = rop_h(a); sel.area = a; mmsel__draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); } MENU_FN_DEFN(paste_flip_menu_reflect_nxy) { int o; ROP_AREA *a; mmsel__draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); o = - (sel.w - 1 - (-mm_c_off.x)); mm_c_off.x = - (sel.h - 1 - (-mm_c_off.y)); mm_c_off.y = o; a = rop_transpose(sel.area,0,ROP_TR_NXY); rop_free(sel.area); sel.w = rop_w(a); sel.h = rop_h(a); sel.area = a; mmsel__draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); } MENU_FN_DEFN(paste_flip_menu_rotate) { switch (arg_i) { default: panic("bad arg %d to paste_flip_menu_rotate",arg_i); break; case 1: /* cw */ { int o; ROP_AREA *a; mmsel__draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); o = mm_c_off.x; mm_c_off.x = - (sel.h - 1 - (-mm_c_off.y)); mm_c_off.y = o; a = rop_transpose(sel.area,0,ROP_TR_CW); rop_free(sel.area); sel.w = rop_w(a); sel.h = rop_h(a); sel.area = a; mmsel__draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); } break; case 2: /* 180° */ { ROP_AREA *tmp; ROP_AREA *tmp2; ROP_AREA *mask; mmsel__draw(DK_ERASE|DK_FATBITS); tmp = rop_create(sel.w,sel.h); tmp2 = rop_create(sel.w,sel.h); mask = rop_create(sel.w,sel.h); area_flip_x(sel.area,tmp,tmp2,mask); area_flip_y(sel.area,tmp,tmp2,mask); rop_free(tmp); rop_free(tmp2); rop_free(mask); mmsel__draw(DK_DRAW|DK_FATBITS); } break; case 3: /* ccw */ { int o; ROP_AREA *a; mmsel__draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); o = - (sel.w - 1 - (-mm_c_off.x)); mm_c_off.x = mm_c_off.y; mm_c_off.y = o; a = rop_transpose(sel.area,0,ROP_TR_CCW); rop_free(sel.area); sel.w = rop_w(a); sel.h = rop_h(a); sel.area = a; mmsel__draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); } break; } } MENU_FN_DEFN(main_menu_quit) { XSync(disp,True); exit(0); } MENU_FN_DEFN(main_menu_update_panner) { reload_pan(); } MENU_FN_DEFN(main_menu_select) { sel_render1_set = 0; mms_clear_lock(); choose_two(mms__draw,mms__ctl); } MENU_FN_DEFN(main_menu_paste) { if (sel.area) { start_choose(mmp__choose); } else { XBell(disp,0); } } static void reset_selection_name(SELECTION *s) { free(s->name); asprintf(&s->name,"%d x %d",s->w,s->h); } static void sel_free(SELECTION *s, int seltoo) { free(s->name); if (s->area) rop_free(s->area); if (seltoo) { free(s); } else { s->name = 0; s->area = 0; } } static void sel_assign(SELECTION *dst, SELECTION *src) { if (dst->area) rop_free(dst->area); dst->w = src->w; dst->h = src->h; dst->area = rop_create(dst->w,src->h); rop_rop(src->area,0,0,src->w,src->h,dst->area,0,0,ROP_SRC); } static void selection_replace(int inx) { if ((inx < 0) || (inx >= nselections)) abort(); if (! sel.area) { XBell(disp,0); return; } sel_assign(selections[inx],&sel); reset_selection_name(selections[inx]); selections_gen ++; } static void selection_drop(int inx) { if ((inx < 0) || (inx >= nselections)) abort(); sel_free(selections[inx],1); nselections --; if (inx < nselections) bcopy(&selections[inx+1],&selections[inx],(nselections-inx)*sizeof(*selections)); selections_gen ++; } static void selection_use(int inx) { if ((inx < 0) || (inx >= nselections)) abort(); sel_assign(&sel,selections[inx]); } MENU_FN_DEFN(selections_menu_save) { SELECTION *s; if (! sel.area) { XBell(disp,0); return; } s = malloc(sizeof(SELECTION)); s->area = 0; sel_assign(s,&sel); s->name = 0; reset_selection_name(s); if (nselections >= aselections) selections = realloc(selections,(aselections=nselections+8)*sizeof(*selections)); selections[nselections++] = s; selections_gen ++; } MENU_FN_DEFN(selections_menu_replace) { if ((nselections < 1) || !sel.area) { XBell(disp,0); return; } selection_op = &selection_replace; menu_choose(&selections_menu_no_head,t); } MENU_FN_DEFN(selections_menu_drop) { if (nselections < 1) { XBell(disp,0); return; } selection_op = &selection_drop; menu_choose(&selections_menu_no_head,t); } MENU_FN_DEFN(selections_menu_pick) { (*selection_op)(arg_i); } static void selections_menu_refresh_menu(MENU *m, MENUENT *head, int nhead) { int i; MENUENT *e; for (i=m->nentries-1;i>=0;i--) free(m->entries[i].textfree); free(m->entries); m->nentries = nhead + nselections; m->entries = malloc(m->nentries*sizeof(MENUENT)); for (i=nhead-1;i>=0;i--) m->entries[i] = head[i]; for (i=nselections-1;i>=0;i--) { e = &m->entries[nhead+i]; asprintf(&e->textfree,"%d: %s",i+1,strdup(selections[i]->name)); e->text = e->textfree; e->handler = &selections_menu_pick; e->flags = 0; e->arg_ptr = 0; e->arg_int = i; e->arg_fxn = 0; } m->flags &= ~MENU_F_DIDINIT; } static void selections_menu_refresh(void) { if (selections_menu_gen == selections_gen) return; selections_menu_refresh_menu(&selections_menu_with_head,&selections_menu_head[0],SELECTIONS_MENU_HEAD_N); selections_menu_refresh_menu(&selections_menu_no_head,0,0); selections_menu_gen = selections_gen; } MENU_FN_DEFN(main_menu_selections) { selections_menu_refresh(); selection_op = &selection_use; menu_choose(&selections_menu_with_head,t); } MENU_FN_DEFN(main_menu_twoop) { choose_two((void (*)(int))arg_fn,0); } static void fp_lines(void (*line)(XY, XY)) { int i; for (i=1;i s[1]) s[1] = p.x; } else { s[0] = p.x; s[1] = p.x; seen[p.y] = 1; } } p1 = mm_c_poly[lno]; p2 = mm_c_poly[fp_next[lno]]; y0 = min(p1.y,p2.y); h = max(p1.y,p2.y) + 1 - y0; if (h > nseen) { nseen = h; free(seen); seen = malloc(nseen); } bzero(seen,h); slabs = malloc(h*sizeof(*fp_slabs[0])); fp_slabs[lno] = slabs; fp_y0[lno] = y0; fp_y1[lno] = y0 + h - 1; walk_line(p1,p2,pix); } static void fp_app_x(int x, int d) { if (fp_xcn >= fp_xca) { fp_xca = fp_xcn + 8; fp_xc = realloc(fp_xc,fp_xca*sizeof(*fp_xc)); } fp_xc[fp_xcn++] = (FPXC){x:x,d:d}; } static void fp_sort_x(void) { #define U(x) (((x)-1)>>1) #define L(x) (((x)<<1)+1) #define R(x) (((x)+1)<<1) int x; int n; FPXC t; static void down(int i) { int l; int r; int s; FPXC t; t = fp_xc[i]; while (1) { l = L(i); r = R(i); if ((l < n) && (fp_xc[l].x > t.x)) { if ((r < n) && (fp_xc[r].x > t.x)) { s = (fp_xc[l].x > fp_xc[r].x) ? l : r; } else { s = l; } } else { if ((r < n) && (fp_xc[r].x > t.x)) { s = r; } else { fp_xc[i] = t; return; } } fp_xc[i] = fp_xc[s]; i = s; } } if (fp_xcn < 2) return; n = fp_xcn; for (x=U(fp_xcn-1);x>=0;x--) down(x); while (n > 1) { t = fp_xc[0]; fp_xc[0] = fp_xc[--n]; fp_xc[n] = t; down(0); } #undef U #undef L #undef R } static void ranges_clear(RANGES *r) { r->n = 0; } static void ranges_add(RANGES *r, int a, int b) { int i; int j; static void ensure(int n) { if (n > r->a) r->v = realloc(r->v,(r->a=n+8)*sizeof(*r->v)); } if (b < a) abort(); for (i=0;;i++) { if (i >= r->n) { ensure(r->n+1); r->v[r->n][0] = a; r->v[r->n][1] = b; r->n ++; return; } if (a <= r->v[i][1]+1) break; } if (b+1 < r->v[i][0]) { ensure(r->n+1); bcopy(r->v+i,r->v+i+1,(r->n-i)*sizeof(*r->v)); r->v[i][0] = a; r->v[i][1] = b; r->n ++; return; } if (a < r->v[i][0]) r->v[i][0] = a; for (j=i+1;(jn)&&(b+1>=r->v[j][0]);j++) ; j --; if (b < r->v[j][1]) b = r->v[j][1]; if (j != i) { if (j < r->n-1) bcopy(r->v+j+1,r->v+i+1,(r->n-1-j)*sizeof(*r->v)); r->n -= j - i; } r->v[i][1] = b; } static void ranges_map(RANGES *r, void (*fn)(int, int)) { int i; for (i=0;in;i++) (*fn)(r->v[i][0],r->v[i][1]); } MENU_FN_DEFN(polygon_menu_done) { int y0; int y1; int y; int i; int i0; int j; int wn; int on; int px; end_choose(); fp_draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); switch (arg_i) { default: panic("bad arg_i %d to polygon_menu_done",arg_i); break; case FP_EO: case FP_NZ: break; } mm_c_draw_val = 1; if (mm_c_poly_n < 1) return; if (mm_c_poly_n == 1) { perform_one_pixel(mm_c_poly[0]); return; } if (mm_c_poly_n == 2) { walk_line(mm_c_poly[0],mm_c_poly[1],perform_one_pixel); return; } y0 = mm_c_poly[0].y; y1 = y0; for (i=mm_c_poly_n-1;i>0;i--) { y = mm_c_poly[i].y; if (y < y0) y0 = y; if (y > y1) y1 = y; } if (y0 == y1) { int x0; int x1; int x; x0 = mm_c_poly[0].x; x1 = x0; for (i=mm_c_poly_n-1;i>0;i--) { x = mm_c_poly[i].x; if (x < x0) x0 = x; if (x > x1) x1 = x; } walk_line((XY){x:x0,y:y0},(XY){x:x1,y:y0},perform_one_pixel); return; } if (mm_c_poly_n > fp_np_a) { fp_np_a = mm_c_poly_n; free(fp_prev); free(fp_next); fp_prev = malloc(fp_np_a*sizeof(int)); fp_next = malloc(fp_np_a*sizeof(int)); } for (i=mm_c_poly_n-1;i>0;i--) { fp_prev[i] = i - 1; fp_next[i-1] = i; } fp_prev[0] = mm_c_poly_n - 1; fp_next[mm_c_poly_n-1] = 0; fp_slabs = malloc(mm_c_poly_n*sizeof(*fp_slabs)); fp_y0 = malloc(mm_c_poly_n*sizeof(*fp_y0)); fp_y1 = malloc(mm_c_poly_n*sizeof(*fp_y0)); for (i=mm_c_poly_n-1;i>=0;i--) fp_compute_slabs(i); for (y=y0;y<=y1;y++) { static void perform_slab(int a, int b) { perform_rectangle(a,y,b+1-a,1); } for (i0=0;mm_c_poly[i0].y==y;i0++) ; ranges_clear(&fp_xr); fp_xcn = 0; i = i0; do { for (j=fp_next[i];mm_c_poly[j].y==y;j=fp_next[j]) ; if (mm_c_poly[i].y > y) { if (mm_c_poly[j].y < y) fp_app_x(fp_slabs[i][y-fp_y0[i]][0],-1); } else if (mm_c_poly[i].y < y) { if (mm_c_poly[j].y > y) fp_app_x(fp_slabs[i][y-fp_y0[i]][0],1); } else { panic("didn't stop on equal y"); } i = j; } while (i != i0); fp_sort_x(); wn = 0; on = 0; for (i=0;i=0;i--) { j = fp_next[i]; if ((fp_y0[i] <= y) && (fp_y1[i] >= y)) { int *s; s = &fp_slabs[i][y-fp_y0[i]][0]; ranges_add(&fp_xr,s[0],s[1]); } } ranges_map(&fp_xr,perform_slab); } drawing_flush(); free(fp_y0); free(fp_y1); for (i=mm_c_poly_n-1;i>=0;i--) free(fp_slabs[i]); free(fp_slabs); } MENU_FN_DEFN(polygon_menu_del_last) { if (mm_c_poly_n > 0) { fp_draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); mm_c_poly_n --; fp_draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); } } static void mm__fp(int op, ...) { va_list ap; XEvent *e; va_start(ap,op); switch (op) { case CP_INIT: mm_c_poly_n = 0; break; case CP_ABORT: fp_draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); break; case CP_CHOSEN: fp_draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); if (mm_c_poly_n >= mm_c_poly_a) { mm_c_poly_a = mm_c_poly_n + 8; mm_c_poly = realloc(mm_c_poly,mm_c_poly_a*sizeof(XY)); } mm_c_last.x = va_arg(ap,int); mm_c_last.y = va_arg(ap,int); fp_savepoint(mm_c_last); fp_draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); break; case CP_MOVE: fp_draw(DK_ERASE|DK_FULLSIZE|DK_FATBITS); mm_c_last.x = va_arg(ap,int); mm_c_last.y = va_arg(ap,int); fp_draw(DK_DRAW|DK_FULLSIZE|DK_FATBITS); break; case CP_HIDEFAT: fp_draw(DK_ERASE|DK_FATBITS); break; case CP_DRAWFAT: fp_draw(DK_DRAW|DK_FATBITS); break; case CP_HIDEFULL: fp_draw(DK_ERASE|DK_FULLSIZE); break; case CP_DRAWFULL: fp_draw(DK_DRAW|DK_FULLSIZE); break; case CP_RBUTTON: e = va_arg(ap,XEvent *); menu_choose(&polygon_menu,e->xbutton.time); break; } va_end(ap); } MENU_FN_DEFN(main_menu_fillpoly) { start_choose(mm__fp); } MENU_FN_DEFN(main_menu_pixcleanup) { int x; int y; unsigned char *rall; unsigned char *r1; unsigned char *r2; unsigned char *r3; unsigned char *r4; unsigned char *rt; rall = malloc((xsize+2)*4); bzero(rall,(xsize+2)*4); r1 = rall; r2 = r1 + xsize + 2; r3 = r2 + xsize + 2; r4 = r3 + xsize + 2; for (x=0;x> 3; { unsigned char row[nb]; for (y=0;y