graphcurses

Curses 2D graph generator
git clone git://git.christosmarg.xyz/graphcurses.git
Log | Files | Refs | README | LICENSE

commit 067ba1b4855ea7ba50964d411bfabb87ddbba706
parent eda41ce995fbd41f7b3f6ef5a9dc2efcf9a23b34
Author: Christos Margiolis <christos@margiolis.net>
Date:   Wed, 26 May 2021 19:01:03 +0300

i don't know

Diffstat:
MLICENSE | 41++++++++++++++++-------------------------
MMakefile | 42+++++++++++++++++++++---------------------
MREADME.md | 4++--
Mconfig.mk | 26+++++++-------------------
Mgraphcurses.1 | 8++++----
Mgraphcurses.c | 565+++++++++++++++++++++++++++++++++++++++++--------------------------------------
6 files changed, 341 insertions(+), 345 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -1,29 +1,20 @@ -BSD 3-Clause License +MIT License -Copyright (c) 2020-present, Christos Margiolis. -All rights reserved. +(c) 2020-Present Christos Margiolis <christos@margiolis.net> -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of images.weserv.nl nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ # See LICENSE file for copyright and license details. -# graphcurses - an ncurses 2D graph generator +# graphcurses - curses(3) 2D graph generator .POSIX: include config.mk @@ -8,47 +8,47 @@ BIN = graphcurses DIST = ${BIN}-${VERSION} MAN1 = ${BIN}.1 -EXT = c SRC = graphcurses.c -OBJ = ${SRC:.${EXT}=.o} +OBJ = ${SRC:.c=.o} all: options ${BIN} options: @echo ${BIN} build options: - @echo "CFLAGS = ${CFLAGS}" - @echo "LDFLAGS = ${LDFLAGS}" - @echo "CC = ${CC}" + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" ${BIN}: ${OBJ} ${CC} ${LDFLAGS} ${OBJ} -o $@ -.${EXT}.o: +.c.o: ${CC} -c ${CFLAGS} $< dist: clean - ${MKDIR} ${DIST} - ${CP} -R config.mk ${MAN1} ${SRC} LICENSE Makefile README.md ${DIST} - ${TAR} ${DIST}.tar ${DIST} - ${GZIP} ${DIST}.tar - ${RM_DIR} ${DIST} + mkdir -p ${DIST} + cp -R config.mk graphcurses.1 graphcurses.c LICENSE Makefile \ + README.md ${DIST} + tar -cf ${DIST}.tar ${DIST} + gzip ${DIST}.tar + rm -rf ${DIST} run: ./${BIN} install: all - ${MKDIR} ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN_DIR} - ${CP} ${BIN} ${BIN_DIR} - ${CP} ${MAN1} ${DESTDIR}${MAN_DIR} - sed "s/VERSION/${VERSION}/g" < ${MAN1} > ${DESTDIR}${MAN_DIR}/${MAN1} - chmod 755 ${DESTDIR}${BIN_DIR}/${BIN} - chmod 644 ${DESTDIR}${MAN_DIR}/${MAN1} + mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${MANPREFIX}/man1 + cp -f ${BIN} ${DESTDIR}${PREFIX}/bin + cp -f ${MAN1} ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < ${MAN1} > ${DESTDIR}${MANPREFIX}/man1/${MAN1} + chmod 755 ${DESTDIR}${PREFIX}/bin/${BIN} + chmod 644 ${DESTDIR}${MANPREFIX}/man1/${MAN1} uninstall: - ${RM} ${DESTDIR}${BIN_DIR}/${BIN} - ${RM} ${DESTDIR}${MAN_DIR}/${MAN1} + rm -f ${DESTDIR}${PREFIX}/bin/${BIN} \ + ${DESTDIR}${MANPREFIX}/man1/${MAN1} clean: - ${RM} ${BIN} ${OBJ} ${DIST}.tar.gz + rm -f ${BIN} ${OBJ} ${DIST}.tar.gz *.core .PHONY: all options clean dist install uninstall run diff --git a/README.md b/README.md @@ -1,12 +1,12 @@ # graphcurses -A simple ncurses graph generator. +A simple curses graph generator. ![preview](https://user-images.githubusercontent.com/54286563/102025554-66e14380-3da1-11eb-81e0-ea7cfc8c1161.png) ## Dependencies -* `ncurses` +* `curses` * `libmatheval` ## Usage diff --git a/config.mk b/config.mk @@ -1,32 +1,20 @@ # See LICENSE file for copyright and license details. # graphcurses version -VERSION = 0 +VERSION = 0.1 # paths PREFIX = /usr/local -MAN_DIR = ${PREFIX}/man/man1 -BIN_DIR = ${PREFIX}/bin +MANPREFIX = ${PREFIX}/share/man # includes and libs -INCS = -Iinclude -LIBS = -Llib -lm -lmatheval -lncurses +INCS = -Iinclude -I${PREFIX}/include +LIBS = -Llib -L${PREFIX}/lib -lncursesw -lmatheval -lm # flags CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \ - -D_XOPEN_SOURCE=600 -DVERSION=\"${VERSION}\" -U__STRICT_ANSI__ -CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations \ - -O3 ${INCS} ${CPPFLAGS} + -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\" +CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} LDFLAGS = ${LIBS} -# utils -CP = cp -f -RM = rm -f -RM_DIR = rm -rf -MV = mv -MKDIR = mkdir -p -RM_DIR = rm -rf -TAR = tar -cf -GZIP = gzip - # compiler -CC = gcc +CC = cc diff --git a/graphcurses.1 b/graphcurses.1 @@ -3,13 +3,13 @@ .Os .Sh NAME .Nm graphcurses -.Nd ncurses 2D graph generator +.Nd curses 2D graph generator .Sh SYNOPSIS .Nm .Sh DESCRIPTION .Nm takes a function as an input and draws it using -.Xr ncurses 3 +.Xr curses 3 to draw the TUI. Functions and derivatives are evaluated by .Xr libmatheval 3 and might use @@ -40,8 +40,8 @@ zoom in .It Sy - zoom out .Sh SEE ALSO -.Xr ncurses 3 , +.Xr curses 3 , .Xr libmatheval 3 , .Xr math 3 .Sh AUTHORS -.An Christos Margiolis Aq Mt christos@christosmarg.xyz +.An Christos Margiolis Aq Mt christos@margiolis.net diff --git a/graphcurses.c b/graphcurses.c @@ -1,338 +1,355 @@ /* See LICENSE file for copyright and license details. */ +#include <err.h> #include <math.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <curses.h> #include <matheval.h> -#include <ncurses.h> #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* M_PI */ - -#define XMIN_PLANE (-2.0f * M_PI) -#define XMAX_PLANE ( 2.0f * M_PI) -#define YMIN_PLANE (-M_PI) -#define YMAX_PLANE M_PI -#define XSCALE_PLANE 1.0f -#define YSCALE_PLANE 1.0f -#define SHIFT_STEP 1.0f -#define ZOOM_IN_FACTOR (1.0f / 1.05f) -#define ZOOM_OUT_FACTOR 1.05f -#define BUFSIZE 256 - -#define YMAX (getmaxy(stdscr)) -#define XMAX (getmaxx(stdscr)) -#define CENTER(x, y) ((x) / 2 - (y) / 2) -#define PLANE_SCALE(val, omin, omax, nmin, nmax) \ - ((((val) - (omin)) / ((omax) - (omin))) * ((nmax) - (nmin)) + (nmin)) -#define PLANE_XSTEP(p, xstep) \ - xstep = (p->xmax - p->xmin) / (p->xmaxs + 1.0f); -#define PLANE_YSTEP(p, ystep) \ - ystep = (p->xmax - p->ymin) / (p->ymaxs + 1.0f); - -#define OPT_QUIT "q Quit" -#define OPT_MOVE_UP "Up/k Move up" -#define OPT_MOVE_DOWN "Down/j Move down" -#define OPT_MOVE_LEFT "Left/h Move left" -#define OPT_MOVE_RIGHT "Right/l Move right" -#define OPT_SHOW_DERIVATIVE "d Show derivative" -#define OPT_NEW_FUNCTION "f New function" -#define OPT_RESTORE_ZOOM "r Restore zoom" -#define OPT_ZOOM_IN "+ Zoom in" -#define OPT_ZOOM_OUT "- Zoom out" -#define MSG_QUIT_MENU "Press any key to quit the menu" - -typedef struct { - float (*f)(float); - void *df; - float ymin; - float ymax; - float xmin; - float xmax; - float xscale; - float yscale; - int ymaxs; - int xmaxs; - int derivshow; -} Plane; - -static void cursesinit(void); -static void funcget(Plane *, char *); -static void exprvalidate(Plane *); -static float expreval(float); -static Plane *planeinit(void); -static void planeshift(Plane *, float, float); -static void zoomrestore(Plane *); -static void zoomhandle(Plane *, float); -static void axesdraw(const Plane *); -static void graphdraw(const Plane *); -static void graphplot(const Plane *, float, float); -static void menuopts(void); - -static void *f = NULL; +#ifndef SIGWINCH +#define SIGWINCH 28 +#endif /* SIGWINCH */ +#define SHIFT_STEP 1.0f +#define BUFSIZE 256 + +#define YMAX (getmaxy(stdscr)) +#define XMAX (getmaxx(stdscr)) +#define CENTER(x, y) (((x) >> 1) - ((y) >> 1)) +#define PLANE_SCALE(val, omin, omax, nmin, nmax) \ + ((((val) - (omin)) / ((omax) - (omin))) * ((nmax) - (nmin)) + (nmin)) +#define PLANE_XSTEP(p, xstep) \ + xstep = (p->xmax - p->xmin) / (p->xmaxs + 1.0f); +#define PLANE_YSTEP(p, ystep) \ + ystep = (p->xmax - p->ymin) / (p->ymaxs + 1.0f); +#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) + +struct plane { + float (*f)(float); + void *df; + float ymin; + float ymax; + float xmin; + float xmax; + float xscale; + float yscale; + int ymaxs; + int xmaxs; + int derivshow; +}; + +enum { + C_FG = 1, + C_F, + C_DF, +}; + +static void cursesinit(void); +static void exprvalidate(void); +static float expreval(float); +static void planeinit(void); +static void planeshift(float, float); +static void zoomrestore(void); +static void zoomhandle(float); +static void axesdraw(void); +static void graphdraw(void); +static void graphplot(float, float); +static void menuopts(void); +static void sighandler(int); +static void cleanup(void); + +static struct plane *p; +static void *f = NULL; +static int colors[] = { + [C_FG] = COLOR_BLUE, + [C_F] = COLOR_YELLOW, + [C_DF] = COLOR_MAGENTA, +}; static void cursesinit(void) { - if (!initscr()) { - fputs("graphcurses: initscr: cannot initialiaze ncurses", stderr); - exit(EXIT_FAILURE); - } - cbreak(); - noecho(); - curs_set(0); - keypad(stdscr, 1); - - start_color(); - init_pair(1, COLOR_WHITE, COLOR_BLACK); - init_pair(2, COLOR_CYAN, COLOR_BLACK); - init_pair(3, COLOR_RED, COLOR_BLACK); -} - -static void -funcget(Plane *p, char *buf) -{ - move(0, 0); - clrtoeol(); - printw("f(x) = "); - echo(); - refresh(); - getnstr(buf, BUFSIZE); - zoomrestore(p); - refresh(); - noecho(); + struct sigaction sa; + int i; + + if (!initscr()) + errx(1, "initscr"); + cbreak(); + noecho(); + curs_set(0); + keypad(stdscr, 1); + + start_color(); + use_default_colors(); + for (i = 1; i < ARRLEN(colors); i++) + (void)init_pair(i, colors[i], -1); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sighandler; + sa.sa_flags = SA_RESTART; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGWINCH, &sa, NULL); } static void -exprvalidate(Plane *p) +exprvalidate(void) { - char *buf; - - if ((buf = malloc(BUFSIZE)) == NULL) { - fputs("graphcurses: exprvalidate: cannot allocate memory", stderr); - exit(EXIT_FAILURE); - } - funcget(p, buf); - while (!(f = evaluator_create(buf))) { - printw("Error in expression! Try again"); - funcget(p, buf); - refresh(); - } - p->df = evaluator_derivative_x(f); - free(buf); + char *buf; + + if ((buf = malloc(BUFSIZE)) == NULL) + err(1, "malloc"); + attron(COLOR_PAIR(C_FG)); + for (;;) { + move(0, 0); + clrtoeol(); + printw("f(x) = "); + echo(); + refresh(); + if (getnstr(buf, BUFSIZE) == ERR) + continue; + zoomrestore(); + refresh(); + noecho(); + if (!(f = evaluator_create(buf))) + printw("Error in expression! Try again"); + else + break; + refresh(); + } + attroff(COLOR_PAIR(C_FG)); + p->df = evaluator_derivative_x(f); + free(buf); } static float expreval(float x) { - return evaluator_evaluate_x(f, x); + return evaluator_evaluate_x(f, x); } -static Plane * +static void planeinit(void) { - Plane *p; - - if ((p = malloc(sizeof(Plane))) == NULL) { - fputs("graphcurses: planeinit: cannot allocate memory", stderr); - exit(EXIT_FAILURE); - } - - p->xmaxs = XMAX; - p->ymaxs = YMAX; - p->derivshow = 0; - p->f = expreval; - zoomrestore(p); - - return p; + if ((p = malloc(sizeof(struct plane))) == NULL) + err(1, "malloc"); + p->xmaxs = XMAX; + p->ymaxs = YMAX; + p->derivshow = 0; + p->f = expreval; + zoomrestore(); } static void -planeshift(Plane *p, float xshift, float yshift) +planeshift(float xshift, float yshift) { - xshift *= (p->xmax - p->xmin) / 16.0f; - yshift *= (p->ymax - p->ymin) / 16.0f; - p->xmin += xshift; - p->xmax += xshift; - p->ymin += yshift; - p->ymax += yshift; + xshift *= (p->xmax - p->xmin) / 16.0f; + yshift *= (p->ymax - p->ymin) / 16.0f; + p->xmin += xshift; + p->xmax += xshift; + p->ymin += yshift; + p->ymax += yshift; } static void -zoomrestore(Plane *p) +zoomrestore(void) { - p->xmin = XMIN_PLANE; - p->xmax = XMAX_PLANE; - p->ymin = YMIN_PLANE; - p->ymax = YMAX_PLANE; - p->xscale = XSCALE_PLANE; - p->yscale = YSCALE_PLANE; + p->xmin = -2.0f * M_PI; + p->xmax = 2.0f * M_PI; + p->ymin = -M_PI; + p->ymax = M_PI; + p->xscale = 1.0f; + p->yscale = 1.0f; } static void -zoomhandle(Plane *p, float factor) +zoomhandle(float factor) { - float xctr = (p->xmin + p->ymax) / 2.0f; - float yctr = (p->ymin + p->ymax) / 2.0f; + float xctr = (p->xmin + p->ymax) / 2.0f; + float yctr = (p->ymin + p->ymax) / 2.0f; - p->xmin = PLANE_SCALE(factor, 1.0f, 0.0f, p->xmin, xctr); - p->xmax = PLANE_SCALE(factor, 1.0f, 0.0f, p->xmax, xctr); - p->ymin = PLANE_SCALE(factor, 1.0f, 0.0f, p->ymin, yctr); - p->ymax = PLANE_SCALE(factor, 1.0f, 0.0f, p->ymax, yctr); + p->xmin = PLANE_SCALE(factor, 1.0f, 0.0f, p->xmin, xctr); + p->xmax = PLANE_SCALE(factor, 1.0f, 0.0f, p->xmax, xctr); + p->ymin = PLANE_SCALE(factor, 1.0f, 0.0f, p->ymin, yctr); + p->ymax = PLANE_SCALE(factor, 1.0f, 0.0f, p->ymax, yctr); } static void -axesdraw(const Plane *p) +axesdraw(void) { - float x0, y0, xstep, ystep, plotx, ploty, i; - int tick; - - x0 = PLANE_SCALE(0.0f, p->xmin, p->xmax, 0.0f, p->xmaxs); - y0 = PLANE_SCALE(0.0f, p->ymin, p->ymax, p->ymaxs, 0.0f); - PLANE_XSTEP(p, xstep); - PLANE_YSTEP(p, ystep); - - for (i = 0.0f; i < p->xmaxs; i += xstep) { - plotx = p->xmin + xstep * i; - tick = fabs(fmod(plotx, p->xscale)) < xstep; - mvaddch(y0, i, tick ? ACS_PLUS : ACS_HLINE); - } - for (i = 0.0f; i < p->ymaxs; i += ystep) { - ploty = p->ymin + ystep * i; - tick = fabs(fmod(ploty, p->yscale)) < ystep; - mvaddch(i, x0, tick ? ACS_PLUS : ACS_VLINE); - } - - mvaddch(y0, x0, ACS_PLUS); + float x0, y0, xstep, ystep, plotx, ploty, i; + int tick; + + x0 = PLANE_SCALE(0.0f, p->xmin, p->xmax, 0.0f, p->xmaxs); + y0 = PLANE_SCALE(0.0f, p->ymin, p->ymax, p->ymaxs, 0.0f); + PLANE_XSTEP(p, xstep); + PLANE_YSTEP(p, ystep); + + for (i = 0.0f; i < p->xmaxs; i += xstep) { + plotx = p->xmin + xstep * i; + tick = fabs(fmod(plotx, p->xscale)) < xstep; + mvaddch(y0, i, tick ? ACS_PLUS : ACS_HLINE); + } + for (i = 0.0f; i < p->ymaxs; i += ystep) { + ploty = p->ymin + ystep * i; + tick = fabs(fmod(ploty, p->yscale)) < ystep; + mvaddch(i, x0, tick ? ACS_PLUS : ACS_VLINE); + } + mvaddch(y0, x0, ACS_PLUS); } static void -graphdraw(const Plane *p) +graphdraw(void) { - float x, y, dy, xstep; - - PLANE_XSTEP(p, xstep); - for (x = p->xmin; x <= p->xmax; x += xstep) { - y = p->f(x); - attron(COLOR_PAIR(2)); - graphplot(p, x, y); - if (p->derivshow) { - dy = evaluator_evaluate_x(p->df, x); - attron(COLOR_PAIR(3)); - graphplot(p, x, dy); - } - } - attroff(COLOR_PAIR(2) | COLOR_PAIR(3)); + float x, y, dy, xstep; + + PLANE_XSTEP(p, xstep); + for (x = p->xmin; x <= p->xmax; x += xstep) { + y = p->f(x); + attron(COLOR_PAIR(C_F)); + graphplot(x, y); + if (p->derivshow) { + dy = evaluator_evaluate_x(p->df, x); + attron(COLOR_PAIR(C_DF)); + graphplot(x, dy); + } + } + attroff(COLOR_PAIR(C_F) | COLOR_PAIR(C_DF)); } static void -graphplot(const Plane *p, float x, float y) +graphplot(float x, float y) { - float xp = PLANE_SCALE(x, p->xmin, p->xmax, 0.0f, p->xmaxs); - float yp = PLANE_SCALE(y, p->ymin, p->ymax, p->ymaxs, 0.0f); + float xp = PLANE_SCALE(x, p->xmin, p->xmax, 0.0f, p->xmaxs); + float yp = PLANE_SCALE(y, p->ymin, p->ymax, p->ymaxs, 0.0f); - mvaddch(yp, xp, '.'); + mvaddch(yp, xp, '.'); } static void menuopts(void) { - WINDOW *opts; - int w, h, wy, wx; - - w = 33; - h = 14; - wy = CENTER(YMAX, h); - wx = CENTER(XMAX, w); - opts = newwin(h, w, wy, wx); - werase(opts); - box(opts, 0, 0); - - /* fill menu */ - mvwprintw(opts, 1, 1, OPT_QUIT); - mvwprintw(opts, 2, 1, OPT_MOVE_UP); - mvwprintw(opts, 3, 1, OPT_MOVE_DOWN); - mvwprintw(opts, 4, 1, OPT_MOVE_LEFT); - mvwprintw(opts, 5, 1, OPT_MOVE_RIGHT); - mvwprintw(opts, 6, 1, OPT_SHOW_DERIVATIVE); - mvwprintw(opts, 7, 1, OPT_NEW_FUNCTION); - mvwprintw(opts, 8, 1, OPT_RESTORE_ZOOM); - mvwprintw(opts, 9, 1, OPT_ZOOM_IN); - mvwprintw(opts, 10, 1, OPT_ZOOM_OUT); - mvwprintw(opts, 12, 1, MSG_QUIT_MENU); - - wrefresh(opts); - wgetch(opts); - werase(opts); - wrefresh(opts); - delwin(opts); + WINDOW *opts; + int w, h, wy, wx; + + w = 33; + h = 14; + wy = CENTER(YMAX, h); + wx = CENTER(XMAX, w); + if ((opts = newwin(h, w, wy, wx)) == NULL) + errx(1, "newwin"); + werase(opts); + wattron(opts, COLOR_PAIR(C_FG)); + box(opts, 0, 0); + + /* fill menu */ + mvwprintw(opts, 1, 1, "q Quit"); + mvwprintw(opts, 2, 1, "Up/k Move up"); + mvwprintw(opts, 3, 1, "Down/j Move down"); + mvwprintw(opts, 4, 1, "Left/h Move left"); + mvwprintw(opts, 5, 1, "Right/l Move right"); + mvwprintw(opts, 6, 1, "d Show derivative"); + mvwprintw(opts, 7, 1, "f New function"); + mvwprintw(opts, 8, 1, "r Restore zoom"); + mvwprintw(opts, 9, 1, "+ Zoom in"); + mvwprintw(opts, 10, 1, "- Zoom out"); + mvwprintw(opts, 12, 1, "Press any key to quit the menu"); + + wrefresh(opts); + wattroff(opts, COLOR_PAIR(C_FG)); + (void)wgetch(opts); + werase(opts); + wrefresh(opts); + delwin(opts); +} + +static void +sighandler(int sig) +{ + switch (sig) { + case SIGINT: /* FALLTHROUGH */ + case SIGTERM: + cleanup(); + exit(0); + case SIGWINCH: + /* TODO: lol... */ + break; + } +} + +static void +cleanup(void) +{ + endwin(); + evaluator_destroy(f); + free(p); } int main(int argc, char *argv[]) { - /* TODO: implement `die` */ - Plane *p; - int key = 0; - - cursesinit(); - p = planeinit(); - exprvalidate(p); - - for (; key != 'q'; key = getch()) { - switch (key) { - case 'k': /* FALLTHROUGH */ - case KEY_UP: - planeshift(p, 0.0f, SHIFT_STEP); - break; - case 'j': /* FALLTHROUGH */ - case KEY_DOWN: - planeshift(p, 0.0f, -SHIFT_STEP); - break; - case 'h': /* FALLTHROUGH */ - case KEY_LEFT: - planeshift(p, -SHIFT_STEP, 0.0f); - break; - case 'l': /* FALLTHROUGH */ - case KEY_RIGHT: - planeshift(p, SHIFT_STEP, 0.0f); - break; - case '+': - zoomhandle(p, ZOOM_IN_FACTOR); - break; - case '-': - zoomhandle(p, ZOOM_OUT_FACTOR); - break; - case 'd': - p->derivshow ^= 1; - break; - case 'r': - zoomrestore(p); - break; - case 'f': - exprvalidate(p); - break; - case 'm': - menuopts(); - break; - } - - erase(); - attron(COLOR_PAIR(1) | A_REVERSE | A_BOLD); - mvprintw(0, 0, "f(x) = %s", evaluator_get_string(f)); - if (p->derivshow) - mvprintw(1, 0, "f'(x) = %s", evaluator_get_string(p->df)); - attroff(COLOR_PAIR(1) | A_REVERSE | A_BOLD); - - axesdraw(p); - graphdraw(p); - refresh(); - } - - endwin(); - free(p); - evaluator_destroy(f); - - return EXIT_SUCCESS; + int key = 0; + + cursesinit(); + planeinit(); + exprvalidate(); + + for (; key != 'q'; key = getch()) { + switch (key) { + case KEY_UP: /* FALLTHROUGH */ + case 'k': + planeshift(0.0f, SHIFT_STEP); + break; + case KEY_DOWN: /* FALLTHROUGH */ + case 'j': + planeshift(0.0f, -SHIFT_STEP); + break; + case KEY_LEFT: /* FALLTHROUGH */ + case 'h': + planeshift(-SHIFT_STEP, 0.0f); + break; + case KEY_RIGHT: /* FALLTHROUGH */ + case 'l': + planeshift(SHIFT_STEP, 0.0f); + break; + case '+': + zoomhandle(1.0f / 1.05f); + break; + case '-': + zoomhandle(1.05f); + break; + case 'd': + p->derivshow ^= 1; + break; + case 'r': + zoomrestore(); + break; + case 'f': + exprvalidate(); + break; + case 'c': + menuopts(); + break; + } + erase(); + attron(COLOR_PAIR(C_FG) | A_REVERSE | A_BOLD); + mvprintw(0, 0, "f(x) = %s", evaluator_get_string(f)); + /* TODO: print controls opt */ + if (p->derivshow) + mvprintw(1, 0, "f'(x) = %s", evaluator_get_string(p->df)); + attroff(A_REVERSE | A_BOLD); + axesdraw(); + attroff(COLOR_PAIR(C_FG)); + graphdraw(); + refresh(); + } + cleanup(); + + return 0; }