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