xref: /plan9/sys/src/cmd/rio/rio.c (revision 666b04c8ce76f8147f9ff295e0d8b14ed1d420f1)
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