#include #include #define XK_MISCELLANY #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BW 2 #define FONT_US "-ncd-terminal-medium-r-normal--18-*-iso646-us" #define FONT_SE "-ncd-terminal-medium-r-normal--18-*-iso646-se2" Display *d; Window w; GC tgc, fgc; Pixmap wsave, csave; Atom wm_protocols, wm_delete_window; int cols = 80; int lines = 25; int pheight = 66; int wx = 0, wy = 0, ww, wh; char *fg = "#acaaac"; char *bg = "black"; char *font_us = FONT_US; char *font_se = FONT_SE; XFontStruct *fi_us, *fi_se; int cw, ch, asc, desc; int x, y; char *shell; int ptyfd, xfd; int glass = 0; int baud, delay; static void wrestore() { XCopyArea(d, wsave, w, tgc, 0, 0, ww + (BW*2), wh + (BW*2), 0, 0); } static void cur(int state) { static int first = 1; if (state) { XCopyArea(d, w, csave, tgc, x, y - asc, cw, ch, 0, 0); XFillRectangle(d, w, tgc, x, y - asc, cw, ch); XCopyArea(d, wsave, csave, tgc, x, y - asc, cw, ch, 0, 0); XFillRectangle(d, wsave, tgc, x, y - asc, cw, ch); first = 0; } else { if (! first) { XCopyArea(d, csave, w, tgc, 0, 0, cw, ch, x, y - asc); XCopyArea(d, csave, wsave, tgc, 0, 0, cw, ch, x, y - asc); } } } static void feed(void) { XCopyArea(d, w, w, tgc, BW, BW + ch, ww, wh - ch, BW, BW); XFillRectangle(d, w, fgc, BW, BW + wh - ch, ww, ch); XCopyArea(d, wsave, wsave, tgc, BW, BW + ch, ww, wh - ch, BW, BW); XFillRectangle(d, wsave, fgc, BW, BW + wh - ch, ww, ch); } static int read_pty(void) { static int line = 0; int i, n; unsigned char c; #define print() if (glass) { \ XDrawImageString(d, w, tgc, x, y, &c, 1); \ XDrawImageString(d, wsave, tgc, x, y, &c, 1); \ } else { \ XDrawString(d, w, tgc, x, y, &c, 1); \ XDrawString(d, wsave, tgc, x, y, &c, 1); \ } if ((n = read(ptyfd, &c, 1)) <= 0) return n; if (delay) usleep(delay); if (glass) cur(0); switch (c) { case 8: if (x > BW) x -= cw; break; case 10: feed(); if (++line == pheight) line = 0; break; case 12: for (i = 0; i < (pheight - line); i++) feed(); line = 0; break; case 13: x = BW; break; default: if ((c < 32) || ((c > 126) && (c < 160))) break; if (x == BW + ww) { feed(); x = BW; } print(); x += cw; } if (glass) cur(1); return n; } static void write_pty(XEvent *e) { KeySym ks; unsigned char c; XLookupString((XKeyEvent *)e, &c, 1, &ks, NULL); switch (ks) { case XK_F9: XSetFont(d, tgc, fi_us->fid); return; case XK_F10: XSetFont(d, tgc, fi_se->fid); return; } if (c != 0) { if (delay) usleep(delay); write(ptyfd, &c, 1); } } static void usage(char *name) { fprintf(stderr, "usage: %s [-x xpos] [-y ypos] [-c columns] [-l lines] [-f us_font] [-s se_font] [-b baud]\n", name); exit(1); } static void init(int argc, char **argv) { XColor fgcol, bgcol; XClassHint *chint; XSizeHints *shints; char *name; int pid; int c, mod; if ((name = strrchr(argv[0], '/'))) name++; else name = argv[0]; if (! strcmp(name, "ttyemu")) { fprintf(stderr, "Error: wrong invocation name\n"); exit(1); } glass = ! strcmp(name, "glasstty"); baud = glass ? 300 : 110; while ((c = getopt(argc, argv, ":x:y:c:l:f:s:b:")) != -1) { switch (c) { case 'x': wx = atoi(optarg); break; case 'y': wy = atoi(optarg); break; case 'c': cols = atoi(optarg); break; case 'l': lines = atoi(optarg); break; case 'f': font_us = (char *)malloc(strlen(optarg) + 1); strcpy(font_us, optarg); break; case 's': font_se = (char *)malloc(strlen(optarg) + 1); strcpy(font_se, optarg); break; case 'b': baud = atoi(optarg); break; case ':': case '?': default: usage(name); } } if (! (pid = forkpty(&ptyfd, NULL, NULL, NULL))) { char *shell, *tail, shminus[80]; setenv(glass ? "GLASSTTY" : "HCTTY", "1", 1); if (! (shell = getenv("SHELL"))) shell = "/bin/sh"; tail = strrchr(shell, '/') + 1; sprintf(shminus, "-%s", tail); execl(shell, shminus, (char *)NULL); perror("execl"); _exit(1); } else if (pid < 0) { perror("fork"); exit(1); } setlocale(LC_ALL, ""); if (! (d = XOpenDisplay(NULL))) { fprintf(stderr, "Error opening display!\n"); exit(1); } xfd = ConnectionNumber(d); if (! (fi_us = XLoadQueryFont(d, font_us))) { fprintf(stderr, "Error loading font!\n"); exit(1); } if (! (fi_se = XLoadQueryFont(d, font_se))) { fprintf(stderr, "Error loading font!\n"); exit(1); } cw = fi_us->per_char[32].width; ch = fi_us->ascent + fi_us->descent; XAllocNamedColor(d, DefaultColormap(d, 0), fg, &fgcol, &fgcol); XAllocNamedColor(d, DefaultColormap(d, 0), bg, &bgcol, &bgcol); ww = cw * cols; wh = ch * lines; if (! (w = XCreateSimpleWindow(d, DefaultRootWindow(d), wx, wy, ww + (BW*2), wh + (BW*2), 0, bgcol.pixel, bgcol.pixel)) ) { printf("Error creating window!\n"); exit(1); } XSelectInput(d, w, KeyPressMask|ExposureMask); chint = XAllocClassHint(); chint->res_name = (glass ? "glasstty" : "hctty"); chint->res_class = (glass ? "Glasstty" : "Hctty"); XSetClassHint(d, w, chint); XStoreName(d, w, glass ? "Glass TTY" : "Hardcopy TTY"); shints = XAllocSizeHints(); shints->x = wx; shints->y = wy; shints->flags = USPosition; XSetWMNormalHints(d, w, shints); wm_protocols = XInternAtom(d, "WM_PROTOCOLS", False); wm_delete_window = XInternAtom(d, "WM_DELETE_WINDOW", False); XSetWMProtocols(d, w, &wm_delete_window, 1); tgc = XCreateGC(d, w, 0, NULL); XSetForeground(d, tgc, fgcol.pixel); XSetBackground(d, tgc, bgcol.pixel); XSetFont(d, tgc, fi_us->fid); fgc = XCreateGC(d, w, 0, NULL); XSetForeground(d, fgc, bgcol.pixel); wsave = XCreatePixmap(d, w, ww + (BW*2), wh + (BW*2), DefaultDepth(d, 0)); XFillRectangle(d, wsave, fgc, 0, 0, ww + (BW*2), wh + (BW*2)); if (glass) csave = XCreatePixmap(d, w, cw, ch, DefaultDepth(d, 0)); x = BW; y = BW + wh - fi_us->descent; asc = fi_us->ascent; desc = fi_us->descent; delay = 1000000 / ((double)baud / (glass ? 10. : 11.)); mod = delay % 10000; delay = (mod > 5000 ? delay + (10000 - mod) : delay - mod) - 10000; if (delay < 0) delay = 0; XMapWindow(d, w); } static void quit() { XDestroyWindow(d, w); XCloseDisplay(d); exit(0); } int main(int argc, char **argv) { init(argc, argv); for (;;) { XEvent e; fd_set io_set; struct timeval timeout; while (XPending(d)) { XNextEvent(d, &e); switch (e.type) { case Expose: if (! e.xexpose.count) wrestore(); break; case KeyPress: write_pty(&e); break; case ClientMessage: if (e.xclient.message_type == wm_protocols && e.xclient.data.l[0] == wm_delete_window) quit(); break; } } FD_ZERO(&io_set); FD_SET(ptyfd, &io_set); FD_SET(xfd, &io_set); timeout.tv_sec = 0; timeout.tv_usec = 5000; if (select(xfd + 1, &io_set, NULL, NULL, &timeout) <= 0) continue; if (FD_ISSET(ptyfd, &io_set)) { if (read_pty() <= 0) quit(); } } } .