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