home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume8 / wscrawl / part03 < prev    next >
Internet Message Format  |  1990-07-15  |  38KB

  1. Path: uunet!cs.utexas.edu!samsung!munnari.oz.au!metro!sunaus.oz!newstop!sun!hpcvlx.cv.hp.com
  2. From: brianw@hpcvlx.cv.hp.com (Brian Wilson)
  3. Newsgroups: comp.sources.x
  4. Subject: v08i055: wscrawl, Part03/05
  5. Message-ID: <138940@sun.Eng.Sun.COM>
  6. Date: 15 Jul 90 18:57:24 GMT
  7. Sender: news@sun.Eng.Sun.COM
  8. Lines: 1047
  9. Approved: argv@sun.com
  10.  
  11. Submitted-by: Brian Wilson <brianw@hpcvlx.cv.hp.com>
  12. Posting-number: Volume 8, Issue 55
  13. Archive-name: wscrawl/part03
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then feed it
  17. # into a shell via "sh file" or similar.  To overwrite existing files,
  18. # type "sh file -c".
  19. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  20. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  21. # If this archive is complete, you will see the following message at the end:
  22. #        "End of archive 3 (of 5)."
  23. # Contents:  wscrawl/xad
  24. # Wrapped by argv@turnpike on Sun Jul 15 11:47:11 1990
  25. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  26. if test -f 'wscrawl/xad' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'wscrawl/xad'\"
  28. else
  29. echo shar: Extracting \"'wscrawl/xad'\" \(35313 characters\)
  30. sed "s/^X//" >'wscrawl/xad' <<'END_OF_FILE'
  31. X          disp_info[input_disp].prompt_width, 
  32. X          disp_info[input_disp].cur_pos.y);
  33. X
  34. X            push_letter_to_history(input_disp, the_char[0]);
  35. X
  36. X                disp_info[input_disp].cur_pos.x = disp_info[input_disp].orig_x;
  37. X                disp_info[input_disp].cur_pos.y += 
  38. X                         disp_info[input_disp].char_height;
  39. X
  40. X            /*draw the cursor*/
  41. X            disp_info[input_disp].cursor_on = TRUE;
  42. X            XDrawLine(disp_info[input_disp].disp, 
  43. X          disp_info[input_disp].win_id,
  44. X          disp_info[input_disp].cursor_gc, 
  45. X          disp_info[input_disp].cur_pos.x, 
  46. X          disp_info[input_disp].cur_pos.y, 
  47. X          disp_info[input_disp].cur_pos.x + 
  48. X          disp_info[input_disp].prompt_width, 
  49. X          disp_info[input_disp].cur_pos.y);
  50. X            }
  51. X            else if ((the_char[0] == 8) ||     /*8 == Backspace*/
  52. X                     (the_char[0] == 127))     /*127 == Del or Rubout*/
  53. X            {
  54. X            /*erase the cursor*/
  55. X            disp_info[input_disp].cursor_on = FALSE;
  56. X            XDrawLine(disp_info[input_disp].disp, 
  57. X          disp_info[input_disp].win_id,
  58. X          disp_info[input_disp].cursor_gc, 
  59. X          disp_info[input_disp].cur_pos.x, 
  60. X          disp_info[input_disp].cur_pos.y, 
  61. X          disp_info[input_disp].cur_pos.x + 
  62. X          disp_info[input_disp].prompt_width, 
  63. X          disp_info[input_disp].cur_pos.y);
  64. X        backspace_letter_from_history(input_disp);
  65. X
  66. X            /*draw the cursor*/
  67. X            disp_info[input_disp].cursor_on = TRUE;
  68. X            XDrawLine(disp_info[input_disp].disp, 
  69. X          disp_info[input_disp].win_id,
  70. X          disp_info[input_disp].cursor_gc, 
  71. X          disp_info[input_disp].cur_pos.x, 
  72. X          disp_info[input_disp].cur_pos.y, 
  73. X          disp_info[input_disp].cur_pos.x + 
  74. X          disp_info[input_disp].prompt_width, 
  75. X          disp_info[input_disp].cur_pos.y);
  76. X            }
  77. X            else     /*it is a valid letter*/
  78. X            {
  79. X        push_letter_to_history(input_disp, the_char[0]);
  80. X
  81. X            /*erase the cursor*/
  82. X            disp_info[input_disp].cursor_on = FALSE;
  83. X            XDrawLine(disp_info[input_disp].disp, 
  84. X          disp_info[input_disp].win_id,
  85. X          disp_info[input_disp].cursor_gc, 
  86. X          disp_info[input_disp].cur_pos.x, 
  87. X          disp_info[input_disp].cur_pos.y, 
  88. X          disp_info[input_disp].cur_pos.x + 
  89. X          disp_info[input_disp].prompt_width, 
  90. X          disp_info[input_disp].cur_pos.y);
  91. X
  92. X                for (i=0; i< num_of_disps; i++)
  93. X                {
  94. X                    if (disp_info[i].in_session_bool) /*if window is alive*/
  95. X                    {
  96. X                    XDrawString(disp_info[i].disp, disp_info[i].win_id,
  97. X                    disp_info[input_disp].win_gc[i], 
  98. X                disp_info[input_disp].cur_pos.x, 
  99. X                disp_info[input_disp].cur_pos.y, the_char, 1);
  100. X                        XFlush(disp_info[i].disp);
  101. X                    }
  102. X                }
  103. X        disp_info[input_disp].cur_pos.x += 
  104. X                     XTextWidth(disp_info[input_disp].the_font_struct, 
  105. X                     the_char, 1);
  106. X
  107. X            /*draw the cursor*/
  108. X            disp_info[input_disp].cursor_on = TRUE;
  109. X            XDrawLine(disp_info[input_disp].disp, 
  110. X          disp_info[input_disp].win_id,
  111. X          disp_info[input_disp].cursor_gc, 
  112. X          disp_info[input_disp].cur_pos.x, 
  113. X          disp_info[input_disp].cur_pos.y, 
  114. X          disp_info[input_disp].cur_pos.x + 
  115. X          disp_info[input_disp].prompt_width, 
  116. X          disp_info[input_disp].cur_pos.y);
  117. X            }
  118. X    }
  119. X    }
  120. X}
  121. X
  122. X
  123. X/*
  124. X * push_letter_to_history - this function takes a pointer to the top of
  125. X *              the history stack and adds the character passed in to that
  126. X *              stack.  This is to save the characters for future backspacing.
  127. X */
  128. Xpush_letter_to_history(input_disp, the_char)
  129. Xint input_disp;
  130. Xchar the_char;
  131. X{
  132. X    struct char_node *temp;
  133. X
  134. X    temp = (struct char_node *) malloc(sizeof(struct char_node));
  135. X    temp->the_char = the_char;
  136. X    temp->next = disp_info[input_disp].type_history;
  137. X
  138. X    if (the_char == 13)   /*carriage return*/
  139. X    {
  140. X    temp->x = disp_info[input_disp].cur_pos.x;
  141. X    temp->y = disp_info[input_disp].cur_pos.y;
  142. X    }
  143. X
  144. X    disp_info[input_disp].type_history = temp;
  145. X}
  146. X
  147. X
  148. X/*
  149. X * backspace_letter_from_history - this function returns the most recently 
  150. X *                 typed character from the history stack, and sets the top of
  151. X *                 the stack pointer appropriately.
  152. X */
  153. Xbackspace_letter_from_history(input_disp)
  154. Xint input_disp;
  155. X{
  156. X    int dr, far, fdr, i;
  157. X    struct char_node *char_node;
  158. X    XCharStruct the_fnt_strct;
  159. X
  160. X    if (disp_info[input_disp].type_history == NULL)
  161. X    return(0);
  162. X    else
  163. X    {
  164. X        char_node = disp_info[input_disp].type_history;
  165. X
  166. X        if (char_node->the_char == 13)     /*it was a carriage return*/
  167. X    {
  168. X        disp_info[input_disp].cur_pos.x = char_node->x;
  169. X        disp_info[input_disp].cur_pos.y = char_node->y;
  170. X    }
  171. X        else                               /*it was a normal letter*/
  172. X    {
  173. X            disp_info[input_disp].cur_pos.x -= XTextWidth(
  174. X          disp_info[input_disp].the_font_struct,&(char_node->the_char), 1);
  175. X
  176. X            XTextExtents(disp_info[input_disp].the_font_struct, 
  177. X          &(char_node->the_char), 1, &dr, &far, &fdr, &the_fnt_strct);
  178. X
  179. X            for (i=0; i< num_of_disps; i++)
  180. X        {
  181. X            XClearArea(disp_info[i].disp, disp_info[i].win_id, 
  182. X              disp_info[input_disp].cur_pos.x,
  183. X              disp_info[input_disp].cur_pos.y - the_fnt_strct.ascent,
  184. X              the_fnt_strct.lbearing + the_fnt_strct.rbearing,
  185. X              the_fnt_strct.ascent + the_fnt_strct.descent, False);
  186. X        }
  187. X    }
  188. X
  189. X    /* now pop the top char node from the history stack */
  190. X        disp_info[input_disp].type_history = 
  191. X                      disp_info[input_disp].type_history->next;
  192. X        free(char_node);
  193. X    }
  194. X}
  195. X
  196. X
  197. X/*
  198. X * draw_status_win_message - this function spits (in camel like fashion) out 
  199. X *                      the status window message to the display indicated.
  200. X */
  201. Xdraw_status_win_message(disp_num)
  202. X{
  203. X    int i, num_disps;
  204. X    char mesg[510], *cur_loc_ptr;
  205. X
  206. X    for (i=0, num_disps=0; i< num_of_disps; i++)
  207. X    if (disp_info[i].in_session_bool)  /*only if this window is alive*/
  208. X            num_disps++;
  209. X
  210. X    if (num_disps == 1)
  211. X        sprintf(mesg, "STATUS:  1 scrawl window currently open.  -->  ");
  212. X    else
  213. X        sprintf(mesg, "STATUS:  %d scrawl windows currently open.  -->  ", 
  214. X        num_disps);
  215. X
  216. X    cur_loc_ptr = mesg + strlen(mesg);
  217. X
  218. X    for (i=0; i< num_of_disps; i++)
  219. X    {
  220. X    if (disp_info[i].in_session_bool)  /*only if this window is alive*/
  221. X    {
  222. X        strncpy(cur_loc_ptr, disp_args[i], 25);
  223. X        cur_loc_ptr += strlen(disp_args[i]);
  224. X        cur_loc_ptr[0] = ',';
  225. X        cur_loc_ptr[1] = ' ';          /*space delineates*/
  226. X        cur_loc_ptr += 2;
  227. X    }
  228. X    }
  229. X    cur_loc_ptr -= 2;
  230. X    cur_loc_ptr[0] = '\0';     /*end the string*/
  231. X
  232. X    XClearWindow(disp_info[disp_num].disp, disp_info[disp_num].status_win_id);
  233. X    XDrawString(disp_info[disp_num].disp, disp_info[disp_num].status_win_id, 
  234. X           disp_info[disp_num].fg_menu_gc, 10, 14, mesg, strlen(mesg));
  235. X    XFlush(disp_info[disp_num].disp);
  236. X}
  237. X
  238. X
  239. X/*
  240. X * draw_rubber_pointer - this function draws the rubber cursor of the input
  241. X *                 disp on the target disp at the indicated location.  The
  242. X *                 indicated location is the upper left corner of the cursor
  243. X *                 area, NOT the hotspot.  This simulated cursor consists of
  244. X *                 the machine name that is drawing, and a small arrow pointing
  245. X *                 to the lower left corner.
  246. X */
  247. Xdraw_rubber_pointer(input_disp, target_disp, x, y)
  248. Xint input_disp, target_disp;
  249. X{
  250. X    XDrawString(disp_info[target_disp].disp, disp_info[target_disp].win_id,
  251. X        disp_info[target_disp].fg_menu_gc,
  252. X        x + 15, y + disp_info[input_disp].rubber_pointer.height - 15, 
  253. X        disp_args[input_disp], strlen(disp_args[input_disp]));
  254. X
  255. X    XDrawLine(disp_info[target_disp].disp, disp_info[target_disp].win_id,
  256. X          disp_info[target_disp].fg_menu_gc,
  257. X          x+2, y - 2 + disp_info[input_disp].rubber_pointer.height, 
  258. X          x+12, y - 12 + disp_info[input_disp].rubber_pointer.height);
  259. X    XDrawLine(disp_info[target_disp].disp, disp_info[target_disp].win_id,
  260. X          disp_info[target_disp].fg_menu_gc,
  261. X          x+3, y - 2 + disp_info[input_disp].rubber_pointer.height, 
  262. X          x+3, y - 9 + disp_info[input_disp].rubber_pointer.height);
  263. X    XDrawLine(disp_info[target_disp].disp, disp_info[target_disp].win_id,
  264. X          disp_info[target_disp].fg_menu_gc,
  265. X          x+3, y - 2 + disp_info[input_disp].rubber_pointer.height, 
  266. X          x+9, y - 2 + disp_info[input_disp].rubber_pointer.height);
  267. X}
  268. X
  269. X
  270. X/*
  271. X * place_and_draw_status_win - this function places the status window at the
  272. X *                      correct location on the scrawl window, and draws
  273. X *                      the contents.  (Also updates the contents of all
  274. X *                      other scrawl windows, but DOES NOT change their 
  275. X *                      locations.)
  276. X */
  277. Xplace_and_draw_status_win(disp_num)
  278. Xint disp_num;
  279. X{
  280. X    XWindowAttributes window_attr;
  281. X
  282. X    if (disp_info[disp_num].in_session_bool == FALSE)
  283. X    return(0);    /*This window is no longer active*/
  284. X    
  285. X    XGetWindowAttributes(disp_info[disp_num].disp, 
  286. X             disp_info[disp_num].win_id, &window_attr);
  287. X    XMoveResizeWindow(disp_info[disp_num].disp, 
  288. X             disp_info[disp_num].status_win_id, 
  289. X             0, window_attr.height - 24, window_attr.width-4, 20);
  290. X
  291. X    draw_status_win_message(disp_num);
  292. X}
  293. X
  294. X
  295. X/*
  296. X * draw_on_screens - this function draws the segment that is indicated
  297. X *                   by the current mouse position and previous mouse 
  298. X *                   position on the display passed to it as an argument.
  299. X *                   This segment is drawn to ALL displays currently open.
  300. X *                   If this dude is airbrushing, then of course an airbrush
  301. X *                   pattern is sprayed all over, and if it is an eraser,
  302. X *                   then erasing happens.  If it is a rubber pointer, rubber
  303. X *                   pointing happens.
  304. X */
  305. Xdraw_on_screens(input_disp)
  306. Xint input_disp;
  307. X{
  308. X    Window rr, cr;
  309. X    unsigned int mskr;
  310. X    int i, j, left, top, oldleft, oldtop, rxr, ryr, win_x, win_y;
  311. X
  312. X    if (disp_info[input_disp].scrawl_mode == SCRAWLING)
  313. X    {
  314. X        if (disp_info[input_disp].first_point_bool)
  315. X        {
  316. X            XQueryPointer(disp_info[input_disp].disp, 
  317. X              disp_info[input_disp].win_id, &rr, &cr, &rxr, 
  318. X              &ryr, &win_x, &win_y,&mskr);
  319. X        disp_info[input_disp].last_point.x = win_x;
  320. X        disp_info[input_disp].last_point.y = win_y;
  321. X        disp_info[input_disp].first_point_bool = FALSE;
  322. X        }
  323. X
  324. X        XQueryPointer(disp_info[input_disp].disp, disp_info[input_disp].win_id, 
  325. X                      &rr, &cr, &rxr, &ryr, &win_x, &win_y, &mskr);
  326. X        for (i=0; i< num_of_disps; i++)
  327. X        {
  328. X        if (disp_info[i].in_session_bool) /*only if this window is alive*/
  329. X        {
  330. X                XDrawLine(disp_info[i].disp,disp_info[i].win_id,
  331. X                  disp_info[input_disp].win_gc[i],
  332. X                      disp_info[input_disp].last_point.x, 
  333. X                      disp_info[input_disp].last_point.y, win_x, win_y);
  334. X                XFlush(disp_info[i].disp);
  335. X        }
  336. X        }
  337. X    
  338. X        disp_info[input_disp].last_point.x = win_x;
  339. X        disp_info[input_disp].last_point.y = win_y;
  340. X    }
  341. X    else if (disp_info[input_disp].scrawl_mode == AIRBRUSHING)
  342. X    {
  343. X        XQueryPointer(disp_info[input_disp].disp, disp_info[input_disp].win_id, 
  344. X                      &rr, &cr, &rxr, &ryr, &win_x, &win_y, &mskr);
  345. X        for (i=0; i< num_of_disps; i++)
  346. X        {
  347. X        if (disp_info[i].in_session_bool) /*only if this window is alive*/
  348. X        {
  349. X                airbrush(input_disp, disp_info[i].disp, disp_info[i].win_id, 
  350. X                 disp_info[input_disp].win_gc[i], win_x, win_y);
  351. X                XFlush(disp_info[i].disp);
  352. X        }
  353. X        }
  354. X    }
  355. X    else if (disp_info[input_disp].scrawl_mode == ERASING)
  356. X    {
  357. X        XQueryPointer(disp_info[input_disp].disp, disp_info[input_disp].win_id, 
  358. X                      &rr, &cr, &rxr, &ryr, &win_x, &win_y, &mskr);
  359. X    XMoveWindow(disp_info[input_disp].disp, 
  360. X            disp_info[input_disp].eraser_win_id, 
  361. X            win_x - disp_info[input_disp].pen_width/2, 
  362. X            win_y - disp_info[input_disp].pen_width/2);
  363. X
  364. X        for (i=0; i< num_of_disps; i++)
  365. X        {
  366. X        if (disp_info[i].in_session_bool) /*only if this window is alive*/
  367. X        {
  368. X                XClearArea(disp_info[i].disp, disp_info[i].win_id, 
  369. X              win_x - disp_info[input_disp].pen_width/2, 
  370. X              win_y - disp_info[input_disp].pen_width/2, 
  371. X              disp_info[input_disp].pen_width, 
  372. X              disp_info[input_disp].pen_width, False);
  373. X                XFlush(disp_info[i].disp);
  374. X        }
  375. X        }
  376. X    }
  377. X    else if (disp_info[input_disp].scrawl_mode == RUBBER_POINTING)
  378. X    {
  379. X        if (disp_info[input_disp].first_point_bool)
  380. X        {
  381. X            XQueryPointer(disp_info[input_disp].disp, 
  382. X              disp_info[input_disp].win_id, &rr, &cr, &rxr, 
  383. X              &ryr, &win_x, &win_y,&mskr);
  384. X        disp_info[input_disp].last_point.x = win_x;
  385. X        disp_info[input_disp].last_point.y = win_y;
  386. X        disp_info[input_disp].first_point_bool = FALSE;
  387. X        /*
  388. X         * turn off any other cursors that are on before getting the
  389. X         * background pixmap.  (So you we don't copy the cursors as
  390. X         * part of the background.)
  391. X         */
  392. X            for (i=0; i< num_of_disps; i++)
  393. X            {
  394. X            if (disp_info[i].in_session_bool)   /*only if window is alive*/
  395. X            {
  396. X                    if (disp_info[i].rubber_pointer.is_mapped_bool == TRUE)
  397. X            {
  398. X                    oldleft = disp_info[i].last_point.x;
  399. X                    oldtop = disp_info[i].last_point.y - 
  400. X                      disp_info[i].rubber_pointer.height;
  401. X                        for (j=0; j< num_of_disps; j++)
  402. X                        if (disp_info[j].in_session_bool) 
  403. X                            {
  404. X                        XCopyArea(disp_info[j].disp, 
  405. X                      disp_info[i].rubber_pointer.rubber_pointer_pix[j],
  406. X                          disp_info[j].win_id, disp_info[i].win_gc[j],
  407. X                          0, 0, disp_info[i].rubber_pointer.width, 
  408. X                          disp_info[i].rubber_pointer.height, oldleft,
  409. X                          oldtop);
  410. X                        XFlush(disp_info[j].disp);
  411. X                }
  412. X            }
  413. X            }
  414. X            }
  415. X        /*
  416. X         * now get the background pixmap.
  417. X         */
  418. X        left = win_x;
  419. X        top = win_y - disp_info[input_disp].rubber_pointer.height;
  420. X            for (i=0; i< num_of_disps; i++)
  421. X            {
  422. X            if (disp_info[i].in_session_bool)   /*only if window is alive*/
  423. X            {
  424. X            XCopyArea(disp_info[i].disp, 
  425. X              disp_info[i].win_id,
  426. X             disp_info[input_disp].rubber_pointer.rubber_pointer_pix[i],
  427. X                  disp_info[input_disp].win_gc[i],
  428. X                  left, top, disp_info[input_disp].rubber_pointer.width,
  429. X                  disp_info[input_disp].rubber_pointer.height, 0, 0);
  430. X            }
  431. X            }
  432. X        /*
  433. X         * draw (map) all of the cursors that are supposed to be mapped
  434. X         */
  435. X            disp_info[input_disp].rubber_pointer.is_mapped_bool = TRUE;
  436. X            for (i=0; i< num_of_disps; i++)
  437. X            if (disp_info[i].in_session_bool)   /*only if window is alive*/
  438. X        {
  439. X                    if (disp_info[i].rubber_pointer.is_mapped_bool == TRUE)
  440. X            {
  441. X                    left = disp_info[i].last_point.x;
  442. X                    top = disp_info[i].last_point.y - 
  443. X                       disp_info[i].rubber_pointer.height;
  444. X                        for (j=0; j< num_of_disps; j++)
  445. X                        if (disp_info[j].in_session_bool) 
  446. X                        draw_rubber_pointer(i, j, left, top);
  447. X            }
  448. X        }
  449. X        }
  450. X
  451. X        XQueryPointer(disp_info[input_disp].disp, disp_info[input_disp].win_id, 
  452. X                      &rr, &cr, &rxr, &ryr, &win_x, &win_y, &mskr);
  453. X    if ((win_x != disp_info[input_disp].last_point.x) ||
  454. X        (win_y != disp_info[input_disp].last_point.y))
  455. X        {
  456. X        /*
  457. X         * turn off any other cursors that are on before getting the
  458. X         * background pixmap.  (So we don't copy the cursors as
  459. X         * part of the background.)
  460. X         */
  461. X            for (i=0; i< num_of_disps; i++)
  462. X            {
  463. X            if (disp_info[i].in_session_bool)   /*only if window is alive*/
  464. X            {
  465. X                    if (disp_info[i].rubber_pointer.is_mapped_bool == TRUE)
  466. X            {
  467. X                    oldleft = disp_info[i].last_point.x;
  468. X                    oldtop = disp_info[i].last_point.y - 
  469. X                      disp_info[i].rubber_pointer.height;
  470. X                        for (j=0; j< num_of_disps; j++)
  471. X                        if (disp_info[j].in_session_bool) 
  472. X                            {
  473. X                        XCopyArea(disp_info[j].disp, 
  474. X                      disp_info[i].rubber_pointer.rubber_pointer_pix[j],
  475. X                          disp_info[j].win_id, disp_info[i].win_gc[j],
  476. X                          0, 0, disp_info[i].rubber_pointer.width, 
  477. X                          disp_info[i].rubber_pointer.height, oldleft,
  478. X                          oldtop);
  479. X                }
  480. X            }
  481. X            }
  482. X            }
  483. X
  484. X        /*
  485. X         * save new cursor area for the input cursor
  486. X         */
  487. X        left = win_x;
  488. X        top = win_y - disp_info[input_disp].rubber_pointer.height;
  489. X            for (i=0; i< num_of_disps; i++)
  490. X            {
  491. X            if (disp_info[i].in_session_bool)   /*only if window is alive*/
  492. X            {
  493. X            XCopyArea(disp_info[i].disp, 
  494. X             disp_info[i].win_id,
  495. X             disp_info[input_disp].rubber_pointer.rubber_pointer_pix[i],
  496. X             disp_info[input_disp].win_gc[i],
  497. X             left, top, disp_info[input_disp].rubber_pointer.width, 
  498. X             disp_info[input_disp].rubber_pointer.height, 0, 0);
  499. X            }
  500. X            }
  501. X
  502. X            disp_info[input_disp].last_point.x = win_x;
  503. X            disp_info[input_disp].last_point.y = win_y;
  504. X        /*
  505. X         * now draw (map) all of the cursors that are currently unmapped
  506. X         */
  507. X            for (i=0; i< num_of_disps; i++)
  508. X            if (disp_info[i].in_session_bool)   /*only if window is alive*/
  509. X            {
  510. X                    if (disp_info[i].rubber_pointer.is_mapped_bool == TRUE)
  511. X                {
  512. X                    left = disp_info[i].last_point.x;
  513. X                    top = disp_info[i].last_point.y - 
  514. X                       disp_info[i].rubber_pointer.height;
  515. X                        for (j=0; j< num_of_disps; j++)
  516. X                        if (disp_info[j].in_session_bool) 
  517. X                        draw_rubber_pointer(i, j, left, top);
  518. X            }
  519. X                }
  520. X            for (i=0; i< num_of_disps; i++)
  521. X            if (disp_info[i].in_session_bool)   /*only if window is alive*/
  522. X            XFlush(disp_info[i].disp);
  523. X    }
  524. X    }
  525. X}
  526. X
  527. X
  528. X/*
  529. X * parse_command_line - this function parses the command line and sets the
  530. X *             various global variables for the opening display
  531. X *             and color party coming up.  Sorry that this routine is so
  532. X *             ugly, but just go slow, and it's not too bad.  I'd look at
  533. X *             the "command_line_err()" routine to understand what each
  534. X *             of the command line options mean.
  535. X *             (ex. % wscrawl -d hpcvxbw:0 -d hpcvxca:0 -pc red -cs CapRound
  536. X *                    -pw 45 -bs off -nd 50 -ps airbrush)
  537. X */
  538. Xparse_command_line(argv, argc)
  539. Xchar *argv[];
  540. Xint argc;
  541. X{
  542. X     int i, current_arg, W_SET = 0;
  543. X
  544. X     strncpy(PEN_COLOR, "magenta", 40);            /*defaults set here*/
  545. X     strncpy(BACKGROUND_COLOR, "white", 40);
  546. X     strncpy(MENU_FOREGROUND_COLOR, "black", 40);
  547. X     strncpy(MENU_BACKGROUND_COLOR, "wheat", 40);
  548. X     strncpy(MENU_HIGHLIGHT_COLOR, "black", 40);
  549. X     strncpy(PEN_STYLE, "dot", 40);
  550. X     strncpy(FONT, "vgl-40", 100);
  551. X     strncpy(disp_args[0], "", 30);
  552. X     TYPE_NOT_DRAW = FALSE;
  553. X     CAP_STYLE = CapRound;
  554. X     PEN_WIDTH = 8;
  555. X     NUM_DOTS = 50;
  556. X     for (i=0; i< num_of_disps; i++)
  557. X     {
  558. X         disp_info[i].pen_width = 8;
  559. X     }
  560. X
  561. X     for (current_arg = 1; current_arg < argc; current_arg+=2)
  562. X     {
  563. X     if (strcmp(argv[current_arg],"-d") == 0)
  564. X     {
  565. X         strncpy(disp_args[num_of_disps], argv[current_arg+1], 48);
  566. X         num_of_disps++;
  567. X         }
  568. X     else if (strcmp(argv[current_arg],"-mfg") == 0)
  569. X     {
  570. X         if (current_arg != argc-1)
  571. X             strncpy(MENU_FOREGROUND_COLOR, argv[current_arg+1], 40);
  572. X         else
  573. X         command_line_err();     /*no color was specified*/
  574. X     }
  575. X     else if (strcmp(argv[current_arg],"-mbg") == 0)
  576. X     {
  577. X         if (current_arg != argc-1)
  578. X             strncpy(MENU_BACKGROUND_COLOR, argv[current_arg+1], 40);
  579. X         else
  580. X         command_line_err();     /*no color was specified*/
  581. X     }
  582. X     else if (strcmp(argv[current_arg],"-mhc") == 0)
  583. X     {
  584. X         if (current_arg != argc-1)
  585. X             strncpy(MENU_HIGHLIGHT_COLOR, argv[current_arg+1], 40);
  586. X         else
  587. X         command_line_err();     /*no color was specified*/
  588. X     }
  589. X     else if (strcmp(argv[current_arg],"-bg") == 0)
  590. X     {
  591. X         if (current_arg != argc-1)
  592. X             strncpy(BACKGROUND_COLOR, argv[current_arg+1], 40);
  593. X         else
  594. X         command_line_err();     /*no color was specified*/
  595. X     }
  596. X     else if (strcmp(argv[current_arg],"-pc") == 0)
  597. X     {
  598. X         if (current_arg != argc-1)
  599. X             strncpy(PEN_COLOR, argv[current_arg+1], 40);
  600. X         else
  601. X         command_line_err();     /*no color was specified*/
  602. X     }
  603. X     else if (strcmp(argv[current_arg],"-fn") == 0)
  604. X     {
  605. X         if (current_arg != argc-1)
  606. X             strncpy(FONT, argv[current_arg+1], 220);
  607. X         else
  608. X         command_line_err();     /*no color was specified*/
  609. X     }
  610. X     else if (strcmp(argv[current_arg],"-tm") == 0)
  611. X     {
  612. X         TYPE_NOT_DRAW = TRUE;
  613. X          current_arg--;  /*type message is an argumentless option*/
  614. X     }
  615. X     else if (strcmp(argv[current_arg],"-nd") == 0)
  616. X     {
  617. X         if (current_arg != argc-1)
  618. X         {
  619. X             sscanf(argv[current_arg+1], "%d", &NUM_DOTS);
  620. X         if ((NUM_DOTS < 1) || (NUM_DOTS > 500))
  621. X             command_line_err(); /*incorrect range*/
  622. X         }
  623. X         else
  624. X         command_line_err();     /*no number was specified*/
  625. X     }
  626. X     else if (strcmp(argv[current_arg],"-ps") == 0)
  627. X     {
  628. X         if (current_arg != argc-1)
  629. X         {
  630. X         if ((strcmp(argv[current_arg+1],"dot") == 0) || 
  631. X             (strcmp(argv[current_arg+1],"airbrush")) == 0)
  632. X         {
  633. X            strncpy(PEN_STYLE, argv[current_arg+1], 40);
  634. X            if ((strcmp(PEN_STYLE,"airbrush") == 0) && !W_SET)
  635. X            disp_info[0].pen_width = 40; /*default penwidth*/
  636. X         }
  637. X         else
  638. X             command_line_err();     /*incorrect pen style specified*/
  639. X         }
  640. X         else
  641. X         command_line_err();     /*no pen style was specified*/
  642. X     }
  643. X     else if (strcmp(argv[current_arg],"-pw") == 0)
  644. X     {
  645. X         if (current_arg != argc-1)
  646. X         {
  647. X             sscanf(argv[current_arg+1], "%d", &PEN_WIDTH);
  648. X         W_SET = TRUE;           /*the width has been set*/
  649. X         }
  650. X         else
  651. X         command_line_err();     /*no pen width was specified*/
  652. X     }
  653. X     else if (strcmp(argv[current_arg],"-cs") == 0)
  654. X     {
  655. X         if (current_arg != argc-1)
  656. X         {
  657. X         if (strcmp(argv[current_arg+1],"CapButt") == 0)
  658. X             CAP_STYLE = CapButt;
  659. X         else if (strcmp(argv[current_arg+1],"CapNotLast") == 0)
  660. X             CAP_STYLE = CapNotLast;
  661. X         else if (strcmp(argv[current_arg+1],"CapRound") == 0)
  662. X             CAP_STYLE = CapRound;
  663. X         else if (strcmp(argv[current_arg+1],"CapProjecting") == 0)
  664. X             CAP_STYLE = CapProjecting;
  665. X         else
  666. X                     command_line_err();     /*bad cap style was specified*/
  667. X         }
  668. X         else
  669. X                 command_line_err();     /*no cap style was specified*/
  670. X     }
  671. X     else
  672. X     {
  673. X             command_line_err();
  674. X     }
  675. X     }
  676. X}
  677. X
  678. X
  679. X/* 
  680. X * command_line_err - this function spits out a standard error message and
  681. X *                    exits the program.
  682. X */
  683. Xcommand_line_err()
  684. X{
  685. X    printf("\nBad command line option dude.\n");
  686. X    printf("Try: wscrawl\n");
  687. X    printf("  [-d displayname1 -d displayname2 . . .]\n");
  688. X    printf("  [-bg background_color]\n");
  689. X    printf("  [-mfg menu_foreground_color]\n");
  690. X    printf("  [-mbg menu_background_color]\n");
  691. X    printf("  [-mhc menu_highlight_color]\n");
  692. X    printf("  [-pc pen_color]\n");
  693. X    printf("  [-pw pen_width]\n");
  694. X    printf("  [-ps pen_style]  (dot,airbrush)\n");
  695. X    printf("  [-cs cap_style]  (CapButt,CapNotLast,CapRound,CapProjecting)\n");
  696. X    printf("  [-nd num_dots]   (number of dots in the airbrush (1-500))\n");
  697. X    printf("  [-tm]            (type a message, don't draw)\n");
  698. X    printf("  [-fn font]       (name of font to use if typing a message)\n\n");
  699. X    exit(0);
  700. X}
  701. X
  702. X
  703. X/*
  704. X * add_a_new_display - this function opens a new display to the scrawl
  705. X *                     session.  It calls the function that initializes 
  706. X *                     all the windows, and it transfers the image.
  707. X */
  708. Xadd_a_new_display(disp_name)
  709. Xchar *disp_name;
  710. X{
  711. X    int i, k, found_source, disp_num, source_disp_num, width, height;
  712. X    XWindowAttributes new_win_attr, old_win_attr, root_win_attr;
  713. X    int cur_depth, print_it, win_left, win_top, left, top, fully_printed;
  714. X    char *mesg;
  715. X    Window dummy;
  716. X
  717. X    set_paused_cursors();         /*this is going to take a while*/
  718. X
  719. X    if (XOpenDisplay(disp_name) == NULL)
  720. X    {
  721. X        printf("XOpenDisplay failed on the Added Display: %s\n", disp_name);
  722. X        unset_paused_cursors();         /*done*/
  723. X        return(0);
  724. X    }
  725. X
  726. X
  727. X    for (disp_num=0; disp_num<num_of_disps; disp_num++)
  728. X        if (disp_info[disp_num].in_session_bool == FALSE) /*this is available*/
  729. X        break;
  730. X       
  731. X    if (disp_num == num_of_disps)
  732. X    {
  733. X        num_of_disps++;            /*it goes after all the rest*/
  734. X    }
  735. X
  736. X    disp_info[disp_num].in_session_bool = TRUE;    /*this is the one*/
  737. X    strncpy(disp_args[disp_num], disp_name, 48);
  738. X
  739. X    if (initialize_a_single_display(disp_num, &num_people_drawing) != TRUE)
  740. X    {
  741. X        printf("ERROR: Could not add display %s for some reason.\n",
  742. X           disp_args[disp_num]);
  743. X        disp_info[disp_num].in_session_bool = FALSE;
  744. X        unset_paused_cursors();         /*done*/
  745. X    return(0);
  746. X    }
  747. X
  748. X    else
  749. X    {
  750. X        if (NOTHING_DRAWN_YET == TRUE)   /*global variable*/
  751. X    {
  752. X            for (k=0; k<num_of_disps; k++)
  753. X                if (disp_info[k].in_session_bool)
  754. X                      place_and_draw_status_win(k);
  755. X        unset_paused_cursors();         /*we are done*/
  756. X        return(1);
  757. X    }
  758. X
  759. X    /*
  760. X     * now figure out what window we should get an image from to
  761. X     * put onto this new window.  Best case is one that is the same
  762. X         * depth and fully visible, next best case is the most depth, etc...
  763. X         */
  764. X        XGetWindowAttributes(disp_info[disp_num].disp, 
  765. X                   disp_info[disp_num].win_id, &new_win_attr);
  766. X
  767. X        /*
  768. X         * get the same depth image fully on screen, if at all possible
  769. X         */
  770. X        for (i=0,found_source=FALSE;(i<num_of_disps)&&(found_source==FALSE);i++)
  771. X        {
  772. X            if ((disp_info[i].in_session_bool==TRUE) && (i!=disp_num))
  773. X            {  
  774. X                XGetWindowAttributes(disp_info[i].disp, disp_info[i].win_id,
  775. X                     &old_win_attr);
  776. X            if (old_win_attr.depth == new_win_attr.depth)
  777. X            {
  778. X                XTranslateCoordinates(disp_info[i].disp, 
  779. X                disp_info[i].win_id, RootWindow(disp_info[i].disp,
  780. X                DefaultScreen(disp_info[i].disp)),
  781. X                0, 0, &left, &top, &dummy);
  782. X                    XGetWindowAttributes(disp_info[i].disp, 
  783. X                RootWindow(disp_info[i].disp, 
  784. X                DefaultScreen(disp_info[i].disp)), 
  785. X                &root_win_attr);
  786. X                    if (((left + old_win_attr.width) < root_win_attr.width) &&
  787. X                        ((top + old_win_attr.height) < root_win_attr.height)) 
  788. X            {
  789. X                source_disp_num = i;   /*we found the ideal case*/
  790. X                found_source = TRUE;   /*same depth, fully on monitor*/
  791. X                break;
  792. X            }
  793. X            }
  794. X        }
  795. X    }
  796. X
  797. X        if (found_source == FALSE)
  798. X    {
  799. X            /*
  800. X             * get the same depth image if at all possible, not fully on screen
  801. X             */
  802. X            for (i=0; (i<num_of_disps) && (found_source==FALSE); i++)
  803. X            {
  804. X                if ((disp_info[i].in_session_bool==TRUE) && (i!=disp_num))
  805. X                {  
  806. X                    XGetWindowAttributes(disp_info[i].disp, disp_info[i].win_id,
  807. X                         &old_win_attr);
  808. X                if (old_win_attr.depth == new_win_attr.depth)
  809. X                {
  810. X                source_disp_num = i;   /*we found an OK case*/
  811. X                found_source = TRUE;   /*same depth, part on monitor*/
  812. X                break;
  813. X                }
  814. X            }
  815. X        }
  816. X        }
  817. X
  818. X        if (found_source == FALSE)
  819. X    {
  820. X        /*
  821. X         * we didn't find a display of equal depth, so look for the one of
  822. X         * the greatest depth in the session.  It is the best one to use.
  823. X         */
  824. X            for (i=0, cur_depth=0; (i<num_of_disps)&&(found_source==FALSE); i++)
  825. X            {
  826. X                if ((disp_info[i].in_session_bool==TRUE) && (i!=disp_num))
  827. X                {  
  828. X                    XGetWindowAttributes(disp_info[i].disp, disp_info[i].win_id,
  829. X                 &old_win_attr);
  830. X                if (old_win_attr.depth > cur_depth)
  831. X            {
  832. X                        source_disp_num = i;
  833. X            cur_depth = old_win_attr.depth;
  834. X            }
  835. X            }
  836. X        }
  837. X        }
  838. X
  839. X    /*
  840. X     * now that we have our favorite choice, make sure we don't get any
  841. X     * image from a part of a window that is offscreen.  This is 
  842. X     * not allowed and causes core dump.
  843. X     */
  844. X    XTranslateCoordinates(disp_info[source_disp_num].disp, 
  845. X                disp_info[source_disp_num].win_id, 
  846. X                RootWindow(disp_info[source_disp_num].disp,
  847. X                DefaultScreen(disp_info[source_disp_num].disp)),
  848. X                0, 0, &left, &top, &dummy);
  849. X        XGetWindowAttributes(disp_info[source_disp_num].disp, 
  850. X                RootWindow(disp_info[source_disp_num].disp, 
  851. X                DefaultScreen(disp_info[source_disp_num].disp)), 
  852. X                &root_win_attr);
  853. X
  854. X        fully_printed = TRUE;
  855. X        print_it = TRUE;
  856. X
  857. X        if (left < 0)
  858. X    {
  859. X        printf("ERROR: Get the dang window from out from under the ");
  860. X        printf("left edge of the monitor.\n");
  861. X        print_it = FALSE;
  862. X        }
  863. X    else
  864. X        win_left = 0;
  865. X
  866. X        if (top < 0)
  867. X    {
  868. X        printf("ERROR: Get the dang window from out from under the ");
  869. X        printf("top edge of the monitor.\n");
  870. X        print_it = FALSE;
  871. X        }
  872. X    else
  873. X        win_top = 0;
  874. X
  875. X        if ((left + old_win_attr.width) < root_win_attr.width)
  876. X        width = old_win_attr.width;          /*we are inside the boundary*/
  877. X    else                                  
  878. X    {
  879. X        width = root_win_attr.width - left;  /*we are outside the boundary*/
  880. X            fully_printed = FALSE;
  881. X    }
  882. X
  883. X        if ((top + old_win_attr.height) < root_win_attr.height)
  884. X        height = old_win_attr.height;        /*we are inside the boundary*/
  885. X    else                                  
  886. X    {
  887. X        height = root_win_attr.height - top; /*we are outside the boundary*/
  888. X            fully_printed = FALSE;
  889. X    }
  890. X
  891. X        if (print_it == TRUE)
  892. X    {
  893. X            transfer_image_between_displays(source_disp_num, disp_num,win_left,
  894. X                    win_top, width, height);
  895. X    }
  896. X    else
  897. X    {
  898. X        printf("WARNING: image not displayed on display %s.\n",
  899. X               disp_args[disp_num]);
  900. X
  901. X        left = 0;
  902. X        top = 0;
  903. X            width = old_win_attr.width;
  904. X            height = old_win_attr.height;
  905. X
  906. X        mesg = "You are missing an image here.";
  907. X        XDrawString(disp_info[disp_num].disp, disp_info[disp_num].win_id,
  908. X                    disp_info[disp_num].fg_menu_gc, 
  909. X                (left + (width/2) - 88), (top + (height/2) + 5),
  910. X                 mesg, strlen(mesg));
  911. X    }
  912. X
  913. X        if (fully_printed == FALSE)
  914. X    {  
  915. X            printf("WARNING: image not fully displayed on display %s.\n",
  916. X                    disp_args[disp_num]);
  917. X    }
  918. X
  919. X        for (k=0; k<num_of_disps; k++)
  920. X            if (disp_info[k].in_session_bool)
  921. X                  place_and_draw_status_win(k);
  922. X        unset_paused_cursors();         /*we are done*/
  923. X    }
  924. X}
  925. X
  926. X
  927. X/*
  928. X * transfer_image_between_displays - this function tranfers an image from
  929. X *                      one display to another as specified by the upper 
  930. X *                      left corner x, y and the width and height parameters.
  931. X *                      Currently this is done using the "save to disk" and
  932. X *                      "restore from disk" functions as an easy shortcut. 
  933. X */
  934. Xtransfer_image_between_displays(source_disp_num, dest_disp_num, x, y, width, 
  935. X                height)
  936. Xint source_disp_num, dest_disp_num, x, y, width, height;
  937. X{
  938. X    XImage *the_full_screen_image;
  939. X    char *mesg, temp_file_name[256];
  940. X    int depth;
  941. X
  942. X    sprintf(temp_file_name, "/tmp/wscr.%d", getpid());
  943. X
  944. X    save_image_on_disk(disp_info[source_disp_num].disp, 
  945. X               disp_info[source_disp_num].win_id, x, y, width, height, 
  946. X               temp_file_name);
  947. X
  948. X    if ((the_full_screen_image = read_image_from_disk(
  949. X                     disp_info[dest_disp_num].disp, 
  950. X                     disp_info[dest_disp_num].win_id, 
  951. X                     temp_file_name, &width,
  952. X                         &height, &depth)) == NULL)
  953. X    {  
  954. X        printf("WARNING: image not displayed on display %s.\n",
  955. X                    disp_args[dest_disp_num]);
  956. X        XClearArea(disp_info[dest_disp_num].disp, 
  957. X               disp_info[dest_disp_num].win_id, 
  958. X               0, 0, width, height, False);
  959. X
  960. X        XSetLineAttributes(disp_info[dest_disp_num].disp, 
  961. X                               disp_info[dest_disp_num].rubber_band_gc, 3, 
  962. X                   LineSolid, CapButt, JoinBevel);
  963. X    if ((width > 4) && (height > 4))
  964. X    {
  965. X            XDrawRectangle(disp_info[dest_disp_num].disp, 
  966. X                       disp_info[dest_disp_num].win_id,
  967. X                           disp_info[dest_disp_num].rubber_band_gc,
  968. X                           2, 2, width - 4, height - 4);
  969. X    }
  970. X    XSetLineAttributes(disp_info[dest_disp_num].disp, 
  971. X                               disp_info[dest_disp_num].rubber_band_gc, 0, 
  972. X                   LineSolid, CapButt, JoinBevel);
  973. X
  974. X    mesg = "You are missing an image here.";
  975. X    XDrawString(disp_info[dest_disp_num].disp, 
  976. X            disp_info[dest_disp_num].win_id,
  977. X                    disp_info[dest_disp_num].fg_menu_gc, (width/2) - 88, 
  978. X            (height/2) + 5, mesg, strlen(mesg));
  979. X    } 
  980. X    else if (DefaultDepth(disp_info[dest_disp_num].disp, 
  981. X             DefaultScreen(disp_info[dest_disp_num].disp)) != depth)
  982. X    {
  983. X        printf("WARNING: image not displayed on display %s. ",
  984. X                       disp_args[dest_disp_num]);
  985. X        printf("(Inconsistent depths.)\n");
  986. X        XDestroyImage(the_full_screen_image);
  987. X    }
  988. X    else
  989. X    {
  990. X        XPutImage(disp_info[dest_disp_num].disp, 
  991. X             disp_info[dest_disp_num].win_id,
  992. X              disp_info[dest_disp_num].win_gc[dest_disp_num], 
  993. X          the_full_screen_image, 
  994. X              0, 0, x, y, width, height);
  995. X        XFlush(disp_info[dest_disp_num].disp);
  996. X        XDestroyImage(the_full_screen_image);
  997. X    }
  998. X}
  999. X
  1000. X
  1001. X/*
  1002. X * initialize_a_single_display - this function creates all the win_ids, gcs,
  1003. X *                      etc, for a new display assuming that the the "disp_num"
  1004. X *                      passed in is the location in the global disp_info 
  1005. X *                      structure of the display, and that all the other 
  1006. X *                      displays are set up VERY well (i.e. the 
  1007. X *                      "in_session_bool" field is accurate.  This function
  1008. X *                      also allocates gc's to draw on THIS NEW DISPLAY from
  1009. X *                      the previous existing displays.
  1010. X */
  1011. Xinitialize_a_single_display(disp_num, num_people_drawing)
  1012. Xint disp_num, *num_people_drawing;
  1013. X{
  1014. X    unsigned long nothing_mask = 0;
  1015. X    unsigned long alter_these_mask = GCSubwindowMode | GCLineWidth | 
  1016. X                     GCForeground | GCCapStyle | GCFont |
  1017. X                     GCBackground | GCGraphicsExposures;
  1018. X    unsigned long alter_menu_mask =  GCSubwindowMode | GCForeground | GCFont |
  1019. X                     GCBackground | GCLineWidth;
  1020. X    unsigned long alter_these2_mask = CWEventMask | CWBackPixel | 
  1021. X                   CWBackingStore | CWWinGravity |
  1022. X                   CWBitGravity | CWSaveUnder;
  1023. X    unsigned long alter_menu2_mask = CWEventMask | CWBackPixel | CWSaveUnder |
  1024. X                     CWBackingStore | CWWinGravity;
  1025. X    unsigned long gc_copy_mask = GCSubwindowMode | GCLineWidth | 
  1026. X                     GCForeground | GCCapStyle | GCFont |
  1027. X                     GCBackground | GCGraphicsExposures;
  1028. X    XSizeHints win_size_hints;
  1029. X    Cursor the_cursor, menu_cursor;
  1030. X    XColor scrn_def_ret, exact_def_ret, scrn_def_ret2, exact_def_ret2;
  1031. END_OF_FILE
  1032. if test 35313 -ne `wc -c <'wscrawl/xad'`; then
  1033.     echo shar: \"'wscrawl/xad'\" unpacked with wrong size!
  1034. fi
  1035. # end of 'wscrawl/xad'
  1036. fi
  1037. echo shar: End of archive 3 \(of 5\).
  1038. cp /dev/null ark3isdone
  1039. MISSING=""
  1040. for I in 1 2 3 4 5 ; do
  1041.     if test ! -f ark${I}isdone ; then
  1042.     MISSING="${MISSING} ${I}"
  1043.     fi
  1044. done
  1045. if test "${MISSING}" = "" ; then
  1046.     echo You have unpacked all 5 archives.
  1047.     rm -f ark[1-9]isdone
  1048. else
  1049.     echo You still need to unpack the following archives:
  1050.     echo "        " ${MISSING}
  1051. fi
  1052. ##  End of shell archive.
  1053. exit 0
  1054. dan
  1055. ----------------------------------------------------
  1056. O'Reilly && Associates   argv@sun.com / argv@ora.com
  1057. Opinions expressed reflect those of the author only.
  1058.