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