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 <complete.h>
12
#include "dat.h"
13
#include "fns.h"
14
 
15
#define MOVEIT if(0)
16
 
17
enum
18
{
19
	HiWater	= 640000,	/* max size of history */
20
	LoWater	= 400000,	/* min size of history after max'ed */
21
	MinWater	= 20000,	/* room to leave available when reallocating */
22
};
23
 
24
static	int		topped;
25
static	int		id;
26
 
27
static	Image	*cols[NCOL];
28
static	Image	*grey;
29
static	Image	*darkgrey;
30
static	Cursor	*lastcursor;
31
static	Image	*titlecol;
32
static	Image	*lighttitlecol;
33
static	Image	*holdcol;
34
static	Image	*lightholdcol;
35
static	Image	*paleholdcol;
36
 
37
Window*
38
wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
39
{
40
	Window *w;
41
	Rectangle r;
42
 
43
	if(cols[0] == nil){
44
		/* greys are multiples of 0x11111100+0xFF, 14* being palest */
45
		grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
46
		darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF);
47
		cols[BACK] = display->white;
48
		cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
49
		cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF);
50
		cols[TEXT] = display->black;
51
		cols[HTEXT] = display->black;
52
		titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen);
53
		lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen);
54
		holdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue);
55
		lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
56
		paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
57
	}
58
	w = emalloc(sizeof(Window));
59
	w->screenr = i->r;
60
	r = insetrect(i->r, Selborder+1);
61
	w->i = i;
62
	w->mc = *mc;
63
	w->ck = ck;
64
	w->cctl = cctl;
65
	w->cursorp = nil;
66
	w->conswrite = chancreate(sizeof(Conswritemesg), 0);
67
	w->consread =  chancreate(sizeof(Consreadmesg), 0);
68
	w->mouseread =  chancreate(sizeof(Mousereadmesg), 0);
69
	w->wctlread =  chancreate(sizeof(Consreadmesg), 0);
70
	w->scrollr = r;
71
	w->scrollr.max.x = r.min.x+Scrollwid;
72
	w->lastsr = ZR;
73
	r.min.x += Scrollwid+Scrollgap;
74
	frinit(w, r, font, i, cols);
75
	w->maxtab = maxtab*stringwidth(font, "0");
76
	w->topped = ++topped;
77
	w->id = ++id;
78
	w->notefd = -1;
79
	w->scrolling = scrolling;
80
	w->dir = estrdup(startdir);
81
	w->label = estrdup("<unnamed>");
82
	r = insetrect(w->i->r, Selborder);
83
	draw(w->i, r, cols[BACK], nil, w->entire.min);
84
	wborder(w, Selborder);
85
	wscrdraw(w);
86
	incref(w);	/* ref will be removed after mounting; avoids delete before ready to be deleted */
87
	return w;
88
}
89
 
90
void
91
wsetname(Window *w)
92
{
93
	int i, n;
94
	char err[ERRMAX];
95
 
96
	n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
97
	for(i='A'; i<='Z'; i++){
98
		if(nameimage(w->i, w->name, 1) > 0)
99
			return;
100
		errstr(err, sizeof err);
101
		if(strcmp(err, "image name in use") != 0)
102
			break;
103
		w->name[n] = i;
104
		w->name[n+1] = 0;
105
	}
106
	w->name[0] = 0;
107
	fprint(2, "rio: setname failed: %s\n", err);
108
}
109
 
110
void
111
wresize(Window *w, Image *i, int move)
112
{
113
	Rectangle r, or;
114
 
115
	or = w->i->r;
116
	if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
117
		draw(i, i->r, w->i, nil, w->i->r.min);
118
	freeimage(w->i);
119
	w->i = i;
120
	wsetname(w);
121
	w->mc.image = i;
122
	r = insetrect(i->r, Selborder+1);
123
	w->scrollr = r;
124
	w->scrollr.max.x = r.min.x+Scrollwid;
125
	w->lastsr = ZR;
126
	r.min.x += Scrollwid+Scrollgap;
127
	if(move)
128
		frsetrects(w, r, w->i);
129
	else{
130
		frclear(w, FALSE);
131
		frinit(w, r, w->font, w->i, cols);
132
		wsetcols(w);
133
		w->maxtab = maxtab*stringwidth(w->font, "0");
134
		r = insetrect(w->i->r, Selborder);
135
		draw(w->i, r, cols[BACK], nil, w->entire.min);
136
		wfill(w);
137
		wsetselect(w, w->q0, w->q1);
138
		wscrdraw(w);
139
	}
140
	wborder(w, Selborder);
141
	w->topped = ++topped;
142
	w->resized = TRUE;
143
	w->mouse.counter++;
144
}
145
 
146
void
147
wrefresh(Window *w, Rectangle)
148
{
149
	/* BUG: rectangle is ignored */
150
	if(w == input)
151
		wborder(w, Selborder);
152
	else
153
		wborder(w, Unselborder);
154
	if(w->mouseopen)
155
		return;
156
	draw(w->i, insetrect(w->i->r, Borderwidth), w->cols[BACK], nil, w->i->r.min);
157
	w->ticked = 0;
158
	if(w->p0 > 0)
159
		frdrawsel(w, frptofchar(w, 0), 0, w->p0, 0);
160
	if(w->p1 < w->nchars)
161
		frdrawsel(w, frptofchar(w, w->p1), w->p1, w->nchars, 0);
162
	frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 1);
163
	w->lastsr = ZR;
164
	wscrdraw(w);
165
}
166
 
167
int
168
wclose(Window *w)
169
{
170
	int i;
171
 
172
	i = decref(w);
173
	if(i > 0)
174
		return 0;
175
	if(i < 0)
176
		error("negative ref count");
177
	if(!w->deleted)
178
		wclosewin(w);
179
	wsendctlmesg(w, Exited, ZR, nil);
180
	return 1;
181
}
182
 
183
 
184
void
185
winctl(void *arg)
186
{
187
	Rune *rp, *bp, *tp, *up, *kbdr;
188
	uint qh;
189
	int nr, nb, c, wid, i, npart, initial, lastb;
190
	char *s, *t, part[3];
191
	Window *w;
192
	Mousestate *mp, m;
193
	enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
194
	Alt alts[NWALT+1];
195
	Mousereadmesg mrm;
196
	Conswritemesg cwm;
197
	Consreadmesg crm;
198
	Consreadmesg cwrm;
199
	Stringpair pair;
200
	Wctlmesg wcm;
201
	char buf[4*12+1];
202
 
203
	w = arg;
204
	snprint(buf, sizeof buf, "winctl-id%d", w->id);
205
	threadsetname(buf);
206
 
207
	mrm.cm = chancreate(sizeof(Mouse), 0);
208
	cwm.cw = chancreate(sizeof(Stringpair), 0);
209
	crm.c1 = chancreate(sizeof(Stringpair), 0);
210
	crm.c2 = chancreate(sizeof(Stringpair), 0);
211
	cwrm.c1 = chancreate(sizeof(Stringpair), 0);
212
	cwrm.c2 = chancreate(sizeof(Stringpair), 0);
213
 
214
 
215
	alts[WKey].c = w->ck;
216
	alts[WKey].v = &kbdr;
217
	alts[WKey].op = CHANRCV;
218
	alts[WMouse].c = w->mc.c;
219
	alts[WMouse].v = &w->mc.Mouse;
220
	alts[WMouse].op = CHANRCV;
221
	alts[WMouseread].c = w->mouseread;
222
	alts[WMouseread].v = &mrm;
223
	alts[WMouseread].op = CHANSND;
224
	alts[WCtl].c = w->cctl;
225
	alts[WCtl].v = &wcm;
226
	alts[WCtl].op = CHANRCV;
227
	alts[WCwrite].c = w->conswrite;
228
	alts[WCwrite].v = &cwm;
229
	alts[WCwrite].op = CHANSND;
230
	alts[WCread].c = w->consread;
231
	alts[WCread].v = &crm;
232
	alts[WCread].op = CHANSND;
233
	alts[WWread].c = w->wctlread;
234
	alts[WWread].v = &cwrm;
235
	alts[WWread].op = CHANSND;
236
	alts[NWALT].op = CHANEND;
237
 
238
	npart = 0;
239
	lastb = -1;
240
	for(;;){
241
		if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter)
242
			alts[WMouseread].op = CHANSND;
243
		else
244
			alts[WMouseread].op = CHANNOP;
245
		if(!w->scrolling && !w->mouseopen && w->qh>w->org+w->nchars)
246
			alts[WCwrite].op = CHANNOP;
247
		else
248
			alts[WCwrite].op = CHANSND;
249
		if(w->deleted || !w->wctlready)
250
			alts[WWread].op = CHANNOP;
251
		else
252
			alts[WWread].op = CHANSND;
253
		/* this code depends on NL and EOT fitting in a single byte */
254
		/* kind of expensive for each loop; worth precomputing? */
255
		if(w->holding)
256
			alts[WCread].op = CHANNOP;
257
		else if(npart || (w->rawing && w->nraw>0))
258
			alts[WCread].op = CHANSND;
259
		else{
260
			alts[WCread].op = CHANNOP;
261
			for(i=w->qh; i<w->nr; i++){
262
				c = w->r[i];
263
				if(c=='\n' || c=='\004'){
264
					alts[WCread].op = CHANSND;
265
					break;
266
				}
267
			}
268
		}
269
		switch(alt(alts)){
270
		case WKey:
271
			for(i=0; kbdr[i]!=L'\0'; i++)
272
				wkeyctl(w, kbdr[i]);
273
//			wkeyctl(w, r);
274
///			while(nbrecv(w->ck, &r))
275
//				wkeyctl(w, r);
276
			break;
277
		case WMouse:
278
			if(w->mouseopen) {
279
				w->mouse.counter++;
280
 
281
				/* queue click events */
282
				if(!w->mouse.qfull && lastb != w->mc.buttons) {	/* add to ring */
283
					mp = &w->mouse.queue[w->mouse.wi];
284
					if(++w->mouse.wi == nelem(w->mouse.queue))
285
						w->mouse.wi = 0;
286
					if(w->mouse.wi == w->mouse.ri)
287
						w->mouse.qfull = TRUE;
288
					mp->Mouse = w->mc;
289
					mp->counter = w->mouse.counter;
290
					lastb = w->mc.buttons;
291
				}
292
			} else
293
				wmousectl(w);
294
			break;
295
		case WMouseread:
296
			/* send a queued event or, if the queue is empty, the current state */
297
			/* if the queue has filled, we discard all the events it contained. */
298
			/* the intent is to discard frantic clicking by the user during long latencies. */
299
			w->mouse.qfull = FALSE;
300
			if(w->mouse.wi != w->mouse.ri) {
301
				m = w->mouse.queue[w->mouse.ri];
302
				if(++w->mouse.ri == nelem(w->mouse.queue))
303
					w->mouse.ri = 0;
304
			} else
305
				m = (Mousestate){w->mc.Mouse, w->mouse.counter};
306
 
307
			w->mouse.lastcounter = m.counter;
308
			send(mrm.cm, &m.Mouse);
309
			continue;
310
		case WCtl:
311
			if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
312
				chanfree(crm.c1);
313
				chanfree(crm.c2);
314
				chanfree(mrm.cm);
315
				chanfree(cwm.cw);
316
				chanfree(cwrm.c1);
317
				chanfree(cwrm.c2);
318
				threadexits(nil);
319
			}
320
			continue;
321
		case WCwrite:
322
			recv(cwm.cw, &pair);
323
			rp = pair.s;
324
			nr = pair.ns;
325
			bp = rp;
326
			for(i=0; i<nr; i++)
327
				if(*bp++ == '\b'){
328
					--bp;
329
					initial = 0;
330
					tp = runemalloc(nr);
331
					runemove(tp, rp, i);
332
					up = tp+i;
333
					for(; i<nr; i++){
334
						*up = *bp++;
335
						if(*up == '\b')
336
							if(up == tp)
337
								initial++;
338
							else
339
								--up;
340
						else
341
							up++;
342
					}
343
					if(initial){
344
						if(initial > w->qh)
345
							initial = w->qh;
346
						qh = w->qh-initial;
347
						wdelete(w, qh, qh+initial);
348
						w->qh = qh;
349
					}
350
					free(rp);
351
					rp = tp;
352
					nr = up-tp;
353
					rp[nr] = 0;
354
					break;
355
				}
356
			w->qh = winsert(w, rp, nr, w->qh)+nr;
357
			if(w->scrolling || w->mouseopen)
358
				wshow(w, w->qh);
359
			wsetselect(w, w->q0, w->q1);
360
			wscrdraw(w);
361
			free(rp);
362
			break;
363
		case WCread:
364
			recv(crm.c1, &pair);
365
			t = pair.s;
366
			nb = pair.ns;
367
			i = npart;
368
			npart = 0;
369
			if(i)
370
				memmove(t, part, i);
371
			while(i<nb && (w->qh<w->nr || w->nraw>0)){
372
				if(w->qh == w->nr){
373
					wid = runetochar(t+i, &w->raw[0]);
374
					w->nraw--;
375
					runemove(w->raw, w->raw+1, w->nraw);
376
				}else
377
					wid = runetochar(t+i, &w->r[w->qh++]);
378
				c = t[i];	/* knows break characters fit in a byte */
379
				i += wid;
380
				if(!w->rawing && (c == '\n' || c=='\004')){
381
					if(c == '\004')
382
						i--;
383
					break;
384
				}
385
			}
386
			if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004')
387
				w->qh++;
388
			if(i > nb){
389
				npart = i-nb;
390
				memmove(part, t+nb, npart);
391
				i = nb;
392
			}
393
			pair.s = t;
394
			pair.ns = i;
395
			send(crm.c2, &pair);
396
			continue;
397
		case WWread:
398
			w->wctlready = 0;
399
			recv(cwrm.c1, &pair);
400
			if(w->deleted || w->i==nil)
401
				pair.ns = sprint(pair.s, "");
402
			else{
403
				s = "visible";
404
				for(i=0; i<nhidden; i++)
405
					if(hidden[i] == w){
406
						s = "hidden";
407
						break;
408
					}
409
				t = "notcurrent";
410
				if(w == input)
411
					t = "current";
412
				pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
413
					w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
414
			}
415
			send(cwrm.c2, &pair);
416
			continue;
417
		}
418
		if(!w->deleted)
419
			flushimage(display, 1);
420
	}
421
}
422
 
423
void
424
waddraw(Window *w, Rune *r, int nr)
425
{
426
	w->raw = runerealloc(w->raw, w->nraw+nr);
427
	runemove(w->raw+w->nraw, r, nr);
428
	w->nraw += nr;
429
}
430
 
431
/*
432
 * Need to do this in a separate proc because if process we're interrupting
433
 * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
434
 */
435
void
436
interruptproc(void *v)
437
{
438
	int *notefd;
439
 
440
	notefd = v;
441
	write(*notefd, "interrupt", 9);
442
	free(notefd);
443
}
444
 
445
int
446
windfilewidth(Window *w, uint q0, int oneelement)
447
{
448
	uint q;
449
	Rune r;
450
 
451
	q = q0;
452
	while(q > 0){
453
		r = w->r[q-1];
454
		if(r<=' ')
455
			break;
456
		if(oneelement && r=='/')
457
			break;
458
		--q;
459
	}
460
	return q0-q;
461
}
462
 
463
void
464
showcandidates(Window *w, Completion *c)
465
{
466
	int i;
467
	Fmt f;
468
	Rune *rp;
469
	uint nr, qline, q0;
470
	char *s;
471
 
472
	runefmtstrinit(&f);
473
	if (c->nmatch == 0)
474
		s = "[no matches in ";
475
	else
476
		s = "[";
477
	if(c->nfile > 32)
478
		fmtprint(&f, "%s%d files]\n", s, c->nfile);
479
	else{
480
		fmtprint(&f, "%s", s);
481
		for(i=0; i<c->nfile; i++){
482
			if(i > 0)
483
				fmtprint(&f, " ");
484
			fmtprint(&f, "%s", c->filename[i]);
485
		}
486
		fmtprint(&f, "]\n");
487
	}
488
	/* place text at beginning of line before host point */
489
	qline = w->qh;
490
	while(qline>0 && w->r[qline-1] != '\n')
491
		qline--;
492
 
493
	rp = runefmtstrflush(&f);
494
	nr = runestrlen(rp);
495
 
496
	q0 = w->q0;
497
	q0 += winsert(w, rp, runestrlen(rp), qline) - qline;
498
	free(rp);
499
	wsetselect(w, q0+nr, q0+nr);
500
}
501
 
502
Rune*
503
namecomplete(Window *w)
504
{
505
	int nstr, npath;
506
	Rune *rp, *path, *str;
507
	Completion *c;
508
	char *s, *dir, *root;
509
 
510
	/* control-f: filename completion; works back to white space or / */
511
	if(w->q0<w->nr && w->r[w->q0]>' ')	/* must be at end of word */
512
		return nil;
513
	nstr = windfilewidth(w, w->q0, TRUE);
514
	str = runemalloc(nstr);
515
	runemove(str, w->r+(w->q0-nstr), nstr);
516
	npath = windfilewidth(w, w->q0-nstr, FALSE);
517
	path = runemalloc(npath);
518
	runemove(path, w->r+(w->q0-nstr-npath), npath);
519
	rp = nil;
520
 
521
	/* is path rooted? if not, we need to make it relative to window path */
522
	if(npath>0 && path[0]=='/'){
523
		dir = malloc(UTFmax*npath+1);
524
		sprint(dir, "%.*S", npath, path);
525
	}else{
526
		if(strcmp(w->dir, "") == 0)
527
			root = ".";
528
		else
529
			root = w->dir;
530
		dir = malloc(strlen(root)+1+UTFmax*npath+1);
531
		sprint(dir, "%s/%.*S", root, npath, path);
532
	}
533
	dir = cleanname(dir);
534
 
535
	s = smprint("%.*S", nstr, str);
536
	c = complete(dir, s);
537
	free(s);
538
	if(c == nil)
539
		goto Return;
540
 
541
	if(!c->advance)
542
		showcandidates(w, c);
543
 
544
	if(c->advance)
545
		rp = runesmprint("%s", c->string);
546
 
547
  Return:
548
	freecompletion(c);
549
	free(dir);
550
	free(path);
551
	free(str);
552
	return rp;
553
}
554
 
555
void
556
wkeyctl(Window *w, Rune r)
557
{
558
	uint q0 ,q1;
559
	int n, nb, nr;
560
	Rune *rp;
561
	int *notefd;
562
 
563
	if(r == 0)
564
		return;
565
	if(w->deleted)
566
		return;
567
	/* navigation keys work only when mouse is not open */
568
	if(!w->mouseopen)
569
		switch(r){
570
		case Kdown:
571
			n = w->maxlines/3;
572
			goto case_Down;
573
		case Kscrollonedown:
574
			n = mousescrollsize(w->maxlines);
575
			if(n <= 0)
576
				n = 1;
577
			goto case_Down;
578
		case Kpgdown:
579
			n = 2*w->maxlines/3;
580
		case_Down:
581
			q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+n*w->font->height));
582
			wsetorigin(w, q0, TRUE);
583
			return;
584
		case Kup:
585
			n = w->maxlines/3;
586
			goto case_Up;
587
		case Kscrolloneup:
588
			n = mousescrollsize(w->maxlines);
589
			if(n <= 0)
590
				n = 1;
591
			goto case_Up;
592
		case Kpgup:
593
			n = 2*w->maxlines/3;
594
		case_Up:
595
			q0 = wbacknl(w, w->org, n);
596
			wsetorigin(w, q0, TRUE);
597
			return;
598
		case Kleft:
599
			if(w->q0 > 0){
600
				q0 = w->q0-1;
601
				wsetselect(w, q0, q0);
602
				wshow(w, q0);
603
			}
604
			return;
605
		case Kright:
606
			if(w->q1 < w->nr){
607
				q1 = w->q1+1;
608
				wsetselect(w, q1, q1);
609
				wshow(w, q1);
610
			}
611
			return;
612
		case Khome:
613
			wshow(w, 0);
614
			return;
615
		case Kend:
616
			wshow(w, w->nr);
617
			return;
618
		case 0x01:	/* ^A: beginning of line */
619
			if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
620
				return;
621
			nb = wbswidth(w, 0x15 /* ^U */);
622
			wsetselect(w, w->q0-nb, w->q0-nb);
623
			wshow(w, w->q0);
624
			return;
625
		case 0x05:	/* ^E: end of line */
626
			q0 = w->q0;
627
			while(q0 < w->nr && w->r[q0]!='\n')
628
				q0++;
629
			wsetselect(w, q0, q0);
630
			wshow(w, w->q0);
631
			return;
632
		}
633
	if(w->rawing && (w->q0==w->nr || w->mouseopen)){
634
		waddraw(w, &r, 1);
635
		return;
636
	}
637
	if(r==0x1B || (w->holding && r==0x7F)){	/* toggle hold */
638
		if(w->holding)
639
			--w->holding;
640
		else
641
			w->holding++;
642
		wrepaint(w);
643
		if(r == 0x1B)
644
			return;
645
	}
646
	if(r != 0x7F){
647
		wsnarf(w);
648
		wcut(w);
649
	}
650
	switch(r){
651
	case 0x7F:		/* send interrupt */
652
		w->qh = w->nr;
653
		wshow(w, w->qh);
654
		notefd = emalloc(sizeof(int));
655
		*notefd = w->notefd;
656
		proccreate(interruptproc, notefd, 4096);
657
		return;
658
	case 0x06:	/* ^F: file name completion */
659
	case Kins:		/* Insert: file name completion */
660
		rp = namecomplete(w);
661
		if(rp == nil)
662
			return;
663
		nr = runestrlen(rp);
664
		q0 = w->q0;
665
		q0 = winsert(w, rp, nr, q0);
666
		wshow(w, q0+nr);
667
		free(rp);
668
		return;
669
	case 0x08:	/* ^H: erase character */
670
	case 0x15:	/* ^U: erase line */
671
	case 0x17:	/* ^W: erase word */
672
		if(w->q0==0 || w->q0==w->qh)
673
			return;
674
		nb = wbswidth(w, r);
675
		q1 = w->q0;
676
		q0 = q1-nb;
677
		if(q0 < w->org){
678
			q0 = w->org;
679
			nb = q1-q0;
680
		}
681
		if(nb > 0){
682
			wdelete(w, q0, q0+nb);
683
			wsetselect(w, q0, q0);
684
		}
685
		return;
686
	}
687
	/* otherwise ordinary character; just insert */
688
	q0 = w->q0;
689
	q0 = winsert(w, &r, 1, q0);
690
	wshow(w, q0+1);
691
}
692
 
693
void
694
wsetcols(Window *w)
695
{
696
	if(w->holding)
697
		if(w == input)
698
			w->cols[TEXT] = w->cols[HTEXT] = holdcol;
699
		else
700
			w->cols[TEXT] = w->cols[HTEXT] = lightholdcol;
701
	else
702
		if(w == input)
703
			w->cols[TEXT] = w->cols[HTEXT] = display->black;
704
		else
705
			w->cols[TEXT] = w->cols[HTEXT] = darkgrey;
706
}
707
 
708
void
709
wrepaint(Window *w)
710
{
711
	wsetcols(w);
712
	if(!w->mouseopen)
713
		frredraw(w);
714
	if(w == input){
715
		wborder(w, Selborder);
716
		wsetcursor(w, 0);
717
	}else
718
		wborder(w, Unselborder);
719
}
720
 
721
int
722
wbswidth(Window *w, Rune c)
723
{
724
	uint q, eq, stop;
725
	Rune r;
726
	int skipping;
727
 
728
	/* there is known to be at least one character to erase */
729
	if(c == 0x08)	/* ^H: erase character */
730
		return 1;
731
	q = w->q0;
732
	stop = 0;
733
	if(q > w->qh)
734
		stop = w->qh;
735
	skipping = TRUE;
736
	while(q > stop){
737
		r = w->r[q-1];
738
		if(r == '\n'){		/* eat at most one more character */
739
			if(q == w->q0)	/* eat the newline */
740
				--q;
741
			break; 
742
		}
743
		if(c == 0x17){
744
			eq = isalnum(r);
745
			if(eq && skipping)	/* found one; stop skipping */
746
				skipping = FALSE;
747
			else if(!eq && !skipping)
748
				break;
749
		}
750
		--q;
751
	}
752
	return w->q0-q;
753
}
754
 
755
void
756
wsnarf(Window *w)
757
{
758
	if(w->q1 == w->q0)
759
		return;
760
	nsnarf = w->q1-w->q0;
761
	snarf = runerealloc(snarf, nsnarf);
762
	snarfversion++;	/* maybe modified by parent */
763
	runemove(snarf, w->r+w->q0, nsnarf);
764
	putsnarf();
765
}
766
 
767
void
768
wcut(Window *w)
769
{
770
	if(w->q1 == w->q0)
771
		return;
772
	wdelete(w, w->q0, w->q1);
773
	wsetselect(w, w->q0, w->q0);
774
}
775
 
776
void
777
wpaste(Window *w)
778
{
779
	uint q0;
780
 
781
	if(nsnarf == 0)
782
		return;
783
	wcut(w);
784
	q0 = w->q0;
785
	if(w->rawing && q0==w->nr){
786
		waddraw(w, snarf, nsnarf);
787
		wsetselect(w, q0, q0);
788
	}else{
789
		q0 = winsert(w, snarf, nsnarf, w->q0);
790
		wsetselect(w, q0, q0+nsnarf);
791
	}
792
}
793
 
794
void
795
wplumb(Window *w)
796
{
797
	Plumbmsg *m;
798
	static int fd = -2;
799
	char buf[32];
800
	uint p0, p1;
801
	Cursor *c;
802
 
803
	if(fd == -2)
804
		fd = plumbopen("send", OWRITE|OCEXEC);
805
	if(fd < 0)
806
		return;
807
	m = emalloc(sizeof(Plumbmsg));
808
	m->src = estrdup("rio");
809
	m->dst = nil;
810
	m->wdir = estrdup(w->dir);
811
	m->type = estrdup("text");
812
	p0 = w->q0;
813
	p1 = w->q1;
814
	if(w->q1 > w->q0)
815
		m->attr = nil;
816
	else{
817
		while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
818
			p0--;
819
		while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
820
			p1++;
821
		sprint(buf, "click=%d", w->q0-p0);
822
		m->attr = plumbunpackattr(buf);
823
	}
824
	if(p1-p0 > messagesize-1024){
825
		plumbfree(m);
826
		return;	/* too large for 9P */
827
	}
828
	m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
829
	if(plumbsend(fd, m) < 0){
830
		c = lastcursor;
831
		riosetcursor(&query, 1);
832
		sleep(300);
833
		riosetcursor(c, 1);
834
	}
835
	plumbfree(m);
836
}
837
 
838
int
839
winborder(Window *w, Point xy)
840
{
841
	return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder));
842
}
843
 
844
void
845
wmousectl(Window *w)
846
{
847
	int but;
848
 
849
	if(w->mc.buttons == 1)
850
		but = 1;
851
	else if(w->mc.buttons == 2)
852
		but = 2;
853
	else if(w->mc.buttons == 4)
854
		but = 3;
855
	else{
856
		if(w->mc.buttons == 8)
857
			wkeyctl(w, Kscrolloneup);
858
		if(w->mc.buttons == 16)
859
			wkeyctl(w, Kscrollonedown);
860
		return;
861
	}
862
 
863
	incref(w);		/* hold up window while we track */
864
	if(w->deleted)
865
		goto Return;
866
	if(ptinrect(w->mc.xy, w->scrollr)){
867
		if(but)
868
			wscroll(w, but);
869
		goto Return;
870
	}
871
	if(but == 1)
872
		wselect(w);
873
	/* else all is handled by main process */
874
   Return:
875
	wclose(w);
876
}
877
 
878
void
879
wdelete(Window *w, uint q0, uint q1)
880
{
881
	uint n, p0, p1;
882
 
883
	n = q1-q0;
884
	if(n == 0)
885
		return;
886
	runemove(w->r+q0, w->r+q1, w->nr-q1);
887
	w->nr -= n;
888
	if(q0 < w->q0)
889
		w->q0 -= min(n, w->q0-q0);
890
	if(q0 < w->q1)
891
		w->q1 -= min(n, w->q1-q0);
892
	if(q1 < w->qh)
893
		w->qh -= n;
894
	else if(q0 < w->qh)
895
		w->qh = q0;
896
	if(q1 <= w->org)
897
		w->org -= n;
898
	else if(q0 < w->org+w->nchars){
899
		p1 = q1 - w->org;
900
		if(p1 > w->nchars)
901
			p1 = w->nchars;
902
		if(q0 < w->org){
903
			w->org = q0;
904
			p0 = 0;
905
		}else
906
			p0 = q0 - w->org;
907
		frdelete(w, p0, p1);
908
		wfill(w);
909
	}
910
}
911
 
912
 
913
static Window	*clickwin;
914
static uint	clickmsec;
915
static Window	*selectwin;
916
static uint	selectq;
917
 
918
/*
919
 * called from frame library
920
 */
921
void
922
framescroll(Frame *f, int dl)
923
{
924
	if(f != &selectwin->Frame)
925
		error("frameselect not right frame");
926
	wframescroll(selectwin, dl);
927
}
928
 
929
void
930
wframescroll(Window *w, int dl)
931
{
932
	uint q0;
933
 
934
	if(dl == 0){
935
		wscrsleep(w, 100);
936
		return;
937
	}
938
	if(dl < 0){
939
		q0 = wbacknl(w, w->org, -dl);
940
		if(selectq > w->org+w->p0)
941
			wsetselect(w, w->org+w->p0, selectq);
942
		else
943
			wsetselect(w, selectq, w->org+w->p0);
944
	}else{
945
		if(w->org+w->nchars == w->nr)
946
			return;
947
		q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+dl*w->font->height));
948
		if(selectq >= w->org+w->p1)
949
			wsetselect(w, w->org+w->p1, selectq);
950
		else
951
			wsetselect(w, selectq, w->org+w->p1);
952
	}
953
	wsetorigin(w, q0, TRUE);
954
}
955
 
956
void
957
wselect(Window *w)
958
{
959
	uint q0, q1;
960
	int b, x, y, first;
961
 
962
	first = 1;
963
	selectwin = w;
964
	/*
965
	 * Double-click immediately if it might make sense.
966
	 */
967
	b = w->mc.buttons;
968
	q0 = w->q0;
969
	q1 = w->q1;
970
	selectq = w->org+frcharofpt(w, w->mc.xy);
971
	if(clickwin==w && w->mc.msec-clickmsec<500)
972
	if(q0==q1 && selectq==w->q0){
973
		wdoubleclick(w, &q0, &q1);
974
		wsetselect(w, q0, q1);
975
		flushimage(display, 1);
976
		x = w->mc.xy.x;
977
		y = w->mc.xy.y;
978
		/* stay here until something interesting happens */
979
		do
980
			readmouse(&w->mc);
981
		while(w->mc.buttons==b && abs(w->mc.xy.x-x)<3 && abs(w->mc.xy.y-y)<3);
982
		w->mc.xy.x = x;	/* in case we're calling frselect */
983
		w->mc.xy.y = y;
984
		q0 = w->q0;	/* may have changed */
985
		q1 = w->q1;
986
		selectq = q0;
987
	}
988
	if(w->mc.buttons == b){
989
		w->scroll = framescroll;
990
		frselect(w, &w->mc);
991
		/* horrible botch: while asleep, may have lost selection altogether */
992
		if(selectq > w->nr)
993
			selectq = w->org + w->p0;
994
		w->Frame.scroll = nil;
995
		if(selectq < w->org)
996
			q0 = selectq;
997
		else
998
			q0 = w->org + w->p0;
999
		if(selectq > w->org+w->nchars)
1000
			q1 = selectq;
1001
		else
1002
			q1 = w->org+w->p1;
1003
	}
1004
	if(q0 == q1){
1005
		if(q0==w->q0 && clickwin==w && w->mc.msec-clickmsec<500){
1006
			wdoubleclick(w, &q0, &q1);
1007
			clickwin = nil;
1008
		}else{
1009
			clickwin = w;
1010
			clickmsec = w->mc.msec;
1011
		}
1012
	}else
1013
		clickwin = nil;
1014
	wsetselect(w, q0, q1);
1015
	flushimage(display, 1);
1016
	while(w->mc.buttons){
1017
		w->mc.msec = 0;
1018
		b = w->mc.buttons;
1019
		if(b & 6){
1020
			if(b & 2){
1021
				wsnarf(w);
1022
				wcut(w);
1023
			}else{
1024
				if(first){
1025
					first = 0;
1026
					getsnarf();
1027
				}
1028
				wpaste(w);
1029
			}
1030
		}
1031
		wscrdraw(w);
1032
		flushimage(display, 1);
1033
		while(w->mc.buttons == b)
1034
			readmouse(&w->mc);
1035
		clickwin = nil;
1036
	}
1037
}
1038
 
1039
void
1040
wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
1041
{
1042
	Wctlmesg wcm;
1043
 
1044
	wcm.type = type;
1045
	wcm.r = r;
1046
	wcm.image = image;
1047
	send(w->cctl, &wcm);
1048
}
1049
 
1050
int
1051
wctlmesg(Window *w, int m, Rectangle r, Image *i)
1052
{
1053
	char buf[64];
1054
 
1055
	switch(m){
1056
	default:
1057
		error("unknown control message");
1058
		break;
1059
	case Wakeup:
1060
		break;
1061
	case Moved:
1062
	case Reshaped:
1063
		if(w->deleted){
1064
			freeimage(i);
1065
			break;
1066
		}
1067
		w->screenr = r;
1068
		strcpy(buf, w->name);
1069
		wresize(w, i, m==Moved);
1070
		w->wctlready = 1;
1071
		proccreate(deletetimeoutproc, estrdup(buf), 4096);
1072
		if(Dx(r) > 0){
1073
			if(w != input)
1074
				wcurrent(w);
1075
		}else if(w == input)
1076
			wcurrent(nil);
1077
		flushimage(display, 1);
1078
		break;
1079
	case Refresh:
1080
		if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
1081
			break;
1082
		if(!w->mouseopen)
1083
			wrefresh(w, r);
1084
		flushimage(display, 1);
1085
		break;
1086
	case Movemouse:
1087
		if(sweeping || !ptinrect(r.min, w->i->r))
1088
			break;
1089
		wmovemouse(w, r.min);
1090
	case Rawon:
1091
		break;
1092
	case Rawoff:
1093
		if(w->deleted)
1094
			break;
1095
		while(w->nraw > 0){
1096
			wkeyctl(w, w->raw[0]);
1097
			--w->nraw;
1098
			runemove(w->raw, w->raw+1, w->nraw);
1099
		}
1100
		break;
1101
	case Holdon:
1102
	case Holdoff:
1103
		if(w->deleted)
1104
			break;
1105
		wrepaint(w);
1106
		flushimage(display, 1);
1107
		break;
1108
	case Deleted:
1109
		if(w->deleted)
1110
			break;
1111
		write(w->notefd, "hangup", 6);
1112
		proccreate(deletetimeoutproc, estrdup(w->name), 4096);
1113
		wclosewin(w);
1114
		break;
1115
	case Exited:
1116
		frclear(w, TRUE);
1117
		close(w->notefd);
1118
		chanfree(w->mc.c);
1119
		chanfree(w->ck);
1120
		chanfree(w->cctl);
1121
		chanfree(w->conswrite);
1122
		chanfree(w->consread);
1123
		chanfree(w->mouseread);
1124
		chanfree(w->wctlread);
1125
		free(w->raw);
1126
		free(w->r);
1127
		free(w->dir);
1128
		free(w->label);
1129
		free(w);
1130
		break;
1131
	}
1132
	return m;
1133
}
1134
 
1135
/*
1136
 * Convert back to physical coordinates
1137
 */
1138
void
1139
wmovemouse(Window *w, Point p)
1140
{
1141
	p.x += w->screenr.min.x-w->i->r.min.x;
1142
	p.y += w->screenr.min.y-w->i->r.min.y;
1143
	moveto(mousectl, p);
1144
}
1145
 
1146
void
1147
wborder(Window *w, int type)
1148
{
1149
	Image *col;
1150
 
1151
	if(w->i == nil)
1152
		return;
1153
	if(w->holding){
1154
		if(type == Selborder)
1155
			col = holdcol;
1156
		else
1157
			col = paleholdcol;
1158
	}else{
1159
		if(type == Selborder)
1160
			col = titlecol;
1161
		else
1162
			col = lighttitlecol;
1163
	}
1164
 
1165
	border(w->i, w->i->r, Selborder, col, ZP);
1166
}
1167
 
1168
Window*
1169
wpointto(Point pt)
1170
{
1171
	int i;
1172
	Window *v, *w;
1173
 
1174
	w = nil;
1175
	for(i=0; i<nwindow; i++){
1176
		v = window[i];
1177
		if(ptinrect(pt, v->screenr))
1178
		if(!v->deleted)
1179
		if(w==nil || v->topped>w->topped)
1180
			w = v;
1181
	}
1182
	return w;
1183
}
1184
 
1185
void
1186
wcurrent(Window *w)
1187
{
1188
	Window *oi;
1189
 
1190
	if(wkeyboard!=nil && w==wkeyboard)
1191
		return;
1192
	oi = input;
1193
	input = w;
1194
	if(oi!=w && oi!=nil)
1195
		wrepaint(oi);
1196
	if(w !=nil){
1197
		wrepaint(w);
1198
		wsetcursor(w, 0);
1199
	}
1200
	if(w != oi){
1201
		if(oi){
1202
			oi->wctlready = 1;
1203
			wsendctlmesg(oi, Wakeup, ZR, nil);
1204
		}
1205
		if(w){
1206
			w->wctlready = 1;
1207
			wsendctlmesg(w, Wakeup, ZR, nil);
1208
		}
1209
	}
1210
}
1211
 
1212
void
1213
wsetcursor(Window *w, int force)
1214
{
1215
	Cursor *p;
1216
 
1217
	if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
1218
		p = nil;
1219
	else if(wpointto(mouse->xy) == w){
1220
		p = w->cursorp;
1221
		if(p==nil && w->holding)
1222
			p = &whitearrow;
1223
	}else
1224
		p = nil;
1225
	if(!menuing)
1226
		riosetcursor(p, force && !menuing);
1227
}
1228
 
1229
void
1230
riosetcursor(Cursor *p, int force)
1231
{
1232
	if(!force && p==lastcursor)
1233
		return;
1234
	setcursor(mousectl, p);
1235
	lastcursor = p;
1236
}
1237
 
1238
Window*
1239
wtop(Point pt)
1240
{
1241
	Window *w;
1242
 
1243
	w = wpointto(pt);
1244
	if(w){
1245
		if(w->topped == topped)
1246
			return nil;
1247
		topwindow(w->i);
1248
		wcurrent(w);
1249
		flushimage(display, 1);
1250
		w->topped = ++topped;
1251
	}
1252
	return w;
1253
}
1254
 
1255
void
1256
wtopme(Window *w)
1257
{
1258
	if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){
1259
		topwindow(w->i);
1260
		flushimage(display, 1);
1261
		w->topped = ++ topped;
1262
	}
1263
}
1264
 
1265
void
1266
wbottomme(Window *w)
1267
{
1268
	if(w!=nil && w->i!=nil && !w->deleted){
1269
		bottomwindow(w->i);
1270
		flushimage(display, 1);
1271
		w->topped = - ++topped;
1272
	}
1273
}
1274
 
1275
Window*
1276
wlookid(int id)
1277
{
1278
	int i;
1279
 
1280
	for(i=0; i<nwindow; i++)
1281
		if(window[i]->id == id)
1282
			return window[i];
1283
	return nil;
1284
}
1285
 
1286
void
1287
wclosewin(Window *w)
1288
{
1289
	Rectangle r;
1290
	int i;
1291
 
1292
	w->deleted = TRUE;
1293
	if(w == input){
1294
		input = nil;
1295
		wsetcursor(w, 0);
1296
	}
1297
	if(w == wkeyboard)
1298
		wkeyboard = nil;
1299
	for(i=0; i<nhidden; i++)
1300
		if(hidden[i] == w){
1301
			--nhidden;
1302
			memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
1303
			hidden[nhidden] = nil;
1304
			break;
1305
		}
1306
	for(i=0; i<nwindow; i++)
1307
		if(window[i] == w){
1308
			--nwindow;
1309
			memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*));
1310
			w->deleted = TRUE;
1311
			r = w->i->r;
1312
			/* move it off-screen to hide it, in case client is slow in letting it go */
1313
			MOVEIT originwindow(w->i, r.min, view->r.max);
1314
			freeimage(w->i);
1315
			w->i = nil;
1316
			return;
1317
		}
1318
	error("unknown window in closewin");
1319
}
1320
 
1321
void
1322
wsetpid(Window *w, int pid, int dolabel)
1323
{
1324
	char buf[128];
1325
	int fd;
1326
 
1327
	w->pid = pid;
1328
	if(dolabel){
1329
		sprint(buf, "rc %d", pid);
1330
		free(w->label);
1331
		w->label = estrdup(buf);
1332
	}
1333
	sprint(buf, "/proc/%d/notepg", pid);
1334
	fd = open(buf, OWRITE|OCEXEC);
1335
	if(w->notefd > 0)
1336
		close(w->notefd);
1337
	w->notefd = fd;
1338
}
1339
 
1340
void
1341
winshell(void *args)
1342
{
1343
	Window *w;
1344
	Channel *pidc;
1345
	void **arg;
1346
	char *cmd, *dir;
1347
	char **argv;
1348
 
1349
	arg = args;
1350
	w = arg[0];
1351
	pidc = arg[1];
1352
	cmd = arg[2];
1353
	argv = arg[3];
1354
	dir = arg[4];
1355
	rfork(RFNAMEG|RFFDG|RFENVG);
1356
	if(filsysmount(filsys, w->id) < 0){
1357
		fprint(2, "mount failed: %r\n");
1358
		sendul(pidc, 0);
1359
		threadexits("mount failed");
1360
	}
1361
	close(0);
1362
	if(open("/dev/cons", OREAD) < 0){
1363
		fprint(2, "can't open /dev/cons: %r\n");
1364
		sendul(pidc, 0);
1365
		threadexits("/dev/cons");
1366
	}
1367
	close(1);
1368
	if(open("/dev/cons", OWRITE) < 0){
1369
		fprint(2, "can't open /dev/cons: %r\n");
1370
		sendul(pidc, 0);
1371
		threadexits("open");	/* BUG? was terminate() */
1372
	}
1373
	if(wclose(w) == 0){	/* remove extra ref hanging from creation */
1374
		notify(nil);
1375
		dup(1, 2);
1376
		if(dir)
1377
			chdir(dir);
1378
		procexec(pidc, cmd, argv);
1379
		_exits("exec failed");
1380
	}
1381
}
1382
 
1383
static Rune left1[] =  { L'{', L'[', L'(', L'<', L'«', 0 };
1384
static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
1385
static Rune left2[] =  { L'\n', 0 };
1386
static Rune left3[] =  { L'\'', L'"', L'`', 0 };
1387
 
1388
Rune *left[] = {
1389
	left1,
1390
	left2,
1391
	left3,
1392
	nil
1393
};
1394
Rune *right[] = {
1395
	right1,
1396
	left2,
1397
	left3,
1398
	nil
1399
};
1400
 
1401
void
1402
wdoubleclick(Window *w, uint *q0, uint *q1)
1403
{
1404
	int c, i;
1405
	Rune *r, *l, *p;
1406
	uint q;
1407
 
1408
	for(i=0; left[i]!=nil; i++){
1409
		q = *q0;
1410
		l = left[i];
1411
		r = right[i];
1412
		/* try matching character to left, looking right */
1413
		if(q == 0)
1414
			c = '\n';
1415
		else
1416
			c = w->r[q-1];
1417
		p = strrune(l, c);
1418
		if(p != nil){
1419
			if(wclickmatch(w, c, r[p-l], 1, &q))
1420
				*q1 = q-(c!='\n');
1421
			return;
1422
		}
1423
		/* try matching character to right, looking left */
1424
		if(q == w->nr)
1425
			c = '\n';
1426
		else
1427
			c = w->r[q];
1428
		p = strrune(r, c);
1429
		if(p != nil){
1430
			if(wclickmatch(w, c, l[p-r], -1, &q)){
1431
				*q1 = *q0+(*q0<w->nr && c=='\n');
1432
				*q0 = q;
1433
				if(c!='\n' || q!=0 || w->r[0]=='\n')
1434
					(*q0)++;
1435
			}
1436
			return;
1437
		}
1438
	}
1439
	/* try filling out word to right */
1440
	while(*q1<w->nr && isalnum(w->r[*q1]))
1441
		(*q1)++;
1442
	/* try filling out word to left */
1443
	while(*q0>0 && isalnum(w->r[*q0-1]))
1444
		(*q0)--;
1445
}
1446
 
1447
int
1448
wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
1449
{
1450
	Rune c;
1451
	int nest;
1452
 
1453
	nest = 1;
1454
	for(;;){
1455
		if(dir > 0){
1456
			if(*q == w->nr)
1457
				break;
1458
			c = w->r[*q];
1459
			(*q)++;
1460
		}else{
1461
			if(*q == 0)
1462
				break;
1463
			(*q)--;
1464
			c = w->r[*q];
1465
		}
1466
		if(c == cr){
1467
			if(--nest==0)
1468
				return 1;
1469
		}else if(c == cl)
1470
			nest++;
1471
	}
1472
	return cl=='\n' && nest==1;
1473
}
1474
 
1475
 
1476
uint
1477
wbacknl(Window *w, uint p, uint n)
1478
{
1479
	int i, j;
1480
 
1481
	/* look for start of this line if n==0 */
1482
	if(n==0 && p>0 && w->r[p-1]!='\n')
1483
		n = 1;
1484
	i = n;
1485
	while(i-->0 && p>0){
1486
		--p;	/* it's at a newline now; back over it */
1487
		if(p == 0)
1488
			break;
1489
		/* at 128 chars, call it a line anyway */
1490
		for(j=128; --j>0 && p>0; p--)
1491
			if(w->r[p-1]=='\n')
1492
				break;
1493
	}
1494
	return p;
1495
}
1496
 
1497
void
1498
wshow(Window *w, uint q0)
1499
{
1500
	int qe;
1501
	int nl;
1502
	uint q;
1503
 
1504
	qe = w->org+w->nchars;
1505
	if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
1506
		wscrdraw(w);
1507
	else{
1508
		nl = 4*w->maxlines/5;
1509
		q = wbacknl(w, q0, nl);
1510
		/* avoid going backwards if trying to go forwards - long lines! */
1511
		if(!(q0>w->org && q<w->org))
1512
			wsetorigin(w, q, TRUE);
1513
		while(q0 > w->org+w->nchars)
1514
			wsetorigin(w, w->org+1, FALSE);
1515
	}
1516
}
1517
 
1518
void
1519
wsetorigin(Window *w, uint org, int exact)
1520
{
1521
	int i, a, fixup;
1522
	Rune *r;
1523
	uint n;
1524
 
1525
	if(org>0 && !exact){
1526
		/* org is an estimate of the char posn; find a newline */
1527
		/* don't try harder than 256 chars */
1528
		for(i=0; i<256 && org<w->nr; i++){
1529
			if(w->r[org] == '\n'){
1530
				org++;
1531
				break;
1532
			}
1533
			org++;
1534
		}
1535
	}
1536
	a = org-w->org;
1537
	fixup = 0;
1538
	if(a>=0 && a<w->nchars){
1539
		frdelete(w, 0, a);
1540
		fixup = 1;	/* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
1541
	}else if(a<0 && -a<w->nchars){
1542
		n = w->org - org;
1543
		r = runemalloc(n);
1544
		runemove(r, w->r+org, n);
1545
		frinsert(w, r, r+n, 0);
1546
		free(r);
1547
	}else
1548
		frdelete(w, 0, w->nchars);
1549
	w->org = org;
1550
	wfill(w);
1551
	wscrdraw(w);
1552
	wsetselect(w, w->q0, w->q1);
1553
	if(fixup && w->p1 > w->p0)
1554
		frdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1);
1555
}
1556
 
1557
void
1558
wsetselect(Window *w, uint q0, uint q1)
1559
{
1560
	int p0, p1;
1561
 
1562
	/* w->p0 and w->p1 are always right; w->q0 and w->q1 may be off */
1563
	w->q0 = q0;
1564
	w->q1 = q1;
1565
	/* compute desired p0,p1 from q0,q1 */
1566
	p0 = q0-w->org;
1567
	p1 = q1-w->org;
1568
	if(p0 < 0)
1569
		p0 = 0;
1570
	if(p1 < 0)
1571
		p1 = 0;
1572
	if(p0 > w->nchars)
1573
		p0 = w->nchars;
1574
	if(p1 > w->nchars)
1575
		p1 = w->nchars;
1576
	if(p0==w->p0 && p1==w->p1)
1577
		return;
1578
	/* screen disagrees with desired selection */
1579
	if(w->p1<=p0 || p1<=w->p0 || p0==p1 || w->p1==w->p0){
1580
		/* no overlap or too easy to bother trying */
1581
		frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 0);
1582
		frdrawsel(w, frptofchar(w, p0), p0, p1, 1);
1583
		goto Return;
1584
	}
1585
	/* overlap; avoid unnecessary painting */
1586
	if(p0 < w->p0){
1587
		/* extend selection backwards */
1588
		frdrawsel(w, frptofchar(w, p0), p0, w->p0, 1);
1589
	}else if(p0 > w->p0){
1590
		/* trim first part of selection */
1591
		frdrawsel(w, frptofchar(w, w->p0), w->p0, p0, 0);
1592
	}
1593
	if(p1 > w->p1){
1594
		/* extend selection forwards */
1595
		frdrawsel(w, frptofchar(w, w->p1), w->p1, p1, 1);
1596
	}else if(p1 < w->p1){
1597
		/* trim last part of selection */
1598
		frdrawsel(w, frptofchar(w, p1), p1, w->p1, 0);
1599
	}
1600
 
1601
    Return:
1602
	w->p0 = p0;
1603
	w->p1 = p1;
1604
}
1605
 
1606
uint
1607
winsert(Window *w, Rune *r, int n, uint q0)
1608
{
1609
	uint m;
1610
 
1611
	if(n == 0)
1612
		return q0;
1613
	if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
1614
		m = min(HiWater-LoWater, min(w->org, w->qh));
1615
		w->org -= m;
1616
		w->qh -= m;
1617
		if(w->q0 > m)
1618
			w->q0 -= m;
1619
		else
1620
			w->q0 = 0;
1621
		if(w->q1 > m)
1622
			w->q1 -= m;
1623
		else
1624
			w->q1 = 0;
1625
		w->nr -= m;
1626
		runemove(w->r, w->r+m, w->nr);
1627
		q0 -= m;
1628
	}
1629
	if(w->nr+n > w->maxr){
1630
		/*
1631
		 * Minimize realloc breakage:
1632
		 *	Allocate at least MinWater
1633
		 * 	Double allocation size each time
1634
		 *	But don't go much above HiWater
1635
		 */
1636
		m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
1637
		if(m > HiWater)
1638
			m = max(HiWater+MinWater, w->nr+n);
1639
		if(m > w->maxr){
1640
			w->r = runerealloc(w->r, m);
1641
			w->maxr = m;
1642
		}
1643
	}
1644
	runemove(w->r+q0+n, w->r+q0, w->nr-q0);
1645
	runemove(w->r+q0, r, n);
1646
	w->nr += n;
1647
	/* if output touches, advance selection, not qh; works best for keyboard and output */
1648
	if(q0 <= w->q1)
1649
		w->q1 += n;
1650
	if(q0 <= w->q0)
1651
		w->q0 += n;
1652
	if(q0 < w->qh)
1653
		w->qh += n;
1654
	if(q0 < w->org)
1655
		w->org += n;
1656
	else if(q0 <= w->org+w->nchars)
1657
		frinsert(w, r, r+n, q0-w->org);
1658
	return q0;
1659
}
1660
 
1661
void
1662
wfill(Window *w)
1663
{
1664
	Rune *rp;
1665
	int i, n, m, nl;
1666
 
1667
	if(w->lastlinefull)
1668
		return;
1669
	rp = malloc(messagesize);
1670
	do{
1671
		n = w->nr-(w->org+w->nchars);
1672
		if(n == 0)
1673
			break;
1674
		if(n > 2000)	/* educated guess at reasonable amount */
1675
			n = 2000;
1676
		runemove(rp, w->r+(w->org+w->nchars), n);
1677
		/*
1678
		 * it's expensive to frinsert more than we need, so
1679
		 * count newlines.
1680
		 */
1681
		nl = w->maxlines-w->nlines;
1682
		m = 0;
1683
		for(i=0; i<n; ){
1684
			if(rp[i++] == '\n'){
1685
				m++;
1686
				if(m >= nl)
1687
					break;
1688
			}
1689
		}
1690
		frinsert(w, rp, rp+i, w->nchars);
1691
	}while(w->lastlinefull == FALSE);
1692
	free(rp);
1693
}
1694
 
1695
char*
1696
wcontents(Window *w, int *ip)
1697
{
1698
	return runetobyte(w->r, w->nr, ip);
1699
}