]> ncurses.scripts.mit.edu Git - ncurses.git/blobdiff - test/worm.c
ncurses 6.4 - patch 20240420
[ncurses.git] / test / worm.c
index 92bdb254986719d46a2fb42c53a70a11ea14a260..7c4842e4004f3bc772080e9dd9f2607402691e60 100644 (file)
@@ -1,5 +1,6 @@
 /****************************************************************************
 /****************************************************************************
- * Copyright (c) 1998-2013,2016 Free Software Foundation, Inc.              *
+ * Copyright 2018-2020,2022 Thomas E. Dickey                                *
+ * Copyright 1998-2016,2017 Free Software Foundation, Inc.                  *
  *                                                                          *
  * Permission is hereby granted, free of charge, to any person obtaining a  *
  * copy of this software and associated documentation files (the            *
  *                                                                          *
  * Permission is hereby granted, free of charge, to any person obtaining a  *
  * copy of this software and associated documentation files (the            *
 
                July 1995 (esr): worms is now in living color! :-)
 
 
                July 1995 (esr): worms is now in living color! :-)
 
-Options:
-       -f                      fill screen with copies of 'WORM' at start.
-       -l <n>                  set worm length
-       -n <n>                  set number of worms
-       -t                      make worms leave droppings
-       -T <start> <end>        set trace interval
-       -S                      set single-stepping during trace interval
-       -N                      suppress cursor-movement optimization
-
   This program makes a good torture-test for the ncurses cursor-optimization
   code.  You can use -T to set the worm move interval over which movement
   traces will be dumped.  The program stops and waits for one character of
   input at the beginning and end of the interval.
 
   This program makes a good torture-test for the ncurses cursor-optimization
   code.  You can use -T to set the worm move interval over which movement
   traces will be dumped.  The program stops and waits for one character of
   input at the beginning and end of the interval.
 
-  $Id: worm.c,v 1.66 2016/09/17 21:12:30 tom Exp $
+  $Id: worm.c,v 1.89 2022/12/24 20:46:49 tom Exp $
 */
 
 #include <test.priv.h>
 
 */
 
 #include <test.priv.h>
 
+#ifndef NCURSES_VERSION
+#undef TRACE
+#endif
+
 #ifdef USE_PTHREADS
 #include <pthread.h>
 #endif
 #ifdef USE_PTHREADS
 #include <pthread.h>
 #endif
@@ -102,6 +98,7 @@ static unsigned long sequence = 0;
 static bool quitting = FALSE;
 
 static WORM worm[MAX_WORMS];
 static bool quitting = FALSE;
 
 static WORM worm[MAX_WORMS];
+static int max_refs;
 static int **refs;
 static int last_x, last_y;
 
 static int **refs;
 static int last_x, last_y;
 
@@ -110,6 +107,18 @@ static int length = 16, number = 3;
 static chtype trail = ' ';
 
 static unsigned pending;
 static chtype trail = ' ';
 
 static unsigned pending;
+
+#ifdef USE_PTHREADS
+#define Locked(statement) { \
+       pthread_mutex_lock(&pending_mutex); \
+       statement; \
+       pthread_mutex_unlock(&pending_mutex); \
+    }
+pthread_mutex_t pending_mutex;
+#else
+#define Locked(statement) statement
+#endif
+
 #ifdef TRACE
 static int generation, trace_start, trace_end;
 #endif /* TRACE */
 #ifdef TRACE
 static int generation, trace_start, trace_end;
 #endif /* TRACE */
@@ -201,20 +210,34 @@ static const struct options {
 };
 /* *INDENT-ON* */
 
 };
 /* *INDENT-ON* */
 
+#if HAVE_USE_WINDOW
+static int
+safe_wgetch(WINDOW *w, void *data GCC_UNUSED)
+{
+    return wgetch(w);
+}
+static int
+safe_wrefresh(WINDOW *w, void *data GCC_UNUSED)
+{
+    return wrefresh(w);
+}
+#endif
+
+#ifdef KEY_RESIZE
 static void
 failed(const char *s)
 {
     perror(s);
 static void
 failed(const char *s)
 {
     perror(s);
-    endwin();
+    stop_curses();
     ExitProgram(EXIT_FAILURE);
 }
     ExitProgram(EXIT_FAILURE);
 }
+#endif
 
 static void
 cleanup(void)
 {
 
 static void
 cleanup(void)
 {
-    USING_WINDOW(stdscr, wrefresh);
-    curs_set(1);
-    endwin();
+    USING_WINDOW1(stdscr, wrefresh, safe_wrefresh);
+    stop_curses();
 }
 
 static void
 }
 
 static void
@@ -237,13 +260,18 @@ draw_worm(WINDOW *win, void *data)
     WORM *w = (WORM *) data;
     const struct options *op;
     unsigned mask = (unsigned) (~(1 << (w - worm)));
     WORM *w = (WORM *) data;
     const struct options *op;
     unsigned mask = (unsigned) (~(1 << (w - worm)));
-    chtype attrs = w->attrs | ((mask & pending) ? A_REVERSE : 0);
+    chtype attrs;
 
     int x;
     int y;
     int h;
 
     bool done = FALSE;
 
     int x;
     int y;
     int h;
 
     bool done = FALSE;
+    bool is_pending;
+
+    Locked(is_pending = ((mask & pending) != 0));
+
+    attrs = w->attrs | (is_pending ? A_REVERSE : 0);
 
     if ((x = w->xpos[h = w->head]) < 0) {
        wmove(win, y = w->ypos[h] = last_y, x = w->xpos[h] = 0);
 
     if ((x = w->xpos[h = w->head]) < 0) {
        wmove(win, y = w->ypos[h] = last_y, x = w->xpos[h] = 0);
@@ -294,6 +322,7 @@ draw_worm(WINDOW *win, void *data)
     switch (op->nopts) {
     case 0:
        done = TRUE;
     switch (op->nopts) {
     case 0:
        done = TRUE;
+       Trace(("done - draw_worm"));
        break;
     case 1:
        w->orientation = op->opts[0];
        break;
     case 1:
        w->orientation = op->opts[0];
@@ -324,9 +353,12 @@ draw_worm(WINDOW *win, void *data)
 static bool
 quit_worm(int bitnum)
 {
 static bool
 quit_worm(int bitnum)
 {
-    pending = (pending | (unsigned) (1 << bitnum));
+    Locked(pending = (pending | (unsigned) (1 << bitnum)));
+
     napms(10);                 /* let the other thread(s) have a chance */
     napms(10);                 /* let the other thread(s) have a chance */
-    pending = (pending & (unsigned) ~(1 << bitnum));
+
+    Locked(pending = (pending & (unsigned) ~(1 << bitnum)));
+
     return quitting;
 }
 
     return quitting;
 }
 
@@ -336,13 +368,13 @@ start_worm(void *arg)
     unsigned long compare = 0;
     Trace(("start_worm"));
     while (!quit_worm((int) (((struct worm *) arg) - worm))) {
     unsigned long compare = 0;
     Trace(("start_worm"));
     while (!quit_worm((int) (((struct worm *) arg) - worm))) {
-       while (compare < sequence) {
+       for (;;) {
+           bool done = FALSE;
+           Locked(done = (compare >= sequence));
+           if (done)
+               break;
            ++compare;
            ++compare;
-#if HAVE_USE_WINDOW
-           use_window(stdscr, draw_worm, arg);
-#else
-           draw_worm(stdscr, arg);
-#endif
+           USING_WINDOW2(stdscr, draw_worm, arg);
        }
     }
     Trace(("...start_worm (done)"));
        }
     }
     Trace(("...start_worm (done)"));
@@ -367,13 +399,7 @@ draw_all_worms(void)
     }
 #else
     for (n = 0, w = &worm[0]; n < number; n++, w++) {
     }
 #else
     for (n = 0, w = &worm[0]; n < number; n++, w++) {
-       if (
-#if HAVE_USE_WINDOW
-              USING_WINDOW2(stdscr, draw_worm, w)
-#else
-              draw_worm(stdscr, w)
-#endif
-           )
+       if (USING_WINDOW2(stdscr, draw_worm, w))
            done = TRUE;
     }
 #endif
            done = TRUE;
     }
 #endif
@@ -384,17 +410,18 @@ static int
 get_input(void)
 {
     int ch;
 get_input(void)
 {
     int ch;
-    ch = USING_WINDOW(stdscr, wgetch);
+    ch = USING_WINDOW1(stdscr, wgetch, safe_wgetch);
     return ch;
 }
 
 #ifdef KEY_RESIZE
 static int
     return ch;
 }
 
 #ifdef KEY_RESIZE
 static int
-update_refs(WINDOW *win)
+update_refs(WINDOW *win, void *data)
 {
     int x, y;
 
     (void) win;
 {
     int x, y;
 
     (void) win;
+    (void) data;
     if (last_x != COLS - 1) {
        for (y = 0; y <= last_y; y++) {
            refs[y] = typeRealloc(int, (size_t) COLS, refs[y]);
     if (last_x != COLS - 1) {
        for (y = 0; y <= last_y; y++) {
            refs[y] = typeRealloc(int, (size_t) COLS, refs[y]);
@@ -408,6 +435,7 @@ update_refs(WINDOW *win)
     if (last_y != LINES - 1) {
        for (y = LINES; y <= last_y; y++)
            free(refs[y]);
     if (last_y != LINES - 1) {
        for (y = LINES; y <= last_y; y++)
            free(refs[y]);
+       max_refs = LINES;
        refs = typeRealloc(int *, (size_t) LINES, refs);
        for (y = last_y + 1; y < LINES; y++) {
            refs[y] = typeMalloc(int, (size_t) COLS);
        refs = typeRealloc(int *, (size_t) LINES, refs);
        for (y = last_y + 1; y < LINES; y++) {
            refs[y] = typeMalloc(int, (size_t) COLS);
@@ -422,41 +450,73 @@ update_refs(WINDOW *win)
 }
 #endif
 
 }
 #endif
 
+static void
+usage(int ok)
+{
+    static const char *msg[] =
+    {
+       "Usage: worm [options]"
+       ,""
+       ,USAGE_COMMON
+       ,"Options:"
+#if HAVE_USE_DEFAULT_COLORS
+       ," -d       invoke use_default_colors"
+#endif
+       ," -f       fill screen with copies of \"WORM\" at start"
+       ," -l <n>   set length of worms"
+       ," -n <n>   set number of worms"
+       ," -t       leave trail of \".\""
+#ifdef TRACE
+       ," -T <start>,<end> set trace interval"
+       ," -N       suppress cursor-movement optimization"
+#endif
+    };
+    size_t n;
+
+    for (n = 0; n < SIZEOF(msg); n++)
+       fprintf(stderr, "%s\n", msg[n]);
+
+    ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+/* *INDENT-OFF* */
+VERSION_COMMON()
+/* *INDENT-ON* */
+
 int
 main(int argc, char *argv[])
 {
 int
 main(int argc, char *argv[])
 {
+    int ch;
     int x, y;
     int n;
     struct worm *w;
     int *ip;
     bool done = FALSE;
     int x, y;
     int n;
     struct worm *w;
     int *ip;
     bool done = FALSE;
-    int max_refs;
+#if HAVE_USE_DEFAULT_COLORS
+    bool opt_d = FALSE;
+#endif
 
     setlocale(LC_ALL, "");
 
 
     setlocale(LC_ALL, "");
 
-    for (x = 1; x < argc; x++) {
-       char *p;
-       p = argv[x];
-       if (*p == '-')
-           p++;
-       switch (*p) {
+    while ((ch = getopt(argc, argv, OPTS_COMMON "dfl:n:tT:N")) != -1) {
+       switch (ch) {
+#if HAVE_USE_DEFAULT_COLORS
+       case 'd':
+           opt_d = TRUE;
+           break;
+#endif
        case 'f':
            field = "WORM";
            break;
        case 'l':
        case 'f':
            field = "WORM";
            break;
        case 'l':
-           if (++x == argc)
-               goto usage;
-           if ((length = atoi(argv[x])) < 2 || length > MAX_LENGTH) {
+           if ((length = atoi(optarg)) < 2 || length > MAX_LENGTH) {
                fprintf(stderr, "%s: Invalid length\n", *argv);
                fprintf(stderr, "%s: Invalid length\n", *argv);
-               ExitProgram(EXIT_FAILURE);
+               usage(FALSE);
            }
            break;
        case 'n':
            }
            break;
        case 'n':
-           if (++x == argc)
-               goto usage;
-           if ((number = atoi(argv[x])) < 1 || number > MAX_WORMS) {
+           if ((number = atoi(optarg)) < 1 || number > MAX_WORMS) {
                fprintf(stderr, "%s: Invalid number of worms\n", *argv);
                fprintf(stderr, "%s: Invalid number of worms\n", *argv);
-               ExitProgram(EXIT_FAILURE);
+               usage(FALSE);
            }
            break;
        case 't':
            }
            break;
        case 't':
@@ -464,20 +524,23 @@ main(int argc, char *argv[])
            break;
 #ifdef TRACE
        case 'T':
            break;
 #ifdef TRACE
        case 'T':
-           trace_start = atoi(argv[++x]);
-           trace_end = atoi(argv[++x]);
+           if (sscanf(optarg, "%d,%d", &trace_start, &trace_end) != 2)
+               usage(FALSE);
            break;
        case 'N':
            _nc_optimize_enable ^= OPTIMIZE_ALL;        /* declared by ncurses */
            break;
 #endif /* TRACE */
            break;
        case 'N':
            _nc_optimize_enable ^= OPTIMIZE_ALL;        /* declared by ncurses */
            break;
 #endif /* TRACE */
+       case OPTS_VERSION:
+           show_version(argv);
+           ExitProgram(EXIT_SUCCESS);
        default:
        default:
-         usage:
-           fprintf(stderr,
-                   "usage: %s [-field] [-length #] [-number #] [-trail]\n", *argv);
-           ExitProgram(EXIT_FAILURE);
+           usage(ch == OPTS_USAGE);
+           /* NOTREACHED */
        }
     }
        }
     }
+    if (optind < argc)
+       usage(FALSE);
 
     signal(SIGINT, onsig);
     initscr();
 
     signal(SIGINT, onsig);
     initscr();
@@ -495,7 +558,7 @@ main(int argc, char *argv[])
        int bg = COLOR_BLACK;
        start_color();
 #if HAVE_USE_DEFAULT_COLORS
        int bg = COLOR_BLACK;
        start_color();
 #if HAVE_USE_DEFAULT_COLORS
-       if (use_default_colors() == OK)
+       if (opt_d && (use_default_colors() == OK))
            bg = -1;
 #endif
 
            bg = -1;
 #endif
 
@@ -558,21 +621,23 @@ main(int argc, char *argv[])
            }
        }
     }
            }
        }
     }
-    USING_WINDOW(stdscr, wrefresh);
+    USING_WINDOW1(stdscr, wrefresh, safe_wrefresh);
     nodelay(stdscr, TRUE);
 
     nodelay(stdscr, TRUE);
 
-    while (!done) {
-       int ch;
+#ifdef USE_PTHREADS
+    pthread_mutex_init(&pending_mutex, NULL);
+#endif
 
 
-       ++sequence;
+    while (!done) {
+       Locked(++sequence);
        if ((ch = get_input()) > 0) {
 #ifdef TRACE
            if (trace_start || trace_end) {
                if (generation == trace_start) {
        if ((ch = get_input()) > 0) {
 #ifdef TRACE
            if (trace_start || trace_end) {
                if (generation == trace_start) {
-                   trace(TRACE_CALLS);
+                   curses_trace(TRACE_CALLS);
                    get_input();
                } else if (generation == trace_end) {
                    get_input();
                } else if (generation == trace_end) {
-                   trace(0);
+                   curses_trace(0);
                    get_input();
                }
 
                    get_input();
                }
 
@@ -593,6 +658,7 @@ main(int argc, char *argv[])
            if (ch == 'q') {
                quitting = TRUE;
                done = TRUE;
            if (ch == 'q') {
                quitting = TRUE;
                done = TRUE;
+               Trace(("done - quitting"));
                continue;
            } else if (ch == 's') {
                nodelay(stdscr, FALSE);
                continue;
            } else if (ch == 's') {
                nodelay(stdscr, FALSE);
@@ -603,21 +669,11 @@ main(int argc, char *argv[])
 
        done = draw_all_worms();
        napms(10);
 
        done = draw_all_worms();
        napms(10);
-       USING_WINDOW(stdscr, wrefresh);
+       USING_WINDOW1(stdscr, wrefresh, safe_wrefresh);
     }
 
     Trace(("Cleanup"));
     cleanup();
     }
 
     Trace(("Cleanup"));
     cleanup();
-#ifdef NO_LEAKS
-    for (y = 0; y < max_refs; y++) {
-       free(refs[y]);
-    }
-    free(refs);
-    for (n = number, w = &worm[0]; --n >= 0; w++) {
-       free(w->xpos);
-       free(w->ypos);
-    }
-#endif
 #ifdef USE_PTHREADS
     /*
      * Do this just in case one of the threads did not really exit.
 #ifdef USE_PTHREADS
     /*
      * Do this just in case one of the threads did not really exit.
@@ -626,6 +682,16 @@ main(int argc, char *argv[])
     for (n = 0; n < number; n++) {
        pthread_join(worm[n].thread, NULL);
     }
     for (n = 0; n < number; n++) {
        pthread_join(worm[n].thread, NULL);
     }
+#endif
+#if NO_LEAKS
+    for (y = 0; y < max_refs; y++) {
+       free(refs[y]);
+    }
+    free(refs);
+    for (n = number, w = &worm[0]; --n >= 0; w++) {
+       free(w->xpos);
+       free(w->ypos);
+    }
 #endif
     ExitProgram(EXIT_SUCCESS);
 }
 #endif
     ExitProgram(EXIT_SUCCESS);
 }