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