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