#include #include #include #include #include #include #include #include #include #include #include #define WIDTH 576 #define HEIGHT 454 typedef unsigned char uint8; typedef unsigned short uint16; uint8 fb[WIDTH*HEIGHT]; int sock; int updatebuf; uint8 readbuf[64*1024]; int rawtty(int fd, struct termios *saved) { struct termios tio; if(tcgetattr(fd, &tio) < 0) return -1; *saved = tio; cfmakeraw(&tio); if(tcsetattr(fd, TCSAFLUSH, &tio) < 0) return -1; return 0; } int resettty(int fd, struct termios *tio) { if(tcsetattr(fd, TCSAFLUSH, tio) < 0) return -1; return 0; } void drawsixel(void) { int c = 0; printf("\e[H\ePq#3"); // #0 black // #1 blue // #2 red // #3 green for(int y = 0; y < HEIGHT; y += 6) { int nrep = 0; char last = 0; printf("#0!%d~$#3", WIDTH); for(int x = 0; x < WIDTH; x++) { char six = 0; int n = HEIGHT-y; if(n > 6) n = 6; for(int i = 0; i < n; i++) if(fb[(y+i)*WIDTH + x]) six |= 1< 0){ m = read(fd, data, n); if(m <= 0) return -1; data += m; n -= m; } return 0; } int dial(const char *host, int port) { char portstr[32]; int sockfd; struct addrinfo *result, *rp, hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; snprintf(portstr, 32, "%d", port); if(getaddrinfo(host, portstr, &hints, &result)){ perror("error: getaddrinfo"); return -1; } for(rp = result; rp; rp = rp->ai_next){ sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if(sockfd < 0) continue; if(connect(sockfd, rp->ai_addr, rp->ai_addrlen) >= 0) goto win; close(sockfd); } freeaddrinfo(result); perror("error"); return -1; win: freeaddrinfo(result); return sockfd; } enum { /* TV to 11 */ MSG_KEYDN = 0, MSG_GETFB, /* 11 to TV */ MSG_FB, MSG_WD, MSG_CLOSE, }; uint16 b2w(uint8 *b) { return b[0] | b[1]<<8; } void w2b(uint8 *b, uint16 w) { b[0] = w; b[1] = w>>8; } void msgheader(uint8 *b, uint8 type, uint16 length) { w2b(b, length); b[2] = type; } void getdpykbd(void) { uint8 buf[2]; if(readn(sock, buf, 2) == -1){ fprintf(stderr, "protocol botch\n"); return; } printf("%o %o\n", buf[0], buf[1]); } void getfb(void) { uint8 *b, buf[11]; int x, y, w, h; x = 0; y = 0; w = WIDTH; h = HEIGHT; b = buf; msgheader(b, MSG_GETFB, 9); b += 3; w2b(b, x); w2b(b+2, y); w2b(b+4, w); w2b(b+6, h); write(sock, buf, 11); } void getupdate(uint16 addr, uint16 wd) { int j; uint8 *dst; dst = &fb[addr*16]; for(j = 0; j < 16; j++){ dst[j] = wd&0100000 ? 1 : 0; wd <<= 1; } updatebuf++; } void unpackfb(uint8 *src, int x, int y, int w, int h) { int i, j; uint8 *dst; uint16 wd; dst = &fb[y*WIDTH + x]; for(i = 0; i < h; i++){ for(j = 0; j < w; j++){ if(j%16 == 0){ wd = b2w(src); src += 2; } dst[j] = wd&0100000 ? 1 : 0; wd <<= 1; } dst += WIDTH; } updatebuf = 100000; } /* These bits are directly sent to the 11 */ enum { MOD_RSHIFT = 0100, MOD_LSHIFT = 0200, MOD_RTOP = 00400, MOD_LTOP = 01000, MOD_RCTRL = 02000, MOD_LCTRL = 04000, MOD_RMETA = 010000, MOD_LMETA = 020000, MOD_SLOCK = 040000, }; #define MOD_SHIFT (MOD_LSHIFT | MOD_RSHIFT) #define MOD_CTRL (MOD_LCTRL | MOD_RCTRL) int symbolmap[128]; void initsymbolmap(void) { for(int i = 0; i < 128; i++) symbolmap[i] = -1; symbolmap[000] = 015 | MOD_LCTRL; symbolmap[001] = 047 | MOD_LCTRL; symbolmap[002] = 071 | MOD_LCTRL; symbolmap[003] = 067 | MOD_LCTRL; symbolmap[004] = 051 | MOD_LCTRL; symbolmap[005] = 026 | MOD_LCTRL; symbolmap[006] = 052 | MOD_LCTRL; symbolmap[007] = 053 | MOD_LCTRL; symbolmap[010] = 054 | MOD_LCTRL; symbolmap[011] = 022; // TAB symbolmap[012] = 055 | MOD_LCTRL; symbolmap[013] = 056 | MOD_LCTRL; symbolmap[014] = 057 | MOD_LCTRL; symbolmap[015] = 073 | MOD_LCTRL; symbolmap[016] = 072 | MOD_LCTRL; symbolmap[017] = 034 | MOD_LCTRL; symbolmap[020] = 035 | MOD_LCTRL; symbolmap[021] = 024 | MOD_LCTRL; symbolmap[022] = 027 | MOD_LCTRL; symbolmap[023] = 050 | MOD_LCTRL; symbolmap[024] = 030 | MOD_LCTRL; symbolmap[025] = 032 | MOD_LCTRL; symbolmap[026] = 070 | MOD_LCTRL; symbolmap[027] = 025 | MOD_LCTRL; symbolmap[030] = 066 | MOD_LCTRL; symbolmap[031] = 031 | MOD_LCTRL; symbolmap[032] = 020; // ^Z -> CALL symbolmap[033] = 023; // ESC -> ALTMODE symbolmap[034] = 040 | MOD_LCTRL; symbolmap[035] = 037 | MOD_LCTRL; symbolmap[036] = 016 | MOD_LCTRL; symbolmap[037] = 013 | MOD_LCTRL; symbolmap[' '] = 077; symbolmap['!'] = 002 | MOD_LSHIFT; symbolmap['"'] = 003 | MOD_LSHIFT; symbolmap['#'] = 004 | MOD_LSHIFT; symbolmap['%'] = 006 | MOD_LSHIFT; symbolmap['$'] = 005 | MOD_LSHIFT; symbolmap['&'] = 007 | MOD_LSHIFT; symbolmap['\''] = 010 | MOD_LSHIFT; symbolmap['('] = 011 | MOD_LSHIFT; symbolmap[')'] = 012 | MOD_LSHIFT; symbolmap['*'] = 061 | MOD_LSHIFT; symbolmap['+'] = 060 | MOD_LSHIFT; symbolmap[','] = 074; symbolmap['-'] = 014; symbolmap['.'] = 075; symbolmap['/'] = 076; symbolmap['0'] = 013; symbolmap['1'] = 002; symbolmap['2'] = 003; symbolmap['3'] = 004; symbolmap['4'] = 005; symbolmap['5'] = 006; symbolmap['6'] = 007; symbolmap['7'] = 010; symbolmap['8'] = 011; symbolmap['9'] = 012; symbolmap[':'] = 061; symbolmap[';'] = 060; symbolmap['<'] = 074 | MOD_LSHIFT; symbolmap['='] = 014 | MOD_LSHIFT; symbolmap['>'] = 075 | MOD_LSHIFT; symbolmap['?'] = 076 | MOD_LSHIFT; symbolmap['@'] = 015; symbolmap['A'] = 047 | MOD_LSHIFT; symbolmap['B'] = 071 | MOD_LSHIFT; symbolmap['C'] = 067 | MOD_LSHIFT; symbolmap['D'] = 051 | MOD_LSHIFT; symbolmap['E'] = 026 | MOD_LSHIFT; symbolmap['F'] = 052 | MOD_LSHIFT; symbolmap['G'] = 053 | MOD_LSHIFT; symbolmap['H'] = 054 | MOD_LSHIFT; symbolmap['I'] = 033 | MOD_LSHIFT; symbolmap['J'] = 055 | MOD_LSHIFT; symbolmap['K'] = 056 | MOD_LSHIFT; symbolmap['L'] = 057 | MOD_LSHIFT; symbolmap['M'] = 073 | MOD_LSHIFT; symbolmap['N'] = 072 | MOD_LSHIFT; symbolmap['O'] = 034 | MOD_LSHIFT; symbolmap['P'] = 035 | MOD_LSHIFT; symbolmap['Q'] = 024 | MOD_LSHIFT; symbolmap['R'] = 027 | MOD_LSHIFT; symbolmap['S'] = 050 | MOD_LSHIFT; symbolmap['T'] = 030 | MOD_LSHIFT; symbolmap['U'] = 032 | MOD_LSHIFT; symbolmap['V'] = 070 | MOD_LSHIFT; symbolmap['W'] = 025 | MOD_LSHIFT; symbolmap['X'] = 066 | MOD_LSHIFT; symbolmap['Y'] = 031 | MOD_LSHIFT; symbolmap['Z'] = 065 | MOD_LSHIFT; symbolmap['['] = 036; symbolmap['\\'] = 040; symbolmap[']'] = 037; symbolmap['^'] = 016; symbolmap['_'] = 013 | MOD_LSHIFT; symbolmap['`'] = 015 | MOD_LSHIFT; symbolmap['a'] = 047; symbolmap['b'] = 071; symbolmap['c'] = 067; symbolmap['d'] = 051; symbolmap['e'] = 026; symbolmap['f'] = 052; symbolmap['g'] = 053; symbolmap['h'] = 054; symbolmap['i'] = 033; symbolmap['j'] = 055; symbolmap['k'] = 056; symbolmap['l'] = 057; symbolmap['m'] = 073; symbolmap['n'] = 072; symbolmap['o'] = 034; symbolmap['p'] = 035; symbolmap['q'] = 024; symbolmap['r'] = 027; symbolmap['s'] = 050; symbolmap['t'] = 030; symbolmap['u'] = 032; symbolmap['v'] = 070; symbolmap['w'] = 025; symbolmap['x'] = 066; symbolmap['y'] = 031; symbolmap['z'] = 065; symbolmap['{'] = 036 | MOD_LSHIFT; symbolmap['|'] = 040 | MOD_LSHIFT; symbolmap['}'] = 037 | MOD_LSHIFT; symbolmap['~'] = 016 | MOD_LSHIFT; symbolmap[0177] = 046; } void tvcon(void) { uint16 len; uint8 *b; uint8 type; int x, y, w, h; struct pollfd pfd[2]; pfd[0].fd = 0; pfd[0].events = POLLIN; pfd[1].fd = sock; pfd[1].events = POLLIN; int n; while(n = poll(pfd, 2, 30), n >= 0) { // draw at timeout or every couple of updates if(n == 0 || updatebuf > 100) { if(updatebuf) { updatebuf = 0; drawsixel(); } } else if(pfd[0].revents & POLLIN) { // read from KBD char c; read(0, &c, 1); if(c == 0) goto done; int code = symbolmap[c]; if(code >= 0) { uint8 buf[5]; msgheader(buf, MSG_KEYDN, 3); w2b(buf+3, code); write(sock, buf, 5); } } else if(pfd[1].revents & POLLIN) { // read from socket readn(sock, &len, 2); len = b2w((uint8*)&len); b = readbuf; readn(sock, b, len); type = *b++; switch(type){ case MSG_FB: x = b2w(b); y = b2w(b+2); w = b2w(b+4); h = b2w(b+6); b += 8; unpackfb(b, x*16, y, w*16, h); break; case MSG_WD: getupdate(b2w(b), b2w(b+2)); break; case MSG_CLOSE: done: close(sock); exit(0); default: fprintf(stderr, "unknown msg type %d\n", type); } } } } int main() { char c; struct termios tiosaved; initsymbolmap(); rawtty(0, &tiosaved); sock = dial("maya.papnet.eu", 11100); getdpykbd(); getfb(); tvcon(); resettty(0, &tiosaved); close(sock); return 0; }