]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/samterm/main.c
merge
[plan9front.git] / sys / src / cmd / samterm / main.c
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 int     spacesindent;
30
31 void
32 threadmain(int argc, char *argv[])
33 {
34         int i, got, nclick, scr, chord;
35         Text *t;
36         Rectangle r;
37         Flayer *nwhich;
38         ulong p;
39
40         getscreen(argc, argv);
41         iconinit();
42         initio();
43         scratch = alloc(100*RUNESIZE);
44         nscralloc = 100;
45         r = screen->r;
46         r.max.y = r.min.y+Dy(r)/5;
47         flstart(screen->clipr);
48         rinit(&cmd.rasp);
49         flnew(&cmd.l[0], gettext, 1, &cmd);
50         flinit(&cmd.l[0], r, font, cmdcols);
51         cmd.nwin = 1;
52         which = &cmd.l[0];
53         cmd.tag = Untagged;
54         outTs(Tversion, VERSION);
55         startnewfile(Tstartcmdfile, &cmd);
56
57         got = 0;
58         chord = 0;
59         for(;;got = waitforio()){
60                 if(hasunlocked && RESIZED())
61                         resize();
62                 if(got&(1<<RHost))
63                         rcv();
64                 if(got&(1<<RPlumb)){
65                         for(i=0; cmd.l[i].textfn==0; i++)
66                                 ;
67                         current(&cmd.l[i]);
68                         flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes);
69                         type(which, RPlumb);
70                 }
71                 if(got&(1<<RKeyboard))
72                         if(which)
73                                 type(which, RKeyboard);
74                         else
75                                 kbdblock();
76                 if(got&(1<<RMouse)){
77                         if(hostlock==2 || !ptinrect(mousep->xy, screen->r)){
78                                 mouseunblock();
79                                 continue;
80                         }
81                         nwhich = flwhich(mousep->xy);
82                         scr = which && (ptinrect(mousep->xy, which->scroll) ||
83                                 mousep->buttons&(8|16));
84                         if(mousep->buttons)
85                                 flushtyping(1);
86                         if((mousep->buttons&1)==0)
87                                 chord = 0;
88                         if(chord && which && which==nwhich){
89                                 chord |= mousep->buttons;
90                                 t = (Text *)which->user1;
91                                 if(!t->lock){
92                                         int w = which-t->l;
93                                         if(chord&2){
94                                                 cut(t, w, 1, 1);
95                                                 chord &= ~2;
96                                         }
97                                         if(chord&4){
98                                                 paste(t, w);
99                                                 chord &= ~4;
100                                         }
101                                 }
102                         }else if(mousep->buttons&(1|8)){
103                                 if(scr)
104                                         scroll(which, (mousep->buttons&8) ? 4 : 1);
105                                 else if(nwhich && nwhich!=which)
106                                         current(nwhich);
107                                 else{
108                                         t=(Text *)which->user1;
109                                         nclick = flselect(which, &p);
110                                         if(nclick > 0){
111                                                 if(nclick > 1)
112                                                         outTsl(Ttclick, t->tag, p);
113                                                 else
114                                                         outTsl(Tdclick, t->tag, p);
115                                                 t->lock++;
116                                         }else if(t!=&cmd)
117                                                 outcmd();
118                                         if(mousep->buttons&1)
119                                                 chord = mousep->buttons;
120                                 }
121                         }else if((mousep->buttons&2) && which){
122                                 if(scr)
123                                         scroll(which, 2);
124                                 else
125                                         menu2hit();
126                         }else if(mousep->buttons&(4|16)){
127                                 if(scr)
128                                         scroll(which, (mousep->buttons&16) ? 5 : 3);
129                                 else
130                                         menu3hit();
131                         }
132                         mouseunblock();
133                 }
134         }
135 }
136
137
138 void
139 resize(void)
140 {
141         int i;
142
143         flresize(screen->clipr);
144         for(i = 0; i<nname; i++)
145                 if(text[i])
146                         hcheck(text[i]->tag);
147 }
148
149 void
150 current(Flayer *nw)
151 {
152         Text *t;
153
154         if(which)
155                 flborder(which, 0);
156         if(nw){
157                 flushtyping(1);
158                 flupfront(nw);
159                 flborder(nw, 1);
160                 buttons(Up);
161                 t = (Text *)nw->user1;
162                 t->front = nw-&t->l[0];
163                 if(t != &cmd)
164                         work = nw;
165         }
166         which = nw;
167 }
168
169 void
170 closeup(Flayer *l)
171 {
172         Text *t=(Text *)l->user1;
173         int m;
174
175         m = whichmenu(t->tag);
176         if(m < 0)
177                 return;
178         flclose(l);
179         if(l == which){
180                 which = 0;
181                 current(flwhich(Pt(0, 0)));
182         }
183         if(l == work)
184                 work = 0;
185         if(--t->nwin == 0){
186                 rclear(&t->rasp);
187                 free((uchar *)t);
188                 text[m] = 0;
189         }else if(l == &t->l[t->front]){
190                 for(m=0; m<NL; m++)     /* find one; any one will do */
191                         if(t->l[m].textfn){
192                                 t->front = m;
193                                 return;
194                         }
195                 panic("close");
196         }
197 }
198
199 Flayer *
200 findl(Text *t)
201 {
202         int i;
203         for(i = 0; i<NL; i++)
204                 if(t->l[i].textfn==0)
205                         return &t->l[i];
206         return 0;
207 }
208
209 void
210 duplicate(Flayer *l, Rectangle r, Font *f, int close)
211 {
212         Text *t=(Text *)l->user1;
213         Flayer *nl = findl(t);
214         Rune *rp;
215         ulong n;
216
217         if(nl){
218                 flnew(nl, gettext, l->user0, (char *)t);
219                 flinit(nl, r, f, l->f.cols);
220                 nl->origin = l->origin;
221                 rp = (*l->textfn)(l, l->f.nchars, &n);
222                 flinsert(nl, rp, rp+n, l->origin);
223                 flsetselect(nl, l->p0, l->p1);
224                 if(close){
225                         flclose(l);
226                         if(l==which)
227                                 which = 0;
228                 }else
229                         t->nwin++;
230                 current(nl);
231                 hcheck(t->tag);
232         }
233         setcursor(mousectl, cursor);
234 }
235
236 void
237 buttons(int updown)
238 {
239         while(((mousep->buttons&7)!=0) != updown)
240                 getmouse();
241 }
242
243 int
244 getr(Rectangle *rp)
245 {
246         Point p;
247         Rectangle r;
248
249         *rp = getrect(3, mousectl);
250         if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){
251                 p = rp->min;
252                 r = cmd.l[cmd.front].entire;
253                 *rp = screen->r;
254                 if(cmd.nwin==1){
255                         if (p.y <= r.min.y)
256                                 rp->max.y = r.min.y;
257                         else if (p.y >= r.max.y)
258                                 rp->min.y = r.max.y;
259                         if (p.x <= r.min.x)
260                                 rp->max.x = r.min.x;
261                         else if (p.x >= r.max.x)
262                                 rp->min.x = r.max.x;
263                 }
264         }
265         return rectclip(rp, screen->r) &&
266            rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
267 }
268
269 void
270 snarf(Text *t, int w)
271 {
272         Flayer *l = &t->l[w];
273
274         if(l->p1>l->p0){
275                 snarflen = l->p1-l->p0;
276                 outTsll(Tsnarf, t->tag, l->p0, l->p1);
277         }
278 }
279
280 void
281 cut(Text *t, int w, int save, int check)
282 {
283         long p0, p1;
284         Flayer *l;
285
286         l = &t->l[w];
287         p0 = l->p0;
288         p1 = l->p1;
289         if(p0 == p1)
290                 return;
291         if(p0 < 0)
292                 panic("cut");
293         if(save)
294                 snarf(t, w);
295         outTsll(Tcut, t->tag, p0, p1);
296         flsetselect(l, p0, p0);
297         t->lock++;
298         hcut(t->tag, p0, p1-p0);
299         if(check)
300                 hcheck(t->tag);
301 }
302
303 void
304 paste(Text *t, int w)
305 {
306         if(snarflen){
307                 cut(t, w, 0, 0);
308                 t->lock++;
309                 outTsl(Tpaste, t->tag, t->l[w].p0);
310         }
311 }
312
313 void
314 scrorigin(Flayer *l, int but, long p0)
315 {
316         Text *t=(Text *)l->user1;
317
318         if(t->tag == Untagged)
319                 return;
320
321         switch(but){
322         case 1:
323                 outTsll(Torigin, t->tag, l->origin, p0);
324                 break;
325         case 2:
326                 outTsll(Torigin, t->tag, p0, 1L);
327                 break;
328         case 3:
329                 horigin(t->tag,p0);
330         }
331 }
332
333 int
334 alnum(int c)
335 {
336         /*
337          * Hard to get absolutely right.  Use what we know about ASCII
338          * and assume anything above the Latin control characters is
339          * potentially an alphanumeric.
340          */
341         if(c<=' ')
342                 return 0;
343         if(0x7F<=c && c<=0xA0)
344                 return 0;
345         if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
346                 return 0;
347         return 1;
348 }
349
350 int
351 raspc(Rasp *r, long p)
352 {
353         ulong n;
354         rload(r, p, p+1, &n);
355         if(n)
356                 return scratch[0];
357         return 0;
358 }
359
360 int
361 getcol(Rasp *r, long p)
362 {
363         int col;
364
365         for(col = 0; p > 0 && raspc(r, p-1)!='\n'; p--, col++)
366                 ;
367         return col;
368 }
369
370 long
371 del(Rasp *r, long o, long p)
372 {
373         int i, col, n;
374
375         if(--p < o)
376                 return o;
377         if(!spacesindent || raspc(r, p)!=' ')
378                 return p;
379         col = getcol(r, p) + 1;
380         if((n = col % maxtab) == 0)
381                 n = maxtab;
382         for(i = 0; p-1>=o && raspc(r, p-1)==' ' && i<n-1; --p, i++)
383                 ;
384         return p>=o? p : o;
385 }
386
387 long
388 ctlw(Rasp *r, long o, long p)
389 {
390         int c;
391
392         if(--p < o)
393                 return o;
394         if(raspc(r, p)=='\n')
395                 return p;
396         for(; p>=o && !alnum(c=raspc(r, p)); --p)
397                 if(c=='\n')
398                         return p+1;
399         for(; p>o && alnum(raspc(r, p-1)); --p)
400                 ;
401         return p>=o? p : o;
402 }
403
404 long
405 ctlu(Rasp *r, long o, long p)
406 {
407         if(--p < o)
408                 return o;
409         if(raspc(r, p)=='\n')
410                 return p;
411         for(; p-1>=o && raspc(r, p-1)!='\n'; --p)
412                 ;
413         return p>=o? p : o;
414 }
415
416 int
417 center(Flayer *l, long a)
418 {
419         Text *t;
420
421         t = l->user1;
422         if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
423                 if(a > t->rasp.nrunes)
424                         a = t->rasp.nrunes;
425                 outTsll(Torigin, t->tag, a, 2L);
426                 return 1;
427         }
428         return 0;
429 }
430
431 int
432 onethird(Flayer *l, long a)
433 {
434         Text *t;
435         Rectangle s;
436         long lines;
437
438         t = l->user1;
439         if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
440                 if(a > t->rasp.nrunes)
441                         a = t->rasp.nrunes;
442                 s = insetrect(l->scroll, 1);
443                 lines = ((s.max.y-s.min.y)/l->f.font->height+1)/3;
444                 if (lines < 2)
445                         lines = 2;
446                 outTsll(Torigin, t->tag, a, lines);
447                 return 1;
448         }
449         return 0;
450 }
451
452 void
453 flushtyping(int clearesc)
454 {
455         Text *t;
456         ulong n;
457
458         if(clearesc)
459                 typeesc = -1;   
460         if(typestart == typeend) {
461                 modified = 0;
462                 return;
463         }
464         t = which->user1;
465         if(t != &cmd)
466                 modified = 1;
467         rload(&t->rasp, typestart, typeend, &n);
468         scratch[n] = 0;
469         if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){
470                 setlock();
471                 outcmd();
472         }
473         outTslS(Ttype, t->tag, typestart, scratch);
474         typestart = -1;
475         typeend = -1;
476 }
477
478 int
479 nontypingkey(int c)
480 {
481         switch(c){
482         case Kup:
483         case Kdown:
484         case Khome:
485         case Kend:
486         case Kpgdown:
487         case Kpgup:
488         case Kleft:
489         case Kright:
490         case Ksoh:
491         case Kenq:
492         case Kstx:
493         case Kbel:
494                 return 1;
495         }
496         return 0;
497 }
498
499
500 void
501 type(Flayer *l, int res)        /* what a bloody mess this is */
502 {
503         Text *t = (Text *)l->user1;
504         Rune buf[100];
505         Rune *p = buf;
506         int c, backspacing;
507         long a, a0;
508         int scrollkey;
509
510         scrollkey = 0;
511         if(res == RKeyboard)
512                 scrollkey = nontypingkey(qpeekc());     /* ICK */
513
514         if(hostlock || t->lock){
515                 kbdblock();
516                 return;
517         }
518         a = l->p0;
519         if(a!=l->p1 && !scrollkey){
520                 flushtyping(1);
521                 cut(t, t->front, 1, 1);
522                 return; /* it may now be locked */
523         }
524         backspacing = 0;
525         while((c = kbdchar())>0){
526                 if(res == RKeyboard){
527                         if(nontypingkey(c) || c==Kesc)
528                                 break;
529                         /* backspace, ctrl-u, ctrl-w, del */
530                         if(c==Kbs || c==Knack || c==Ketb || c==Kdel){
531                                 backspacing = 1;
532                                 break;
533                         }
534                 }
535                 if(spacesindent && c == '\t'){
536                         int i, col, n;
537                         col = getcol(&t->rasp, a);
538                         n = maxtab - col % maxtab;
539                         for(i = 0; i < n && p < buf+nelem(buf); i++)
540                                 *p++ = ' ';
541                 } else
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;
554                         }
555                 }
556                 if(c == '\n' || p >= buf+sizeof(buf)/sizeof(buf[0]))
557                         break;
558         }
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);
574         }
575         if(c==Kdown || c==Kpgdown){
576                 flushtyping(0);
577                 center(l, l->origin+l->f.nchars+1);
578                 /* backspacing immediately after outcmd(): sorry */
579         }else if(c==Kup || c==Kpgup){
580                 flushtyping(0);
581                 a0 = l->origin-l->f.nchars;
582                 if(a0 < 0)
583                         a0 = 0;
584                 center(l, a0);
585         }else if(c == Kright){
586                 flushtyping(0);
587                 a0 = l->p0;
588                 if(a0 < t->rasp.nrunes)
589                         a0++;
590                 flsetselect(l, a0, a0);
591                 center(l, a0);
592         }else if(c == Kleft){
593                 flushtyping(0);
594                 a0 = l->p0;
595                 if(a0 > 0)
596                         a0--;
597                 flsetselect(l, a0, a0);
598                 center(l, a0);
599         }else if(c == Khome){
600                 flushtyping(0);
601                 center(l, 0);
602         }else if(c == Kend){
603                 flushtyping(0);
604                 center(l, t->rasp.nrunes);
605         }else if(c == Ksoh || c == Kenq){
606                 flushtyping(1);
607                 if(c == Ksoh)
608                         while(a > 0 && raspc(&t->rasp, a-1)!='\n')
609                                 a--;
610                 else
611                         while(a < t->rasp.nrunes && raspc(&t->rasp, a)!='\n')
612                                 a++;
613                 l->p0 = l->p1 = a;
614                 for(l=t->l; l<&t->l[NL]; l++)
615                         if(l->textfn)
616                                 flsetselect(l, l->p0, l->p1);
617         }else if(backspacing && !hostlock){
618                 /* backspacing immediately after outcmd(): sorry */
619                 if(l->f.p0>0 && a>0){
620                         switch(c){
621                         case Kbs:
622                         case Kdel:      /* del */
623                                 l->p0 = del(&t->rasp, l->origin, a);
624                                 break;
625                         case Knack:     /* ctrl-u */
626                                 l->p0 = ctlu(&t->rasp, l->origin, a);
627                                 break;
628                         case Ketb:      /* ctrl-w */
629                                 l->p0 = ctlw(&t->rasp, l->origin, a);
630                                 break;
631                         }
632                         l->p1 = a;
633                         if(l->p1 != l->p0){
634                                 /* cut locally if possible */
635                                 if(typestart<=l->p0 && l->p1<=typeend){
636                                         t->lock++;      /* to call hcut */
637                                         hcut(t->tag, l->p0, l->p1-l->p0);
638                                         /* hcheck is local because we know rasp is contiguous */
639                                         hcheck(t->tag);
640                                 }else{
641                                         flushtyping(0);
642                                         cut(t, t->front, 0, 1);
643                                 }
644                         }
645                         if(typeesc >= l->p0)
646                                 typeesc = l->p0;
647                         if(typestart >= 0){
648                                 if(typestart >= l->p0)
649                                         typestart = l->p0;
650                                 typeend = l->p0;
651                                 if(typestart == typeend){
652                                         typestart = -1;
653                                         typeend = -1;
654                                         modified = 0;
655                                 }
656                         }
657                 }
658         }else if(c == Kstx){
659                 t = &cmd;
660                 for(l=t->l; l->textfn==0; l++)
661                         ;
662                 current(l);
663                 flushtyping(0);
664                 a = t->rasp.nrunes;
665                 flsetselect(l, a, a);
666                 center(l, a);
667         }else if(c == Kbel){
668                 int i;
669                 if(work == nil)
670                         return;
671                 if(which != work){
672                         current(work);
673                         return;
674                 }
675                 t = (Text*)work->user1;
676                 l = &t->l[t->front];
677                 for(i=t->front; t->nwin>1 && (i = (i+1)%NL) != t->front; )
678                         if(t->l[i].textfn != 0){
679                                 l = &t->l[i];
680                                 break;
681                         }
682                 current(l);
683         }else{
684                 if(c==Kesc && typeesc>=0){
685                         l->p0 = typeesc;
686                         l->p1 = a;
687                         flushtyping(1);
688                 }
689                 for(l=t->l; l<&t->l[NL]; l++)
690                         if(l->textfn)
691                                 flsetselect(l, l->p0, l->p1);
692         }
693 }
694
695
696 void
697 outcmd(void){
698         if(work)
699                 outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1);
700 }
701
702 void
703 panic(char *s)
704 {
705         panic1(display, s);
706 }
707
708 void
709 panic1(Display*, char *s)
710 {
711         fprint(2, "samterm:panic: ");
712         perror(s);
713         abort();
714 }
715
716 Rune*
717 gettext(Flayer *l, long n, ulong *np)
718 {
719         Text *t;
720
721         t = l->user1;
722         rload(&t->rasp, l->origin, l->origin+n, np);
723         return scratch;
724 }
725
726 long
727 scrtotal(Flayer *l)
728 {
729         return ((Text *)l->user1)->rasp.nrunes;
730 }
731
732 void*
733 alloc(ulong n)
734 {
735         void *p;
736
737         p = malloc(n);
738         if(p == 0)
739                 panic("alloc");
740         memset(p, 0, n);
741         return p;
742 }