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

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

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:
        !            61: #if defined(__MACH__) || defined(__FreeBSD__) || defined(__NetBSD__)
        !            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