home *** CD-ROM | disk | FTP | other *** search
/ Atari FTP / ATARI_FTP_0693.zip / ATARI_FTP_0693 / Mint / toswinsc.zoo / winops.c < prev    next >
C/C++ Source or Header  |  1992-10-27  |  17KB  |  769 lines

  1. /*
  2.  * Copyright 1992 Eric R. Smith. All rights reserved.
  3.  * Redistribution is permitted only if the distribution
  4.  * is not for profit, and only if all documentation
  5.  * (including, in particular, the file "copying")
  6.  * is included in the distribution in unmodified form.
  7.  * THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTY, NOT
  8.  * EVEN THE IMPLIED WARRANTIES OF MERCHANTIBILITY OR
  9.  * FITNESS FOR A PARTICULAR PURPOSE. USE AT YOUR OWN
  10.  * RISK.
  11.  */
  12. #include "xgem.h"
  13. #include <osbind.h>
  14. #include <mintbind.h>
  15. #include <fcntl.h>
  16. #include <signal.h>
  17. #include <string.h>
  18. #include <unistd.h>
  19. #include <stdlib.h>
  20. #include <time.h>
  21. #include "toswin.h"
  22. #include "twdefs.h"
  23. #include "twproto.h"
  24.  
  25. #define FLASHTIME 30
  26.  
  27. /* external flag: set to indicate the flag for opening a
  28.  * "global" file (if we need to) when we're starting up
  29.  */
  30. extern int global_flag;
  31.  
  32. /* file descriptors that have kids attached to them */
  33. long fd_mask = 0L;
  34.  
  35. /* file descriptor of TOSRUN */
  36. extern int trun_fd;
  37.  
  38. static void write_win __PROTO((TEXTWIN *, char *, int));
  39.  
  40. /*
  41.  * get a file name using the file selector.
  42.  * "title" is the file selector box title
  43.  * "path" is where to look for the file;
  44.  * "default_name" is a default name for the file;
  45.  * the final name selected is returned in "name"
  46.  */
  47.  
  48. int
  49. getfilename(title, name, path, default_name)
  50.     char *title;
  51.     char *name;
  52.     char *path, *default_name;
  53. {
  54.     extern char *rindex();
  55.     char *s;
  56.     int fresult, fbutton;
  57.     extern int gl_ap_version;
  58.  
  59.     if (path[0] == 0) {
  60.         path[0] = Dgetdrv() + 'A';
  61.         path[1] = ':';
  62.         (void)Dgetpath(&path[2], 0);
  63.         strcat(path, "\\");
  64.     }
  65.     for (s = path; *s; s++) {
  66.         if (*s == '*') goto nowildcard;
  67.     }
  68.     strcpy(s, "*.*");
  69.  
  70. nowildcard:
  71.     if (gl_ap_version >= 0x140)
  72.         fresult = fsel_exinput(path, default_name, &fbutton, title);
  73.     else
  74.         fresult = fsel_input(path, default_name, &fbutton);
  75.     if (fresult <= 0 || fbutton != 1 || !default_name[0])
  76.         return FAIL;
  77.     s = rindex(path, '\\');
  78.     if (!s)
  79.         s = &path[2];
  80.     else
  81.         s++;
  82.     *s = 0;
  83.     strcpy(name, path);
  84.     strcat(name, default_name);
  85.     return OK;
  86. }
  87.  
  88. /*
  89.  * if the program named by "fname" is a .TTP one, prompt the
  90.  * user for arguments and copy them into "argstr"; otherwise
  91.  * copy in a null command line. Return FAIL if the user
  92.  * cancelled the request.
  93.  */
  94.  
  95. int
  96. getargs(fname, argstr)
  97.     char *fname, *argstr;
  98. {
  99.     char *lastdot = 0;
  100.     int i, x, y, w, h;
  101.     TEDINFO *ted;
  102.     OBJECT *argdial;
  103.     char *inp;
  104.  
  105.     *argstr = 0;
  106.     while (*fname) {
  107.         if (*fname == '.')
  108.             lastdot = fname;
  109.         else if (*fname == '\\')
  110.             lastdot = 0;
  111.         fname++;
  112.     }
  113.     if (lastdot && !strcmp(lastdot, ".TTP")) {
  114.         rsrc_gaddr(0, ARGDIAL, &argdial);
  115.         ted = (TEDINFO *)argdial[ARGSTR].ob_spec;
  116.         inp = (char *)ted->te_ptext;
  117.         *inp = 0;
  118.         form_center(argdial, &x, &y, &w, &h);
  119.         wind_update(1);
  120.         form_dial(FMD_START, 0, 0, 32, 32, x, y, w, h);
  121.         if (win_flourishes)
  122.             form_dial(FMD_GROW, 0, 0, 32, 32, x, y, w, h);
  123.  
  124.         objc_draw(argdial, 0, 2, x, y, w, h);
  125.  
  126.         i = form_do(argdial, ARGSTR);
  127.  
  128.         if (win_flourishes)
  129.             form_dial(FMD_SHRINK, 0, 0, 32, 32, x, y, w, h);
  130.  
  131.         form_dial(FMD_FINISH, 0, 0, 32, 32, x, y, w, h);
  132.         objc_change(argdial, i, 0, x, y, w, h, NORMAL, 0);
  133.         wind_update(0);
  134.         if (i == ARGCAN) return FAIL;
  135.         strcpy(argstr, inp);
  136.     }
  137.     return OK;
  138. }
  139.  
  140. char progpath[128];
  141. char dfltprog[128];
  142.  
  143. int
  144. getprogname(name)
  145.     char *name;
  146. {
  147.     return getfilename(Strng(EXECPRG), name, progpath, dfltprog);
  148. }
  149.  
  150. /*
  151.  * typeit: type the user's input into a window. Note that this routine is
  152.  * called when doing a 'paste' operation, so we must watch out for possible
  153.  * deadlock conditions
  154.  */
  155.  
  156. #define READBUFSIZ 256
  157. static char buf[READBUFSIZ];
  158.  
  159. int
  160. typeit(w, code, shift)
  161.     WINDOW *w;
  162.     int code, shift;
  163. {
  164.     TEXTWIN *t = w->extra;
  165.     long offset, height;
  166.     long c = (code & 0x00ff) | (((long)code & 0x0000ff00) << 8L) |
  167.          ((long)shift << 24L);
  168.     long r;
  169.     if (t->miny) {
  170.         offset = t->miny * t->cheight;
  171.         if (offset > t->offy) {
  172.     /* we were looking at scroll back */
  173.     /* now move so the cursor is visible */
  174.             height = t->cheight * t->maxy - w->wi_h;
  175.             if (height <= 0)
  176.                 offset = 0;
  177.             else {
  178.                 offset = 1000L * t->cy * t->cheight/height;
  179.                 if (offset > 1000L) offset = 1000L;
  180.             }
  181.             (*w->vslid)(w, offset);
  182.         }
  183.     }
  184.  
  185.     if (t->fd) {
  186.         r = Foutstat(t->fd);
  187.         if (r <= 0) {
  188.             r = Fread(t->fd, (long)READBUFSIZ, buf);
  189.             if (r > 0) {
  190.                 write_win(t, buf, (int)r);
  191.             }
  192.             (void)Fselect(500,0L,0L,0L);
  193.             r = Foutstat(t->fd);
  194.         }
  195.         if (r > 0) {
  196.             (void)Fputchar(t->fd, c, 0);
  197.             return 1;
  198.         }
  199.     }
  200.     return 0;
  201. }
  202.  
  203. void (*oldtopped)(), (*oldclosed)();
  204.  
  205. extern MENU *sysbar;
  206. extern int appl_menus, sys_menu;
  207.  
  208. void
  209. top_menu(v, f)
  210.     WINDOW *v;
  211.     void (*f)();
  212. {
  213.     ENTRY *e;
  214.     TEXTWIN *t = v->extra;
  215.     TEXTWIN *oldt;
  216.     int enable = (v->wtype == TEXT_WIN);
  217.  
  218.     if (gl_topwin && gl_topwin->wtype == TEXT_WIN &&
  219.         gl_topwin->extra != t)
  220.         oldt = gl_topwin->extra;
  221.     else
  222.         oldt = 0;
  223.  
  224.     for (e = windowmenu->contents; e; e = e->next) {
  225.         if (e->entry[0] != '-')
  226.             if (enable)
  227.                 enable_entry(windowmenu, e);
  228.             else
  229.                 disable_entry(windowmenu, e);
  230.     }
  231.     for (e = gadgmenu->contents; e; e = e->next) {
  232.         if (e->entry[0] != '-')
  233.             if (enable)
  234.                 enable_entry(gadgmenu, e);
  235.             else
  236.                 disable_entry(gadgmenu, e);
  237.     }
  238.  
  239.     enable_entry(filemenu, closeentry);
  240.  
  241. #ifdef GLOBL_APPL_MENUS
  242.     if (v->menu && appl_menus) {
  243.         show_menu(v->menu);
  244.         sys_menu = 0;
  245.     } else if (!sys_menu) {
  246.         show_menu(sysbar);
  247.         sys_menu = 1;
  248.     }
  249. #endif
  250.     if (enable)
  251.         curs_off(t);
  252.     if (oldt) {
  253.     /* change the cursor states, maybe */
  254.         curs_off(oldt);
  255.     }
  256.     (*f)(v);
  257.     focuswin = gl_topwin;
  258.     if (enable) {
  259.         curs_on(t);
  260.         refresh_textwin(t);
  261.     }
  262.     if (oldt) {
  263.         curs_on(oldt);
  264.         refresh_textwin(oldt);
  265.     }
  266. }
  267.  
  268. static void
  269. top_text(v)
  270.     WINDOW *v;
  271. {
  272.     top_menu(v, oldtopped);
  273. }
  274.  
  275. static void
  276. desk_menu(v)
  277.     WINDOW *v;
  278. {
  279.     ENTRY *e;
  280.  
  281.     (*oldclosed)(v);
  282.     if (v->menu)
  283.         unloadmenu(v->menu);
  284.     if (!gl_topwin) {
  285.         for (e = windowmenu->contents; e; e = e->next) {
  286.             if (e->entry[0] != '-')
  287.                 disable_entry(windowmenu, e);
  288.         }
  289.         for (e = gadgmenu->contents; e; e = e->next) {
  290.             if (e->entry[0] != '-')
  291.                 disable_entry(gadgmenu, e);
  292.         }
  293.         disable_entry(filemenu, closeentry);
  294. #ifdef GLOBAL_APPL_MENUS
  295.         if (!sys_menu) {
  296.             show_menu(sysbar);
  297.             sys_menu = 1;
  298.         }
  299. #endif
  300.     }
  301. }
  302.  
  303. /*
  304.  * set up a new text window with a process running in it
  305.  * "progname" is the program's name;
  306.  * "progargs" are the arguments for it;
  307.  * "progdir" is the directory to change to;
  308.  * "x" and "y" give where the window is to be opened
  309.  * "rows" and "cols" are the number of rows and columns for it,
  310.  * respectively;
  311.  * "scroll" are the number of lines of scrollback to be
  312.  * alloted for the window
  313.  *
  314.  * NOTE: the window is *not* actually opened here; it's the
  315.  * caller's responsibility to do that.
  316.  */
  317.  
  318. TEXTWIN *
  319. newproc(progname, progargs, progdir, x, y, cols, rows, scroll, kind,
  320.  font, points)
  321.     char *progname, *progargs, *progdir;
  322.     int x, y, cols, rows, kind, scroll, font, points;
  323. {
  324.     extern void vt52_putch();
  325.     extern long termfunc;    /* set in main.c */
  326.     TEXTWIN *t;
  327.     int i, ourfd, kidfd;
  328. #ifdef OLD_WAY
  329.     long oldblock;
  330. #else
  331.     long r;
  332. #endif
  333.     static char termname[64];
  334.     static char argbuf[128];
  335.     struct winsize tw;
  336.     char *p, *arg, c;
  337.     char *newenv = 0;
  338.     int oldfont, oldheight;
  339.  
  340.     /* copy over the args */
  341.     p = argbuf+1;
  342.     arg = progargs;
  343.     for (i = 0; i < 125; i++) {
  344.         c = *arg++;
  345.         if (!c || c == '\r' || c == '\n') break;
  346.         *p++ = c;
  347.     }
  348.     *p = 0;
  349.     argbuf[0] = i;
  350.  
  351.     oldfont = default_font;
  352.     oldheight = default_height;
  353.     default_font = font;
  354.     default_height = points;
  355.     t = create_textwin(progname, x, y, cols, rows, scroll, kind);
  356.     default_font = oldfont;
  357.     default_height = oldheight;
  358.  
  359.     if (!t) {
  360.         form_alert(1, AlertStrng(NOWINS));
  361.         return 0;
  362.     }
  363.     t->output = vt52_putch;
  364.     t->prog = strdup(progname);
  365.     t->cmdlin = strdup(progargs);
  366.     t->progdir = strdup(progdir);
  367.     t->win->menu = loadmenu(progname);
  368.     if (t->win->menu) {
  369.         t->win->infostr = strdup(menustr(t->win->menu));
  370.     } else {
  371.         change_window_gadgets(t->win, t->win->wi_kind & ~INFO);
  372.     }
  373.  
  374.     newenv = envstr(progname, progargs, progdir, cols, rows);
  375.  
  376.     if (newenv && (env_options & E_ARGV)) {
  377.         if (!*argbuf)
  378.             argbuf[1] = 0;
  379.         argbuf[0] = 127;    /* mark ARGV arg. passing */
  380.     }
  381.  
  382.     strcpy(termname, "U:\\pipe\\pty.A");
  383.     for (i = 0; i < 20; i++) {
  384.         termname[12] = 'A' + i;
  385.         ourfd = Fcreate(termname, FA_SYSTEM|FA_HIDDEN|global_flag);
  386.         if (ourfd > 0) {
  387.         /* set to non-delay mode, so Fread() won't block */
  388.             (void)Fcntl(ourfd, (long)O_NDELAY, F_SETFL);
  389.             break;
  390.         }
  391.     }
  392.     if (ourfd < 0) {
  393.         form_alert(1, AlertStrng(NOPTY));
  394. abort_open:
  395.         destroy_textwin(t);
  396.         return 0;
  397.     }
  398.     t->fd = ourfd;
  399.     tw.ws_xpixel = tw.ws_ypixel = 0;
  400.     tw.ws_row = rows;
  401.     tw.ws_col = cols;
  402.  
  403.     (void)Fcntl(ourfd, &tw, TIOCSWINSZ);
  404.     t->win->keyinp = typeit;
  405.     t->win->mouseinp = win_click;
  406.     oldtopped = t->win->topped;
  407.     t->win->topped = top_text;
  408.     oldclosed = t->win->closed;
  409.     t->win->closed = desk_menu;
  410.  
  411. #ifdef OLD_WAY
  412.     oldblock = Psigblock(1L << SIGCHLD);
  413. #endif
  414.  
  415.     i = Pvfork();
  416.     if (i < 0) {
  417.         (void)Fclose(ourfd);
  418. #ifdef OLD_WAY
  419.         (void)Psigsetmask(oldblock);
  420. #endif
  421.         form_alert(1, AlertStrng(NOPROC));
  422.         goto abort_open;
  423.     } else if (i == 0) {
  424.         if (oldACC)
  425.             (void)Setexc(0x102, termfunc);
  426. #ifndef OLD_WAY
  427.         i = Pvfork();
  428.         if (i != 0)
  429.             Pterm(i);
  430. #endif
  431.         (void)Psetpgrp(0, 0);
  432.         kidfd = Fopen(termname, 2);
  433.         if (kidfd < 0) _exit(998);
  434.         (void)Fforce(-1, kidfd);
  435.         (void)Fforce(0, kidfd);
  436.         (void)Fforce(1, kidfd);
  437.         (void)Fforce(2, kidfd);
  438.         (void)Fclose(kidfd);
  439.         (void)chdir(progdir);
  440.         i = Pexec(200, progname, argbuf, newenv);
  441.         _exit(i);
  442.     }
  443. #ifndef OLD_WAY
  444. #define WNOHANG 1
  445. /* reap the exit code of the just terminated process */
  446.     do {
  447.         r = Pwait3(WNOHANG, (void *)0);
  448.     } while (((r & 0xffff0000L) >> 16L) != i);
  449.     i = r & 0x0000ffff;
  450. #endif
  451.     t->pgrp = i;
  452.     if (!global_flag)
  453.         fd_mask |= (1L << ourfd);
  454. /* turn on the cursor in the window */
  455.     (*t->output)(t, '\033');
  456.     (*t->output)(t, 'e');
  457.  
  458. /* and make it flash */
  459.     (*t->output)(t, '\033');
  460.     (*t->output)(t, 't');
  461.     (*t->output)(t, ' '+FLASHTIME);
  462.  
  463. /* align the window, if necessary */
  464.     if (align_windows)
  465.         t->win->wi_x &= ~7;
  466.  
  467. #ifdef OLD_WAY
  468.     (void)Psigsetmask(oldblock);
  469. #endif
  470.     if (newenv) free(newenv);
  471.  
  472.     return t;
  473. }
  474.  
  475. /* After this many bytes have been written to a window, it's time to
  476.  * update it. Note that we must also keep a timer and update all windows
  477.  * when the timer expires, or else small writes will never be shown!
  478.  */
  479. #define THRESHOLD 400
  480.  
  481. static void
  482. write_win(t, b, cnt)
  483.     TEXTWIN *t;
  484.     char *b;
  485.     int cnt;
  486. {
  487.     unsigned char *buf = (unsigned char *)b, c;
  488.     int limit = smoothscroll ? 1 : NROWS(t) - 1;
  489.  
  490.     while (cnt-- > 0) {
  491.         c = *buf++;
  492.         (*t->output)(t, c);
  493.         t->nbytes++;
  494.         if (t->nbytes >= THRESHOLD || t->scrolled >= limit) {
  495.             refresh_textwin(t);
  496.         }
  497.     }
  498. }
  499.  
  500. #define EIHNDL -37
  501.  
  502. static char exit_msg[] = "\r\n<EXITED>\r\n";
  503.  
  504. static void
  505. rebuild_fdmask(which)
  506.     long which;
  507. {
  508.     int i;
  509.     WINDOW *w, *wnext;
  510.     TEXTWIN *t;
  511.  
  512.     for (i = 0; i < 32; i++) {
  513.         if ((which & (1L << i)) && (Finstat(i) < 0)) {
  514.         /* window has died now */
  515.             fd_mask &= ~(1L << i);
  516.             for (w = gl_winlist; w; w = wnext) {
  517.                 wnext = w->next;
  518.                 if (w->wtype != TEXT_WIN) continue;
  519.                 t = w->extra;
  520.                 if (t && t->fd == i) {
  521.                     if (autoclose)
  522.                         (*w->closed)(w);
  523.                     else {
  524.                         write_win(t, exit_msg,
  525.                             (int)strlen(exit_msg));
  526.                         refresh_textwin(t);
  527.                         (void)Fclose(i);
  528.                         t->fd = 0;
  529.                     }
  530.                 }
  531.             }
  532.         }
  533.     }
  534. }
  535.  
  536. void
  537. exec_tos(buf)
  538.     char *buf;
  539. {
  540.     TEXTWIN *t;
  541.     char *dir, *name, *args;
  542.  
  543.     dir = buf;
  544.     while (*buf && *buf != ' ') buf++;
  545.     if (*buf) *buf++ = 0;
  546.     name = buf;
  547.     while (*buf && *buf != ' ') buf++;
  548.     if (*buf) *buf++ = 0;
  549.     args = buf;
  550.     t = newproc(name, args, dir, 0, 0, stdcol, stdrow, stdscroll, default_kind,
  551.          default_font, default_height);
  552.     if (_app || opened)
  553.         open_window(t->win);
  554. }
  555.  
  556. static long lasthz;
  557.  
  558. void
  559. fd_input()
  560. {
  561. #define MAX_DELAY 3        /* max no. of 50Hz ticks before a refresh */
  562.     long readfds, r, checkdead;
  563.     int read;
  564.     int workdone = 0;
  565.     WINDOW *w;
  566.     TEXTWIN *t;
  567.     int updtime;
  568.     long newhz;
  569.  
  570.     r  = 0;
  571.     checkdead = 0;
  572.  
  573.     newhz = clock();
  574.     updtime = (newhz - lasthz) >> 2;
  575.     lasthz = newhz;
  576.  
  577.     if (fd_mask) {
  578.         readfds = fd_mask;
  579.         if ((r = Fselect(1, &readfds, 0L, 0L)) > 0) {
  580.             if (trun_fd && (readfds & (1L << trun_fd))) {
  581.                 read = Finstat(trun_fd);
  582.                 if (read > 0)
  583.                     read = Fread(trun_fd, (long)READBUFSIZ,
  584.                              buf);
  585.                 if (read > 0)
  586.                     exec_tos(buf);
  587.             }
  588.             for (w = gl_winlist; w; w = w->next) {
  589.                 if (w->wtype != TEXT_WIN) continue;
  590.                 t = w->extra;
  591.                 if (!t || !t->fd) continue;
  592.                 if (readfds & (1L << t->fd)) {
  593.                     read = Fread(t->fd, (long)READBUFSIZ,
  594.                              buf);
  595.                     if (read > 0) {
  596.                         write_win(t, buf, read);
  597.                     } else {
  598.                         checkdead |= (1L << t->fd);
  599.                     }
  600.                 }
  601.             }
  602.             workdone = 1;
  603.         }
  604.         if (checkdead)
  605.             rebuild_fdmask(checkdead);
  606.     }
  607.     if (r == EIHNDL) {    /* invalid handle?? */
  608.         rebuild_fdmask(fd_mask);
  609.     }
  610.  
  611.     if (workdone) {
  612.         gl_timer = MultiTOS ? SHORTWAIT : NOMULTIWAIT;
  613.     } else {
  614.         gl_timer = MultiTOS ? LONGWAIT : NOMULTIWAIT;
  615.     }
  616.  
  617. /* for all windows on screen, see if it's time to refresh them */
  618.     for (w = gl_winlist; w; w = w->next) {
  619.         if (w->wtype != TEXT_WIN) continue;
  620.         t = w->extra;
  621.     /* should we flash the cursor in this window? */
  622.         if (t->flashperiod) {
  623.             t->flashtimer -= updtime;
  624.             if (t->flashtimer <= 0) {
  625.                 t->flashtimer = t->flashperiod;
  626.                 curs_flash(t);
  627.                 t->nbytes++;
  628.             }
  629.         }
  630.         if (!t || !t->fd || !t->nbytes) continue;
  631.         t->draw_time += updtime;
  632.         if (t->draw_time < MAX_DELAY) continue;
  633.         refresh_textwin(t);
  634.     }
  635. }
  636.  
  637. /*
  638.  * similar to fd_input, but used only when we're an ACC;
  639.  * this loop doesn't use Fselect, so it's less efficient
  640.  */
  641.  
  642. void
  643. acc_input()
  644. {
  645.     long r, checkdead;
  646.     int read;
  647.     int workdone = 0;
  648.     WINDOW *w;
  649.     TEXTWIN *t;
  650.     int updtime;
  651.     long newhz;
  652.  
  653.     r  = 0;
  654.     checkdead = 0;
  655.  
  656.     newhz = clock();
  657.     updtime = (newhz - lasthz) >> 2;
  658.     lasthz = newhz;
  659.  
  660.     read = Finstat(trun_fd);
  661.     if (read > 0) {
  662.         read = Fread(trun_fd, (long)READBUFSIZ, buf);
  663.         if (read > 0)
  664.             exec_tos(buf);
  665.     }
  666.     for (w = gl_winlist; w; w = w->next) {
  667.         if (w->wtype != TEXT_WIN) continue;
  668.         t = w->extra;
  669.         if (!t || !t->fd) continue;
  670.         if ((r = Finstat(t->fd)) != 0) {
  671.             read = Fread(t->fd, (long)READBUFSIZ, buf);
  672.             if (read > 0) {
  673.                 write_win(t, buf, read);
  674.             } else if (r < 0) {
  675.                 if (autoclose) {
  676.                     (*w->closed)(w);
  677.                 } else {
  678.                     write_win(t, exit_msg,
  679.                          (int)strlen(exit_msg));
  680.                     refresh_textwin(t);
  681.                     (void)Fclose(t->fd);
  682.                     t->fd = 0;
  683.                 }
  684.             }
  685.             workdone = 1;
  686.         }
  687.     }
  688.  
  689.     if (workdone) {
  690.         gl_timer = MultiTOS ? SHORTWAIT : NOMULTIWAIT;
  691.     } else {
  692.         gl_timer = MultiTOS ? LONGWAIT : NOMULTIWAIT;
  693.     }
  694.  
  695. /* for all windows on screen, see if it's time to refresh them */
  696.     for (w = gl_winlist; w; w = w->next) {
  697.         if (w->wtype != TEXT_WIN) continue;
  698.         if (w->wi_handle < 0) continue;
  699.         t = w->extra;
  700.     /* should we flash the cursor in this window? */
  701.         if (t->flashperiod) {
  702.             t->flashtimer -= updtime;
  703.             if (t->flashtimer <= 0) {
  704.                 t->flashtimer = t->flashperiod;
  705.                 curs_flash(t);
  706.                 t->nbytes++;
  707.             }
  708.         }
  709.         if (!t || !t->fd || !t->nbytes) continue;
  710.         t->draw_time += updtime;
  711.         if (t->draw_time < MAX_DELAY) continue;
  712.         refresh_textwin(t);
  713.     }
  714. }
  715.  
  716. /*
  717.  * send an untranslated character to the top window
  718.  */
  719.  
  720. void
  721. send_char()
  722. {
  723.     WINDOW *v;
  724.     OBJECT *quote_dial;
  725.     int event, mshift, keycode, dummy;
  726.     int x, y, w, h;
  727.  
  728.     v = gl_topwin;
  729.     if (v && v->wtype == TEXT_WIN) {
  730.         event = evnt_multi(MU_KEYBD|MU_TIMER,
  731.             0, 0, 0,
  732.             0, 0, 0, 0, 0,
  733.             0, 0, 0, 0, 0,
  734.             NULL,
  735.             1200L,        /* wait 1.2 seconds */
  736.             &dummy, &dummy, &dummy, &mshift,
  737.             &keycode, &dummy);
  738.         if (event & MU_KEYBD) {
  739.             (*v->keyinp)(v, keycode, mshift);
  740.         } else {
  741.             rsrc_gaddr(0, QUOTEIT, "e_dial);
  742.             form_center(quote_dial, &x, &y, &w, &h);
  743.             wind_update(BEG_MCTRL);
  744.             form_dial(FMD_START, 0, 0, 32, 32, x, y, w, h);
  745.             if (win_flourishes)
  746.                 form_dial(FMD_GROW, 0, 0, 32, 32, x, y, w, h);
  747.  
  748.             objc_draw(quote_dial, 0, 2, x, y, w, h);
  749.             event = evnt_multi(MU_KEYBD|MU_BUTTON,
  750.                 0x0101, 0x0003, 0,
  751.                 0, 0, 0, 0, 0,
  752.                 0, 0, 0, 0, 0,
  753.                 NULL,
  754.                 0L,
  755.                 &dummy, &dummy, &dummy, &mshift,
  756.                 &keycode, &dummy);
  757.  
  758.             if (win_flourishes)
  759.                 form_dial(FMD_SHRINK, 0, 0, 32, 32, x, y, w, h);
  760.             form_dial(FMD_FINISH, 0, 0, 32, 32, x, y, w, h);
  761.             wind_update(END_MCTRL);
  762.             if (event & MU_KEYBD) {
  763.                 (*v->keyinp)(v, keycode, mshift);
  764.             }
  765.         }
  766.     }
  767.  
  768. }
  769.