15 * WASHINGTON (AP) - The Food and Drug Administration warned
16 * consumers Wednesday not to use ``Rio'' hair relaxer products
17 * because they may cause severe hair loss or turn hair green....
18 * The FDA urged consumers who have experienced problems with Rio
19 * to notify their local FDA office, local health department or the
20 * company at 1‑800‑543‑3002.
30 Image *bandsize(Window*);
31 Image* drag(Window*, Rectangle*);
32 void refresh(Rectangle);
34 Channel *exitchan; /* chan(int) */
35 Channel *winclosechan; /* chan(Window*); */
36 Channel *kbdchan; /* chan(char*); */
38 int threadrforkflag = 0; /* should be RFENVG but that hides rio from plumber */
40 void mousethread(void*);
41 void keyboardthread(void*);
42 void winclosethread(void*);
43 void deletethread(void*);
45 Channel* initkbd(void);
86 char *menu3str[100] = {
101 char *rcargv[] = { "rc", "-i", nil };
102 char *kbdargv[] = { "rc", "-c", nil, nil };
104 int errorshouldabort = 0;
107 derror(Display*, char *errorstr)
115 fprint(2, "usage: rio [-b] [-f font] [-i initcmd] [-k kbdcmd] [-s]\n");
120 threadmain(int argc, char *argv[])
122 char *initstr, *kbdin, *s;
128 if(strstr(argv[0], ".out") == nil){
129 menu3str[Exit] = nil;
161 if(getwd(buf, sizeof buf) == nil)
162 startdir = estrdup(".");
164 startdir = estrdup(buf);
166 fontname = getenv("font");
167 s = getenv("tabstop");
169 maxtab = strtol(s, nil, 0);
175 /* check font before barging ahead */
176 if(access(fontname, 0) < 0){
177 fprint(2, "rio: can't access %s: %r\n", fontname);
180 putenv("font", fontname);
183 snarffd = open("/dev/snarf", OREAD|OCEXEC);
184 gotscreen = access("/dev/screen", AEXIST)==0;
186 if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) < 0){
187 fprint(2, "rio: can't open display: %r\n");
188 exits("display open");
193 mousectl = initmouse(nil, screen);
195 error("can't find mouse");
199 error("can't find keyboard");
200 wscreen = allocscreen(screen, background, 0);
202 error("can't allocate screen");
203 draw(view, viewr, background, nil, ZP);
204 flushimage(display, 1);
206 exitchan = chancreate(sizeof(int), 0);
207 winclosechan = chancreate(sizeof(Window*), 0);
208 deletechan = chancreate(sizeof(char*), 0);
211 threadcreate(keyboardthread, nil, STACK);
212 threadcreate(mousethread, nil, STACK);
213 threadcreate(winclosethread, nil, STACK);
214 threadcreate(deletethread, nil, STACK);
215 filsys = filsysinit(xfidinit());
218 fprint(2, "rio: can't create file system server: %r\n");
220 errorshouldabort = 1; /* suicide if there's trouble after this */
222 proccreate(initcmd, initstr, STACK);
226 r.min.y = r.max.y-Dy(r)/3;
227 i = allocwindow(wscreen, r, Refbackup, DNofill);
228 wkeyboard = new(i, FALSE, scrolling, 0, nil, "/bin/rc", kbdargv);
230 error("can't create keyboard window");
232 threadnotify(shutdown, 1);
240 * /dev/snarf updates when the file is closed, so we must open our own
241 * fd here rather than use snarffd
248 if(snarffd<0 || nsnarf==0)
250 fd = open("/dev/snarf", OWRITE);
253 /* snarf buffer could be huge, so fprint will truncate; do it in blocks */
254 for(i=0; i<nsnarf; i+=n){
258 if(fprint(fd, "%.*S", n, snarf+i) < 0)
275 while((n = read(snarffd, buf, sizeof buf)) > 0){
276 sn = erealloc(sn, i+n+1);
277 memmove(sn+i, buf, n);
282 snarf = runerealloc(snarf, i+1);
283 cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls);
294 rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG);
295 procexecl(nil, "/bin/rc", "rc", "-c", cmd, nil);
296 fprint(2, "rio: exec failed: %r\n");
310 shutdown(void *, char *msg)
313 static Lock shutdownlk;
316 for(i=0; oknotes[i]; i++)
317 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0){
318 lock(&shutdownlk); /* only one can threadexitsall */
321 fprint(2, "rio %d: abort: %s\n", getpid(), msg);
332 for(i=0; i<nwindow; i++)
333 if(window[i]->notefd >= 0)
334 write(window[i]->notefd, "hangup", 6);
338 keyboardthread(void*)
342 threadsetname("keyboardthread");
344 while(s = recvp(kbdchan)){
345 if(*s == 'k' || *s == 'K')
346 shiftdown = utfrune(s+1, Kshift) != nil;
347 if(input == nil || sendp(input->ck, s) <= 0)
356 keyboardsend(char *s, int cnt)
361 chanprint(kbdchan, "%s", s);
367 cvttorunes(s, cnt, r, &nb, &nr, nil);
370 chanprint(kbdchan, "c%C", r[i]);
377 portion(int x, int lo, int hi)
389 whichcorner(Window *w, Point p)
393 i = portion(p.x, w->screenr.min.x, w->screenr.max.x);
394 j = portion(p.y, w->screenr.min.y, w->screenr.max.y);
399 cornercursor(Window *w, Point p, int force)
401 if(w!=nil && winborder(w, p))
402 riosetcursor(corners[whichcorner(w, p)], force);
404 wsetcursor(w, force);
407 /* thread to allow fsysproc to synchronize window closing with main proc */
409 winclosethread(void*)
413 threadsetname("winclosethread");
415 w = recvp(winclosechan);
420 /* thread to make Deleted windows that the client still holds disappear offscreen after an interval */
427 threadsetname("deletethread");
429 s = recvp(deletechan);
430 i = namedimage(display, s);
432 /* move it off-screen to hide it, since client is slow in letting it go */
433 originwindow(i, i->r.min, view->r.max);
441 deletetimeoutproc(void *v)
446 sleep(750); /* remove window from screen after 3/4 of a second */
447 sendp(deletechan, s);
451 * Button 6 - keyboard toggle - has been pressed.
452 * Send event to keyboard, wait for button up, send that.
453 * Note: there is no coordinate translation done here; this
454 * is just about getting button 6 to the keyboard simulator.
459 send(wkeyboard->mc.c, mouse);
462 while(mouse->buttons & (1<<5));
463 send(wkeyboard->mc.c, mouse);
469 int sending, inside, scrolling, moving, band;
480 static Alt alts[NALT+1];
482 threadsetname("mousethread");
487 alts[MReshape].c = mousectl->resizec;
488 alts[MReshape].v = nil;
489 alts[MReshape].op = CHANRCV;
490 alts[MMouse].c = mousectl->c;
491 alts[MMouse].v = &mousectl->Mouse;
492 alts[MMouse].op = CHANRCV;
493 alts[NALT].op = CHANEND;
501 if(wkeyboard!=nil && (mouse->buttons & (1<<5))){
507 /* override everything for the keyboard window */
508 if(wkeyboard!=nil && ptinrect(mouse->xy, wkeyboard->screenr)){
509 /* make sure it's on top; this call is free if it is */
513 if(winput!=nil && !winput->deleted && winput->i!=nil){
514 /* convert to logical coordinates */
515 xy.x = mouse->xy.x + (winput->i->r.min.x-winput->screenr.min.x);
516 xy.y = mouse->xy.y + (winput->i->r.min.y-winput->screenr.min.y);
518 /* the up and down scroll buttons are not subject to the usual rules */
519 if((mouse->buttons&(8|16)) && !winput->mouseopen)
522 inside = ptinrect(mouse->xy, insetrect(winput->screenr, Selborder));
523 if(winput->mouseopen)
526 scrolling = mouse->buttons;
528 scrolling = mouse->buttons && ptinrect(xy, winput->scrollr);
529 /* topped will be zero or less if window has been bottomed */
530 if(sending == FALSE && !scrolling && winborder(winput, mouse->xy) && winput->topped>0){
532 }else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
538 if(mouse->buttons == 0){
539 cornercursor(winput, mouse->xy, 0);
542 wsetcursor(winput, 0);
543 tmp = mousectl->Mouse;
545 send(winput->mc.c, &tmp);
548 w = wpointto(mouse->xy);
549 /* change cursor if over anyone's border */
551 cornercursor(w, mouse->xy, 0);
553 riosetcursor(nil, 0);
554 if(moving && (mouse->buttons&7)){
556 band = mouse->buttons & 3;
559 i = bandsize(winput);
561 i = drag(winput, &r);
565 wsendctlmesg(winput, Reshaped, i->r, i);
567 wsendctlmesg(winput, Moved, r, i);
568 cornercursor(winput, mouse->xy, 1);
570 if(wclose(winput) == 0)
573 riosetcursor(nil, 0);
577 /* we're not sending the event, but if button is down maybe we should */
579 /* w->topped will be zero or less if window has been bottomed */
580 if(w==nil || (w==winput && w->topped>0)){
581 if(mouse->buttons & 1){
583 }else if(mouse->buttons & 2){
584 if(winput && !winput->deleted && !winput->mouseopen){
589 }else if(mouse->buttons & 4)
592 /* if button 1 event in the window, top the window and wait for button up. */
593 /* otherwise, top the window and pass the event on */
594 if(wtop(mouse->xy) && (mouse->buttons!=1 || winborder(w, mouse->xy)))
605 while(mousectl->buttons);
607 goto Again; /* recalculate mouse position, cursor */
612 wtopcmp(void *a, void *b)
614 return (*(Window**)a)->topped - (*(Window**)b)->topped;
626 if(getwindow(display, Refnone) < 0)
627 error("failed to re-attach window");
631 wscreen = allocscreen(screen, background, 0);
633 error("can't re-allocate screen");
634 draw(view, view->r, background, nil, ZP);
635 o = subpt(viewr.max, viewr.min);
636 n = subpt(view->clipr.max, view->clipr.min);
637 qsort(window, nwindow, sizeof(window[0]), wtopcmp);
638 for(i=0; i<nwindow; i++){
640 r = rectsubpt(w->i->r, viewr.min);
641 r.min.x = (r.min.x*n.x)/o.x;
642 r.min.y = (r.min.y*n.y)/o.y;
643 r.max.x = (r.max.x*n.x)/o.x;
644 r.max.y = (r.max.y*n.y)/o.y;
646 r = rectsubpt(w->i->r, viewr.min);
647 r = rectaddpt(r, screen->clipr.min);
648 for(j=0; j<nhidden; j++)
653 im = allocimage(display, r, screen->chan, 0, DNofill);
656 im = allocwindow(wscreen, r, Refbackup, DNofill);
658 wsendctlmesg(w, Reshaped, r, im);
662 flushimage(display, 1);
666 obscured(Window *w, Rectangle r, int i)
670 if(Dx(r) < font->height || Dy(r) < font->height)
672 if(!rectclip(&r, screen->r))
674 for(; i<nwindow; i++){
676 if(t == w || t->topped <= w->topped)
678 if(Dx(t->screenr) == 0 || Dy(t->screenr) == 0 || rectXrect(r, t->screenr) == 0)
680 if(r.min.y < t->screenr.min.y)
681 if(!obscured(w, Rect(r.min.x, r.min.y, r.max.x, t->screenr.min.y), i))
683 if(r.min.x < t->screenr.min.x)
684 if(!obscured(w, Rect(r.min.x, r.min.y, t->screenr.min.x, r.max.y), i))
686 if(r.max.y > t->screenr.max.y)
687 if(!obscured(w, Rect(r.min.x, t->screenr.max.y, r.max.x, r.max.y), i))
689 if(r.max.x > t->screenr.max.x)
690 if(!obscured(w, Rect(t->screenr.max.x, r.min.y, r.max.x, r.max.y), i))
701 static char buf[NBUF*UTFmax];
710 k += chartorune(&r, s+k);
714 strcpy(buf+k, "...");
715 while((l-i) >= NBUF/2-4){
716 k += chartorune(&r, s+k);
729 for(i=0; i<nwindow; i++){
731 if(window[i] == hidden[j])
734 if(obscured(window[i], window[i]->screenr, 0)){
735 hidden[n++] = window[i];
736 if(n >= nelem(hidden))
740 if(n >= nelem(menu3str)-Hidden)
741 n = nelem(menu3str)-Hidden-1;
743 free(menu3str[i+Hidden]);
744 menu3str[i+Hidden] = shortlabel(hidden[i]->label);
746 for(i+=Hidden; menu3str[i]; i++){
751 switch(i = menuhit(3, mousectl, &menu3, wscreen)){
755 new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil);
774 /* else fall through */
783 button2menu(Window *w)
786 menu2str[Scroll] = "noscroll";
788 menu2str[Scroll] = "scroll";
789 switch(menuhit(2, mousectl, &menu2, wscreen)){
816 waddraw(w, snarf, nsnarf);
817 if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
818 waddraw(w, L"\n", 1);
820 winsert(w, snarf, nsnarf, w->nr);
821 if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
822 winsert(w, L"\n", 1, w->nr);
824 wsetselect(w, w->nr, w->nr);
829 if(w->scrolling ^= 1)
833 wsendctlmesg(w, Wakeup, ZR, nil);
834 flushimage(display, 1);
840 p.x = max(screen->clipr.min.x, p.x);
841 p.x = min(screen->clipr.max.x, p.x);
842 p.y = max(screen->clipr.min.y, p.y);
843 p.y = min(screen->clipr.max.y, p.y);
856 riosetcursor(&crosscursor, 1);
857 while(mouse->buttons == 0)
859 p0 = onscreen(mouse->xy);
864 while(mouse->buttons == 4){
866 if(mouse->buttons != 4 && mouse->buttons != 0)
868 if(!eqpt(mouse->xy, p)){
869 p = onscreen(mouse->xy);
870 r = canonrect(Rpt(p0, p));
871 if(Dx(r)>5 && Dy(r)>5){
872 i = allocwindow(wscreen, r, Refnone, DNofill);
877 border(i, r, Selborder, sizecol, ZP);
878 draw(i, insetrect(r, Selborder), cols[BACK], nil, ZP);
879 flushimage(display, 1);
883 if(mouse->buttons != 0)
885 if(i==nil || Dx(i->r)<100 || Dy(i->r)<3*font->height)
888 i = allocwindow(wscreen, oi->r, Refbackup, DNofill);
892 cornercursor(input, mouse->xy, 1);
898 cornercursor(input, mouse->xy, 1);
899 while(mouse->buttons)
903 moveto(mousectl, mouse->xy); /* force cursor update; ugly */
909 drawedge(Image **bp, Rectangle r)
912 if(b != nil && Dx(b->r) == Dx(r) && Dy(b->r) == Dy(r))
913 originwindow(b, r.min, r.min);
916 b = allocwindow(wscreen, r, Refbackup, DNofill);
917 if(b != nil) draw(b, r, sizecol, nil, ZP);
923 drawborder(Rectangle r, int show)
928 for(i = 0; i < 4; i++){
934 drawedge(&b[0], Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y));
935 drawedge(&b[1], Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth));
936 drawedge(&b[2], Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y));
937 drawedge(&b[3], Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y));
942 drag(Window *w, Rectangle *rp)
945 Point p, op, d, dm, om;
951 riosetcursor(&boxcursor, 1);
952 dm = subpt(mouse->xy, w->screenr.min);
953 d = subpt(i->r.max, i->r.min);
954 op = subpt(mouse->xy, dm);
955 drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
956 flushimage(display, 1);
957 while(mouse->buttons == 4){
958 p = subpt(mouse->xy, dm);
960 drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
961 flushimage(display, 1);
966 r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
968 cornercursor(w, mouse->xy, 1);
969 moveto(mousectl, mouse->xy); /* force cursor update; ugly */
971 flushimage(display, 1);
972 if(mouse->buttons!=0 || (ni=allocwindow(wscreen, r, Refbackup, DNofill))==nil){
973 moveto(mousectl, om);
974 while(mouse->buttons)
976 *rp = Rect(0, 0, 0, 0);
979 draw(ni, ni->r, i, nil, i->r.min);
985 cornerpt(Rectangle r, Point p, int which)
988 case 0: /* top left */
989 p = Pt(r.min.x, r.min.y);
991 case 2: /* top right */
992 p = Pt(r.max.x,r.min.y);
994 case 6: /* bottom left */
995 p = Pt(r.min.x, r.max.y);
997 case 8: /* bottom right */
998 p = Pt(r.max.x, r.max.y);
1000 case 1: /* top edge */
1001 p = Pt(p.x,r.min.y);
1003 case 5: /* right edge */
1004 p = Pt(r.max.x, p.y);
1006 case 7: /* bottom edge */
1007 p = Pt(p.x, r.max.y);
1009 case 3: /* left edge */
1010 p = Pt(r.min.x, p.y);
1017 whichrect(Rectangle r, Point p, int which)
1020 case 0: /* top left */
1021 r = Rect(p.x, p.y, r.max.x, r.max.y);
1023 case 2: /* top right */
1024 r = Rect(r.min.x, p.y, p.x, r.max.y);
1026 case 6: /* bottom left */
1027 r = Rect(p.x, r.min.y, r.max.x, p.y);
1029 case 8: /* bottom right */
1030 r = Rect(r.min.x, r.min.y, p.x, p.y);
1032 case 1: /* top edge */
1033 r = Rect(r.min.x, p.y, r.max.x, r.max.y);
1035 case 5: /* right edge */
1036 r = Rect(r.min.x, r.min.y, p.x, r.max.y);
1038 case 7: /* bottom edge */
1039 r = Rect(r.min.x, r.min.y, r.max.x, p.y);
1041 case 3: /* left edge */
1042 r = Rect(p.x, r.min.y, r.max.x, r.max.y);
1045 return canonrect(r);
1056 but = mouse->buttons;
1057 which = whichcorner(w, p);
1058 p = cornerpt(w->screenr, p, which);
1060 readmouse(mousectl);
1061 r = whichrect(w->screenr, p, which);
1066 while(mouse->buttons == but){
1067 p = onscreen(mouse->xy);
1068 r = whichrect(w->screenr, p, which);
1069 if(!eqrect(r, or) && goodrect(r)){
1071 flushimage(display, 1);
1074 readmouse(mousectl);
1078 flushimage(display, 1);
1080 if(mouse->buttons!=0 || Dx(or)<100 || Dy(or)<3*font->height){
1081 while(mouse->buttons)
1082 readmouse(mousectl);
1085 if(abs(p.x-startp.x)+abs(p.y-startp.y) <= 1)
1087 return allocwindow(wscreen, or, Refbackup, DNofill);
1096 riosetcursor(&sightcursor, 1);
1097 while(mouse->buttons == 0)
1098 readmouse(mousectl);
1099 if(mouse->buttons == 4)
1100 w = wpointto(mouse->xy);
1104 while(mouse->buttons){
1105 if(mouse->buttons!=4 && w !=nil){ /* cancel */
1106 cornercursor(input, mouse->xy, 0);
1109 readmouse(mousectl);
1111 if(w != nil && wpointto(mouse->xy) != w)
1114 cornercursor(input, mouse->xy, 0);
1115 moveto(mousectl, mouse->xy); /* force cursor update; ugly */
1127 wsendctlmesg(w, Deleted, ZR, nil);
1142 wsendctlmesg(w, Reshaped, i->r, i);
1159 wsendctlmesg(w, Moved, r, i);
1160 cornercursor(w, mouse->xy, 1);
1170 for(j=0; j<nhidden; j++)
1171 if(hidden[j] == w) /* already hidden */
1173 if(nhidden >= nelem(hidden))
1176 i = allocimage(display, w->screenr, w->i->chan, 0, DNofill);
1178 hidden[nhidden++] = w;
1179 wsendctlmesg(w, Reshaped, ZR, i);
1191 for(j=0; j<nhidden; j++)
1195 return -1; /* not hidden */
1197 i = allocwindow(wscreen, w->i->r, Refbackup, DNofill);
1200 memmove(hidden+j, hidden+j+1, (nhidden-j)*sizeof(Window*));
1201 wsendctlmesg(w, Reshaped, w->i->r, i);
1232 /* uncover obscured window */
1233 for(j=0; j<nwindow; j++)
1244 new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
1248 Channel *cm, *ck, *cctl, *cpid;
1253 if(hideit && nhidden >= nelem(hidden)){
1257 cm = chancreate(sizeof(Mouse), 0);
1258 ck = chancreate(sizeof(char*), 0);
1259 cctl = chancreate(sizeof(Wctlmesg), 4);
1260 cpid = chancreate(sizeof(int), 0);
1261 if(cm==nil || ck==nil || cctl==nil)
1262 error("new: channel alloc failed");
1263 mc = emalloc(sizeof(Mousectl));
1267 w = wmk(i, mc, ck, cctl, scrollit);
1268 free(mc); /* wmk copies *mc */
1269 window = erealloc(window, ++nwindow*sizeof(Window*));
1270 window[nwindow-1] = w;
1272 hidden[nhidden++] = w;
1275 threadcreate(winctl, w, 8192);
1279 arg = emalloc(5*sizeof(void*));
1288 proccreate(winshell, arg, 8192);
1293 /* window creation failed */
1294 wsendctlmesg(w, Deleted, ZR, nil);
1302 w->dir = estrdup(dir);
1312 char buf[128], *p, *e;
1313 int fd, cfd, kfd, n;
1315 threadsetname("kbdproc");
1317 if((fd = open("/dev/cons", OREAD)) < 0){
1321 if((cfd = open("/dev/consctl", OWRITE)) < 0){
1325 fprint(cfd, "rawon");
1327 if(sendp(c, nil) <= 0)
1330 if((kfd = open("/dev/kbd", OREAD)) >= 0){
1333 /* read kbd state */
1334 while((n = read(kfd, buf, sizeof(buf))) > 0)
1335 chanprint(c, "%.*s", n, buf);
1338 /* read single characters */
1343 e = buf + sizeof(buf);
1344 if((n = read(fd, p, e-p)) <= 0)
1347 while(p < e && fullrune(p, e - p)){
1348 p += chartorune(&r, p);
1350 chanprint(c, "c%C", r);
1365 c = chancreate(sizeof(char*), 16);
1366 procrfork(kbdproc, c, STACK, RFCFDG);