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