Blob


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