xref: /freebsd/sys/teken/demo/teken_demo.c (revision 13ea0450a9c8742119d36f3bf8f47accdce46e54)
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