]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/rio/rio.c
audiohda: fix syntax error
[plan9front.git] / sys / src / cmd / rio / rio.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include <plumb.h>
11 #include "dat.h"
12 #include "fns.h"
13
14 /*
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.
21  */
22
23 void            resize(void);
24 void            move(void);
25 void            delete(void);
26 void            hide(void);
27 void            unhide(int);
28 void            newtile(int);
29 Image*  sweep(void);
30 Image*  bandsize(Window*);
31 Image*  drag(Window*);
32 void            resized(void);
33 Channel *exitchan;      /* chan(int) */
34 Channel *winclosechan; /* chan(Window*); */
35 Channel *kbdchan;       /* chan(char*); */
36 Rectangle       viewr;
37 int             threadrforkflag = 0;    /* should be RFENVG but that hides rio from plumber */
38
39 void    mousethread(void*);
40 void    keyboardthread(void*);
41 void    winclosethread(void*);
42 void    initcmd(void*);
43 Channel* initkbd(void);
44
45 char            *fontname;
46
47 enum
48 {
49         New,
50         Reshape,
51         Move,
52         Delete,
53         Hide,
54         Exit,
55 };
56
57 enum
58 {
59         Cut,
60         Paste,
61         Snarf,
62         Plumb,
63         Look,
64         Send,
65         Scroll,
66 };
67
68 char            *menu2str[] = {
69  [Cut]          "cut",
70  [Paste]                "paste",
71  [Snarf]                "snarf",
72  [Plumb]                "plumb",
73  [Look]         "look",
74  [Send]         "send",
75  [Scroll]               "scroll",
76                         nil
77 };
78
79 Menu menu2 =
80 {
81         menu2str
82 };
83
84 int     Hidden = Exit+1;
85
86 char            *menu3str[100] = {
87  [New]          "New",
88  [Reshape]      "Resize",
89  [Move]         "Move",
90  [Delete]               "Delete",
91  [Hide]         "Hide",
92  [Exit]         "Exit",
93                         nil
94 };
95
96 Menu menu3 =
97 {
98         menu3str
99 };
100
101 char *rcargv[] = { "rc", "-i", nil };
102 char *kbdargv[] = { "rc", "-c", nil, nil };
103
104 int errorshouldabort = 0;
105
106 void
107 derror(Display*, char *errorstr)
108 {
109         error(errorstr);
110 }
111
112 void
113 usage(void)
114 {
115         fprint(2, "usage: rio [-b] [-f font] [-i initcmd] [-k kbdcmd] [-s]\n");
116         exits("usage");
117 }
118
119 void
120 threadmain(int argc, char *argv[])
121 {
122         char *initstr, *kbdin, *s;
123         char buf[256];
124         Image *i;
125         Rectangle r;
126
127         if(strstr(argv[0], ".out") == nil){
128                 menu3str[Exit] = nil;
129                 Hidden--;
130         }
131         initstr = nil;
132         kbdin = nil;
133         maxtab = 0;
134         ARGBEGIN{
135         case 'b':
136                 reverse = ~0xFF;
137                 break;
138         case 'f':
139                 fontname = EARGF(usage());
140                 break;
141         case 'i':
142                 initstr = EARGF(usage());
143                 break;
144         case 'k':
145                 if(kbdin != nil)
146                         usage();
147                 kbdin = EARGF(usage());
148                 break;
149         case 's':
150                 scrolling = TRUE;
151                 break;
152         case 'D':
153                 debug++;
154                 break;
155         default:
156                 usage();
157         }ARGEND
158
159         if(getwd(buf, sizeof buf) == nil)
160                 startdir = estrdup(".");
161         else
162                 startdir = estrdup(buf);
163         if(fontname == nil)
164                 fontname = getenv("font");
165         s = getenv("tabstop");
166         if(s != nil)
167                 maxtab = strtol(s, nil, 0);
168         if(maxtab == 0)
169                 maxtab = 4;
170         free(s);
171
172         if(fontname){
173                 /* check font before barging ahead */
174                 if(access(fontname, 0) < 0){
175                         fprint(2, "rio: can't access %s: %r\n", fontname);
176                         exits("font open");
177                 }
178                 putenv("font", fontname);
179         }
180
181         snarffd = open("/dev/snarf", OREAD|OCEXEC);
182         gotscreen = access("/dev/screen", AEXIST)==0;
183
184         if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) < 0){
185                 fprint(2, "rio: can't open display: %r\n");
186                 exits("display open");
187         }
188         iconinit();
189
190         exitchan = chancreate(sizeof(int), 0);
191         winclosechan = chancreate(sizeof(Window*), 0);
192
193         view = screen;
194         viewr = view->r;
195         mousectl = initmouse(nil, screen);
196         if(mousectl == nil)
197                 error("can't find mouse");
198         mouse = mousectl;
199         kbdchan = initkbd();
200         if(kbdchan == nil)
201                 error("can't find keyboard");
202         wscreen = allocscreen(screen, background, 0);
203         if(wscreen == nil)
204                 error("can't allocate screen");
205         draw(view, viewr, background, nil, ZP);
206         flushimage(display, 1);
207
208         timerinit();
209         threadcreate(keyboardthread, nil, STACK);
210         threadcreate(mousethread, nil, STACK);
211         threadcreate(winclosethread, nil, STACK);
212         filsys = filsysinit(xfidinit());
213
214         if(filsys == nil)
215                 fprint(2, "rio: can't create file system server: %r\n");
216         else{
217                 errorshouldabort = 1;   /* suicide if there's trouble after this */
218                 if(initstr)
219                         proccreate(initcmd, initstr, STACK);
220                 if(kbdin){
221                         kbdargv[2] = kbdin;
222                         r = screen->r;
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);
226                         if(wkeyboard == nil)
227                                 error("can't create keyboard window");
228                 }
229                 threadnotify(shutdown, 1);
230                 recv(exitchan, nil);
231         }
232         killprocs();
233         threadexitsall(nil);
234 }
235
236 /*
237  * /dev/snarf updates when the file is closed, so we must open our own
238  * fd here rather than use snarffd
239  */
240 void
241 putsnarf(void)
242 {
243         int fd, i, n;
244
245         if(snarffd<0 || nsnarf==0)
246                 return;
247         fd = open("/dev/snarf", OWRITE|OCEXEC);
248         if(fd < 0)
249                 return;
250         /* snarf buffer could be huge, so fprint will truncate; do it in blocks */
251         for(i=0; i<nsnarf; i+=n){
252                 n = nsnarf-i;
253                 if(n >= 256)
254                         n = 256;
255                 if(fprint(fd, "%.*S", n, snarf+i) < 0)
256                         break;
257         }
258         close(fd);
259 }
260
261 void
262 getsnarf(void)
263 {
264         int i, n, nb, nulls;
265         char *s, *sn;
266
267         if(snarffd < 0)
268                 return;
269         sn = nil;
270         i = 0;
271         seek(snarffd, 0, 0);
272         for(;;){
273                 if(i > MAXSNARF)
274                         break;
275                 if((s = realloc(sn, i+1024+1)) == nil)
276                         break;
277                 sn = s;
278                 if((n = read(snarffd, sn+i, 1024)) <= 0)
279                         break;
280                 i += n;
281         }
282         if(i == 0)
283                 return;
284         sn[i] = 0;
285         if((snarf = runerealloc(snarf, i+1)) != nil)
286                 cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls);
287         free(sn);
288 }
289
290 void
291 initcmd(void *arg)
292 {
293         char *cmd;
294
295         cmd = arg;
296         rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG);
297         procexecl(nil, "/bin/rc", "rc", "-c", cmd, nil);
298         fprint(2, "rio: exec failed: %r\n");
299         exits("exec");
300 }
301
302 char *oknotes[] =
303 {
304         "delete",
305         "hangup",
306         "kill",
307         "exit",
308         nil
309 };
310
311 int
312 shutdown(void *, char *msg)
313 {
314         int i;
315         static Lock shutdownlk;
316         
317         killprocs();
318         for(i=0; oknotes[i]; i++)
319                 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0){
320                         lock(&shutdownlk);      /* only one can threadexitsall */
321                         threadexitsall(msg);
322                 }
323         fprint(2, "rio %d: abort: %s\n", getpid(), msg);
324         abort();
325         exits(msg);
326         return 0;
327 }
328
329 void
330 killprocs(void)
331 {
332         int i;
333
334         for(i=0; i<nwindow; i++)
335                 if(window[i]->notefd >= 0)
336                         write(window[i]->notefd, "hangup", 6); 
337 }
338
339 void
340 keyboardthread(void*)
341 {
342         char *s;
343
344         threadsetname("keyboardthread");
345
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)
350                         free(s);
351         }
352 }
353
354 int
355 inborder(Rectangle r, Point xy)
356 {
357         return ptinrect(xy, r) && !ptinrect(xy, insetrect(r, Selborder));
358 }
359
360 Rectangle
361 whichrect(Rectangle r, Point p, int which)
362 {
363         switch(which){
364         case 0: /* top left */
365                 r = Rect(p.x, p.y, r.max.x, r.max.y);
366                 break;
367         case 2: /* top right */
368                 r = Rect(r.min.x, p.y, p.x+1, r.max.y);
369                 break;
370         case 6: /* bottom left */
371                 r = Rect(p.x, r.min.y, r.max.x, p.y+1);
372                 break;
373         case 8: /* bottom right */
374                 r = Rect(r.min.x, r.min.y, p.x+1, p.y+1);
375                 break;
376         case 1: /* top edge */
377                 r = Rect(r.min.x, p.y, r.max.x, r.max.y);
378                 break;
379         case 5: /* right edge */
380                 r = Rect(r.min.x, r.min.y, p.x+1, r.max.y);
381                 break;
382         case 7: /* bottom edge */
383                 r = Rect(r.min.x, r.min.y, r.max.x, p.y+1);
384                 break;
385         case 3: /* left edge */
386                 r = Rect(p.x, r.min.y, r.max.x, r.max.y);
387                 break;
388         }
389         return canonrect(r);
390 }
391
392 int
393 portion(int x, int lo, int hi)
394 {
395         x -= lo;
396         hi -= lo;
397         if(x < hi/2){
398                 if(x < 20)
399                         return 0;
400         } else {
401                 if(x > hi-20)
402                         return 2;
403         }
404         return 1;
405 }
406
407 int
408 whichcorner(Rectangle r, Point p)
409 {
410         int i, j;
411         
412         i = portion(p.x, r.min.x, r.max.x);
413         j = portion(p.y, r.min.y, r.max.y);
414         return 3*j+i;
415 }
416
417 /* thread to allow fsysproc to synchronize window closing with main proc */
418 void
419 winclosethread(void*)
420 {
421         Window *w;
422
423         threadsetname("winclosethread");
424         for(;;){
425                 w = recvp(winclosechan);
426                 wclose(w);
427         }
428 }
429
430 /*
431  * Button 6 - keyboard toggle - has been pressed.
432  * Send event to keyboard, wait for button up, send that.
433  * Note: there is no coordinate translation done here; this
434  * is just about getting button 6 to the keyboard simulator.
435  */
436 void
437 keyboardhide(void)
438 {
439         send(wkeyboard->mc.c, mouse);
440         do
441                 readmouse(mousectl);
442         while(mouse->buttons & (1<<5));
443         send(wkeyboard->mc.c, mouse);
444 }
445
446 void
447 mousethread(void*)
448 {
449         int sending, inside, scrolling, moving;
450         Window *w, *winput;
451         Image *i;
452         Point xy;
453         Mouse tmp;
454         enum {
455                 MReshape,
456                 MMouse,
457                 NALT
458         };
459         static Alt alts[NALT+1];
460
461         threadsetname("mousethread");
462         sending = FALSE;
463         scrolling = FALSE;
464
465         alts[MReshape].c = mousectl->resizec;
466         alts[MReshape].v = nil;
467         alts[MReshape].op = CHANRCV;
468         alts[MMouse].c = mousectl->c;
469         alts[MMouse].v = &mousectl->Mouse;
470         alts[MMouse].op = CHANRCV;
471         alts[NALT].op = CHANEND;
472
473         for(;;)
474             switch(alt(alts)){
475                 case MReshape:
476                         resized();
477                         break;
478                 case MMouse:
479                         if(wkeyboard!=nil && (mouse->buttons & (1<<5))){
480                                 keyboardhide();
481                                 break;
482                         }
483                 Again:
484                         moving = FALSE;
485                         winput = input;
486                         /* override everything for the keyboard window */
487                         if(wkeyboard!=nil && ptinrect(mouse->xy, wkeyboard->screenr)){
488                                 /* make sure it's on top; this call is free if it is */
489                                 wtopme(wkeyboard);
490                                 winput = wkeyboard;
491                         }
492                         if(winput!=nil && !winput->deleted && winput->i!=nil){
493                                 /* convert to logical coordinates */
494                                 xy.x = mouse->xy.x + (winput->i->r.min.x-winput->screenr.min.x);
495                                 xy.y = mouse->xy.y + (winput->i->r.min.y-winput->screenr.min.y);
496
497                                 /* the up and down scroll buttons are not subject to the usual rules */
498                                 if((mouse->buttons&(8|16)) && !winput->mouseopen)
499                                         goto Sending;
500
501                                 inside = ptinrect(mouse->xy, insetrect(winput->screenr, Selborder));
502                                 if(winput->mouseopen)
503                                         scrolling = FALSE;
504                                 else if(scrolling)
505                                         scrolling = mouse->buttons;
506                                 else
507                                         scrolling = mouse->buttons && ptinrect(xy, winput->scrollr);
508                                 /* topped will be zero or less if window has been bottomed */
509                                 if(sending == FALSE && !scrolling && inborder(winput->screenr, mouse->xy) && winput->topped>0)
510                                         moving = TRUE;
511                                 else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
512                                         sending = TRUE;
513                         }else
514                                 sending = FALSE;
515                         if(sending){
516                         Sending:
517                                 wsetcursor(winput, FALSE);
518                                 if(mouse->buttons == 0)
519                                         sending = FALSE;
520                                 tmp = mousectl->Mouse;
521                                 tmp.xy = xy;
522                                 send(winput->mc.c, &tmp);
523                                 continue;
524                         }
525                         if(moving && (mouse->buttons&7)){
526                                 incref(winput);
527                                 sweeping = TRUE;
528                                 if(mouse->buttons & 3)
529                                         i = bandsize(winput);
530                                 else
531                                         i = drag(winput);
532                                 sweeping = FALSE;
533                                 if(i != nil){
534                                         wcurrent(winput);
535                                         wsendctlmesg(winput, Reshaped, i->r, i);
536                                 }
537                                 wclose(winput);
538                                 continue;
539                         }
540                         w = wpointto(mouse->xy);
541                         if(w!=nil && inborder(w->screenr, mouse->xy))
542                                 riosetcursor(corners[whichcorner(w->screenr, mouse->xy)]);
543                         else
544                                 wsetcursor(w, FALSE);
545                         /* we're not sending the event, but if button is down maybe we should */
546                         if(mouse->buttons){
547                                 /* w->topped will be zero or less if window has been bottomed */
548                                 if(w==nil || (w==winput && w->topped>0)){
549                                         if(mouse->buttons & 1){
550                                                 ;
551                                         }else if(mouse->buttons & 2){
552                                                 if(winput && !winput->deleted && !winput->mouseopen){
553                                                         incref(winput);
554                                                         button2menu(winput);
555                                                         wclose(winput);
556                                                 }
557                                         }else if(mouse->buttons & 4)
558                                                 button3menu();
559                                 }else{
560                                         /* if button 1 event in the window, top the window and wait for button up. */
561                                         /* otherwise, top the window and pass the event on */
562                                         if(wtop(mouse->xy) && (mouse->buttons!=1 || inborder(w->screenr, mouse->xy)))
563                                                 goto Again;
564                                         goto Drain;
565                                 }
566                         }
567                         break;
568
569                 Drain:
570                         do
571                                 readmouse(mousectl);
572                         while(mousectl->buttons);
573                         goto Again;     /* recalculate mouse position, cursor */
574                 }
575 }
576
577 int
578 wtopcmp(void *a, void *b)
579 {
580         return (*(Window**)a)->topped - (*(Window**)b)->topped;
581 }
582
583 void
584 resized(void)
585 {
586         Image *im;
587         int i, j;
588         Rectangle r;
589         Point o, n;
590         Window *w;
591
592         if(getwindow(display, Refnone) < 0)
593                 error("failed to re-attach window");
594         freescrtemps();
595         view = screen;
596         freescreen(wscreen);
597         wscreen = allocscreen(screen, background, 0);
598         if(wscreen == nil)
599                 error("can't re-allocate screen");
600         draw(view, view->r, background, nil, ZP);
601         o = subpt(viewr.max, viewr.min);
602         n = subpt(view->clipr.max, view->clipr.min);
603         qsort(window, nwindow, sizeof(window[0]), wtopcmp);
604         for(i=0; i<nwindow; i++){
605                 w = window[i];
606                 r = rectsubpt(w->i->r, viewr.min);
607                 r.min.x = (r.min.x*n.x)/o.x;
608                 r.min.y = (r.min.y*n.y)/o.y;
609                 r.max.x = (r.max.x*n.x)/o.x;
610                 r.max.y = (r.max.y*n.y)/o.y;
611                 r = rectaddpt(r, view->clipr.min);
612                 if(!goodrect(r))
613                         r = rectsubpt(w->i->r, subpt(w->i->r.min, r.min));
614                 for(j=0; j<nhidden; j++)
615                         if(w == hidden[j])
616                                 break;
617                 incref(w);
618                 if(j < nhidden){
619                         im = allocimage(display, r, screen->chan, 0, DNofill);
620                         r = ZR;
621                 } else {
622                         im = allocwindow(wscreen, r, Refbackup, DNofill);
623                 }
624                 if(im!=nil)
625                         wsendctlmesg(w, Reshaped, r, im);
626                 wclose(w);
627         }
628         viewr = view->r;
629         flushimage(display, 1);
630 }
631
632 int
633 obscured(Window *w, Rectangle r, int i)
634 {
635         Window *t;
636
637         if(Dx(r) < font->height || Dy(r) < font->height)
638                 return 1;
639         if(!rectclip(&r, screen->r))
640                 return 1;
641         for(; i<nwindow; i++){
642                 t = window[i];
643                 if(t == w || t->topped <= w->topped)
644                         continue;
645                 if(Dx(t->screenr) == 0 || Dy(t->screenr) == 0 || rectXrect(r, t->screenr) == 0)
646                         continue;
647                 if(r.min.y < t->screenr.min.y)
648                         if(!obscured(w, Rect(r.min.x, r.min.y, r.max.x, t->screenr.min.y), i))
649                                 return 0;
650                 if(r.min.x < t->screenr.min.x)
651                         if(!obscured(w, Rect(r.min.x, r.min.y, t->screenr.min.x, r.max.y), i))
652                                 return 0;
653                 if(r.max.y > t->screenr.max.y)
654                         if(!obscured(w, Rect(r.min.x, t->screenr.max.y, r.max.x, r.max.y), i))
655                                 return 0;
656                 if(r.max.x > t->screenr.max.x)
657                         if(!obscured(w, Rect(t->screenr.max.x, r.min.y, r.max.x, r.max.y), i))
658                                 return 0;
659                 return 1;
660         }
661         return 0;
662 }
663
664 static char*
665 shortlabel(char *s)
666 {
667         enum { NBUF=60 };
668         static char buf[NBUF*UTFmax];
669         int i, k, l;
670         Rune r;
671
672         l = utflen(s);
673         if(l < NBUF-2)
674                 return estrdup(s);
675         k = i = 0;
676         while(i < NBUF/2){
677                 k += chartorune(&r, s+k);
678                 i++;
679         }
680         strncpy(buf, s, k);
681         strcpy(buf+k, "...");
682         while((l-i) >= NBUF/2-4){
683                 k += chartorune(&r, s+k);
684                 i++;
685         }
686         strcat(buf, s+k);
687         return estrdup(buf);
688 }
689
690 void
691 button3menu(void)
692 {
693         int i, j, n;
694
695         n = nhidden;
696         for(i=0; i<nwindow; i++){
697                 for(j=0; j<n; j++)
698                         if(window[i] == hidden[j])
699                                 break;
700                 if(j == n)
701                         if(obscured(window[i], window[i]->screenr, 0)){
702                                 hidden[n++] = window[i];
703                                 if(n >= nelem(hidden))
704                                         break;
705                         }
706         }
707         if(n >= nelem(menu3str)-Hidden)
708                 n = nelem(menu3str)-Hidden-1;
709         for(i=0; i<n; i++){
710                 free(menu3str[i+Hidden]);
711                 menu3str[i+Hidden] = shortlabel(hidden[i]->label);
712         }
713         for(i+=Hidden; menu3str[i]; i++){
714                 free(menu3str[i]);
715                 menu3str[i] = nil;
716         }
717         sweeping = TRUE;
718         switch(i = menuhit(3, mousectl, &menu3, wscreen)){
719         case -1:
720                 break;
721         case New:
722                 new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil);
723                 break;
724         case Reshape:
725                 resize();
726                 break;
727         case Move:
728                 move();
729                 break;
730         case Delete:
731                 delete();
732                 break;
733         case Hide:
734                 hide();
735                 break;
736         case Exit:
737                 if(Hidden > Exit){
738                         send(exitchan, nil);
739                         break;
740                 }
741                 /* else fall through */
742         default:
743                 unhide(i);
744                 break;
745         }
746         sweeping = FALSE;
747 }
748
749 void
750 button2menu(Window *w)
751 {
752         if(w->scrolling)
753                 menu2str[Scroll] = "noscroll";
754         else
755                 menu2str[Scroll] = "scroll";
756         switch(menuhit(2, mousectl, &menu2, wscreen)){
757         case Cut:
758                 wsnarf(w);
759                 wcut(w);
760                 wscrdraw(w);
761                 break;
762
763         case Snarf:
764                 wsnarf(w);
765                 break;
766
767         case Paste:
768                 getsnarf();
769                 wpaste(w);
770                 wscrdraw(w);
771                 break;
772
773         case Plumb:
774                 wplumb(w);
775                 break;
776
777         case Look:
778                 wlook(w);
779                 break;
780
781         case Send:
782                 wsend(w);
783                 break;
784
785         case Scroll:
786                 if(w->scrolling ^= 1)
787                         wshow(w, w->nr);
788                 break;
789         }
790         flushimage(display, 1);
791         wsendctlmesg(w, Wakeup, ZR, nil);
792 }
793
794 Point
795 onscreen(Point p)
796 {
797         p.x = max(screen->clipr.min.x, p.x);
798         p.x = min(screen->clipr.max.x-1, p.x);
799         p.y = max(screen->clipr.min.y, p.y);
800         p.y = min(screen->clipr.max.y-1, p.y);
801         return p;
802 }
803
804 Image*
805 sweep(void)
806 {
807         Image *i, *oi;
808         Rectangle r;
809         Point p0, p;
810
811         i = nil;
812         menuing = TRUE;
813         riosetcursor(&crosscursor);
814         while(mouse->buttons == 0)
815                 readmouse(mousectl);
816         p0 = onscreen(mouse->xy);
817         p = p0;
818         r.min = p;
819         r.max = p;
820         oi = nil;
821         while(mouse->buttons == 4){
822                 if(!eqpt(mouse->xy, p)){
823                         p = onscreen(mouse->xy);
824                         r = canonrect(Rpt(p0, p));
825                         r = whichrect(r, p, whichcorner(r, p));
826                         if(Dx(r)>5 && Dy(r)>5){
827                                 i = allocwindow(wscreen, r, Refnone, DNofill);
828                                 freeimage(oi);
829                                 if(i == nil)
830                                         goto Rescue;
831                                 oi = i;
832                                 border(i, r, Selborder, sizecol, ZP);
833                                 draw(i, insetrect(r, Selborder), cols[BACK], nil, ZP);
834                         }
835                 }
836                 readmouse(mousectl);
837         }
838         if(mouse->buttons != 0)
839                 goto Rescue;
840         if(i==nil || !goodrect(r))
841                 goto Rescue;
842         oi = i;
843         i = allocwindow(wscreen, oi->r, Refbackup, DNofill);
844         freeimage(oi);
845         if(i == nil)
846                 goto Rescue;
847         riosetcursor(corners[whichcorner(i->r, mouse->xy)]);
848         goto Return;
849
850  Rescue:
851         riosetcursor(nil);
852         freeimage(i);
853         i = nil;
854         flushimage(display, 1);
855         while(mouse->buttons)
856                 readmouse(mousectl);
857
858  Return:
859         menuing = FALSE;
860         return i;
861 }
862
863 void
864 drawedge(Image **bp, Image *col, Rectangle r)
865 {
866         Image *b = *bp;
867         if(b != nil && Dx(b->r) == Dx(r) && Dy(b->r) == Dy(r))
868                 originwindow(b, r.min, r.min);
869         else{
870                 freeimage(b);
871                 b = allocwindow(wscreen, r, Refbackup, DNofill);
872                 if(b != nil) draw(b, r, col, nil, ZP);
873                 *bp = b;
874         }
875 }
876
877 void
878 drawborder(Rectangle r, Image *col)
879 {
880         static Image *b[4], *lastcol;
881
882         if(col != lastcol){
883                 freeimage(b[0]), b[0] = nil;
884                 freeimage(b[1]), b[1] = nil;
885                 freeimage(b[2]), b[2] = nil;
886                 freeimage(b[3]), b[3] = nil;
887         }
888         if(col != nil){
889                 r = canonrect(r);
890                 drawedge(&b[0], col, Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y));
891                 drawedge(&b[1], col, Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth));
892                 drawedge(&b[2], col, Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y));
893                 drawedge(&b[3], col, Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y));
894         }
895         lastcol = col;
896 }
897
898 Image*
899 drag(Window *w)
900 {
901         Point p, op, d, dm, om;
902         Rectangle r;
903
904         menuing = TRUE;
905         riosetcursor(&boxcursor);
906         om = mouse->xy;
907         dm = subpt(om, w->screenr.min);
908         d = subpt(w->screenr.max, w->screenr.min);
909         op = subpt(om, dm);
910         drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), sizecol);
911         while(mouse->buttons==4){
912                 p = subpt(mouse->xy, dm);
913                 if(!eqpt(p, op)){
914                         drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), sizecol);
915                         op = p;
916                 }
917                 readmouse(mousectl);
918         }
919         r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
920         drawborder(r, nil);
921         p = mouse->xy;
922         riosetcursor(inborder(r, p) ? corners[whichcorner(r, p)] : nil);
923         menuing = FALSE;
924         if(mouse->buttons!=0 || !goodrect(r) || eqrect(r, w->screenr)){
925                 flushimage(display, 1);
926                 while(mouse->buttons)
927                         readmouse(mousectl);
928                 return nil;
929         }
930         return allocwindow(wscreen, r, Refbackup, DNofill);
931 }
932
933 Image*
934 bandsize(Window *w)
935 {
936         Rectangle r, or;
937         Point p, startp;
938         int which, owhich, but;
939
940         owhich = -1;
941         or = w->screenr;
942         but = mouse->buttons;
943         startp = onscreen(mouse->xy);
944         drawborder(or, sizecol);
945         while(mouse->buttons == but) {
946                 p = onscreen(mouse->xy);
947                 which = whichcorner(or, p);
948                 if(which != owhich && which != 4 && (owhich|~which) & 1){
949                         owhich = which;
950                         riosetcursor(corners[which]);
951                 }
952                 r = whichrect(or, p, owhich);
953                 if(!eqrect(r, or) && goodrect(r)){
954                         drawborder(r, sizecol);
955                         or = r;
956                 }
957                 readmouse(mousectl);
958         }
959         drawborder(or, nil);
960         if(!goodrect(or))
961                 riosetcursor(nil);
962         if(mouse->buttons!=0 || !goodrect(or) || eqrect(or, w->screenr)
963         || abs(p.x-startp.x)+abs(p.y-startp.y) <= 1){
964                 flushimage(display, 1);
965                 while(mouse->buttons)
966                         readmouse(mousectl);
967                 return nil;
968         }
969         return allocwindow(wscreen, or, Refbackup, DNofill);
970 }
971
972 Window*
973 pointto(int wait)
974 {
975         Window *w;
976
977         menuing = TRUE;
978         riosetcursor(&sightcursor);
979         while(mouse->buttons == 0)
980                 readmouse(mousectl);
981         if(mouse->buttons == 4)
982                 w = wpointto(mouse->xy);
983         else
984                 w = nil;
985         if(wait){
986                 while(mouse->buttons){
987                         if(mouse->buttons!=4 && w !=nil){       /* cancel */
988                                 riosetcursor(nil);
989                                 w = nil;
990                         }
991                         readmouse(mousectl);
992                 }
993                 if(w != nil && wpointto(mouse->xy) != w)
994                         w = nil;
995         }
996         riosetcursor(nil);
997         menuing = FALSE;
998         return w;
999 }
1000
1001 void
1002 delete(void)
1003 {
1004         Window *w;
1005
1006         w = pointto(TRUE);
1007         if(w!=nil)
1008                 wsendctlmesg(w, Deleted, ZR, nil);
1009 }
1010
1011 void
1012 resize(void)
1013 {
1014         Window *w;
1015         Image *i;
1016
1017         w = pointto(TRUE);
1018         if(w == nil)
1019                 return;
1020         incref(w);
1021         i = sweep();
1022         if(i!=nil){
1023                 wcurrent(w);
1024                 wsendctlmesg(w, Reshaped, i->r, i);
1025         }
1026         wclose(w);
1027 }
1028
1029 void
1030 move(void)
1031 {
1032         Window *w;
1033         Image *i;
1034
1035         w = pointto(FALSE);
1036         if(w == nil)
1037                 return;
1038         incref(w);
1039         i = drag(w);
1040         if(i!=nil){
1041                 wcurrent(w);
1042                 wsendctlmesg(w, Reshaped, i->r, i);
1043         }
1044         wclose(w);
1045 }
1046
1047 int
1048 whide(Window *w)
1049 {
1050         Image *i;
1051         int j;
1052
1053         for(j=0; j<nhidden; j++)
1054                 if(hidden[j] == w)      /* already hidden */
1055                         return -1;
1056         if(nhidden >= nelem(hidden))
1057                 return 0;
1058         incref(w);
1059         wuncurrent(w);
1060         i = allocimage(display, w->screenr, w->i->chan, 0, DNofill);
1061         if(i!=nil){
1062                 hidden[nhidden++] = w;
1063                 wsendctlmesg(w, Reshaped, ZR, i);
1064         }
1065         wclose(w);
1066         return i!=0;
1067 }
1068
1069 int
1070 wunhide(Window *w)
1071 {
1072         int j;
1073         Image *i;
1074
1075         for(j=0; j<nhidden; j++)
1076                 if(hidden[j] == w)
1077                         break;
1078         if(j == nhidden)
1079                 return -1;      /* not hidden */
1080         incref(w);
1081         wcurrent(w);
1082         i = allocwindow(wscreen, w->i->r, Refbackup, DNofill);
1083         if(i!=nil){
1084                 --nhidden;
1085                 memmove(hidden+j, hidden+j+1, (nhidden-j)*sizeof(Window*));
1086                 wsendctlmesg(w, Reshaped, w->i->r, i);
1087         }
1088         wclose(w);
1089         return i!=0;
1090 }
1091
1092 void
1093 hide(void)
1094 {
1095         Window *w;
1096
1097         w = pointto(TRUE);
1098         if(w)
1099                 whide(w);
1100 }
1101
1102 void
1103 unhide(int j)
1104 {
1105         Window *w;
1106
1107         if(j < Hidden)
1108                 return;
1109         j -= Hidden;
1110         w = hidden[j];
1111         if(w == nil)
1112                 return;
1113         if(j < nhidden){
1114                 wunhide(w);
1115                 return;
1116         }
1117         /* uncover obscured window */
1118         for(j=0; j<nwindow; j++)
1119                 if(window[j] == w){
1120                         incref(w);
1121                         wcurrent(w);
1122                         wtopme(w);
1123                         wsendctlmesg(w, Topped, ZR, nil);
1124                         wclose(w);
1125                         return;
1126                 }
1127 }
1128
1129 Window*
1130 new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
1131 {
1132         Window *w;
1133         Mousectl *mc;
1134         Channel *cm, *ck, *cctl, *cpid;
1135         void **arg;
1136
1137         if(i == nil)
1138                 return nil;
1139         if(hideit && nhidden >= nelem(hidden)){
1140                 freeimage(i);
1141                 return nil;
1142         }
1143         cm = chancreate(sizeof(Mouse), 0);
1144         ck = chancreate(sizeof(char*), 0);
1145         cctl = chancreate(sizeof(Wctlmesg), 4);
1146         cpid = chancreate(sizeof(int), 0);
1147         if(cm==nil || ck==nil || cctl==nil)
1148                 error("new: channel alloc failed");
1149         mc = emalloc(sizeof(Mousectl));
1150         *mc = *mousectl;
1151         mc->image = i;
1152         mc->c = cm;
1153         w = wmk(i, mc, ck, cctl, scrollit);
1154         free(mc);       /* wmk copies *mc */
1155         window = erealloc(window, ++nwindow*sizeof(Window*));
1156         window[nwindow-1] = w;
1157         if(hideit){
1158                 hidden[nhidden++] = w;
1159                 w->screenr = ZR;
1160         }
1161         threadcreate(winctl, w, STACK);
1162         if(!hideit)
1163                 wcurrent(w);
1164         if(pid == 0){
1165                 arg = emalloc(5*sizeof(void*));
1166                 arg[0] = w;
1167                 arg[1] = cpid;
1168                 arg[2] = cmd;
1169                 if(argv == nil)
1170                         arg[3] = rcargv;
1171                 else
1172                         arg[3] = argv;
1173                 arg[4] = dir;
1174                 proccreate(winshell, arg, STACK);
1175                 pid = recvul(cpid);
1176                 free(arg);
1177         }
1178         if(pid == 0){
1179                 /* window creation failed */
1180                 wsendctlmesg(w, Deleted, ZR, nil);
1181                 chanfree(cpid);
1182                 return nil;
1183         }
1184         wsetpid(w, pid, 1);
1185         wsetname(w);
1186         if(dir){
1187                 free(w->dir);
1188                 w->dir = estrdup(dir);
1189         }
1190         chanfree(cpid);
1191         return w;
1192 }
1193
1194 static void
1195 kbdproc(void *arg)
1196 {
1197         Channel *c = arg;
1198         char buf[1024], *p, *e;
1199         int fd, cfd, kfd, n;
1200
1201         threadsetname("kbdproc");
1202
1203         if((fd = open("/dev/cons", OREAD)) < 0){
1204                 chanprint(c, "%r");
1205                 return;
1206         }
1207         if((cfd = open("/dev/consctl", OWRITE)) < 0){
1208                 chanprint(c, "%r");
1209                 return;
1210         }
1211         fprint(cfd, "rawon");
1212
1213         if(sendp(c, nil) <= 0)
1214                 return;
1215
1216         if((kfd = open("/dev/kbd", OREAD)) >= 0){
1217                 close(fd);
1218
1219                 /* only serve a kbd file per window when we got one */
1220                 servekbd = 1;
1221
1222                 /* read kbd state */
1223                 while((n = read(kfd, buf, sizeof(buf)-1)) > 0){
1224                         e = buf+n;
1225                         e[-1] = 0;
1226                         e[0] = 0;
1227                         for(p = buf; p < e; p += strlen(p)+1)
1228                                 chanprint(c, "%s", p);
1229                 }
1230         } else {
1231                 /* read single characters */
1232                 p = buf;
1233                 for(;;){
1234                         Rune r;
1235
1236                         e = buf + sizeof(buf);
1237                         if((n = read(fd, p, e-p)) <= 0)
1238                                 break;
1239                         e = p + n;
1240                         while(p < e && fullrune(p, e - p)){
1241                                 p += chartorune(&r, p);
1242                                 if(r)
1243                                         chanprint(c, "c%C", r);
1244                         }
1245                         n = e - p;
1246                         memmove(buf, p, n);
1247                         p = buf + n;
1248                 }
1249         }
1250         send(exitchan, nil);
1251 }
1252
1253 Channel*
1254 initkbd(void)
1255 {
1256         Channel *c;
1257         char *e;
1258
1259         c = chancreate(sizeof(char*), 16);
1260         procrfork(kbdproc, c, STACK, RFCFDG);
1261         if(e = recvp(c)){
1262                 chanfree(c);
1263                 c = nil;
1264                 werrstr("%s", e);
1265                 free(e);
1266         }
1267         return c;
1268 }