Skip to content

Commit

Permalink
Merge pull request #22 from StefansM/feature-colors
Browse files Browse the repository at this point in the history
Allow terminal colors to be changed either via `initc`, or using direct colors.
  • Loading branch information
StefansM authored Apr 16, 2018
2 parents 13442f6 + 3e5b59f commit bcaa6d5
Show file tree
Hide file tree
Showing 10 changed files with 808 additions and 71 deletions.
7 changes: 4 additions & 3 deletions Makefile.am
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
bin_PROGRAMS = cpipes

check_PROGRAMS = test_locale
TEST_LOG_DRIVER = prove
TESTS = test_locale

cpipes_SOURCES = src/cpipes.c \
src/pipe.c src/pipe.h \
src/render.c src/render.h \
src/util.c src/util.h
src/util.c src/util.h \
src/err.c src/err.h
cpipes_CFLAGS = $(WARN_CFLAGS)
cpipes_LDFLAGS = $(WARN_LDFLAGS)
cpipes_LDADD = $(CURSES_LIBS)
cpipes_CPPFLAGS = $(CURSES_CFLAGS)

test_locale_SOURCES = t/locale.c \
src/pipe.c src/pipe.h \
src/util.c src/util.h
src/util.c src/util.h \
src/err.c src/err.h
test_locale_CFLAGS = -I$(srcdir)/src $(WARN_CFLAGS)
test_locale_LDFLAGS = $(WARN_LDFLAGS)
test_locale_LDADD = $(CURSES_LIBS)
Expand Down
33 changes: 33 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,32 @@ AX_IS_RELEASE([dash-version])
# Require curses
AX_WITH_CURSES


libs=$LIBS
LIBS=$CURSES_LIBS

# Macro for checking for a particular function in all libraies in LIBS
# have_function([preamble], [function(1, 2, 3)], [symbol to define])
AC_DEFUN([have_fn], [dnl
AC_MSG_CHECKING([for $2])
AC_LINK_IFELSE([AC_LANG_PROGRAM([$1],
[$2;])],
[AC_DEFINE([$3],
[1],
[have $2])
AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])])
])
have_fn([#include <curses.h>],
[init_extended_color(0, 0, 0, 0)],
[HAVE_EXTENDED_COLOR])

have_fn([#include <curses.h>],
[alloc_pair(0, 0)],
[HAVE_ALLOC_PAIR])

LIBS=$libs

# Enable as many compiler warnings as possible
AX_COMPILER_FLAGS()

Expand All @@ -43,6 +69,13 @@ AC_SEARCH_LIBS([iconv], [iconv], [],
AC_SEARCH_LIBS([clock_gettime], [rt], [],
[AC_MSG_ERROR(["clock_gettime is required for timing"])])

# Some systems require libm for certain math functions
AC_SEARCH_LIBS([fmod], [m], [],
[AC_MSG_ERROR(["fmod is required"])])

# Check for format attribute
AX_GCC_FUNC_ATTRIBUTE([format])

# Use noreturn attribute if available
AC_CHECK_HEADERS_ONCE([stdnoreturn.h])

Expand Down
94 changes: 87 additions & 7 deletions src/cpipes.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include <config.h>

#include <inttypes.h>
#include <langinfo.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
Expand All @@ -14,6 +16,7 @@

#include "pipe.h"
#include "render.h"
#include "util.h"

// Use noreturn on die() if possible
#ifdef HAVE_STDNORETURN_H
Expand All @@ -24,6 +27,7 @@

void interrupt_signal(int param);
void parse_options(int argc, char **argv);
static void find_num_colors(int argc, char **argv);
float parse_float_opt(const char *optname);
int parse_int_opt(const char *optname);
noreturn void die(void);
Expand All @@ -43,6 +47,8 @@ const char *usage =
" -l, --length=N Minimum length of pipe. (Default: 2 )\n"
" -r, --prob=N Probability of changing direction.(Default: 0.1 )\n"
" -i, --init=N Initial state (0,1,2,3 => R,D,L,U)(Default: random)\n"
" -c, --color=C Add color C (in RRGGBB hexadecimal) to palette.\n"
" --backup-colors Backup colors and restore when exiting.\n"
" -h, --help This help message.\n";

static struct option opts[] = {
Expand All @@ -52,6 +58,8 @@ static struct option opts[] = {
{"length", required_argument, 0, 'l'},
{"prob", required_argument, 0, 'r'},
{"help", no_argument, 0, 'h'},
{"color", required_argument, 0, 'c'},
{"backup-colors", no_argument, 0, 'b'},
{0, 0, 0, 0 }
};

Expand Down Expand Up @@ -83,6 +91,16 @@ const char *selected_chars = NULL;

char pipe_char_buf[CHAR_BUF_SZ];

// Colour information stored here.
struct palette palette;
// Colours set by parse_options by the "-c" flag
uint32_t *custom_colors = NULL;
size_t num_custom_colors = 0;

// Keep a separate pointer because this is optional.
struct color_backup backup;
struct color_backup *backup_ptr = NULL;

// Convenience macro for bailing in init_chars
#define X(a) do { \
if( ((a)) == -1 ) { \
Expand All @@ -104,12 +122,13 @@ int init_chars(void) {

X(locale_to_utf8(inbuf, utf8buf, source_charset, CHAR_BUF_SZ));
X(utf8_to_locale(utf8buf, pipe_char_buf, CHAR_BUF_SZ, term_charset));
X(assign_matrices(pipe_char_buf, trans, pipe_chars));
assign_matrices(pipe_char_buf, trans, pipe_chars);
X(multicolumn_adjust(pipe_chars));
return 0;
}

int main(int argc, char **argv){
cpipes_errno err = 0;
srand(time(NULL));
setlocale(LC_ALL, "");
//Set a flag upon interrupt to allow proper cleaning
Expand All @@ -122,29 +141,53 @@ int main(int argc, char **argv){
initscr();
curs_set(0);
cbreak();
nodelay(stdscr, true);
noecho();
setbuf(stdout, NULL);
getmaxyx(stdscr, screen_height, screen_width);
init_colours();

err = init_color_palette(
custom_colors, num_custom_colors,
&palette, backup_ptr);
if(err)
goto cleanup;

// Called after init_color_palette because that needs to have a
// timeout on getch() to determine whether the query worked.
nodelay(stdscr, true);

//Init pipes. Use predetermined initial state, if any.
pipes = malloc(num_pipes * sizeof(struct pipe));
for(unsigned int i=0; i<num_pipes;i++)
init_pipe(&pipes[i], COLORS, initial_state,
for(unsigned int i=0; i<num_pipes;i++) {
init_pipe(&pipes[i], &palette, initial_state,
screen_width, screen_height);
random_pipe_color(&pipes[i], &palette);
}

animate(fps, render, &screen_width, &screen_height, &interrupted, NULL);

cleanup:
curs_set(1);
endwin();

if(err)
print_error();

if(backup_ptr) {
restore_colors(backup_ptr);
free_colors(backup_ptr);
}

free(custom_colors);
free(pipes);
palette_destroy(&palette);
return 0;
}

void render(unsigned int width, unsigned int height, void *data){
for(size_t i=0; i<num_pipes && !interrupted; i++){
move_pipe(&pipes[i]);
if(wrap_pipe(&pipes[i], width, height))
random_pipe_colour(&pipes[i], COLORS);
random_pipe_color(&pipes[i], &palette);

char old_state = pipes[i].state;
if(should_flip_state(&pipes[i], min_len, prob)){
Expand Down Expand Up @@ -188,9 +231,36 @@ float parse_float_opt(const char *optname){
return f_res;
}

void find_num_colors(int argc, char **argv) {
opterr = 0;
// First work out how many colors were specified on the command line
int c;
while((c = getopt_long(argc, argv, "c:", opts, NULL)) != -1) {
if(c == 'c')
num_custom_colors++;
}
if(num_custom_colors > 0) {
custom_colors = malloc(sizeof(*custom_colors) * num_custom_colors);
if(!custom_colors) {
perror("Error allocating memory for color palette.");
exit(1);
}
}
// Reset optind so we can do the proper parsing run
optind = 0;
}

void parse_options(int argc, char **argv){
opterr = 1;
int c;
while((c = getopt_long(argc, argv, "p:f:al:r:i:h", opts, NULL)) != -1){

find_num_colors(argc, argv);
// Current color (`-c`) and index into `custom_colors`
size_t color_index = 0;
uint32_t color = 0;

optind = 0;
while((c = getopt_long(argc, argv, "p:f:al:r:i:c:h", opts, NULL)) != -1){
switch(c){
errno = 0;
case 'p':
Expand Down Expand Up @@ -223,6 +293,16 @@ void parse_options(int argc, char **argv){
exit(1);
}
break;
case 'b':
backup_ptr = &backup;
break;
case 'c':
if(sscanf(optarg, "%" SCNx32, &color) != 1) {
fprintf(stderr, "Invalid color '%s'\n", optarg);
die();
}
custom_colors[color_index++] = color;
break;
case 'h':
usage_msg(0);
exit(0);
Expand Down
106 changes: 106 additions & 0 deletions src/err.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include <config.h>

#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include "err.h"

// Maximum size of an error message. Any messages longer than this are just
// truncated: I don't want to handle errors within errors.
#define ERR_BUF_SZ 256

// Buffer containing an error message
static char ERR_BUF[ERR_BUF_SZ];
// Offset into ERR_BUF: new messages should be appended after this.
static size_t ERR_OFFSET = 0;

/** These correspond to the numbers PIPES_C_ERRORS multipled by -1. */
static const char *PIPES_C_ERROR_STRINGS[] = {
"Success. If you see this an error message, please raise an issue!",

// Parameter 1: Detailed message indicating ncurses error
"An error was encountered in ncurses: %s.",

// Parameter 1: Number of colors in the palette
// Parameter 2: Number of available colors.
"Too many colors for the terminal to handle were given in the palette."
" %d colors were specified, but only %d are available.",

"A color palette was supplied, but the terminal cannot redefine colors.",
"A color palette was supplied, but the terminal cannot use color.",
"Error allocating memory.",
"Terminal did not reply to query.",
"Buffer (%d items) too small: tried to insert %d items.",
"Error encountered calling standard library function: %s",
"An error occurred when calling iconv."
};

static void err_provenance(const char *file, int line, const char *function);

/// Write prefix containing file name, line and function.
static void err_provenance(const char *file, int line, const char *function) {
ERR_OFFSET += snprintf(ERR_BUF + ERR_OFFSET,
ERR_BUF_SZ - ERR_OFFSET,
"[%s:%d (%s)] ", file, line, function);
}


/** Store message in buffer. */
void set_error_(const char *file, int line, const char *function,
cpipes_errno err_num, ...) {
assert(ERR_ICONV_ERROR <= err_num && err_num <= ERR_CURSES_ERR
/* Error number is within the correct range */);

err_provenance(file, line, function);

va_list extra_args;
va_start(extra_args, err_num);

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
ERR_OFFSET += vsnprintf(
ERR_BUF + ERR_OFFSET, ERR_BUF_SZ - ERR_OFFSET,
PIPES_C_ERROR_STRINGS[-err_num], extra_args);
#pragma GCC diagnostic pop
va_end(extra_args);

// Always terminate with a nul, even if snprintf didn't allow anything to
// actually be appended.
ERR_BUF[ERR_BUF_SZ - 1] = '\0';
}

#ifdef HAVE_FUNC_ATTRIBUTE_FORMAT
__attribute__((__format__ (__printf__, 4, 5)))
#endif
void add_error_info_(const char *file, int line, const char *function,
const char *fmt, ...) {
ERR_OFFSET += snprintf(
ERR_BUF + ERR_OFFSET,
ERR_BUF_SZ - ERR_OFFSET,
"%s", "\n");
err_provenance(file, line, function);

va_list extra_args;
va_start(extra_args, fmt);
ERR_OFFSET += vsnprintf(
ERR_BUF + ERR_OFFSET, ERR_BUF_SZ - ERR_OFFSET,
fmt, extra_args);
va_end(extra_args);
}

/** Clear error buffer. */
void clear_error(void) {
ERR_BUF[0] = '\0';
ERR_OFFSET = 0;
}


void print_error(void) {
fprintf(stderr, "%s\n", ERR_BUF);
}

const char *string_error(void) {
return ERR_BUF;
}
Loading

0 comments on commit bcaa6d5

Please sign in to comment.