home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / mgr / sparcmgr / demo1.zoo / demo / ify / ify.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-10-02  |  28.3 KB  |  973 lines

  1. /* ify.c - iconify an mgr window
  2.    Jim Blandy - July, 1989 - Bell Communications Research
  3.  
  4. This program is designed to replace the 'close' program that comes
  5. with MGR - ify doesn't require you to stop the program running in a
  6. window and start up another one, like close does.
  7.  
  8. When you first click on a window, ify finds out which pseudo-tty that
  9. window is attached to, finds all the other windows attached to that
  10. psuedo-tty, and closes them by sending the appropriate escape codes to
  11. the pseudo-tty, changing the window's NOTIFY string to
  12. "CLOSED:<tty>:<old notify name>", using the window's original NOTIFY
  13. as a title for the closed window, and then doing a TIOCSTOP ioctl on
  14. it, basically equivalent to a ^S.  After sending the ^S, ify sends the
  15. escape codes to restore the window's position and content; since the
  16. tty has been ^S'ed, these don't take effect yet.
  17.  
  18. When you click on a closed window, ify notices that the window is
  19. already closed by looking at its NOTIFY string, opens the window's
  20. ptty, and does a TIOCSTART ioctl on it (equivalent to a ^Q).  This
  21. lets MGR see the escape codes ify queued up when the window was
  22. closed, so MGR opens the window.
  23.  
  24. The list of closed windows displayed on the menu is obtained by asking
  25. MGR for everyone's NOTIFY strings, and selecting those windows whose
  26. NOTIFY strings start with "CLOSED:".  This menu is refreshed when a
  27. window is closed or opened.
  28.  
  29. Note that MGR divides its attention among several ttys; if you send
  30. data to someone else's tty, as ify does, MGR takes its own sweet time
  31. to notice what you've sent; you have to wait for it to take effect.
  32. After sending the codes to close up a group of windows, ify sends a
  33. m_sendto() message to itself; when it gets the message, it knows that
  34. the window is all done.  It pulls the same trick when opening windows,
  35. in order to keep the menu up to date.  The only kink in this setup is
  36. that, in order to receive m_sendto() messages, ify needs to be able to
  37. turn off general write permission to its tty, which means it needs to
  38. own its tty.  This can only happen if you're running MGR with the
  39. setuid bit on.  If ify can't change its tty, it gives up on the
  40. message-passing strategy and just sleeps a bit (see the WAITTIME
  41. constant) after each operation.
  42.                     -JimB
  43. */
  44.  
  45. #include <sys/ioctl.h>        /* for ioctl(): TIOCSTOP & TIOCSTART */
  46. #include <sys/types.h>        /* these two for fstat() */
  47. #include <sys/stat.h>        /* ... also for fstat() */
  48. #include <signal.h>        /* signal-catching stuff */
  49. #include <strings.h>        /* for strchr() */
  50. #include <string.h>        /* for other stuff */
  51. #include <ctype.h>        /* for isspace() */
  52. #include "term.h"        /* for MGR declarations */
  53.  
  54. /* compatibility with different versions of MGR */
  55. #ifdef OLDMGR
  56. #define m_setcursor(s)
  57. #undef m_whatsat
  58. #define m_whatsat(x,y) \
  59.     (fprintf(m_termout,"%c%d,%d%c",m_escchar,x,y,E_GETINFO),m_flush())
  60. /* older version of m_whatsat() forgets to send the y co-ord */
  61. #endif
  62.  
  63. /* general tty mashing stuff: */
  64. #define TTYSUFFIXLEN (3)    /* length of ptty suffix + 1 */
  65. #define TTYPREFIX "/dev/tty"    /* prefix for pttys */
  66. #define TTYPREFIXLEN (sizeof(TTYPREFIX)+1) /* length of TTYPREFIX+1 */
  67. char ttytemp[TTYPREFIXLEN+TTYSUFFIXLEN];    /* for FULLTTYNAME() */
  68. #define FULLTTYNAME(suff)    /* make full name from suffix - temporary! */\
  69.        (strcat(strcpy(ttytemp, TTYPREFIX), suff))
  70.  
  71. /* notify string stuff: */
  72. char closednotify[] = "CLOSED:"; /* notify prefix for closed windows */
  73. #define CLNLEN (7)        /* length of above */
  74.  
  75. /* ify's icon stuff: */
  76. #define LOCALICONNAME "./.ify.icon" /* name for local icon */
  77. #define MGRICONNAME "ify.icon"    /* name for global mgr icon */
  78. #define ICONBITMAP 1        /* scratch bitmap for icon's image */
  79. int icon_w, icon_h;        /* size of icon */
  80. int icon_x, icon_y;        /* position of icon within window */
  81.  
  82. /* ify window position stuff: */
  83. #define MINW (50)        /* minimum window size */
  84. #define MINH (font_h)        /* minimum height */
  85. int wind_x, wind_y;        /* position of our window on the screen */
  86. int wind_w, wind_h;        /* desired width & height */
  87. int shapetofit = 1;        /* should we shape to fit? */
  88.  
  89. /* stuff for figuring out where to put closed windows: */
  90. int disp_w, disp_h;        /* display size */
  91. int font_w, font_h;        /* font size */
  92. int border;            /* border thickness */
  93. struct window_data layout[200];    /* space for list of windows on the screen */
  94. int layoutlen;            /* # of windows in layout */
  95. #define Max(x,y)    ((x)>(y)?(x):(y))
  96. #define XSLOP (6)        /* extra horizontal space around window */
  97. #define YSLOP (2)        /* extra vertical space around window */
  98.  
  99. /* error reporting stuff: */
  100. char *progname;            /* name of this program */
  101. extern int errno, sys_nerr;
  102. extern char *sys_errlist[];
  103. char errtemp[100];        /* space for THEERROR() */
  104. #define THEERROR(msg)\
  105.   (strcat(strcpy(errtemp, msg),\
  106.     (errno<sys_nerr?sys_errlist[errno]:"unknown error")))
  107.  
  108.  
  109. #define iocuddle ioctl        /* to be cute */
  110.  
  111. /* stuff for changing our general write permission: */
  112. char ourtty[TTYPREFIXLEN+TTYSUFFIXLEN];    /* the tty we're running on */
  113. int windowpid;            /* process id representing this window */
  114. #define WAITTIME (2)        /* # of seconds to wait for MGR to finish */
  115.                 /* closing windows.  Not used if we can */
  116.                 /* change the protection on our ptty. */
  117. u_short oldmode;        /* protection bits of the tty when the
  118.                    program started */
  119.  
  120. /* debugging error messages & stuff: */
  121. #define dprintf            /* define dprintf or eprintf to be fprintf */
  122. #define eprintf            /* to turn on debugging messages */
  123.  
  124. /* Note that the dupkey we use for this program is ^E, written here
  125.    as \005.  I didn't bother defining it, since it appears in strings
  126.    and it's a pain to include a constant in a string with a non-ANSI
  127.    compiler. */
  128. #define DUPKEYON { if (dupkeyokay) m_dupkey('\005'); }
  129. #define DUPKEYOFF { if (dupkeyokay) m_clearmode(M_DUPKEY); }
  130. int dupkeyokay, hasdupkey();    /* is dupkey available? */
  131.  
  132. #define CNULL ((char *) 0)
  133.  
  134. char usage[] = "\
  135. While this program's window is active, click on windows with the right\n\
  136. button to open or close.  The middle button's menu lets you quit or choose\n\
  137. a closed window to open.\n";
  138.  
  139. main(argc, argv)
  140.      int argc;
  141.      char **argv;
  142. {
  143.     int catchint();
  144. #   define BUFLEN (80)
  145.     char buf[BUFLEN+1];        /* space for info from MGR */
  146.     
  147.     /* ==================== process arguments ==================== */
  148.     progname = argv[0];
  149.     for (argc--, argv++; argc>0; argc--,argv++)
  150.       if ((*argv)[0] == '-')
  151.     switch((*argv)[1]) {
  152.       case 's':
  153.         shapetofit = 0;
  154.         break;
  155.       default:
  156.         fprintf(stderr, "%s: unrecognized option: %s\n",
  157.             progname, *argv);
  158.         /* fall through */
  159.       case '\0':
  160.         fprintf(stderr, "Usage: %s\n%s", progname, usage);
  161.         exit(1);
  162.     }
  163.       else {
  164.       fprintf(stderr, "Usage: %s\n%s", progname, usage);
  165.       exit(1);
  166.       }
  167.  
  168.     /* ==================== Generic Initialization ==================== */
  169.  
  170.     m_setup(M_FLUSH);
  171.     dupkeyokay = hasdupkey();
  172.     m_ttyset();
  173.     m_push(P_EVENT | P_FLAGS | P_MENU | P_POSITION);
  174.     signal(SIGINT, catchint);
  175.     DUPKEYOFF;
  176.     m_setmode(M_ABS);
  177.     m_setmode(M_NOWRAP);
  178.     m_setcursor(9);
  179.     m_func(B_SRC);
  180.     m_flush();
  181.  
  182.     {
  183.     char *ttyname();
  184.     char *t = ttyname(2);    /* get tty attached to stderr */
  185.  
  186.     if (t == CNULL)
  187.         shutdown("Can't find name of tty");
  188.     strcpy(ourtty, t);    /* make a copy of ttyname()'s value */
  189.     }
  190.  
  191.     /* turn off general write permission to the tty, so we can accept
  192.        m_sendme() messages */
  193.     {
  194.     struct stat ttymode;
  195.  
  196.     if (stat(ourtty, &ttymode) == -1)
  197.       closedown(THEERROR("ourtty stat: "));
  198.     oldmode = ttymode.st_mode; /* get the old protection bits */
  199.     }
  200.  
  201.     /* Can we use m_sendto messages? */
  202.     if ((oldmode & 077) == 0 || chmod(ourtty, 00600) != -1)
  203.       windowpid = getwid();
  204.     else {
  205.     static char mesg[] =
  206.       "Ify runs better if MGR has its setuid bit on.";
  207.  
  208.     windowpid = 0;
  209.     m_clear();
  210.     m_push(P_POSITION);
  211.     m_size(sizeof(mesg)-1, 1);
  212.     m_printstr(mesg);
  213.     m_flush();
  214.     sleep(5);
  215.     m_pop();
  216.     m_flush();
  217.     }
  218.  
  219.     get_param(NULL, &disp_w, &disp_h, &border);
  220.     get_font(&font_w, &font_h);
  221.     DUPKEYON;
  222.  
  223.     /* ==================== try to read in icon file ==================== */
  224.     m_bitfromfile(ICONBITMAP, LOCALICONNAME); /* try to find local icon */
  225.     skipgarbage(); m_gets(buf);
  226.     eprintf(stderr, "buf: \"%s\"\n", buf);
  227.     if (buf[0] == '\n') {    /* did we get the file? */
  228.     m_bitfromfile(ICONBITMAP, MGRICONNAME); /* no - try global icon */
  229.     skipgarbage(); m_gets(buf);
  230.     eprintf(stderr, "buf: \"%s\"\n", buf);
  231.     if (buf[0] == '\n')    /* no go there either? */
  232.       loadicon(ICONBITMAP, &icon_w, &icon_h);
  233.     }
  234.     if (buf[0] != '\n')        /* unless we used our built-in icon... */
  235.       sscanf(buf, "%d %d", &icon_w, &icon_h); /* get the icon size */
  236.     /* now we've got the icon in ICONBITMAP, and its size in icon_[wh]. */
  237.     DUPKEYOFF;
  238.     get_size(&wind_x, &wind_y, &wind_w, &wind_h);
  239.     DUPKEYON;
  240.     if (shapetofit) {
  241.     wind_w = Max(MINW, icon_w);
  242.     wind_h = Max(MINH, icon_h);
  243.     }
  244.     icon_x = (wind_w - icon_w)/2;
  245.     icon_y = (wind_h - icon_h)/2;
  246.     wind_w += 2*border;
  247.     wind_h += 2*border;
  248.     if (shapetofit) {
  249.         m_shapewindow(wind_x, wind_y, wind_w, wind_h);
  250.     }
  251.     m_clear();
  252.     m_bitcopyto(icon_x, icon_y, icon_w, icon_h, 0, 0, 0, ICONBITMAP);
  253.     m_flush();
  254.  
  255.     eprintf(stderr, "icon: %dx%d\n", icon_w, icon_h);
  256.     eprintf(stderr, "buf: \"%s\"\n", buf);
  257.  
  258.     /* ==================== set up events ==================== */
  259.  
  260.     /* clicked on a window */
  261.     m_setevent(BUTTON_1, "\005b%p,%n\\r");
  262.  
  263.     /* screen is being redrawn */
  264.     m_setevent(REDRAW, "\005r\\r");
  265.  
  266.     /* window was reshaped */
  267.     m_setevent(RESHAPE, "\005s\\r");
  268.  
  269.     /* a window is done with being opened - rebuild the menu */
  270.     m_setevent(ACCEPT, "\005n\\r");
  271.  
  272.     /* This window's name */
  273.     m_setevent(NOTIFY, "== Ify ==");
  274.  
  275.     /* build the initial menu */
  276.     makemenu();
  277.  
  278.     /* ==================== main event loop ==================== */
  279.     for (;;) {
  280.  
  281.     skipgarbage();
  282.     if (fgets(buf, BUFLEN, m_termin) == CNULL) /* end of file? */
  283.       closedown(CNULL);
  284.     buf[strlen(buf)-1] = '\0'; /* zap the newline */
  285.  
  286.     switch(buf[0]) {
  287.  
  288.       case 'q':        /* quit program */
  289.         closedown(CNULL);
  290.         break;
  291.  
  292.       case 'b':        /* button pressed */
  293.         {
  294.         int x, y;
  295.         char *notify;
  296.         
  297.         /* extract information from the event string */
  298.         sscanf(buf+1, "%d %d", &x, &y);
  299.         if ((notify = strchr(buf, ',')) == CNULL)
  300.           closedown("no comma in button event!");
  301.         notify++;
  302.         clickon(x, y, notify);
  303.         }
  304.         break;
  305.  
  306.       case 's':        /* they've reshaped the window - unshape it */
  307.         DUPKEYOFF;
  308.         get_size(&wind_x, &wind_y, NULL, NULL);
  309.         DUPKEYON;
  310.         m_shapewindow(wind_x, wind_y, wind_w, wind_h);
  311.         /* fall through to redraw */
  312.  
  313.       case 'r':        /* redraw the screen */
  314.         m_clear();
  315.         m_bitcopyto(icon_x, icon_y, icon_w, icon_h, 0, 0, 0, ICONBITMAP);
  316.         m_flush();
  317.         break;
  318.  
  319.       case 'n':        /* remake the menu */
  320.         makemenu();
  321.         m_flush();
  322.         break;
  323.  
  324.       case 'm':        /* window selected from menu */
  325.         openwindows(buf+1);    /* open the windows attached to the given */
  326.                 /* ptty */
  327.         break;
  328.  
  329.       default:        /* something we weren't expecting */
  330.         {
  331.         char errtmp[BUFLEN*2];
  332.         sprintf(errtmp, "unrecognized event: %s", buf);
  333.         closedown(errtmp);
  334.         break;
  335.         }
  336.         break;
  337.     }
  338.     }
  339. }
  340.  
  341.  
  342. /* =============== get our events and only our events =============== */
  343.  
  344. skipgarbage()
  345. /* scan the input stream until we find a ^E followed by a non-
  346.    ^E character.  Skip any spaces or tabs.  The first non-space/tab
  347.    is pushed back into the input stream. */
  348. {
  349.     int c;
  350.     
  351.     m_flush();
  352.     
  353.     if (dupkeyokay) {
  354.     do {
  355.         while ((c = m_getchar()) != EOF && c != '\005')
  356.           ;
  357.         if (c != EOF)
  358.           c = m_getchar();
  359.     } while (c == '\005');
  360.     
  361.     while (isspace(c) && c != '\n')
  362.       c = m_getchar();
  363.     
  364.     if (c != EOF)
  365.       ungetc(c, m_termin);
  366.     }
  367.     else {
  368.     if ((c = m_getchar()) != '\005')
  369.       ungetc(c, m_termin);
  370.     }
  371. }
  372.  
  373.  
  374. /* ===================== Exit Cleanly ==================== */
  375.  
  376. catchint()
  377. /* function to call on interrupt - exit with error status */
  378. {
  379.     signal(SIGINT, SIG_DFL);    /* turn off interrupt catching */
  380.     closedown("");
  381. }
  382.  
  383. closedown(msg)
  384.      char *msg;
  385. /* Clean up window, restore window to its original shape.  If msg == NULL,
  386.    shut down with no error status.  If msg == "", exit with an error
  387.    status.  Otherwise, print <progname>: <msg>\n and exit with an error. */
  388. {
  389.     m_popall();
  390.     m_setcursor(CS_BLOCK);
  391.     m_clear();
  392.     m_flush();
  393.     m_ttyreset();
  394.  
  395.     /* restore old protection, if we ever changed it */
  396.     if ((oldmode & 077) != 0 && windowpid && chmod(ourtty, oldmode) == -1)
  397.       msg = THEERROR("chmod ourtty II: ");
  398.  
  399.     if (msg == CNULL)
  400.       exit(0);
  401.  
  402.     if (msg[0] != '\0')
  403.     fprintf(stderr, "%s: %s\n", progname, msg);
  404.     exit(2);
  405. }
  406.  
  407.  
  408. /* ==================== Open or close a window ==================== */
  409.  
  410. clickon(x, y, notify)
  411.      int x, y;
  412.      char *notify;
  413. /* deal with a click on the given position - we get the notify string at
  414.    that position for free */
  415. {
  416.     char tty[TTYSUFFIXLEN];    /* the tty for that window */
  417.     int win, pid;        /* alternate window #, pid */
  418.     void foreachwindow();    /* iterate over some windows */
  419.     void dontnotice();        /* mark window to be ignored */
  420.     void shapeclose();        /* reshape & relabel a window as closed */
  421.     void queueopen();        /* send the escape codes to re-open a
  422.                    window when the output is re-enabled */
  423.  
  424.     DUPKEYOFF;
  425.     get_size(&wind_x, &wind_y, NULL, NULL); /* where are we? */
  426.     DUPKEYON;
  427.  
  428.     x += wind_x; y += wind_y;    /* adjust click location to display */
  429.                 /* co-ordinates */
  430.  
  431.     /* what did the user click on? */
  432.     if (get_whatsat(x, y, tty, &win, &pid) != 0) {
  433.     if (strncmp(notify, closednotify, CLNLEN) == 0)
  434.       openwindows(tty);
  435.     else {
  436.         /* get a handle on their tty */
  437.         FILE *ttyfp, *oldtermout;
  438.         int ttyfd;
  439.  
  440.         if ((ttyfp = fopen(FULLTTYNAME(tty), "w")) == NULL)
  441.           closedown(THEERROR("open tty II: "));
  442.         ttyfd = fileno(ttyfp);
  443.  
  444.         /* disconnect the menu - the menu isn't going to be valid
  445.            until this process completes */
  446.         m_nomenu();
  447.         m_flush();
  448.  
  449.         DUPKEYOFF;
  450.         layoutlen = get_all(layout); /* get list of all the windows */
  451.         DUPKEYON;
  452.         
  453.         /* make sure that the window isn't currently ^S-ed */
  454.         if (iocuddle(ttyfd, TIOCSTART) == -1)
  455.           closedown(THEERROR("iocuddle TIOCSTART II: "));
  456.  
  457.         /* don't notice the windows that haven't been closed yet */
  458.         foreachwindow(layout, layoutlen, tty, dontnotice, CNULL);
  459.  
  460.         /* close the windows attached to this tty */
  461.         foreachwindow(layout, layoutlen, tty, shapeclose, notify);
  462.  
  463.         /* We need to know when MGR is finished interpreting all those
  464.            commands we've queued up there so we don't iocuddle too soon
  465.            and freeze the tty before MGR has read the commands.  If we
  466.            succeeded in turning off general write permission up at the
  467.            top, we can have the closee send the closer a message with
  468.            the m_sendto()/ACCEPT mechanism.  Otherwise, we'll just wait
  469.            an arbitrary amount of time for MGR to finish up, and then
  470.            iocuddle.  windowpid is set iff we could set up the tty to have
  471.            write permission turned off. */
  472.         
  473.         if (windowpid != 0) {
  474.         /* have mgr tell me when it's finished with all that
  475.            stuff */
  476.         m_push(P_EVENT);
  477.         m_setevent(ACCEPT, "\005done!\\r");
  478.         m_flush();
  479.         oldtermout = m_termout; m_termout = ttyfp;
  480.         m_sendto(windowpid, "fini!");
  481.         m_flush();
  482.         m_termout = oldtermout;
  483.         skipgarbage();
  484.         while (m_getchar() != '\n')
  485.           ;
  486.         m_pop();
  487.         m_flush();
  488.         }
  489.         else
  490.           sleep(WAITTIME);
  491.  
  492.         /* stop all output to that ptty */
  493.         if (iocuddle(ttyfd, TIOCSTOP) == -1)
  494.           closedown(THEERROR("iocuddle TIOCSTOP: "));
  495.         
  496.         /* queue escape codes to re-open them when output is
  497.            re-enabled */
  498.         foreachwindow(layout, layoutlen, tty, queueopen, CNULL);
  499.  
  500.         fclose(ttyfp);    /* close the tty - we're done */
  501.  
  502.         makemenu();        /* refresh the menu's view of life */
  503.     }
  504.     }
  505. }
  506.  
  507.  
  508. /* =============== the nitty-gritty of opening and closing =============== */
  509.  
  510. /*ARGSUSED*/
  511. void
  512. dontnotice(t, w)
  513.      FILE *t;
  514.      struct window_data *w;
  515. /* mark *w as a window for find_spot to ignore */
  516. {
  517.     if (w->w > 0) w->w = -w->w;
  518. }
  519.  
  520. void
  521. shapeclose(t, w, notify)
  522.      FILE *t;
  523.      struct window_data *w;
  524.      char *notify;
  525. /* reshape, retitle, reposition, and renotify the given window.  Use
  526.    layout[] to find a nice position for the closed window, and ignore
  527.    windows with negative widths in layout[] - these are windows that
  528.    will be closed, but haven't been dealt with yet.  Once we've closed
  529.    this window, record its new position in layout[]. */
  530. {
  531.     int width, height;
  532.     int new_x, new_y;
  533.     char title[BUFLEN*2];    /* space for window's title */
  534.  
  535.     /* quick hack for positioning windows on a full screen */
  536.     static int next_x, next_y, widest;
  537.  
  538.     if (w->num == 0)
  539.       sprintf(title, "%-5s",
  540.           (notify[0] == '\0' ? FULLTTYNAME(w->tty) : notify));
  541.     else
  542.       sprintf(title, "%-5s(%d)",
  543.           (notify[0] == '\0' ? FULLTTYNAME(w->tty) : notify),
  544.           w->num);
  545.  
  546.     width = strlen(title)*font_w + 2*border;
  547.     height = font_h + 2*border;
  548.     if (find_spot(width, height, layout, layoutlen, &new_x, &new_y) == 0) {
  549.  
  550.     /* quick hack to position a window on a full screen */
  551.     new_x = next_x; new_y = next_y;
  552.     width += XSLOP;
  553.     height += YSLOP;
  554.     if (width > widest) widest = width;
  555.     if ((next_y += height) > disp_h - height) {
  556.         next_y = 0;
  557.         next_x += widest;
  558.         widest = 0;
  559.     }
  560.     }
  561.  
  562.     {
  563.     FILE *oldtermout = m_termout;
  564.     char newnotify[500];
  565.     m_termout = t;
  566.  
  567.     /* build up the new notify string: CLOSED:<tty>:<window title> */
  568.     sprintf(newnotify, "%s%s:%s", closednotify, w->tty, title);
  569.     
  570.     m_selectwin(w->num);
  571.     m_push(P_CURSOR|P_EVENT|P_FLAGS|P_FONT|P_POSITION|P_TEXT|P_WINDOW);
  572.                 /* push window state */
  573.     m_envcount--;        /* that push isn't OUR push */
  574.     m_font(0);        /* use the default font */
  575.     m_size(strlen(title),1); /* resize it to display message */
  576.     m_movewindow(new_x, new_y); /* move it to a good place */
  577.     m_setevent(NOTIFY,newnotify); /* mark window as closed */
  578.     m_setmode(M_NOWRAP);    /* turn off line wrap */
  579.     m_clearmode(M_ACTIVATE); /* and bury the window */
  580.     m_clear();        /* make it blank */
  581.     m_printstr(title);    /* print new label */
  582.     m_flush();
  583.  
  584.     m_termout = oldtermout;
  585.     }
  586.  
  587.     /* record this window's new size and shape in layout[], and thus
  588.        mark it as something to be considered in subsequent find_spot()s */
  589.     w->x = new_x; w->y = new_y;
  590.     w->w = width; w->h = height;
  591. }
  592.  
  593. void
  594. queueopen(t, w)
  595.      FILE *t;
  596.      struct window_data *w;
  597. /* queue an m_selectwin and an m_pop for this window */
  598. {
  599.     FILE *oldtermout = m_termout;
  600.  
  601.     m_termout = t;
  602.  
  603.     m_selectwin(w->num);
  604.     m_envcount++;        /* that pop's not OUR pop */
  605.     m_pop();
  606.     m_flush();
  607.  
  608.     m_termout = oldtermout;
  609. }
  610.  
  611.  
  612. openwindows(tty)
  613.      char *tty;
  614. /* Do the stuff to open a window- do a TIOCSTART iocuddle to let the
  615.    commands sent by queueopen() take effect, and tell ify when it's done. */
  616. {
  617.     FILE *oldtermout, *ttyfp;
  618.     int ttyfd;
  619.     
  620.     if ((ttyfp = fopen(FULLTTYNAME(tty), "r+")) == NULL)
  621.       closedown(THEERROR("open tty: "));
  622.     ttyfd = fileno(ttyfp);
  623.     
  624.     /* disconnect the menu - the menu isn't going to be valid
  625.        until this process completes */
  626.     m_nomenu();
  627.     m_flush();
  628.  
  629.     /* re-enable output to that ptty */
  630.     if (iocuddle(ttyfd, TIOCSTART) == -1)
  631.       closedown(THEERROR("iocuddle TIOCSTART: "));
  632.     
  633.     /* queue escape codes to let us know when they're all
  634.        opened, if we can */
  635.     if (windowpid != 0) {
  636.     oldtermout = m_termout;
  637.     m_termout = ttyfp;
  638.     m_sendto(windowpid, "fini!");
  639.     m_flush();
  640.     m_termout = oldtermout;
  641.     }
  642.     else {
  643.     sleep(WAITTIME);    /* just wait for it to finish */
  644.     makemenu();        /* make a new menu */
  645.     }
  646.  
  647.     fclose(ttyfp);
  648. }
  649.  
  650.  
  651. /* =============== What's on the screen here? =============== */
  652.  
  653. int
  654. get_whatsat(x, y, tty, win, pid)
  655.      int x, y;
  656.      char *tty;
  657.      int *win, *pid;
  658. /* find the window under position x, y, and return the suffix of its tty
  659.    in tty and its pid in *pid.  If there is no window at (x, y), or
  660.    if the window there is one of our own, return 0.  Otherwise, return 1. */
  661. {
  662.     char buf[80];        /* space for the reply */
  663.     char fulltty[TTYPREFIXLEN + TTYSUFFIXLEN];
  664.  
  665.     m_whatsat(x, y);        /* send the request */
  666.     m_flush();
  667.     skipgarbage(); m_gets(buf);    /* read the response */
  668.  
  669.     if (buf[0] == '\n')        /* empty line?  Nothing there */
  670.       return 0;
  671.  
  672.     /* old MGR gives you "/dev/ttyxx win pid", newer mgr gives you
  673.        "xx yy win pid", where xx and yy are tty suffixes.  Deal with
  674.        both. */
  675.     sscanf(buf, "%s", fulltty);
  676.     if (fulltty[0] == '/') {    /* full tty name? */
  677.     sscanf(buf, "/dev/tty%s %d %d", tty, win, pid);
  678.     return(windowpid == 0 || windowpid != *pid);
  679.     }
  680.     else {
  681.     sscanf(buf, "%s %s %d %d", fulltty, tty, win, pid);
  682.     if (windowpid == 0)
  683.       return(strcmp(fulltty, tty) != 0);
  684.     else
  685.       return(windowpid != *pid);
  686.     }
  687. }
  688.  
  689.  
  690. /* =============== Iteration over a list of windows =============== */
  691.  
  692. void
  693. foreachwindow(win, count, tty, func, data)
  694.      struct window_data win[];
  695.      int count;
  696.      char *tty;
  697.      void (*func)();
  698.      char *data;
  699. /* find every window in win[0..count-1] attached to the given tty, and
  700.    apply func to a file pointer to the tty and the window's parameters.
  701.    if tty==NULL, apply func to all the windows.  tty should be just the
  702.    suffix of the pseudo-tty's name.
  703.  
  704.    func is unsigned long (*func)(FILE *t, struct window_data *w,
  705.                                  char *data),
  706.    where t is the window's tty, w is a pointer to the window's parameters,
  707.    and data is the same value passed to foreachwindow(). */
  708. {
  709.     FILE *ttyfp = NULL;
  710.     char *lastttyopened = "";
  711.     int i;
  712.  
  713.     if (tty && (ttyfp = fopen(FULLTTYNAME(tty), "r+")) == NULL)
  714.       closedown(THEERROR("open tty foreachwindow: "));
  715.     
  716.     for (i = 0; i < count; i++) {
  717.     
  718.     /* if the user didn't specify a particular tty and the next window
  719.        is attached to the one different from the one we've already
  720.        opened, open the new one. */
  721.     if (!tty && strcmp(win[i].tty, lastttyopened)!=0) {
  722.         if (ttyfp)
  723.           fclose(ttyfp);
  724.         if ((ttyfp = fopen(FULLTTYNAME(win[i].tty), "r+")) == NULL)
  725.           closedown(THEERROR("open tty foreachwindow II: "));
  726.         lastttyopened = win[i].tty;
  727.     }
  728.  
  729.     /* apply the function to the window and its stuff */
  730.     if (!tty || strcmp(tty, win[i].tty)==0)
  731.       (*func)(ttyfp, &win[i], data);
  732.     }
  733.  
  734.     if (ttyfp)
  735.       fclose(ttyfp);
  736. }
  737.  
  738.  
  739. /* ==================== Put the window someplace nice ==================== */
  740.  
  741.  
  742. int
  743. find_spot(wide, high, coords, count, newx, newy)
  744.      int wide,high;        /* minimum spot size */
  745.      struct window_data coords[]; /* current screen layout */
  746.      int count;            /* # of entries in coords[] */
  747.      int *newx, *newy;        /* put resulting co-ords here */
  748. /* scan the screen (as described in coords[]) for a free spot of the given
  749.    size (wide x high).  Place the resulting co-ordinates in *newx and
  750.    *newy.  Ignore entries in coords whose widths are negative.
  751.    
  752.    =============== This Code Was Stolen From Steve Uhler. ===============
  753.                                        (credit where credit is due dept.)*/
  754. {
  755.     register int c, intersection, x, y, nexty;
  756.     static int in_win();
  757.     
  758.     dprintf(stderr,"found %d windows\n", count);
  759.     
  760.     wide += XSLOP;
  761.     high += YSLOP;
  762.  
  763.     /* Find the best spot.  We want to avoid too exhaustive a search.
  764.        We march through the screen, trying to fit the moving window into
  765.        spaces.  Any time we collide with a window, we skip to the right edge
  766.        of that window and note if it's top edge is the lowest one we've seen
  767.        which is still above where we are.  This allows us to skip over the
  768.        larger areas of occupied screen quickly. */
  769.     for( y = disp_h-high;  y >= 0;  y = nexty - 1 ) {
  770.     nexty = 0;
  771.     for( x = disp_w-wide;  x >= 0;  x -= 1 ) {
  772.         intersection = 0;
  773.         dprintf(stderr,"  - Checking %d,%d\n",x,y);
  774.         for( c = 0;  c < count;  c++ ) {
  775.         if( coords[c].w < 0 )
  776.           continue;
  777.         if( in_win( coords+c, x, y, x + wide, y + high ) ) {
  778.             intersection = 1;
  779.             nexty = Max(nexty, coords[c].y - high);   
  780.             x = coords[c].x - wide;
  781.             break;
  782.         }
  783.         }
  784.         if( !intersection ) {
  785.         dprintf(stderr,"going to %d, %d\n", x, y);
  786.         *newx = x+XSLOP/2; *newy = y+YSLOP/2;
  787.         return( 1 );
  788.         }
  789.     }
  790.     }
  791.     dprintf(stderr,"no openings\n");
  792.     return( 0 );
  793. }
  794.  
  795.  
  796. /* check for window-rectangle intersection */
  797.  
  798. static int
  799. in_win(list,x0,y0,x1,y1)
  800.      register struct window_data *list;    /* window coordinates */
  801.      register int x0,y0,x1,y1;    /* rectangle coordinates */
  802. {
  803.     return((list->x + list->w < x0  ||  x1 < list->x ||
  804.         list->y + list->h < y0  ||  y1 < list->y)
  805.        ?  0  :  1);
  806. }
  807.  
  808. /* ==================== get window id ==================== */
  809.  
  810. getwid()
  811. {
  812.     char name[80], buf[80];
  813.  
  814.     /* establish a unique notify name */
  815.     sprintf(name, "getwid %d", getpid());
  816.     m_push(P_EVENT);
  817.     m_setevent(NOTIFY, name);
  818.     m_getinfo(G_NOTIFY);
  819.     /* skipgarbage() would go here, but this gets called with no dupkey. */
  820.     for (;;) {
  821.     m_gets(buf);
  822.     if (buf[0] == '\n')
  823.       break;
  824.     buf[strlen(buf)-1] = '\0'; /* wipe out newline */
  825.     if (strcmp(buf+strlen(buf)-strlen(name), name) == 0) {
  826.         int wid = atoi(buf);
  827.         do
  828.           m_gets(buf);
  829.         while (buf[0] != '\n');
  830.         m_pop();
  831.         return(wid);
  832.     }
  833.     }
  834.  
  835.     m_pop();
  836.     return(0);
  837. }
  838.  
  839.  
  840. /* ==================== build the menu ==================== */
  841.  
  842. #define MAXITEMS (500)
  843. #define MAXTEXTLEN (80)
  844. #define MAXEVENTLEN (30)
  845.  
  846. makemenu()
  847. /* Look at the list of NOTIFY-ing windows and build a menu listing
  848.    the closed windows, where the event string for each item provides
  849.    the name of the ptty running the window. */
  850. {
  851.     static char toptext[] = "don't quit,quit,remake menu,";
  852.     static char topevents[] = ",\005q\\r,\005n\\r,";
  853.  
  854.     char data[80];         /* hold a line of data */
  855.     char text[MAXITEMS*MAXTEXTLEN]; /* text of menu items */
  856.     char events[MAXITEMS*MAXEVENTLEN]; /* text of events */
  857.     static char all[MAXITEMS*(MAXTEXTLEN+MAXEVENTLEN)]; /* the whole menu */
  858.  
  859.     char *textend, *eventend;    /* free space at end of text[] and event[] */
  860.     char *windowname;        /* name of the window within data[] */
  861.  
  862.     static char line[] = "------------------------------";
  863.     char *partline;
  864.     int longesttext = 11;    /* length of the longest menu item */
  865.  
  866.     DUPKEYOFF;            /* turn dupkey off, since m_getinfo(G_NOTIFY)
  867.                    won't work with dupkey on (MGR bug! Poor
  868.                    Steve!) */
  869.  
  870.     textend = text;
  871.     eventend = events;
  872.     text[0] = events[0] = '\0';
  873.  
  874.     /* Get the list of available windows, store their ttys in ttys[],
  875.        and use their names to build up the text of the menu */
  876.     m_push(P_EVENT);
  877.     m_nomenu();
  878.     m_clearmenu(1);
  879.     m_getinfo(G_NOTIFY);    /* ask for the data */
  880.     m_flush();
  881.     for (;;) {
  882.     if (fgets(data, sizeof(data), m_termin) == NULL)
  883.       break;
  884.     if (data[0] == '\n')
  885.       break;
  886.     
  887.     data[strlen(data)-1] = '\0';
  888.     
  889.     /* Is this a valid G_NOTIFY info string?  Check delimiters.*/
  890.     {
  891.         char *p;
  892.         if ((p = strchr(data, '.')) == NULL ||
  893.         (p = strchr(p+1, ' ')) == NULL ||
  894.         (p = strchr(p+1, ' ')) == NULL)
  895.           continue;
  896.         
  897.         windowname = p+1;
  898.         if ((p = strchr(p+1, ':')) == NULL ||
  899.         (p = strchr(p+1, ':')) == NULL)
  900.           continue;
  901.     }
  902.     
  903.     /* do we want to include this menu item in the list?  Does its
  904.        name start with closednotify[]? */
  905.     if (strncmp(windowname, closednotify, CLNLEN) == 0) {
  906.         
  907.         /* zap all occurrences of the menu separator */
  908.         {
  909.         char *p;
  910.         
  911.         for (p = windowname; p = strchr(p, ',');)
  912.           *p = ' ';
  913.         }
  914.         
  915.         strcat(eventend, "\005m"); eventend += strlen(eventend);
  916.         sscanf(windowname, "CLOSED:%[^:]:%[^:]", eventend, textend);
  917.         if (strlen(textend) > longesttext)
  918.           longesttext = strlen(textend);
  919.         strcat(textend, ",");
  920.         strcat(eventend, "\\r,");
  921.         
  922.         textend += strlen(textend);
  923.         eventend += strlen(eventend);
  924.     }
  925.     }
  926.     
  927.     /* build up a dividing line with the appropriate length */
  928.     if (longesttext > sizeof(line)-1)
  929.       longesttext = sizeof(line)-1;
  930.     partline = line+(sizeof(line)-1-longesttext);
  931.  
  932.     /* now put all the pieces together */
  933.     sprintf(all, ",%s%s,%s%s,%s",
  934.         toptext,partline,text,topevents, events);
  935.  
  936.     m_pop();
  937.     m_loadmenu(1, all);
  938.     m_selectmenu(1);
  939.     DUPKEYON;
  940.     m_flush();
  941. }
  942.  
  943.  
  944. /* ==================== Does this MGR have a dupkey? ==================== */
  945.  
  946. int
  947. hasdupkey()
  948. {
  949.     int c, gotdupkey;
  950.  
  951.     m_push(P_FLAGS);
  952.     m_dupkey('\005');        /* turn on dupkey mode (maybe) */
  953.     m_ttyset();
  954.     m_getinfo(G_WINSIZE);    /* ask for something innocuous */
  955.     
  956.     /* examine what we got back and see if we got a dupkey with it */
  957.     gotdupkey = 0;
  958.     while ((c = getchar()) != '\n')
  959.       if (c == '\005')
  960.     gotdupkey = 1;
  961.     m_ttyreset();
  962.     m_pop();
  963.  
  964.     return gotdupkey;
  965. }
  966.  
  967.  
  968. print(s)
  969.      char *s;
  970. {
  971.     write(2, s, strlen(s));
  972. }
  973.