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