Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
106
derror(Display*, char *errorstr)
107
{
108
	error(errorstr);
109
}
110
 
111
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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*
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
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
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*
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
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
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*
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*
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
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
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
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
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
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
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
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*
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
}