/* * cedit: Cursor Editor for Plan9 Operation System * * version: 2.0 * usage: cedit [-l] * * You can edit cursor under GUI. * The result is written to stdout in C/Alef data format. * The option '-l' enable to read the data from stdin. * You are recomended to work under 8-bit color mode. * * Don't transfer this program under text mode. * This program include ½ (half) UTF codes in winit(): * They may be mistransfered. * * BUGS: * If you invoke cedit under noscroll mode, cedit window will not * disappear at exit; the reason is that exit is blocked. * * Kenar (Kenji Arisawa) * E-mail: arisawa@aichi-u.ac.jp * ftp://plan9.aichi-u.ac.jp * http://plan9.aichi-u.ac.jp */ #include #include #define USED if void usage(){ fprint(2,"usage: cedit [-l]\n"); exits("usage"); } void err(byte *fmt, ...){ byte buf[128]; sprint(buf, fmt, ...); fprint(2,"%s\n", buf); exits(buf); } /* * I now know only the color data for 8 bits colors. * Please let me know other data * if someone extend them to other colors. * They may be under default color map: * 1-bit color: gwhite=0, gblack=1, ggray=0; * 2-bit color: gwhite=0, gblack=3, ggray=1; * 4-bit color: gwhite=0, gblack=15, ggray=3; * 8-bit color: gwhite=0, gblack=255, ggray=85; * We can get screen.ldepth values 0, 1, 2, 3 corresponding * to these bits value respectively. */ int pblack[4] = { 1, 3, 15, 255,}; /* * Only gwhite is given here. * Other data, i.e., gblack and ggray will be given in main(). * I assumed in this work that gray is gblack/3 but this may * not be true for 4-bit color. * * Cedit under 1-bit color may not be confortable. */ int gwhite = 0; int gblack; int ggray; /* * We can find some types of cursor data * in /sys/src/cmd/8½/main.c */ Cursor sweep0 = { {-7, -7}, {0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0}, {0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE, 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00} }; Cursor boxcurs = { {-7, -7}, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00} }; Cursor sight = { {-7, -7}, {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,}, {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,} }; Cursor whitearrow = { {0, 0}, {0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xC0, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0xC0, 0xFF, 0xE0, 0xE7, 0xF0, 0xE3, 0xF8, 0xC1, 0xFC, 0x00, 0xFE, 0x00, 0x7F, 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x08,}, {0xFF, 0xE0, 0xFF, 0xE0, 0xC1, 0xC0, 0xC3, 0x00, 0xC3, 0x00, 0xC1, 0x80, 0xD8, 0xC0, 0xFC, 0x60, 0xE6, 0x30, 0xE3, 0x18, 0xC1, 0x8C, 0x00, 0xC6, 0x00, 0x63, 0x00, 0x36, 0x00, 0x1C, 0x00, 0x08,} }; int mousepid; /* * The following g functions may be useful for other programs. * They are fundamental graphic parts * and independent of other parts of this program. * Arguments are organized as: position, size, options * Position is upper-left corner of a figure and is relative to the * current window. * Left-upper corner of the window is (0,0). * * Macros Rt, Rs and Dr are prepared. * Rt(p,s) translate (position, size) to rectangle * Rs(p,s) also translate (position, size) to rectangle, but co-ordinate * of the rectangle is screen wise. * Dr(r) get size of rectangle r. */ #define Rt(p,s) (Rectangle)(p, add(p, s)) #define Rs(p, s) raddp(Rt(p,s), screen.r.min) #define Dr(r) sub(r.max,r.min) #define gputs(p,size,str) glabel(p, size, str, CL) enum{ UL = 0, UC = 1, UR = 2, CL = 3, CC = 4, CR = 5, LL = 6, LC = 7, LR = 8, }; void glabel(Point p, Point size, byte *s, int align){ Point ssize; int ax, ay; ax = align %3; ay = align / 3; if(!s) s= ""; ssize = strsize(font, s); switch(ax){ case 0: break; case 1: p.x += (size.x - ssize.x)/2; break; case 2: p.x += size.x - ssize.x; break; } switch(ay){ case 0: break; case 1: p.y += (size.y - ssize.y)/2; break; case 2: p.y += size.y - ssize.y; break; } if((ssize.x < size.x - 4)&&(ssize.y < size.y - 2)) string(&screen, add(screen.r.min, p), font, s, DorS); else{ Bitmap *b; b = balloc(Rt(p, size), screen.ldepth); string(b, p, font, s, S); bitblt(&screen, add(screen.r.min, p), b, Rt(p, size), DorS); bfree(b); } } void gline(Point p, Point size, int color){ Rectangle r; r = Rs(p,size); segment(&screen, r.min, r.max, color, S); } void gbox(Point p, Point size, int color){ gline(p, (0, size.y), color); gline(p, (size.x, 0), color); gline(add(p,size), (0, -size.y), color); gline(add(p,size), (-size.x, 0), color); } void gfbox(Point p, Point size, int color){ Bitmap *b; Rectangle r; b =balloc(Rect(0,0,1,1), screen.ldepth); point(b , (0,0), color, S); r = Rs(p, size); texture(&screen, r, b, S); bfree(b); } void gclr(int color){ gfbox((0,0), Dr(screen.r), color); } void gfrbox(Point p, Point size){ gfbox(p, size, gwhite); gbox(p, size, gblack); } void gfrbox2(Point p, Point size){ gfbox(p, size, ggray); gbox(p, size, gblack); p = add(p, (4,4)); size = sub(size, (8,8)); gfbox(p, size, gwhite); gbox(p, size, gblack); } Point gtext(Point p, Point size, byte *s){ gfrbox(p, size); glabel(p, size, s,CC); return size; } void gbutton(Point p, Point size, int mode){ Point q; int upper, lower; /* line colors */ gfbox(p, size, ggray); if(mode) {/* pushed state */ upper = gblack; lower = gwhite; } else{ /* normal state */ upper = gwhite; lower = gblack; } gline(p, (size.x - 1, 0), upper); gline(p, (0, size.y - 1), upper); q = add(p, size); q = sub(q, (1,1)); gline(q, (0, 1 - size.y), lower); gline(q, (1 - size.x, 0), lower); } Point glbutton(Point p, Point size, byte *label, int mode){ gbutton(p, size, mode); if(label) glabel(p, size, label, CC); return size; } /* ---- end of g functions --------- */ /* * A general mouse event handler * Do you need time stamp in Mevent? */ /* Mevent.b: * 1: left button, 2: middle button, 3: right button * Mevent.Point: * Point value is relative to the working window * Upper-left corner is (0,0). */ aggr Mevent { int b; Point; }; aggr Mchan { Rectangle r; chan(Mevent) c; }; void mouse(Mchan *mc, int n){ int fd; int i; Mevent e; byte buf[256]; mousepid = getpid(); fd = open("/dev/mouse", OREAD); if(fd < 0) { fprint(2,"%r\n"); exits("/dev/mouse"); } while(read(fd, buf, sizeof(buf)) > 0){ e.x = BGLONG(buf + 2) - screen.r.min.x; e.y = BGLONG(buf + 6) - screen.r.min.y; e.b = buf[1]; if( e.b & 0x80) screen.r = bscreenrect(&screen.clipr); if( e.b != 0){ for(i = 0; i < n; i++) if(ptinrect(e.Point, mc[i].r)){ mc[i].c <-= e; break; } } } } adt TextField { Rectangle r; byte *str; TextField* init(*TextField, Point p, Point size, int n); void set(*TextField, byte *str); byte* get(*TextField); }; TextField* TextField.init(TextField *self, Point p, Point size, int n){ self->r = Rt(p, size); self->str = malloc(n+1); gtext(p, size, nil); return self; } void TextField.set(TextField *self, byte *str){ strcpy(self->str, str); gtext(self->r.min, Dr(self->r), str); } byte* TextField.get(TextField *self){ return self->str; } adt NumField { Rectangle r; int *num; int n; NumField* init(*NumField, Point p, Point size, int n); void set(*NumField, ...); int* get(*NumField); }; NumField *NumField.init(NumField *self, Point p, Point size, int n){ self->r = Rt(p,size); self->n = n; self->num = malloc(n*sizeof(int)); gtext(p, size, nil); return self; } void NumField.set(NumField *self, ...){ /* see "The documents" p.99 for the usage ... */ int i; byte buf[128]; for(i = 0; i < self->n; i++) self->num[i] = ((int*) ...)[i]; sprint(buf, "%d", ((int*) ...)[0]); if(self->n > 1){ byte *b; for(i = 1; i < self->n; i++){ b = buf + strlen(buf); sprint(b, ",%d", ((int*) ...)[i]); } } gtext(self->r.min, Dr(self->r), buf); } int* NumField.get(NumField *self){ return self->num; } NumField offset; adt Cell{ Rectangle r; Cell* init(*Cell, Point size); Point size(*Cell); void draw(*Cell, Point, int op); }; Cell* Cell.init(Cell *self, Point size){ self->r = ((0,0),(size)); return self; } Point Cell.size(Cell *self){ return self->r.max; } void Cell.draw(Cell *self, Point p, int op){ Rectangle r; p = add(p, screen. r.min); r = raddp(self->r,p); bitblt(&screen, p, &screen, r, F); if(op == 0) { p = add(p, Pt(2,2)); bitblt(&screen, p, &screen, inset(r,2), Zero); } } adt Bmatrix { Rectangle r; Cell* e; Point csize; /* cell size */ byte** cell; Bmatrix* init(*Bmatrix, Cell *t, Point); void draw(*Bmatrix); Rectangle rect(*Bmatrix); void load(*Bmatrix, byte *s); /* s is 32 byte */ void save(*Bmatrix, byte *s); void run(*Bmatrix, chan(Mevent)); }; Bmatrix* Bmatrix.init(Bmatrix *self, Cell *t, Point p){ int i, j, n; int mbx,mby; byte** cell; n = 16; self->e = t; /* pointer to it's subcomponent */ (mbx, mby) = self->csize = t->size(); self->r.min = p; self->r.max=(p.x + n*mbx, p.y + n*mby); self->cell = cell = (byte**)malloc(n*sizeof(byte*)); for(i = 0; i< n; i++) cell[i] = (byte*)malloc(n); for(i = 0; i < n; i++) for(j = 0; j < n; j++) { t->draw(add(p, Pt(i*mbx, j*mby)), 0); cell[j][i] = 0; } gbox(sub(self->r.min,(1,1)), add(sub(self->r.max, self->r.min),(2,2)), gwhite); bflush(); return self; } void Bmatrix.draw(Bmatrix *self){ int i,j; int mbx, mby; Cell *t; (mbx, mby) = self->csize; t = self->e; /* Consider cell[3][2]. * Note that 3 refers to y co-ordinate * and 2 to x. */ for(j = 0; j < 16; j++) for(i = 0; i < 16; i++) t->draw(add(self->r.min, Pt(i*mbx, j*mby)), self->cell[j][i]); bflush(); } Rectangle Bmatrix.rect(Bmatrix *self){ return self->r; } void Bmatrix.load(Bmatrix *self, byte *s){ byte **t; int i,j; t = self->cell; for(i = 0; i < 16; i++){ for(j = 0; j< 8; j++) t[i][j] = (s[2*i] << j) & 0x80; for(j = 0; j< 8; j++) t[i][j + 8] = (s[2*i + 1] << j) & 0x80; } self->draw(); } void Bmatrix.save(Bmatrix *self, byte *s){ byte **t; byte a; int i,j; a = 1; t = self->cell; memset(s, 0, 32); for(i = 0; i < 16; i++){ for(j = 0; j< 8; j++) if(t[i][j]) s[2*i] |= (a << (7 - j)); for(j = 0; j< 8; j++) if(t[i][j + 8]) s[2*i + 1] |= (a << (7 - j)); } } void Bmatrix.run(Bmatrix *self, chan(Mevent) c){ Mevent m; Cell *t; int x,y; int nx, ny; int cx, cy; /* cell size */ int cs; cs = 0; t = self->e; (cx,cy) = t->size(); for(;;){ m = <-c; switch(m.b){ case 1: cs = 1; /* left button */ break; case 2: cs = 0; /* middle button */ break; case 4: cs = -1; /* right button */ break; } (x,y) = sub(m.Point,self->r.min); (nx,ny) = (x / cx, y / cy); if(cs < 0) offset.set(-nx, -ny); else{/* * Consider cell[3][2]. * Note that 3 refers to y coordinate and 2 to x. */ self->cell[ny][nx] = cs; t->draw(add(self->r.min, Pt(nx*cx, ny*cy)), cs); } bflush(); } } adt Button { Rectangle r; byte *label; int type;/* radio or not */ int status; void (*action)(int); Button *init(*Button, Point, Point size, byte *label, int type, void(*action)(int)); Rectangle rect(*Button); void run(*Button, chan(Mevent)); }; /* * type: 0 for normal button, 1 for toggle button. */ Button *Button.init (Button *self, Point p, Point size, byte *label, int type, void (*action)(int)){ self->label = label; size = glbutton(p, size, label, 0); self->r = (p, add(p,size)); self->status = 0; self->action = action; self->type = type; return self; } Rectangle Button.rect(Button *self){ return self->r; } void Button.run(Button *self, chan(Mevent) c){ Mevent m; for(;;){ m = <-c; self->status = !(self->status); glbutton(self->r.min, sub(self->r.max, self->r.min), self->label, self->status); bflush(); (*(self->action))(self->status); if(!(self->type)){ self->status = !(self->status); glbutton(self->r.min, sub(self->r.max, self->r.min), self->label, self->status); bflush(); } } } Bmatrix clrmat, setmat; void try(int status){ Cursor cur; int *np; if(status){ clrmat.save(cur.clr); setmat.save(cur.set); np =offset.get(); cur.offset = (np[0], np[1]); cursorswitch(&cur); } else cursorswitch(nil); } /* * Quit: print out the result to stdout, * post kill note to the child process and quit. * * Note that quit() is blocked if stdout is blocked. * I don't know how to avoid this blocking. */ void quit(int status){ Cursor cur; int i,j; int *np; USED(status); clrmat.save(cur.clr); setmat.save(cur.set); np =offset.get(); cur.offset = (np[0], np[1]); print("\n"); print("Cursor NoName = {\n"); np = offset.get(); print("{%d, %d},\n", np[0], np[1]); print("{"); for(i = 0; i < 4; i++){ for(j = 0; j < 8; j++) print(" 0x%.02X,", cur.clr[8*i + j]); if(i != 3) print("\n"); } print("},\n"); print("{"); for(i = 0; i < 4; i++){ for(j = 0; j < 8; j++) print(" 0x%.02X,", cur.set[8*i + j]); if(i != 3) print("\n"); } print("}\n"); print("};\n"); postnote(PNPROC, mousepid,"kill"); exits(nil); } /* load cursor */ void cload(Cursor *cur){ clrmat.load(cur->clr); setmat.load(cur->set); offset.set(cur->offset); bflush(); } void carrow(int status){ USED(status); cload(&whitearrow); } void csweep(int status){ USED(status); cload(&sweep0); } void cbox(int status){ USED(status); cload(&boxcurs); } void csight(int status){ USED(status); cload(&sight); } /* * Alef doesn't have ctype.h * so I coded some functions. */ int isdigit(int c){ if(c >= '0' && c <= '9') return 1; return 0; } int isalnum(int c){ if(c >= '0' && c <= '9') return 1; if(c >= 'a' && c <= 'z') return 1; if(c >= 'A' && c <= 'Z') return 1; return 0; } /* * tiny Biobuf * downward compatible to authorized Biobuf */ adt Biobuf { byte *bp, *ebp; int ateof; byte buf[4096]; int fd; int init(*Biobuf, int, int); int getc(*Biobuf); }; int Biobuf.init(Biobuf *self, int fd, int mode){ self->fd = fd; USED(mode); return 0; } int Biobuf.getc(Biobuf *self){ int n; if(self->ateof) return -1; if(self->bp==self->ebp){ n=read(self->fd, self->buf, sizeof(self->buf)); if(n<=0){ self->ateof=1; return -1; } self->bp=self->buf; self->ebp=self->buf+n; } return *(self->bp)++; } /* ---- end of tiny Biobuf ---- */ Biobuf iobuf; #define getc() iobuf.getc() /* * read stdin and pic up only numeric data. others are ignored. * numeric data may be decimal or hexadecimal */ int nextnum(void){ byte buf[64], *b; int c; c = getc(); while(c != -1 && !isdigit(c)) c = getc(); if(c == -1) err("unexpected EOF"); b = buf; *b++ = c; c = getc(); while( isalnum(c)){ *b++ = c; c = getc(); } *b = 0; return atoi(buf); } void ldcursor(Cursor *cur){ /* load cursor data from stdin */ int i; /* I pic up only numeric data. others are ignored. */ cur->offset.x = nextnum(); cur->offset.y = nextnum(); for(i = 0; i < 32; i++) cur->clr[i] = nextnum(); for(i = 0; i < 32; i++) cur->set[i] = nextnum(); /* and we must discard the rest */ while(getc() != -1); } /* * This code is borrowed from fb/colors.c * and translated to alef */ byte *rdenv(byte *name){ byte *v; int fd, size; fd=open(name, OREAD); if(fd<0) return nil; size=seek(fd, 0, 2); v=malloc(size+1); if(v==nil){ fprint(2, "Can't malloc: %r\n"); exits("no mem"); } seek(fd, 0, 0); read(fd, v, size); v[size]=0; close(fd); return v; } /* * This code is borrowed from fb/colors.c * and translated to alef. * Some parts are discarded for this program. */ void winit(void (*errfun)(byte *), byte *font, byte *label, Rectangle r){ byte *srv, *mntsrv; byte spec[100]; int srvfd, pid; switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){ case -1: fprint(2, "Can't fork: %r\n"); exits("no fork"); case 0: break; default: exits(nil); } srv=rdenv("/env/8½srv"); if(srv==nil){ free(srv); mntsrv=rdenv("/mnt/term/env/8½srv"); srv=malloc(strlen(mntsrv)+10); sprint(srv, "/mnt/term%s", mntsrv); free(mntsrv); pid=0; /* 8½srv can't send notes to remote processes! */ } else pid=getpid(); srvfd=open(srv, ORDWR); free(srv); if(srvfd==-1){ fprint(2, "Can't open %s: %r\n", srv); exits("no srv"); } sprint(spec, "N%d,%d,%d,%d,%d\n", pid, r.min.x, r.min.y, r.max.x, r.max.y); if(mount(srvfd, "/mnt/8½", 0, spec)==-1){ fprint(2, "Can't mount: %r\n"); exits("no mount"); } close(srvfd); bind("/mnt/8½", "/dev", MBEFORE); binit(errfun, font, label); } void main(int argc, byte **argv){ Cell cell; Font *font0; Button bot_try, bot_quit, bot_load[4]; Mchan mc[8]; chan(int) term; Arg *arg; Cursor cur; int lflag; int i, ac; lflag = 0; arg = arginit(argc, argv); while(ac = argopt(arg)) switch(ac){ case 'l': /* load cursor data from stdin */ lflag = 1; break; default: usage(); } USED(argc); if(lflag){ /* we must read stdin before winit() */ iobuf.init(0, OREAD); ldcursor(&cur); } /* initialize window and some data */ winit(nil,nil,nil, ((100, 50), (750, 500))); gblack = pblack[screen.ldepth]; ggray = gblack / 3; /* I believe this relation */ gclr(ggray); cell.init((16,16)); clrmat.init(&cell,(50,100)); setmat.init(&cell, (350,100)); font0 = font; font = rdfontfile("/lib/font/bit/pelm/euro.8.font", screen.ldepth); if(!font) font = font0; gputs((50, 20), (200, 20), "left button: draw"); gputs((50, 40), (200, 20),"middle button: erase"); gputs((50, 60), (300, 20),"right button: set cursor offset"); glabel((50, 400),(200, 20),"load cursor",CL); gputs((540, 400),(200, 20),"by Kenar"); bot_try.init((350, 20), (60, 20), "try it", 1, try); bot_quit.init((500, 20), (106, 20), "print & exit", 0, quit); bot_load[0].init((150, 400),(70, 20),"arrow", 0, carrow); bot_load[1].init((220, 400),(70, 20),"sweep", 0, csweep); bot_load[2].init((290, 400),(70, 20),"box", 0, cbox); bot_load[3].init((360, 400),(70, 20),"sight", 0, csight); gputs( (130, 360),(200, 20), "cursor filter"); gputs((430,360),(200, 20),"cursor shape"); gputs((350, 60), (150, 20), "Cursor offset is"); offset.init((500, 60),(80, 20), 2); bflush(); if(lflag){ /* we initially set a sample cursor */ cload(&cur); } for(i = 0; i < 8; i++) alloc mc[i].c; mc[0].r = clrmat.rect(); mc[1].r = setmat.rect(); mc[2].r = bot_try.rect(); mc[3].r = bot_quit.rect(); mc[4].r = bot_load[0].rect(); mc[5].r = bot_load[1].rect(); mc[6].r = bot_load[2].rect(); mc[7].r = bot_load[3].rect(); proc mouse(mc, 8); task clrmat.run(mc[0].c); task setmat.run(mc[1].c); task bot_try.run(mc[2].c); task bot_quit.run(mc[3].c); task bot_load[0].run(mc[4].c); task bot_load[1].run(mc[5].c); task bot_load[2].run(mc[6].c); task bot_load[3].run(mc[7].c); alloc term; <-term; }