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;
27 void
28 threadmain(int argc, char *argv[])
29 {
30 int i, got, scr;
31 Text *t;
32 Rectangle r;
33 Flayer *nwhich;
35 /*
36 * sam is talking to us on fd 0 and 1.
37 * move these elsewhere so that if we accidentally
38 * use 0 and 1 in other code, nothing bad happens.
39 */
40 dup(0, 3);
41 dup(1, 4);
42 hostfd[0] = 3;
43 hostfd[1] = 4;
44 close(0);
45 close(1);
46 open("/dev/null", OREAD);
47 if(open("/dev/tty", OWRITE) < 0)
48 open("/dev/null", OWRITE);
49 dup(2, 1);
51 if(protodebug) print("getscreen\n");
52 getscreen(argc, argv);
53 if(protodebug) print("iconinit\n");
54 iconinit();
55 if(protodebug) print("initio\n");
56 initio();
57 if(protodebug) print("scratch\n");
58 scratch = alloc(100*RUNESIZE);
59 nscralloc = 100;
60 r = screen->r;
61 r.max.y = r.min.y+Dy(r)/5;
62 if(protodebug) print("flstart\n");
63 flstart(screen->clipr);
64 rinit(&cmd.rasp);
65 flnew(&cmd.l[0], gettext, 1, &cmd);
66 flinit(&cmd.l[0], r, font, cmdcols);
67 cmd.nwin = 1;
68 which = &cmd.l[0];
69 cmd.tag = Untagged;
70 outTs(Tversion, VERSION);
71 startnewfile(Tstartcmdfile, &cmd);
73 got = 0;
74 if(protodebug) print("loop\n");
75 for(;;got = waitforio()){
76 if(hasunlocked && RESIZED())
77 resize();
78 if(got&(1<<RHost))
79 rcv();
80 if(got&(1<<RPlumb)){
81 for(i=0; cmd.l[i].textfn==0; i++)
82 ;
83 current(&cmd.l[i]);
84 flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes);
85 type(which, RPlumb);
86 }
87 if(got&(1<<RKeyboard))
88 if(which)
89 type(which, RKeyboard);
90 else
91 kbdblock();
92 if(got&(1<<RMouse)){
93 if(hostlock==2 || !ptinrect(mousep->xy, screen->r)){
94 mouseunblock();
95 continue;
96 }
97 nwhich = flwhich(mousep->xy);
98 scr = which && ptinrect(mousep->xy, which->scroll);
99 if(mousep->buttons)
100 flushtyping(1);
101 if(mousep->buttons&1){
102 if(nwhich){
103 if(nwhich!=which)
104 current(nwhich);
105 else if(scr)
106 scroll(which, 1);
107 else{
108 t=(Text *)which->user1;
109 if(flselect(which)){
110 outTsl(Tdclick, t->tag, which->p0);
111 t->lock++;
112 }else if(t!=&cmd)
113 outcmd();
116 }else if((mousep->buttons&2) && which){
117 if(scr)
118 scroll(which, 2);
119 else
120 menu2hit();
121 }else if((mousep->buttons&4)){
122 if(scr)
123 scroll(which, 3);
124 else
125 menu3hit();
127 mouseunblock();
133 void
134 resize(void)
136 int i;
138 flresize(screen->clipr);
139 for(i = 0; i<nname; i++)
140 if(text[i])
141 hcheck(text[i]->tag);
144 void
145 current(Flayer *nw)
147 Text *t;
149 if(which)
150 flborder(which, 0);
151 if(nw){
152 flushtyping(1);
153 flupfront(nw);
154 flborder(nw, 1);
155 buttons(Up);
156 t = (Text *)nw->user1;
157 t->front = nw-&t->l[0];
158 if(t != &cmd)
159 work = nw;
161 which = nw;
164 void
165 closeup(Flayer *l)
167 Text *t=(Text *)l->user1;
168 int m;
170 m = whichmenu(t->tag);
171 if(m < 0)
172 return;
173 flclose(l);
174 if(l == which){
175 which = 0;
176 current(flwhich(Pt(0, 0)));
178 if(l == work)
179 work = 0;
180 if(--t->nwin == 0){
181 rclear(&t->rasp);
182 free((uchar *)t);
183 text[m] = 0;
184 }else if(l == &t->l[t->front]){
185 for(m=0; m<NL; m++) /* find one; any one will do */
186 if(t->l[m].textfn){
187 t->front = m;
188 return;
190 panic("close");
194 Flayer *
195 findl(Text *t)
197 int i;
198 for(i = 0; i<NL; i++)
199 if(t->l[i].textfn==0)
200 return &t->l[i];
201 return 0;
204 void
205 duplicate(Flayer *l, Rectangle r, Font *f, int close)
207 Text *t=(Text *)l->user1;
208 Flayer *nl = findl(t);
209 Rune *rp;
210 ulong n;
212 if(nl){
213 flnew(nl, gettext, l->user0, (char *)t);
214 flinit(nl, r, f, l->f.cols);
215 nl->origin = l->origin;
216 rp = (*l->textfn)(l, l->f.nchars, &n);
217 flinsert(nl, rp, rp+n, l->origin);
218 flsetselect(nl, l->p0, l->p1);
219 if(close){
220 flclose(l);
221 if(l==which)
222 which = 0;
223 }else
224 t->nwin++;
225 current(nl);
226 hcheck(t->tag);
228 setcursor(mousectl, cursor);
231 void
232 buttons(int updown)
234 while(((mousep->buttons&7)!=0) != updown)
235 getmouse();
238 int
239 getr(Rectangle *rp)
241 Point p;
242 Rectangle r;
244 *rp = getrect(3, mousectl);
245 if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){
246 p = rp->min;
247 r = cmd.l[cmd.front].entire;
248 *rp = screen->r;
249 if(cmd.nwin==1){
250 if (p.y <= r.min.y)
251 rp->max.y = r.min.y;
252 else if (p.y >= r.max.y)
253 rp->min.y = r.max.y;
254 if (p.x <= r.min.x)
255 rp->max.x = r.min.x;
256 else if (p.x >= r.max.x)
257 rp->min.x = r.max.x;
260 return rectclip(rp, screen->r) &&
261 rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
264 void
265 snarf(Text *t, int w)
267 Flayer *l = &t->l[w];
269 if(l->p1>l->p0){
270 snarflen = l->p1-l->p0;
271 outTsll(Tsnarf, t->tag, l->p0, l->p1);
275 void
276 cut(Text *t, int w, int save, int check)
278 long p0, p1;
279 Flayer *l;
281 l = &t->l[w];
282 p0 = l->p0;
283 p1 = l->p1;
284 if(p0 == p1)
285 return;
286 if(p0 < 0)
287 panic("cut");
288 if(save)
289 snarf(t, w);
290 outTsll(Tcut, t->tag, p0, p1);
291 flsetselect(l, p0, p0);
292 t->lock++;
293 hcut(t->tag, p0, p1-p0);
294 if(check)
295 hcheck(t->tag);
298 void
299 paste(Text *t, int w)
301 if(snarflen){
302 cut(t, w, 0, 0);
303 t->lock++;
304 outTsl(Tpaste, t->tag, t->l[w].p0);
308 void
309 scrorigin(Flayer *l, int but, long p0)
311 Text *t=(Text *)l->user1;
313 switch(but){
314 case 1:
315 outTsll(Torigin, t->tag, l->origin, p0);
316 break;
317 case 2:
318 outTsll(Torigin, t->tag, p0, 1L);
319 break;
320 case 3:
321 horigin(t->tag,p0);
325 int
326 alnum(int c)
328 /*
329 * Hard to get absolutely right. Use what we know about ASCII
330 * and assume anything above the Latin control characters is
331 * potentially an alphanumeric.
332 */
333 if(c<=' ')
334 return 0;
335 if(0x7F<=c && c<=0xA0)
336 return 0;
337 if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
338 return 0;
339 return 1;
342 int
343 raspc(Rasp *r, long p)
345 ulong n;
346 rload(r, p, p+1, &n);
347 if(n)
348 return scratch[0];
349 return 0;
352 long
353 ctlw(Rasp *r, long o, long p)
355 int c;
357 if(--p < o)
358 return o;
359 if(raspc(r, p)=='\n')
360 return p;
361 for(; p>=o && !alnum(c=raspc(r, p)); --p)
362 if(c=='\n')
363 return p+1;
364 for(; p>o && alnum(raspc(r, p-1)); --p)
366 return p>=o? p : o;
369 long
370 ctlu(Rasp *r, long o, long p)
372 if(--p < o)
373 return o;
374 if(raspc(r, p)=='\n')
375 return p;
376 for(; p-1>=o && raspc(r, p-1)!='\n'; --p)
378 return p>=o? p : o;
381 int
382 center(Flayer *l, long a)
384 Text *t;
386 t = l->user1;
387 if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
388 if(a > t->rasp.nrunes)
389 a = t->rasp.nrunes;
390 outTsll(Torigin, t->tag, a, 2L);
391 return 1;
393 return 0;
396 int
397 onethird(Flayer *l, long a)
399 Text *t;
400 Rectangle s;
401 long lines;
403 t = l->user1;
404 if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
405 if(a > t->rasp.nrunes)
406 a = t->rasp.nrunes;
407 s = insetrect(l->scroll, 1);
408 lines = ((s.max.y-s.min.y)/l->f.font->height+1)/3;
409 if (lines < 2)
410 lines = 2;
411 outTsll(Torigin, t->tag, a, lines);
412 return 1;
414 return 0;
417 void
418 flushtyping(int clearesc)
420 Text *t;
421 ulong n;
423 if(clearesc)
424 typeesc = -1;
425 if(typestart == typeend) {
426 modified = 0;
427 return;
429 t = which->user1;
430 if(t != &cmd)
431 modified = 1;
432 rload(&t->rasp, typestart, typeend, &n);
433 scratch[n] = 0;
434 if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){
435 setlock();
436 outcmd();
438 outTslS(Ttype, t->tag, typestart, scratch);
439 typestart = -1;
440 typeend = -1;
443 #define SCROLLKEY Kdown
444 #define BACKSCROLLKEY Kup
445 #define ESC 0x1B
446 #define HOMEKEY Khome
447 #define ENDKEY Kend
448 #define PAGEUP Kpgup
449 #define PAGEDOWN Kpgdown
450 #define LEFTARROW Kleft
451 #define RIGHTARROW Kright
453 int
454 nontypingkey(int c)
456 return c==SCROLLKEY || c==BACKSCROLLKEY
457 || c==LEFTARROW || c==RIGHTARROW
458 || c==HOMEKEY || c==ENDKEY
459 || c==PAGEUP || c==PAGEDOWN;
462 void
463 type(Flayer *l, int res) /* what a bloody mess this is */
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;
472 scrollkey = 0;
473 if(res == RKeyboard)
474 scrollkey = nontypingkey(qpeekc()); /* ICK */
476 if(hostlock || t->lock){
477 kbdblock();
478 return;
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 */
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;
497 *p++ = c;
498 if(c == '\n' || p >= buf+sizeof(buf)/sizeof(buf[0]))
499 break;
501 if(p > buf){
502 if(typestart < 0)
503 typestart = a;
504 if(typeesc < 0)
505 typeesc = a;
506 hgrow(t->tag, a, p-buf, 0);
507 t->lock++; /* pretend we Trequest'ed for hdatarune*/
508 hdatarune(t->tag, a, buf, p-buf);
509 a += p-buf;
510 l->p0 = a;
511 l->p1 = a;
512 typeend = a;
513 if(c=='\n' || typeend-typestart>100)
514 flushtyping(0);
515 onethird(l, a);
517 if(c==SCROLLKEY || c==PAGEDOWN){
518 flushtyping(0);
519 center(l, l->origin+l->f.nchars+1);
520 }else if(c==BACKSCROLLKEY || c==PAGEUP){
521 flushtyping(0);
522 a0 = l->origin-l->f.nchars;
523 if(a0 < 0)
524 a0 = 0;
525 center(l, a0);
526 }else if(c == RIGHTARROW){
527 flushtyping(0);
528 a0 = l->p0;
529 if(a0 < t->rasp.nrunes)
530 a0++;
531 flsetselect(l, a0, a0);
532 center(l, a0);
533 }else if(c == LEFTARROW){
534 flushtyping(0);
535 a0 = l->p0;
536 if(a0 > 0)
537 a0--;
538 flsetselect(l, a0, a0);
539 center(l, a0);
540 }else if(c == HOMEKEY){
541 flushtyping(0);
542 center(l, 0);
543 }else if(c == ENDKEY){
544 flushtyping(0);
545 center(l, t->rasp.nrunes);
546 }else if(backspacing && !hostlock){
547 /* backspacing immediately after outcmd(): sorry */
548 if(l->f.p0>0 && a>0){
549 switch(c){
550 case '\b':
551 case 0x7F: /* del */
552 l->p0 = a-1;
553 break;
554 case 0x15: /* ctrl-u */
555 l->p0 = ctlu(&t->rasp, l->origin, a);
556 break;
557 case 0x17: /* ctrl-w */
558 l->p0 = ctlw(&t->rasp, l->origin, a);
559 break;
561 l->p1 = a;
562 if(l->p1 != l->p0){
563 /* cut locally if possible */
564 if(typestart<=l->p0 && l->p1<=typeend){
565 t->lock++; /* to call hcut */
566 hcut(t->tag, l->p0, l->p1-l->p0);
567 /* hcheck is local because we know rasp is contiguous */
568 hcheck(t->tag);
569 }else{
570 flushtyping(0);
571 cut(t, t->front, 0, 1);
574 if(typeesc >= l->p0)
575 typeesc = l->p0;
576 if(typestart >= 0){
577 if(typestart >= l->p0)
578 typestart = l->p0;
579 typeend = l->p0;
580 if(typestart == typeend){
581 typestart = -1;
582 typeend = -1;
583 modified = 0;
587 }else{
588 if(c==ESC && typeesc>=0){
589 l->p0 = typeesc;
590 l->p1 = a;
591 flushtyping(1);
593 for(l=t->l; l<&t->l[NL]; l++)
594 if(l->textfn)
595 flsetselect(l, l->p0, l->p1);
600 void
601 outcmd(void){
602 if(work)
603 outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1);
606 void
607 panic(char *s)
609 panic1(display, s);
612 void
613 panic1(Display *d, char *s)
615 fprint(2, "samterm:panic: ");
616 perror(s);
617 abort();
620 Rune*
621 gettext(Flayer *l, long n, ulong *np)
623 Text *t;
625 t = l->user1;
626 rload(&t->rasp, l->origin, l->origin+n, np);
627 return scratch;
630 long
631 scrtotal(Flayer *l)
633 return ((Text *)l->user1)->rasp.nrunes;
636 void*
637 alloc(ulong n)
639 void *p;
641 p = malloc(n);
642 if(p == 0)
643 panic("alloc");
644 memset(p, 0, n);
645 return p;