plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

main.c (12993B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <draw.h>
      4 #include <thread.h>
      5 #include <mouse.h>
      6 #include <cursor.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 int	chord;
     27 int	autoindent;
     28 
     29 #define chording 0	/* code here for reference but it causes deadlocks */
     30 
     31 void
     32 notifyf(void *a, char *msg)
     33 {
     34 	if(strcmp(msg, "interrupt") == 0)
     35 		noted(NCONT);
     36 	noted(NDFLT);
     37 }
     38 
     39 void
     40 threadmain(int argc, char *argv[])
     41 {
     42 	int i, got, scr, w;
     43 	Text *t;
     44 	Rectangle r;
     45 	Flayer *nwhich;
     46 
     47 	/*
     48 	 * sam is talking to us on fd 0 and 1.
     49 	 * move these elsewhere so that if we accidentally
     50 	 * use 0 and 1 in other code, nothing bad happens.
     51 	 */
     52 	dup(0, 3);
     53 	dup(1, 4);
     54 	hostfd[0] = 3;
     55 	hostfd[1] = 4;
     56 	close(0);
     57 	close(1);
     58 	open("/dev/null", OREAD);
     59 	if(open("/dev/tty", OWRITE) < 0)
     60 		open("/dev/null", OWRITE);
     61 
     62 	notify(notifyf);
     63 
     64 	if(protodebug) print("getscreen\n");
     65 	getscreen(argc, argv);
     66 	if(protodebug) print("iconinit\n");
     67 	iconinit();
     68 	if(protodebug) print("initio\n");
     69 	initio();
     70 	if(protodebug) print("scratch\n");
     71 	scratch = alloc(100*RUNESIZE);
     72 	nscralloc = 100;
     73 	r = screen->r;
     74 	r.max.y = r.min.y+Dy(r)/5;
     75 	if(protodebug) print("flstart\n");
     76 	flstart(screen->clipr);
     77 	rinit(&cmd.rasp);
     78 	flnew(&cmd.l[0], gettext, 1, &cmd);
     79 	flinit(&cmd.l[0], r, font, cmdcols);
     80 	cmd.nwin = 1;
     81 	which = &cmd.l[0];
     82 	cmd.tag = Untagged;
     83 	outTs(Tversion, VERSION);
     84 	startnewfile(Tstartcmdfile, &cmd);
     85 
     86 	got = 0;
     87 	if(protodebug) print("loop\n");
     88 	for(;;got = waitforio()){
     89 		if(hasunlocked && RESIZED())
     90 			resize();
     91 		if(got&(1<<RHost))
     92 			rcv();
     93 		if(got&(1<<RPlumb)){
     94 			for(i=0; cmd.l[i].textfn==0; i++)
     95 				;
     96 			current(&cmd.l[i]);
     97 			flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes);
     98 			type(which, RPlumb);
     99 		}
    100 		if(got&(1<<RKeyboard))
    101 			if(which)
    102 				type(which, RKeyboard);
    103 			else
    104 				kbdblock();
    105 		if(got&(1<<RMouse)){
    106 			if(hostlock==2 || !ptinrect(mousep->xy, screen->r)){
    107 				mouseunblock();
    108 				continue;
    109 			}
    110 			nwhich = flwhich(mousep->xy);
    111 			scr = which && ptinrect(mousep->xy, which->scroll);
    112 			if(mousep->buttons)
    113 				flushtyping(1);
    114 			if(chording && chord==1 && !mousep->buttons)
    115 				chord = 0;
    116 			if(chording && chord)
    117 				chord |= mousep->buttons;
    118 			else if(mousep->buttons&1){
    119 				if(nwhich){
    120 					if(nwhich!=which)
    121 						current(nwhich);
    122 					else if(scr)
    123 						scroll(which, 1);
    124 					else{
    125 						t=(Text *)which->user1;
    126 						if(flselect(which)){
    127 							outTsl(Tdclick, t->tag, which->p0);
    128 							t->lock++;
    129 						}else if(t!=&cmd)
    130 							outcmd();
    131 						if(mousep->buttons&1)
    132 							chord = mousep->buttons;
    133 					}
    134 				}
    135 			}else if((mousep->buttons&2) && which){
    136 				if(scr)
    137 					scroll(which, 2);
    138 				else
    139 					menu2hit();
    140 			}else if((mousep->buttons&4)){
    141 				if(scr)
    142 					scroll(which, 3);
    143 				else
    144 					menu3hit();
    145 			}
    146 			mouseunblock();
    147 		}
    148 		if(chording && chord){
    149 			t = (Text*)which->user1;
    150 			if(!t->lock && !hostlock){
    151 				w = which-t->l;
    152 				if(chord&2){
    153 					cut(t, w, 1, 1);
    154 					chord &= ~2;
    155 				}else if(chord&4){
    156 					paste(t, w);
    157 					chord &= ~4;
    158 				}
    159 			}
    160 		}
    161 	}
    162 }
    163 
    164 void
    165 resize(void)
    166 {
    167 	int i;
    168 
    169 	flresize(screen->clipr);
    170 	for(i = 0; i<nname; i++)
    171 		if(text[i])
    172 			hcheck(text[i]->tag);
    173 }
    174 
    175 void
    176 current(Flayer *nw)
    177 {
    178 	Text *t;
    179 
    180 	if(which)
    181 		flborder(which, 0);
    182 	if(nw){
    183 		flushtyping(1);
    184 		flupfront(nw);
    185 		flborder(nw, 1);
    186 		buttons(Up);
    187 		t = (Text *)nw->user1;
    188 		t->front = nw-&t->l[0];
    189 		if(t != &cmd)
    190 			work = nw;
    191 	}
    192 	which = nw;
    193 }
    194 
    195 void
    196 closeup(Flayer *l)
    197 {
    198 	Text *t=(Text *)l->user1;
    199 	int m;
    200 
    201 	m = whichmenu(t->tag);
    202 	if(m < 0)
    203 		return;
    204 	flclose(l);
    205 	if(l == which){
    206 		which = 0;
    207 		current(flwhich(Pt(0, 0)));
    208 	}
    209 	if(l == work)
    210 		work = 0;
    211 	if(--t->nwin == 0){
    212 		rclear(&t->rasp);
    213 		free((uchar *)t);
    214 		text[m] = 0;
    215 	}else if(l == &t->l[t->front]){
    216 		for(m=0; m<NL; m++)	/* find one; any one will do */
    217 			if(t->l[m].textfn){
    218 				t->front = m;
    219 				return;
    220 			}
    221 		panic("close");
    222 	}
    223 }
    224 
    225 Flayer *
    226 findl(Text *t)
    227 {
    228 	int i;
    229 	for(i = 0; i<NL; i++)
    230 		if(t->l[i].textfn==0)
    231 			return &t->l[i];
    232 	return 0;
    233 }
    234 
    235 void
    236 duplicate(Flayer *l, Rectangle r, Font *f, int close)
    237 {
    238 	Text *t=(Text *)l->user1;
    239 	Flayer *nl = findl(t);
    240 	Rune *rp;
    241 	ulong n;
    242 
    243 	if(nl){
    244 		flnew(nl, gettext, l->user0, (char *)t);
    245 		flinit(nl, r, f, l->f.cols);
    246 		nl->origin = l->origin;
    247 		rp = (*l->textfn)(l, l->f.nchars, &n);
    248 		flinsert(nl, rp, rp+n, l->origin);
    249 		flsetselect(nl, l->p0, l->p1);
    250 		if(close){
    251 			flclose(l);
    252 			if(l==which)
    253 				which = 0;
    254 		}else
    255 			t->nwin++;
    256 		current(nl);
    257 		hcheck(t->tag);
    258 	}
    259 	setcursor(mousectl, cursor);
    260 }
    261 
    262 void
    263 buttons(int updown)
    264 {
    265 	while(((mousep->buttons&7)!=0) != updown)
    266 		getmouse();
    267 }
    268 
    269 int
    270 getr(Rectangle *rp)
    271 {
    272 	Point p;
    273 	Rectangle r;
    274 
    275 	*rp = getrect(3, mousectl);
    276 	if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){
    277 		p = rp->min;
    278 		r = cmd.l[cmd.front].entire;
    279 		*rp = screen->r;
    280 		if(cmd.nwin==1){
    281 			if (p.y <= r.min.y)
    282 				rp->max.y = r.min.y;
    283 			else if (p.y >= r.max.y)
    284 				rp->min.y = r.max.y;
    285 			if (p.x <= r.min.x)
    286 				rp->max.x = r.min.x;
    287 			else if (p.x >= r.max.x)
    288 				rp->min.x = r.max.x;
    289 		}
    290 	}
    291 	return rectclip(rp, screen->r) &&
    292 	   rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
    293 }
    294 
    295 void
    296 snarf(Text *t, int w)
    297 {
    298 	Flayer *l = &t->l[w];
    299 
    300 	if(l->p1>l->p0){
    301 		snarflen = l->p1-l->p0;
    302 		outTsll(Tsnarf, t->tag, l->p0, l->p1);
    303 	}
    304 }
    305 
    306 void
    307 cut(Text *t, int w, int save, int check)
    308 {
    309 	long p0, p1;
    310 	Flayer *l;
    311 
    312 	l = &t->l[w];
    313 	p0 = l->p0;
    314 	p1 = l->p1;
    315 	if(p0 == p1)
    316 		return;
    317 	if(p0 < 0)
    318 		panic("cut");
    319 	if(save)
    320 		snarf(t, w);
    321 	outTsll(Tcut, t->tag, p0, p1);
    322 	flsetselect(l, p0, p0);
    323 	t->lock++;
    324 	hcut(t->tag, p0, p1-p0);
    325 	if(check)
    326 		hcheck(t->tag);
    327 }
    328 
    329 void
    330 paste(Text *t, int w)
    331 {
    332 	if(snarflen){
    333 		cut(t, w, 0, 0);
    334 		t->lock++;
    335 		outTsl(Tpaste, t->tag, t->l[w].p0);
    336 	}
    337 }
    338 
    339 void
    340 scrorigin(Flayer *l, int but, long p0)
    341 {
    342 	Text *t=(Text *)l->user1;
    343 
    344 	switch(but){
    345 	case 1:
    346 		outTsll(Torigin, t->tag, l->origin, p0);
    347 		break;
    348 	case 2:
    349 		outTsll(Torigin, t->tag, p0, 1L);
    350 		break;
    351 	case 3:
    352 		horigin(t->tag,p0);
    353 	}
    354 }
    355 
    356 int
    357 alnum(int c)
    358 {
    359 	/*
    360 	 * Hard to get absolutely right.  Use what we know about ASCII
    361 	 * and assume anything above the Latin control characters is
    362 	 * potentially an alphanumeric.
    363 	 */
    364 	if(c<=' ')
    365 		return 0;
    366 	if(0x7F<=c && c<=0xA0)
    367 		return 0;
    368 	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
    369 		return 0;
    370 	return 1;
    371 }
    372 
    373 int
    374 raspc(Rasp *r, long p)
    375 {
    376 	ulong n;
    377 	rload(r, p, p+1, &n);
    378 	if(n)
    379 		return scratch[0];
    380 	return 0;
    381 }
    382 
    383 long
    384 ctlw(Rasp *r, long o, long p)
    385 {
    386 	int c;
    387 
    388 	if(--p < o)
    389 		return o;
    390 	if(raspc(r, p)=='\n')
    391 		return p;
    392 	for(; p>=o && !alnum(c=raspc(r, p)); --p)
    393 		if(c=='\n')
    394 			return p+1;
    395 	for(; p>o && alnum(raspc(r, p-1)); --p)
    396 		;
    397 	return p>=o? p : o;
    398 }
    399 
    400 long
    401 ctlu(Rasp *r, long o, long p)
    402 {
    403 	if(--p < o)
    404 		return o;
    405 	if(raspc(r, p)=='\n')
    406 		return p;
    407 	for(; p-1>=o && raspc(r, p-1)!='\n'; --p)
    408 		;
    409 	return p>=o? p : o;
    410 }
    411 
    412 int
    413 center(Flayer *l, long a)
    414 {
    415 	Text *t;
    416 
    417 	t = l->user1;
    418 	if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
    419 		if(a > t->rasp.nrunes)
    420 			a = t->rasp.nrunes;
    421 		outTsll(Torigin, t->tag, a, 2L);
    422 		return 1;
    423 	}
    424 	return 0;
    425 }
    426 
    427 int
    428 thirds(Flayer *l, long a, int n)
    429 {
    430 	Text *t;
    431 	Rectangle s;
    432 	long lines;
    433 
    434 	t = l->user1;
    435 	if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
    436 		if(a > t->rasp.nrunes)
    437 			a = t->rasp.nrunes;
    438 		s = insetrect(l->scroll, 1);
    439 		lines = (n*(s.max.y-s.min.y)/l->f.font->height+1)/3;
    440 		if (lines < 2)
    441 			lines = 2;
    442 		outTsll(Torigin, t->tag, a, lines);
    443 		return 1;
    444 	}
    445 	return 0;
    446 }
    447 
    448 int
    449 onethird(Flayer *l, long a)
    450 {
    451 	return thirds(l, a, 1);
    452 }
    453 
    454 int
    455 twothirds(Flayer *l, long a)
    456 {
    457 	return thirds(l, a, 2);
    458 }
    459 
    460 void
    461 flushtyping(int clearesc)
    462 {
    463 	Text *t;
    464 	ulong n;
    465 
    466 	if(clearesc)
    467 		typeesc = -1;
    468 	if(typestart == typeend) {
    469 		modified = 0;
    470 		return;
    471 	}
    472 	t = which->user1;
    473 	if(t != &cmd)
    474 		modified = 1;
    475 	rload(&t->rasp, typestart, typeend, &n);
    476 	scratch[n] = 0;
    477 	if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){
    478 		setlock();
    479 		outcmd();
    480 	}
    481 	outTslS(Ttype, t->tag, typestart, scratch);
    482 	typestart = -1;
    483 	typeend = -1;
    484 }
    485 
    486 #define	BACKSCROLLKEY	Kup
    487 #define	ENDKEY	Kend
    488 #define	ESC		0x1B
    489 #define	HOMEKEY	Khome
    490 #define	LEFTARROW	Kleft
    491 #define	LINEEND	0x05
    492 #define	LINESTART	0x01
    493 #define	PAGEDOWN	Kpgdown
    494 #define	PAGEUP	Kpgup
    495 #define	RIGHTARROW	Kright
    496 #define	SCROLLKEY	Kdown
    497 #define	CUT	(Kcmd+'x')
    498 #define	COPY	(Kcmd+'c')
    499 #define	PASTE	(Kcmd+'v')
    500 
    501 int
    502 nontypingkey(int c)
    503 {
    504 	switch(c){
    505 	case BACKSCROLLKEY:
    506 	case ENDKEY:
    507 	case HOMEKEY:
    508 	case LEFTARROW:
    509 	case LINEEND:
    510 	case LINESTART:
    511 	case PAGEDOWN:
    512 	case PAGEUP:
    513 	case RIGHTARROW:
    514 	case SCROLLKEY:
    515 	case CUT:
    516 	case COPY:
    517 	case PASTE:
    518 		return 1;
    519 	}
    520 	return 0;
    521 }
    522 
    523 void
    524 type(Flayer *l, int res)	/* what a bloody mess this is */
    525 {
    526 	Text *t = (Text *)l->user1;
    527 	Rune buf[100];
    528 	Rune *p = buf;
    529 	int c, backspacing;
    530 	long a, a0;
    531 	int scrollkey;
    532 
    533 	scrollkey = 0;
    534 	if(res == RKeyboard)
    535 		scrollkey = nontypingkey(qpeekc());	/* ICK */
    536 
    537 	if(hostlock || t->lock){
    538 		kbdblock();
    539 		return;
    540 	}
    541 	a = l->p0;
    542 	if(a!=l->p1 && !scrollkey){
    543 		flushtyping(1);
    544 		cut(t, t->front, 1, 1);
    545 		return;	/* it may now be locked */
    546 	}
    547 	backspacing = 0;
    548 	while((c = kbdchar())>0){
    549 		if(res == RKeyboard){
    550 			if(nontypingkey(c) || c==ESC)
    551 				break;
    552 			/* backspace, ctrl-u, ctrl-w, del */
    553 			if(c=='\b' || c==0x15 || c==0x17 || c==0x7F){
    554 				backspacing = 1;
    555 				break;
    556 			}
    557 		}
    558 		*p++ = c;
    559 		if(autoindent)
    560 		if(c == '\n'){
    561 			/* autoindent */
    562 			int cursor, ch;
    563 			cursor = ctlu(&t->rasp, 0, a+(p-buf)-1);
    564 			while(p < buf+nelem(buf)){
    565 				ch = raspc(&t->rasp, cursor++);
    566 				if(ch == ' ' || ch == '\t')
    567 					*p++ = ch;
    568 				else
    569 					break;
    570 			}
    571 		}
    572 		if(c == '\n' || p >= buf+nelem(buf))
    573 			break;
    574 	}
    575 	if(p > buf){
    576 		if(typestart < 0)
    577 			typestart = a;
    578 		if(typeesc < 0)
    579 			typeesc = a;
    580 		hgrow(t->tag, a, p-buf, 0);
    581 		t->lock++;	/* pretend we Trequest'ed for hdatarune*/
    582 		hdatarune(t->tag, a, buf, p-buf);
    583 		a += p-buf;
    584 		l->p0 = a;
    585 		l->p1 = a;
    586 		typeend = a;
    587 		if(c=='\n' || typeend-typestart>100)
    588 			flushtyping(0);
    589 		onethird(l, a);
    590 	}
    591 	if(c==SCROLLKEY || c==PAGEDOWN){
    592 		flushtyping(0);
    593 		center(l, l->origin+l->f.nchars+1);
    594 	}else if(c==BACKSCROLLKEY || c==PAGEUP){
    595 		flushtyping(0);
    596 		a0 = l->origin-l->f.nchars;
    597 		if(a0 < 0)
    598 			a0 = 0;
    599 		center(l, a0);
    600 	}else if(c == RIGHTARROW){
    601 		flushtyping(0);
    602 		a0 = l->p0;
    603 		if(a0 < t->rasp.nrunes)
    604 			a0++;
    605 		flsetselect(l, a0, a0);
    606 		center(l, a0);
    607 	}else if(c == LEFTARROW){
    608 		flushtyping(0);
    609 		a0 = l->p0;
    610 		if(a0 > 0)
    611 			a0--;
    612 		flsetselect(l, a0, a0);
    613 		center(l, a0);
    614 	}else if(c == HOMEKEY){
    615 		flushtyping(0);
    616 		center(l, 0);
    617 	}else if(c == ENDKEY){
    618 		flushtyping(0);
    619 		center(l, t->rasp.nrunes);
    620 	}else if(c == LINESTART || c == LINEEND){
    621 		flushtyping(1);
    622 		if(c == LINESTART)
    623 			while(a > 0 && raspc(&t->rasp, a-1)!='\n')
    624 				a--;
    625 		else
    626 			while(a < t->rasp.nrunes && raspc(&t->rasp, a)!='\n')
    627 				a++;
    628 		l->p0 = l->p1 = a;
    629 		for(l=t->l; l<&t->l[NL]; l++)
    630 			if(l->textfn)
    631 				flsetselect(l, l->p0, l->p1);
    632 	}else if(backspacing && !hostlock){
    633 		/* backspacing immediately after outcmd(): sorry */
    634 		if(l->f.p0>0 && a>0){
    635 			switch(c){
    636 			case '\b':
    637 			case 0x7F:	/* del */
    638 				l->p0 = a-1;
    639 				break;
    640 			case 0x15:	/* ctrl-u */
    641 				l->p0 = ctlu(&t->rasp, l->origin, a);
    642 				break;
    643 			case 0x17:	/* ctrl-w */
    644 				l->p0 = ctlw(&t->rasp, l->origin, a);
    645 				break;
    646 			}
    647 			l->p1 = a;
    648 			if(l->p1 != l->p0){
    649 				/* cut locally if possible */
    650 				if(typestart<=l->p0 && l->p1<=typeend){
    651 					t->lock++;	/* to call hcut */
    652 					hcut(t->tag, l->p0, l->p1-l->p0);
    653 					/* hcheck is local because we know rasp is contiguous */
    654 					hcheck(t->tag);
    655 				}else{
    656 					flushtyping(0);
    657 					cut(t, t->front, 0, 1);
    658 				}
    659 			}
    660 			if(typeesc >= l->p0)
    661 				typeesc = l->p0;
    662 			if(typestart >= 0){
    663 				if(typestart >= l->p0)
    664 					typestart = l->p0;
    665 				typeend = l->p0;
    666 				if(typestart == typeend){
    667 					typestart = -1;
    668 					typeend = -1;
    669 					modified = 0;
    670 				}
    671 			}
    672 		}
    673 	}else{
    674 		if(c==ESC && typeesc>=0){
    675 			l->p0 = typeesc;
    676 			l->p1 = a;
    677 			flushtyping(1);
    678 		}
    679 		for(l=t->l; l<&t->l[NL]; l++)
    680 			if(l->textfn)
    681 				flsetselect(l, l->p0, l->p1);
    682 		switch(c) {
    683 		case CUT:
    684 			flushtyping(0);
    685 			cut(t, t->front, 1, 1);
    686 			break;
    687 		case COPY:
    688 			flushtyping(0);
    689 			snarf(t, t->front);
    690 			break;
    691 		case PASTE:
    692 			flushtyping(0);
    693 			paste(t, t->front);
    694 			break;
    695 		}
    696 	}
    697 }
    698 
    699 
    700 void
    701 outcmd(void){
    702 	if(work)
    703 		outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1);
    704 }
    705 
    706 void
    707 panic(char *s)
    708 {
    709 	panic1(display, s);
    710 }
    711 
    712 void
    713 panic1(Display *d, char *s)
    714 {
    715 	fprint(2, "samterm:panic: ");
    716 	perror(s);
    717 	abort();
    718 }
    719 
    720 Rune*
    721 gettext(Flayer *l, long n, ulong *np)
    722 {
    723 	Text *t;
    724 
    725 	t = l->user1;
    726 	rload(&t->rasp, l->origin, l->origin+n, np);
    727 	return scratch;
    728 }
    729 
    730 long
    731 scrtotal(Flayer *l)
    732 {
    733 	return ((Text *)l->user1)->rasp.nrunes;
    734 }
    735 
    736 void*
    737 alloc(ulong n)
    738 {
    739 	void *p;
    740 
    741 	p = malloc(n);
    742 	if(p == 0)
    743 		panic("alloc");
    744 	memset(p, 0, n);
    745 	return p;
    746 }