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 notifyf(void *a, char *msg)
33 {
34 if(strcmp(msg, "interrupt") == 0)
35 noted(NCONT);
36 noted(NDFLT);
37 }
39 void
40 threadmain(int argc, char *argv[])
41 {
42 int i, got, scr, w;
43 Text *t;
44 Rectangle r;
45 Flayer *nwhich;
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);
62 notify(notifyf);
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);
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;
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;
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();
146 mouseunblock();
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;
164 void
165 resize(void)
167 int i;
169 flresize(screen->clipr);
170 for(i = 0; i<nname; i++)
171 if(text[i])
172 hcheck(text[i]->tag);
175 void
176 current(Flayer *nw)
178 Text *t;
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;
192 which = nw;
195 void
196 closeup(Flayer *l)
198 Text *t=(Text *)l->user1;
199 int m;
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)));
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;
221 panic("close");
225 Flayer *
226 findl(Text *t)
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;
235 void
236 duplicate(Flayer *l, Rectangle r, Font *f, int close)
238 Text *t=(Text *)l->user1;
239 Flayer *nl = findl(t);
240 Rune *rp;
241 ulong n;
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);
259 setcursor(mousectl, cursor);
262 void
263 buttons(int updown)
265 while(((mousep->buttons&7)!=0) != updown)
266 getmouse();
269 int
270 getr(Rectangle *rp)
272 Point p;
273 Rectangle r;
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;
291 return rectclip(rp, screen->r) &&
292 rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
295 void
296 snarf(Text *t, int w)
298 Flayer *l = &t->l[w];
300 if(l->p1>l->p0){
301 snarflen = l->p1-l->p0;
302 outTsll(Tsnarf, t->tag, l->p0, l->p1);
306 void
307 cut(Text *t, int w, int save, int check)
309 long p0, p1;
310 Flayer *l;
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);
329 void
330 paste(Text *t, int w)
332 if(snarflen){
333 cut(t, w, 0, 0);
334 t->lock++;
335 outTsl(Tpaste, t->tag, t->l[w].p0);
339 void
340 scrorigin(Flayer *l, int but, long p0)
342 Text *t=(Text *)l->user1;
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);
356 int
357 alnum(int c)
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;
373 int
374 raspc(Rasp *r, long p)
376 ulong n;
377 rload(r, p, p+1, &n);
378 if(n)
379 return scratch[0];
380 return 0;
383 long
384 ctlw(Rasp *r, long o, long p)
386 int c;
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)
397 return p>=o? p : o;
400 long
401 ctlu(Rasp *r, long o, long p)
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)
409 return p>=o? p : o;
412 int
413 center(Flayer *l, long a)
415 Text *t;
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;
424 return 0;
427 int
428 thirds(Flayer *l, long a, int n)
430 Text *t;
431 Rectangle s;
432 long lines;
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;
445 return 0;
448 int
449 onethird(Flayer *l, long a)
451 return thirds(l, a, 1);
454 int
455 twothirds(Flayer *l, long a)
457 return thirds(l, a, 2);
460 void
461 flushtyping(int clearesc)
463 Text *t;
464 ulong n;
466 if(clearesc)
467 typeesc = -1;
468 if(typestart == typeend) {
469 modified = 0;
470 return;
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();
481 outTslS(Ttype, t->tag, typestart, scratch);
482 typestart = -1;
483 typeend = -1;
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')
501 int
502 nontypingkey(int c)
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;
520 return 0;
523 void
524 type(Flayer *l, int res) /* what a bloody mess this is */
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;
533 scrollkey = 0;
534 if(res == RKeyboard)
535 scrollkey = nontypingkey(qpeekc()); /* ICK */
537 if(hostlock || t->lock){
538 kbdblock();
539 return;
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 */
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;
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;
572 if(c == '\n' || p >= buf+nelem(buf))
573 break;
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);
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;
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);
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;
673 }else{
674 if(c==ESC && typeesc>=0){
675 l->p0 = typeesc;
676 l->p1 = a;
677 flushtyping(1);
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;
700 void
701 outcmd(void){
702 if(work)
703 outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1);
706 void
707 panic(char *s)
709 panic1(display, s);
712 void
713 panic1(Display *d, char *s)
715 fprint(2, "samterm:panic: ");
716 perror(s);
717 abort();
720 Rune*
721 gettext(Flayer *l, long n, ulong *np)
723 Text *t;
725 t = l->user1;
726 rload(&t->rasp, l->origin, l->origin+n, np);
727 return scratch;
730 long
731 scrtotal(Flayer *l)
733 return ((Text *)l->user1)->rasp.nrunes;
736 void*
737 alloc(ulong n)
739 void *p;
741 p = malloc(n);
742 if(p == 0)
743 panic("alloc");
744 memset(p, 0, n);
745 return p;