diff --git a/games/Makefile b/games/Makefile index 4cef47099e25..450df04fe1c3 100644 --- a/games/Makefile +++ b/games/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.30 2020/11/10 08:49:08 kamil Exp $ +# $NetBSD: Makefile,v 1.31 2021/01/02 03:21:39 nat Exp $ # @(#)Makefile 8.3 (Berkeley) 7/24/94 # Missing: dungeon @@ -13,7 +13,7 @@ SUBDIR= adventure arithmetic atc \ dm factor fish fortune gomoku \ hack hals_end hangman hunt larn mille monop morse number \ phantasia pig pom ppt primes quiz \ - rain random robots rogue sail snake tetris trek \ + rain random robots rogue sail snake testpat tetris trek \ wargames warp worm worms wtf wump .if ${MKCXX} != "no" diff --git a/games/testpat/Makefile b/games/testpat/Makefile new file mode 100644 index 000000000000..675cd9b7363c --- /dev/null +++ b/games/testpat/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2021/01/02 03:21:39 nat Exp $ + +PROG= testpat +MAN= testpat.6 +DPADD= ${LIBMATH} ${LIBCURSES} ${LIBTERMINFO} +LDADD= -lcurses -lm -lterminfo + +.include diff --git a/games/testpat/testpat.6 b/games/testpat/testpat.6 new file mode 100644 index 000000000000..1bf42c61fd00 --- /dev/null +++ b/games/testpat/testpat.6 @@ -0,0 +1,50 @@ +.\" $NetBSD: testpat.6,v 1.1 2021/01/02 03:21:39 nat Exp $ +.\" +.\" Copyright (c) 2021 Nathanial Sloss +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +.\" +.Dd Janurary 2, 2021 +.Dt TESTPAT 6 +.Os +.Sh NAME +.Nm testpat +.Nd display test pattern +.Sh SYNOPSIS +.Nm +.Op title +.Sh DESCRIPTION +The +.Nm +command displays a test pattern on a color capable terminal using +.Xr curses 3 . + +Title is a string to display as the title of the test pattern. +.Sh SEE ALSO +.Xr curses 3 +.Sh HISTORY +.Nm +appeared in +.Nx 10.0 . +.Sh AUTHORS +Nathanial Sloss diff --git a/games/testpat/testpat.c b/games/testpat/testpat.c new file mode 100644 index 000000000000..95bf4ae80d74 --- /dev/null +++ b/games/testpat/testpat.c @@ -0,0 +1,459 @@ +/* $NetBSD: testpat.c,v 1.1 2021/01/02 03:21:39 nat Exp $ */ + +/*- + * Copyright (c) 2016 Nathanial Sloss + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + int i, col, colour, line, x_limit, y_limit, colourOK, spacing; + int xpos, ypos, spacing_residual, spacing_start, spacing_end; + int grid_x, grid_y; + float grid_unit; + + char title[255] = "NetBSD"; + int numcolours = 6; + + int colour_list[6] = { + COLOR_YELLOW, + COLOR_CYAN, + COLOR_GREEN, + COLOR_MAGENTA, + COLOR_RED, + COLOR_BLUE, + }; + + float coord_x, circle_int; + float a_axis, b_axis; + + if (!(initscr())) { + printf("\nUnknown terminal type."); + printf("\n"); + return EXIT_FAILURE; + } + + if (argc > 2) { + endwin(); + errx(EINVAL, "usage: testpat [title]"); + } + + if (argc == 2 && strlen(argv[1]) < (size_t)COLS) + snprintf(title, sizeof(title), "%s", argv[1]); + else if (argc == 2 && (int)strlen(argv[1]) > COLS) { + endwin(); + errx(EINVAL, "title string must be less than display columns"); + } + + colourOK = has_colors(); + + if (COLS < 13 || LINES < 13) { + endwin(); + printf("\nTerminal size must be at least 72x25."); + printf("\n"); + return EXIT_FAILURE; + } + + if (colourOK) { + start_color(); + + init_pair( 0, COLOR_WHITE, COLOR_BLACK ); + init_pair( 1, COLOR_WHITE, COLOR_RED ); + init_pair( 2, COLOR_WHITE, COLOR_GREEN ); + init_pair( 3, COLOR_WHITE, COLOR_YELLOW ); + init_pair( 4, COLOR_WHITE, COLOR_BLUE ); + init_pair( 5, COLOR_WHITE, COLOR_MAGENTA ); + init_pair( 6, COLOR_WHITE, COLOR_CYAN ); + init_pair( 7, COLOR_BLACK, COLOR_WHITE ); + + attrset(COLOR_PAIR(0)); + } + + x_limit = (COLS - 1) / 2; + x_limit = x_limit * 2; + y_limit = (LINES - 2) / 2; + y_limit = y_limit * 2; + spacing = 2 * y_limit / numcolours; + spacing_residual = ((2 * y_limit) % numcolours) / 2; + a_axis = y_limit / 2; + b_axis = y_limit; + grid_unit = b_axis / 13; + grid_y = grid_unit; + grid_x = grid_unit * 2; + + int circle_pos[y_limit][2]; + memset(circle_pos, -1, sizeof(circle_pos)); + + for (i = 0; i < y_limit; i++) { + /* Draw an elipse (looks more circular.) */ + circle_int = (i - a_axis) / a_axis; + circle_int = 1 - powf(circle_int, 2); + circle_int = circle_int * powf(b_axis, 2); +#if 0 + /* Draw a circle, commented out as elipse looks better.*/ + circle_int = powf(a_axis, 2) - powf(i - a_axis, 2); +#endif + coord_x = sqrtf(circle_int); + circle_pos[i][0] = (-coord_x + ((float)x_limit / 2)); + circle_pos[i][1] = (coord_x + ((float)x_limit / 2)); + } + + clear(); + + attron(A_ALTCHARSET); + move(0, 0); + + /* Draw a grid. */ + for (line = 1; line < y_limit; line += grid_y) { + for (col = 1; col < x_limit; col = col + grid_x) { + xpos = col; + while ((xpos < col + grid_x - 1) && (xpos < + x_limit)) { + mvaddch(line + grid_y - 1, xpos, 113 | + A_ALTCHARSET); + xpos++; + } + if (xpos < x_limit) + mvaddch(line + grid_y - 1, xpos, 110 | + A_ALTCHARSET); + } + ypos = line; + while (ypos < line + grid_y - 1) { + for (col = grid_x - 1; col < x_limit; col += grid_x) { + mvaddch(ypos, col + 1, 120 | A_ALTCHARSET); + } + ypos++; + } + } + + for (line = 1; line < y_limit; line += grid_y) { + mvaddch(line + grid_y - 1, 0, 116 | A_ALTCHARSET); + mvaddch(line + grid_y - 1, x_limit, 117 | A_ALTCHARSET); + + ypos = line; + while (ypos < line + grid_y - 1) { + mvaddch(ypos, 0, 120 | A_ALTCHARSET); + mvaddch(ypos, x_limit, 120 | A_ALTCHARSET); + ypos++; + } + } + + for (col = 1; col < x_limit; col += grid_x) { + mvaddch(0, col + grid_x - 1, 119 | A_ALTCHARSET); + mvaddch(y_limit, col + grid_x - 1, 118 | A_ALTCHARSET); + + xpos = col; + while ((xpos < col + grid_x - 1) && (xpos < x_limit)) { + mvaddch(0, xpos, 113 | A_ALTCHARSET); + mvaddch(y_limit, xpos, 113 | A_ALTCHARSET); + xpos++; + } + } + + mvaddch(0, 0, 108 | A_ALTCHARSET); + mvaddch(0, x_limit, 107 | A_ALTCHARSET); + mvaddch(y_limit, 0, 109 | A_ALTCHARSET); + mvaddch(y_limit, x_limit, 106 | A_ALTCHARSET); + + /* Draw a white circle. */ + for (i = 1; i < y_limit; i++) { + for (col = circle_pos[i][0]; col <= circle_pos[i][1]; col++) { + mvaddch(i, col, 32 | A_REVERSE); + } + } + + /* Add title segment. */ + for (i = roundf(1 * grid_unit); i < roundf(2 * grid_unit); i++) { + if (colourOK) + attrset(COLOR_PAIR(COLOR_BLACK)); + + for (col = roundf((4 * grid_unit * 2) + + circle_pos[y_limit /2][0]); col <= roundf((9 * grid_unit + * 2) + circle_pos[y_limit /2][0]); col++) + mvaddch(i, col, ' '); + } + + i = roundf(1.4 * grid_unit); + + col = y_limit - (strlen(title) / 2) + circle_pos[y_limit / 2][0]; + mvprintw(i, col, "%s", title); + + /* Add black segments at top. */ + for (line = roundf(2 * grid_unit); line < 4 * grid_unit; line++) { + if (colourOK) + attrset(COLOR_PAIR(COLOR_BLACK)); + + for (col = 0; col <= roundf((3.5 * grid_unit * 2)); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos <= circle_pos[line][1]) + mvaddch(line, xpos, ' '); + } + + for (col = roundf((9.5 * grid_unit * 2)); col < + roundf((13 * grid_unit * 2)); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos <= circle_pos[line][1]) + mvaddch(line, xpos, ' '); + } + } + + /* Add black and white squares close to top. */ + int gap = (circle_pos[(int)(5 * grid_unit)][1] - + circle_pos[(int)(5 * grid_unit)][0]) / 13; + + for (i = roundf(3 * grid_unit); i < roundf(4 * grid_unit); i++) { + for (xpos = 0; xpos <= x_limit; xpos += 2 * gap) { + if (colourOK) + attrset(COLOR_PAIR(COLOR_BLACK)); + + for (col = xpos; col < xpos + gap; col++) { + if (col >= circle_pos[i][0] && + col <= circle_pos[i][1]) + mvaddch(i, col, ' '); + } + + if (colourOK) + attrset(COLOR_PAIR(COLOR_WHITE)); + + for (col = xpos + gap ; col < xpos + (2 * gap); + col++) { + if (col >= circle_pos[i][0] && + col <= circle_pos[i][1]) + mvaddch(i, col, ' '); + } + } + } + + /* Add colour bars. */ + for (i = 0; i < numcolours; i++) { + colour = colour_list[i]; + if (colourOK) + attrset(COLOR_PAIR(colour)); + + if (i == 0) + spacing_start = 0; + else + spacing_start = (spacing * i) + spacing_residual; + + if (i == numcolours - 1) + spacing_end = circle_pos[y_limit / 2][1]; + else + spacing_end = (spacing * (i + 1)) + spacing_residual; + + for (line = roundf(4 * grid_unit); line < (y_limit / 2); + line++) { + for (col = spacing_start; col < spacing_end; col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos <= circle_pos[line][1]) + mvprintw(line, xpos, " "); + } + } + } + + /* Add black segment under centre line. */ + for (line = y_limit / 2; line < (9.5 * grid_unit); line++) { + if (colourOK) + attrset(COLOR_PAIR(COLOR_BLACK)); + + for (col = circle_pos[line][0]; col <= circle_pos[line][1]; + col++) + mvaddch(line, col, ' '); + + for (col = roundf((1.5 * grid_unit * 2)); col < + roundf((4.3 * grid_unit * 2)); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, 120 | A_ALTCHARSET); + } + + for (col = roundf((4.3 * grid_unit * 2)); col < + roundf((7.6 * grid_unit * 2)); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, '|'); + } + + for (col = roundf((7.6 * grid_unit * 2)); col < + roundf((11.5 * grid_unit * 2)); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, 97 | A_ALTCHARSET); + } + } + + /* Add black segment close to bottom. */ + for (line = roundf(9.5 * grid_unit); line <= (10.5 * grid_unit); + line++) { + if (colourOK) + attrset(COLOR_PAIR(COLOR_BLACK)); + + for (col = roundf((0 * grid_unit * 2)); col < + roundf((4 * grid_unit * 2)); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, ' '); + } + + for (col = roundf((4 * grid_unit * 2)); col < + roundf((6.5 * grid_unit * 2)); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, 97 | A_ALTCHARSET); + } + + if (colourOK) + attrset(COLOR_PAIR(COLOR_WHITE)); + + for (col = roundf((6.5 * grid_unit * 2)); col < + roundf((9 * grid_unit * 2)); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, 97 | A_ALTCHARSET); + } + + for (col = roundf((9 * grid_unit * 2)); col < + roundf((13 * grid_unit * 2)); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, ' '); + } + } + + /* Add name segment close to bottom. */ + for (line = roundf(10.5 * grid_unit); line < (12 * grid_unit); + line++) { + if (colourOK) + attrset(COLOR_PAIR(COLOR_BLACK)); + + for (col = roundf(3.5 * grid_unit * 2); col <= roundf(9.5 * + grid_unit * 2); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, ' '); + } + + if (colourOK) + attrset(COLOR_PAIR(COLOR_WHITE)); + + for (col = roundf(0 * grid_unit * 2); col <= roundf(3.5 * + grid_unit * 2); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, ' '); + } + + for (col = roundf(9.5 * grid_unit * 2); col <= roundf(13 * + grid_unit * 2); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, ' '); + } + } + + /* Add yellow segment at bottom. */ + for (line = 12 * grid_unit; line < y_limit; line++) { + if (colourOK) + attrset(COLOR_PAIR(COLOR_YELLOW)); + + for (col = circle_pos[line][0]; col <= circle_pos[line][1]; + col++) + mvaddch(line, col, ' '); + + if (colourOK) + attrset(COLOR_PAIR(COLOR_RED)); + + for (col = roundf((6 * grid_unit * 2)); col < + roundf((7 * grid_unit * 2)); col++) { + xpos = col + circle_pos[y_limit / 2][0]; + if (xpos >= circle_pos[line][0] && + xpos < circle_pos[line][1]) + mvaddch(line, xpos, ' '); + } + } + + if (colourOK) + attrset(COLOR_PAIR(COLOR_BLACK)); + + for (line = 6 * grid_unit; line <= (7 * grid_unit) + 1; line++) { + if (colourOK) + attrset(COLOR_PAIR(COLOR_BLACK)); + + col = x_limit / 2; + if (line != a_axis) { + mvaddch(line, col - 1, ' '); + mvaddch(line, col, 120 | A_ALTCHARSET); + mvaddch(line, col + 1, ' '); + } + } + + line = y_limit / 2; + for (col = 1; col < x_limit; col = col + grid_x) { + xpos = col; + while (xpos < col + grid_x - 1) { + if (xpos >= circle_pos[line][0] && xpos < circle_pos[line][1]) + mvaddch(line, xpos, 113 | A_ALTCHARSET); + xpos++; + } + if (xpos >= circle_pos[line][0] && xpos < circle_pos[line][1]) + mvaddch(line, xpos, 110 | A_ALTCHARSET); + } + + line = y_limit / 2; + col = x_limit / 2; + mvaddch(line, col, 110 | A_ALTCHARSET); + mvaddch(line, col - 1, 113 | A_ALTCHARSET); + mvaddch(line, col + 1, 113 | A_ALTCHARSET); + + refresh(); + + getch(); + + endwin(); + + return EXIT_SUCCESS; +} +