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*);
33 Channel *exitchan; /* chan(int) */
34 Channel *winclosechan; /* chan(Window*); */
35 Channel *kbdchan; /* chan(char*); */
37 int threadrforkflag = 0; /* should be RFENVG but that hides rio from plumber */
39 void mousethread(void*);
40 void keyboardthread(void*);
41 void winclosethread(void*);
43 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;
127 if(strstr(argv[0], ".out") == nil){
128 menu3str[Exit] = nil;
139 fontname = EARGF(usage());
142 initstr = EARGF(usage());
147 kbdin = EARGF(usage());
159 if(getwd(buf, sizeof buf) == nil)
160 startdir = estrdup(".");
162 startdir = estrdup(buf);
164 fontname = getenv("font");
165 s = getenv("tabstop");
167 maxtab = strtol(s, nil, 0);
173 /* check font before barging ahead */
174 if(access(fontname, 0) < 0){
175 fprint(2, "rio: can't access %s: %r\n", fontname);
178 putenv("font", fontname);
181 snarffd = open("/dev/snarf", OREAD|OCEXEC);
182 gotscreen = access("/dev/screen", AEXIST)==0;
184 if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) < 0){
185 fprint(2, "rio: can't open display: %r\n");
186 exits("display open");
190 exitchan = chancreate(sizeof(int), 0);
191 winclosechan = chancreate(sizeof(Window*), 0);
195 mousectl = initmouse(nil, screen);
197 error("can't find mouse");
201 error("can't find keyboard");
202 wscreen = allocscreen(screen, background, 0);
204 error("can't allocate screen");
205 draw(view, viewr, background, nil, ZP);
206 flushimage(display, 1);
209 threadcreate(keyboardthread, nil, STACK);
210 threadcreate(mousethread, nil, STACK);
211 threadcreate(winclosethread, nil, STACK);
212 filsys = filsysinit(xfidinit());
215 fprint(2, "rio: can't create file system server: %r\n");
217 errorshouldabort = 1; /* suicide if there's trouble after this */
219 proccreate(initcmd, initstr, STACK);
223 r.min.y = r.max.y-Dy(r)/3;
224 i = allocwindow(wscreen, r, Refbackup, DNofill);
225 wkeyboard = new(i, FALSE, scrolling, 0, nil, "/bin/rc", kbdargv);
227 error("can't create keyboard window");
229 threadnotify(shutdown, 1);
237 * /dev/snarf updates when the file is closed, so we must open our own
238 * fd here rather than use snarffd
245 if(snarffd<0 || nsnarf==0)
247 fd = open("/dev/snarf", OWRITE);
250 /* snarf buffer could be huge, so fprint will truncate; do it in blocks */
251 for(i=0; i<nsnarf; i+=n){
255 if(fprint(fd, "%.*S", n, snarf+i) < 0)
275 if((s = realloc(sn, i+1024+1)) == nil)
278 if((n = read(snarffd, sn+i, 1024)) <= 0)
285 if((snarf = runerealloc(snarf, i+1)) != nil)
286 cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls);
296 rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG);
297 procexecl(nil, "/bin/rc", "rc", "-c", cmd, nil);
298 fprint(2, "rio: exec failed: %r\n");
312 shutdown(void *, char *msg)
315 static Lock shutdownlk;
318 for(i=0; oknotes[i]; i++)
319 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0){
320 lock(&shutdownlk); /* only one can threadexitsall */
323 fprint(2, "rio %d: abort: %s\n", getpid(), msg);
334 for(i=0; i<nwindow; i++)
335 if(window[i]->notefd >= 0)
336 write(window[i]->notefd, "hangup", 6);
340 keyboardthread(void*)
344 threadsetname("keyboardthread");
346 while(s = recvp(kbdchan)){
347 if(*s == 'k' || *s == 'K')
348 shiftdown = utfrune(s+1, Kshift) != nil;
349 if(input == nil || sendp(input->ck, s) <= 0)
355 inborder(Rectangle r, Point xy)
357 return ptinrect(xy, r) && !ptinrect(xy, insetrect(r, Selborder));
361 whichrect(Rectangle r, Point p, int which)
364 case 0: /* top left */
365 r = Rect(p.x, p.y, r.max.x, r.max.y);
367 case 2: /* top right */
368 r = Rect(r.min.x, p.y, p.x+1, r.max.y);
370 case 6: /* bottom left */
371 r = Rect(p.x, r.min.y, r.max.x, p.y+1);
373 case 8: /* bottom right */
374 r = Rect(r.min.x, r.min.y, p.x+1, p.y+1);
376 case 1: /* top edge */
377 r = Rect(r.min.x, p.y, r.max.x, r.max.y);
379 case 5: /* right edge */
380 r = Rect(r.min.x, r.min.y, p.x+1, r.max.y);
382 case 7: /* bottom edge */
383 r = Rect(r.min.x, r.min.y, r.max.x, p.y+1);
385 case 3: /* left edge */
386 r = Rect(p.x, r.min.y, r.max.x, r.max.y);
393 portion(int x, int lo, int hi)
398 return x > 0 ? 2 : 0;
407 whichcorner(Rectangle r, Point p)
411 i = portion(p.x, r.min.x, r.max.x);
412 j = portion(p.y, r.min.y, r.max.y);
416 /* thread to allow fsysproc to synchronize window closing with main proc */
418 winclosethread(void*)
422 threadsetname("winclosethread");
424 w = recvp(winclosechan);
430 * Button 6 - keyboard toggle - has been pressed.
431 * Send event to keyboard, wait for button up, send that.
432 * Note: there is no coordinate translation done here; this
433 * is just about getting button 6 to the keyboard simulator.
438 send(wkeyboard->mc.c, mouse);
441 while(mouse->buttons & (1<<5));
442 send(wkeyboard->mc.c, mouse);
448 int sending, inside, scrolling, moving;
458 static Alt alts[NALT+1];
460 threadsetname("mousethread");
464 alts[MReshape].c = mousectl->resizec;
465 alts[MReshape].v = nil;
466 alts[MReshape].op = CHANRCV;
467 alts[MMouse].c = mousectl->c;
468 alts[MMouse].v = &mousectl->Mouse;
469 alts[MMouse].op = CHANRCV;
470 alts[NALT].op = CHANEND;
478 if(wkeyboard!=nil && (mouse->buttons & (1<<5))){
485 /* override everything for the keyboard window */
486 if(wkeyboard!=nil && ptinrect(mouse->xy, wkeyboard->screenr)){
487 /* make sure it's on top; this call is free if it is */
491 if(winput!=nil && !winput->deleted && winput->i!=nil){
492 /* convert to logical coordinates */
493 xy.x = mouse->xy.x + (winput->i->r.min.x-winput->screenr.min.x);
494 xy.y = mouse->xy.y + (winput->i->r.min.y-winput->screenr.min.y);
496 /* the up and down scroll buttons are not subject to the usual rules */
497 if((mouse->buttons&(8|16)) && !winput->mouseopen)
500 inside = ptinrect(mouse->xy, insetrect(winput->screenr, Selborder));
501 if(winput->mouseopen)
504 scrolling = mouse->buttons;
506 scrolling = mouse->buttons && ptinrect(xy, winput->scrollr);
507 /* topped will be zero or less if window has been bottomed */
508 if(sending == FALSE && !scrolling && inborder(winput->screenr, mouse->xy) && winput->topped>0)
510 else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
516 wsetcursor(winput, FALSE);
517 if(mouse->buttons == 0)
519 tmp = mousectl->Mouse;
521 send(winput->mc.c, &tmp);
524 if(moving && (mouse->buttons&7)){
527 if(mouse->buttons & 3)
528 i = bandsize(winput);
533 wsendctlmesg(winput, Reshaped, i->r, i);
537 w = wpointto(mouse->xy);
538 if(w!=nil && inborder(w->screenr, mouse->xy))
539 riosetcursor(corners[whichcorner(w->screenr, mouse->xy)]);
541 wsetcursor(w, FALSE);
542 /* we're not sending the event, but if button is down maybe we should */
544 /* w->topped will be zero or less if window has been bottomed */
545 if(w==nil || (w==winput && w->topped>0)){
546 if(mouse->buttons & 1){
548 }else if(mouse->buttons & 2){
549 if(winput && !winput->deleted && !winput->mouseopen){
554 }else if(mouse->buttons & 4)
557 /* if button 1 event in the window, top the window and wait for button up. */
558 /* otherwise, top the window and pass the event on */
559 if(wtop(mouse->xy) && (mouse->buttons!=1 || inborder(w->screenr, mouse->xy)))
569 while(mousectl->buttons);
570 goto Again; /* recalculate mouse position, cursor */
575 wtopcmp(void *a, void *b)
577 return (*(Window**)a)->topped - (*(Window**)b)->topped;
589 if(getwindow(display, Refnone) < 0)
590 error("failed to re-attach window");
594 wscreen = allocscreen(screen, background, 0);
596 error("can't re-allocate screen");
597 draw(view, view->r, background, nil, ZP);
598 o = subpt(viewr.max, viewr.min);
599 n = subpt(view->clipr.max, view->clipr.min);
600 qsort(window, nwindow, sizeof(window[0]), wtopcmp);
601 for(i=0; i<nwindow; i++){
603 r = rectsubpt(w->i->r, viewr.min);
604 r.min.x = (r.min.x*n.x)/o.x;
605 r.min.y = (r.min.y*n.y)/o.y;
606 r.max.x = (r.max.x*n.x)/o.x;
607 r.max.y = (r.max.y*n.y)/o.y;
608 r = rectaddpt(r, view->clipr.min);
610 r = rectsubpt(w->i->r, subpt(w->i->r.min, r.min));
611 for(j=0; j<nhidden; j++)
616 im = allocimage(display, r, screen->chan, 0, DNofill);
619 im = allocwindow(wscreen, r, Refbackup, DNofill);
621 wsendctlmesg(w, Reshaped, r, im);
625 flushimage(display, 1);
629 obscured(Window *w, Rectangle r, int i)
633 if(Dx(r) < font->height || Dy(r) < font->height)
635 if(!rectclip(&r, screen->r))
637 for(; i<nwindow; i++){
639 if(t == w || t->topped <= w->topped)
641 if(Dx(t->screenr) == 0 || Dy(t->screenr) == 0 || rectXrect(r, t->screenr) == 0)
643 if(r.min.y < t->screenr.min.y)
644 if(!obscured(w, Rect(r.min.x, r.min.y, r.max.x, t->screenr.min.y), i))
646 if(r.min.x < t->screenr.min.x)
647 if(!obscured(w, Rect(r.min.x, r.min.y, t->screenr.min.x, r.max.y), i))
649 if(r.max.y > t->screenr.max.y)
650 if(!obscured(w, Rect(r.min.x, t->screenr.max.y, r.max.x, r.max.y), i))
652 if(r.max.x > t->screenr.max.x)
653 if(!obscured(w, Rect(t->screenr.max.x, r.min.y, r.max.x, r.max.y), i))
664 static char buf[NBUF*UTFmax];
673 k += chartorune(&r, s+k);
677 strcpy(buf+k, "...");
678 while((l-i) >= NBUF/2-4){
679 k += chartorune(&r, s+k);
692 for(i=0; i<nwindow; i++){
694 if(window[i] == hidden[j])
697 if(obscured(window[i], window[i]->screenr, 0)){
698 hidden[n++] = window[i];
699 if(n >= nelem(hidden))
703 if(n >= nelem(menu3str)-Hidden)
704 n = nelem(menu3str)-Hidden-1;
706 free(menu3str[i+Hidden]);
707 menu3str[i+Hidden] = shortlabel(hidden[i]->label);
709 for(i+=Hidden; menu3str[i]; i++){
714 switch(i = menuhit(3, mousectl, &menu3, wscreen)){
718 new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil);
737 /* else fall through */
746 button2menu(Window *w)
749 menu2str[Scroll] = "noscroll";
751 menu2str[Scroll] = "scroll";
752 switch(menuhit(2, mousectl, &menu2, wscreen)){
783 waddraw(w, snarf, nsnarf);
784 if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
785 waddraw(w, L"\n", 1);
787 winsert(w, snarf, nsnarf, w->nr);
788 if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
789 winsert(w, L"\n", 1, w->nr);
791 wsetselect(w, w->nr, w->nr);
796 if(w->scrolling ^= 1)
800 flushimage(display, 1);
801 wsendctlmesg(w, Wakeup, ZR, nil);
807 p.x = max(screen->clipr.min.x, p.x);
808 p.x = min(screen->clipr.max.x-1, p.x);
809 p.y = max(screen->clipr.min.y, p.y);
810 p.y = min(screen->clipr.max.y-1, p.y);
823 riosetcursor(&crosscursor);
824 while(mouse->buttons == 0)
826 p0 = onscreen(mouse->xy);
831 while(mouse->buttons == 4){
832 if(!eqpt(mouse->xy, p)){
833 p = onscreen(mouse->xy);
834 r = canonrect(Rpt(p0, p));
835 r = whichrect(r, p, whichcorner(r, p));
836 if(Dx(r)>5 && Dy(r)>5){
837 i = allocwindow(wscreen, r, Refnone, DNofill);
842 border(i, r, Selborder, sizecol, ZP);
843 draw(i, insetrect(r, Selborder), cols[BACK], nil, ZP);
848 if(mouse->buttons != 0)
850 if(i==nil || !goodrect(r))
853 i = allocwindow(wscreen, oi->r, Refbackup, DNofill);
857 riosetcursor(corners[whichcorner(i->r, mouse->xy)]);
864 flushimage(display, 1);
865 while(mouse->buttons)
874 drawedge(Image **bp, Rectangle r)
877 if(b != nil && Dx(b->r) == Dx(r) && Dy(b->r) == Dy(r))
878 originwindow(b, r.min, r.min);
881 b = allocwindow(wscreen, r, Refbackup, DNofill);
882 if(b != nil) draw(b, r, sizecol, nil, ZP);
888 drawborder(Rectangle r, int show)
893 for(i = 0; i < 4; i++){
899 drawedge(&b[0], Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y));
900 drawedge(&b[1], Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth));
901 drawedge(&b[2], Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y));
902 drawedge(&b[3], Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y));
909 Point p, op, d, dm, om;
913 riosetcursor(&boxcursor);
915 dm = subpt(om, w->screenr.min);
916 d = subpt(w->screenr.max, w->screenr.min);
918 drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
919 while(mouse->buttons==4){
920 p = subpt(mouse->xy, dm);
922 drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
927 r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
930 riosetcursor(inborder(r, p) ? corners[whichcorner(r, p)] : nil);
932 if(mouse->buttons!=0 || !goodrect(r) || eqrect(r, w->screenr)){
933 flushimage(display, 1);
934 while(mouse->buttons)
938 return allocwindow(wscreen, r, Refbackup, DNofill);
946 int which, owhich, but;
950 but = mouse->buttons;
951 startp = onscreen(mouse->xy);
953 while(mouse->buttons == but) {
954 p = onscreen(mouse->xy);
955 which = whichcorner(or, p);
956 if(which != owhich && which != 4 && (owhich|~which) & 1){
958 riosetcursor(corners[which]);
960 r = whichrect(or, p, owhich);
961 if(!eqrect(r, or) && goodrect(r)){
968 if(mouse->buttons!=0 || !goodrect(or) || eqrect(or, w->screenr)
969 || abs(p.x-startp.x)+abs(p.y-startp.y) <= 1){
970 flushimage(display, 1);
971 while(mouse->buttons)
975 return allocwindow(wscreen, or, Refbackup, DNofill);
984 riosetcursor(&sightcursor);
985 while(mouse->buttons == 0)
987 if(mouse->buttons == 4)
988 w = wpointto(mouse->xy);
992 while(mouse->buttons){
993 if(mouse->buttons!=4 && w !=nil){ /* cancel */
999 if(w != nil && wpointto(mouse->xy) != w)
1014 wsendctlmesg(w, Deleted, ZR, nil);
1029 wsendctlmesg(w, Reshaped, i->r, i);
1045 wsendctlmesg(w, Reshaped, i->r, i);
1055 for(j=0; j<nhidden; j++)
1056 if(hidden[j] == w) /* already hidden */
1058 if(nhidden >= nelem(hidden))
1061 i = allocimage(display, w->screenr, w->i->chan, 0, DNofill);
1063 hidden[nhidden++] = w;
1064 wsendctlmesg(w, Reshaped, ZR, i);
1076 for(j=0; j<nhidden; j++)
1080 return -1; /* not hidden */
1082 i = allocwindow(wscreen, w->i->r, Refbackup, DNofill);
1085 memmove(hidden+j, hidden+j+1, (nhidden-j)*sizeof(Window*));
1086 wsendctlmesg(w, Reshaped, w->i->r, i);
1117 /* uncover obscured window */
1118 for(j=0; j<nwindow; j++)
1129 new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
1133 Channel *cm, *ck, *cctl, *cpid;
1138 if(hideit && nhidden >= nelem(hidden)){
1142 cm = chancreate(sizeof(Mouse), 0);
1143 ck = chancreate(sizeof(char*), 0);
1144 cctl = chancreate(sizeof(Wctlmesg), 4);
1145 cpid = chancreate(sizeof(int), 0);
1146 if(cm==nil || ck==nil || cctl==nil)
1147 error("new: channel alloc failed");
1148 mc = emalloc(sizeof(Mousectl));
1152 w = wmk(i, mc, ck, cctl, scrollit);
1153 free(mc); /* wmk copies *mc */
1154 window = erealloc(window, ++nwindow*sizeof(Window*));
1155 window[nwindow-1] = w;
1157 hidden[nhidden++] = w;
1160 threadcreate(winctl, w, 8192);
1164 arg = emalloc(5*sizeof(void*));
1173 proccreate(winshell, arg, 8192);
1178 /* window creation failed */
1179 wsendctlmesg(w, Deleted, ZR, nil);
1187 w->dir = estrdup(dir);
1197 char buf[128], *p, *e;
1198 int fd, cfd, kfd, n;
1200 threadsetname("kbdproc");
1202 if((fd = open("/dev/cons", OREAD)) < 0){
1206 if((cfd = open("/dev/consctl", OWRITE)) < 0){
1210 fprint(cfd, "rawon");
1212 if(sendp(c, nil) <= 0)
1215 if((kfd = open("/dev/kbd", OREAD)) >= 0){
1218 /* only serve a kbd file per window when we got one */
1221 /* read kbd state */
1222 while((n = read(kfd, buf, sizeof(buf)-1)) > 0){
1226 for(p = buf; p < e; p += strlen(p)+1)
1227 chanprint(c, "%s", p);
1230 /* read single characters */
1235 e = buf + sizeof(buf);
1236 if((n = read(fd, p, e-p)) <= 0)
1239 while(p < e && fullrune(p, e - p)){
1240 p += chartorune(&r, p);
1242 chanprint(c, "c%C", r);
1249 send(exitchan, nil);
1258 c = chancreate(sizeof(char*), 16);
1259 procrfork(kbdproc, c, STACK, RFCFDG);