1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/ioctl.h> 32 33 #include <assert.h> 34 #include <errno.h> 35 #include <inttypes.h> 36 #include <locale.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 41 #include <ncurses.h> 42 #if defined(__FreeBSD__) 43 #include <libutil.h> 44 #elif defined(__linux__) 45 #include <pty.h> 46 #else 47 #include <util.h> 48 #endif 49 50 #include <teken.h> 51 52 static tf_bell_t test_bell; 53 static tf_cursor_t test_cursor; 54 static tf_putchar_t test_putchar; 55 static tf_fill_t test_fill; 56 static tf_copy_t test_copy; 57 static tf_param_t test_param; 58 static tf_respond_t test_respond; 59 60 static teken_funcs_t tf = { 61 .tf_bell = test_bell, 62 .tf_cursor = test_cursor, 63 .tf_putchar = test_putchar, 64 .tf_fill = test_fill, 65 .tf_copy = test_copy, 66 .tf_param = test_param, 67 .tf_respond = test_respond, 68 }; 69 70 struct pixel { 71 teken_char_t c; 72 teken_attr_t a; 73 }; 74 75 #define NCOLS 80 76 #define NROWS 24 77 static struct pixel buffer[NCOLS][NROWS]; 78 79 static int ptfd; 80 81 static void 82 printchar(const teken_pos_t *p) 83 { 84 int y, x, attr = 0; 85 struct pixel *px; 86 char str[5] = { 0 }; 87 88 assert(p->tp_row < NROWS); 89 assert(p->tp_col < NCOLS); 90 91 px = &buffer[p->tp_col][p->tp_row]; 92 /* No need to print right hand side of CJK character manually. */ 93 if (px->a.ta_format & TF_CJK_RIGHT) 94 return; 95 96 /* Convert Unicode to UTF-8. */ 97 if (px->c < 0x80) { 98 str[0] = px->c; 99 } else if (px->c < 0x800) { 100 str[0] = 0xc0 | (px->c >> 6); 101 str[1] = 0x80 | (px->c & 0x3f); 102 } else if (px->c < 0x10000) { 103 str[0] = 0xe0 | (px->c >> 12); 104 str[1] = 0x80 | ((px->c >> 6) & 0x3f); 105 str[2] = 0x80 | (px->c & 0x3f); 106 } else { 107 str[0] = 0xf0 | (px->c >> 18); 108 str[1] = 0x80 | ((px->c >> 12) & 0x3f); 109 str[2] = 0x80 | ((px->c >> 6) & 0x3f); 110 str[3] = 0x80 | (px->c & 0x3f); 111 } 112 113 if (px->a.ta_format & TF_BOLD) 114 attr |= A_BOLD; 115 if (px->a.ta_format & TF_UNDERLINE) 116 attr |= A_UNDERLINE; 117 if (px->a.ta_format & TF_BLINK) 118 attr |= A_BLINK; 119 if (px->a.ta_format & TF_REVERSE) 120 attr |= A_REVERSE; 121 122 bkgdset(attr | COLOR_PAIR(teken_256to8(px->a.ta_fgcolor) + 123 8 * teken_256to8(px->a.ta_bgcolor))); 124 getyx(stdscr, y, x); 125 mvaddstr(p->tp_row, p->tp_col, str); 126 move(y, x); 127 } 128 129 static void 130 test_bell(void *s __unused) 131 { 132 133 beep(); 134 } 135 136 static void 137 test_cursor(void *s __unused, const teken_pos_t *p) 138 { 139 140 move(p->tp_row, p->tp_col); 141 } 142 143 static void 144 test_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c, 145 const teken_attr_t *a) 146 { 147 148 buffer[p->tp_col][p->tp_row].c = c; 149 buffer[p->tp_col][p->tp_row].a = *a; 150 printchar(p); 151 } 152 153 static void 154 test_fill(void *s, const teken_rect_t *r, teken_char_t c, 155 const teken_attr_t *a) 156 { 157 teken_pos_t p; 158 159 /* Braindead implementation of fill() - just call putchar(). */ 160 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; p.tp_row++) 161 for (p.tp_col = r->tr_begin.tp_col; p.tp_col < r->tr_end.tp_col; p.tp_col++) 162 test_putchar(s, &p, c, a); 163 } 164 165 static void 166 test_copy(void *s __unused, const teken_rect_t *r, const teken_pos_t *p) 167 { 168 int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ 169 teken_pos_t d; 170 171 /* 172 * Copying is a little tricky. We must make sure we do it in 173 * correct order, to make sure we don't overwrite our own data. 174 */ 175 176 nrow = r->tr_end.tp_row - r->tr_begin.tp_row; 177 ncol = r->tr_end.tp_col - r->tr_begin.tp_col; 178 179 if (p->tp_row < r->tr_begin.tp_row) { 180 /* Copy from top to bottom. */ 181 if (p->tp_col < r->tr_begin.tp_col) { 182 /* Copy from left to right. */ 183 for (y = 0; y < nrow; y++) { 184 d.tp_row = p->tp_row + y; 185 for (x = 0; x < ncol; x++) { 186 d.tp_col = p->tp_col + x; 187 buffer[d.tp_col][d.tp_row] = 188 buffer[r->tr_begin.tp_col + x][r->tr_begin.tp_row + y]; 189 printchar(&d); 190 } 191 } 192 } else { 193 /* Copy from right to left. */ 194 for (y = 0; y < nrow; y++) { 195 d.tp_row = p->tp_row + y; 196 for (x = ncol - 1; x >= 0; x--) { 197 d.tp_col = p->tp_col + x; 198 buffer[d.tp_col][d.tp_row] = 199 buffer[r->tr_begin.tp_col + x][r->tr_begin.tp_row + y]; 200 printchar(&d); 201 } 202 } 203 } 204 } else { 205 /* Copy from bottom to top. */ 206 if (p->tp_col < r->tr_begin.tp_col) { 207 /* Copy from left to right. */ 208 for (y = nrow - 1; y >= 0; y--) { 209 d.tp_row = p->tp_row + y; 210 for (x = 0; x < ncol; x++) { 211 d.tp_col = p->tp_col + x; 212 buffer[d.tp_col][d.tp_row] = 213 buffer[r->tr_begin.tp_col + x][r->tr_begin.tp_row + y]; 214 printchar(&d); 215 } 216 } 217 } else { 218 /* Copy from right to left. */ 219 for (y = nrow - 1; y >= 0; y--) { 220 d.tp_row = p->tp_row + y; 221 for (x = ncol - 1; x >= 0; x--) { 222 d.tp_col = p->tp_col + x; 223 buffer[d.tp_col][d.tp_row] = 224 buffer[r->tr_begin.tp_col + x][r->tr_begin.tp_row + y]; 225 printchar(&d); 226 } 227 } 228 } 229 } 230 } 231 232 static void 233 test_param(void *s __unused, int cmd, unsigned int value) 234 { 235 236 switch (cmd) { 237 case TP_SHOWCURSOR: 238 curs_set(value); 239 break; 240 case TP_KEYPADAPP: 241 keypad(stdscr, value ? TRUE : FALSE); 242 break; 243 } 244 } 245 246 static void 247 test_respond(void *s __unused, const void *buf, size_t len) 248 { 249 250 write(ptfd, buf, len); 251 } 252 253 static void 254 redraw_border(void) 255 { 256 unsigned int i; 257 258 for (i = 0; i < NROWS; i++) 259 mvaddch(i, NCOLS, '|'); 260 for (i = 0; i < NCOLS; i++) 261 mvaddch(NROWS, i, '-'); 262 263 mvaddch(NROWS, NCOLS, '+'); 264 } 265 266 static void 267 redraw_all(void) 268 { 269 teken_pos_t tp; 270 271 for (tp.tp_row = 0; tp.tp_row < NROWS; tp.tp_row++) 272 for (tp.tp_col = 0; tp.tp_col < NCOLS; tp.tp_col++) 273 printchar(&tp); 274 275 redraw_border(); 276 } 277 278 int 279 main(int argc __unused, char *argv[] __unused) 280 { 281 struct winsize ws; 282 teken_t t; 283 teken_pos_t tp; 284 fd_set rfds; 285 char b[256]; 286 ssize_t bl; 287 const int ccolors[8] = { 288 COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, 289 COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE 290 }; 291 int i, j; 292 293 setlocale(LC_CTYPE, "UTF-8"); 294 295 tp.tp_row = ws.ws_row = NROWS; 296 tp.tp_col = ws.ws_col = NCOLS; 297 298 switch (forkpty(&ptfd, NULL, NULL, &ws)) { 299 case -1: 300 perror("forkpty"); 301 exit(1); 302 case 0: 303 setenv("TERM", "xterm", 1); 304 setenv("LC_CTYPE", "UTF-8", 0); 305 execlp("zsh", "-zsh", NULL); 306 execlp("bash", "-bash", NULL); 307 execlp("sh", "-sh", NULL); 308 _exit(1); 309 } 310 311 teken_init(&t, &tf, NULL); 312 teken_set_winsize(&t, &tp); 313 314 initscr(); 315 raw(); 316 start_color(); 317 for (i = 0; i < 8; i++) 318 for (j = 0; j < 8; j++) 319 init_pair(i + 8 * j, ccolors[i], ccolors[j]); 320 321 redraw_border(); 322 323 FD_ZERO(&rfds); 324 325 for (;;) { 326 FD_SET(STDIN_FILENO, &rfds); 327 FD_SET(ptfd, &rfds); 328 329 if (select(ptfd + 1, &rfds, NULL, NULL, NULL) < 0) { 330 if (errno == EINTR) { 331 redraw_all(); 332 refresh(); 333 continue; 334 } 335 break; 336 } 337 338 if (FD_ISSET(STDIN_FILENO, &rfds)) { 339 bl = read(STDIN_FILENO, b, sizeof b); 340 if (bl <= 0) 341 break; 342 write(ptfd, b, bl); 343 } 344 345 if (FD_ISSET(ptfd, &rfds)) { 346 bl = read(ptfd, b, sizeof b); 347 if (bl <= 0) 348 break; 349 teken_input(&t, b, bl); 350 refresh(); 351 } 352 } 353 354 endwin(); 355 356 return (0); 357 } 358