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*);
42 void deletethread(void*);
44 Channel* initkbd(void);
87 char *menu3str[100] = {
102 char *rcargv[] = { "rc", "-i", nil };
103 char *kbdargv[] = { "rc", "-c", nil, nil };
105 int errorshouldabort = 0;
108 derror(Display*, char *errorstr)
116 fprint(2, "usage: rio [-b] [-f font] [-i initcmd] [-k kbdcmd] [-s]\n");
121 threadmain(int argc, char *argv[])
123 char *initstr, *kbdin, *s;
129 if(strstr(argv[0], ".out") == nil){
130 menu3str[Exit] = nil;
141 fontname = EARGF(usage());
144 initstr = EARGF(usage());
149 kbdin = EARGF(usage());
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");
192 exitchan = chancreate(sizeof(int), 0);
193 winclosechan = chancreate(sizeof(Window*), 0);
194 deletechan = chancreate(sizeof(char*), 0);
198 mousectl = initmouse(nil, screen);
200 error("can't find mouse");
204 error("can't find keyboard");
205 wscreen = allocscreen(screen, background, 0);
207 error("can't allocate screen");
208 draw(view, viewr, background, nil, ZP);
209 flushimage(display, 1);
212 threadcreate(keyboardthread, nil, STACK);
213 threadcreate(mousethread, nil, STACK);
214 threadcreate(winclosethread, nil, STACK);
215 threadcreate(deletethread, nil, STACK);
216 filsys = filsysinit(xfidinit());
219 fprint(2, "rio: can't create file system server: %r\n");
221 errorshouldabort = 1; /* suicide if there's trouble after this */
223 proccreate(initcmd, initstr, STACK);
227 r.min.y = r.max.y-Dy(r)/3;
228 i = allocwindow(wscreen, r, Refbackup, DNofill);
229 wkeyboard = new(i, FALSE, scrolling, 0, nil, "/bin/rc", kbdargv);
231 error("can't create keyboard window");
233 threadnotify(shutdown, 1);
241 * /dev/snarf updates when the file is closed, so we must open our own
242 * fd here rather than use snarffd
249 if(snarffd<0 || nsnarf==0)
251 fd = open("/dev/snarf", OWRITE);
254 /* snarf buffer could be huge, so fprint will truncate; do it in blocks */
255 for(i=0; i<nsnarf; i+=n){
259 if(fprint(fd, "%.*S", n, snarf+i) < 0)
279 if((s = realloc(sn, i+1024+1)) == nil)
282 if((n = read(snarffd, sn+i, 1024)) <= 0)
289 if((snarf = runerealloc(snarf, i+1)) != nil)
290 cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls);
300 rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG);
301 procexecl(nil, "/bin/rc", "rc", "-c", cmd, nil);
302 fprint(2, "rio: exec failed: %r\n");
316 shutdown(void *, char *msg)
319 static Lock shutdownlk;
322 for(i=0; oknotes[i]; i++)
323 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0){
324 lock(&shutdownlk); /* only one can threadexitsall */
327 fprint(2, "rio %d: abort: %s\n", getpid(), msg);
338 for(i=0; i<nwindow; i++)
339 if(window[i]->notefd >= 0)
340 write(window[i]->notefd, "hangup", 6);
344 keyboardthread(void*)
348 threadsetname("keyboardthread");
350 while(s = recvp(kbdchan)){
351 if(*s == 'k' || *s == 'K')
352 shiftdown = utfrune(s+1, Kshift) != nil;
353 if(input == nil || sendp(input->ck, s) <= 0)
359 inborder(Rectangle r, Point xy)
361 return ptinrect(xy, r) && !ptinrect(xy, insetrect(r, Selborder));
365 whichrect(Rectangle r, Point p, int which)
368 case 0: /* top left */
369 r = Rect(p.x, p.y, r.max.x, r.max.y);
371 case 2: /* top right */
372 r = Rect(r.min.x, p.y, p.x+1, r.max.y);
374 case 6: /* bottom left */
375 r = Rect(p.x, r.min.y, r.max.x, p.y+1);
377 case 8: /* bottom right */
378 r = Rect(r.min.x, r.min.y, p.x+1, p.y+1);
380 case 1: /* top edge */
381 r = Rect(r.min.x, p.y, r.max.x, r.max.y);
383 case 5: /* right edge */
384 r = Rect(r.min.x, r.min.y, p.x+1, r.max.y);
386 case 7: /* bottom edge */
387 r = Rect(r.min.x, r.min.y, r.max.x, p.y+1);
389 case 3: /* left edge */
390 r = Rect(p.x, r.min.y, r.max.x, r.max.y);
397 portion(int x, int lo, int hi)
402 return x > 0 ? 2 : 0;
411 whichcorner(Rectangle r, Point p)
415 i = portion(p.x, r.min.x, r.max.x);
416 j = portion(p.y, r.min.y, r.max.y);
420 /* thread to allow fsysproc to synchronize window closing with main proc */
422 winclosethread(void*)
426 threadsetname("winclosethread");
428 w = recvp(winclosechan);
433 /* thread to make Deleted windows that the client still holds disappear offscreen after an interval */
440 threadsetname("deletethread");
442 s = recvp(deletechan);
443 i = namedimage(display, s);
445 /* move it off-screen to hide it, since client is slow in letting it go */
446 originwindow(i, i->r.min, view->r.max);
448 flushimage(display, 1);
455 deletetimeoutproc(void *v)
460 sleep(750); /* remove window from screen after 3/4 of a second */
461 sendp(deletechan, s);
465 * Button 6 - keyboard toggle - has been pressed.
466 * Send event to keyboard, wait for button up, send that.
467 * Note: there is no coordinate translation done here; this
468 * is just about getting button 6 to the keyboard simulator.
473 send(wkeyboard->mc.c, mouse);
476 while(mouse->buttons & (1<<5));
477 send(wkeyboard->mc.c, mouse);
483 int sending, inside, scrolling, moving;
493 static Alt alts[NALT+1];
495 threadsetname("mousethread");
499 alts[MReshape].c = mousectl->resizec;
500 alts[MReshape].v = nil;
501 alts[MReshape].op = CHANRCV;
502 alts[MMouse].c = mousectl->c;
503 alts[MMouse].v = &mousectl->Mouse;
504 alts[MMouse].op = CHANRCV;
505 alts[NALT].op = CHANEND;
513 if(wkeyboard!=nil && (mouse->buttons & (1<<5))){
520 /* override everything for the keyboard window */
521 if(wkeyboard!=nil && ptinrect(mouse->xy, wkeyboard->screenr)){
522 /* make sure it's on top; this call is free if it is */
526 if(winput!=nil && !winput->deleted && winput->i!=nil){
527 /* convert to logical coordinates */
528 xy.x = mouse->xy.x + (winput->i->r.min.x-winput->screenr.min.x);
529 xy.y = mouse->xy.y + (winput->i->r.min.y-winput->screenr.min.y);
531 /* the up and down scroll buttons are not subject to the usual rules */
532 if((mouse->buttons&(8|16)) && !winput->mouseopen)
535 inside = ptinrect(mouse->xy, insetrect(winput->screenr, Selborder));
536 if(winput->mouseopen)
539 scrolling = mouse->buttons;
541 scrolling = mouse->buttons && ptinrect(xy, winput->scrollr);
542 /* topped will be zero or less if window has been bottomed */
543 if(sending == FALSE && !scrolling && inborder(winput->screenr, mouse->xy) && winput->topped>0)
545 else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
551 wsetcursor(winput, FALSE);
552 if(mouse->buttons == 0)
554 tmp = mousectl->Mouse;
556 send(winput->mc.c, &tmp);
559 if(moving && (mouse->buttons&7)){
562 if(mouse->buttons & 3)
563 i = bandsize(winput);
568 wsendctlmesg(winput, Reshaped, i->r, i);
572 w = wpointto(mouse->xy);
573 if(w!=nil && inborder(w->screenr, mouse->xy))
574 riosetcursor(corners[whichcorner(w->screenr, mouse->xy)]);
576 wsetcursor(w, FALSE);
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 || inborder(w->screenr, mouse->xy)))
604 while(mousectl->buttons);
605 goto Again; /* recalculate mouse position, cursor */
610 wtopcmp(void *a, void *b)
612 return (*(Window**)a)->topped - (*(Window**)b)->topped;
624 if(getwindow(display, Refnone) < 0)
625 error("failed to re-attach window");
629 wscreen = allocscreen(screen, background, 0);
631 error("can't re-allocate screen");
632 draw(view, view->r, background, nil, ZP);
633 o = subpt(viewr.max, viewr.min);
634 n = subpt(view->clipr.max, view->clipr.min);
635 qsort(window, nwindow, sizeof(window[0]), wtopcmp);
636 for(i=0; i<nwindow; i++){
638 r = rectsubpt(w->i->r, viewr.min);
639 r.min.x = (r.min.x*n.x)/o.x;
640 r.min.y = (r.min.y*n.y)/o.y;
641 r.max.x = (r.max.x*n.x)/o.x;
642 r.max.y = (r.max.y*n.y)/o.y;
643 r = rectaddpt(r, view->clipr.min);
645 r = rectsubpt(w->i->r, subpt(w->i->r.min, r.min));
646 for(j=0; j<nhidden; j++)
651 im = allocimage(display, r, screen->chan, 0, DNofill);
654 im = allocwindow(wscreen, r, Refbackup, DNofill);
656 wsendctlmesg(w, Reshaped, r, im);
660 flushimage(display, 1);
664 obscured(Window *w, Rectangle r, int i)
668 if(Dx(r) < font->height || Dy(r) < font->height)
670 if(!rectclip(&r, screen->r))
672 for(; i<nwindow; i++){
674 if(t == w || t->topped <= w->topped)
676 if(Dx(t->screenr) == 0 || Dy(t->screenr) == 0 || rectXrect(r, t->screenr) == 0)
678 if(r.min.y < t->screenr.min.y)
679 if(!obscured(w, Rect(r.min.x, r.min.y, r.max.x, t->screenr.min.y), i))
681 if(r.min.x < t->screenr.min.x)
682 if(!obscured(w, Rect(r.min.x, r.min.y, t->screenr.min.x, r.max.y), i))
684 if(r.max.y > t->screenr.max.y)
685 if(!obscured(w, Rect(r.min.x, t->screenr.max.y, r.max.x, r.max.y), i))
687 if(r.max.x > t->screenr.max.x)
688 if(!obscured(w, Rect(t->screenr.max.x, r.min.y, r.max.x, r.max.y), i))
699 static char buf[NBUF*UTFmax];
708 k += chartorune(&r, s+k);
712 strcpy(buf+k, "...");
713 while((l-i) >= NBUF/2-4){
714 k += chartorune(&r, s+k);
727 for(i=0; i<nwindow; i++){
729 if(window[i] == hidden[j])
732 if(obscured(window[i], window[i]->screenr, 0)){
733 hidden[n++] = window[i];
734 if(n >= nelem(hidden))
738 if(n >= nelem(menu3str)-Hidden)
739 n = nelem(menu3str)-Hidden-1;
741 free(menu3str[i+Hidden]);
742 menu3str[i+Hidden] = shortlabel(hidden[i]->label);
744 for(i+=Hidden; menu3str[i]; i++){
749 switch(i = menuhit(3, mousectl, &menu3, wscreen)){
753 new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil);
772 /* else fall through */
781 button2menu(Window *w)
784 menu2str[Scroll] = "noscroll";
786 menu2str[Scroll] = "scroll";
787 switch(menuhit(2, mousectl, &menu2, wscreen)){
818 waddraw(w, snarf, nsnarf);
819 if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
820 waddraw(w, L"\n", 1);
822 winsert(w, snarf, nsnarf, w->nr);
823 if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
824 winsert(w, L"\n", 1, w->nr);
826 wsetselect(w, w->nr, w->nr);
831 if(w->scrolling ^= 1)
835 flushimage(display, 1);
836 wsendctlmesg(w, Wakeup, ZR, nil);
842 p.x = max(screen->clipr.min.x, p.x);
843 p.x = min(screen->clipr.max.x, p.x);
844 p.y = max(screen->clipr.min.y, p.y);
845 p.y = min(screen->clipr.max.y, p.y);
858 riosetcursor(&crosscursor);
859 while(mouse->buttons == 0)
861 p0 = onscreen(mouse->xy);
866 while(mouse->buttons == 4){
867 if(!eqpt(mouse->xy, p)){
868 p = onscreen(mouse->xy);
869 r = canonrect(Rpt(p0, p));
870 r = whichrect(r, p, whichcorner(r, 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);
883 if(mouse->buttons != 0)
885 if(i==nil || !goodrect(r))
888 i = allocwindow(wscreen, oi->r, Refbackup, DNofill);
892 riosetcursor(corners[whichcorner(i->r, mouse->xy)]);
899 flushimage(display, 1);
900 while(mouse->buttons)
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));
944 Point p, op, d, dm, om;
948 riosetcursor(&boxcursor);
950 dm = subpt(om, w->screenr.min);
951 d = subpt(w->screenr.max, w->screenr.min);
953 drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
954 while(mouse->buttons==4){
955 p = subpt(mouse->xy, dm);
957 drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
962 r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
965 riosetcursor(inborder(r, p) ? corners[whichcorner(r, p)] : nil);
967 if(mouse->buttons!=0 || !goodrect(r) || eqrect(r, w->screenr)){
968 flushimage(display, 1);
969 while(mouse->buttons)
973 return allocwindow(wscreen, r, Refbackup, DNofill);
984 but = mouse->buttons;
985 which = whichcorner(w->screenr, p);
986 riosetcursor(corners[which]);
987 r = whichrect(w->screenr, p, which);
992 while(mouse->buttons==but){
993 p = onscreen(mouse->xy);
994 r = whichrect(w->screenr, p, which);
995 if(!eqrect(r, or) && goodrect(r)){
1003 if(mouse->buttons!=0 || !goodrect(or) || eqrect(or, w->screenr)
1004 || abs(p.x-startp.x)+abs(p.y-startp.y) <= 1){
1005 flushimage(display, 1);
1006 while(mouse->buttons)
1007 readmouse(mousectl);
1010 return allocwindow(wscreen, or, Refbackup, DNofill);
1019 riosetcursor(&sightcursor);
1020 while(mouse->buttons == 0)
1021 readmouse(mousectl);
1022 if(mouse->buttons == 4)
1023 w = wpointto(mouse->xy);
1027 while(mouse->buttons){
1028 if(mouse->buttons!=4 && w !=nil){ /* cancel */
1032 readmouse(mousectl);
1034 if(w != nil && wpointto(mouse->xy) != w)
1049 wsendctlmesg(w, Deleted, ZR, nil);
1064 wsendctlmesg(w, Reshaped, i->r, i);
1080 wsendctlmesg(w, Reshaped, i->r, i);
1090 for(j=0; j<nhidden; j++)
1091 if(hidden[j] == w) /* already hidden */
1093 if(nhidden >= nelem(hidden))
1096 i = allocimage(display, w->screenr, w->i->chan, 0, DNofill);
1098 hidden[nhidden++] = w;
1099 wsendctlmesg(w, Reshaped, ZR, i);
1111 for(j=0; j<nhidden; j++)
1115 return -1; /* not hidden */
1117 i = allocwindow(wscreen, w->i->r, Refbackup, DNofill);
1120 memmove(hidden+j, hidden+j+1, (nhidden-j)*sizeof(Window*));
1121 wsendctlmesg(w, Reshaped, w->i->r, i);
1152 /* uncover obscured window */
1153 for(j=0; j<nwindow; j++)
1164 new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
1168 Channel *cm, *ck, *cctl, *cpid;
1173 if(hideit && nhidden >= nelem(hidden)){
1177 cm = chancreate(sizeof(Mouse), 0);
1178 ck = chancreate(sizeof(char*), 0);
1179 cctl = chancreate(sizeof(Wctlmesg), 4);
1180 cpid = chancreate(sizeof(int), 0);
1181 if(cm==nil || ck==nil || cctl==nil)
1182 error("new: channel alloc failed");
1183 mc = emalloc(sizeof(Mousectl));
1187 w = wmk(i, mc, ck, cctl, scrollit);
1188 free(mc); /* wmk copies *mc */
1189 window = erealloc(window, ++nwindow*sizeof(Window*));
1190 window[nwindow-1] = w;
1192 hidden[nhidden++] = w;
1195 threadcreate(winctl, w, 8192);
1199 arg = emalloc(5*sizeof(void*));
1208 proccreate(winshell, arg, 8192);
1213 /* window creation failed */
1214 wsendctlmesg(w, Deleted, ZR, nil);
1222 w->dir = estrdup(dir);
1232 char buf[128], *p, *e;
1233 int fd, cfd, kfd, n;
1235 threadsetname("kbdproc");
1237 if((fd = open("/dev/cons", OREAD)) < 0){
1241 if((cfd = open("/dev/consctl", OWRITE)) < 0){
1245 fprint(cfd, "rawon");
1247 if(sendp(c, nil) <= 0)
1250 if((kfd = open("/dev/kbd", OREAD)) >= 0){
1253 /* only serve a kbd file per window when we got one */
1256 /* read kbd state */
1257 while((n = read(kfd, buf, sizeof(buf)-1)) > 0){
1261 for(p = buf; p < e; p += strlen(p)+1)
1262 chanprint(c, "%s", p);
1265 /* read single characters */
1270 e = buf + sizeof(buf);
1271 if((n = read(fd, p, e-p)) <= 0)
1274 while(p < e && fullrune(p, e - p)){
1275 p += chartorune(&r, p);
1277 chanprint(c, "c%C", r);
1284 send(exitchan, nil);
1293 c = chancreate(sizeof(char*), 16);
1294 procrfork(kbdproc, c, STACK, RFCFDG);