diff options
Diffstat (limited to 'demo-draw.c')
| -rw-r--r-- | demo-draw.c | 230 | 
1 files changed, 230 insertions, 0 deletions
diff --git a/demo-draw.c b/demo-draw.c new file mode 100644 index 0000000..85d81fc --- /dev/null +++ b/demo-draw.c @@ -0,0 +1,230 @@ +// <poll.h> might need this for sigset_t +#define _XOPEN_SOURCE 600 + +#include <stdio.h> +#include <unistd.h> +#include <locale.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> + +#include <poll.h> +#include <signal.h> + +#include <curses.h> +#include "termo.h" + +typedef struct app_data app_data_t; +struct app_data +{ +	termo_t *tk; + +	// Current attributes for the left mouse button +	int current_attrs_left; +	// Current attributes for the right mouse button +	int current_attrs_right; +}; + +static int g_winch_pipe[2]; + +static void +display (const char *format, ...) +{ +	va_list ap; + +	mvwhline (stdscr, 0, 0, A_REVERSE, COLS); +	attron (A_REVERSE); + +	va_start (ap, format); +	vw_printw (stdscr, format, ap); +	va_end (ap); + +	attroff (A_REVERSE); +	refresh (); +} + +static void +init_palette (app_data_t *app) +{ +	start_color (); + +	// Also does init_pair (0, -1, -1); +	use_default_colors (); +	// Duplicate it for convenience. +	init_pair (9, -1, -1); + +	// Add the basic 8 colors to the default pair.  Once normally, once +	// inverted to workaround VTE's inability to set a bright background. +	for (int i = 0; i < 8; i++) +	{ +		init_pair (1  + i, COLOR_WHITE, COLOR_BLACK + i); +		init_pair (10 + i, COLOR_BLACK + i, COLOR_WHITE); +	} + +	// This usually creates a solid black or white. +	app->current_attrs_left = +	app->current_attrs_right = COLOR_PAIR (9) | A_REVERSE | A_BOLD; +} + +static void +redraw (void) +{ +	int i; + +	mvwhline (stdscr, 1, 0, A_REVERSE, COLS); +	mvwhline (stdscr, 2, 0, A_REVERSE, COLS); + +	for (i = 0; i < COLS; i++) +	{ +		int pair = (float) i / COLS  * 9; +		mvaddch (1, i, ' ' | COLOR_PAIR (pair)); +		mvaddch (2, i, ' ' | COLOR_PAIR (pair + 9) | A_REVERSE | A_BOLD); +	} + +	display ("Choose a color from the palette and draw.  " +		"Press Escape or ^C to quit."); +	refresh (); +} + +static bool +on_key (app_data_t *app, termo_key_t *key) +{ +	if (key->type == TERMO_TYPE_KEYSYM +	 && key->code.sym == TERMO_SYM_ESCAPE) +		return false; + +	if (key->type == TERMO_TYPE_KEY +	 && (key->modifiers & TERMO_KEYMOD_CTRL) +	 && (key->code.codepoint == 'C' || key->code.codepoint == 'c')) +		return false; + +	if (key->type != TERMO_TYPE_MOUSE) +		return true; + +	int line, col, button; +	termo_mouse_event_t event; + +	termo_interpret_mouse (app->tk, key, &event, &button, &line, &col); +	if (event != TERMO_MOUSE_PRESS && event != TERMO_MOUSE_DRAG) +		return true; + +	int *attrs; +	if (button == 1) +		attrs = &app->current_attrs_left; +	else if (button == 3) +		attrs = &app->current_attrs_right; +	else +		return true; + +	chtype ch = mvwinch (stdscr, line, col); +	if (line >= 3) +	{ +		// Paste the attributes where the user clicked. +		addch (' ' | *attrs); +		refresh (); +	} +	else if (line > 0) +		// Copy attributes from the pallete. +		*attrs = ch & (A_COLOR | A_ATTRIBUTES); + +	return true; +} + +static void +winch_handler (int signum) +{ +	(void) signum; +	write (g_winch_pipe[1], "x", 1); +} + +int +main (int argc, char *argv[]) +{ +	(void) argc; +	(void) argv; + +	TERMO_CHECK_VERSION; +	setlocale (LC_CTYPE, ""); + +	struct sigaction act; +	act.sa_handler = winch_handler; +	act.sa_flags = SA_RESTART; +	sigemptyset (&act.sa_mask); + +	// Set up a self-pipe so that we can actually poll on SIGWINCH +	if (sigaction (SIGWINCH, &act, NULL) || pipe (g_winch_pipe)) +	{ +		fprintf (stderr, "Cannot set up signal handler\n"); +		exit (EXIT_FAILURE); +	} + +	termo_t *tk = termo_new (STDIN_FILENO, NULL, 0); +	if (!tk) +	{ +		fprintf (stderr, "Cannot allocate termo instance\n"); +		exit (EXIT_FAILURE); +	} + +	termo_set_mouse_proto (tk, termo_guess_mouse_proto (tk)); +	termo_set_mouse_tracking_mode (tk, TERMO_MOUSE_TRACKING_DRAG); + +	// Set up curses for our drawing needs +	if (!initscr () || nonl () == ERR || curs_set (0) == ERR) +	{ +		fprintf (stderr, "Cannot initialize curses\n"); +		exit (EXIT_FAILURE); +	} + +	app_data_t app; +	memset (&app, 0, sizeof app); +	app.tk = tk; + +	init_palette (&app); +	redraw (); + +	termo_result_t ret; +	termo_key_t key; + +	// We listen for mouse/key input and terminal resize events +	struct pollfd fds[2] = +	{ +		{ .fd = STDIN_FILENO,    .events = POLLIN }, +		{ .fd = g_winch_pipe[0], .events = POLLIN }, +	}; + +	// Run a simple event loop with poll() +	int nextwait = -1; +	bool running = true; +	while (running) +	{ +		if (!poll (fds, 2, nextwait)) +			if (termo_getkey_force (tk, &key) == TERMO_RES_KEY) +				running &= on_key (&app, &key); + +		if (fds[1].revents & (POLLIN | POLLHUP | POLLERR)) +		{ +			char x; +			read (fds[1].fd, &x, 1); + +			// The "official" simple and flicker-prone method of resizing +			// the internal buffers of curses +			endwin (); +			refresh (); + +			redraw (); +		} +		if (fds[0].revents & (POLLIN | POLLHUP | POLLERR)) +			termo_advisereadable (tk); + +		while ((ret = termo_getkey (tk, &key)) == TERMO_RES_KEY) +			running &= on_key (&app, &key); + +		nextwait = -1; +		if (ret == TERMO_RES_AGAIN) +			nextwait = termo_get_waittime (tk); +	} + +	endwin (); +	termo_destroy (tk); +} +  | 
