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);
53 dup(2, 1);
55 if(protodebug) print("getscreen\n");
56 getscreen(argc, argv);
57 if(protodebug) print("iconinit\n");
58 iconinit();
59 if(protodebug) print("initio\n");
60 initio();
61 if(protodebug) print("scratch\n");
62 scratch = alloc(100*RUNESIZE);
63 nscralloc = 100;
64 r = screen->r;
65 r.max.y = r.min.y+Dy(r)/5;
66 if(protodebug) print("flstart\n");
67 flstart(screen->clipr);
68 rinit(&cmd.rasp);
69 flnew(&cmd.l[0], gettext, 1, &cmd);
70 flinit(&cmd.l[0], r, font, cmdcols);
71 cmd.nwin = 1;
72 which = &cmd.l[0];
73 cmd.tag = Untagged;
74 outTs(Tversion, VERSION);
75 startnewfile(Tstartcmdfile, &cmd);
77 got = 0;
78 if(protodebug) print("loop\n");
79 for(;;got = waitforio()){
80 if(hasunlocked && RESIZED())
81 resize();
82 if(got&(1<<RHost))
83 rcv();
84 if(got&(1<<RPlumb)){
85 for(i=0; cmd.l[i].textfn==0; i++)
86 ;
87 current(&cmd.l[i]);
88 flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes);
89 type(which, RPlumb);
90 }
91 if(got&(1<<RKeyboard))
92 if(which)
93 type(which, RKeyboard);
94 else
95 kbdblock();
96 if(got&(1<<RMouse)){
97 if(hostlock==2 || !ptinrect(mousep->xy, screen->r)){
98 mouseunblock();
99 continue;
101 nwhich = flwhich(mousep->xy);
102 scr = which && ptinrect(mousep->xy, which->scroll);
103 if(mousep->buttons)
104 flushtyping(1);
105 if(chording && chord==1 && !mousep->buttons)
106 chord = 0;
107 if(chording && chord)
108 chord |= mousep->buttons;
109 else if(mousep->buttons&1){
110 if(nwhich){
111 if(nwhich!=which)
112 current(nwhich);
113 else if(scr)
114 scroll(which, 1);
115 else{
116 t=(Text *)which->user1;
117 if(flselect(which)){
118 outTsl(Tdclick, t->tag, which->p0);
119 t->lock++;
120 }else if(t!=&cmd)
121 outcmd();
122 if(mousep->buttons&1)
123 chord = mousep->buttons;
126 }else if((mousep->buttons&2) && which){
127 if(scr)
128 scroll(which, 2);
129 else
130 menu2hit();
131 }else if((mousep->buttons&4)){
132 if(scr)
133 scroll(which, 3);
134 else
135 menu3hit();
137 mouseunblock();
139 if(chording && chord){
140 t = (Text*)which->user1;
141 if(!t->lock && !hostlock){
142 w = which-t->l;
143 if(chord&2){
144 cut(t, w, 1, 1);
145 chord &= ~2;
146 }else if(chord&4){
147 paste(t, w);
148 chord &= ~4;
155 void
156 resize(void)
158 int i;
160 flresize(screen->clipr);
161 for(i = 0; i<nname; i++)
162 if(text[i])
163 hcheck(text[i]->tag);
166 void
167 current(Flayer *nw)
169 Text *t;
171 if(which)
172 flborder(which, 0);
173 if(nw){
174 flushtyping(1);
175 flupfront(nw);
176 flborder(nw, 1);
177 buttons(Up);
178 t = (Text *)nw->user1;
179 t->front = nw-&t->l[0];
180 if(t != &cmd)
181 work = nw;
183 which = nw;
186 void
187 closeup(Flayer *l)
189 Text *t=(Text *)l->user1;
190 int m;
192 m = whichmenu(t->tag);
193 if(m < 0)
194 return;
195 flclose(l);
196 if(l == which){
197 which = 0;
198 current(flwhich(Pt(0, 0)));
200 if(l == work)
201 work = 0;
202 if(--t->nwin == 0){
203 rclear(&t->rasp);
204 free((uchar *)t);
205 text[m] = 0;
206 }else if(l == &t->l[t->front]){
207 for(m=0; m<NL; m++) /* find one; any one will do */
208 if(t->l[m].textfn){
209 t->front = m;
210 return;
212 panic("close");
216 Flayer *
217 findl(Text *t)
219 int i;
220 for(i = 0; i<NL; i++)
221 if(t->l[i].textfn==0)
222 return &t->l[i];
223 return 0;
226 void
227 duplicate(Flayer *l, Rectangle r, Font *f, int close)
229 Text *t=(Text *)l->user1;
230 Flayer *nl = findl(t);
231 Rune *rp;
232 ulong n;
234 if(nl){
235 flnew(nl, gettext, l->user0, (char *)t);
236 flinit(nl, r, f, l->f.cols);
237 nl->origin = l->origin;
238 rp = (*l->textfn)(l, l->f.nchars, &n);
239 flinsert(nl, rp, rp+n, l->origin);
240 flsetselect(nl, l->p0, l->p1);
241 if(close){
242 flclose(l);
243 if(l==which)
244 which = 0;
245 }else
246 t->nwin++;
247 current(nl);
248 hcheck(t->tag);
250 setcursor(mousectl, cursor);
253 void
254 buttons(int updown)
256 while(((mousep->buttons&7)!=0) != updown)
257 getmouse();
260 int
261 getr(Rectangle *rp)
263 Point p;
264 Rectangle r;
266 *rp = getrect(3, mousectl);
267 if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){
268 p = rp->min;
269 r = cmd.l[cmd.front].entire;
270 *rp = screen->r;
271 if(cmd.nwin==1){
272 if (p.y <= r.min.y)
273 rp->max.y = r.min.y;
274 else if (p.y >= r.max.y)
275 rp->min.y = r.max.y;
276 if (p.x <= r.min.x)
277 rp->max.x = r.min.x;
278 else if (p.x >= r.max.x)
279 rp->min.x = r.max.x;
282 return rectclip(rp, screen->r) &&
283 rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
286 void
287 snarf(Text *t, int w)
289 Flayer *l = &t->l[w];
291 if(l->p1>l->p0){
292 snarflen = l->p1-l->p0;
293 outTsll(Tsnarf, t->tag, l->p0, l->p1);
297 void
298 cut(Text *t, int w, int save, int check)
300 long p0, p1;
301 Flayer *l;
303 l = &t->l[w];
304 p0 = l->p0;
305 p1 = l->p1;
306 if(p0 == p1)
307 return;
308 if(p0 < 0)
309 panic("cut");
310 if(save)
311 snarf(t, w);
312 outTsll(Tcut, t->tag, p0, p1);
313 flsetselect(l, p0, p0);
314 t->lock++;
315 hcut(t->tag, p0, p1-p0);
316 if(check)
317 hcheck(t->tag);
320 void
321 paste(Text *t, int w)
323 if(snarflen){
324 cut(t, w, 0, 0);
325 t->lock++;
326 outTsl(Tpaste, t->tag, t->l[w].p0);
330 void
331 scrorigin(Flayer *l, int but, long p0)
333 Text *t=(Text *)l->user1;
335 switch(but){
336 case 1:
337 outTsll(Torigin, t->tag, l->origin, p0);
338 break;
339 case 2:
340 outTsll(Torigin, t->tag, p0, 1L);
341 break;
342 case 3:
343 horigin(t->tag,p0);
347 int
348 alnum(int c)
350 /*
351 * Hard to get absolutely right. Use what we know about ASCII
352 * and assume anything above the Latin control characters is
353 * potentially an alphanumeric.
354 */
355 if(c<=' ')
356 return 0;
357 if(0x7F<=c && c<=0xA0)
358 return 0;
359 if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
360 return 0;
361 return 1;
364 int
365 raspc(Rasp *r, long p)
367 ulong n;
368 rload(r, p, p+1, &n);
369 if(n)
370 return scratch[0];
371 return 0;
374 long
375 ctlw(Rasp *r, long o, long p)
377 int c;
379 if(--p < o)
380 return o;
381 if(raspc(r, p)=='\n')
382 return p;
383 for(; p>=o && !alnum(c=raspc(r, p)); --p)
384 if(c=='\n')
385 return p+1;
386 for(; p>o && alnum(raspc(r, p-1)); --p)
388 return p>=o? p : o;
391 long
392 ctlu(Rasp *r, long o, long p)
394 if(--p < o)
395 return o;
396 if(raspc(r, p)=='\n')
397 return p;
398 for(; p-1>=o && raspc(r, p-1)!='\n'; --p)
400 return p>=o? p : o;
403 int
404 center(Flayer *l, long a)
406 Text *t;
408 t = l->user1;
409 if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
410 if(a > t->rasp.nrunes)
411 a = t->rasp.nrunes;
412 outTsll(Torigin, t->tag, a, 2L);
413 return 1;
415 return 0;
418 int
419 thirds(Flayer *l, long a, int n)
421 Text *t;
422 Rectangle s;
423 long lines;
425 t = l->user1;
426 if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
427 if(a > t->rasp.nrunes)
428 a = t->rasp.nrunes;
429 s = insetrect(l->scroll, 1);
430 lines = (n*(s.max.y-s.min.y)/l->f.font->height+1)/3;
431 if (lines < 2)
432 lines = 2;
433 outTsll(Torigin, t->tag, a, lines);
434 return 1;
436 return 0;
439 int
440 onethird(Flayer *l, long a)
442 return thirds(l, a, 1);
445 int
446 twothirds(Flayer *l, long a)
448 return thirds(l, a, 2);
451 void
452 flushtyping(int clearesc)
454 Text *t;
455 ulong n;
457 if(clearesc)
458 typeesc = -1;
459 if(typestart == typeend) {
460 modified = 0;
461 return;
463 t = which->user1;
464 if(t != &cmd)
465 modified = 1;
466 rload(&t->rasp, typestart, typeend, &n);
467 scratch[n] = 0;
468 if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){
469 setlock();
470 outcmd();
472 outTslS(Ttype, t->tag, typestart, scratch);
473 typestart = -1;
474 typeend = -1;
477 #define SCROLLKEY Kdown
478 #define BACKSCROLLKEY Kup
479 #define ESC 0x1B
480 #define HOMEKEY Khome
481 #define ENDKEY Kend
482 #define PAGEUP Kpgup
483 #define PAGEDOWN Kpgdown
484 #define LEFTARROW Kleft
485 #define RIGHTARROW Kright
487 int
488 nontypingkey(int c)
490 return c==SCROLLKEY || c==BACKSCROLLKEY
491 || c==LEFTARROW || c==RIGHTARROW
492 || c==HOMEKEY || c==ENDKEY
493 || c==PAGEUP || c==PAGEDOWN;
496 void
497 type(Flayer *l, int res) /* what a bloody mess this is */
499 Text *t = (Text *)l->user1;
500 Rune buf[100];
501 Rune *p = buf;
502 int c, backspacing;
503 long a, a0;
504 int scrollkey;
506 scrollkey = 0;
507 if(res == RKeyboard)
508 scrollkey = nontypingkey(qpeekc()); /* ICK */
510 if(hostlock || t->lock){
511 kbdblock();
512 return;
514 a = l->p0;
515 if(a!=l->p1 && !scrollkey){
516 flushtyping(1);
517 cut(t, t->front, 1, 1);
518 return; /* it may now be locked */
520 backspacing = 0;
521 while((c = kbdchar())>0){
522 if(res == RKeyboard){
523 if(nontypingkey(c) || c==ESC)
524 break;
525 /* backspace, ctrl-u, ctrl-w, del */
526 if(c=='\b' || c==0x15 || c==0x17 || c==0x7F){
527 backspacing = 1;
528 break;
531 *p++ = c;
532 if(autoindent)
533 if(c == '\n'){
534 /* autoindent */
535 int cursor, ch;
536 cursor = ctlu(&t->rasp, 0, a+(p-buf)-1);
537 while(p < buf+nelem(buf)){
538 ch = raspc(&t->rasp, cursor++);
539 if(ch == ' ' || ch == '\t')
540 *p++ = ch;
541 else
542 break;
545 if(c == '\n' || p >= buf+nelem(buf))
546 break;
548 if(p > buf){
549 if(typestart < 0)
550 typestart = a;
551 if(typeesc < 0)
552 typeesc = a;
553 hgrow(t->tag, a, p-buf, 0);
554 t->lock++; /* pretend we Trequest'ed for hdatarune*/
555 hdatarune(t->tag, a, buf, p-buf);
556 a += p-buf;
557 l->p0 = a;
558 l->p1 = a;
559 typeend = a;
560 if(c=='\n' || typeend-typestart>100)
561 flushtyping(0);
562 onethird(l, a);
564 if(c==SCROLLKEY || c==PAGEDOWN){
565 flushtyping(0);
566 center(l, l->origin+l->f.nchars+1);
567 }else if(c==BACKSCROLLKEY || c==PAGEUP){
568 flushtyping(0);
569 a0 = l->origin-l->f.nchars;
570 if(a0 < 0)
571 a0 = 0;
572 center(l, a0);
573 }else if(c == RIGHTARROW){
574 flushtyping(0);
575 a0 = l->p0;
576 if(a0 < t->rasp.nrunes)
577 a0++;
578 flsetselect(l, a0, a0);
579 center(l, a0);
580 }else if(c == LEFTARROW){
581 flushtyping(0);
582 a0 = l->p0;
583 if(a0 > 0)
584 a0--;
585 flsetselect(l, a0, a0);
586 center(l, a0);
587 }else if(c == HOMEKEY){
588 flushtyping(0);
589 center(l, 0);
590 }else if(c == ENDKEY){
591 flushtyping(0);
592 center(l, t->rasp.nrunes);
593 }else if(backspacing && !hostlock){
594 /* backspacing immediately after outcmd(): sorry */
595 if(l->f.p0>0 && a>0){
596 switch(c){
597 case '\b':
598 case 0x7F: /* del */
599 l->p0 = a-1;
600 break;
601 case 0x15: /* ctrl-u */
602 l->p0 = ctlu(&t->rasp, l->origin, a);
603 break;
604 case 0x17: /* ctrl-w */
605 l->p0 = ctlw(&t->rasp, l->origin, a);
606 break;
608 l->p1 = a;
609 if(l->p1 != l->p0){
610 /* cut locally if possible */
611 if(typestart<=l->p0 && l->p1<=typeend){
612 t->lock++; /* to call hcut */
613 hcut(t->tag, l->p0, l->p1-l->p0);
614 /* hcheck is local because we know rasp is contiguous */
615 hcheck(t->tag);
616 }else{
617 flushtyping(0);
618 cut(t, t->front, 0, 1);
621 if(typeesc >= l->p0)
622 typeesc = l->p0;
623 if(typestart >= 0){
624 if(typestart >= l->p0)
625 typestart = l->p0;
626 typeend = l->p0;
627 if(typestart == typeend){
628 typestart = -1;
629 typeend = -1;
630 modified = 0;
634 }else{
635 if(c==ESC && typeesc>=0){
636 l->p0 = typeesc;
637 l->p1 = a;
638 flushtyping(1);
640 for(l=t->l; l<&t->l[NL]; l++)
641 if(l->textfn)
642 flsetselect(l, l->p0, l->p1);
647 void
648 outcmd(void){
649 if(work)
650 outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1);
653 void
654 panic(char *s)
656 panic1(display, s);
659 void
660 panic1(Display *d, char *s)
662 fprint(2, "samterm:panic: ");
663 perror(s);
664 abort();
667 Rune*
668 gettext(Flayer *l, long n, ulong *np)
670 Text *t;
672 t = l->user1;
673 rload(&t->rasp, l->origin, l->origin+n, np);
674 return scratch;
677 long
678 scrtotal(Flayer *l)
680 return ((Text *)l->user1)->rasp.nrunes;
683 void*
684 alloc(ulong n)
686 void *p;
688 p = malloc(n);
689 if(p == 0)
690 panic("alloc");
691 memset(p, 0, n);
692 return p;