├── default.nix ├── refresh.h ├── Makefile ├── draw.h ├── scrsnap.c ├── conf.h ├── fbpad.h ├── font.c ├── draw.c ├── README ├── isdw.c ├── pad.c ├── fbpad.c ├── mxcfb-kobo.h ├── refresh.c ├── term.c └── LICENSE /default.nix: -------------------------------------------------------------------------------- 1 | with import { 2 | crossSystem = { 3 | config = "arm-linux-gnueabihf"; 4 | platform = (import ).systems.platforms.armv7l-hf-multiplatform; 5 | }; 6 | }; 7 | 8 | mkShell { 9 | buildInputs = [ ]; # your dependencies here 10 | } 11 | -------------------------------------------------------------------------------- /refresh.h: -------------------------------------------------------------------------------- 1 | int 2 | fbink_refresh(int fbfd, 3 | uint32_t region_top, 4 | uint32_t region_left, 5 | uint32_t region_width, 6 | uint32_t region_height, 7 | uint8_t dithering_mode); 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = arm-linux-gnueabihf-cc 2 | CFLAGS = -Wall -O2 3 | LDFLAGS = -lutil # for openpty 4 | 5 | all: fbpad 6 | 7 | %.o: %.c conf.h 8 | $(CC) -c $(CFLAGS) $< 9 | 10 | fbpad: fbpad.o term.o pad.o draw.o font.o isdw.o scrsnap.o refresh.o 11 | $(CC) -o $@ $^ $(LDFLAGS) 12 | patchelf --set-interpreter /lib/ld-linux-armhf.so.3 fbpad 13 | 14 | clean: 15 | rm -f *.o fbpad 16 | -------------------------------------------------------------------------------- /draw.h: -------------------------------------------------------------------------------- 1 | /* fbpad's framebuffer interface */ 2 | 3 | /* fb_mode() interpretation */ 4 | #define FBM_BPP(m) (((m) >> 16) & 0x0f) /* bytes per pixel (4 bits) */ 5 | #define FBM_CLR(m) ((m) & 0x0fff) /* bits per color (12 bits) */ 6 | #define FBM_ORD(m) (((m) >> 20) & 0x07) /* color order (3 bits) */ 7 | 8 | /* main functions */ 9 | int fb_init(char *dev); 10 | void fb_free(void); 11 | unsigned fb_mode(void); 12 | void *fb_mem(int r); 13 | int fb_rows(void); 14 | int fb_cols(void); 15 | int fb_fd(void); 16 | void fb_cmap(void); 17 | unsigned fb_val(int r, int g, int b); 18 | -------------------------------------------------------------------------------- /scrsnap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "draw.h" 4 | 5 | #define NSCRS 128 6 | 7 | static void *scrs[NSCRS]; 8 | 9 | void scr_snap(int idx) 10 | { 11 | int rowsz = FBM_BPP(fb_mode()) * fb_cols(); 12 | int i; 13 | if (idx < NSCRS && !scrs[idx]) 14 | scrs[idx] = malloc(fb_rows() * rowsz); 15 | if (idx < NSCRS && scrs[idx]) 16 | for (i = 0; i < fb_rows(); i++) 17 | memcpy(scrs[idx] + i * rowsz, fb_mem(i), rowsz); 18 | } 19 | 20 | void scr_free(int idx) 21 | { 22 | if (idx < NSCRS) { 23 | free(scrs[idx]); 24 | scrs[idx] = NULL; 25 | } 26 | } 27 | 28 | int scr_load(int idx) 29 | { 30 | int rowsz = FBM_BPP(fb_mode()) * fb_cols(); 31 | int i; 32 | if (idx < NSCRS && scrs[idx]) 33 | for (i = 0; i < fb_rows(); i++) 34 | memcpy(fb_mem(i), scrs[idx] + i * rowsz, rowsz); 35 | return 0; 36 | } 37 | 38 | void scr_done(void) 39 | { 40 | int i; 41 | for (i = 0; i < NSCRS; i++) 42 | free(scrs[i]); 43 | } 44 | -------------------------------------------------------------------------------- /conf.h: -------------------------------------------------------------------------------- 1 | /* framebuffer device */ 2 | #define FBDEV "/dev/fb0" 3 | 4 | /* list of tags */ 5 | #define TAGS "xnlhtr01uiva-" 6 | #define TAGS_SAVED "" 7 | 8 | /* programs mapped to m-c, m-m, m-e */ 9 | #define SHELL {"sh"} 10 | #define EDITOR {"vi"} 11 | #define MAIL {"mailx", "-f", "+inbox"} 12 | 13 | /* TERM variable for launched programs */ 14 | #define TERM "linux" 15 | 16 | /* fbval_t should match framebuffer depth */ 17 | typedef unsigned short fbval_t; 18 | 19 | /* tinyfont files for regular, italic, and bold fonts */ 20 | #define FR "terminus.tf" 21 | #define FI NULL 22 | #define FB "terminus-bold.tf" 23 | 24 | /* foreground and background colors */ 25 | #define FGCOLOR COLOR0 26 | #define BGCOLOR COLORF 27 | 28 | /* where to write the screen shot */ 29 | #define SCRSHOT "/tmp/scr" 30 | 31 | /* lock command password; NULL disables locking */ 32 | #define PASS NULL 33 | 34 | /* optimized version of fb_val() */ 35 | #define FB_VAL(r, g, b) fb_val((r), (g), (b)) 36 | 37 | /* brighten colors 0-7 for bold text */ 38 | #define BRIGHTEN 1 39 | 40 | /* black */ 41 | #define COLOR0 0x000000 42 | #define COLOR8 0x555555 43 | /* red */ 44 | #define COLOR1 0xaa0000 45 | #define COLOR9 0xff5555 46 | /* green */ 47 | #define COLOR2 0x00aa00 48 | #define COLORA 0x55ff55 49 | /* yellow */ 50 | #define COLOR3 0xaa5500 51 | #define COLORB 0xffff55 52 | /* blue */ 53 | #define COLOR4 0x0000aa 54 | #define COLORC 0x5555ff 55 | /* magenta */ 56 | #define COLOR5 0xaa00aa 57 | #define COLORD 0xff55ff 58 | /* cyan */ 59 | #define COLOR6 0x00aaaa 60 | #define COLORE 0x55ffff 61 | /* white */ 62 | #define COLOR7 0xaaaaaa 63 | #define COLORF 0xffffff 64 | -------------------------------------------------------------------------------- /fbpad.h: -------------------------------------------------------------------------------- 1 | /* fbpad header file */ 2 | 3 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 4 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 5 | #define LEN(a) (sizeof(a) / sizeof((a)[0])) 6 | 7 | #define ESC 27 /* escape code */ 8 | #define NCOLS 256 /* maximum number of screen columns */ 9 | #define NROWS 128 /* maximum number of screen rows */ 10 | #define NDOTS 1024 /* maximum pixels in glyphs */ 11 | #define NHIST 128 /* scrolling history lines */ 12 | 13 | /* isdw.c */ 14 | #define DWCHAR 0x40000000u /* 2nd half of a fullwidth char */ 15 | 16 | int isdw(int c); 17 | int iszw(int c); 18 | 19 | /* term.c */ 20 | struct term_state { 21 | int row, col; 22 | int fg, bg; 23 | int mode; 24 | }; 25 | 26 | struct term { 27 | int screen[NROWS * NCOLS]; /* screen content */ 28 | int hist[NHIST * NCOLS]; /* scrolling history */ 29 | int fgs[NROWS * NCOLS]; /* foreground color */ 30 | int bgs[NROWS * NCOLS]; /* background color */ 31 | int dirty[NROWS]; /* changed rows in lazy mode */ 32 | struct term_state cur, sav; /* terminal saved state */ 33 | int fd; /* terminal file descriptor */ 34 | int hrow; /* the next history row in hist[] */ 35 | int hpos; /* scrolling history; position */ 36 | int lazy; /* lazy mode */ 37 | int pid; /* pid of the terminal program */ 38 | int top, bot; /* terminal scrolling region */ 39 | }; 40 | 41 | void term_load(struct term *term, int visible); 42 | void term_save(struct term *term); 43 | 44 | void term_read(void); 45 | void term_send(int c); 46 | void term_exec(char **args); 47 | void term_end(void); 48 | void term_screenshot(void); 49 | void term_scrl(int pos); 50 | void term_redraw(int all); 51 | 52 | /* pad.c */ 53 | #define FN_I 0x01000000 /* italic font */ 54 | #define FN_B 0x02000000 /* bold font */ 55 | #define FN_C 0x00ffffff /* font color mask */ 56 | 57 | int pad_init(void); 58 | void pad_free(void); 59 | int pad_font(char *fr, char *fi, char *fb); 60 | void pad_put(int ch, int r, int c, int fg, int bg); 61 | int pad_rows(void); 62 | int pad_cols(void); 63 | int pad_refresh(void); 64 | void pad_fill(int sr, int er, int sc, int ec, int c); 65 | 66 | /* font.c */ 67 | struct font *font_open(char *path); 68 | void font_free(struct font *font); 69 | int font_rows(struct font *font); 70 | int font_cols(struct font *font); 71 | int font_bitmap(struct font *font, void *dst, int c); 72 | 73 | /* scrsnap.c */ 74 | void scr_snap(int idx); 75 | int scr_load(int idx); 76 | void scr_free(int idx); 77 | void scr_done(void); 78 | -------------------------------------------------------------------------------- /font.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "fbpad.h" 7 | 8 | struct font { 9 | int rows, cols; /* glyph bitmap rows and columns */ 10 | int n; /* number of font glyphs */ 11 | int *glyphs; /* glyph unicode character codes */ 12 | char *data; /* glyph bitmaps */ 13 | }; 14 | 15 | /* 16 | * This tinyfont header is followed by: 17 | * 18 | * glyphs[n] unicode character codes (int) 19 | * bitmaps[n] character bitmaps (char[rows * cols]) 20 | */ 21 | struct tinyfont { 22 | char sig[8]; /* tinyfont signature; "tinyfont" */ 23 | int ver; /* version; 0 */ 24 | int n; /* number of glyphs */ 25 | int rows, cols; /* glyph dimensions */ 26 | }; 27 | 28 | static void *xread(int fd, int len) 29 | { 30 | void *buf = malloc(len); 31 | if (buf && read(fd, buf, len) == len) 32 | return buf; 33 | free(buf); 34 | return NULL; 35 | } 36 | 37 | struct font *font_open(char *path) 38 | { 39 | struct font *font; 40 | struct tinyfont head; 41 | int fd = open(path, O_RDONLY); 42 | if (fd < 0 || read(fd, &head, sizeof(head)) != sizeof(head)) { 43 | close(fd); 44 | return NULL; 45 | } 46 | font = malloc(sizeof(*font)); 47 | font->n = head.n; 48 | font->rows = head.rows; 49 | font->cols = head.cols; 50 | font->glyphs = xread(fd, font->n * sizeof(int)); 51 | font->data = xread(fd, font->n * font->rows * font->cols); 52 | close(fd); 53 | if (!font->glyphs || !font->data) { 54 | font_free(font); 55 | return NULL; 56 | } 57 | return font; 58 | } 59 | 60 | static int find_glyph(struct font *font, int c) 61 | { 62 | int l = 0; 63 | int h = font->n; 64 | while (l < h) { 65 | int m = (l + h) / 2; 66 | if (font->glyphs[m] == c) 67 | return m; 68 | if (c < font->glyphs[m]) 69 | h = m; 70 | else 71 | l = m + 1; 72 | } 73 | return -1; 74 | } 75 | 76 | int font_bitmap(struct font *font, void *dst, int c) 77 | { 78 | int i = find_glyph(font, c); 79 | int len = font->rows * font->cols; 80 | if (i < 0) 81 | return 1; 82 | memcpy(dst, font->data + i * len, len); 83 | return 0; 84 | } 85 | 86 | void font_free(struct font *font) 87 | { 88 | if (font->data) 89 | free(font->data); 90 | if (font->glyphs) 91 | free(font->glyphs); 92 | free(font); 93 | } 94 | 95 | int font_rows(struct font *font) 96 | { 97 | return font->rows; 98 | } 99 | 100 | int font_cols(struct font *font) 101 | { 102 | return font->cols; 103 | } 104 | -------------------------------------------------------------------------------- /draw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "draw.h" 10 | 11 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 12 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 13 | #define NLEVELS (1 << 8) 14 | 15 | static struct fb_var_screeninfo vinfo; /* linux-specific FB structure */ 16 | static struct fb_fix_screeninfo finfo; /* linux-specific FB structure */ 17 | static int fd; /* FB device file descriptor */ 18 | static void *fb; /* mmap()ed FB memory */ 19 | static int bpp; /* bytes per pixel */ 20 | static int nr, ng, nb; /* color levels */ 21 | static int rl, rr, gl, gr, bl, br; /* shifts per color */ 22 | 23 | static int fb_len(void) 24 | { 25 | return finfo.line_length * vinfo.yres_virtual; 26 | } 27 | 28 | static void fb_cmap_save(int save) 29 | { 30 | static unsigned short red[NLEVELS], green[NLEVELS], blue[NLEVELS]; 31 | struct fb_cmap cmap; 32 | if (finfo.visual == FB_VISUAL_TRUECOLOR) 33 | return; 34 | cmap.start = 0; 35 | cmap.len = MAX(nr, MAX(ng, nb)); 36 | cmap.red = red; 37 | cmap.green = green; 38 | cmap.blue = blue; 39 | cmap.transp = NULL; 40 | ioctl(fd, save ? FBIOGETCMAP : FBIOPUTCMAP, &cmap); 41 | } 42 | 43 | void fb_cmap(void) 44 | { 45 | unsigned short red[NLEVELS], green[NLEVELS], blue[NLEVELS]; 46 | struct fb_cmap cmap; 47 | int i; 48 | if (finfo.visual == FB_VISUAL_TRUECOLOR) 49 | return; 50 | 51 | for (i = 0; i < nr; i++) 52 | red[i] = (65535 / (nr - 1)) * i; 53 | for (i = 0; i < ng; i++) 54 | green[i] = (65535 / (ng - 1)) * i; 55 | for (i = 0; i < nb; i++) 56 | blue[i] = (65535 / (nb - 1)) * i; 57 | 58 | cmap.start = 0; 59 | cmap.len = MAX(nr, MAX(ng, nb)); 60 | cmap.red = red; 61 | cmap.green = green; 62 | cmap.blue = blue; 63 | cmap.transp = NULL; 64 | 65 | ioctl(fd, FBIOPUTCMAP, &cmap); 66 | } 67 | 68 | unsigned fb_mode(void) 69 | { 70 | return ((rl < gl) << 22) | ((rl < bl) << 21) | ((gl < bl) << 20) | 71 | (bpp << 16) | (vinfo.red.length << 8) | 72 | (vinfo.green.length << 4) | (vinfo.blue.length); 73 | } 74 | 75 | static void init_colors(void) 76 | { 77 | nr = 1 << vinfo.red.length; 78 | ng = 1 << vinfo.blue.length; 79 | nb = 1 << vinfo.green.length; 80 | rr = 8 - vinfo.red.length; 81 | rl = vinfo.red.offset; 82 | gr = 8 - vinfo.green.length; 83 | gl = vinfo.green.offset; 84 | br = 8 - vinfo.blue.length; 85 | bl = vinfo.blue.offset; 86 | } 87 | 88 | int fb_init(char *dev) 89 | { 90 | fd = open(dev, O_RDWR); 91 | if (fd < 0) 92 | goto failed; 93 | if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0) 94 | goto failed; 95 | if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) < 0) 96 | goto failed; 97 | 98 | fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 99 | bpp = (vinfo.bits_per_pixel + 7) >> 3; 100 | fb = mmap(NULL, fb_len(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 101 | if (fb == MAP_FAILED) 102 | goto failed; 103 | init_colors(); 104 | fb_cmap_save(1); 105 | fb_cmap(); 106 | return 0; 107 | failed: 108 | perror("fb_init()"); 109 | close(fd); 110 | return 1; 111 | } 112 | 113 | int fb_fd(void) 114 | { 115 | return fd; 116 | } 117 | 118 | void fb_free(void) 119 | { 120 | fb_cmap_save(0); 121 | munmap(fb, fb_len()); 122 | close(fd); 123 | } 124 | 125 | int fb_rows(void) 126 | { 127 | return vinfo.yres; 128 | } 129 | 130 | int fb_cols(void) 131 | { 132 | return vinfo.xres; 133 | } 134 | 135 | void *fb_mem(int r) 136 | { 137 | return fb + (r + vinfo.yoffset) * finfo.line_length + vinfo.xoffset * bpp; 138 | } 139 | 140 | unsigned fb_val(int r, int g, int b) 141 | { 142 | return ((r >> rr) << rl) | ((g >> gr) << gl) | ((b >> br) << bl); 143 | } 144 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | fbpad-eink 2 | ========== 3 | 4 | This is a fork of Ali Gholami Rudi's https://github.com/aligrudi/fbpad 5 | 6 | with the eink code pulled from NiLuJe's impressive 7 | https://github.com/NiLuJe/FBInk 8 | 9 | The resulting derivative work is AGPL licensed (since FBInk is so 10 | licensed). 11 | 12 | As a warning, this is *NOT* using FBInk as a library, so it should be 13 | seen as a weekend hack for the particular kobo device that I happened 14 | to have access to. I'd like to package this up nicely and rely on 15 | FBInk more broadly to make fbpad work on a variety of devices. 16 | 17 | ~jim 18 | 19 | ---------------------------------------------------------------- 20 | 21 | FBPAD 22 | ===== 23 | 24 | Fbpad is a small linux framebuffer virtual terminal. It manages many 25 | terminals through single-letter tags; each tag can hold two terminals. 26 | The following table indicates fbpad's key-bindings (m-k means pressing 27 | k, while holding the alt key). 28 | 29 | ============== ======================================= 30 | KEY COMMAND 31 | ============== ======================================= 32 | m-c execute a shell (SHELL) 33 | m-m execute mail program (MAIL) 34 | m-e execute editor program (EDITOR) 35 | m-x switch to tag 'x' (TAGS) 36 | m-j switch to current tag's alternative terminal 37 | m-k like m-j 38 | m-p show tag summary 39 | m-o jump to the previous tag 40 | m-tab show the next terminal 41 | m-s create a text screenshot (SCRSHOT) 42 | m-y redraw the terminal 43 | c-m-l lock the screen; use PASS to unlock 44 | c-m-o disable/enable tag switching 45 | c-m-q quit fbpad 46 | m-, scroll up 47 | m-. scroll down 48 | ============== ======================================= 49 | 50 | Fbpad can be instructed to execute a single program: the program and 51 | its arguments should be passed as command line arguments of fbpad. 52 | Fbpad executes the specified program in a terminal, disables all 53 | terminal switching commands, and exits as soon as the program 54 | finishes. 55 | 56 | 57 | SETTING UP 58 | ========== 59 | 60 | To configure fbpad, you should edit conf.h. To get fbpad running 61 | you need to make sure FR points to a valid fbpad font (for testing 62 | you can try http://litcave.rudi.ir/courr.tf) and SHELL to the shell 63 | you want fbpad to execute. Also the type of fbval_t should match the 64 | framebuffer depth: unsigned char for 8-bit, short for 16-bit and int 65 | for 24/32-bit framebuffers. Once these are set, you should be able to 66 | start fbpad without problems. 67 | 68 | Fbpad fonts can be generated using fbpad_mkfn program 69 | (http://litcave.rudi.ir/). The FR, FI, and FB macros specify the path 70 | of fbpad fonts for regular, italic, and bold fonts. If FI or FB is 71 | NULL, the regular font is used for italic or bold text. 72 | 73 | Next, you may want to change the list of fbpad tags by changing TAGS. 74 | FGCOLOR and BGCOLOR macros specify foreground and background colors, 75 | for instance the FGCOLOR of 2 and BGCOLOR of 0 creates green on black 76 | terminals. Individual colors can be customized by editing the hex RGB 77 | color description of COLOR* macros. Also SCRSHOT macro specifies where 78 | fbpad text screenshots, created by "m-s" command, should be saved. 79 | 80 | If you want to use fbpad's scrsnap feature, you can edit TAGS_SAVED to 81 | change the list of saved terminals. Framebuffer memory is saved and 82 | reloaded for these tags, which is very convenient when you are using 83 | programs that change the framebuffer simultaneously, like fbpdf. 84 | 85 | 86 | 256-COLOR MODE 87 | ============== 88 | 89 | Fbpad supports xterm's 256-color extension, but most programs will not 90 | use this extension, unless the $TERM terminfo entry declares this 91 | feature. For this purpose, fbpad-256 terminfo file can be created to 92 | contain (the two-space identation should be ignored): 93 | 94 | fbpad-256, 95 | use=linux, U8#0, 96 | colors#256, 97 | pairs#32767, 98 | setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, 99 | setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, 100 | 101 | Which can be installed with tic command: 102 | 103 | $ tic -x ./fbpad-256 104 | 105 | The following line should be added to shell's startup script: 106 | 107 | export TERM=fbpad-256 108 | 109 | Note that in order to use this feature and italic fonts in Vim, adding 110 | fbpad-256 terminfo is not necessary. Including the following lines in 111 | the vimrc file should enable them: 112 | 113 | set t_ZH= 114 | set t_Co=256 115 | -------------------------------------------------------------------------------- /isdw.c: -------------------------------------------------------------------------------- 1 | #include "fbpad.h" 2 | 3 | static int dwchars[][2] = { 4 | {0x1100, 0x115f}, {0x11a3, 0x11a7}, {0x11fa, 0x11ff}, {0x2329, 0x232a}, 5 | {0x2e80, 0x2e99}, {0x2e9b, 0x2ef3}, {0x2f00, 0x2fd5}, {0x2ff0, 0x2ffb}, 6 | {0x3000, 0x3029}, {0x3030, 0x303e}, {0x3041, 0x3096}, {0x309b, 0x30ff}, 7 | {0x3105, 0x312d}, {0x3131, 0x318e}, {0x3190, 0x31b7}, {0x31c0, 0x31e3}, 8 | {0x31f0, 0x321e}, {0x3220, 0x3247}, {0x3250, 0x32fe}, {0x3300, 0x4dbf}, 9 | {0x4e00, 0xa48c}, {0xa490, 0xa4c6}, {0xa960, 0xa97c}, {0xac00, 0xd7a3}, 10 | {0xd7b0, 0xd7c6}, {0xd7cb, 0xd7fb}, {0xf900, 0xfaff}, {0xfe10, 0xfe19}, 11 | {0xfe30, 0xfe52}, {0xfe54, 0xfe66}, {0xfe68, 0xfe6b}, {0xff01, 0xff60}, 12 | {0xffe0, 0xffe6}, {0x1f200, 0x1f200}, {0x1f210, 0x1f231}, {0x1f240, 0x1f248}, 13 | {0x20000,0x2ffff}, 14 | }; 15 | 16 | static int zwchars[][2] = { 17 | {0x0300, 0x036f}, {0x0483, 0x0489}, {0x0591, 0x05bd}, {0x05bf, 0x05bf}, 18 | {0x05c1, 0x05c2}, {0x05c4, 0x05c5}, {0x05c7, 0x05c7}, {0x0610, 0x061a}, 19 | {0x064b, 0x065e}, {0x0670, 0x0670}, {0x06d6, 0x06dc}, {0x06de, 0x06e4}, 20 | {0x06e7, 0x06e8}, {0x06ea, 0x06ed}, {0x0711, 0x0711}, {0x0730, 0x074a}, 21 | {0x07a6, 0x07b0}, {0x07eb, 0x07f3}, {0x0816, 0x0819}, {0x081b, 0x0823}, 22 | {0x0825, 0x0827}, {0x0829, 0x082d}, {0x0900, 0x0903}, {0x093c, 0x093c}, 23 | {0x093e, 0x094e}, {0x0951, 0x0955}, {0x0962, 0x0963}, {0x0981, 0x0983}, 24 | {0x09bc, 0x09bc}, {0x09be, 0x09c4}, {0x09c7, 0x09c8}, {0x09cb, 0x09cd}, 25 | {0x09d7, 0x09d7}, {0x09e2, 0x09e3}, {0x0a01, 0x0a03}, {0x0a3c, 0x0a3c}, 26 | {0x0a3e, 0x0a42}, {0x0a47, 0x0a48}, {0x0a4b, 0x0a4d}, {0x0a51, 0x0a51}, 27 | {0x0a70, 0x0a71}, {0x0a75, 0x0a75}, {0x0a81, 0x0a83}, {0x0abc, 0x0abc}, 28 | {0x0abe, 0x0ac5}, {0x0ac7, 0x0ac9}, {0x0acb, 0x0acd}, {0x0ae2, 0x0ae3}, 29 | {0x0b01, 0x0b03}, {0x0b3c, 0x0b3c}, {0x0b3e, 0x0b44}, {0x0b47, 0x0b48}, 30 | {0x0b4b, 0x0b4d}, {0x0b56, 0x0b57}, {0x0b62, 0x0b63}, {0x0b82, 0x0b82}, 31 | {0x0bbe, 0x0bc2}, {0x0bc6, 0x0bc8}, {0x0bca, 0x0bcd}, {0x0bd7, 0x0bd7}, 32 | {0x0c01, 0x0c03}, {0x0c3e, 0x0c44}, {0x0c46, 0x0c48}, {0x0c4a, 0x0c4d}, 33 | {0x0c55, 0x0c56}, {0x0c62, 0x0c63}, {0x0c82, 0x0c83}, {0x0cbc, 0x0cbc}, 34 | {0x0cbe, 0x0cc4}, {0x0cc6, 0x0cc8}, {0x0cca, 0x0ccd}, {0x0cd5, 0x0cd6}, 35 | {0x0ce2, 0x0ce3}, {0x0d02, 0x0d03}, {0x0d3e, 0x0d44}, {0x0d46, 0x0d48}, 36 | {0x0d4a, 0x0d4d}, {0x0d57, 0x0d57}, {0x0d62, 0x0d63}, {0x0d82, 0x0d83}, 37 | {0x0dca, 0x0dca}, {0x0dcf, 0x0dd4}, {0x0dd6, 0x0dd6}, {0x0dd8, 0x0ddf}, 38 | {0x0df2, 0x0df3}, {0x0e31, 0x0e31}, {0x0e34, 0x0e3a}, {0x0e47, 0x0e4e}, 39 | {0x0eb1, 0x0eb1}, {0x0eb4, 0x0eb9}, {0x0ebb, 0x0ebc}, {0x0ec8, 0x0ecd}, 40 | {0x0f18, 0x0f19}, {0x0f35, 0x0f35}, {0x0f37, 0x0f37}, {0x0f39, 0x0f39}, 41 | {0x0f3e, 0x0f3f}, {0x0f71, 0x0f84}, {0x0f86, 0x0f87}, {0x0f90, 0x0f97}, 42 | {0x0f99, 0x0fbc}, {0x0fc6, 0x0fc6}, {0x102b, 0x103e}, {0x1056, 0x1059}, 43 | {0x105e, 0x1060}, {0x1062, 0x1064}, {0x1067, 0x106d}, {0x1071, 0x1074}, 44 | {0x1082, 0x108d}, {0x108f, 0x108f}, {0x109a, 0x109d}, {0x135f, 0x135f}, 45 | {0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753}, {0x1772, 0x1773}, 46 | {0x17b6, 0x17d3}, {0x17dd, 0x17dd}, {0x180b, 0x180d}, {0x18a9, 0x18a9}, 47 | {0x1920, 0x192b}, {0x1930, 0x193b}, {0x19b0, 0x19c0}, {0x19c8, 0x19c9}, 48 | {0x1a17, 0x1a1b}, {0x1a55, 0x1a5e}, {0x1a60, 0x1a7c}, {0x1a7f, 0x1a7f}, 49 | {0x1b00, 0x1b04}, {0x1b34, 0x1b44}, {0x1b6b, 0x1b73}, {0x1b80, 0x1b82}, 50 | {0x1ba1, 0x1baa}, {0x1c24, 0x1c37}, {0x1cd0, 0x1cd2}, {0x1cd4, 0x1ce8}, 51 | {0x1ced, 0x1ced}, {0x1cf2, 0x1cf2}, {0x1dc0, 0x1de6}, {0x1dfd, 0x1dff}, 52 | {0x200b, 0x200f}, 53 | {0x20d0, 0x20f0}, {0x2cef, 0x2cf1}, {0x2de0, 0x2dff}, {0x302a, 0x302f}, 54 | {0x3099, 0x309a}, {0xa66f, 0xa672}, {0xa67c, 0xa67d}, {0xa6f0, 0xa6f1}, 55 | {0xa802, 0xa802}, {0xa806, 0xa806}, {0xa80b, 0xa80b}, {0xa823, 0xa827}, 56 | {0xa880, 0xa881}, {0xa8b4, 0xa8c4}, {0xa8e0, 0xa8f1}, {0xa926, 0xa92d}, 57 | {0xa947, 0xa953}, {0xa980, 0xa983}, {0xa9b3, 0xa9c0}, {0xaa29, 0xaa36}, 58 | {0xaa43, 0xaa43}, {0xaa4c, 0xaa4d}, {0xaa7b, 0xaa7b}, {0xaab0, 0xaab0}, 59 | {0xaab2, 0xaab4}, {0xaab7, 0xaab8}, {0xaabe, 0xaabf}, {0xaac1, 0xaac1}, 60 | {0xabe3, 0xabea}, {0xabec, 0xabed}, {0xfb1e, 0xfb1e}, {0xfe00, 0xfe0f}, 61 | {0xfe20, 0xfe26}, {0x101fd, 0x101fd}, {0x10a01, 0x10a03}, {0x10a05, 0x10a06}, 62 | {0x10a0c, 0x10a0f}, {0x10a38, 0x10a3a}, {0x10a3f, 0x10a3f}, {0x11080, 0x11082}, 63 | {0x110b0, 0x110ba}, {0x1d165, 0x1d169}, {0x1d16d, 0x1d172}, {0x1d17b, 0x1d182}, 64 | {0x1d185, 0x1d18b}, {0x1d1aa, 0x1d1ad}, {0x1d242, 0x1d244}, {0xe0100, 0xe01ef} 65 | }; 66 | 67 | static int find(int c, int tab[][2], int n) 68 | { 69 | int l = 0; 70 | int h = n - 1; 71 | int m; 72 | if (c < tab[0][0]) 73 | return 0; 74 | while (l <= h) { 75 | m = (h + l) / 2; 76 | if (c >= tab[m][0] && c <= tab[m][1]) 77 | return 1; 78 | if (c < tab[m][0]) 79 | h = m - 1; 80 | else 81 | l = m + 1; 82 | } 83 | return 0; 84 | } 85 | 86 | /* double-width characters */ 87 | int isdw(int c) 88 | { 89 | return c >= 0x1100 && find(c, dwchars, LEN(dwchars)); 90 | } 91 | 92 | /* zero-width and combining characters */ 93 | int iszw(int c) 94 | { 95 | return c >= 0x0300 && find(c, zwchars, LEN(zwchars)); 96 | } 97 | -------------------------------------------------------------------------------- /pad.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "conf.h" 6 | #include "draw.h" 7 | #include "fbpad.h" 8 | 9 | #include 10 | #include "refresh.h" 11 | 12 | static int rows, cols; 13 | static int fnrows, fncols; 14 | static struct font *fonts[3]; 15 | 16 | static int invalid_nonempty = 0; 17 | // invalid region is measured in character cells and does NOT include 18 | // the right or bottom edge 19 | static int invalid_top = 0, invalid_left = 0, invalid_right = 0, invalid_bottom = 0; 20 | 21 | int pad_init(void) 22 | { 23 | if (pad_font(FR, FI, FB)) 24 | return 1; 25 | rows = fb_rows() / fnrows; 26 | cols = fb_cols() / fncols; 27 | printf("%dx%d pixels, so %dx%d characters\n", fb_cols(), fb_rows(), cols, rows ); 28 | return 0; 29 | } 30 | 31 | void pad_free(void) 32 | { 33 | int i; 34 | for (i = 0; i < 3; i++) 35 | if (fonts[i]) 36 | font_free(fonts[i]); 37 | } 38 | 39 | #define CR(a) (((a) >> 16) & 0x0000ff) 40 | #define CG(a) (((a) >> 8) & 0x0000ff) 41 | #define CB(a) ((a) & 0x0000ff) 42 | #define COLORMERGE(f, b, c) ((b) + (((f) - (b)) * (c) >> 8u)) 43 | 44 | static unsigned mixed_color(int fg, int bg, unsigned val) 45 | { 46 | unsigned char r = COLORMERGE(CR(fg), CR(bg), val); 47 | unsigned char g = COLORMERGE(CG(fg), CG(bg), val); 48 | unsigned char b = COLORMERGE(CB(fg), CB(bg), val); 49 | return FB_VAL(r, g, b); 50 | } 51 | 52 | static unsigned color2fb(int c) 53 | { 54 | return FB_VAL(CR(c), CG(c), CB(c)); 55 | } 56 | 57 | /* glyph bitmap cache */ 58 | #define GCLCNT (1 << 7) /* glyph cache list count */ 59 | #define GCLLEN (1 << 4) /* glyph cache list length */ 60 | #define GCIDX(c) ((c) & (GCLCNT - 1)) 61 | 62 | static fbval_t gc_mem[GCLCNT][GCLLEN][NDOTS]; 63 | static int gc_next[GCLCNT]; 64 | static struct glyph { 65 | int c; 66 | int fg, bg; 67 | } gc_info[GCLCNT][GCLLEN]; 68 | 69 | static fbval_t *gc_get(int c, int fg, int bg) 70 | { 71 | struct glyph *g = gc_info[GCIDX(c)]; 72 | int i; 73 | for (i = 0; i < GCLLEN; i++) 74 | if (g[i].c == c && g[i].fg == fg && g[i].bg == bg) 75 | return gc_mem[GCIDX(c)][i]; 76 | return NULL; 77 | } 78 | 79 | static fbval_t *gc_put(int c, int fg, int bg) 80 | { 81 | int idx = GCIDX(c); 82 | int pos = gc_next[idx]++; 83 | struct glyph *g = &gc_info[idx][pos]; 84 | if (gc_next[idx] >= GCLLEN) 85 | gc_next[idx] = 0; 86 | g->c = c; 87 | g->fg = fg; 88 | g->bg = bg; 89 | return gc_mem[idx][pos]; 90 | } 91 | 92 | static void bmp2fb(fbval_t *d, char *s, int fg, int bg, int nr, int nc) 93 | { 94 | int i, j; 95 | for (i = 0; i < fnrows; i++) { 96 | for (j = 0; j < fncols; j++) { 97 | unsigned v = i < nr && j < nc ? 98 | (unsigned char) s[i * nc + j] : 0; 99 | d[i * fncols + j] = mixed_color(fg, bg, v); 100 | } 101 | } 102 | } 103 | 104 | static fbval_t *ch2fb(int fn, int c, int fg, int bg) 105 | { 106 | char bits[NDOTS]; 107 | fbval_t *fbbits; 108 | if (c < 0 || (c < 128 && (!isprint(c) || isspace(c)))) 109 | return NULL; 110 | if ((fbbits = gc_get(c, fg, bg))) 111 | return fbbits; 112 | if (font_bitmap(fonts[fn], bits, c)) 113 | return NULL; 114 | fbbits = gc_put(c, fg, bg); 115 | bmp2fb(fbbits, bits, fg & FN_C, bg & FN_C, 116 | font_rows(fonts[fn]), font_cols(fonts[fn])); 117 | return fbbits; 118 | } 119 | 120 | static void fb_set(int r, int c, void *mem, int len) 121 | { 122 | int bpp = FBM_BPP(fb_mode()); 123 | memcpy(fb_mem(r) + c * bpp, mem, len * bpp); 124 | } 125 | 126 | static void fb_box(int sr, int er, int sc, int ec, fbval_t val) 127 | { 128 | static fbval_t line[32 * NCOLS]; 129 | int i; 130 | for (i = sc; i < ec; i++) 131 | line[i - sc] = val; 132 | for (i = sr; i < er; i++) 133 | fb_set(i, sc, line, ec - sc); 134 | } 135 | 136 | static int fnsel(int fg, int bg) 137 | { 138 | if ((fg | bg) & FN_B) 139 | return fonts[2] ? 2 : 0; 140 | if ((fg | bg) & FN_I) 141 | return fonts[1] ? 1 : 0; 142 | return 0; 143 | } 144 | 145 | void pad_put(int ch, int r, int c, int fg, int bg) 146 | { 147 | if (invalid_nonempty) { 148 | if (c < invalid_left) invalid_left = c; 149 | if (c >= invalid_right) invalid_right = c + 1; 150 | if (r < invalid_top) invalid_top = r; 151 | if (r >= invalid_bottom) invalid_bottom = r + 1; 152 | } else { 153 | invalid_nonempty = 1; 154 | invalid_left = c; 155 | invalid_right = c+1; 156 | invalid_top = r; 157 | invalid_bottom = r+1; 158 | } 159 | 160 | int sr = fnrows * r; 161 | int sc = fncols * c; 162 | fbval_t *bits; 163 | int i; 164 | bits = ch2fb(fnsel(fg, bg), ch, fg, bg); 165 | if (!bits) 166 | bits = ch2fb(0, ch, fg, bg); 167 | if (!bits) 168 | fb_box(sr, sr + fnrows, sc, sc + fncols, color2fb(bg & FN_C)); 169 | else 170 | for (i = 0; i < fnrows; i++) 171 | fb_set(sr + i, sc, bits + (i * fncols), fncols); 172 | } 173 | 174 | void pad_fill(int sr, int er, int sc, int ec, int c) 175 | { 176 | int fber = er >= 0 ? er * fnrows : fb_rows(); 177 | int fbec = ec >= 0 ? ec * fncols : fb_cols(); 178 | fb_box(sr * fnrows, fber, sc * fncols, fbec, color2fb(c & FN_C)); 179 | 180 | if (invalid_nonempty) { 181 | if (sc < invalid_left) invalid_left = sc; 182 | if (ec > invalid_right) invalid_right = ec; 183 | if (sr < invalid_top) invalid_top = sr; 184 | if (er > invalid_bottom) invalid_bottom = er; 185 | } else { 186 | invalid_nonempty = 1; 187 | invalid_left = sc; 188 | invalid_right = ec + 1; 189 | invalid_top = sr; 190 | invalid_bottom = er + 1; 191 | } 192 | } 193 | 194 | int pad_refresh(void) { 195 | printf( "refresh: (%d,%d)+(%d,%d)\n", invalid_left, invalid_top, invalid_right - invalid_left, invalid_bottom - invalid_top ); 196 | 197 | fbink_refresh( fb_fd(), 198 | invalid_top * fnrows, 199 | invalid_left * fncols, 200 | (invalid_right - invalid_left) * fncols, 201 | (invalid_bottom - invalid_top) * fnrows, 202 | 0 ); 203 | 204 | invalid_nonempty = 0; 205 | invalid_top = invalid_bottom = invalid_left = invalid_right = 0; 206 | } 207 | 208 | int pad_rows(void) 209 | { 210 | return rows; 211 | } 212 | 213 | int pad_cols(void) 214 | { 215 | return cols; 216 | } 217 | 218 | int pad_font(char *fr, char *fi, char *fb) 219 | { 220 | struct font *r = fr ? font_open(fr) : NULL; 221 | if (!r) 222 | return 1; 223 | fonts[0] = r; 224 | fonts[1] = fi ? font_open(fi) : NULL; 225 | fonts[2] = fb ? font_open(fb) : NULL; 226 | memset(gc_info, 0, sizeof(gc_info)); 227 | fnrows = font_rows(fonts[0]); 228 | fncols = font_cols(fonts[0]); 229 | return 0; 230 | } 231 | 232 | -------------------------------------------------------------------------------- /fbpad.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FBPAD FRAMEBUFFER VIRTUAL TERMINAL 3 | * 4 | * Copyright (C) 2009-2018 Ali Gholami Rudi 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "conf.h" 31 | #include "fbpad.h" 32 | #include "draw.h" 33 | 34 | #define CTRLKEY(x) ((x) - 96) 35 | #define POLLFLAGS (POLLIN | POLLHUP | POLLERR | POLLNVAL) 36 | #define NTAGS (sizeof(tags) - 1) 37 | #define NTERMS (NTAGS * 2) 38 | #define TERMOPEN(i) (terms[i].fd) 39 | #define TERMSNAP(i) (strchr(TAGS_SAVED, tags[(i) % NTAGS])) 40 | 41 | static char tags[] = TAGS; 42 | static struct term terms[NTERMS]; 43 | static int tops[NTAGS]; /* top terms of tags */ 44 | static int ctag; /* current tag */ 45 | static int ltag; /* the last tag */ 46 | static int exitit; 47 | static int hidden; /* do not touch the framebuffer */ 48 | static int locked; 49 | static int taglock; /* disable tag switching */ 50 | static char pass[1024]; 51 | static int passlen; 52 | static int cmdmode; /* execute a command and exit */ 53 | 54 | static int readchar(void) 55 | { 56 | char b; 57 | if (read(0, &b, 1) > 0) 58 | return (unsigned char) b; 59 | return -1; 60 | } 61 | 62 | static int cterm(void) 63 | { 64 | return tops[ctag] * NTAGS + ctag; 65 | } 66 | 67 | static int altterm(int n) 68 | { 69 | return n < NTAGS ? n + NTAGS : n - NTAGS; 70 | } 71 | 72 | static int nextterm(void) 73 | { 74 | int n = (cterm() + 1) % NTERMS; 75 | while (n != cterm()) { 76 | if (TERMOPEN(n)) 77 | break; 78 | n = (n + 1) % NTERMS; 79 | } 80 | return n; 81 | } 82 | 83 | static struct term *mainterm(void) 84 | { 85 | return TERMOPEN(cterm()) ? &terms[cterm()] : NULL; 86 | } 87 | 88 | static void switchterm(int oidx, int nidx, int show, int save, int load) 89 | { 90 | if (save && TERMOPEN(oidx) && TERMSNAP(oidx)) 91 | scr_snap(oidx); 92 | term_save(&terms[oidx]); 93 | term_load(&terms[nidx], show); 94 | if (show) 95 | term_redraw(load && (!TERMOPEN(nidx) || !TERMSNAP(nidx) || 96 | scr_load(nidx))); 97 | } 98 | 99 | static void showterm(int n) 100 | { 101 | if (cterm() == n || cmdmode) 102 | return; 103 | if (taglock && ctag != n % NTAGS) 104 | return; 105 | if (ctag != n % NTAGS) 106 | ltag = ctag; 107 | switchterm(cterm(), n, !hidden, !hidden, !hidden); 108 | ctag = n % NTAGS; 109 | tops[ctag] = n / NTAGS; 110 | } 111 | 112 | static void showtag(int n) 113 | { 114 | showterm(tops[n] * NTAGS + n); 115 | } 116 | 117 | static void execterm(char **args) 118 | { 119 | if (!mainterm()) 120 | term_exec(args); 121 | } 122 | 123 | static void listtags(void) 124 | { 125 | /* colors for tags based on their number of terminals */ 126 | int colors[] = {COLOR7, FGCOLOR, FGCOLOR | FN_B}; 127 | int c = 0; 128 | int r = pad_rows() - 1; 129 | int i; 130 | pad_put('T', r, c++, FGCOLOR, BGCOLOR); 131 | pad_put('A', r, c++, FGCOLOR, BGCOLOR); 132 | pad_put('G', r, c++, FGCOLOR, BGCOLOR); 133 | pad_put('S', r, c++, FGCOLOR, BGCOLOR); 134 | pad_put(':', r, c++, FGCOLOR, BGCOLOR); 135 | pad_put(' ', r, c++, FGCOLOR, BGCOLOR); 136 | for (i = 0; i < NTAGS; i++) { 137 | int nt = 0; 138 | if (TERMOPEN(i)) 139 | nt++; 140 | if (TERMOPEN(altterm(i))) 141 | nt++; 142 | pad_put(i == ctag ? '(' : ' ', r, c++, FGCOLOR, BGCOLOR); 143 | if (TERMSNAP(i)) 144 | pad_put(tags[i], r, c++, !nt ? BGCOLOR : colors[nt], colors[0]); 145 | else 146 | pad_put(tags[i], r, c++, colors[nt], BGCOLOR); 147 | pad_put(i == ctag ? ')' : ' ', r, c++, FGCOLOR, BGCOLOR); 148 | } 149 | } 150 | 151 | static void directkey(void) 152 | { 153 | char *shell[32] = SHELL; 154 | char *mail[32] = MAIL; 155 | char *editor[32] = EDITOR; 156 | int c = readchar(); 157 | if (PASS && locked) { 158 | if (c == '\r') { 159 | pass[passlen] = '\0'; 160 | if (!strcmp(PASS, pass)) 161 | locked = 0; 162 | passlen = 0; 163 | return; 164 | } 165 | if (isprint(c) && passlen + 1 < sizeof(pass)) 166 | pass[passlen++] = c; 167 | return; 168 | } 169 | if (c == ESC) { 170 | switch ((c = readchar())) { 171 | case 'c': 172 | execterm(shell); 173 | return; 174 | case 'm': 175 | execterm(mail); 176 | return; 177 | case 'e': 178 | execterm(editor); 179 | return; 180 | case 'j': 181 | case 'k': 182 | showterm(altterm(cterm())); 183 | return; 184 | case 'o': 185 | showtag(ltag); 186 | return; 187 | case 'p': 188 | listtags(); 189 | return; 190 | case '\t': 191 | if (nextterm() != cterm()) 192 | showterm(nextterm()); 193 | return; 194 | case CTRLKEY('q'): 195 | exitit = 1; 196 | return; 197 | case 's': 198 | term_screenshot(); 199 | return; 200 | case 'y': 201 | term_redraw(1); 202 | return; 203 | case CTRLKEY('l'): 204 | locked = 1; 205 | passlen = 0; 206 | return; 207 | case CTRLKEY('o'): 208 | taglock = 1 - taglock; 209 | return; 210 | case ',': 211 | term_scrl(pad_rows() / 2); 212 | return; 213 | case '.': 214 | term_scrl(-pad_rows() / 2); 215 | return; 216 | default: 217 | if (strchr(tags, c)) { 218 | showtag(strchr(tags, c) - tags); 219 | return; 220 | } 221 | if (mainterm()) 222 | term_send(ESC); 223 | } 224 | } 225 | if (c != -1 && mainterm()) 226 | term_send(c); 227 | } 228 | 229 | static void peepterm(int termid) 230 | { 231 | if (termid != cterm()) 232 | switchterm(cterm(), termid, 0, 0, 0); 233 | } 234 | 235 | static void peepback(int termid) 236 | { 237 | if (termid != cterm()) 238 | switchterm(termid, cterm(), !hidden, 0, 0); 239 | } 240 | 241 | static int pollterms(void) 242 | { 243 | struct pollfd ufds[NTERMS + 1]; 244 | int term_idx[NTERMS + 1]; 245 | int i; 246 | int n = 1; 247 | ufds[0].fd = 0; 248 | ufds[0].events = POLLIN; 249 | for (i = 0; i < NTERMS; i++) { 250 | if (TERMOPEN(i)) { 251 | ufds[n].fd = terms[i].fd; 252 | ufds[n].events = POLLIN; 253 | term_idx[n++] = i; 254 | } 255 | } 256 | if (poll(ufds, n, 1000) < 1) 257 | return 0; 258 | if (ufds[0].revents & (POLLFLAGS & ~POLLIN)) 259 | return 1; 260 | if (ufds[0].revents & POLLIN) 261 | directkey(); 262 | for (i = 1; i < n; i++) { 263 | if (!(ufds[i].revents & POLLFLAGS)) 264 | continue; 265 | peepterm(term_idx[i]); 266 | if (ufds[i].revents & POLLIN) { 267 | term_read(); 268 | pad_refresh(); // BADBAD 269 | } else { 270 | scr_free(term_idx[i]); 271 | term_end(); 272 | if (cmdmode) 273 | exitit = 1; 274 | } 275 | peepback(term_idx[i]); 276 | } 277 | return 0; 278 | } 279 | 280 | static void mainloop(char **args) 281 | { 282 | struct termios oldtermios, termios; 283 | tcgetattr(0, &termios); 284 | oldtermios = termios; 285 | cfmakeraw(&termios); 286 | tcsetattr(0, TCSAFLUSH, &termios); 287 | term_load(&terms[cterm()], 1); 288 | term_redraw(1); 289 | if (args) { 290 | cmdmode = 1; 291 | execterm(args); 292 | } 293 | while (!exitit) 294 | if (pollterms()) 295 | break; 296 | tcsetattr(0, 0, &oldtermios); 297 | } 298 | 299 | static void signalreceived(int n) 300 | { 301 | if (exitit) 302 | return; 303 | switch (n) { 304 | case SIGUSR1: 305 | hidden = 1; 306 | switchterm(cterm(), cterm(), 0, 1, 0); 307 | ioctl(0, VT_RELDISP, 1); 308 | break; 309 | case SIGUSR2: 310 | hidden = 0; 311 | fb_cmap(); 312 | switchterm(cterm(), cterm(), 1, 0, 1); 313 | break; 314 | case SIGCHLD: 315 | while (waitpid(-1, NULL, WNOHANG) > 0) 316 | ; 317 | break; 318 | } 319 | } 320 | 321 | static void signalsetup(void) 322 | { 323 | struct vt_mode vtm; 324 | vtm.mode = VT_PROCESS; 325 | vtm.waitv = 0; 326 | vtm.relsig = SIGUSR1; 327 | vtm.acqsig = SIGUSR2; 328 | vtm.frsig = 0; 329 | signal(SIGUSR1, signalreceived); 330 | signal(SIGUSR2, signalreceived); 331 | signal(SIGCHLD, signalreceived); 332 | ioctl(0, VT_SETMODE, &vtm); 333 | } 334 | 335 | int main(int argc, char **argv) 336 | { 337 | char *hide = "\x1b[2J\x1b[H\x1b[?25l"; 338 | char *show = "\x1b[?25h"; 339 | char **args = argv + 1; 340 | if (fb_init(FBDEV)) { 341 | fprintf(stderr, "fbpad: failed to initialize the framebuffer\n"); 342 | return 1; 343 | } 344 | if (sizeof(fbval_t) != FBM_BPP(fb_mode())) { 345 | fprintf(stderr, "fbpad: fbval_t does not match framebuffer depth (%d bytes)\n", FBM_BPP(fb_mode()) ); 346 | return 1; 347 | } 348 | if (pad_init()) { 349 | fprintf(stderr, "fbpad: cannot find fonts\n"); 350 | return 1; 351 | } 352 | write(1, hide, strlen(hide)); 353 | signalsetup(); 354 | fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK); 355 | while (args[0] && args[0][0] == '-') 356 | args++; 357 | mainloop(args[0] ? args : NULL); 358 | write(1, show, strlen(show)); 359 | pad_free(); 360 | scr_done(); 361 | fb_free(); 362 | return 0; 363 | } 364 | -------------------------------------------------------------------------------- /mxcfb-kobo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved. 3 | * 4 | * - slightly modified (commented out include of fb.h) for Lua integration 5 | * - Frankensteined w/ Mark6 stuff -- NiLuJe 6 | */ 7 | 8 | /* 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License along 20 | * with this program; if not, write to the Free Software Foundation, Inc., 21 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 | */ 23 | 24 | /* 25 | * @file uapi/linux/mxcfb.h 26 | * 27 | * @brief Global header file for the MXC frame buffer 28 | * 29 | * @ingroup Framebuffer 30 | */ 31 | #ifndef __ASM_ARCH_MXCFB_H__ 32 | #define __ASM_ARCH_MXCFB_H__ 33 | 34 | //#include 35 | 36 | #define FB_SYNC_OE_LOW_ACT 0x80000000 37 | #define FB_SYNC_CLK_LAT_FALL 0x40000000 38 | #define FB_SYNC_DATA_INVERT 0x20000000 39 | #define FB_SYNC_CLK_IDLE_EN 0x10000000 40 | #define FB_SYNC_SHARP_MODE 0x08000000 41 | #define FB_SYNC_SWAP_RGB 0x04000000 42 | /* Mark 7 */ 43 | #define FB_ACCEL_TRIPLE_FLAG 0x00000000 44 | #define FB_ACCEL_DOUBLE_FLAG 0x00000001 45 | 46 | struct mxcfb_gbl_alpha { 47 | int enable; 48 | int alpha; 49 | }; 50 | 51 | struct mxcfb_loc_alpha { 52 | int enable; 53 | int alpha_in_pixel; 54 | unsigned long alpha_phy_addr0; 55 | unsigned long alpha_phy_addr1; 56 | }; 57 | 58 | struct mxcfb_color_key { 59 | int enable; 60 | __u32 color_key; 61 | }; 62 | 63 | struct mxcfb_pos { 64 | __u16 x; 65 | __u16 y; 66 | }; 67 | 68 | struct mxcfb_gamma { 69 | int enable; 70 | int constk[16]; 71 | int slopek[16]; 72 | }; 73 | 74 | /* Mark 7 */ 75 | struct mxcfb_gpu_split_fmt { 76 | struct fb_var_screeninfo var; 77 | unsigned long offset; 78 | }; 79 | 80 | struct mxcfb_rect { 81 | __u32 top; 82 | __u32 left; 83 | __u32 width; 84 | __u32 height; 85 | }; 86 | 87 | #define GRAYSCALE_8BIT 0x1 88 | #define GRAYSCALE_8BIT_INVERTED 0x2 89 | 90 | /* Mark 7 */ 91 | #define GRAYSCALE_4BIT 0x3 92 | #define GRAYSCALE_4BIT_INVERTED 0x4 93 | 94 | #define AUTO_UPDATE_MODE_REGION_MODE 0 95 | #define AUTO_UPDATE_MODE_AUTOMATIC_MODE 1 96 | 97 | #define UPDATE_SCHEME_SNAPSHOT 0 98 | #define UPDATE_SCHEME_QUEUE 1 99 | #define UPDATE_SCHEME_QUEUE_AND_MERGE 2 100 | 101 | #define UPDATE_MODE_PARTIAL 0x0 102 | #define UPDATE_MODE_FULL 0x1 103 | 104 | /* 105 | * Those are sneaked in in drivers/video/mxc/mxc_epdc_fb.c 106 | * Or drivers/video/fbdev/mxc/mxc_epdc_v2_fb.c since Mark 7 107 | */ 108 | #define NTX_WFM_MODE_INIT 0 109 | #define NTX_WFM_MODE_DU 1 110 | #define NTX_WFM_MODE_GC16 2 111 | #define NTX_WFM_MODE_GC4 3 112 | #define NTX_WFM_MODE_A2 4 113 | #define NTX_WFM_MODE_GL16 5 114 | #define NTX_WFM_MODE_GLR16 6 115 | #define NTX_WFM_MODE_GLD16 7 116 | #define NTX_WFM_MODE_TOTAL 8 117 | /* Match 'em to the Kindle ones, for sanity's sake... */ 118 | #define WAVEFORM_MODE_INIT NTX_WFM_MODE_INIT 119 | #define WAVEFORM_MODE_DU NTX_WFM_MODE_DU 120 | #define WAVEFORM_MODE_GC16 NTX_WFM_MODE_GC16 121 | #define WAVEFORM_MODE_GC4 NTX_WFM_MODE_GC4 122 | /* 123 | #define WAVEFORM_MODE_GC16_FAST 0xFFFF 124 | */ 125 | #define WAVEFORM_MODE_A2 NTX_WFM_MODE_A2 126 | #define WAVEFORM_MODE_GL16 NTX_WFM_MODE_GL16 127 | /* 128 | #define WAVEFORM_MODE_GL16_FAST 0xFFFF 129 | #define WAVEFORM_MODE_DU4 0xFFFF 130 | */ 131 | #define WAVEFORM_MODE_REAGL NTX_WFM_MODE_GLR16 132 | #define WAVEFORM_MODE_REAGLD NTX_WFM_MODE_GLD16 133 | /* 134 | #define WAVEFORM_MODE_GL4 0xFFFF 135 | #define WAVEFORM_MODE_GL16_INV 0xFFFF 136 | */ 137 | 138 | /* Nickel */ 139 | //#define WAVEFORM_MODE_GL16 5 140 | #define WAVEFORM_MODE_GLR32 6 141 | 142 | /* Mark 7 */ 143 | #define WAVEFORM_MODE_GLR16 6 144 | #define WAVEFORM_MODE_GLD16 7 145 | 146 | #define WAVEFORM_MODE_AUTO 257 147 | 148 | #define TEMP_USE_AMBIENT 0x1000 149 | 150 | #define EPDC_FLAG_ENABLE_INVERSION 0x01 151 | #define EPDC_FLAG_FORCE_MONOCHROME 0x02 152 | 153 | /* Aura */ 154 | #define EPDC_FLAG_USE_CMAP 0x04 155 | 156 | #define EPDC_FLAG_USE_ALT_BUFFER 0x100 157 | 158 | /* Aura ONLY */ 159 | #define EPDC_FLAG_USE_AAD 0x1000 160 | 161 | /* Mark 7 */ 162 | #define EPDC_FLAG_TEST_COLLISION 0x200 163 | #define EPDC_FLAG_GROUP_UPDATE 0x400 164 | /* Nickel: only for alyssum and above */ 165 | #define EPDC_FLAG_USE_DITHERING_Y1 0x2000 166 | #define EPDC_FLAG_USE_DITHERING_Y4 0x4000 167 | #define EPDC_FLAG_USE_REGAL 0x8000 168 | 169 | /* Nickel */ 170 | #define EPDC_FLAG_USE_DITHERING_NTX_D8 0x100000 171 | 172 | /* Mark 7 */ 173 | enum mxcfb_dithering_mode { 174 | EPDC_FLAG_USE_DITHERING_PASSTHROUGH = 0x0, 175 | EPDC_FLAG_USE_DITHERING_FLOYD_STEINBERG, 176 | EPDC_FLAG_USE_DITHERING_ATKINSON, 177 | EPDC_FLAG_USE_DITHERING_ORDERED, 178 | EPDC_FLAG_USE_DITHERING_QUANT_ONLY, 179 | EPDC_FLAG_USE_DITHERING_MAX, 180 | }; 181 | 182 | #define FB_POWERDOWN_DISABLE -1 183 | 184 | /* Mark 7 */ 185 | #define FB_TEMP_AUTO_UPDATE_DISABLE -1 186 | 187 | /* 188 | * NOTE: Mark 7 renamed some of these to maintain backwards compatibility while providing a newer interface 189 | * Was: mxcfb_alt_buffer_data 190 | * Nickel: imx5/imx6 191 | */ 192 | struct mxcfb_alt_buffer_data_ntx { 193 | void *virt_addr; 194 | __u32 phys_addr; 195 | __u32 width; /* width of entire buffer */ 196 | __u32 height; /* height of entire buffer */ 197 | struct mxcfb_rect alt_update_region; /* region within buffer to update */ 198 | }; 199 | 200 | 201 | /* 202 | * NOTE: Was: mxcfb_alt_buffer_data_org (appeared w/ the Aura) 203 | * Nickel: mxcfb_alt_buffer_data_v2 204 | */ 205 | struct mxcfb_alt_buffer_data { 206 | __u32 phys_addr; 207 | __u32 width; /* width of entire buffer */ 208 | __u32 height; /* height of entire buffer */ 209 | struct mxcfb_rect alt_update_region; /* region within buffer to update */ 210 | }; 211 | 212 | /* 213 | * Mark 7 214 | * NOTE: Was: mxcfb_update_data 215 | * Nickel: imx5/imx6 216 | */ 217 | // mxcfb_update_data v1 for NTX linux since from mx50/mx6sl . 218 | struct mxcfb_update_data_v1_ntx { 219 | struct mxcfb_rect update_region; 220 | __u32 waveform_mode; 221 | __u32 update_mode; 222 | __u32 update_marker; 223 | int temp; 224 | unsigned int flags; 225 | struct mxcfb_alt_buffer_data_ntx alt_buffer_data; 226 | }; 227 | 228 | 229 | /* NOTE: Was: mxcfb_update_data_org (appeared w/ the Aura) */ 230 | // mxcfb_update_data v1 since from mx50/mx6sl . 231 | struct mxcfb_update_data_v1 { 232 | struct mxcfb_rect update_region; 233 | __u32 waveform_mode; 234 | __u32 update_mode; 235 | __u32 update_marker; 236 | int temp; 237 | unsigned int flags; 238 | struct mxcfb_alt_buffer_data alt_buffer_data; 239 | }; 240 | 241 | 242 | /* 243 | * Mark 7 244 | * Nickel: mxcfb_update_data_v2 245 | */ 246 | // mxcfb_update_data v2 since from mx7d . 247 | #define mxcfb_update_data_v2 mxcfb_update_data 248 | struct mxcfb_update_data { 249 | struct mxcfb_rect update_region; 250 | __u32 waveform_mode; 251 | __u32 update_mode; 252 | __u32 update_marker; 253 | int temp; 254 | unsigned int flags; 255 | int dither_mode; 256 | int quant_bit; 257 | struct mxcfb_alt_buffer_data alt_buffer_data; 258 | }; 259 | 260 | /* Mark 7 */ 261 | struct mxcfb_update_marker_data { 262 | __u32 update_marker; 263 | __u32 collision_test; 264 | }; 265 | 266 | 267 | /* Mark 7 */ 268 | #define WFM_ENABLE_AA 1 269 | #define WFM_ENABLE_AAD 1 270 | 271 | 272 | /* 273 | * Structure used to define waveform modes for driver 274 | * Needed for driver to perform auto-waveform selection 275 | */ 276 | /* 277 | * NOTE: Once again, Mark 7 renamed some stuff 278 | * Was mxcfb_waveform_modes 279 | */ 280 | struct mxcfb_waveform_modes_ntx { 281 | int mode_init; 282 | int mode_du; 283 | int mode_gc4; 284 | int mode_gc8; 285 | int mode_gc16; 286 | int mode_gc32; 287 | /* Aura */ 288 | int mode_gl16; 289 | int mode_a2; 290 | 291 | int mode_aa; 292 | int mode_aad; 293 | }; 294 | 295 | /* Mark 7 */ 296 | struct mxcfb_waveform_modes { 297 | int mode_init; 298 | int mode_du; 299 | int mode_gc4; 300 | int mode_gc8; 301 | int mode_gc16; 302 | int mode_gc32; 303 | }; 304 | 305 | /* Mark 7 */ 306 | /* 307 | * Structure used to define a 5*3 matrix of parameters for 308 | * setting IPU DP CSC module related to this framebuffer. 309 | */ 310 | struct mxcfb_csc_matrix { 311 | int param[5][3]; 312 | }; 313 | 314 | 315 | #define MXCFB_WAIT_FOR_VSYNC _IOW('F', 0x20, u_int32_t) 316 | #define MXCFB_SET_GBL_ALPHA _IOW('F', 0x21, struct mxcfb_gbl_alpha) 317 | #define MXCFB_SET_CLR_KEY _IOW('F', 0x22, struct mxcfb_color_key) 318 | #define MXCFB_SET_OVERLAY_POS _IOWR('F', 0x24, struct mxcfb_pos) 319 | #define MXCFB_GET_FB_IPU_CHAN _IOR('F', 0x25, u_int32_t) 320 | #define MXCFB_SET_LOC_ALPHA _IOWR('F', 0x26, struct mxcfb_loc_alpha) 321 | #define MXCFB_SET_LOC_ALP_BUF _IOW('F', 0x27, unsigned long) 322 | #define MXCFB_SET_GAMMA _IOW('F', 0x28, struct mxcfb_gamma) 323 | #define MXCFB_GET_FB_IPU_DI _IOR('F', 0x29, u_int32_t) 324 | #define MXCFB_GET_DIFMT _IOR('F', 0x2A, u_int32_t) 325 | #define MXCFB_GET_FB_BLANK _IOR('F', 0x2B, u_int32_t) 326 | #define MXCFB_SET_DIFMT _IOW('F', 0x2C, u_int32_t) 327 | 328 | /* Mark 7 */ 329 | #define MXCFB_CSC_UPDATE _IOW('F', 0x2D, struct mxcfb_csc_matrix) 330 | #define MXCFB_SET_GPU_SPLIT_FMT _IOW('F', 0x2F, struct mxcfb_gpu_split_fmt) 331 | #define MXCFB_SET_PREFETCH _IOW('F', 0x30, int) 332 | #define MXCFB_GET_PREFETCH _IOR('F', 0x31, int) 333 | 334 | /* IOCTLs for E-ink panel updates */ 335 | #define MXCFB_SET_WAVEFORM_MODES _IOW('F', 0x2B, struct mxcfb_waveform_modes) 336 | 337 | /* Mark 7 */ 338 | #define MXCFB_SET_WAVEFORM_MODES_NTX _IOW('F', 0x2B, struct mxcfb_waveform_modes_ntx) 339 | 340 | #define MXCFB_SET_TEMPERATURE _IOW('F', 0x2C, int32_t) 341 | #define MXCFB_SET_AUTO_UPDATE_MODE _IOW('F', 0x2D, __u32) 342 | 343 | /* Mark 7 */ 344 | /* NOTE: Was MXCFB_SEND_UPDATE before Mark 7! Is still that in Nickel's header */ 345 | #define MXCFB_SEND_UPDATE_V1_NTX _IOW('F', 0x2E, struct mxcfb_update_data_v1_ntx) 346 | /* NOTE: Was: MXCFB_SEND_UPDATE_ORG before Mark 7 (appeared w/ Aura)! */ 347 | #define MXCFB_SEND_UPDATE_V1 _IOW('F', 0x2E, struct mxcfb_update_data_v1) 348 | /* NOTE: -> MXCFB_SEND_UPDATE_V2 ! */ 349 | #define MXCFB_SEND_UPDATE _IOW('F', 0x2E, struct mxcfb_update_data) 350 | 351 | /* Mark 7 */ 352 | #define MXCFB_SEND_UPDATE_V2 _IOW('F', 0x2E, struct mxcfb_update_data) 353 | /* NOTE: -> MXCFB_WAIT_FOR_UPDATE_COMPLETE_V3 ! */ 354 | #define MXCFB_WAIT_FOR_UPDATE_COMPLETE _IOWR('F', 0x2F, struct mxcfb_update_marker_data) // mx7d/mx6ull/mx6sll interface . 355 | /* NOTE: Nickel: MXCFB_WAIT_FOR_UPDATE_COMPLETE_V2 */ 356 | #define MXCFB_WAIT_FOR_UPDATE_COMPLETE_V3 _IOWR('F', 0x2F, struct mxcfb_update_marker_data) // mx7d/mx6ull/mx6sll interface . 357 | /* NOTE: Was MXCFB_WAIT_FOR_UPDATE_COMPLETE before Mark 7! Is still that in Nickel's header */ 358 | #define MXCFB_WAIT_FOR_UPDATE_COMPLETE_V1 _IOW('F', 0x2F, __u32) // mx50/NTX interface . 359 | #define MXCFB_WAIT_FOR_UPDATE_COMPLETE_V2 _IOWR('F', 0x35, struct mxcfb_update_marker_data) // mx6sl BSP interface . 360 | 361 | #define MXCFB_SET_PWRDOWN_DELAY _IOW('F', 0x30, int32_t) 362 | #define MXCFB_GET_PWRDOWN_DELAY _IOR('F', 0x31, int32_t) 363 | #define MXCFB_SET_UPDATE_SCHEME _IOW('F', 0x32, __u32) 364 | 365 | /* Went poof w/ Mark 7 */ 366 | #define MXCFB_SET_MERGE_ON_WAVEFORM_MISMATCH _IOW('F', 0x37, int32_t) 367 | 368 | /* Mark 7 */ 369 | #define MXCFB_GET_WORK_BUFFER _IOWR('F', 0x34, unsigned long) 370 | #define MXCFB_DISABLE_EPDC_ACCESS _IO('F', 0x35) 371 | #define MXCFB_ENABLE_EPDC_ACCESS _IO('F', 0x36) 372 | #define MXCFB_SET_TEMP_AUTO_UPDATE_PERIOD _IOW('F', 0x37, int32_t) 373 | 374 | #ifdef __KERNEL__ 375 | 376 | extern struct fb_videomode mxcfb_modedb[]; 377 | extern int mxcfb_modedb_sz; 378 | 379 | enum { 380 | MXC_DISP_SPEC_DEV = 0, 381 | MXC_DISP_DDC_DEV = 1, 382 | }; 383 | 384 | enum { 385 | MXCFB_REFRESH_OFF, 386 | MXCFB_REFRESH_AUTO, 387 | MXCFB_REFRESH_PARTIAL, 388 | }; 389 | 390 | int mxcfb_set_refresh_mode(struct fb_info *fbi, int mode, 391 | struct mxcfb_rect *update_region); 392 | 393 | /* Mark 7 */ 394 | void mxcfb_elcdif_register_mode(const struct fb_videomode *modedb, 395 | int num_modes, int dev_mode); 396 | 397 | /* Went poof in Mark 7*/ 398 | void mxcfb_register_mode(int disp_port, 399 | const struct fb_videomode *modedb, 400 | int num_modes, int dev_mode); 401 | 402 | /* Went poof in Mark 7*/ 403 | void mxcfb_register_presetup(int disp_port, 404 | int (*pre_setup)(struct fb_info *info)); 405 | 406 | int mxc_elcdif_frame_addr_setup(dma_addr_t phys); 407 | #endif /* __KERNEL__ */ 408 | #endif 409 | -------------------------------------------------------------------------------- /refresh.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "fbink.h" 13 | #include 14 | #include "conf.h" 15 | #include 16 | 17 | #include 18 | #include "mxcfb-kobo.h" 19 | #include "refresh.h" 20 | 21 | #include "draw.h" 22 | 23 | const int isKoboMk7 = 0; 24 | const int canHWInvert = 0; 25 | 26 | #define ERRCODE(e) (-(e)) 27 | #define WARN(fmt, ...) ({ fprintf(stderr, "[FBInk] " fmt "!\n", ##__VA_ARGS__); }) 28 | #define LOG(fmt, ...) 29 | 30 | #define FBFD_AUTO -1 31 | 32 | // Kobo devices ([Mk3<->Mk6]) 33 | static int 34 | refresh_kobo(int fbfd, 35 | const struct mxcfb_rect region, 36 | uint32_t waveform_mode, 37 | uint32_t update_mode, 38 | bool is_nightmode, 39 | uint32_t marker) 40 | { 41 | struct mxcfb_update_data_v1_ntx update = { 42 | .update_region = region, 43 | .waveform_mode = waveform_mode, 44 | .update_mode = update_mode, 45 | .update_marker = marker, 46 | .temp = TEMP_USE_AMBIENT, 47 | .flags = (waveform_mode == WAVEFORM_MODE_REAGLD) 48 | ? EPDC_FLAG_USE_AAD 49 | : (waveform_mode == WAVEFORM_MODE_A2) ? EPDC_FLAG_FORCE_MONOCHROME : 0U, 50 | .alt_buffer_data = { 0U }, 51 | }; 52 | 53 | if (is_nightmode && canHWInvert) { 54 | update.flags |= EPDC_FLAG_ENABLE_INVERSION; 55 | } 56 | 57 | int rv; 58 | rv = ioctl(fbfd, MXCFB_SEND_UPDATE_V1_NTX, &update); 59 | 60 | if (rv < 0) { 61 | char buf[256]; 62 | const char* errstr = (char*)strerror_r(errno, buf, sizeof(buf)); 63 | WARN("MXCFB_SEND_UPDATE_V1_NTX: %s", errstr); 64 | if (errno == EINVAL) { 65 | WARN("update_region={top=%u, left=%u, width=%u, height=%u}", 66 | region.top, 67 | region.left, 68 | region.width, 69 | region.height); 70 | } 71 | return ERRCODE(EXIT_FAILURE); 72 | } 73 | 74 | if (update_mode == UPDATE_MODE_FULL) { 75 | rv = ioctl(fbfd, MXCFB_WAIT_FOR_UPDATE_COMPLETE_V1, &marker); 76 | 77 | if (rv < 0) { 78 | char buf[256]; 79 | const char* errstr = (char*)strerror_r(errno, buf, sizeof(buf)); 80 | WARN("MXCFB_WAIT_FOR_UPDATE_COMPLETE_V1: %s", errstr); 81 | return ERRCODE(EXIT_FAILURE); 82 | } else { 83 | // NOTE: Timeout is set to 10000ms 84 | LOG("Waited %ldms for completion of flashing update %u", (10000 - jiffies_to_ms(rv)), marker); 85 | } 86 | } 87 | 88 | return EXIT_SUCCESS; 89 | } 90 | 91 | // Kobo Mark 7 devices ([Mk7<->??) 92 | static int 93 | refresh_kobo_mk7(int fbfd, 94 | const struct mxcfb_rect region, 95 | uint32_t waveform_mode, 96 | uint32_t update_mode, 97 | int dithering_mode, 98 | bool is_nightmode, 99 | uint32_t marker) 100 | { 101 | struct mxcfb_update_data_v2 update = { 102 | .update_region = region, 103 | .waveform_mode = waveform_mode, 104 | .update_mode = update_mode, 105 | .update_marker = marker, 106 | .temp = TEMP_USE_AMBIENT, 107 | .flags = (waveform_mode == WAVEFORM_MODE_GLD16) 108 | ? EPDC_FLAG_USE_REGAL 109 | : (waveform_mode == WAVEFORM_MODE_A2) ? EPDC_FLAG_FORCE_MONOCHROME : 0U, 110 | .dither_mode = dithering_mode, 111 | .quant_bit = (dithering_mode == EPDC_FLAG_USE_DITHERING_PASSTHROUGH) 112 | ? 0 113 | : (waveform_mode == WAVEFORM_MODE_A2 || waveform_mode == WAVEFORM_MODE_DU) 114 | ? 1 115 | : (waveform_mode == WAVEFORM_MODE_GC4) ? 3 : 7, 116 | .alt_buffer_data = { 0U }, 117 | }; 118 | 119 | if (is_nightmode && canHWInvert) { 120 | update.flags |= EPDC_FLAG_ENABLE_INVERSION; 121 | } 122 | 123 | int rv; 124 | rv = ioctl(fbfd, MXCFB_SEND_UPDATE_V2, &update); 125 | 126 | if (rv < 0) { 127 | char buf[256]; 128 | const char* errstr = (char*)strerror_r(errno, buf, sizeof(buf)); 129 | WARN("MXCFB_SEND_UPDATE_V2: %s", errstr); 130 | if (errno == EINVAL) { 131 | WARN("update_region={top=%u, left=%u, width=%u, height=%u}", 132 | region.top, 133 | region.left, 134 | region.width, 135 | region.height); 136 | } 137 | return ERRCODE(EXIT_FAILURE); 138 | } 139 | 140 | if (update_mode == UPDATE_MODE_FULL) { 141 | struct mxcfb_update_marker_data update_marker = { 142 | .update_marker = marker, 143 | .collision_test = 0U, 144 | }; 145 | 146 | rv = ioctl(fbfd, MXCFB_WAIT_FOR_UPDATE_COMPLETE_V3, &update_marker); 147 | 148 | if (rv < 0) { 149 | char buf[256]; 150 | const char* errstr = (char*)strerror_r(errno, buf, sizeof(buf)); 151 | WARN("MXCFB_WAIT_FOR_UPDATE_COMPLETE_V3: %s", errstr); 152 | return ERRCODE(EXIT_FAILURE); 153 | } else { 154 | // NOTE: Timeout is set to 5000ms 155 | LOG("Waited %ldms for completion of flashing update %u", (5000 - jiffies_to_ms(rv)), marker); 156 | } 157 | } 158 | 159 | return EXIT_SUCCESS; 160 | } 161 | 162 | // And another variant that'll try to get a nonblocking fd when using FBFD_AUTO 163 | // NOTE: Only use this for functions that don't actually need to write to the fb, and only need an fd for ioctls! 164 | // Generally, those won't try to mmap the fb either ;). 165 | static int 166 | open_fb_fd_nonblock(int* restrict fbfd, bool* restrict keep_fd) 167 | { 168 | if (*fbfd == FBFD_AUTO) { 169 | // If we're opening a fd now, don't keep it around. 170 | *keep_fd = false; 171 | // We only need an fd for ioctl, hence O_NONBLOCK (as per open(2)). 172 | *fbfd = open(FBDEV, O_RDONLY | O_NONBLOCK | O_CLOEXEC); 173 | if (!*fbfd) { 174 | WARN("Error: cannot open framebuffer character device, aborting"); 175 | return ERRCODE(EXIT_FAILURE); 176 | } 177 | } 178 | 179 | return EXIT_SUCCESS; 180 | } 181 | 182 | int 183 | refresh(int fbfd, 184 | const struct mxcfb_rect region, 185 | uint32_t waveform_mode, 186 | int dithering_mode, 187 | bool is_nightmode, 188 | bool is_flashing, 189 | bool no_refresh) 190 | { 191 | // Were we asked to skip refreshes? 192 | if (no_refresh) { 193 | LOG("Skipping eInk refresh, as requested."); 194 | return EXIT_SUCCESS; 195 | } 196 | 197 | // NOTE: Discard bogus regions, they can cause a softlock on some devices. 198 | // A 0x0 region is a no go on most devices, while a 1x1 region may only upset some Kindle models. 199 | // Some devices even balk at 1xN or Nx1, so, catch that, too. 200 | if (region.width <= 1 || region.height <= 1) { 201 | WARN("Discarding bogus empty region (%ux%u) to avoid a softlock", region.width, region.height); 202 | return ERRCODE(EXIT_FAILURE); 203 | } 204 | 205 | // NOTE: There are also a number of hardware quirks (which got better on newer devices) related to region alignment, 206 | // that the driver should already be taking care of... 207 | // c.f., epdc_process_update @ mxc_epdc_fb.c or mxc_epdc_v2_fb.c 208 | // If you see strays "unaligned" ... "Copying update before processing" entries in your dmesg, that's it. 209 | // I'm hoping everything handles this sanely, because I really don't want to duplicate the driver's job... 210 | 211 | // NOTE: While we'd be perfect candidates for using A2 waveform mode, it's all kinds of fucked up on Kobos, 212 | // and may lead to disappearing text or weird blending depending on the surrounding fb content... 213 | // It only shows up properly when FULL, which isn't great... 214 | 215 | // NOTE: On the Forma, the (apparently randomly) broken A2 behavior is exacerbated if the FB is UR @ 8bpp... 216 | // Which is intriguing, because that should make the driver's job easier (except maybe not on latest epdc v2 revs), 217 | // c.f., epdc_submit_work_func @ drivers/video/fbdev/mxc/mxc_epdc_v2_fb.c 218 | 219 | // NOTE: And while we're on the fun quirks train: FULL never flashes w/ AUTO on (some?) Kobos, 220 | // so request GC16 if we want a flash... 221 | 222 | // NOTE: FWIW, DU behaves properly when PARTIAL, but doesn't flash when FULL. 223 | // Which somewhat tracks given AUTO's behavior on Kobos, as well as on Kindles. 224 | // (i.e., DU or GC16 is most likely often what AUTO will land on). 225 | 226 | // So, handle this common switcheroo here... 227 | uint32_t wfm = (is_flashing && waveform_mode == WAVEFORM_MODE_AUTO) ? WAVEFORM_MODE_GC16 : waveform_mode; 228 | uint32_t upm = is_flashing ? UPDATE_MODE_FULL : UPDATE_MODE_PARTIAL; 229 | // We'll want to increment the marker on each subsequent calls (for API users) 230 | static uint32_t marker_counter = 0U; 231 | uint32_t marker = (uint32_t) getpid() + marker_counter; 232 | marker_counter++; 233 | 234 | // NOTE: Make sure update_marker is valid, an invalid marker *may* hang the kernel instead of failing gracefully, 235 | // depending on the device/FW... 236 | if (marker == 0U) { 237 | marker = (70U + 66U + 73U + 78U + 75U); 238 | } 239 | 240 | if (isKoboMk7) { 241 | return refresh_kobo_mk7(fbfd, region, wfm, upm, dithering_mode, is_nightmode, marker); 242 | } else { 243 | return refresh_kobo(fbfd, region, wfm, upm, is_nightmode, marker); 244 | } 245 | } 246 | 247 | // Convert our public HW_DITHER_INDEX_T values to an appropriate mxcfb dithering mode constant 248 | static int 249 | get_hwd_mode(uint8_t hw_dither_index) 250 | { 251 | // NOTE: This hardware dithering (handled by the PxP) is only supported since EPDC v2! 252 | // AFAICT, most of our eligible target devices only support PASSTHROUGH & ORDERED... 253 | // (c.f., drivers/dma/pxp/pxp_dma_v3.c) 254 | int dither_algo = EPDC_FLAG_USE_DITHERING_PASSTHROUGH; 255 | 256 | // Parse dithering algo... 257 | switch (hw_dither_index) { 258 | case HWD_PASSTHROUGH: 259 | dither_algo = EPDC_FLAG_USE_DITHERING_PASSTHROUGH; 260 | break; 261 | case HWD_FLOYD_STEINBERG: 262 | dither_algo = EPDC_FLAG_USE_DITHERING_FLOYD_STEINBERG; 263 | break; 264 | case HWD_ATKINSON: 265 | dither_algo = EPDC_FLAG_USE_DITHERING_ATKINSON; 266 | break; 267 | case HWD_ORDERED: 268 | dither_algo = EPDC_FLAG_USE_DITHERING_ORDERED; 269 | break; 270 | case HWD_QUANT_ONLY: 271 | dither_algo = EPDC_FLAG_USE_DITHERING_QUANT_ONLY; 272 | break; 273 | default: 274 | LOG("Unknown (or unsupported) dithering mode '%s' @ index %hhu, defaulting to PASSTHROUGH", 275 | hwd_to_string(hw_dither_index), 276 | hw_dither_index); 277 | dither_algo = EPDC_FLAG_USE_DITHERING_PASSTHROUGH; 278 | break; 279 | } 280 | 281 | return dither_algo; 282 | } 283 | 284 | 285 | // Convert our public WFM_MODE_INDEX_T values to an appropriate mxcfb waveform mode constant for the current device 286 | static uint32_t 287 | get_wfm_mode(uint8_t wfm_mode_index) 288 | { 289 | uint32_t waveform_mode = WAVEFORM_MODE_AUTO; 290 | 291 | // Parse waveform mode... 292 | switch (wfm_mode_index) { 293 | case WFM_AUTO: 294 | waveform_mode = WAVEFORM_MODE_AUTO; 295 | break; 296 | case WFM_DU: 297 | waveform_mode = WAVEFORM_MODE_DU; 298 | break; 299 | case WFM_GC16: 300 | waveform_mode = WAVEFORM_MODE_GC16; 301 | break; 302 | case WFM_GC4: 303 | waveform_mode = WAVEFORM_MODE_GC4; 304 | break; 305 | case WFM_A2: 306 | waveform_mode = WAVEFORM_MODE_A2; 307 | break; 308 | case WFM_GL16: 309 | waveform_mode = WAVEFORM_MODE_GL16; 310 | break; 311 | case WFM_REAGL: 312 | waveform_mode = WAVEFORM_MODE_REAGL; 313 | break; 314 | case WFM_REAGLD: 315 | waveform_mode = WAVEFORM_MODE_REAGLD; 316 | break; 317 | default: 318 | LOG("Unknown (or unsupported) waveform mode @ index %hhu, defaulting to AUTO", 319 | wfm_mode_index); 320 | waveform_mode = WAVEFORM_MODE_AUTO; 321 | break; 322 | } 323 | 324 | return waveform_mode; 325 | } 326 | 327 | // Tweak the region to cover the full screen 328 | static void 329 | fullscreen_region(struct mxcfb_rect* restrict region) 330 | { 331 | region->top = 0U; 332 | region->left = 0U; 333 | region->width = fb_cols(); 334 | region->height = fb_rows(); 335 | } 336 | 337 | // Small public wrapper around refresh(), without the caller having to depend on mxcfb headers 338 | int 339 | fbink_refresh(int fbfd, 340 | uint32_t region_top, 341 | uint32_t region_left, 342 | uint32_t region_width, 343 | uint32_t region_height, 344 | uint8_t dithering_mode) 345 | { 346 | // Open the framebuffer if need be (nonblock, we'll only do ioctls)... 347 | bool keep_fd = true; 348 | if (open_fb_fd_nonblock(&fbfd, &keep_fd) != EXIT_SUCCESS) { 349 | return ERRCODE(EXIT_FAILURE); 350 | } 351 | 352 | // Same for the dithering mode, if we actually requested dithering... 353 | int region_dither = EPDC_FLAG_USE_DITHERING_PASSTHROUGH; 354 | if (dithering_mode > 0U) { 355 | region_dither = get_hwd_mode(dithering_mode); 356 | } else { 357 | LOG("No hardware dithering requested"); 358 | } 359 | // NOTE: Right now, we enforce quant_bit to what appears to be sane values depending on the waveform mode. 360 | 361 | struct mxcfb_rect region = { 362 | .top = region_top, 363 | .left = region_left, 364 | .width = region_width, 365 | .height = region_height, 366 | }; 367 | 368 | // If region is empty, do a full-screen refresh! 369 | if (region.top == 0U && region.left == 0U && region.width == 0U && region.height == 0U) { 370 | fullscreen_region(®ion); 371 | } 372 | 373 | int ret; 374 | if (EXIT_SUCCESS != (ret = refresh(fbfd, 375 | region, 376 | get_wfm_mode(WFM_AUTO), //fbink_cfg->wfm_mode), 377 | region_dither, 378 | false,//fbink_cfg->is_nightmode, 379 | false,//fbink_cfg->is_flashing, 380 | false))) { 381 | WARN("Failed to refresh the screen"); 382 | } 383 | 384 | if (!keep_fd) { 385 | close(fbfd); 386 | } 387 | 388 | return ret; 389 | } 390 | -------------------------------------------------------------------------------- /term.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "conf.h" 13 | #include "fbpad.h" 14 | 15 | #include 16 | 17 | #define MODE_CURSOR 0x01 18 | #define MODE_WRAP 0x02 19 | #define MODE_ORIGIN 0x04 20 | #define MODE_AUTOCR 0x08 21 | #define ATTR_BOLD 0x10 22 | #define ATTR_ITALIC 0x20 23 | #define ATTR_REV 0x40 24 | #define ATTR_ALL (ATTR_BOLD | ATTR_ITALIC | ATTR_REV) 25 | #define MODE_INSERT 0x100 26 | #define MODE_WRAPREADY 0x200 27 | #define MODE_CLR8 0x400 /* colours 0-7 */ 28 | 29 | #define LIMIT(n, a, b) ((n) < (a) ? (a) : ((n) > (b) ? (b) : (n))) 30 | #define BIT_SET(i, b, val) ((val) ? ((i) | (b)) : ((i) & ~(b))) 31 | #define OFFSET(r, c) ((r) * pad_cols() + (c)) 32 | 33 | static struct term *term; 34 | static int *screen; 35 | static int *fgs, *bgs; 36 | static int *dirty; 37 | static int lazy; 38 | static int row, col; 39 | static int fg, bg; 40 | static int top, bot; 41 | static int mode; 42 | static int visible; 43 | 44 | static unsigned int clr16[16] = { 45 | COLOR0, COLOR1, COLOR2, COLOR3, COLOR4, COLOR5, COLOR6, COLOR7, 46 | COLOR8, COLOR9, COLORA, COLORB, COLORC, COLORD, COLORE, COLORF, 47 | }; 48 | 49 | static int clrmap(int c) 50 | { 51 | int g = (c - 232) * 10 + 8; 52 | if (c < 16) { 53 | return clr16[c]; 54 | } 55 | if (c < 232) { 56 | int ri = (c - 16) / 36 ; 57 | int gi = (c - 16) % 36 / 6; 58 | int bi = (c - 16) % 6; 59 | int rc = ri ? (ri * 40 + 55) : 0; 60 | int gc = gi ? (gi * 40 + 55) : 0; 61 | int bc = bi ? (bi * 40 + 55) : 0; 62 | return (rc << 16) | (gc << 8) | bc; 63 | } 64 | return (g << 16) | (g << 8) | g; 65 | } 66 | 67 | /* low level drawing and lazy updating */ 68 | 69 | static int fgcolor(void) 70 | { 71 | int c = mode & ATTR_REV ? bg : fg; 72 | if (mode & ATTR_BOLD) 73 | c |= FN_B; 74 | if (mode & ATTR_ITALIC) 75 | c |= FN_I; 76 | return c; 77 | } 78 | 79 | static int bgcolor(void) 80 | { 81 | return mode & ATTR_REV ? fg : bg; 82 | } 83 | 84 | /* assumes visible && !lazy */ 85 | static void _draw_pos(int r, int c, int cursor) 86 | { 87 | int rev = cursor && mode & MODE_CURSOR; 88 | int i = OFFSET(r, c); 89 | int fg = rev ? bgs[i] : fgs[i]; 90 | int bg = rev ? fgs[i] : bgs[i]; 91 | pad_put(screen[i], r, c, fg, bg); 92 | } 93 | 94 | /* assumes visible && !lazy */ 95 | static void _draw_row(int r) 96 | { 97 | int cbg, cch; /* current background and character */ 98 | int fbg, fsc = -1; /* filling background and start column */ 99 | int i; 100 | /* call pad_fill() only once for blank columns with identical backgrounds */ 101 | for (i = 0; i < pad_cols(); i++) { 102 | cbg = bgs[OFFSET(r, i)]; 103 | cch = screen[OFFSET(r, i)] ? screen[OFFSET(r, i)] : ' '; 104 | if (fsc >= 0 && (cbg != fbg || cch != ' ')) { 105 | pad_fill(r, r + 1, fsc, i, fbg & FN_C); 106 | fsc = -1; 107 | } 108 | if (cch != ' ') { 109 | _draw_pos(r, i, 0); 110 | } else if (fsc < 0) { 111 | fsc = i; 112 | fbg = cbg; 113 | } 114 | } 115 | pad_fill(r, r + 1, fsc >= 0 ? fsc : pad_cols(), -1, cbg & FN_C); 116 | } 117 | 118 | static int candraw(int sr, int er) 119 | { 120 | int i; 121 | if (lazy) 122 | for (i = sr; i < er; i++) 123 | dirty[i] = 1; 124 | return visible && !lazy; 125 | } 126 | 127 | static void draw_rows(int sr, int er) 128 | { 129 | int i; 130 | if (candraw(sr, er)) 131 | for (i = sr; i < er; i++) 132 | _draw_row(i); 133 | } 134 | 135 | static void draw_cols(int r, int sc, int ec) 136 | { 137 | int i; 138 | if (candraw(r, r + 1)) 139 | for (i = sc; i < ec; i++) 140 | _draw_pos(r, i, 0); 141 | } 142 | 143 | static void draw_char(int ch, int r, int c) 144 | { 145 | int i = OFFSET(r, c); 146 | screen[i] = ch; 147 | fgs[i] = fgcolor(); 148 | bgs[i] = bgcolor(); 149 | if (candraw(r, r + 1)) 150 | _draw_pos(r, c, 0); 151 | } 152 | 153 | static void draw_cursor(int put) 154 | { 155 | if (candraw(row, row + 1)) 156 | _draw_pos(row, col, put); 157 | } 158 | 159 | static void lazy_start(void) 160 | { 161 | memset(dirty, 0, pad_rows() * sizeof(*dirty)); 162 | lazy = 1; 163 | } 164 | 165 | static void lazy_flush(void) 166 | { 167 | int i; 168 | if (!visible || !lazy) 169 | return; 170 | for (i = 0; i < pad_rows(); i++) 171 | if (dirty[i]) 172 | _draw_row(i); 173 | if (dirty[row]) 174 | _draw_pos(row, col, 1); 175 | lazy = 0; 176 | term->hpos = 0; 177 | } 178 | 179 | static void screen_reset(int i, int n) 180 | { 181 | int c; 182 | candraw(i / pad_cols(), (i + n) / pad_cols() + 1); 183 | memset(screen + i, 0, n * sizeof(*screen)); 184 | for (c = 0; c < n; c++) 185 | fgs[i + c] = fg; 186 | for (c = 0; c < n; c++) 187 | bgs[i + c] = bg; 188 | } 189 | 190 | static void screen_move(int dst, int src, int n) 191 | { 192 | int srow = (MIN(src, dst) + (n > 0 ? 0 : n)) / pad_cols(); 193 | int drow = (MAX(src, dst) + (n > 0 ? n : 0)) / pad_cols() + 1; 194 | candraw(srow, drow); 195 | memmove(screen + dst, screen + src, n * sizeof(*screen)); 196 | memmove(fgs + dst, fgs + src, n * sizeof(*fgs)); 197 | memmove(bgs + dst, bgs + src, n * sizeof(*bgs)); 198 | } 199 | 200 | /* terminal input buffering */ 201 | 202 | #define PTYLEN (1 << 16) 203 | 204 | static char ptybuf[PTYLEN]; /* always emptied in term_read() */ 205 | static int ptylen; /* buffer length */ 206 | static int ptycur; /* current offset */ 207 | 208 | static int waitpty(int us) 209 | { 210 | struct pollfd ufds[1]; 211 | ufds[0].fd = term->fd; 212 | ufds[0].events = POLLIN; 213 | return poll(ufds, 1, us) <= 0; 214 | } 215 | 216 | static int readpty(void) 217 | { 218 | int nr; 219 | if (ptycur < ptylen) 220 | return (unsigned char) ptybuf[ptycur++]; 221 | if (!term->fd) 222 | return -1; 223 | ptylen = 0; 224 | while ((nr = read(term->fd, ptybuf + ptylen, PTYLEN - ptylen)) > 0) 225 | ptylen += nr; 226 | if (!ptylen && errno == EAGAIN && !waitpty(100)) 227 | ptylen = read(term->fd, ptybuf, PTYLEN); 228 | ptycur = 1; 229 | return ptylen > 0 ? (unsigned char) ptybuf[0] : -1; 230 | } 231 | 232 | /* term interface functions */ 233 | 234 | void term_send(int c) 235 | { 236 | char b = c; 237 | if (term->fd) 238 | write(term->fd, &b, 1); 239 | } 240 | 241 | static void term_sendstr(char *s) 242 | { 243 | if (term->fd) 244 | write(term->fd, s, strlen(s)); 245 | } 246 | 247 | static void term_blank(void) 248 | { 249 | screen_reset(0, pad_rows() * pad_cols()); 250 | if (visible) 251 | pad_fill(0, -1, 0, -1, bgcolor() & FN_C); 252 | } 253 | 254 | static void ctlseq(void); 255 | void term_read(void) 256 | { 257 | ctlseq(); 258 | while (ptycur < ptylen) { 259 | if (visible && !lazy && ptylen - ptycur > 15) 260 | lazy_start(); 261 | ctlseq(); 262 | } 263 | lazy_flush(); 264 | } 265 | 266 | static void term_reset(void) 267 | { 268 | row = col = 0; 269 | top = 0; 270 | bot = pad_rows(); 271 | mode = MODE_CURSOR | MODE_WRAP | MODE_CLR8; 272 | fg = FGCOLOR; 273 | bg = BGCOLOR; 274 | term_blank(); 275 | } 276 | 277 | static int _openpty(int *master, int *slave) 278 | { 279 | return openpty(master, slave, NULL, NULL, NULL ); 280 | /* 281 | int unlock = 0; 282 | int ptyno = 0; 283 | char name[20]; 284 | if ((*master = open("/dev/ptmx", O_RDWR)) == -1) 285 | return -1; 286 | if (ioctl(*master, TIOCSPTLCK, &unlock) == -1) 287 | return -1; 288 | if (ioctl(*master, TIOCGPTN, &ptyno) == -1) 289 | return -1; 290 | sprintf(name, "/dev/pts/%d", ptyno); 291 | *slave = open(name, O_RDWR | O_NOCTTY); 292 | return 0;*/ 293 | } 294 | 295 | static void _login(int fd) 296 | { 297 | struct winsize winp; 298 | winp.ws_col = pad_cols(); 299 | winp.ws_row = pad_rows(); 300 | winp.ws_xpixel = 0; 301 | winp.ws_ypixel = 0; 302 | setsid(); 303 | ioctl(fd, TIOCSCTTY, NULL); 304 | ioctl(fd, TIOCSWINSZ, &winp); 305 | dup2(fd, 0); 306 | dup2(fd, 1); 307 | dup2(fd, 2); 308 | if (fd > 2) 309 | close(fd); 310 | } 311 | 312 | #define MAXENV (1 << 8) 313 | 314 | static void envcpy(char **d, char **s) 315 | { 316 | while (*s) 317 | *d++ = *s++; 318 | *d = NULL; 319 | } 320 | 321 | static void execvep(char *cmd, char **argv, char **envp) 322 | { 323 | char path[512]; 324 | char *p = getenv("PATH"); 325 | execve(cmd, argv, envp); 326 | while (*p) { 327 | char *s = path; 328 | while (*p && *p != ':') 329 | *s++ = *p++; 330 | *s++ = '/'; 331 | strcpy(s, cmd); 332 | execve(path, argv, envp); 333 | while (*p == ':') 334 | p++; 335 | } 336 | } 337 | 338 | extern char **environ; 339 | void term_exec(char **args) 340 | { 341 | int master, slave; 342 | memset(term, 0, sizeof(*term)); 343 | if (_openpty(&master, &slave) == -1) 344 | return; 345 | if ((term->pid = fork()) == -1) 346 | return; 347 | if (!term->pid) { 348 | char *envp[MAXENV] = {"TERM=" TERM}; 349 | envcpy(envp + 1, environ); 350 | _login(slave); 351 | close(master); 352 | execvep(args[0], args, envp); 353 | exit(1); 354 | } 355 | close(slave); 356 | term->fd = master; 357 | fcntl(term->fd, F_SETFD, fcntl(term->fd, F_GETFD) | FD_CLOEXEC); 358 | fcntl(term->fd, F_SETFL, fcntl(term->fd, F_GETFL) | O_NONBLOCK); 359 | term_reset(); 360 | memset(term->hist, 0, sizeof(term->hist)); 361 | } 362 | 363 | static void misc_save(struct term_state *state) 364 | { 365 | state->row = row; 366 | state->col = col; 367 | state->fg = fg; 368 | state->bg = bg; 369 | state->mode = mode; 370 | } 371 | 372 | static void misc_load(struct term_state *state) 373 | { 374 | row = state->row; 375 | col = state->col; 376 | fg = state->fg; 377 | bg = state->bg; 378 | mode = state->mode; 379 | } 380 | 381 | void term_save(struct term *term) 382 | { 383 | visible = 0; 384 | if (!lazy) 385 | lazy_start(); 386 | misc_save(&term->cur); 387 | term->top = top; 388 | term->bot = bot; 389 | term->lazy = lazy; 390 | } 391 | 392 | /* redraw the screen; if all is zero, update changed lines only */ 393 | void term_redraw(int all) 394 | { 395 | if (term->fd) { 396 | if (all) { 397 | pad_fill(pad_rows(), -1, 0, -1, BGCOLOR); 398 | lazy_start(); 399 | memset(dirty, 1, pad_rows() * sizeof(*dirty)); 400 | } 401 | if (all || !term->hpos) 402 | lazy_flush(); 403 | } else { 404 | if (all) 405 | pad_fill(0, -1, 0, -1, 0); 406 | } 407 | } 408 | 409 | void term_load(struct term *t, int flags) 410 | { 411 | term = t; 412 | misc_load(&term->cur); 413 | screen = term->screen; 414 | fgs = term->fgs; 415 | bgs = term->bgs; 416 | visible = flags; 417 | top = term->top; 418 | bot = term->bot; 419 | lazy = term->lazy; 420 | dirty = term->dirty; 421 | } 422 | 423 | void term_end(void) 424 | { 425 | if (term->fd) 426 | close(term->fd); 427 | memset(term, 0, sizeof(*term)); 428 | term_load(term, visible); 429 | if (visible) 430 | term_redraw(1); 431 | } 432 | 433 | static int writeutf8(char *dst, int c) 434 | { 435 | char *d = dst; 436 | int l; 437 | if (c > 0xffff) { 438 | *d++ = 0xf0 | (c >> 18); 439 | l = 3; 440 | } else if (c > 0x7ff) { 441 | *d++ = 0xe0 | (c >> 12); 442 | l = 2; 443 | } else if (c > 0x7f) { 444 | *d++ = 0xc0 | (c >> 6); 445 | l = 1; 446 | } else { 447 | *d++ = c > 0 ? c : ' '; 448 | l = 0; 449 | } 450 | while (l--) 451 | *d++ = 0x80 | ((c >> (l * 6)) & 0x3f); 452 | return d - dst; 453 | } 454 | 455 | void term_screenshot(void) 456 | { 457 | char buf[1 << 11]; 458 | int fd = open(SCRSHOT, O_CREAT | O_TRUNC | O_WRONLY, 0600); 459 | int i, j; 460 | for (i = 0; i < pad_rows(); i++) { 461 | char *s = buf; 462 | for (j = 0; j < pad_cols(); j++) 463 | if (~screen[OFFSET(i, j)] & DWCHAR) 464 | s += writeutf8(s, screen[OFFSET(i, j)]); 465 | *s++ = '\n'; 466 | write(fd, buf, s - buf); 467 | } 468 | close(fd); 469 | } 470 | 471 | /* high-level drawing functions */ 472 | 473 | static void empty_rows(int sr, int er) 474 | { 475 | screen_reset(OFFSET(sr, 0), (er - sr) * pad_cols()); 476 | } 477 | 478 | static void blank_rows(int sr, int er) 479 | { 480 | empty_rows(sr, er); 481 | draw_rows(sr, er); 482 | draw_cursor(1); 483 | } 484 | 485 | #define HISTROW(pos) (term->hist + ((term->hrow + NHIST - (pos)) % NHIST) * pad_cols()) 486 | 487 | static void scrl_rows(int nr) 488 | { 489 | int i; 490 | for (i = 0; i < nr; i++) { 491 | memcpy(HISTROW(0), screen + i * pad_cols(), 492 | pad_cols() * sizeof(screen[0])); 493 | term->hrow = (term->hrow + 1) % NHIST; 494 | } 495 | } 496 | 497 | void term_scrl(int scrl) 498 | { 499 | int i, j; 500 | int hpos = LIMIT(term->hpos + scrl, 0, NHIST); 501 | term->hpos = hpos; 502 | if (!hpos) { 503 | lazy_flush(); 504 | return; 505 | } 506 | lazy_start(); 507 | memset(dirty, 1, pad_rows() * sizeof(*dirty)); 508 | for (i = 0; i < pad_rows(); i++) { 509 | int off = (i - hpos) * pad_cols(); 510 | int *_scr = i < hpos ? HISTROW(hpos - i) : term->screen + off; 511 | int *_fgs = i < hpos ? NULL : term->fgs + off; 512 | int *_bgs = i < hpos ? NULL : term->bgs + off; 513 | for (j = 0; j < pad_cols(); j++) 514 | pad_put(_scr[j], i, j, _fgs ? _fgs[j] : BGCOLOR, 515 | _bgs ? _bgs[j] : FGCOLOR); 516 | } 517 | } 518 | 519 | static void scroll_screen(int sr, int nr, int n) 520 | { 521 | draw_cursor(0); 522 | if (sr + n == 0) 523 | scrl_rows(sr); 524 | screen_move(OFFSET(sr + n, 0), OFFSET(sr, 0), nr * pad_cols()); 525 | if (n > 0) 526 | empty_rows(sr, sr + n); 527 | else 528 | empty_rows(sr + nr + n, sr + nr); 529 | draw_rows(MIN(sr, sr + n), MAX(sr + nr, sr + nr + n)); 530 | draw_cursor(1); 531 | } 532 | 533 | static void insert_lines(int n) 534 | { 535 | int sr = MAX(top, row); 536 | int nr = bot - row - n; 537 | if (nr > 0) 538 | scroll_screen(sr, nr, n); 539 | } 540 | 541 | static void delete_lines(int n) 542 | { 543 | int r = MAX(top, row); 544 | int sr = r + n; 545 | int nr = bot - r - n; 546 | if (nr > 0) 547 | scroll_screen(sr, nr, -n); 548 | } 549 | 550 | static int origin(void) 551 | { 552 | return mode & MODE_ORIGIN; 553 | } 554 | 555 | static void move_cursor(int r, int c) 556 | { 557 | int t, b; 558 | draw_cursor(0); 559 | t = origin() ? top : 0; 560 | b = origin() ? bot : pad_rows(); 561 | row = LIMIT(r, t, b - 1); 562 | col = LIMIT(c, 0, pad_cols() - 1); 563 | draw_cursor(1); 564 | mode = BIT_SET(mode, MODE_WRAPREADY, 0); 565 | } 566 | 567 | static void set_region(int t, int b) 568 | { 569 | top = LIMIT(t - 1, 0, pad_rows() - 1); 570 | bot = LIMIT(b ? b : pad_rows(), top + 1, pad_rows()); 571 | if (origin()) 572 | move_cursor(top, 0); 573 | } 574 | 575 | static void setattr(int m) 576 | { 577 | if (!m || (m / 10) == 3) 578 | mode |= MODE_CLR8; 579 | switch (m) { 580 | case 0: 581 | fg = FGCOLOR; 582 | bg = BGCOLOR; 583 | mode &= ~ATTR_ALL; 584 | break; 585 | case 1: 586 | mode |= ATTR_BOLD; 587 | break; 588 | case 3: 589 | mode |= ATTR_ITALIC; 590 | break; 591 | case 7: 592 | mode |= ATTR_REV; 593 | break; 594 | case 22: 595 | mode &= ~ATTR_BOLD; 596 | break; 597 | case 23: 598 | mode &= ~ATTR_ITALIC; 599 | break; 600 | case 27: 601 | mode &= ~ATTR_REV; 602 | break; 603 | default: 604 | if ((m / 10) == 3) 605 | fg = m > 37 ? FGCOLOR : clrmap(m - 30); 606 | if ((m / 10) == 4) 607 | bg = m > 47 ? BGCOLOR : clrmap(m - 40); 608 | if ((m / 10) == 9) 609 | fg = clrmap(8 + m - 90); 610 | if ((m / 10) == 10) 611 | bg = clrmap(8 + m - 100); 612 | } 613 | } 614 | 615 | static void kill_chars(int sc, int ec) 616 | { 617 | int i; 618 | for (i = sc; i < ec; i++) 619 | draw_char(0, row, i); 620 | draw_cursor(1); 621 | } 622 | 623 | static void move_chars(int sc, int nc, int n) 624 | { 625 | draw_cursor(0); 626 | screen_move(OFFSET(row, sc + n), OFFSET(row, sc), nc); 627 | if (n > 0) 628 | screen_reset(OFFSET(row, sc), n); 629 | else 630 | screen_reset(OFFSET(row, pad_cols() + n), -n); 631 | draw_cols(row, MIN(sc, sc + n), pad_cols()); 632 | draw_cursor(1); 633 | } 634 | 635 | static void delete_chars(int n) 636 | { 637 | int sc = col + n; 638 | int nc = pad_cols() - sc; 639 | move_chars(sc, nc, -n); 640 | } 641 | 642 | static void insert_chars(int n) 643 | { 644 | int nc = pad_cols() - col - n; 645 | move_chars(col, nc, n); 646 | } 647 | 648 | static void advance(int dr, int dc, int scrl) 649 | { 650 | int r = row + dr; 651 | int c = col + dc; 652 | if (dr && r >= bot && scrl) { 653 | int n = bot - r - 1; 654 | int nr = (bot - top) + n; 655 | if (nr > 0) 656 | scroll_screen(top + -n, nr, n); 657 | } 658 | if (dr && r < top && scrl) { 659 | int n = top - r; 660 | int nr = (bot - top) - n; 661 | if (nr > 0) 662 | scroll_screen(top, nr, n); 663 | } 664 | r = dr ? LIMIT(r, top, bot - 1) : r; 665 | c = LIMIT(c, 0, pad_cols() - 1); 666 | move_cursor(r, c); 667 | } 668 | 669 | static void insertchar(int c) 670 | { 671 | if (mode & MODE_WRAPREADY) 672 | advance(1, -col, 1); 673 | if (mode & MODE_INSERT) 674 | insert_chars(1); 675 | draw_char(c, row, col); 676 | if (col == pad_cols() - 1) 677 | mode = BIT_SET(mode, MODE_WRAPREADY, 1); 678 | else 679 | advance(0, 1, 1); 680 | } 681 | 682 | 683 | /* partial vt102 implementation */ 684 | 685 | static void escseq(void); 686 | static void escseq_cs(void); 687 | static void escseq_g0(void); 688 | static void escseq_g1(void); 689 | static void escseq_g2(void); 690 | static void escseq_g3(void); 691 | static void csiseq(void); 692 | static void csiseq_da(int c); 693 | static void csiseq_dsr(int c); 694 | static void modeseq(int c, int set); 695 | 696 | /* comments taken from: http://www.ivarch.com/programs/termvt102.shtml */ 697 | 698 | static int readutf8(int c) 699 | { 700 | int result; 701 | int l = 1; 702 | if (~c & 0xc0) 703 | return c; 704 | while (l < 6 && c & (0x40 >> l)) 705 | l++; 706 | result = (0x3f >> l) & c; 707 | while (l--) 708 | result = (result << 6) | (readpty() & 0x3f); 709 | return result; 710 | } 711 | 712 | #define unknown(ctl, c) 713 | 714 | /* control sequences */ 715 | static void ctlseq(void) 716 | { 717 | int c = readpty(); 718 | switch (c) { 719 | case 0x09: /* HT horizontal tab to next tab stop */ 720 | advance(0, 8 - col % 8, 0); 721 | break; 722 | case 0x0a: /* LF line feed */ 723 | case 0x0b: /* VT line feed */ 724 | case 0x0c: /* FF line feed */ 725 | advance(1, (mode & MODE_AUTOCR) ? -col : 0, 1); 726 | break; 727 | case 0x08: /* BS backspace one column */ 728 | advance(0, -1, 0); 729 | break; 730 | case 0x1b: /* ESC start escape sequence */ 731 | escseq(); 732 | break; 733 | case 0x0d: /* CR carriage return */ 734 | advance(0, -col, 0); 735 | break; 736 | case 0x9b: /* CSI equivalent to ESC [ */ 737 | csiseq(); 738 | break; 739 | case 0x00: /* NUL ignored */ 740 | case 0x07: /* BEL beep */ 741 | case 0x7f: /* DEL ignored */ 742 | break; 743 | case 0x05: /* ENQ trigger answerback message */ 744 | case 0x0e: /* SO activate G1 character set & newline */ 745 | case 0x0f: /* SI activate G0 character set */ 746 | case 0x11: /* XON resume transmission */ 747 | case 0x13: /* XOFF stop transmission, ignore characters */ 748 | case 0x18: /* CAN interrupt escape sequence */ 749 | case 0x1a: /* SUB interrupt escape sequence */ 750 | unknown("ctlseq", c); 751 | break; 752 | default: 753 | c = readutf8(c); 754 | if (isdw(c) && col + 1 == pad_cols() && ~mode & MODE_WRAPREADY) 755 | insertchar(0); 756 | if (!iszw(c)) 757 | insertchar(c); 758 | if (isdw(c)) 759 | insertchar(c | DWCHAR); 760 | break; 761 | } 762 | } 763 | 764 | #define ESCM(c) (((c) & 0xf0) == 0x20) 765 | #define ESCF(c) ((c) > 0x30 && (c) < 0x7f) 766 | 767 | /* escape sequences */ 768 | static void escseq(void) 769 | { 770 | int c = readpty(); 771 | while (ESCM(c)) 772 | c = readpty(); 773 | switch (c) { 774 | case '[': /* CSI control sequence introducer */ 775 | csiseq(); 776 | break; 777 | case '%': /* CS... escseq_cs table */ 778 | escseq_cs(); 779 | break; 780 | case '(': /* G0... escseq_g0 table */ 781 | escseq_g0(); 782 | break; 783 | case ')': /* G1... escseq_g1 table */ 784 | escseq_g1(); 785 | break; 786 | case '*': /* G2... escseq_g2 table */ 787 | escseq_g2(); 788 | break; 789 | case '+': /* G3... escseq_g3 table */ 790 | escseq_g3(); 791 | break; 792 | case '7': /* DECSC save state (position, charset, attributes) */ 793 | misc_save(&term->sav); 794 | break; 795 | case '8': /* DECRC restore most recently saved state */ 796 | misc_load(&term->sav); 797 | break; 798 | case 'M': /* RI reverse line feed */ 799 | advance(-1, 0, 1); 800 | break; 801 | case 'D': /* IND line feed */ 802 | advance(1, 0, 1); 803 | break; 804 | case 'E': /* NEL newline */ 805 | advance(1, -col, 1); 806 | break; 807 | case 'c': /* RIS reset */ 808 | term_reset(); 809 | break; 810 | case 'H': /* HTS set tab stop at current column */ 811 | case 'Z': /* DECID DEC private ID; return ESC [ ? 6 c (VT102) */ 812 | case '#': /* DECALN ("#8") DEC alignment test - fill screen with E's */ 813 | case '>': /* DECPNM set numeric keypad mode */ 814 | case '=': /* DECPAM set application keypad mode */ 815 | case 'N': /* SS2 select G2 charset for next char only */ 816 | case 'O': /* SS3 select G3 charset for next char only */ 817 | case 'P': /* DCS device control string (ended by ST) */ 818 | case 'X': /* SOS start of string */ 819 | case '^': /* PM privacy message (ended by ST) */ 820 | case '_': /* APC application program command (ended by ST) */ 821 | case '\\': /* ST string terminator */ 822 | case 'n': /* LS2 invoke G2 charset */ 823 | case 'o': /* LS3 invoke G3 charset */ 824 | case '|': /* LS3R invoke G3 charset as GR */ 825 | case '}': /* LS2R invoke G2 charset as GR */ 826 | case '~': /* LS1R invoke G1 charset as GR */ 827 | case ']': /* OSC operating system command */ 828 | case 'g': /* BEL alternate BEL */ 829 | default: 830 | unknown("escseq", c); 831 | break; 832 | } 833 | } 834 | 835 | static void escseq_cs(void) 836 | { 837 | int c = readpty(); 838 | switch (c) { 839 | case '@': /* CSDFL select default charset (ISO646/8859-1) */ 840 | case 'G': /* CSUTF8 select UTF-8 */ 841 | case '8': /* CSUTF8 select UTF-8 (obsolete) */ 842 | default: 843 | unknown("escseq_cs", c); 844 | break; 845 | } 846 | } 847 | 848 | static void escseq_g0(void) 849 | { 850 | int c = readpty(); 851 | switch (c) { 852 | case '8': /* G0DFL G0 charset = default mapping (ISO8859-1) */ 853 | case '0': /* G0GFX G0 charset = VT100 graphics mapping */ 854 | case 'U': /* G0ROM G0 charset = null mapping (straight to ROM) */ 855 | case 'K': /* G0USR G0 charset = user defined mapping */ 856 | case 'B': /* G0TXT G0 charset = ASCII mapping */ 857 | default: 858 | unknown("escseq_g0", c); 859 | break; 860 | } 861 | } 862 | 863 | static void escseq_g1(void) 864 | { 865 | int c = readpty(); 866 | switch (c) { 867 | case '8': /* G1DFL G1 charset = default mapping (ISO8859-1) */ 868 | case '0': /* G1GFX G1 charset = VT100 graphics mapping */ 869 | case 'U': /* G1ROM G1 charset = null mapping (straight to ROM) */ 870 | case 'K': /* G1USR G1 charset = user defined mapping */ 871 | case 'B': /* G1TXT G1 charset = ASCII mapping */ 872 | default: 873 | unknown("escseq_g1", c); 874 | break; 875 | } 876 | } 877 | 878 | static void escseq_g2(void) 879 | { 880 | int c = readpty(); 881 | switch (c) { 882 | case '8': /* G2DFL G2 charset = default mapping (ISO8859-1) */ 883 | case '0': /* G2GFX G2 charset = VT100 graphics mapping */ 884 | case 'U': /* G2ROM G2 charset = null mapping (straight to ROM) */ 885 | case 'K': /* G2USR G2 charset = user defined mapping */ 886 | default: 887 | unknown("escseq_g2", c); 888 | break; 889 | } 890 | } 891 | 892 | static void escseq_g3(void) 893 | { 894 | int c = readpty(); 895 | switch (c) { 896 | case '8': /* G3DFL G3 charset = default mapping (ISO8859-1) */ 897 | case '0': /* G3GFX G3 charset = VT100 graphics mapping */ 898 | case 'U': /* G3ROM G3 charset = null mapping (straight to ROM) */ 899 | case 'K': /* G3USR G3 charset = user defined mapping */ 900 | default: 901 | unknown("escseq_g3", c); 902 | break; 903 | } 904 | } 905 | 906 | static int absrow(int r) 907 | { 908 | return origin() ? top + r : r; 909 | } 910 | 911 | #define CSIP(c) (((c) & 0xf0) == 0x30) 912 | #define CSII(c) (((c) & 0xf0) == 0x20) 913 | #define CSIF(c) ((c) >= 0x40 && (c) < 0x80) 914 | 915 | #define MAXCSIARGS 32 916 | /* ECMA-48 CSI sequences */ 917 | static void csiseq(void) 918 | { 919 | int args[MAXCSIARGS + 8] = {0}; 920 | int i; 921 | int n = 0; 922 | int c = readpty(); 923 | int priv = 0; 924 | 925 | if (strchr("<=>?", c)) { 926 | priv = c; 927 | c = readpty(); 928 | } 929 | while (CSIP(c)) { 930 | int arg = 0; 931 | while (isdigit(c)) { 932 | arg = arg * 10 + (c - '0'); 933 | c = readpty(); 934 | } 935 | if (CSIP(c)) 936 | c = readpty(); 937 | if (n < MAXCSIARGS) 938 | args[n++] = arg; 939 | } 940 | while (CSII(c)) 941 | c = readpty(); 942 | switch (c) { 943 | case 'H': /* CUP move cursor to row, column */ 944 | case 'f': /* HVP move cursor to row, column */ 945 | move_cursor(absrow(MAX(0, args[0] - 1)), MAX(0, args[1] - 1)); 946 | break; 947 | case 'J': /* ED erase display */ 948 | switch (args[0]) { 949 | case 0: 950 | kill_chars(col, pad_cols()); 951 | blank_rows(row + 1, pad_rows()); 952 | break; 953 | case 1: 954 | kill_chars(0, col + 1); 955 | blank_rows(0, row - 1); 956 | break; 957 | case 2: 958 | term_blank(); 959 | break; 960 | } 961 | break; 962 | case 'A': /* CUU move cursor up */ 963 | advance(-MAX(1, args[0]), 0, 0); 964 | break; 965 | case 'e': /* VPR move cursor down */ 966 | case 'B': /* CUD move cursor down */ 967 | advance(MAX(1, args[0]), 0, 0); 968 | break; 969 | case 'a': /* HPR move cursor right */ 970 | case 'C': /* CUF move cursor right */ 971 | advance(0, MAX(1, args[0]), 0); 972 | break; 973 | case 'D': /* CUB move cursor left */ 974 | advance(0, -MAX(1, args[0]), 0); 975 | break; 976 | case 'K': /* EL erase line */ 977 | switch (args[0]) { 978 | case 0: 979 | kill_chars(col, pad_cols()); 980 | break; 981 | case 1: 982 | kill_chars(0, col + 1); 983 | break; 984 | case 2: 985 | kill_chars(0, pad_cols()); 986 | break; 987 | } 988 | break; 989 | case 'L': /* IL insert blank lines */ 990 | if (row >= top && row < bot) 991 | insert_lines(MAX(1, args[0])); 992 | break; 993 | case 'M': /* DL delete lines */ 994 | if (row >= top && row < bot) 995 | delete_lines(MAX(1, args[0])); 996 | break; 997 | case 'd': /* VPA move to row (current column) */ 998 | move_cursor(absrow(MAX(1, args[0]) - 1), col); 999 | break; 1000 | case 'm': /* SGR set graphic rendition */ 1001 | if (!n) 1002 | setattr(0); 1003 | for (i = 0; i < n; i++) { 1004 | if (args[i] == 38 && args[i + 1] == 2) { 1005 | mode &= ~MODE_CLR8; 1006 | fg = (args[i + 2] << 16) | 1007 | (args[i + 3] << 8) | args[i + 4]; 1008 | i += 5; 1009 | continue; 1010 | } 1011 | if (args[i] == 38) { 1012 | mode &= ~MODE_CLR8; 1013 | fg = clrmap(args[i + 2]); 1014 | i += 2; 1015 | continue; 1016 | } 1017 | if (args[i] == 48 && args[i + 1] == 2) { 1018 | bg = (args[i + 2] << 16) | 1019 | (args[i + 3] << 8) | args[i + 4]; 1020 | i += 5; 1021 | continue; 1022 | } 1023 | if (args[i] == 48) { 1024 | bg = clrmap(args[i + 2]); 1025 | i += 2; 1026 | continue; 1027 | } 1028 | setattr(args[i]); 1029 | } 1030 | if (mode & MODE_CLR8 && mode & ATTR_BOLD && BRIGHTEN) 1031 | for (i = 0; i < 8; i++) 1032 | if (clr16[i] == fg) 1033 | fg = clr16[8 + i]; 1034 | break; 1035 | case 'r': /* DECSTBM set scrolling region to (top, bottom) rows */ 1036 | set_region(args[0], args[1]); 1037 | break; 1038 | case 'c': /* DA return ESC [ ? 6 c (VT102) */ 1039 | csiseq_da(priv == '?' ? args[0] | 0x80 : args[0]); 1040 | break; 1041 | case 'h': /* SM set mode */ 1042 | for (i = 0; i < n; i++) 1043 | modeseq(priv == '?' ? args[i] | 0x80 : args[i], 1); 1044 | draw_cursor(1); 1045 | break; 1046 | case 'l': /* RM reset mode */ 1047 | for (i = 0; i < n; i++) 1048 | modeseq(priv == '?' ? args[i] | 0x80 : args[i], 0); 1049 | draw_cursor(1); 1050 | break; 1051 | case 'P': /* DCH delete characters on current line */ 1052 | delete_chars(LIMIT(args[0], 1, pad_cols() - col)); 1053 | break; 1054 | case '@': /* ICH insert blank characters */ 1055 | insert_chars(LIMIT(args[0], 1, pad_cols() - col)); 1056 | break; 1057 | case 'n': /* DSR device status report */ 1058 | csiseq_dsr(args[0]); 1059 | break; 1060 | case 'G': /* CHA move cursor to column in current row */ 1061 | advance(0, MAX(0, args[0] - 1) - col, 0); 1062 | break; 1063 | case 'X': /* ECH erase characters on current line */ 1064 | kill_chars(col, MIN(col + MAX(1, args[0]), pad_cols())); 1065 | break; 1066 | case '[': /* IGN ignored control sequence */ 1067 | case 'E': /* CNL move cursor down and to column 1 */ 1068 | case 'F': /* CPL move cursor up and to column 1 */ 1069 | case 'g': /* TBC clear tab stop (CSI 3 g = clear all stops) */ 1070 | case 'q': /* DECLL set keyboard LEDs */ 1071 | case 's': /* CUPSV save cursor position */ 1072 | case 'u': /* CUPRS restore cursor position */ 1073 | case '`': /* HPA move cursor to column in current row */ 1074 | default: 1075 | unknown("csiseq", c); 1076 | break; 1077 | } 1078 | } 1079 | 1080 | static void csiseq_da(int c) 1081 | { 1082 | switch (c) { 1083 | case 0x00: 1084 | term_sendstr("\x1b[?6c"); 1085 | break; 1086 | default: 1087 | /* we don't care much about cursor shape */ 1088 | /* printf("csiseq_da <0x%x>\n", c); */ 1089 | break; 1090 | } 1091 | } 1092 | 1093 | static void csiseq_dsr(int c) 1094 | { 1095 | char status[1 << 5]; 1096 | switch (c) { 1097 | case 0x05: 1098 | term_sendstr("\x1b[0n"); 1099 | break; 1100 | case 0x06: 1101 | sprintf(status, "\x1b[%d;%dR", 1102 | (origin() ? row - top : row) + 1, col + 1); 1103 | term_sendstr(status); 1104 | break; 1105 | default: 1106 | unknown("csiseq_dsr", c); 1107 | break; 1108 | } 1109 | } 1110 | 1111 | /* ANSI/DEC specified modes for SM/RM ANSI Specified Modes */ 1112 | static void modeseq(int c, int set) 1113 | { 1114 | switch (c) { 1115 | case 0x87: /* DECAWM Auto Wrap */ 1116 | mode = BIT_SET(mode, MODE_WRAP, set); 1117 | break; 1118 | case 0x99: /* DECTCEM Cursor on (set); Cursor off (reset) */ 1119 | mode = BIT_SET(mode, MODE_CURSOR, set); 1120 | break; 1121 | case 0x86: /* DECOM Sets relative coordinates (set); Sets absolute coordinates (reset) */ 1122 | mode = BIT_SET(mode, MODE_ORIGIN, set); 1123 | break; 1124 | case 0x14: /* LNM Line Feed / New Line Mode */ 1125 | mode = BIT_SET(mode, MODE_AUTOCR, set); 1126 | break; 1127 | case 0x04: /* IRM insertion/replacement mode (always reset) */ 1128 | mode = BIT_SET(mode, MODE_INSERT, set); 1129 | break; 1130 | case 0x00: /* IGN Error (Ignored) */ 1131 | case 0x01: /* GATM guarded-area transfer mode (ignored) */ 1132 | case 0x02: /* KAM keyboard action mode (always reset) */ 1133 | case 0x03: /* CRM control representation mode (always reset) */ 1134 | case 0x05: /* SRTM status-reporting transfer mode */ 1135 | case 0x06: /* ERM erasure mode (always set) */ 1136 | case 0x07: /* VEM vertical editing mode (ignored) */ 1137 | case 0x0a: /* HEM horizontal editing mode */ 1138 | case 0x0b: /* PUM positioning unit mode */ 1139 | case 0x0c: /* SRM send/receive mode (echo on/off) */ 1140 | case 0x0d: /* FEAM format effector action mode */ 1141 | case 0x0e: /* FETM format effector transfer mode */ 1142 | case 0x0f: /* MATM multiple area transfer mode */ 1143 | case 0x10: /* TTM transfer termination mode */ 1144 | case 0x11: /* SATM selected area transfer mode */ 1145 | case 0x12: /* TSM tabulation stop mode */ 1146 | case 0x13: /* EBM editing boundary mode */ 1147 | /* DEC Private Modes: "?NUM" -> (NUM | 0x80) */ 1148 | case 0x80: /* IGN Error (Ignored) */ 1149 | case 0x81: /* DECCKM Cursorkeys application (set); Cursorkeys normal (reset) */ 1150 | case 0x82: /* DECANM ANSI (set); VT52 (reset) */ 1151 | case 0x83: /* DECCOLM 132 columns (set); 80 columns (reset) */ 1152 | case 0x84: /* DECSCLM Jump scroll (set); Smooth scroll (reset) */ 1153 | case 0x85: /* DECSCNM Reverse screen (set); Normal screen (reset) */ 1154 | case 0x88: /* DECARM Auto Repeat */ 1155 | case 0x89: /* DECINLM Interlace */ 1156 | case 0x92: /* DECPFF Send FF to printer after print screen (set); No char after PS (reset) */ 1157 | case 0x93: /* DECPEX Print screen: prints full screen (set); prints scroll region (reset) */ 1158 | default: 1159 | unknown("modeseq", c); 1160 | break; 1161 | } 1162 | } 1163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | --------------------------------------------------------------------------------