home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume3 / wm.new < prev    next >
Internet Message Format  |  1986-11-30  |  47KB

  1. From: wjh12!pixel!pixutl!chris
  2. Newsgroups: mod.sources
  3. Subject: updated window manager - wm
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 45
  7. Submitted by: wjh12!pixel!pixutl!chris
  8.  
  9.  
  10. Quite a while ago, Rob Jacob from NRL posted a window manager to the net.
  11. It sat on my machine for a long time until I decided to play with it.
  12. Eventually, I did. I enjoyed using it but I found some problems with it
  13. and starting modifying it. Later someone else here got interested too
  14. and we now have this version which we think is very nice. The list of
  15. modifications is at the top of 'wm.c'. The compile options are described
  16. in the makefile.
  17.  
  18. This version runs on our systems, which run a mostly 4.1 kernel. It compiled
  19. on a 2.9 system, but ran very slowly. The main area that might cause problems
  20. on non 68K systems is the code included by the 'FAST' #define. 'wm' runs a
  21. lot faster if it is defined but that stuff is not necessarily portable (it
  22. doesn't work on the PDP, for example).
  23.  
  24. Adapting it to 4.2 should require very little work and if someone does it and
  25. enjoys 'wm', I would appreciate getting the changes back. I would also like
  26. hearing about problems, bugs, improvements, etc...
  27.  
  28. The archive contains the following files:
  29.     Makefile
  30.     read.c
  31.     win.c
  32.     wm.1
  33.     wm.c
  34.     wm.h
  35. This is a complete posting, not just the diff's because the changes are
  36. everywhere and it was ran through 'cb' very early.
  37.  
  38. Warning: the format of the .wmrc file is different from that of the original
  39. version, mostly because the control character is saved from one session to the
  40. next so be sure to remove older .wmrc's or to used the '-' option on the first
  41. run, if you plan on using it.
  42.  
  43. NOTE: This new version is posted with the authorization of the original author.
  44.  
  45. ---------------------------- cut here -------------------------------
  46. echo 'sh - Makefile'
  47. sed 's/^X//' <<'________This_Is_The_END________'             >>Makefile
  48. X#
  49. X# Note: Set NUMSUFFIX, PTYBASENAME, PTSBASENAME, PTFIRST, and PTLAST
  50. X# in wm.h according to the instructions there before compiling
  51. X#
  52. X# If REVVIDEO is defined, the windows will handle reverse video.
  53. X# If FAST is defined, some possibly non-portable routines will be used
  54. X#    for string copy.
  55. X# If SHOWMAP is defined, the 'M' command will display the window layout.
  56. X# If SMOOTHIO is defined, programs that do a lot of cursor motions run
  57. X#    smoother but hog system.
  58. X#
  59. X
  60. XCFLAGS=-O -DREVVIDEO -DSHOWMAP -DFAST -DSMOOTHIO
  61. XLDFLAGS=-n
  62. XOBJS=wm.o read.o win.o
  63. X
  64. Xall: wm
  65. X
  66. Xwm: ${OBJS}
  67. X    cc $(LDFLAGS) -o wm wm.o read.o win.o -lcurses -ltermcap
  68. X
  69. X${OBJS}: wm.h
  70. X
  71. Xinstall: all
  72. X    install -s wm ${DESTDIR}/usr/local/bin
  73. X
  74. Xclean:
  75. X    rm -f *.o wm core a.out mon.out
  76. ________This_Is_The_END________
  77. echo 'sh - read.c'
  78. sed 's/^X//' <<'________This_Is_The_END________'             >>read.c
  79. X/*
  80. X * read.c  R. Jacob
  81. X * This is the code that does all kinds of reading.
  82. X *
  83. X * Modified by Chris Bertin and Mike Tellier, October 1985.
  84. X */
  85. X
  86. X#include <stdio.h>
  87. X#include "wm.h" /* includes sgtty.h */
  88. X#include <signal.h>
  89. X
  90. X/*
  91. X * Old-fashioned blocking version of getchar
  92. X * But uses unbuffered system call read
  93. X * and strips parity bit
  94. X */
  95. Xbgetch(fildes)
  96. Xint fildes; 
  97. X{
  98. X    char c;
  99. X
  100. X    if (read(fildes, &c, 1) <= 0)
  101. X        return(EOF);
  102. X    else
  103. X        return((int)(c&0177));
  104. X}
  105. X
  106. X/*
  107. X * Returns the number of characters to be read.
  108. X */
  109. Xttychars(fildes)
  110. Xint fildes; 
  111. X{
  112. X    long int count;
  113. X
  114. X    ioctl(fildes, FIONREAD, &count);
  115. X    return(count);
  116. X}
  117. X
  118. X/*
  119. X * Key definitions used only by routine getpos
  120. X * These keys are used only for entering position of new window
  121. X */
  122. X# define RIGHTCHAR    'r'
  123. X# define UPCHAR        'u'
  124. X# define LEFTCHAR    'l'
  125. X# define DOWNCHAR    'd'
  126. X# define BIGRIGHTCHAR    'R'    /* jump            */
  127. X# define BIGUPCHAR    'U'    /* one-fifth of the    */
  128. X# define BIGLEFTCHAR    'L'    /* way across        */
  129. X# define BIGDOWNCHAR    'D'    /* the screen        */
  130. X# define EXECCHAR    'x'
  131. X
  132. X/*
  133. X * obtain a y, x position using UPCHAR, etc.
  134. X * and return it as integers *yptr and *xptr
  135. X * start off at position y0, x0
  136. X * Does not permit bottom (y = LINES - 1) line, as it is saved for messages
  137. X */
  138. Xgetpos(yptr, xptr, y0, x0)
  139. Xint *yptr, *xptr; 
  140. Xregister int y0, x0; 
  141. X{
  142. X    register char c;
  143. X    register int bigvert, bighoriz;
  144. X    extern char cmdchar;
  145. X
  146. X    bigvert = LINES / 5 + 1;
  147. X    bighoriz = COLS / 5 + 1;
  148. X    showcursor(y0, x0);
  149. X    while ((c = bgetch(0)) != EXECCHAR) {
  150. X        if (c == RIGHTCHAR)
  151. X            x0 = MIN(x0 + 1, COLS - 1);
  152. X        else if (c == UPCHAR)
  153. X            y0 = MAX(y0 - 1, 0);
  154. X        else if (c == LEFTCHAR)
  155. X            x0 = MAX(x0 - 1, 0);
  156. X        else if (c == DOWNCHAR)
  157. X            y0 = MIN(y0 + 1, LINES - 2);
  158. X        else if (c == BIGRIGHTCHAR)
  159. X            x0 = MIN(x0 + bighoriz, COLS - 1);
  160. X        else if (c == BIGUPCHAR)
  161. X            y0 = MAX(y0 - bigvert, 0);
  162. X        else if (c == BIGLEFTCHAR)
  163. X            x0 = MAX(x0 - bighoriz, 0);
  164. X        else if (c == BIGDOWNCHAR)
  165. X            y0 = MIN(y0 + bigvert, LINES - 2);
  166. X        else if (c == cmdchar || c == 'q') {
  167. X            showmsg("");
  168. X            return(1);
  169. X        }
  170. X        showcursor(y0, x0);
  171. X    }
  172. X    *xptr = x0; 
  173. X    *yptr = y0;
  174. X    return(0);
  175. X}
  176. ________This_Is_The_END________
  177. echo 'sh - win.c'
  178. sed 's/^X//' <<'________This_Is_The_END________'             >>win.c
  179. X/*
  180. X * win.c  R. Jacob
  181. X * This is the code for manipulating the window and curses data structures
  182. X *
  183. X * Modified by Chris Bertin and Mike Tellier, October 1985.
  184. X */
  185. X
  186. X#include "wm.h"
  187. X#include <ctype.h>
  188. X
  189. Xmap_t *winmap;
  190. X
  191. Xinit() {}
  192. Xcloseup() {}
  193. X
  194. X/*
  195. X * create byte-mapped top window image.
  196. X */
  197. Xmakemap()
  198. X{
  199. X    register struct win_struct *wsp;
  200. X    register x, y, n, atlast;
  201. X    register map_t *cp;
  202. X    char c, lastc;
  203. X
  204. X#ifdef    FAST
  205. X    zero(winmap, LINES * COLS * sizeof *winmap);
  206. X#else
  207. X    for (y = LINES * COLS; --y >= 0; )
  208. X        winmap[y] = 0;
  209. X#endif
  210. X    for (wsp = topw->prev, atlast = 0; atlast == 0; wsp = wsp->prev) {
  211. X        atlast = (wsp == topw);
  212. X        if (wsp->flags & WERASED)
  213. X            continue;
  214. X        mapwindow(wsp->boxleft, wsp->id, 1);
  215. X        mapwindow(wsp->boxright, wsp->id, 1);
  216. X        mapwindow(wsp->boxtop, wsp->id, 0);
  217. X        mapwindow(wsp->boxbot, wsp->id, 0);
  218. X        mapwindow(wsp->wptr, wsp->id, 0);
  219. X    }
  220. X    for (y = LINES - 1; y >= 0; y--) {
  221. X        cp = &winmap[y * COLS] + COLS - 1;
  222. X        for (n = 0, lastc = 0, c = 0, x = 0; x < COLS; x++, cp--) {
  223. X            if ((c = mapchar(*cp)) != 0
  224. X#ifdef    FAST
  225. X                && (maptocount(*cp) == 0)
  226. X#endif
  227. X                               ) {
  228. X                if (c != lastc) {
  229. X                    n = 0;
  230. X                    lastc = c;
  231. X                }
  232. X#ifdef    FAST
  233. X                *cp = chartomap(c, ++n);
  234. X#else
  235. X                *cp = c;
  236. X#endif
  237. X            }
  238. X        }
  239. X    }
  240. X}
  241. X
  242. X/*
  243. X * Map window onto byte-mapped image
  244. X */
  245. Xmapwindow(wp, id, flag)
  246. Xregister WINDOW *wp;
  247. Xchar id;
  248. X{
  249. X    register x, y;
  250. X    register map_t *cp;
  251. X
  252. X    if (wp == NULL)
  253. X        return;
  254. X    for (y = wp->_begy; y < wp->_maxy + wp->_begy; y++) {
  255. X        cp = &winmap[y * COLS];
  256. X        for (x = wp->_begx; x < wp->_maxx + wp->_begx; x++)
  257. X#ifdef    FAST
  258. X            cp[x] = chartomap(id, flag);
  259. X#else
  260. X            cp[x] = id;
  261. X#endif
  262. X    }
  263. X}
  264. X
  265. X/*
  266. X * make wsp the top window by changing topw and adjusting linked list
  267. X */
  268. Xchgwin(wsp)
  269. Xregister struct win_struct *wsp;
  270. X{
  271. X    if (wsp == topw)
  272. X        return;
  273. X    wsp->flags &= ~WERASED;
  274. X    wsp->flags |= WTOUCHED;
  275. X    if (wsp->next) {
  276. X        wsp->prev->next = wsp->next;
  277. X        wsp->next->prev = wsp->prev;
  278. X    }
  279. X    if (topw) {
  280. X        wsp->next = topw;
  281. X        wsp->prev = topw->prev;
  282. X        topw->prev = wsp;
  283. X        wsp->prev->next = wsp;
  284. X    }
  285. X    else
  286. X        wsp->next = wsp->prev = wsp;
  287. X    topw = wsp;
  288. X    makemap();
  289. X}
  290. X
  291. X#define doscroll(w)    scrollok(w, TRUE), scroll(w), scrollok(w, FALSE), wmove(w, w->_maxy - 1, w->_curx)
  292. X/*
  293. X * Add character c to window wsp and to corresponding virtual window
  294. X * and interpret virtual terminal commands
  295. X */
  296. Xadd(wsp, c)
  297. Xstruct win_struct *wsp;
  298. Xchar c;
  299. X{
  300. X    register WINDOW *wp, *vp;
  301. X    register wpy, wpx, vpy, vpx;
  302. X    int doit, moveit;
  303. X
  304. X    wp = wsp->wptr;
  305. X    vp = wsp->virt;
  306. X    getyx(wp, wpy, wpx);
  307. X    getyx(vp, vpy, vpx);
  308. X    doit = FALSE;
  309. X    moveit = FALSE;
  310. X
  311. X    if (wsp->pend[0] == CODE && wsp->pend[1] == 'Y') { /* cursor motion */
  312. X        if (wsp->pend[2] != '\0') { /* ESC-Y-pos */
  313. X            wpy = vpy = (int)(wsp->pend[2] - ' ');
  314. X            wpx = vpy = (int)(c - ' ');
  315. X            moveit = TRUE;
  316. X            wsp->pend[0] = '\0';
  317. X        }
  318. X        else {
  319. X            wsp->pend[2] = c;
  320. X            wsp->pend[3] = '\0';
  321. X            return;
  322. X        } /* ESC-Y */
  323. X    }
  324. X    else if (wsp->pend[0] == CODE) { /* ESC */
  325. X#ifdef    SMOOTHIO
  326. X        wsp->flags |= WTOUCHED;
  327. X        repaint(wsp);
  328. X#endif
  329. X        if (c == 'Y') {
  330. X            wsp->pend[1] = c;
  331. X            wsp->pend[2] = '\0';
  332. X        }
  333. X        else if (c == 'K') {
  334. X            wclrtoeol(wp);
  335. X            wclrtoeol(vp);
  336. X            wsp->pend[0] = '\0';
  337. X        }
  338. X        else if (c == 'S') {
  339. X            werase(wp);
  340. X            werase(vp);
  341. X            wpx = vpx = 0;
  342. X            wpy = vpy = 0;
  343. X            moveit = TRUE;
  344. X            wsp->pend[0] = '\0';
  345. X        }
  346. X        else if (c == 'C') {
  347. X            wpx++;
  348. X            vpx++;
  349. X            moveit = TRUE;
  350. X            wsp->pend[0] = '\0';
  351. X        }
  352. X        else if (c == 'A') {
  353. X            wpy--;
  354. X            vpy--;
  355. X            moveit = TRUE;
  356. X            wsp->pend[0] = '\0';
  357. X        }
  358. X#ifdef    REVVIDEO
  359. X        else if (c == 'O') {
  360. X            wstandout(wp);
  361. X            wstandout(vp);
  362. X            wsp->pend[0] = '\0';
  363. X        }
  364. X        else if (c == 'E') {
  365. X            wstandend(wp);
  366. X            wstandend(vp);
  367. X            wsp->pend[0] = '\0';
  368. X        }
  369. X#endif
  370. X        else {
  371. X            doit = TRUE;
  372. X            wsp->pend[0] = '\0';
  373. X        }
  374. X    }
  375. X    else if (c == CODE) {
  376. X        wsp->pend[0] = c;
  377. X        wsp->pend[1] = '\0';
  378. X    }
  379. X    else
  380. X        doit = TRUE;
  381. X    if (moveit || !doit) {
  382. X        if (wpx < 0)
  383. X            wpx = 0;
  384. X        else if (wpx >= wp->_maxx)
  385. X            wpx = wp->_maxx - 1;
  386. X        if (wpy < 0)
  387. X            wpy = 0;
  388. X        else if (wpy >= wp->_maxy)
  389. X            wpy = wp->_maxy - 1;
  390. X        wmove(wp, wpy, wpx);
  391. X        if (vpx < 0)
  392. X            vpx = 0;
  393. X        else if (vpx >= vp->_maxx)
  394. X            vpx = vp->_maxx - 1;
  395. X        if (vpy < 0)
  396. X            vpy = 0;
  397. X        else if (vpy >= vp->_maxy)
  398. X            vpy = vp->_maxy - 1;
  399. X        wmove(vp, vpy, vpx);
  400. X        return;
  401. X    }
  402. X    if (c == '\007') {
  403. X        showmsg("        ");
  404. X        showmsg("");
  405. X        showmsg("Bell");
  406. X        return;
  407. X    }
  408. X    else if (c == '\b') {
  409. X        if (wp->_curx > 0)
  410. X            wp->_curx--;
  411. X        if (vp->_curx > 0)
  412. X            vp->_curx--;
  413. X    }
  414. X    else if (c == '\r') {
  415. X        wp->_curx = 0;
  416. X        vp->_curx = 0;
  417. X    }
  418. X    else if (c == '\n') {
  419. X        wp->_cury++;
  420. X        if (wp->_cury >= wp->_maxy)
  421. X            doscroll(wp);
  422. X        vp->_cury++;
  423. X        if (vp->_cury >= vp->_maxy)
  424. X            doscroll(vp);
  425. X        if (wsp == topw || wp->_cury == wp->_maxy - 1) {
  426. X            wsp->flags |= WTOUCHED;
  427. X            repaint(wsp);
  428. X        }
  429. X    }
  430. X    else if (isprint(c) || c == ' ' || c == '\t') {
  431. X        int corner = (wp->_curx == wp->_maxx - 1) &&
  432. X                 (wp->_cury == wp->_maxy - 1);
  433. X        waddch(wp, c);
  434. X        waddch(vp, c);
  435. X        if (corner) {
  436. X            add(wsp, '\n');
  437. X            wmove(wp, wp->_cury, 0);
  438. X            wmove(vp, vp->_cury, 0);
  439. X        }
  440. X    }
  441. X    else if (c != '\0') {
  442. X        if (c < '\040')
  443. X            c += 0100;
  444. X        else if (c == '\177')
  445. X            c = '?';
  446. X        waddch(wp, '^');
  447. X        waddch(vp, '^');
  448. X        waddch(wp, c);
  449. X        waddch(vp, c);
  450. X    }
  451. X}
  452. X
  453. X/*
  454. X * Creates up to 4 windows making a box to surround window wsp and puts
  455. X * pointer to the new windows into proper places in global win[].
  456. X * Sets WFULLSCREEN if applicable.
  457. X */
  458. Xsetupbox(wsp)
  459. Xstruct win_struct *wsp;
  460. X{
  461. X    WINDOW *wp;
  462. X    int top, bot, left, right;
  463. X    int begx, begy, maxx, maxy;
  464. X    int i;
  465. X
  466. X    wp = wsp->wptr; /* just to save typing */
  467. X    begx = wp->_begx;
  468. X    begy = wp->_begy;
  469. X    maxx = wp->_maxx;
  470. X    maxy = wp->_maxy;
  471. X    wsp->boxtop = wsp->boxleft = wsp->boxright = wsp->boxbot = 0;
  472. X    left = (begx > 0)? TRUE: FALSE;
  473. X    top = (begy > 0)? TRUE: FALSE;
  474. X    right = (begx+maxx < COLS)? TRUE: FALSE;
  475. X    bot = (begy+maxy < LINES - 1)? TRUE: FALSE;
  476. X    if (top)
  477. X        begy--, maxy++;
  478. X    if (bot)
  479. X        maxy++;
  480. X    if (left)
  481. X        begx--, maxx++;
  482. X    if (right)
  483. X        maxx++;
  484. X    if (top) {
  485. X        wsp->boxtop = newwin(1, maxx, begy, begx);
  486. X        wsp->boxtop->_flags = 0; /* get around curses bug */
  487. X        leaveok(wsp->boxtop, TRUE);
  488. X        for (i = 0; i < maxx; i++)
  489. X            waddch(wsp->boxtop, wsp->id);
  490. X        begy++;
  491. X        maxy--;
  492. X    }
  493. X    if (left) {
  494. X        wsp->boxleft = newwin(maxy, 1, begy, begx);
  495. X        wsp->boxleft->_flags = 0; /* get around curses bug */
  496. X        leaveok(wsp->boxleft, TRUE);
  497. X        for (i = 0; i < maxy - 1; i++)
  498. X            waddch(wsp->boxleft, '|');
  499. X        waddch(wsp->boxleft, wsp->id);
  500. X        begx++;
  501. X        maxx--;
  502. X    }
  503. X    if (right) {
  504. X        wsp->boxright = newwin(maxy, 1, begy, maxx+begx - 1);
  505. X        wsp->boxright->_flags = 0; /* get around curses bug */
  506. X        leaveok(wsp->boxright, TRUE);
  507. X        for (i = 0; i < maxy - 1; i++)
  508. X            waddch(wsp->boxright, '|');
  509. X        waddch(wsp->boxright, wsp->id);
  510. X        maxx--;
  511. X    }
  512. X    if (bot) {
  513. X        wsp->boxbot = newwin(1, maxx, maxy+begy - 1, begx);
  514. X        wsp->boxbot->_flags = 0; /* get around curses bug */
  515. X        leaveok(wsp->boxbot, TRUE);
  516. X        for (i = 0; i < maxx; i++)
  517. X            waddch(wsp->boxbot, '-');
  518. X    }
  519. X    if ((top | bot | right | left) == 0)
  520. X        wsp->flags |= WFULLSCREEN;
  521. X    else
  522. X        wsp->flags &= ~WFULLSCREEN;
  523. X}
  524. X
  525. X/*
  526. X * Clear the screen and then repaint all windows
  527. X */
  528. Xrepaintall()
  529. X{
  530. X    register struct win_struct *wsp;
  531. X    int atlast;
  532. X
  533. X    ttyclear();
  534. X    makemap();
  535. X    wsp = (topw->flags & WFULLSCREEN)? topw: topw->prev;
  536. X    for (atlast = 0; atlast == 0; wsp = wsp->prev) {
  537. X        atlast = (wsp == topw);
  538. X        if (wsp->boxtop)
  539. X            touchwin(wsp->boxtop);
  540. X        if (wsp->boxbot)
  541. X            touchwin(wsp->boxbot);
  542. X        if (wsp->boxleft)
  543. X            touchwin(wsp->boxleft);
  544. X        if (wsp->boxright)
  545. X            touchwin(wsp->boxright);
  546. X        touchwin(wsp->wptr);
  547. X        wsp->flags |= WTOUCHED;
  548. X        repaint(wsp);
  549. X    }
  550. X}
  551. X
  552. X/*
  553. X * Repaint window wsp
  554. X */
  555. Xrepaint(wsp)
  556. Xregister struct win_struct *wsp;
  557. X{
  558. X    if ((wsp->flags & WERASED) || (wsp->flags & WTOUCHED) == 0 ||
  559. X        ((wsp != topw) && (topw->flags & WFULLSCREEN)))
  560. X        return;
  561. X    wcopy(wsp->boxleft, wsp->id);
  562. X    wcopy(wsp->boxright, wsp->id);
  563. X    wcopy(wsp->boxtop, wsp->id);
  564. X    wcopy(wsp->boxbot, wsp->id);
  565. X    wcopy(wsp->wptr, wsp->id);
  566. X    wsp->flags &= ~WTOUCHED;
  567. X    refresh();
  568. X}
  569. X
  570. X/*
  571. X * Copy window image into byte-mapped top window
  572. X */
  573. Xwcopy(win, id)
  574. Xregister WINDOW *win;
  575. Xregister char id;
  576. X{
  577. X    register char *screen;
  578. X    register map_t *map;
  579. X    register count, x, y, bx, by, my;
  580. X
  581. X    if (win == NULL)
  582. X        return;
  583. X    bx = win->_begx;
  584. X    by = win->_begy;
  585. X    my = win->_maxy;
  586. X    for (y = 0; y < my; y++) {
  587. X        if ((x = win->_firstch[y]) == _NOCHANGE)
  588. X            continue;
  589. X        screen = &stdscr->_y[y + by][x + bx];
  590. X        map = &winmap[(y + by) * COLS + x + bx];
  591. X        for (; x <= win->_lastch[y]; x++, screen++, map++) {
  592. X            if (mapchar(*map) != id)
  593. X                continue;
  594. X            if (stdscr->_firstch[y + by] == _NOCHANGE) {
  595. X                stdscr->_firstch[y + by] = x + bx;
  596. X                stdscr->_lastch[y + by] = x + bx;
  597. X            }
  598. X            if (stdscr->_firstch[y + by] > x + bx)
  599. X                stdscr->_firstch[y + by] = x + bx;
  600. X#ifdef    FAST
  601. X            if ((count = maptocount(*map)) > 1) {
  602. X                bcopy(&win->_y[y][x], screen, count);
  603. X                x += count - 1;
  604. X                screen += count - 1;
  605. X                map += count - 1;
  606. X            } else
  607. X#endif
  608. X                *screen = win->_y[y][x];
  609. X            if (stdscr->_lastch[y + by] < x + bx)
  610. X                stdscr->_lastch[y + by] = x + bx;
  611. X        }
  612. X    }
  613. X}
  614. X
  615. X/*
  616. X * show message at bottom of screen
  617. X */
  618. Xshowmsg(s)
  619. Xchar *s; 
  620. X{
  621. X    extern msg;
  622. X    int l = strlen(s);
  623. X    extern WINDOW *msgwin;
  624. X
  625. X    wclear(msgwin);
  626. X    wmove(msgwin, 0, 0);
  627. X    wstandout(msgwin);
  628. X    wprintw(msgwin, "%s", s);
  629. X    overwrite(msgwin, stdscr);
  630. X    refresh();
  631. X    showcursor(msgwin->_begy, msgwin->_begx + l + 1);
  632. X    msg = l > 0;
  633. X}
  634. X
  635. X#ifdef    FAST
  636. X/*
  637. X * copy 'count' bytes from 'from' to 'to'. (optimized)
  638. X */
  639. Xbcopy(from, to, count)
  640. Xregister char *from, *to;
  641. Xregister count;
  642. X{
  643. X    register c1;
  644. X
  645. X    if ((((int)to ^ (int)from) & 1) == 0) {
  646. X        if ((int)to & 1) {
  647. X            count--;
  648. X            *to++ = *from++;
  649. X        }
  650. X        c1 = count >> 2;
  651. X        while (--c1 >= 0)
  652. X            *(long *) to++ = *(long *) from++;
  653. X        count &= 3;
  654. X    }
  655. X    while (--count >= 0)
  656. X        *to++ = *from++;
  657. X}
  658. X
  659. X/*
  660. X * zero 'count' bytes in 'str'. (optimized)
  661. X */
  662. Xzero(str, count)
  663. Xregister char *str;
  664. Xregister count;
  665. X{
  666. X    register c1;
  667. X
  668. X    if ((int)str & 1) {
  669. X        count--;
  670. X        *str++ = 0;
  671. X    }
  672. X    c1 = count >> 2;
  673. X    while (--c1 >= 0)
  674. X        *(long *) str++ = 0L;
  675. X    count &= 3;
  676. X    while (--count >= 0)
  677. X        *str++ = 0;
  678. X}
  679. X#endif
  680. ________This_Is_The_END________
  681. echo 'sh - wm.1'
  682. sed 's/^X//' <<'________This_Is_The_END________'             >>wm.1
  683. X.TH WM 1 wm.v42.1
  684. X.SH NAME
  685. Xwm \- window manager
  686. X.SH SYNOPSIS
  687. X.B wm
  688. X[ \- ]
  689. X.SH DESCRIPTION
  690. X.I Wm
  691. Xmanages a collection of windows on a display terminal.
  692. X.PP
  693. XIt determines what type of terminal you are using from
  694. Xthe environment parameter $TERM
  695. X(see
  696. X.IR environ (5))
  697. Xand then uses
  698. X.IR curses (3)
  699. Xto adapt to it.
  700. X.PP
  701. XEach window has its own UNIX shell
  702. X.IR sh (1),
  703. Xrunning in parallel with those in the other windows.
  704. XThe idea behind this scheme is that you can use each window
  705. Xfor a series of commands dealing with a single topic.
  706. XThen, you can change back and forth between the windows without
  707. Xlosing your place in any of them.
  708. X.PP
  709. XAt any time, you can give commands to change the window you are typing in,
  710. Xto move a window or change its size,
  711. Xor to create or kill a window.
  712. XWindows can overlap or completely obscure one another;
  713. Xobscured windows can subsequently be "lifted" up
  714. Xand placed on top of the other windows.
  715. X.PP
  716. XWhen the program is started,
  717. Xit will attempt to restore the sizes and positions of the windows
  718. Xand the control character
  719. Xin use when you last ran
  720. X.IR wm .
  721. XFailing that (or if you give the optional argument \- ),
  722. Xit will ask you to indicate
  723. Xthe size and position of the first window.
  724. XTo do this, move the cursor to the position you want the
  725. Xlower left corner of your window
  726. Xto occupy,
  727. Xusing the keys
  728. X.B u
  729. X(up),
  730. X.B d
  731. X(down),
  732. X.B l
  733. X(left), and
  734. X.B r
  735. X(right)
  736. Xand also
  737. X.BR U ,
  738. X.BR D ,
  739. X.BR L ,
  740. Xand
  741. X.B R
  742. X(for large increments in the respective directions).
  743. XThen type an
  744. X.BR x .
  745. XThen move the cursor (using the same commands) to the upper
  746. Xright corner, and type another
  747. X.BR x .
  748. XNow, you can type UNIX commands in the new window.
  749. X.PP
  750. X.I Wm
  751. Xcommands can be issued at any time.
  752. XEvery
  753. X.I wm
  754. Xcommand consists of a prefix character followed by one
  755. Xmore character, possibly followed by some arguments.
  756. XAny other characters typed on the terminal are treated as input
  757. Xto the program in the current window.
  758. XTo enter the prefix character itself as input to a program, type it twice.
  759. XThe prefix character is initially set to ASCII ESC, but can be changed
  760. Xwith the
  761. X.I C
  762. Xcommand.  All commands can be interrupted by typing another prefix
  763. Xcharacter or a
  764. X.I q .
  765. X.PP
  766. XThe available
  767. X.I wm
  768. Xcommands are prefix character (ESC) followed by:
  769. X.IP \fBn\fP
  770. XCreate a new window.
  771. X.I Wm
  772. Xwill ask for the lower left and upper right corners,
  773. Xusing the same convention described above for creating the first window.
  774. X.IP \fBc\fP
  775. XChange the current window.
  776. XEnter the name (lower-case
  777. X.BR a ,
  778. X.BR b ,
  779. X.BR c ,
  780. Xetc.) of the
  781. Xwindow to which you want to change.
  782. XThe window names are shown in the top row of each window.
  783. XThe chosen window is placed on "top" of the screen
  784. X(that is, in front any other windows had been obscuring it).
  785. XInput from the keyboard will now be sent to this window.
  786. X(Delayed responses to commands entered in other windows will,
  787. Xof course, continue to be routed to their correct windows.)
  788. X.IP \fBl\fP
  789. XChange to the last-used window.
  790. XChange back to the window that had most recently been the current window.
  791. X.IP \fBm\fP
  792. XMove and/or change the size of a window.
  793. X.I Wm
  794. Xasks for the window name (same convention as for changing current window)
  795. Xand then for the lower left and upper right corners of desired
  796. Xnew position of the window (same convention as for the first window).
  797. XIf you enlarge a window, material that had scrolled off the top or been
  798. Xtruncated at the right margin will become visible again.
  799. XIt is not advisable to move a window except when
  800. Xthe program running in it is at the shell prompt level.
  801. X.IP \fBe\fP
  802. XErase the named window from the screen.
  803. XThe window still exists, any pending processing continues in it
  804. Xinvisibly, and it can be seen again with the change window command.
  805. X.IP \fBk\fP
  806. XKill the named window.
  807. XPermanently kills the processes associated with the window
  808. Xand erases the window.
  809. X(The
  810. X.I name
  811. Xof this window may later be re-used in creating a new window.)
  812. X.IP \fBC\fP
  813. XChange the prefix character.
  814. X.IP \fBr\fP
  815. XRedraw the screen.
  816. XClears the entire screen and redraws what should be there.
  817. XHelpful after garbage (like a broadcast message) has arrived.
  818. X.IP \fBp\fP
  819. XScroll to the top of a new page in the current window.
  820. XMaterial in a window is normally scrolled, one line at a time.
  821. XThis command forces the window to scroll forward by a distance equal
  822. Xto the height of the window, thereby clearing the window.
  823. X.IP "\fBq\fP or \fBQ\fP"
  824. XQuit.
  825. XThis stops all processes associated with
  826. X.I wm
  827. Xand exits. If
  828. X.I Q
  829. Xis used, the
  830. X.I .wmrc
  831. Xfile is not updated.
  832. X.IP \fBz\fP
  833. XSuspend
  834. X.I wm
  835. Xusing the Berkeley job control system.
  836. XThis should only be done if
  837. X.I wm
  838. Xitself was run from a shell that handles job control, i.e. from
  839. X.IR csh (1),
  840. Xrather than from the standard
  841. X.IR sh (1).
  842. X.IP \fBL\fP
  843. XList the windows. The level (top first), name, shell's pid, virtual
  844. Xdevice, coordinates, lines, columns and state are displayed.
  845. X.IP \fBM\fP
  846. XDisplay the map.
  847. X.IP "\fBh\fP or \fB?\fP"
  848. XHelp.
  849. XDisplays a summary of
  850. X.I wm
  851. Xcommands.
  852. X.PP
  853. XIt is also possible to send data from one window to another.
  854. XEach window is associated with the file name
  855. X.B Window?
  856. Xin the directory from which
  857. X.I wm
  858. Xwas run
  859. X(where the
  860. X.B ?
  861. Xis replaced by
  862. X.BR a ,
  863. X.BR b ,
  864. X.BR c ,
  865. Xetc. for the particular windows).
  866. XData sent to those file names will appear in the respective
  867. Xwindows, and data read from those file names will be taken from
  868. Xsubsequent keyboard input to those windows.
  869. X(If real files by those names already exist, this feature will
  870. Xbe disabled.)\ 
  871. X.SH FILES
  872. X.IP $HOME/.wmrc 20
  873. Xis used to save the arrangement of your
  874. Xwindows from one run to the next.
  875. X.IP /dev/vt[sc]? 20
  876. Xare the virtual terminals.
  877. X.SH BUGS
  878. XSome programs assume that their output devices are at
  879. Xleast some minimum size;
  880. Xthey will not run well in small windows.
  881. XAlso, programs that attempt to manipulate the controlling
  882. Xterminals of process groups will not work properly under
  883. X.IR wm .
  884. XFor this reason,
  885. X.IR csh (1)
  886. Xcannot be run in the individual windows instead of
  887. X.IR sh (1).
  888. X.PP
  889. XEach window emulates a cursor-control terminal,
  890. Xbut the emulation is provided largely by
  891. X.IR curses ,
  892. Xand it does not always do the same thing that a real terminal
  893. Xwould do or that a program expects.
  894. X.PP
  895. XBecause of a deficiency in the virtual terminal driver in
  896. Xhandling interrupt characters, the
  897. X.I interrupt
  898. Xand
  899. X.I quit
  900. Xkeyboard characters always send their
  901. Xrespective signals to the program in the current window,
  902. Xregardless of whether that program changed the definitions of
  903. Xthe keys or not.
  904. XThus, the program cannot re-map the keys,
  905. Xeven if it has set raw mode,
  906. Xbut it can still catch the signal
  907. Xitself (as is usually done), rather than the character.
  908. X.PP
  909. XPrograms that do a lot of character I/O have make every other
  910. Xwindow run slow.
  911. X.SH "SEE ALSO"
  912. XR.J.K. Jacob,
  913. X"User-Level Window Managers for UNIX,"
  914. X.I "Proc. UniForum International Conference on UNIX"
  915. X(January, 1984).
  916. X.SH AUTHORS
  917. XRobert J.K. Jacob, Naval Research Laboratory
  918. X.br
  919. XChris Bertin and Mike Tellier, Pixel Systems Inc.
  920. ________This_Is_The_END________
  921. echo 'sh - wm.c'
  922. sed 's/^X//' <<'________This_Is_The_END________'             >>wm.c
  923. X/*
  924. X * wm.c  R. Jacob  7/28/1980
  925. X * Simple multiple-window monitor for Unix -- allows windows to overlap
  926. X *
  927. X * This is the code for the main program.
  928. X * This version runs as only one process (plus the shells)
  929. X * This is intended for Berkeley Unix with virtual tty's only.
  930. X *
  931. X * Modified by Chris Bertin and Mike Tellier, October 1985.
  932. X * Major changes:
  933. X *    - Use a byte-mapped screen image to keep track of which portions
  934. X *      of the windows should be displayed. This eliminates the jumping
  935. X *      from one window to another when updating hidden portions.
  936. X *    - Read's from virtual tty's are now buffered. This assumes that
  937. X *      your kernel has 'FIONREAD' fixed for virtual tty's.
  938. X *    - Window structures are now a doubly linked list for quicker scans.
  939. X *    - Improved handling of the message window.
  940. X *    - Reverse video handled in windows.
  941. X *    - Improved user interface (Command are now interruptible by a 'q'
  942. X *      or by typing another command character, for example).
  943. X *    - Lines wrap around inside the windows which means you always get
  944. X *      the full output.
  945. X *    - VI's behaviour improved inside windows.
  946. X *    - The command character can be passed to the window's process by
  947. X *      typing it twice.
  948. X *    - The command character is saved from in the .wmrc file.
  949. X *    - New commands: 'Q', 'L' and 'M'. See #define's below or manual.
  950. X *    - WMRC now an environment variable.
  951. X */
  952. X
  953. X#include <signal.h>
  954. X#include <stdio.h>
  955. X#include <curses.h>
  956. X#include <ctype.h>
  957. X#include "wm.h"
  958. X
  959. X/*
  960. X * Command definitions
  961. X */
  962. X# define NEWWINDOW    'n'    /* make New window */
  963. X# define CHGWINDOW    'c'    /* Change to another window */
  964. X# define LASTWINDOW    'l'    /* change to Last-used window */
  965. X# define PAGE        'p'    /* Page (vs scroll) the window */
  966. X# define MOVEWINDOW    'm'    /* Move locn and/or change size of window */
  967. X# define ERASEWINDOW    'e'    /* but it can reappear */
  968. X# define KILLWINDOW    'k'    /* get rid of this window forever */
  969. X# define REDRAW        'r'    /* Redraw all windows */
  970. X# define HELP1        '?'    /* Command summary */
  971. X# define HELP2        'h'    /* Command summary */
  972. X# define SUSPEND    'z'    /* suspend wm */
  973. X# define QUIT        'q'    /* close up everything and Quit */
  974. X# define EXIT        'Q'    /* QUIT but exit without updating .wmrc */
  975. X# define CHGCODE    'C'    /* change command character */
  976. X# define LIST        'L'    /* list windows */
  977. X# ifdef     SHOWMAP
  978. X# define MAP        'M'    /* display map */
  979. X# endif
  980. X
  981. X# define DEFCMD        '\033'    /* default control character */
  982. X# define INCR        500
  983. X# define NOWIN        (struct win_struct *)0
  984. X# define RDBSIZE    128
  985. X# define pause()    showmsg("Hit any character to return to WM"),bgetch(0);
  986. X# define interrupt(sig)    ioctl(topw->pty, TIOCFLUSH, 0), killpg(topw->pid, sig)
  987. X# define setalarm()    rcount = 1, signal(SIGALRM, gotalarm), alarm(1)
  988. X# define freespace(wsp)    { if (wsp->buf) free(wsp->buf); free(wsp); }
  989. X
  990. Xchar cmdchar = DEFCMD;
  991. X
  992. X/*
  993. X * Real declarations for stuff defined as extern in wm.h
  994. X */
  995. Xstruct win_struct *topw, *askwpnt(), *getwpnt();
  996. X
  997. Xchar *ptslinkname();
  998. XWINDOW *msgwin;
  999. X
  1000. Xchar shellpgm[100], shellname[20], wmrc[100];
  1001. Xstruct sgttyb sgttybuf;
  1002. Xstruct tchars tcharsbuf;
  1003. Xstruct ltchars ltcharsbuf;
  1004. Xint ttymode, ttydisc, timetoquit, nowmrc, rcount, msg;
  1005. X
  1006. Xgotalarm()
  1007. X{ 
  1008. X    signal(SIGALRM, gotalarm); 
  1009. X    rcount = 0;
  1010. X}
  1011. X
  1012. Xmain(argc)
  1013. Xint argc;
  1014. X{
  1015. X    register int alive, atlast, count, x, y;
  1016. X    register struct win_struct *wsp;
  1017. X    register char *pnt;
  1018. X    register WINDOW *wp;
  1019. X    FILE *file;
  1020. X    char intrc, quitc, c, buf[LINEBUF], *cp;
  1021. X    struct sw_buf savewin;
  1022. X
  1023. X    init();
  1024. X#ifdef    CSHWORKS /* for now force user to use sh cause csh doesn't work */
  1025. X    if ((cp = getenv("SHELL")) == (char *)0)
  1026. X        strcpy(shellpgm, "sh");
  1027. X    else
  1028. X        strcpy(shellpgm, cp);
  1029. X    if ((cp = rindex(shellpgm,'/')) == (char *)0)
  1030. X        strcpy(shellname, shellpgm);
  1031. X    else
  1032. X        strcpy(shellname, &cp[1]);
  1033. X#else
  1034. X    strcpy(shellpgm, "sh");
  1035. X    strcpy(shellname, "sh");
  1036. X#endif
  1037. X    ioctl(0, TIOCGETD, &ttydisc);
  1038. X    gtty(0, &sgttybuf);
  1039. X    ioctl(0, TIOCGETC, &tcharsbuf);
  1040. X    ioctl(0, TIOCLGET, &ttymode);
  1041. X    ioctl(0, TIOCGLTC, <charsbuf);
  1042. X    intrc = tcharsbuf.t_intrc;
  1043. X    quitc = tcharsbuf.t_quitc;
  1044. X    initscr();
  1045. X    if ((winmap = (map_t *)malloc(LINES * COLS * sizeof (map_t))) == NULL) {
  1046. X        fprintf(stderr, "Can't allocate memory\n");
  1047. X        endwin();
  1048. X        exit(2);
  1049. X    }
  1050. X    noecho();
  1051. X    raw();
  1052. X    msgwin = newwin(1, 0, LINES - 1, 0);
  1053. X    msgwin->_flags = 0; /* to get around bug in curses */
  1054. X    firstwindow(argc);
  1055. X    strcpy(buf, "Ready");
  1056. X    if (cmdchar != DEFCMD)
  1057. X        sprintf(&buf[strlen(buf)], " (command character is '\\%04o')",
  1058. X            cmdchar);
  1059. X    showmsg(buf);
  1060. X    timetoquit = FALSE;
  1061. X    alive = INCR;
  1062. X    while (!timetoquit) {
  1063. X        for (atlast = 0, wsp = topw; atlast == 0; wsp = wsp->next) {
  1064. X            while (ttychars(0)) {
  1065. X                alive = INCR;
  1066. X                setalarm();
  1067. X                c = bgetch(0);
  1068. X                alarm(0);
  1069. X                if (rcount == 0 || c <= 0)
  1070. X                    break;
  1071. X                if (c == cmdchar && docmd() != cmdchar)
  1072. X                    break;
  1073. X                if (msg-- == 1)
  1074. X                    showmsg("");
  1075. X                /*
  1076. X                 * We fake quit and interrupt here, since just
  1077. X                 * sending the characters on the pty doesn't do.
  1078. X                 * But don't change your intr or quit chars
  1079. X                 * inside a window!
  1080. X                 */
  1081. X                if (c == intrc)
  1082. X                    interrupt(SIGINT);
  1083. X                else if (c == quitc)
  1084. X                    interrupt(SIGQUIT);
  1085. X                else
  1086. X                    write(topw->pty, &c, 1);
  1087. X            }
  1088. X            atlast = (wsp->next == topw);
  1089. X            if ((count = ttychars(wsp->pty)) == 0)
  1090. X                continue;
  1091. X            setalarm();
  1092. X            rcount = read(wsp->pty, wsp->buf, MIN(count, RDBSIZE));
  1093. X            alarm(0);
  1094. X            if (rcount <= 0)
  1095. X                continue;
  1096. X            wsp->flags |= WTOUCHED;
  1097. X            pnt = wsp->buf;
  1098. X            while(rcount-- > 0)
  1099. X                add(wsp, *pnt++);
  1100. X            alive = INCR * 2;
  1101. X            if (wsp != topw)
  1102. X                repaint(wsp);
  1103. X            topw->flags |= WTOUCHED;
  1104. X        }
  1105. X        if (topw->flags & WTOUCHED)
  1106. X            repaint(topw);
  1107. X        getyx(topw->wptr, y, x);
  1108. X        showcursor(y + topw->wptr->_begy, x + topw->wptr->_begx);
  1109. X        if (alive-- < 0)
  1110. X            sleep(1);
  1111. X    }
  1112. X    showmsg("");
  1113. X    showcursor(LINES - 1, 0);
  1114. X    if (!nowmrc && (file = fopen(wmrc, "w")) != NULL) {
  1115. X        for (atlast=0, wsp = topw->prev; atlast == 0; wsp = wsp->prev) {
  1116. X            atlast = (wsp == topw);
  1117. X            wp = wsp->wptr;
  1118. X            savewin.sw_begline = wp->_begy;
  1119. X            savewin.sw_begcol = wp->_begx;
  1120. X            savewin.sw_lines = wp->_maxy;
  1121. X            savewin.sw_cols = wp->_maxx;
  1122. X            savewin.sw_cmdchar = cmdchar;
  1123. X            fwrite(&savewin, sizeof(savewin), 1, file);
  1124. X        }
  1125. X    }
  1126. X    for (atlast = 0, wsp = topw; atlast == 0; wsp = wsp->next) {
  1127. X        atlast = (wsp->next == topw);
  1128. X        close(wsp->pty);
  1129. X        if (wsp->flags & WPTSLINK)
  1130. X            unlink(ptslinkname(wsp->id));
  1131. X        if (wsp->pid != 0) {
  1132. X            killpg(wsp->pid, SIGHUP);
  1133. X            kill(wsp->pid, SIGTERM);
  1134. X        }
  1135. X    }
  1136. X    closeup();
  1137. X    endwin();
  1138. X}
  1139. X
  1140. X/*
  1141. X * docmd() handles commands and returns the character it reads so that
  1142. X * the control character can be passed on to the windows.
  1143. X */
  1144. Xdocmd()
  1145. X{
  1146. X    register struct win_struct *wsp;
  1147. X    register char c;
  1148. X    char buf[LINEBUF];
  1149. X    int begline, begcol, lines, cols, savey, savex, y, x, x1, y1, x2, y2;
  1150. X    register l, lmax;
  1151. X
  1152. X    showmsg("Command?");
  1153. X    c = bgetch(0);
  1154. X    showmsg("");
  1155. X    switch(c) {
  1156. X
  1157. X    case NEWWINDOW:
  1158. X        showmsg("Move to lower left, then type x");
  1159. X        if (getpos(&y1, &x1, LINES-2, 0))
  1160. X            break;
  1161. X        showmsg("Move to upper right, then type x");
  1162. X        if (getpos(&y2, &x2, y1, x1))
  1163. X            break;
  1164. X        begline = y2;
  1165. X        begcol = x1;
  1166. X        lines = y1 - y2 + 1;
  1167. X        cols = x2 - x1 + 1;
  1168. X        if (lines > 0 && cols > 0) {
  1169. X            if (makewin(lines, cols, begline, begcol)) {
  1170. X                repaintall();
  1171. X                showmsg("Created new window");
  1172. X            }
  1173. X            else
  1174. X                showmsg("Can't create another shell");
  1175. X        }
  1176. X        else
  1177. X            showmsg("That's an impossible window size");
  1178. X        break;
  1179. X
  1180. X    case CHGWINDOW:
  1181. X        if ((wsp = askwpnt("Change to", 0)) == NOWIN || wsp == topw)
  1182. X            break;
  1183. X        chgwin(wsp);
  1184. X        repaintall();
  1185. X        showmsg("Changing windows");
  1186. X        break;
  1187. X
  1188. X    case LASTWINDOW:
  1189. X        if ((wsp = topw->next) == topw)
  1190. X            showmsg("No last window");
  1191. X        else {
  1192. X            chgwin(wsp);
  1193. X            repaintall();
  1194. X            showmsg("Changing windows");
  1195. X        }
  1196. X        break;
  1197. X
  1198. X    case PAGE:
  1199. X        showmsg("Page");
  1200. X        getyx(topw->wptr, savey, savex);
  1201. X        lmax = savey;
  1202. X        wmove(topw->wptr, 0, 0);
  1203. X        for (l = 0; l < lmax; l++)
  1204. X            wdeleteln(topw->wptr);
  1205. X        wmove(topw->wptr, savey - lmax, savex);
  1206. X        getyx(topw->virt, savey, savex);
  1207. X        wmove(topw->virt, 0, 0);
  1208. X        for (l = 0; l < lmax; l++)
  1209. X            wdeleteln(topw->virt);
  1210. X        wmove(topw->virt, savey - lmax, savex);
  1211. X        topw->flags |= WTOUCHED;
  1212. X        repaint(topw);
  1213. X        break;
  1214. X
  1215. X    case MOVEWINDOW:
  1216. X        if ((wsp = askwpnt("Move", 1)) == NOWIN)
  1217. X            break;
  1218. X        showmsg("Move to new lower left, then type x");
  1219. X        if (getpos(&y1, &x1, LINES-2, 0))
  1220. X            break;
  1221. X        showmsg("Move to new upper right, then type x");
  1222. X        if (getpos(&y2, &x2, y1, x1))
  1223. X            break;
  1224. X        begline = y2;
  1225. X        begcol = x1;
  1226. X        lines = y1 - y2 + 1;
  1227. X        cols = x2 - x1 + 1;
  1228. X        if (lines > 0 && cols > 0) {
  1229. X            int haslink = wsp->flags & WPTSLINK;
  1230. X            getyx(wsp->wptr, savey, savex);
  1231. X            savey = wsp->wptr->_maxy - savey;
  1232. X            delwin(wsp->wptr);
  1233. X            if (wsp->boxbot != 0)
  1234. X                delwin(wsp->boxbot);
  1235. X            if (wsp->boxtop != 0)
  1236. X                delwin(wsp->boxtop);
  1237. X            if (wsp->boxleft != 0)
  1238. X                delwin(wsp->boxleft);
  1239. X            if (wsp->boxright != 0)
  1240. X                delwin(wsp->boxright);
  1241. X            wsp->wptr = newwin(lines, cols, begline, begcol);
  1242. X            wsp->wptr->_flags = 0; /* get around curses bug */
  1243. X            for (y = 0; y < wsp->wptr->_maxy; y++) {
  1244. X                for (x = 0; x < wsp->wptr->_maxx; x++){
  1245. X                    wmove(wsp->wptr, y, x);
  1246. X                    wmove(wsp->virt, y + wsp->virt->_maxy - wsp->wptr->_maxy, x);
  1247. X                    waddch(wsp->wptr, winch(wsp->virt));
  1248. X                }
  1249. X            }
  1250. X            if (haslink)
  1251. X                wsp->flags |= WPTSLINK;
  1252. X            wmove(wsp->virt, MAX(wsp->virt->_maxy - savey, 0),
  1253. X                savex);
  1254. X            wmove(wsp->wptr, MAX(wsp->wptr->_maxy - savey, 0),
  1255. X                savex);
  1256. X            ttyclear();
  1257. X            setupbox(wsp);
  1258. X            setenv(wsp);
  1259. X            makemap();
  1260. X            repaintall();
  1261. X            showmsg("Moving the window");
  1262. X        }
  1263. X        else
  1264. X            showmsg("That's an impossible window size");
  1265. X        break;
  1266. X
  1267. X    case ERASEWINDOW:
  1268. X        if ((wsp = askwpnt("Erase", 0)) == NOWIN)
  1269. X            break;
  1270. X        if (wsp == topw)
  1271. X            showmsg("Can't erase the top window");
  1272. X        else {
  1273. X            wsp->flags |= WERASED;
  1274. X            ttyclear();
  1275. X            repaintall();
  1276. X            showmsg("Erasing");
  1277. X        }
  1278. X        break;
  1279. X
  1280. X    case KILLWINDOW:
  1281. X        if ((wsp = askwpnt("Kill", 0)) == NOWIN)
  1282. X            break;
  1283. X        if (wsp == topw)
  1284. X            showmsg("Can't kill the top window");
  1285. X        else {
  1286. X            delwin(wsp->wptr);
  1287. X            delwin(wsp->virt);
  1288. X            if (wsp->boxbot != 0)
  1289. X                delwin(wsp->boxbot);
  1290. X            if (wsp->boxtop != 0)
  1291. X                delwin(wsp->boxtop);
  1292. X            if (wsp->boxleft != 0)
  1293. X                delwin(wsp->boxleft);
  1294. X            if (wsp->boxright != 0)
  1295. X                delwin(wsp->boxright);
  1296. X            if (wsp->flags & WPTSLINK)
  1297. X                unlink(ptslinkname(wsp->id));
  1298. X            close(wsp->pty);
  1299. X            if (wsp->pid != 0) {
  1300. X                killpg(wsp->pid, SIGHUP);
  1301. X                kill(wsp->pid, SIGKILL);
  1302. X            }
  1303. X            wsp->prev->next = wsp->next;
  1304. X            wsp->next->prev = wsp->prev;
  1305. X            freespace(wsp);
  1306. X            makemap();
  1307. X            ttyclear();
  1308. X            repaintall();
  1309. X            showmsg("Killing the window");
  1310. X        }
  1311. X        break;
  1312. X
  1313. X    case REDRAW:
  1314. X        repaintall();
  1315. X        showmsg("Redrawing");
  1316. X        break;
  1317. X
  1318. X    case LIST:
  1319. X        ttyclear();
  1320. X        refresh();
  1321. X        showwindows();
  1322. X        pause();
  1323. X        repaintall();
  1324. X        break;
  1325. X
  1326. X    case HELP1:
  1327. X    case HELP2:
  1328. X        ttyclear();
  1329. X        refresh();
  1330. X        helpmsg();
  1331. X        pause();
  1332. X        repaintall();
  1333. X        break;
  1334. X
  1335. X#ifdef    SHOWMAP
  1336. X    case MAP:
  1337. X        ttyclear();
  1338. X        for (x = 0; x < COLS; x++)
  1339. X        for (y = 0; y < LINES; y++)
  1340. X            if (winmap[y * COLS + x])
  1341. X            mvwaddch(stdscr, y, x, mapchar(winmap[y * COLS + x]));
  1342. X        refresh();
  1343. X        pause();
  1344. X        repaintall();
  1345. X        break;
  1346. X#endif
  1347. X
  1348. X    case CHGCODE: /* change cmdchar */
  1349. X        showmsg("Enter new command character");
  1350. X        cmdchar = bgetch(0);
  1351. X        sprintf(buf, "New command character is '\\%04o'", cmdchar);
  1352. X        showmsg(buf);
  1353. X        break;
  1354. X
  1355. X#ifdef    SIGTSTP
  1356. X    case SUSPEND: /* Berkeley suspend job */
  1357. X        showmsg("Suspending");
  1358. X        kill(0, SIGTSTP);
  1359. X        showmsg("Resuming"); /* resume here */
  1360. X        break;
  1361. X#endif
  1362. X
  1363. X    case EXIT:
  1364. X        nowmrc++;
  1365. X    case QUIT: /* quit */
  1366. X        timetoquit = TRUE;
  1367. X        break;
  1368. X    default:
  1369. X        if (c != cmdchar) /* bad command */
  1370. X            showmsg("No such command -- use 'h' command for help");
  1371. X    }
  1372. X    return(c);
  1373. X}
  1374. X
  1375. X/*
  1376. X * Read in the file in $HOME/.wmrc if possible
  1377. X * Otherwise prompt for the first window
  1378. X */
  1379. Xfirstwindow(argc)
  1380. Xint argc;
  1381. X{
  1382. X    struct sw_buf savewin;
  1383. X    FILE *file;
  1384. X    int okflg;
  1385. X    int x1, y1, x2, y2;
  1386. X    int begline, begcol, cols, lines;
  1387. X    char *cp;
  1388. X
  1389. X    if ((cp = getenv("WMRC")) != (char *)0)
  1390. X        strcpy(wmrc, cp);
  1391. X    else {
  1392. X        if ((cp = getenv("HOME")) == (char *)0)
  1393. X            cp = ".";
  1394. X        sprintf(wmrc, "%s/.wmrc", cp);
  1395. X    }
  1396. X    if ((file = fopen(wmrc, "r")) != NULL && argc == 1) {
  1397. X        int reads = 0;
  1398. X        okflg = FALSE;
  1399. X        while (fread(&savewin, sizeof(savewin), 1, file) == 1) {
  1400. X            reads++;
  1401. X            if ((cmdchar = savewin.sw_cmdchar) == 0)
  1402. X                cmdchar = DEFCMD;
  1403. X            if (makewin(savewin.sw_lines, savewin.sw_cols,
  1404. X               savewin.sw_begline, savewin.sw_begcol))
  1405. X                okflg = TRUE;
  1406. X            else {
  1407. X                if (! okflg) {
  1408. X                    showmsg("Can't create any shells--sorry");
  1409. X                    closeup();
  1410. X                    endwin();
  1411. X                    exit(1);
  1412. X                }
  1413. X            }
  1414. X        }
  1415. X        fclose(file);
  1416. X        if (! reads) {
  1417. X            unlink(wmrc);
  1418. X            return(firstwindow(0));
  1419. X        }
  1420. X        repaint(topw);
  1421. X    }
  1422. X    else {
  1423. X        showmsg("Please create the first window: Move to lower left, then type x");
  1424. X        okflg = FALSE;
  1425. X        while (!okflg) {
  1426. X            if (getpos(&y1, &x1, LINES-2, 0))
  1427. X                closeup(), endwin(), exit(1);
  1428. X            showmsg("Move to upper right, then type x");
  1429. X            if (getpos(&y2, &x2, y1, x1))
  1430. X                closeup(), endwin(), exit(1);
  1431. X            begline = y2;
  1432. X            begcol = x1;
  1433. X            lines = y1 - y2 + 1;
  1434. X            cols = x2 - x1 + 1;
  1435. X            if (lines > 0 && cols > 0) {
  1436. X                if (makewin(lines, cols, begline, begcol)){
  1437. X                    okflg = TRUE;
  1438. X                    repaint(topw);
  1439. X                    showmsg("Created new window");
  1440. X                }
  1441. X                else {
  1442. X                    showmsg("Can't create any shells--sorry");
  1443. X                    closeup();
  1444. X                    endwin();
  1445. X                    exit(1);
  1446. X                }
  1447. X            }
  1448. X            else
  1449. X                showmsg("Illegal window size: Move to lower left, then type x");
  1450. X        }
  1451. X    }
  1452. X}
  1453. X
  1454. X/*
  1455. X * Set up win structure and associated junk for new window
  1456. X * Return TRUE if it worked (ie, the pty and fork in shell were ok)
  1457. X * else FALSE
  1458. X */
  1459. Xmakewin(lines, cols, begline, begcol)
  1460. Xint lines, cols, begline, begcol;
  1461. X{
  1462. X    register struct win_struct *wsp;
  1463. X
  1464. X    if ((wsp = malloc(sizeof (struct win_struct))) == NOWIN)
  1465. X        return(FALSE);
  1466. X    wsp->next = wsp->prev = 0;
  1467. X    wsp->virt = newwin(0, 0, 0, 0);
  1468. X    wsp->virt->_flags = 0; /* get around curses bug */
  1469. X    werase(wsp->virt);
  1470. X    wsp->wptr = newwin(lines, cols, begline, begcol);
  1471. X    wsp->wptr->_flags = 0; /* get around curses bug */
  1472. X    if ((wsp->buf = (char *)malloc(RDBSIZE)) == 0 || shell(wsp) == FALSE) {
  1473. X        werase(wsp->virt);
  1474. X        freespace(wsp);
  1475. X        return(FALSE);
  1476. X    }
  1477. X    wmove(wsp->virt, wsp->virt->_maxy - wsp->wptr->_maxy, 0);
  1478. X    setupbox(wsp);
  1479. X    chgwin(wsp);
  1480. X    return(TRUE);
  1481. X}
  1482. X
  1483. X/*
  1484. X * spawn shell process for window wsp
  1485. X * Finds first available pty and its matching pts and opens them
  1486. X * Returns TRUE if it found you some pty's else FALSE
  1487. X */
  1488. Xshell(wsp)
  1489. Xregister struct win_struct *wsp;
  1490. X{
  1491. X    char ptyname[100], ptsname[100], pchar;
  1492. X    int fpty, fpts;
  1493. X
  1494. X    for (pchar = PTFIRST; pchar <= PTLAST; pchar++) {
  1495. X        sprintf(ptyname, PTYFORMAT, PTYBASENAME, pchar);
  1496. X        fpty = open(ptyname, 2);
  1497. X        if (fpty >= 0) {
  1498. X            sprintf(ptsname, PTYFORMAT, PTSBASENAME, pchar);
  1499. X            fpts = open(ptsname, 2);
  1500. X            if (fpts >= 0)
  1501. X                break;
  1502. X            close(fpty);
  1503. X        }
  1504. X    }
  1505. X    if (pchar > PTLAST)
  1506. X        return(FALSE);
  1507. X    wsp->suffix = pchar;
  1508. X    for (pchar = 'a'; pchar <= 'z'; ++pchar)
  1509. X        if (getwpnt(pchar) == NOWIN)
  1510. X            break;
  1511. X    wsp->id = pchar;
  1512. X    ioctl(fpty, TIOCSETD, &ttydisc);
  1513. X    stty(fpty, &sgttybuf);
  1514. X    ioctl(fpty, TIOCSETC, &tcharsbuf);
  1515. X    ioctl(fpty, TIOCLSET, &ttymode);
  1516. X    ioctl(fpty, TIOCSLTC, <charsbuf);
  1517. X    switch(wsp->pid = fork()) {
  1518. X    case 0: /* child */
  1519. X        close(fpty);
  1520. X        dup2(fpts, 0);
  1521. X        dup2(fpts, 1);
  1522. X        dup2(fpts, 2);
  1523. X        close(fpts);
  1524. X        setpgrp(0, getpid());/* set me up in my own new process group */
  1525. X        execlp(shellpgm, shellname, 0);
  1526. X        exit(1);
  1527. X    default:
  1528. X        close(fpts);
  1529. X        wsp->pty = fpty;
  1530. X        wsp->flags = 0;
  1531. X        if (symlink(ptsname, ptslinkname(wsp->id)) == 0)
  1532. X            wsp->flags = WPTSLINK;
  1533. X        setenv(wsp);
  1534. X        return(TRUE);
  1535. X    case -1: /* error */
  1536. X        close(fpty);
  1537. X        close(fpts);
  1538. X        return(FALSE);
  1539. X    }
  1540. X}
  1541. X
  1542. X#define TERMCAP "wm|wmvirt:am:bs:ce=\\EK:cl=\\ES:nd=\\EC:up=\\EA:cm=\\EY%+ %+ :"
  1543. X#ifdef    REVVIDEO
  1544. X# define REV "so=\\EO:se=\\EE:"
  1545. X#else
  1546. X# define REV ""
  1547. X#endif
  1548. X
  1549. X/*
  1550. X * send a setenv command for wmvirt terminal to shell in window wsp
  1551. X */
  1552. Xsetenv(wsp)
  1553. Xregister struct win_struct *wsp;
  1554. X{
  1555. X    char buf[LINEBUF];
  1556. X
  1557. X    if (strcmp(shellname, "csh") == 0)
  1558. X        sprintf(buf, "setenv TERM wmvirt;setenv TERMCAP '%sco#%d:li#%d:%s'\n",
  1559. X        TERMCAP, wsp->wptr->_maxx, wsp->wptr->_maxy, REV);
  1560. X    else
  1561. X        sprintf(buf, "TERM=wmvirt; TERMCAP='%sco#%d:li#%d:%s';export TERM TERMCAP\n",
  1562. X        TERMCAP, wsp->wptr->_maxx, wsp->wptr->_maxy, REV);
  1563. X    write(wsp->pty, buf, strlen(buf));
  1564. X}
  1565. X
  1566. Xchar *
  1567. Xptslinkname(c)
  1568. Xchar c;
  1569. X{
  1570. X    static char lnk[100];
  1571. X
  1572. X    return(sprintf(lnk, "Window%c", c));
  1573. X}
  1574. X
  1575. Xstruct win_struct *
  1576. Xaskwpnt(str, oneok)
  1577. Xchar *str;
  1578. X{
  1579. X    struct win_struct *wsp;
  1580. X    char buf[40], c;
  1581. X
  1582. X    if (topw->next == topw) {
  1583. X        if (oneok)
  1584. X            return(topw);
  1585. X        showmsg("Only 1 window");
  1586. X        return(NOWIN);
  1587. X    }
  1588. X    showmsg(sprintf(buf, "%s which window?", str));
  1589. X    if (isupper(c = bgetch(0)))
  1590. X        c = tolower(c);
  1591. X    if ((wsp = getwpnt(c)) == NOWIN)
  1592. X        showmsg("No such window");
  1593. X    return(wsp);
  1594. X}
  1595. X
  1596. X/*
  1597. X * translate window name to window pointer
  1598. X */
  1599. Xstruct win_struct *
  1600. Xgetwpnt(c)
  1601. Xchar c;
  1602. X{
  1603. X    register struct win_struct *wsp;
  1604. X    int atlast;
  1605. X
  1606. X    for (atlast = 0, wsp = topw; topw && atlast == 0; wsp = wsp->next) {
  1607. X        atlast = (wsp->next == topw);
  1608. X        if (wsp->id == c)
  1609. X            return(wsp);
  1610. X    }
  1611. X    return(NOWIN);
  1612. X}
  1613. X
  1614. X/*
  1615. X * print a command summary over the top of the screen, then redraw
  1616. X */
  1617. Xhelpmsg()
  1618. X{
  1619. Xprintf("All WM commands consist of COMMAND character plus one more character.\r\n");
  1620. Xprintf("The command character is initially ESCAPE, unless you reset it.\r\n");
  1621. Xprintf("To enter the command character itself, type it twice.\r\n\n");
  1622. Xprintf("The WM commands are COMMAND character plus:\r\n");
  1623. Xprintf("q or Q     Quit WM. 'Q' doesn't update .wmrc\r\n");
  1624. Xprintf("r          Redraw entire screen\r\n");
  1625. Xprintf("c          Change to another window\r\n");
  1626. Xprintf("l          change to Last-used window\r\n");
  1627. Xprintf("n          make a New window\r\n");
  1628. Xprintf("m          Move and/or change size of window\r\n");
  1629. Xprintf("e          Erase window temporarily\r\n");
  1630. Xprintf("k          Kill window permanently\r\n");
  1631. Xprintf("p          Page window forward (as opposed to standard scrolling)\r\n");
  1632. X#ifdef    SIGTSTP
  1633. Xprintf("z          suspend wm (Do this ONLY if you are using CSH)\r\n");
  1634. X#endif
  1635. Xprintf("C          change COMMAND character\r\n");
  1636. Xprintf("L          list windows\r\n");
  1637. X#ifdef    SHOWMAP
  1638. Xprintf("M          display map\r\n");
  1639. X#endif
  1640. Xprintf("h or ?     print this command summary\r\n\n");
  1641. Xprintf("If you are asked 'which window?' the window names are a, b, c, etc...\r\n");
  1642. Xprintf("The name of each window is shown at the top of the window.\r\n");
  1643. Xprintf("To move cursor, use 'u', 'd', 'r' and 'l' for 'up, 'down', 'right' and 'left'\r\n");
  1644. X}
  1645. X
  1646. Xshowwindows()
  1647. X{
  1648. X    register struct win_struct *wsp;
  1649. X    register WINDOW *wp;
  1650. X    int atlast, n;
  1651. X
  1652. X    printf("\n\n\n\r");
  1653. X    printf(" # ID  PID      Device     Coord.(X/Y)  Lines  Cols  State\n\r");
  1654. X    printf("-- -- -----  ------------  -----------  -----  ----  -----\n\r");
  1655. X    for (n = 1, atlast = 0, wsp = topw; atlast == 0; wsp = wsp->next, n++) {
  1656. X        atlast = (wsp->next == topw);
  1657. X        wp = wsp->wptr;
  1658. X#ifdef    NUMSUFFIX
  1659. X        printf("%2d  %c %5d  %11.11s%-2d %5d/%-5d  %4d  %4d   %s%s%s\r\n",
  1660. X#else
  1661. X        printf("%2d  %c %5d  %11.11s%c  %5d/%-5d  %4d  %4d   %s%s%s\r\n",
  1662. X#endif
  1663. X            n, wsp->id, wsp->pid, PTSBASENAME, wsp->suffix,
  1664. X            wp->_begx, wp->_begy, wp->_maxy, wp->_maxx,
  1665. X            wsp->flags & WERASED? "Erased": "Live",
  1666. X            wsp->flags & WFULLSCREEN? "/Full-screen": "",
  1667. X            wsp->flags & WPTSLINK? "/Linked": "");
  1668. X    }
  1669. X}
  1670. ________This_Is_The_END________
  1671. echo 'sh - wm.h'
  1672. sed 's/^X//' <<'________This_Is_The_END________'             >>wm.h
  1673. X/*
  1674. X * definitions for wm
  1675. X */
  1676. X
  1677. X#include <curses.h>
  1678. X#define MENLO_JCL    /* for 2.9 - get SIGTSTP from signal.h */
  1679. X
  1680. X/*
  1681. X * The number of windows for each user is limited by the number of
  1682. X * open files a process (i.e., main) may have, by the number of
  1683. X * processes each user may create and by the number of virtual tty's
  1684. X * available. (PTLAST - PTFIRST) sets a limit on the number of
  1685. X * windows available to each 'wm' process.
  1686. X *
  1687. X *
  1688. X * PTYBASENAME is the special file name of the controlling PTY's
  1689. X * available to wm, sans the last character
  1690. X * PTSBASENAME is the special file name of the slave PTY's,
  1691. X * sans last character
  1692. X * PTFIRST through PTLAST are all the final characters
  1693. X * of the special file names. If the names have a numeric suffix,
  1694. X * define NUMSUFFIX
  1695. X *
  1696. X * Likely names on 4.1bsd would be something like
  1697. X * /dev/ptypA ... for controlling and
  1698. X * /dev/ttypA ... for slave
  1699. X *
  1700. X * These PTY's should be separate from those used by networks,
  1701. X * since these should not automatically run login
  1702. X * (i.e., they should not be enabled in /etc/ttys)
  1703. X */
  1704. X
  1705. X# define PTYBASENAME    "/dev/wm/pty"        /* must be string */
  1706. X# define PTSBASENAME    "/dev/wm/pts"        /* must be string */
  1707. X#ifdef    NUMSUFFIX
  1708. X# define PTFIRST    0            /* must be int */
  1709. X# define PTLAST        8            /* must be int */
  1710. X# define PTYFORMAT    "%s%d"
  1711. X#else
  1712. X# define PTFIRST    'A'            /* must be char */
  1713. X# define PTLAST        'F'            /* must be char */
  1714. X# define PTYFORMAT    "%s%c"
  1715. X#endif
  1716. X
  1717. X# define LINEBUF    256    /* size of line buffers */
  1718. X# define MIN(a,b)    (a<b?a:b)
  1719. X# define MAX(a,b)    (a>b?a:b)
  1720. X# define showcursor(y, x)    move(y, x), refresh()
  1721. X# define ttyclear()        clear(), refresh()
  1722. X
  1723. X/*
  1724. X * flags
  1725. X */
  1726. X# define WTOUCHED    0x01    /* The window is modified */
  1727. X# define WERASED    0x02    /* The window is erased */
  1728. X# define WFULLSCREEN    0x04    /* The window is a full screen window */
  1729. X# define WPTSLINK    0x08    /* Slave pty was linked */
  1730. X
  1731. X/*
  1732. X * Global data with miscellaneous information about each window.
  1733. X */
  1734. Xstruct win_struct {
  1735. X    struct win_struct *next;    /* pointer to next window */
  1736. X    struct win_struct *prev;    /* pointer to prev window */
  1737. X    WINDOW *wptr;        /* ptr to small curses win to be displayed */
  1738. X    WINDOW *virt;        /* ptr to large curses win in case chg size */
  1739. X    WINDOW *boxtop, *boxbot;    /* ptrs to curses wins for box or 0 */
  1740. X    WINDOW *boxleft, *boxright;    /* ptrs to curses wins for box or 0 */
  1741. X    char pend[4];        /* pending characters from recent add()s */
  1742. X    char *buf;            /* read buffer */
  1743. X    int pid;            /* pid of this wondow's shell */
  1744. X    int pty;            /* fildes of pty to/from this win's shell */
  1745. X    char id;            /* name of the window */
  1746. X    char suffix;        /* last character of the virtual tty's name */
  1747. X    char flags;            /* global flags */
  1748. X};
  1749. X
  1750. Xstruct win_struct *malloc();
  1751. Xextern struct win_struct *botw, *topw;
  1752. X
  1753. X/*
  1754. X * If FAST is defined, the window copy routine will use routines that are
  1755. X * not necessarily portable.
  1756. X */
  1757. X#ifdef    FAST
  1758. X    typedef short        map_t;
  1759. X#   define mapchar(c)        ((c) & 0xff)
  1760. X#   define chartomap(c, n)    ((map_t)(c) | ((n) << 8))
  1761. X#   define maptocount(c)    ((c) >> 8)
  1762. X# else
  1763. X    typedef char        map_t;
  1764. X#   define mapchar(c)        (c)
  1765. X#endif
  1766. X
  1767. Xextern map_t *winmap;
  1768. X#define visible(w, x, y) (winmap[(y)*COLS+(x)] == mapchar((w)->id))
  1769. X
  1770. X/*
  1771. X * This is what goes in .wmrc to recreate same layout.
  1772. X */
  1773. Xstruct sw_buf {
  1774. X    int sw_begline;
  1775. X    int sw_begcol;
  1776. X    int sw_lines;
  1777. X    int sw_cols;
  1778. X    char sw_cmdchar;
  1779. X};
  1780. X
  1781. X/* command char for virtual terminal special functions. Check TERMCAP in wm.c */
  1782. X#define CODE '\033'
  1783. X
  1784. X/*
  1785. X * Forward references
  1786. X */
  1787. Xchar *getenv();
  1788. XFILE *fopen();
  1789. Xint onsusp();
  1790. Xchar *sprintf(), *strcpy(), *strcat(), *rindex();
  1791. Xchar windowname(); int windownum();
  1792. ________This_Is_The_END________
  1793. ---------------------------- cut here -------------------------------
  1794.  
  1795.  Chris Bertin            :         (617) 933-7735 x2336
  1796.  Pixel Systems Inc.      :       (800) 325-3342
  1797.  300 Wildwood street     :  {allegra|ihnp4|cbosgd|ima|genrad|amd|harvard}\
  1798.  Woburn, Ma 01801        :     !wjh12!pixel!pixutl!chris
  1799.  
  1800.  
  1801.  
  1802.  
  1803.