xref: /plan9/sys/src/cmd/samterm/main.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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 "flayer.h"
10 #include "samterm.h"
11 
12 Text	cmd;
13 Rune	*scratch;
14 long	nscralloc;
15 Cursor	*cursor;
16 Flayer	*which = 0;
17 Flayer	*work = 0;
18 long	snarflen;
19 long	typestart = -1;
20 long	typeend = -1;
21 long	typeesc = -1;
22 long	modified = 0;		/* strange lookahead for menus */
23 char	hostlock = 1;
24 char	hasunlocked = 0;
25 int	maxtab = 8;
26 
27 void
28 threadmain(int argc, char *argv[])
29 {
30 	int i, got, scr;
31 	Text *t;
32 	Rectangle r;
33 	Flayer *nwhich;
34 
35 	getscreen(argc, argv);
36 	iconinit();
37 	initio();
38 	scratch = alloc(100*RUNESIZE);
39 	nscralloc = 100;
40 	r = screen->r;
41 	r.max.y = r.min.y+Dy(r)/5;
42 	flstart(screen->clipr);
43 	rinit(&cmd.rasp);
44 	flnew(&cmd.l[0], gettext, 1, &cmd);
45 	flinit(&cmd.l[0], r, font, cmdcols);
46 	cmd.nwin = 1;
47 	which = &cmd.l[0];
48 	cmd.tag = Untagged;
49 	outTs(Tversion, VERSION);
50 	startnewfile(Tstartcmdfile, &cmd);
51 
52 	got = 0;
53 	for(;;got = waitforio()){
54 		if(hasunlocked && RESIZED())
55 			resize();
56 		if(got&(1<<RHost))
57 			rcv();
58 		if(got&(1<<RPlumb)){
59 			for(i=0; cmd.l[i].textfn==0; i++)
60 				;
61 			current(&cmd.l[i]);
62 			flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes);
63 			type(which, RPlumb);
64 		}
65 		if(got&(1<<RKeyboard))
66 			if(which)
67 				type(which, RKeyboard);
68 			else
69 				kbdblock();
70 		if(got&(1<<RMouse)){
71 			if(hostlock==2 || !ptinrect(mousep->xy, screen->r)){
72 				mouseunblock();
73 				continue;
74 			}
75 			nwhich = flwhich(mousep->xy);
76 			scr = which && ptinrect(mousep->xy, which->scroll);
77 			if(mousep->buttons)
78 				flushtyping(1);
79 			if(mousep->buttons&1){
80 				if(nwhich){
81 					if(nwhich!=which)
82 						current(nwhich);
83 					else if(scr)
84 						scroll(which, 1);
85 					else{
86 						t=(Text *)which->user1;
87 						if(flselect(which)){
88 							outTsl(Tdclick, t->tag, which->p0);
89 							t->lock++;
90 						}else if(t!=&cmd)
91 							outcmd();
92 					}
93 				}
94 			}else if((mousep->buttons&2) && which){
95 				if(scr)
96 					scroll(which, 2);
97 				else
98 					menu2hit();
99 			}else if((mousep->buttons&4)){
100 				if(scr)
101 					scroll(which, 3);
102 				else
103 					menu3hit();
104 			}
105 			mouseunblock();
106 		}
107 	}
108 }
109 
110 
111 void
112 resize(void){
113 	int i;
114 
115 	flresize(screen->clipr);
116 	for(i = 0; i<nname; i++)
117 		if(text[i])
118 			hcheck(text[i]->tag);
119 }
120 
121 void
122 current(Flayer *nw)
123 {
124 	Text *t;
125 
126 	if(which)
127 		flborder(which, 0);
128 	if(nw){
129 		flushtyping(1);
130 		flupfront(nw);
131 		flborder(nw, 1);
132 		buttons(Up);
133 		t = (Text *)nw->user1;
134 		t->front = nw-&t->l[0];
135 		if(t != &cmd)
136 			work = nw;
137 	}
138 	which = nw;
139 }
140 
141 void
142 closeup(Flayer *l)
143 {
144 	Text *t=(Text *)l->user1;
145 	int m;
146 
147 	m = whichmenu(t->tag);
148 	if(m < 0)
149 		return;
150 	flclose(l);
151 	if(l == which){
152 		which = 0;
153 		current(flwhich(Pt(0, 0)));
154 	}
155 	if(l == work)
156 		work = 0;
157 	if(--t->nwin == 0){
158 		rclear(&t->rasp);
159 		free((uchar *)t);
160 		text[m] = 0;
161 	}else if(l == &t->l[t->front]){
162 		for(m=0; m<NL; m++)	/* find one; any one will do */
163 			if(t->l[m].textfn){
164 				t->front = m;
165 				return;
166 			}
167 		panic("close");
168 	}
169 }
170 
171 Flayer *
172 findl(Text *t)
173 {
174 	int i;
175 	for(i = 0; i<NL; i++)
176 		if(t->l[i].textfn==0)
177 			return &t->l[i];
178 	return 0;
179 }
180 
181 void
182 duplicate(Flayer *l, Rectangle r, Font *f, int close)
183 {
184 	Text *t=(Text *)l->user1;
185 	Flayer *nl = findl(t);
186 	Rune *rp;
187 	ulong n;
188 
189 	if(nl){
190 		flnew(nl, gettext, l->user0, (char *)t);
191 		flinit(nl, r, f, l->f.cols);
192 		nl->origin = l->origin;
193 		rp = (*l->textfn)(l, l->f.nchars, &n);
194 		flinsert(nl, rp, rp+n, l->origin);
195 		flsetselect(nl, l->p0, l->p1);
196 		if(close){
197 			flclose(l);
198 			if(l==which)
199 				which = 0;
200 		}else
201 			t->nwin++;
202 		current(nl);
203 		hcheck(t->tag);
204 	}
205 	setcursor(mousectl, cursor);
206 }
207 
208 void
209 buttons(int updown)
210 {
211 	while(((mousep->buttons&7)!=0) != updown)
212 		getmouse();
213 }
214 
215 int
216 getr(Rectangle *rp)
217 {
218 	Point p;
219 	Rectangle r;
220 
221 	*rp = getrect(3, mousectl);
222 	if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){
223 		p = rp->min;
224 		r = cmd.l[cmd.front].entire;
225 		*rp = screen->r;
226 		if(cmd.nwin==1){
227 			if (p.y <= r.min.y)
228 				rp->max.y = r.min.y;
229 			else if (p.y >= r.max.y)
230 				rp->min.y = r.max.y;
231 			if (p.x <= r.min.x)
232 				rp->max.x = r.min.x;
233 			else if (p.x >= r.max.x)
234 				rp->min.x = r.max.x;
235 		}
236 	}
237 	return rectclip(rp, screen->r) &&
238 	   rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
239 }
240 
241 void
242 snarf(Text *t, int w)
243 {
244 	Flayer *l = &t->l[w];
245 
246 	if(l->p1>l->p0){
247 		snarflen = l->p1-l->p0;
248 		outTsll(Tsnarf, t->tag, l->p0, l->p1);
249 	}
250 }
251 
252 void
253 cut(Text *t, int w, int save, int check)
254 {
255 	long p0, p1;
256 	Flayer *l;
257 
258 	l = &t->l[w];
259 	p0 = l->p0;
260 	p1 = l->p1;
261 	if(p0 == p1)
262 		return;
263 	if(p0 < 0)
264 		panic("cut");
265 	if(save)
266 		snarf(t, w);
267 	outTsll(Tcut, t->tag, p0, p1);
268 	flsetselect(l, p0, p0);
269 	t->lock++;
270 	hcut(t->tag, p0, p1-p0);
271 	if(check)
272 		hcheck(t->tag);
273 }
274 
275 void
276 paste(Text *t, int w)
277 {
278 	if(snarflen){
279 		cut(t, w, 0, 0);
280 		t->lock++;
281 		outTsl(Tpaste, t->tag, t->l[w].p0);
282 	}
283 }
284 
285 void
286 scrorigin(Flayer *l, int but, long p0)
287 {
288 	Text *t=(Text *)l->user1;
289 
290 	switch(but){
291 	case 1:
292 		outTsll(Torigin, t->tag, l->origin, p0);
293 		break;
294 	case 2:
295 		outTsll(Torigin, t->tag, p0, 1L);
296 		break;
297 	case 3:
298 		horigin(t->tag,p0);
299 	}
300 }
301 
302 int
303 alnum(int c)
304 {
305 	/*
306 	 * Hard to get absolutely right.  Use what we know about ASCII
307 	 * and assume anything above the Latin control characters is
308 	 * potentially an alphanumeric.
309 	 */
310 	if(c<=' ')
311 		return 0;
312 	if(0x7F<=c && c<=0xA0)
313 		return 0;
314 	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
315 		return 0;
316 	return 1;
317 }
318 
319 int
320 raspc(Rasp *r, long p)
321 {
322 	ulong n;
323 	rload(r, p, p+1, &n);
324 	if(n)
325 		return scratch[0];
326 	return 0;
327 }
328 
329 long
330 ctlw(Rasp *r, long o, long p)
331 {
332 	int c;
333 
334 	if(--p < o)
335 		return o;
336 	if(raspc(r, p)=='\n')
337 		return p;
338 	for(; p>=o && !alnum(c=raspc(r, p)); --p)
339 		if(c=='\n')
340 			return p+1;
341 	for(; p>o && alnum(raspc(r, p-1)); --p)
342 		;
343 	return p>=o? p : o;
344 }
345 
346 long
347 ctlu(Rasp *r, long o, long p)
348 {
349 	if(--p < o)
350 		return o;
351 	if(raspc(r, p)=='\n')
352 		return p;
353 	for(; p-1>=o && raspc(r, p-1)!='\n'; --p)
354 		;
355 	return p>=o? p : o;
356 }
357 
358 int
359 center(Flayer *l, long a)
360 {
361 	Text *t;
362 
363 	t = l->user1;
364 	if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
365 		if(a > t->rasp.nrunes)
366 			a = t->rasp.nrunes;
367 		outTsll(Torigin, t->tag, a, 2L);
368 		return 1;
369 	}
370 	return 0;
371 }
372 
373 int
374 onethird(Flayer *l, long a)
375 {
376 	Text *t;
377 	Rectangle s;
378 	long lines;
379 
380 	t = l->user1;
381 	if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
382 		if(a > t->rasp.nrunes)
383 			a = t->rasp.nrunes;
384 		s = insetrect(l->scroll, 1);
385 		lines = ((s.max.y-s.min.y)/l->f.font->height+1)/3;
386 		if (lines < 2)
387 			lines = 2;
388 		outTsll(Torigin, t->tag, a, lines);
389 		return 1;
390 	}
391 	return 0;
392 }
393 
394 void
395 flushtyping(int clearesc)
396 {
397 	Text *t;
398 	ulong n;
399 
400 	if(clearesc)
401 		typeesc = -1;
402 	if(typestart == typeend) {
403 		modified = 0;
404 		return;
405 	}
406 	t = which->user1;
407 	if(t != &cmd)
408 		modified = 1;
409 	rload(&t->rasp, typestart, typeend, &n);
410 	scratch[n] = 0;
411 	if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){
412 		setlock();
413 		outcmd();
414 	}
415 	outTslS(Ttype, t->tag, typestart, scratch);
416 	typestart = -1;
417 	typeend = -1;
418 }
419 
420 #define	SCROLLKEY	Kdown
421 #define	BACKSCROLLKEY	Kup
422 #define	ESC		0x1B
423 
424 void
425 type(Flayer *l, int res)	/* what a bloody mess this is */
426 {
427 	Text *t = (Text *)l->user1;
428 	Rune buf[100];
429 	Rune *p = buf;
430 	int c, backspacing;
431 	long a, a0;
432 	int scrollkey;
433 
434 	scrollkey = 0;
435 	if(res == RKeyboard)
436 		scrollkey = (qpeekc()==SCROLLKEY || qpeekc()==BACKSCROLLKEY);	/* ICK */
437 
438 	if(hostlock || t->lock){
439 		kbdblock();
440 		return;
441 	}
442 	a = l->p0;
443 	if(a!=l->p1 && !scrollkey){
444 		flushtyping(1);
445 		cut(t, t->front, 1, 1);
446 		return;	/* it may now be locked */
447 	}
448 	backspacing = 0;
449 	while((c = kbdchar())>0){
450 		if(res == RKeyboard){
451 			if(c==SCROLLKEY || c==BACKSCROLLKEY || c==ESC)
452 				break;
453 			/* backspace, ctrl-u, ctrl-w, del */
454 			if(c=='\b' || c==0x15 || c==0x17 || c==0x7F){
455 				backspacing = 1;
456 				break;
457 			}
458 		}
459 		*p++ = c;
460 		if(c == '\n' || p >= buf+sizeof(buf)/sizeof(buf[0]))
461 			break;
462 	}
463 	if(p > buf){
464 		if(typestart < 0)
465 			typestart = a;
466 		if(typeesc < 0)
467 			typeesc = a;
468 		hgrow(t->tag, a, p-buf, 0);
469 		t->lock++;	/* pretend we Trequest'ed for hdatarune*/
470 		hdatarune(t->tag, a, buf, p-buf);
471 		a += p-buf;
472 		l->p0 = a;
473 		l->p1 = a;
474 		typeend = a;
475 		if(c=='\n' || typeend-typestart>100)
476 			flushtyping(0);
477 		onethird(l, a);
478 	}
479 	if(c == SCROLLKEY){
480 		flushtyping(0);
481 		center(l, l->origin+l->f.nchars+1);
482 		/* backspacing immediately after outcmd(): sorry */
483 	}else if(c == BACKSCROLLKEY){
484 		flushtyping(0);
485 		a0 = l->origin-l->f.nchars;
486 		if(a0 < 0)
487 			a0 = 0;
488 		center(l, a0);
489 	}else if(backspacing && !hostlock){
490 		if(l->f.p0>0 && a>0){
491 			switch(c){
492 			case '\b':
493 			case 0x7F:	/* del */
494 				l->p0 = a-1;
495 				break;
496 			case 0x15:	/* ctrl-u */
497 				l->p0 = ctlu(&t->rasp, l->origin, a);
498 				break;
499 			case 0x17:	/* ctrl-w */
500 				l->p0 = ctlw(&t->rasp, l->origin, a);
501 				break;
502 			}
503 			l->p1 = a;
504 			if(l->p1 != l->p0){
505 				/* cut locally if possible */
506 				if(typestart<=l->p0 && l->p1<=typeend){
507 					t->lock++;	/* to call hcut */
508 					hcut(t->tag, l->p0, l->p1-l->p0);
509 					/* hcheck is local because we know rasp is contiguous */
510 					hcheck(t->tag);
511 				}else{
512 					flushtyping(0);
513 					cut(t, t->front, 0, 1);
514 				}
515 			}
516 			if(typeesc >= l->p0)
517 				typeesc = l->p0;
518 			if(typestart >= 0){
519 				if(typestart >= l->p0)
520 					typestart = l->p0;
521 				typeend = l->p0;
522 				if(typestart == typeend){
523 					typestart = -1;
524 					typeend = -1;
525 					modified = 0;
526 				}
527 			}
528 		}
529 	}else{
530 		if(c==ESC && typeesc>=0){
531 			l->p0 = typeesc;
532 			l->p1 = a;
533 			flushtyping(1);
534 		}
535 		for(l=t->l; l<&t->l[NL]; l++)
536 			if(l->textfn)
537 				flsetselect(l, l->p0, l->p1);
538 	}
539 }
540 
541 
542 void
543 outcmd(void){
544 	if(work)
545 		outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1);
546 }
547 
548 void
549 panic(char *s)
550 {
551 	panic1(display, s);
552 }
553 
554 void
555 panic1(Display*, char *s)
556 {
557 	fprint(2, "samterm:panic: ");
558 	perror(s);
559 	abort();
560 }
561 
562 Rune*
563 gettext(Flayer *l, long n, ulong *np)
564 {
565 	Text *t;
566 
567 	t = l->user1;
568 	rload(&t->rasp, l->origin, l->origin+n, np);
569 	return scratch;
570 }
571 
572 long
573 scrtotal(Flayer *l)
574 {
575 	return ((Text *)l->user1)->rasp.nrunes;
576 }
577 
578 void*
579 alloc(ulong n)
580 {
581 	void *p;
582 
583 	p = malloc(n);
584 	if(p == 0)
585 		panic("alloc");
586 	memset(p, 0, n);
587 	return p;
588 }
589