Xinqi Bao's Git

added sigchld handler, cleaned error checking.
[st.git] / st.c
1 /* See LICENSE for licence details. */
2 #include "st.h"
3
4 /* Globals */
5 DC dc;
6 XWindow xw;
7 Term term;
8 Escseq escseq;
9 int cmdfd;
10 pid_t pid;
11 int running;
12
13 void
14 die(const char *errstr, ...) {
15 va_list ap;
16
17 va_start(ap, errstr);
18 vfprintf(stderr, errstr, ap);
19 va_end(ap);
20 exit(EXIT_FAILURE);
21 }
22
23 void
24 execsh(void) {
25 char *args[3] = {SHELL, "-i", NULL};
26 putenv("TERM=" TNAME);
27 execvp(SHELL, args);
28 }
29
30 void
31 xbell(void) { /* visual bell */
32 XRectangle r = { 0, 0, xw.w, xw.h };
33 XSetForeground(xw.dis, dc.gc, dc.col[BellCol]);
34 XFillRectangles(xw.dis, xw.win, dc.gc, &r, 1);
35 /* usleep(30000); */
36 draw(SCredraw);
37 }
38
39 void
40 sigchld(int a) {
41 int stat = 0;
42 if(waitpid(pid, &stat, 0) < 0)
43 die("Waiting for pid %hd failed: %s\n", pid, SERRNO);
44 if(WIFEXITED(stat))
45 exit(WEXITSTATUS(stat));
46 else
47 exit(EXIT_FAILURE);
48 }
49
50
51 void
52 ttynew(void) {
53 int m, s;
54 char *pts;
55
56 if((m = posix_openpt(O_RDWR | O_NOCTTY)) < 0)
57 die("openpt failed: %s\n", SERRNO);
58 if(grantpt(m) < 0)
59 die("grandpt failed: %s\n", SERRNO);
60 if(unlockpt(m) < 0)
61 die("unlockpt failed: %s\n", SERRNO);
62 if(!(pts = ptsname(m)))
63 die("ptsname failed: %s\n", SERRNO);
64 if((s = open(pts, O_RDWR | O_NOCTTY)) < 0)
65 die("Couldn't open slave: %s\n", SERRNO);
66 fcntl(s, F_SETFL, O_NDELAY);
67 switch(pid = fork()) {
68 case -1:
69 die("fork failed\n");
70 break;
71 case 0:
72 setsid(); /* create a new process group */
73 dup2(s, STDIN_FILENO);
74 dup2(s, STDOUT_FILENO);
75 dup2(s, STDERR_FILENO);
76 if(ioctl(s, TIOCSCTTY, NULL) < 0)
77 die("ioctl TTIOCSTTY failed: %s\n", SERRNO);
78 execsh();
79 break;
80 default:
81 close(s);
82 cmdfd = m;
83 signal(SIGCHLD, sigchld);
84 }
85 }
86
87 void
88 dump(char c) {
89 static int col;
90 fprintf(stderr, " %02x %c ", c, isprint(c)?c:'.');
91 if(++col % 10 == 0)
92 fprintf(stderr, "\n");
93 }
94
95 void
96 ttyread(void) {
97 char buf[BUFSIZ] = {0};
98 int ret;
99
100 switch(ret = read(cmdfd, buf, BUFSIZ)) {
101 case -1:
102 die("Couldn't read from shell: %s\n", SERRNO);
103 break;
104 default:
105 tputs(buf, ret);
106 }
107 }
108
109 void
110 ttywrite(char *s, size_t n) {
111 if(write(cmdfd, s, n) == -1)
112 die("write error on tty: %s\n", SERRNO);
113 }
114
115 void
116 ttyresize(int x, int y) {
117 struct winsize w;
118
119 w.ws_row = term.row;
120 w.ws_col = term.col;
121 w.ws_xpixel = w.ws_ypixel = 0;
122 if(ioctl(cmdfd, TIOCSWINSZ, &w) < 0)
123 fprintf(stderr, "Couldn't set window size: %s\n", SERRNO);
124 }
125
126 int
127 escfinal(char c) {
128 if(escseq.len == 1)
129 switch(c) {
130 case '[':
131 case ']':
132 case '(':
133 return 0;
134 case '=':
135 case '>':
136 default:
137 return 1;
138 }
139 else if(BETWEEN(c, 0x40, 0x7E))
140 return 1;
141 return 0;
142 }
143
144 void
145 tcpos(int mode) {
146 static int x = 0;
147 static int y = 0;
148
149 if(mode == CSsave)
150 x = term.c.x, y = term.c.y;
151 else if(mode == CSload)
152 tmoveto(x, y);
153 }
154
155 void
156 tnew(int col, int row) { /* screen size */
157 term.row = row, term.col = col;
158 term.top = 0, term.bot = term.row - 1;
159 /* mode */
160 term.mode = TMwrap;
161 /* cursor */
162 term.c.attr.mode = ATnone;
163 term.c.attr.fg = DefaultFG;
164 term.c.attr.bg = DefaultBG;
165 term.c.x = term.c.y = 0;
166 term.c.hidden = 0;
167 /* allocate screen */
168 term.line = calloc(term.row, sizeof(Line));
169 for(row = 0 ; row < term.row; row++)
170 term.line[row] = calloc(term.col, sizeof(Glyph));
171 }
172
173 void
174 tscroll(void) {
175 Line temp = term.line[term.top];
176 int i;
177
178 for(i = term.top; i < term.bot; i++)
179 term.line[i] = term.line[i+1];
180 memset(temp, 0, sizeof(Glyph) * term.col);
181 term.line[term.bot] = temp;
182 xscroll();
183 }
184
185 void
186 tnewline(void) {
187 int y = term.c.y + 1;
188
189 if(y > term.bot) {
190 tscroll(), y = term.bot;
191 }
192 tmoveto(0, y);
193 }
194
195 int
196 escaddc(char c) {
197 escseq.buf[escseq.len++] = c;
198 if(escfinal(c) || escseq.len >= ESCSIZ) {
199 escparse(), eschandle();
200 return 0;
201 }
202 return 1;
203 }
204
205 void
206 escparse(void) {
207 /* int noarg = 1; */
208 char *p = escseq.buf;
209
210 escseq.narg = 0;
211 switch(escseq.pre = *p++) {
212 case '[': /* CSI */
213 if(*p == '?')
214 escseq.priv = 1, p++;
215
216 while(p < escseq.buf+escseq.len) {
217 while(isdigit(*p)) {
218 escseq.arg[escseq.narg] *= 10;
219 escseq.arg[escseq.narg] += *(p++) - '0'/*, noarg = 0 */;
220 }
221 if(*p == ';')
222 escseq.narg++, p++;
223 else {
224 escseq.mode = *p;
225 escseq.narg++;
226 return;
227 }
228 }
229 break;
230 case '(':
231 /* XXX: graphic character set */
232 break;
233 }
234 }
235
236 void
237 tmoveto(int x, int y) {
238 term.c.x = x < 0 ? 0 : x >= term.col ? term.col-1 : x;
239 term.c.y = y < 0 ? 0 : y >= term.row ? term.row-1 : y;
240 }
241
242 void
243 tcursor(int dir) {
244 int xf = term.c.x, yf = term.c.y;
245
246 switch(dir) {
247 case CSup:
248 yf--;
249 break;
250 case CSdown:
251 yf++;
252 break;
253 case CSleft:
254 xf--;
255 if(xf < 0) {
256 xf = term.col-1, yf--;
257 if(yf < term.top)
258 yf = term.top, xf = 0;
259 }
260 break;
261 case CSright:
262 xf++;
263 if(xf >= term.col) {
264 xf = 0, yf++;
265 if(yf > term.bot)
266 yf = term.bot, tscroll();
267 }
268 break;
269 }
270 tmoveto(xf, yf);
271 }
272
273 void
274 tsetchar(char c) {
275 term.line[term.c.y][term.c.x] = term.c.attr;
276 term.line[term.c.y][term.c.x].c = c;
277 term.line[term.c.y][term.c.x].state |= CRset | CRupdate;
278 }
279
280 void
281 tclearregion(int x1, int y1, int x2, int y2) {
282 int x, y;
283
284 LIMIT(x1, 0, term.col-1);
285 LIMIT(x2, 0, term.col-1);
286 LIMIT(y1, 0, term.row-1);
287 LIMIT(y2, 0, term.row-1);
288
289 /* XXX: could be optimized */
290 for(x = x1; x <= x2; x++)
291 for(y = y1; y <= y2; y++)
292 memset(&term.line[y][x], 0, sizeof(Glyph));
293
294 xclear(x1, y1, x2, y2);
295 }
296
297 void
298 tdeletechar(int n) {
299 int src = term.c.x + n;
300 int dst = term.c.x;
301 int size = term.col - src;
302
303 if(src >= term.col) {
304 tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
305 return;
306 }
307 memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], size * sizeof(Glyph));
308 tclearregion(term.col-size, term.c.y, term.col-1, term.c.y);
309 }
310
311 void
312 tinsertblank(int n) {
313 int src = term.c.x;
314 int dst = src + n;
315 int size = term.col - n - src;
316
317 if(dst >= term.col) {
318 tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
319 return;
320 }
321 memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], size * sizeof(Glyph));
322 tclearregion(src, term.c.y, dst, term.c.y);
323 }
324
325 void
326 tinsertblankline (int n) {
327 int i;
328 Line blank;
329 int bot = term.bot;
330
331 if(term.c.y > term.bot)
332 bot = term.row - 1;
333 else if(term.c.y < term.top)
334 bot = term.top - 1;
335 if(term.c.y + n >= bot) {
336 tclearregion(0, term.c.y, term.col-1, bot);
337 return;
338 }
339 for(i = bot; i >= term.c.y+n; i--) {
340 /* swap deleted line <-> blanked line */
341 blank = term.line[i];
342 term.line[i] = term.line[i-n];
343 term.line[i-n] = blank;
344 /* blank it */
345 memset(blank, 0, term.col * sizeof(Glyph));
346 }
347 }
348
349
350 void
351 tdeleteline(int n) {
352 int i;
353 Line blank;
354 int bot = term.bot;
355
356 if(term.c.y > term.bot)
357 bot = term.row - 1;
358 else if(term.c.y < term.top)
359 bot = term.top - 1;
360 if(term.c.y + n >= bot) {
361 tclearregion(0, term.c.y, term.col-1, bot);
362 return;
363 }
364 for(i = term.c.y; i <= bot-n; i++) {
365 /* swap deleted line <-> blanked line */
366 blank = term.line[i];
367 term.line[i] = term.line[i+n];
368 term.line[i+n] = blank;
369 /* blank it */
370 memset(blank, 0, term.col * sizeof(Glyph));
371 }
372 }
373
374 void
375 tsetattr(int *attr, int l) {
376 int i;
377
378 for(i = 0; i < l; i++) {
379 switch(attr[i]) {
380 case 0:
381 memset(&term.c.attr, 0, sizeof(term.c.attr));
382 term.c.attr.fg = DefaultFG;
383 term.c.attr.bg = DefaultBG;
384 break;
385 case 1:
386 term.c.attr.mode |= ATbold;
387 break;
388 case 4:
389 term.c.attr.mode |= ATunderline;
390 break;
391 case 7:
392 term.c.attr.mode |= ATreverse;
393 break;
394 case 8:
395 term.c.hidden = CShide;
396 break;
397 case 22:
398 term.c.attr.mode &= ~ATbold;
399 break;
400 case 24:
401 term.c.attr.mode &= ~ATunderline;
402 break;
403 case 27:
404 term.c.attr.mode &= ~ATreverse;
405 break;
406 case 39:
407 term.c.attr.fg = DefaultFG;
408 break;
409 case 49:
410 term.c.attr.fg = DefaultBG;
411 break;
412 default:
413 if(BETWEEN(attr[i], 30, 37))
414 term.c.attr.fg = attr[i] - 30;
415 else if(BETWEEN(attr[i], 40, 47))
416 term.c.attr.bg = attr[i] - 40;
417 break;
418 }
419 }
420 }
421
422 void
423 tsetscroll(int t, int b) {
424 int temp;
425
426 LIMIT(t, 0, term.row-1);
427 LIMIT(b, 0, term.row-1);
428 if(t > b) {
429 temp = t;
430 t = b;
431 b = temp;
432 }
433 term.top = t;
434 term.bot = b;
435 }
436
437
438 void
439 eschandle(void) {
440 /* escdump(); */
441 switch(escseq.pre) {
442 case '[':
443 switch(escseq.mode) {
444 case '@': /* Insert <n> blank char */
445 DEFAULT(escseq.arg[0], 1);
446 tinsertblank(escseq.arg[0]);
447 break;
448 case 'A': /* Cursor <n> Up */
449 case 'e':
450 DEFAULT(escseq.arg[0], 1);
451 tmoveto(term.c.x, term.c.y-escseq.arg[0]);
452 break;
453 case 'B': /* Cursor <n> Down */
454 DEFAULT(escseq.arg[0], 1);
455 tmoveto(term.c.x, term.c.y+escseq.arg[0]);
456 break;
457 case 'C': /* Cursor <n> Forward */
458 case 'a':
459 DEFAULT(escseq.arg[0], 1);
460 tmoveto(term.c.x+escseq.arg[0], term.c.y);
461 break;
462 case 'D': /* Cursor <n> Backward */
463 DEFAULT(escseq.arg[0], 1);
464 tmoveto(term.c.x-escseq.arg[0], term.c.y);
465 break;
466 case 'E': /* Cursor <n> Down and first col */
467 DEFAULT(escseq.arg[0], 1);
468 tmoveto(0, term.c.y+escseq.arg[0]);
469 break;
470 case 'F': /* Cursor <n> Up and first col */
471 DEFAULT(escseq.arg[0], 1);
472 tmoveto(0, term.c.y-escseq.arg[0]);
473 break;
474 case 'G': /* Move to <col> */
475 case '`':
476 DEFAULT(escseq.arg[0], 1);
477 tmoveto(escseq.arg[0]-1, term.c.y);
478 break;
479 case 'H': /* Move to <row> <col> */
480 case 'f':
481 DEFAULT(escseq.arg[0], 1);
482 DEFAULT(escseq.arg[1], 1);
483 tmoveto(escseq.arg[1]-1, escseq.arg[0]-1);
484 break;
485 case 'J': /* Clear screen */
486 switch(escseq.arg[0]) {
487 case 0: /* below */
488 tclearregion(term.c.x, term.c.y, term.col-1, term.row-1);
489 break;
490 case 1: /* above */
491 tclearregion(0, 0, term.c.x, term.c.y);
492 break;
493 case 2: /* all */
494 tclearregion(0, 0, term.col-1, term.row-1);
495 break;
496 }
497 break;
498 case 'K': /* Clear line */
499 switch(escseq.arg[0]) {
500 case 0: /* right */
501 tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
502 break;
503 case 1: /* left */
504 tclearregion(0, term.c.y, term.c.x, term.c.y);
505 break;
506 case 2: /* all */
507 tclearregion(0, term.c.y, term.col-1, term.c.y);
508 break;
509 }
510 break;
511 case 'L': /* Insert <n> blank lines */
512 DEFAULT(escseq.arg[0], 1);
513 tinsertblankline(escseq.arg[0]);
514 break;
515 case 'l':
516 if(escseq.priv && escseq.arg[0] == 25)
517 term.c.hidden = 1;
518 break;
519 case 'M': /* Delete <n> lines */
520 DEFAULT(escseq.arg[0], 1);
521 tdeleteline(escseq.arg[0]);
522 break;
523 case 'P': /* Delete <n> char */
524 DEFAULT(escseq.arg[0], 1);
525 tdeletechar(escseq.arg[0]);
526 break;
527 case 'd': /* Move to <row> */
528 DEFAULT(escseq.arg[0], 1);
529 tmoveto(term.c.x, escseq.arg[0]-1);
530 break;
531 case 'h': /* Set terminal mode */
532 if(escseq.priv && escseq.arg[0] == 25)
533 term.c.hidden = 0;
534 break;
535 case 'm': /* Terminal attribute (color) */
536 tsetattr(escseq.arg, escseq.narg);
537 break;
538 case 'r':
539 if(escseq.priv)
540 ;
541 else {
542 DEFAULT(escseq.arg[0], 1);
543 DEFAULT(escseq.arg[1], term.row);
544 tsetscroll(escseq.arg[0]-1, escseq.arg[1]-1);
545 }
546 break;
547 case 's': /* Save cursor position */
548 tcpos(CSsave);
549 break;
550 case 'u': /* Load cursor position */
551 tcpos(CSload);
552 break;
553 }
554 break;
555 }
556 }
557
558 void
559 escdump(void) {
560 int i;
561 puts("------");
562 printf("rawbuf : %s\n", escseq.buf);
563 printf("prechar : %c\n", escseq.pre);
564 printf("private : %c\n", escseq.priv ? '?' : ' ');
565 printf("narg : %d\n", escseq.narg);
566 if(escseq.narg) {
567 for(i = 0; i < escseq.narg; i++)
568 printf("\targ %d = %d\n", i, escseq.arg[i]);
569 }
570 printf("mode : %c\n", escseq.mode);
571 }
572
573 void
574 escreset(void) {
575 memset(&escseq, 0, sizeof(escseq));
576 }
577
578 void
579 tputtab(void) {
580 int space = TAB - term.c.x % TAB;
581
582 if(term.c.x + space >= term.col)
583 space--;
584
585 for(; space > 0; space--)
586 tcursor(CSright);
587 }
588
589 void
590 tputc(char c) {
591 static int inesc = 0;
592 #if 0
593 dump(c);
594 #endif
595 /* start of escseq */
596 if(c == '\033')
597 escreset(), inesc = 1;
598 else if(inesc) {
599 inesc = escaddc(c);
600 } /* normal char */
601 else switch(c) {
602 default:
603 tsetchar(c);
604 tcursor(CSright);
605 break;
606 case '\t':
607 tputtab();
608 break;
609 case '\b':
610 tcursor(CSleft);
611 break;
612 case '\r':
613 tmoveto(0, term.c.y);
614 break;
615 case '\n':
616 tnewline();
617 break;
618 case '\a':
619 xbell();
620 break;
621 }
622 }
623
624 void
625 tputs(char *s, int len) {
626 for(; len > 0; len--)
627 tputc(*s++);
628 }
629
630 void
631 tdump(void) {
632 int row, col;
633 Glyph c;
634
635 for(row = 0; row < term.row; row++) {
636 for(col = 0; col < term.col; col++) {
637 if(col == term.c.x && row == term.c.y)
638 putchar('#');
639 else {
640 c = term.line[row][col];
641 putchar(c.state & CRset ? c.c : '.');
642 }
643 }
644 putchar('\n');
645 }
646 }
647
648 void
649 tresize(int col, int row) {
650 int i;
651 Line *line;
652 int minrow = MIN(row, term.row);
653 int mincol = MIN(col, term.col);
654
655 if(col < 1 || row < 1)
656 return;
657 /* alloc */
658 line = calloc(row, sizeof(Line));
659 for(i = 0 ; i < row; i++)
660 line[i] = calloc(col, sizeof(Glyph));
661 /* copy */
662 for(i = 0 ; i < minrow; i++)
663 memcpy(line[i], term.line[i], mincol * sizeof(Glyph));
664 /* free */
665 for(i = 0; i < term.row; i++)
666 free(term.line[i]);
667 free(term.line);
668
669 LIMIT(term.c.x, 0, col-1);
670 LIMIT(term.c.y, 0, row-1);
671 LIMIT(term.top, 0, row-1);
672 LIMIT(term.bot, 0, row-1);
673
674 term.bot = row-1;
675 term.line = line;
676 term.col = col, term.row = row;
677 }
678
679 unsigned long
680 xgetcol(const char *s) {
681 XColor color;
682 Colormap cmap = DefaultColormap(xw.dis, xw.scr);
683
684 if(!XAllocNamedColor(xw.dis, cmap, s, &color, &color)) {
685 color.pixel = WhitePixel(xw.dis, xw.scr);
686 fprintf(stderr, "Could not allocate color '%s'\n", s);
687 }
688 return color.pixel;
689 }
690
691
692 void
693 xclear(int x1, int y1, int x2, int y2) {
694 XClearArea(xw.dis, xw.win,
695 x1 * xw.cw, y1 * xw.ch,
696 (x2-x1+1) * xw.cw, (y2-y1+1) * xw.ch,
697 False);
698 }
699
700
701 void
702 xscroll(void) {
703 int srcy = (term.top+1) * xw.ch;
704 int dsty = term.top * xw.ch;
705 int height = (term.bot-term.top) * xw.ch;
706
707 xcursor(CShide);
708 XCopyArea(xw.dis, xw.win, xw.win, dc.gc, 0, srcy, xw.w, height, 0, dsty);
709 xclear(0, term.bot, term.col-1, term.bot);
710 }
711
712
713
714
715 void
716 xinit(void) {
717 XGCValues values;
718 unsigned long valuemask;
719 XClassHint chint;
720 XWMHints wmhint;
721 XSizeHints shint;
722 char *args[] = {NULL};
723 int i;
724
725 xw.dis = XOpenDisplay(NULL);
726 xw.scr = XDefaultScreen(xw.dis);
727 if(!xw.dis)
728 die("can not open display");
729
730 /* font */
731 if(!(dc.font = XLoadQueryFont(xw.dis, FONT)))
732 die("can not find font " FONT);
733
734 xw.cw = dc.font->max_bounds.rbearing - dc.font->min_bounds.lbearing;
735 xw.ch = dc.font->ascent + dc.font->descent + LINESPACE;
736
737 /* colors */
738 for(i = 0; i < LEN(colorname); i++)
739 dc.col[i] = xgetcol(colorname[i]);
740
741 term.c.attr.fg = DefaultFG;
742 term.c.attr.bg = DefaultBG;
743 term.c.attr.mode = ATnone;
744 /* windows */
745 xw.h = term.row * xw.ch;
746 xw.w = term.col * xw.cw;
747 /* XXX: this BORDER is useless after the first resize, handle it in xdraws() */
748 xw.win = XCreateSimpleWindow(xw.dis, XRootWindow(xw.dis, xw.scr), 0, 0,
749 xw.w, xw.h, BORDER,
750 dc.col[DefaultBG],
751 dc.col[DefaultBG]);
752 /* gc */
753 values.foreground = XWhitePixel(xw.dis, xw.scr);
754 values.font = dc.font->fid;
755 valuemask = GCForeground | GCFont;
756 dc.gc = XCreateGC(xw.dis, xw.win, valuemask, &values);
757 XMapWindow(xw.dis, xw.win);
758 /* wm stuff */
759 chint.res_name = TNAME, chint.res_class = TNAME;
760 wmhint.input = 1, wmhint.flags = InputHint;
761 shint.height_inc = xw.ch, shint.width_inc = xw.cw;
762 shint.height = xw.h, shint.width = xw.w;
763 shint.flags = PSize | PResizeInc;
764 XSetWMProperties(xw.dis, xw.win, NULL, NULL, &args[0], 0, &shint, &wmhint, &chint);
765 XStoreName(xw.dis, xw.win, TNAME);
766 XSync(xw.dis, 0);
767 }
768
769 void
770 xdrawc(int x, int y, Glyph g) {
771 XRectangle r = { x * xw.cw, y * xw.ch, xw.cw, xw.ch };
772 unsigned long xfg, xbg;
773
774 /* reverse video */
775 if(g.mode & ATreverse)
776 xfg = dc.col[g.bg], xbg = dc.col[g.fg];
777 else
778 xfg = dc.col[g.fg], xbg = dc.col[g.bg];
779 /* background */
780 XSetForeground(xw.dis, dc.gc, xbg);
781 XFillRectangles(xw.dis, xw.win, dc.gc, &r, 1);
782 /* string */
783 XSetForeground(xw.dis, dc.gc, xfg);
784 XDrawString(xw.dis, xw.win, dc.gc, r.x, r.y+dc.font->ascent, &(g.c), 1);
785 if(g.mode & ATbold) /* XXX: bold hack (draw again at x+1) */
786 XDrawString(xw.dis, xw.win, dc.gc, r.x+1, r.y+dc.font->ascent, &(g.c), 1);
787 /* underline */
788 if(g.mode & ATunderline) {
789 r.y += dc.font->ascent + 1;
790 XDrawLine(xw.dis, xw.win, dc.gc, r.x, r.y, r.x+r.width-1, r.y);
791 }
792 }
793
794 void
795 xcursor(int mode) {
796 static int oldx = 0;
797 static int oldy = 0;
798 Glyph g = {' ', ATnone, DefaultBG, DefaultCS, 0};
799
800 LIMIT(oldx, 0, term.col-1);
801 LIMIT(oldy, 0, term.row-1);
802
803 if(term.line[term.c.y][term.c.x].state & CRset)
804 g.c = term.line[term.c.y][term.c.x].c;
805 /* remove the old cursor */
806 if(term.line[oldy][oldx].state & CRset)
807 xdrawc(oldx, oldy, term.line[oldy][oldx]);
808 else xclear(oldx, oldy, oldx, oldy); /* XXX: maybe a bug */
809 if(mode == CSdraw && !term.c.hidden) {
810 xdrawc(term.c.x, term.c.y, g);
811 oldx = term.c.x, oldy = term.c.y;
812 }
813 }
814
815
816 void
817 draw(int redraw_all) {
818 int x, y;
819 int changed, set;
820
821 if(redraw_all)
822 XClearWindow(xw.dis, xw.win);
823 /* XXX: drawing could be optimised */
824 for(y = 0; y < term.row; y++) {
825 for(x = 0; x < term.col; x++) {
826 changed = term.line[y][x].state & CRupdate;
827 set = term.line[y][x].state & CRset;
828 if((changed && set) || (redraw_all && set)) {
829 term.line[y][x].state &= ~CRupdate;
830 xdrawc(x, y, term.line[y][x]);
831 }
832 }
833 }
834 xcursor(CSdraw);
835 }
836
837 void
838 kpress(XKeyEvent *e) {
839 KeySym ksym;
840 char buf[32];
841 int len;
842 int meta;
843 int shift;
844
845 meta = e->state & Mod1Mask;
846 shift = e->state & ShiftMask;
847 len = XLookupString(e, buf, sizeof(buf), &ksym, NULL);
848 if(len > 0) {
849 buf[sizeof(buf)-1] = '\0';
850 if(meta && len == 1)
851 ttywrite("\033", 1);
852 ttywrite(buf, len);
853 return;
854 }
855 switch(ksym) {
856 default:
857 fprintf(stderr, "errkey: %d\n", (int)ksym);
858 break;
859 case XK_Up:
860 case XK_Down:
861 case XK_Left:
862 case XK_Right:
863 sprintf(buf, "\033[%c", "DACB"[ksym - XK_Left]);
864 ttywrite(buf, 3);
865 break;
866 case XK_Delete: ttywrite(KEYDELETE, sizeof(KEYDELETE)-1); break;
867 case XK_Home: ttywrite(KEYHOME, sizeof(KEYHOME)-1); break;
868 case XK_End: ttywrite(KEYEND, sizeof(KEYEND) -1); break;
869 case XK_Prior: ttywrite(KEYPREV, sizeof(KEYPREV)-1); break;
870 case XK_Next: ttywrite(KEYNEXT, sizeof(KEYNEXT)-1); break;
871 case XK_Insert:
872 /* XXX: paste X clipboard */
873 if(shift)
874 ;
875 break;
876 }
877 }
878
879 void
880 resize(XEvent *e) {
881 int col, row;
882 col = e->xconfigure.width / xw.cw;
883 row = e->xconfigure.height / xw.ch;
884
885 if(term.col != col || term.row != row) {
886 tresize(col, row);
887 ttyresize(col, row);
888 xw.w = e->xconfigure.width;
889 xw.h = e->xconfigure.height;
890 draw(SCredraw);
891 }
892 }
893
894
895 void
896 run(void) {
897 int ret;
898 XEvent ev;
899 fd_set rfd;
900 int xfd = XConnectionNumber(xw.dis);
901
902 running = 1;
903 XSelectInput(xw.dis, xw.win, ExposureMask | KeyPressMask | StructureNotifyMask);
904 XResizeWindow(xw.dis, xw.win, xw.w , xw.h); /* fix resize bug in wmii (?) */
905
906 while(running) {
907 FD_ZERO(&rfd);
908 FD_SET(cmdfd, &rfd);
909 FD_SET(xfd, &rfd);
910 XFlush(xw.dis);
911 ret = select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, NULL);
912
913 if(ret < 0)
914 die("select failed: %s\n", SERRNO);
915
916 if(FD_ISSET(xfd, &rfd)) {
917 while(XPending(xw.dis)) {
918 XNextEvent(xw.dis, &ev);
919 switch (ev.type) {
920 default:
921 break;
922 case KeyPress:
923 kpress(&ev.xkey);
924 break;
925 case Expose:
926 draw(SCredraw);
927 break;
928 case ConfigureNotify:
929 resize(&ev);
930 break;
931 }
932 }
933 }
934 if(FD_ISSET(cmdfd, &rfd)) {
935 ttyread();
936 draw(SCupdate);
937 }
938 }
939 }
940
941 int
942 main(int argc, char *argv[]) {
943 if(argc == 2 && !strncmp("-v", argv[1], 3))
944 die("st-" VERSION ", © 2009 st engineers\n");
945 else if(argc != 1)
946 die("usage: st [-v]\n");
947 setlocale(LC_CTYPE, "");
948 tnew(80, 24);
949 ttynew();
950 xinit();
951 run();
952 return 0;
953 }