[BACK]Return to ttyplay.c CVS log [TXT][DIR] Up to [contributed] / dgamelaunch-openbsd

Annotation of dgamelaunch-openbsd/ttyplay.c, Revision 1.2

1.1       rubenllo    1: /*
                      2:  * Copyright (c) 2000 Satoru Takabayashi <satoru@namazu.org>
                      3:  * All rights reserved.
                      4:  *
                      5:  * Redistribution and use in source and binary forms, with or without
                      6:  * modification, are permitted provided that the following conditions
                      7:  * are met:
                      8:  * 1. Redistributions of source code must retain the above copyright
                      9:  *    notice, this list of conditions and the following disclaimer.
                     10:  * 2. Redistributions in binary form must reproduce the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer in the
                     12:  *    documentation and/or other materials provided with the distribution.
                     13:  * 3. All advertising materials mentioning features or use of this software
                     14:  *    must display the following acknowledgement:
                     15:  *     This product includes software developed by the University of
                     16:  *     California, Berkeley and its contributors.
                     17:  * 4. Neither the name of the University nor the names of its contributors
                     18:  *    may be used to endorse or promote products derived from this software
                     19:  *    without specific prior written permission.
                     20:  *
                     21:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     22:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     23:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     24:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     25:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     26:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     27:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     28:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     29:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     30:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     31:  * SUCH DAMAGE.
                     32:  */
                     33:
                     34: #include "config.h"
                     35:
                     36: #define _GNU_SOURCE /* need sighandler_t */
                     37:
                     38: #include <sys/types.h>
                     39: #include <sys/time.h>
                     40: #include <sys/stat.h>
                     41: #ifdef HAVE_KQUEUE
                     42: #include <sys/event.h>
                     43: #endif
                     44:
                     45: #include <stdio.h>
                     46: #include <stdlib.h>
                     47: #include <assert.h>
                     48: #include <unistd.h>
                     49: #include <termios.h>
                     50: #include <string.h>
                     51: #include <curses.h>
                     52: #include <signal.h>
                     53: #include <errno.h>
                     54:
                     55: #include "dgamelaunch.h"
                     56: #include "ttyplay.h"
                     57: #include "ttyrec.h"
                     58: #include "io.h"
                     59: #include "stripgfx.h"
                     60:
1.2     ! rubenllo   61: #if defined(__MACH__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
1.1       rubenllo   62: typedef void (*sighandler_t)(int);
                     63: #endif
                     64:
                     65: int stripped = NO_GRAPHICS;
                     66: static int got_sigwinch = 0;
                     67:
                     68: static int term_resizex = -1;
                     69: static int term_resizey = -1;
                     70:
                     71: void
                     72: ttyplay_sigwinch_func(int sig)
                     73: {
                     74:     signal(SIGWINCH, ttyplay_sigwinch_func);
                     75:     got_sigwinch = 1;
                     76: }
                     77:
                     78: struct timeval
                     79: timeval_diff (struct timeval tv1, struct timeval tv2)
                     80: {
                     81:   struct timeval diff;
                     82:
                     83:   diff.tv_sec = tv2.tv_sec - tv1.tv_sec;
                     84:   diff.tv_usec = tv2.tv_usec - tv1.tv_usec;
                     85:   if (diff.tv_usec < 0)
                     86:     {
                     87:       diff.tv_sec--;
                     88:       diff.tv_usec += 1000000;
                     89:     }
                     90:
                     91:   return diff;
                     92: }
                     93:
                     94: struct timeval
                     95: timeval_div (struct timeval tv1, double n)
                     96: {
                     97:   double x = ((double) tv1.tv_sec + (double) tv1.tv_usec / 1000000.0) / n;
                     98:   struct timeval div;
                     99:
                    100:   div.tv_sec = (int) x;
                    101:   div.tv_usec = (x - (int) x) * 1000000;
                    102:
                    103:   return div;
                    104: }
                    105:
                    106: double
                    107: ttywait (struct timeval prev, struct timeval cur, double speed)
                    108: {
                    109:   struct timeval diff = timeval_diff (prev, cur);
                    110:
                    111:   assert (speed != 0);
                    112:   diff = timeval_div (diff, speed);
                    113:
                    114:   select (1, NULL, NULL, NULL, &diff); /* skip if a user hits any key */
                    115:
                    116:   return speed;
                    117: }
                    118:
                    119: double
                    120: ttynowait (struct timeval prev, struct timeval cur, double speed)
                    121: {
                    122:   return 0;                     /* Speed isn't important. */
                    123: }
                    124:
                    125: int
                    126: kbhit(void)
                    127: {
                    128:     int i = 0;
                    129:     nodelay(stdscr, TRUE);
                    130:     timeout(0);
                    131:     i = wgetch(stdscr);
                    132:     nodelay(stdscr, FALSE);
                    133:
                    134:     if (i == -1)
                    135:         i = 0;
                    136:     else
                    137:         ungetch(i);
                    138:     return (i);
                    139: }
                    140:
                    141: int
                    142: ttyplay_keyboard_action(int c)
                    143: {
                    144:     struct termios t;
                    145:     switch (c)
                    146:     {
                    147:     case ERR:
                    148:     case 'q':
                    149:         return READ_QUIT;
                    150:     case 'r':
                    151:         if (term_resizex > 0 && term_resizey > 0) {
                    152:             printf ("\033[8;%d;%dt", term_resizey, term_resizex);
                    153:             return READ_RESTART;
                    154:         }
                    155:         break;
                    156:     case 's':
                    157:         switch (stripped)
                    158:         {
                    159:        case NO_GRAPHICS: populate_gfx_array ((stripped = DEC_GRAPHICS)); break;
                    160:        case DEC_GRAPHICS: populate_gfx_array ((stripped = IBM_GRAPHICS)); break;
                    161:        case IBM_GRAPHICS: populate_gfx_array ((stripped = NO_GRAPHICS)); break;
                    162:         }
                    163:         return READ_RESTART;
                    164:
                    165:     case 'm':
                    166:         tcgetattr (0, &t);
                    167:         if (!loggedin)
                    168:         {
                    169:             initcurses();
                    170:             loginprompt(1);
                    171:         }
                    172:         if (loggedin)
                    173:         {
                    174:             initcurses ();
                    175:             domailuser (chosen_name);
                    176:         }
                    177:         endwin ();
                    178:         tcsetattr (0, TCSANOW, &t);
                    179:         return READ_RESTART;
                    180:     case '?':
                    181:         tcgetattr (0, &t);
                    182:         initcurses();
                    183:         (void) runmenuloop(dgl_find_menu("watchmenu_help"));
                    184:         endwin ();
                    185:         tcsetattr (0, TCSANOW, &t);
                    186:         return READ_RESTART;
                    187:     }
                    188:     return (READ_DATA);
                    189: }
                    190:
                    191: int
                    192: ttyread (FILE * fp, Header * h, char **buf, int pread)
                    193: {
                    194:   long offset;
                    195:   int kb = kbhit();
                    196:
                    197:   if (kb == ERR) return READ_QUIT;
                    198:   else if (kb) {
                    199:       const int c = dgl_getch();
                    200:       const int action = ttyplay_keyboard_action(c);
                    201:       if (action != READ_DATA)
                    202:          return (action);
                    203:   }
                    204:
                    205:   /* do this BEFORE header read, hlen bug */
                    206:   offset = ftell (fp);
                    207:
                    208:   if (read_header (fp, h) == 0)
                    209:     {
                    210:       return READ_EOF;
                    211:     }
                    212:
                    213:   /* length should never be longer than one BUFSIZ */
                    214:   if (h->len > BUFSIZ)
                    215:     {
                    216:       fprintf (stderr, "h->len too big (%ld) limit %ld\n",
                    217:                      (long)h->len, (long)BUFSIZ);
                    218:       return READ_QUIT;
                    219:     }
                    220:
                    221:   *buf = malloc (h->len + 1);
                    222:   if (*buf == NULL)
                    223:     {
                    224:       perror ("malloc");
                    225:       return READ_QUIT;
                    226:     }
                    227:
                    228:   if (fread (*buf, 1, h->len, fp) != h->len)
                    229:     {
                    230:       fseek (fp, offset, SEEK_SET);
                    231:       return READ_EOF;
                    232:     }
                    233:   (*buf)[h->len] = 0;
                    234:   return READ_DATA;
                    235: }
                    236:
                    237: int
                    238: ttypread (FILE * fp, Header * h, char **buf, int pread)
                    239: {
                    240:   int n;
                    241: #ifdef HAVE_KQUEUE
                    242:   struct kevent evt[2];
                    243:   static int kq = -1;
                    244: #endif
                    245:   struct timeval w = { 0, 100000 };
                    246:   struct timeval origw = { 0, 100000 };
                    247:   int counter = 0;
                    248:   fd_set readfs;
                    249:   int doread = 0;
                    250:   int action = READ_DATA;
                    251:
                    252: #ifdef HAVE_KQUEUE
                    253:   if (kq == -1)
                    254:     kq = kqueue ();
                    255:   if (kq == -1)
                    256:     {
                    257:       printf ("kqueue() failed.\n");
                    258:       return READ_QUIT;
                    259:     }
                    260: #endif
                    261:
                    262:   /*
                    263:    * Read persistently just like tail -f.
                    264:    */
                    265:   while ((action = ttyread (fp, h, buf, 1)) == READ_EOF)
                    266:     {
                    267:       idle_alarm_reset();
                    268:       fflush(stdout);
                    269:       clearerr (fp);
                    270: #ifdef HAVE_KQUEUE
                    271:       n = -1;
                    272:       if (kq != -2)
                    273:       {
                    274:        EV_SET (&evt[0], STDIN_FILENO, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, NULL);
                    275:        EV_SET (&evt[1], fileno (fp), EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, NULL);
                    276:        n = kevent (kq, evt, 2, evt, 1, NULL);
                    277:        doread = (n >= 1 && evt[0].ident == STDIN_FILENO &&
                    278:          evt[0].filter == EVFILT_READ) ||
                    279:          (n >= 2 && evt[1].ident == STDIN_FILENO &&
                    280:           evt[1].filter == EVFILT_READ);
                    281:        if (n == -1)
                    282:          {
                    283:            /*
                    284:             * Perhaps kevent(2) doesn't work on this fstype,
                    285:             * use select(2) instead. Never use kevent again, assuming all
                    286:             * active ttyrecs are on the same fstype.
                    287:             */
                    288:            close(kq);
                    289:            kq = -2;
                    290:          }
                    291:       }
                    292:       if (n == -1)
                    293: #endif
                    294:       {
                    295:        if (counter++ > (20 * 60 * 10))
                    296:          {
                    297:            /*
                    298:             * The reason for this timeout is that the select() method uses
                    299:             * some CPU in waiting. The kqueue() method does not do that, so it
                    300:             * does not need the timeout.
                    301:             */
                    302:            endwin ();
                    303:            printf ("Exiting due to 20 minutes of inactivity.\n");
                    304:            return READ_QUIT;
                    305:          }
                    306:        FD_ZERO (&readfs);
                    307:        FD_SET (STDIN_FILENO, &readfs);
                    308:        n = select (1, &readfs, NULL, NULL, &w);
                    309:        w = origw;
                    310:        doread = n >= 1 && FD_ISSET (0, &readfs);
                    311:       }
                    312:       if (n == -1)
                    313:        {
                    314:            if ((errno == EINTR) && got_sigwinch) {
                    315:                got_sigwinch = 0;
                    316:                return READ_RESTART;
                    317:            } else {
                    318:                printf("select()/kevent() failed.\n");
                    319:                return READ_QUIT;
                    320:            }
                    321:        }
                    322:       if (doread)
                    323:         {                       /* user hits a character? */
                    324:          const int c = dgl_getch();
                    325:          action = ttyplay_keyboard_action(c);
                    326:          if (action != READ_DATA)
                    327:              return action;
                    328:
                    329:         }
                    330:     }
                    331:   return (action);
                    332: }
                    333:
                    334: void
                    335: ttywrite (char *buf, int len)
                    336: {
                    337:   int i;
                    338:
                    339:   for (i = 0; i < len; i++)
                    340:   {
                    341:     if (stripped != NO_GRAPHICS)
                    342:       buf[i] = strip_gfx (buf[i]);
                    343:   }
                    344:
                    345:   fwrite (buf, 1, len, stdout);
                    346: }
                    347:
                    348: void
                    349: ttynowrite (char *buf, int len)
                    350: {
                    351:   /* do nothing */
                    352: }
                    353:
                    354: int
                    355: ttyplay (FILE * fp, double speed, ReadFunc read_func,
                    356:          WriteFunc write_func, WaitFunc wait_func, off_t offset)
                    357: {
                    358:   int first_time = 1;
                    359:   int r = READ_EOF;
                    360:   struct timeval prev;
                    361:
                    362:   /* for dtype's attempt to get the last clrscr and playback from there */
                    363:   if (offset != -1)
                    364:     {
                    365:       fseek (fp, offset, SEEK_SET);
                    366:     }
                    367:
                    368:   while (1)
                    369:     {
                    370:       char *buf;
                    371:       Header h;
                    372:
                    373:       r = read_func (fp, &h, &buf, 0);
                    374:       if (r != READ_DATA)
                    375:         {
                    376:           break;
                    377:         }
                    378:
                    379:       if (!first_time)
                    380:         {
                    381:           speed = wait_func (prev, h.tv, speed);
                    382:         }
                    383:       first_time = 0;
                    384:
                    385:       write_func (buf, h.len);
                    386:       prev = h.tv;
                    387:       free (buf);
                    388:     }
                    389:   return r;
                    390: }
                    391:
                    392: static off_t
                    393: find_last_string_in_file(FILE * fp, const char *seq)
                    394: {
                    395:     char buf[512];
                    396:     struct stat mystat;
                    397:     off_t offset = 0L;
                    398:     const long readsz = sizeof(buf);
                    399:     int bytes_read = 0;
                    400:     const int seqlen = strlen(seq);
                    401:     const char *reset_pos = seq + seqlen - 1;
                    402:     const char *match_pos = reset_pos;
                    403:
                    404:     fstat(fileno (fp), &mystat);
                    405:     offset = mystat.st_size - readsz;
                    406:     if (offset < 0)
                    407:         offset = 0;
                    408:     while (1)
                    409:     {
                    410:         const char *search_pos = 0;
                    411:
                    412:         fseeko(fp, offset, SEEK_SET);
                    413:         bytes_read = fread(buf, 1, readsz, fp);
                    414:
                    415:         if (bytes_read <= 0)
                    416:             break;
                    417:
                    418:         search_pos = buf + bytes_read - 1;
                    419:         while (search_pos >= buf)
                    420:         {
                    421:             int matched = *search_pos == *match_pos;
                    422:             if (!matched && match_pos != reset_pos)
                    423:             {
                    424:                 match_pos = reset_pos;
                    425:                 matched = *search_pos == *match_pos;
                    426:             }
                    427:             if (matched)
                    428:             {
                    429:                 if (match_pos == seq)
                    430:                     return offset + (search_pos - buf);
                    431:                 --match_pos;
                    432:             }
                    433:             --search_pos;
                    434:         }
                    435:
                    436:         // If we've reached the start of the file, exit.
                    437:         if (!offset)
                    438:             break;
                    439:
                    440:         offset -= readsz;
                    441:         if (offset < 0)
                    442:             offset = 0;
                    443:     }
                    444:
                    445:     return 0;
                    446: }
                    447:
                    448: static off_t
                    449: find_seek_offset_clrscr (FILE * fp)
                    450: {
                    451:   off_t raw_seek_offset = 0;
                    452:   off_t raw_seek_offset2 = 0;
                    453:   off_t seek_offset_clrscr;
                    454:
                    455:   raw_seek_offset = find_last_string_in_file(fp, "\033[2J");
                    456:   raw_seek_offset2 = find_last_string_in_file(fp, "\033[H\033[J");
                    457:   if (raw_seek_offset2>raw_seek_offset) raw_seek_offset=raw_seek_offset2;
                    458:
                    459:   seek_offset_clrscr = 0;
                    460:   /* now find last filepos that is less than seek offset */
                    461:   fseek (fp, 0, SEEK_SET);
                    462:   while (1)
                    463:     {
                    464:       char *buf;
                    465:       Header h;
                    466:       long offset;
                    467:
                    468:       if (ttyread (fp, &h, &buf, 0) != READ_DATA)
                    469:         {
                    470:           break;
                    471:         }
                    472:
                    473:       free (buf);
                    474:
                    475:       offset = ftell(fp);
                    476:       if (offset < raw_seek_offset)
                    477:           seek_offset_clrscr = ftell (fp);
                    478:       else
                    479:           break;
                    480:     }
                    481:
                    482:   return seek_offset_clrscr;
                    483: }
                    484:
                    485: #if 0 /* not used anymore */
                    486: void
                    487: ttyskipall (FILE * fp)
                    488: {
                    489:   /*
                    490:    * Skip all records.
                    491:    */
                    492:   ttyplay (fp, 0, ttyread, ttynowrite, ttynowait, 0);
                    493: }
                    494: #endif
                    495:
                    496: void
                    497: ttyplayback (FILE * fp, double speed, ReadFunc read_func, WaitFunc wait_func)
                    498: {
                    499:   ttyplay (fp, speed, ttyread, ttywrite, wait_func, 0);
                    500: }
                    501:
                    502: void
                    503: ttypeek (FILE * fp, double speed)
                    504: {
                    505:   int r;
                    506:   do
                    507:   {
                    508:     setvbuf (fp, NULL, _IOFBF, 0);
                    509:     r = ttyplay(fp, 0, ttyread, ttywrite, ttynowait, find_seek_offset_clrscr(fp));
                    510:     if (r == READ_EOF) {
                    511:        clearerr (fp);
                    512:         setvbuf (fp, NULL, _IONBF, 0);
                    513:         fflush (stdout);
                    514:         r = ttyplay (fp, speed, ttypread, ttywrite, ttynowait, -1);
                    515:     }
                    516:   } while (r == READ_RESTART);
                    517: }
                    518:
                    519:
                    520: int
                    521: ttyplay_main (char *ttyfile, int mode, int resizex, int resizey)
                    522: {
                    523:   double speed = 1.0;
                    524:   ReadFunc read_func = ttyread;
                    525:   WaitFunc wait_func = ttywait;
                    526:   FILE *input = stdin;
                    527:   struct termios old, new;
                    528:   sighandler_t old_sigwinch;
                    529:
                    530:   populate_gfx_array (stripped);
                    531:
                    532:   input = efopen (ttyfile, "r");
                    533:
                    534:   tcgetattr (0, &old);          /* Get current terminal state */
                    535:   new = old;                    /* Make a copy */
                    536:   new.c_lflag &= ~(ICANON | ECHO | ECHONL); /* unbuffered, no echo */
                    537:   new.c_cc[VMIN] = 1;
                    538:   new.c_cc[VTIME] = 0;
                    539:   tcsetattr (0, TCSANOW, &new); /* Make it current */
                    540:   raw();
                    541:
                    542:   if (resizex > 0 && resizey > 0) {
                    543:       term_resizex = resizex;
                    544:       term_resizey = resizey;
                    545:   }
                    546:
                    547:   got_sigwinch = 0;
                    548:   old_sigwinch = signal(SIGWINCH, ttyplay_sigwinch_func);
                    549:
                    550:   if (mode == 1)
                    551:     ttypeek (input, speed);
                    552:   else
                    553:     ttyplayback (input, speed, read_func, wait_func);
                    554:
                    555:   tcsetattr (0, TCSANOW, &old); /* Return terminal state */
                    556:   fclose (input);
                    557:
                    558:   if (old_sigwinch != SIG_ERR)
                    559:       signal(SIGWINCH, old_sigwinch);
                    560:
                    561:   term_resizex = term_resizey = -1;
                    562:
                    563:   printf("\033[2J"); /* clear screen afterwards */
                    564:
                    565:   return 0;
                    566: }

CVSweb