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