home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume8 / chaos / part01 / widgets / Canvas.c next >
Encoding:
C/C++ Source or Header  |  1990-08-20  |  36.6 KB  |  1,361 lines

  1. /*
  2.  * Copyright (c) Ken W. Marks 1989, 1990.
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <fcntl.h>
  7. #include <math.h>
  8. #include <X11/IntrinsicP.h>
  9. #include <X11/StringDefs.h>
  10. #include <X11/XWDFile.h>
  11. #include <Chaos.h>
  12. #include <LocalDefs.h>
  13. #include <Ipc.h>
  14. #include <Menu.h>
  15. #include <MenuItems.h>
  16. #include <CanvasP.h>
  17. #include <Task.h>
  18. #include <Queue.h>
  19. #include <Colormap.h>
  20.  
  21. static Cardinal pad_amount[] = {0, 3, 2, 1};
  22.  
  23. /* macro for padding a length to an even multiple of 4 (uses pad_amount[]) */
  24. #define PAD(length) ((length) + pad_amount[(length) & 3])
  25.  
  26. #define STARTING_RADIUS        2.5
  27.  
  28. #define IN_BOUNDS_X(w, x)    ((int) ((x) < 0 ? 0 : \
  29.                 (x) >= (w)->core.width ? \
  30.                 (w)->core.width - 1 : (x)))
  31.  
  32. #define IN_BOUNDS_Y(w, y)    ((int) ((y) < 0 ? 0 : \
  33.                 (y) >= (w)->core.height ? \
  34.                 (w)->core.height - 1 : (y)))
  35.  
  36. #define MAX_SAVE_IMAGE_SIZE    65536
  37.  
  38.  
  39. #define offset(field) XtOffset(CanvasWidget,canvas.field)
  40. #define goffset(field) XtOffset(Widget,core.field)
  41.  
  42. static XtResource resources[] = {
  43.     {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
  44.     goffset(width), XtRString, "512"},
  45.     {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
  46.     goffset(height), XtRString, "512"},
  47. };
  48.  
  49. #undef offset
  50. #undef goffset
  51.  
  52. static void CanvasInitialize(), CanvasRealize(), CanvasResize();
  53. static void CanvasRedisplay();
  54.  
  55. static void CanvasRegister(), CanvasUnregister(), CanvasMessage();
  56. static void CanvasBtnDown(), CanvasBtnUp(), CanvasMotion();
  57. static void CanvasMoveBox();
  58. static void CanvasQuit();
  59. static void CanvasReverse();
  60. static void CanvasStartStop();
  61. extern void MenuPopup();
  62.  
  63. static XtActionsRec canvas_actions[] =
  64. {
  65.     {"unregister", CanvasUnregister},
  66.     {"receive_msg", CanvasMessage},
  67.     {"press", CanvasBtnDown},
  68.     {"release", CanvasBtnUp},
  69.     {"move", CanvasMotion},
  70.     {"popup", MenuPopup},
  71.     {"quit", CanvasQuit},
  72.     {"reverse", CanvasReverse},
  73.     {"start_stop", CanvasStartStop},
  74. };
  75.  
  76. static char canvas_translations[] =
  77. "<Destroy>:    unregister()\n\
  78.  <Prop>:    receive_msg()\n\
  79.  <Btn1Down>:    press()\n\
  80.  <Btn1Up>:    release()\n\
  81.  <Btn1Motion>:    move()\n\
  82.  <Btn3Down>:    popup(menu_popup.menu)\n\
  83.  Ctrl<Key>C:    quit()\n\
  84.  <Key>Q:    quit()\n\
  85.  <Key>Return:    reverse()\n\
  86.  <Key>0x20:    start_stop()\n\
  87. ";
  88.  
  89. #define superclass              (&widgetClassRec)
  90.  
  91. CanvasClassRec canvasClassRec = {
  92.     {
  93.     /* core fields          */
  94.      /* superclass         */ (WidgetClass) superclass,
  95.      /* class_name         */ "Canvas",
  96.      /* widget_size         */ sizeof(CanvasRec),
  97.      /* class_initialize     */ NULL,
  98.      /* class_part_initialize */ NULL,
  99.      /* class_inited         */ FALSE,
  100.      /* initialize         */ CanvasInitialize,
  101.      /* initialize_hook     */ NULL,
  102.      /* realize         */ CanvasRealize,
  103.      /* actions         */ canvas_actions,
  104.      /* num_actions         */ XtNumber(canvas_actions),
  105.      /* resources         */ resources,
  106.      /* resource_count     */ XtNumber(resources),
  107.      /* xrm_class         */ NULL,
  108.      /* compress_motion     */ TRUE,
  109.      /* compress_exposure     */ TRUE,
  110.      /* compress_enterleave     */ TRUE,
  111.      /* visible_interest     */ FALSE,
  112.      /* destroy         */ NULL,
  113.      /* resize         */ CanvasResize,
  114.      /* expose         */ CanvasRedisplay,
  115.      /* set_values         */ NULL,
  116.      /* set_values_hook     */ NULL,
  117.      /* set_values_almost     */ XtInheritSetValuesAlmost,
  118.      /* get_values_hook     */ NULL,
  119.      /* accept_focus         */ NULL,
  120.      /* version         */ XtVersion,
  121.      /* callback_private     */ NULL,
  122.      /* tm_table         */ canvas_translations,
  123.      /* query_geometry       */ NULL,
  124.     }
  125. };
  126.  
  127. WidgetClass canvasWidgetClass = (WidgetClass) & canvasClassRec;
  128.  
  129.  
  130. /************************************************************/
  131. /******************** Private Procedures ********************/
  132. /************************************************************/
  133.  
  134.  
  135. /*ARGSUSED*/
  136. static void CanvasInitialize(request, new)
  137. Widget request;            /* unused */
  138. Widget new;
  139. {
  140.     CanvasWidget w = (CanvasWidget) new;
  141.     Screen *screen = XtScreen(w);
  142.     extern Boolean retain_aspect_ratio;
  143.     extern int iteration_limit;
  144.     extern int task_width;
  145.     extern int task_height;
  146.  
  147.     w->canvas.p_inc = (double) (2 * STARTING_RADIUS) / (w->core.width - 1);
  148.     w->canvas.q_inc = (double) (2 * STARTING_RADIUS) / (w->core.height - 1);
  149.  
  150.     w->canvas.p_inc = w->canvas.q_inc = MAX(w->canvas.p_inc, w->canvas.q_inc);
  151.  
  152.     w->canvas.p_lo = (double) -(w->core.width * w->canvas.p_inc / 2);
  153.     w->canvas.p_hi = (double) (w->canvas.p_lo + w->canvas.p_inc *
  154.       (w->core.width - 1));
  155.  
  156.     w->canvas.q_lo = (double) -(w->core.height * w->canvas.q_inc / 2);
  157.     w->canvas.q_hi = (double) (w->canvas.q_lo + w->canvas.q_inc *
  158.       (w->core.height - 1));
  159.  
  160.     w->canvas.task_x = 0;
  161.     w->canvas.task_y = 0;
  162.     w->canvas.num_drones = 0;
  163.  
  164.     w->canvas.num_colors = CellsOfScreen(screen);
  165.     w->canvas.pixmap = NULL;
  166.     w->canvas.image = NULL;
  167.  
  168.     w->canvas.serial = 0;
  169.     w->canvas.method = MANDELBROT;
  170.  
  171.     w->canvas.finished = False;
  172.     w->canvas.paused = False;
  173.     w->canvas.needs_saving = True;
  174.  
  175.     /* set up the initial iteration limit */
  176.     w->canvas.curr_limit = iteration_limit;
  177.  
  178.     /* set up the initial aspect ratio retention state */
  179.     w->canvas.retain_aspect_ratio = retain_aspect_ratio;
  180.  
  181.     /* set size to zero to force initial change in CanvasChangeTaskSize() */
  182.     w->canvas.curr_task_width = 0;
  183.     w->canvas.curr_task_height = 0;
  184.  
  185.     CanvasChangeTaskSize(new, (unsigned int) task_width,
  186.       (unsigned int) task_height);
  187. }
  188.  
  189.  
  190. static void CanvasRealize(widget, valueMask, attrs)
  191. Widget widget;
  192. XtValueMask *valueMask;
  193. XSetWindowAttributes *attrs;
  194. {
  195.     CanvasWidget w = (CanvasWidget) widget;
  196.     Display *dpy = XtDisplay(w);
  197.     Screen *screen = XtScreen(w);
  198.     XtGCMask valuemask;
  199.     XGCValues gcv;
  200.     extern Colormap colormap;
  201.     extern Widget top_level;
  202.  
  203.     /* we assign the colormap here since we know that the top level shell has
  204.      * been realized. */
  205.     colormap = CreateColormap(dpy, screen, XtWindow(top_level));
  206.  
  207.     XtCreateWindow(widget, InputOutput, (Visual *) CopyFromParent,
  208.       *valueMask, attrs);
  209.  
  210.     gcv.function = GXinvert;
  211.     gcv.plane_mask = 0xff;
  212.     valuemask = GCFunction | GCPlaneMask;
  213.     w->canvas.xor_gc = XtGetGC((Widget) w, valuemask, &gcv);
  214.  
  215.     gcv.background = w->core.background_pixel;
  216.     gcv.fill_style = FillSolid;
  217.     valuemask = GCBackground | GCFillStyle;
  218.     w->canvas.normal_gc = XtGetGC((Widget) w, valuemask, &gcv);
  219.  
  220.     gcv.foreground = BLACK;
  221.     gcv.background = WHITE;
  222.     gcv.fill_style = FillTiled;
  223.     gcv.tile = XmuCreateStippledPixmap(screen, BlackPixelOfScreen(screen),
  224.       WhitePixelOfScreen(screen), (unsigned) DefaultDepthOfScreen(screen));
  225.  
  226.     valuemask = GCForeground | GCBackground | GCTile | GCFillStyle;
  227.     w->canvas.clear_gc = XtGetGC((Widget) w, valuemask, &gcv);
  228.  
  229.     /* Call CanvasResize() to initialize pixmap at creation time since the
  230.      * CanvasRedisplay() will be called before CanvasResize() would.   */
  231.     (*XtClass(widget)->core_class.resize) (widget);
  232. }
  233.  
  234.  
  235. static void CanvasResize(widget)
  236. Widget widget;
  237. {
  238.     CanvasWidget w = (CanvasWidget) widget;
  239.     Display *dpy = XtDisplay(w);
  240.     Window window = XtWindow(w);
  241.     static unsigned int height = 0, width = 0;
  242.  
  243.     if (XtIsRealized(widget) &&
  244.       (height != w->core.height || width != w->core.width))
  245.     {
  246.     height = w->core.height;
  247.     width = w->core.width;
  248.  
  249.     CanvasChangeSize(widget);
  250.  
  251.     MakeTasksStale();
  252.     WasteTasks();
  253.  
  254.     if (w->canvas.pixmap)
  255.         XFreePixmap(dpy, w->canvas.pixmap);
  256.  
  257.     w->canvas.pixmap = XCreatePixmap(dpy, window, width, height,
  258.       w->core.depth);
  259.     if (!w->canvas.pixmap)
  260.     {
  261.         eprintf("Insufficient space for pixmap");
  262.         abort();
  263.     }
  264.  
  265.     XFillRectangle(dpy, w->canvas.pixmap, w->canvas.clear_gc,
  266.       0, 0, width, height);
  267.     }
  268. }
  269.  
  270.  
  271. /*ARGSUSED*/
  272. static void CanvasRedisplay(widget, event, region)
  273. Widget widget;
  274. XExposeEvent *event;
  275. Region region;            /* unused */
  276. {
  277.     CanvasWidget w = (CanvasWidget) widget;
  278.     Display *dpy = XtDisplay(w);
  279.     Window window = XtWindow(w);
  280.  
  281.     /* Reset any previously drawn zoom box */
  282.     CanvasMoveBox(w, -1, -1, -1, -1);
  283.  
  284.     if (XtIsRealized(widget) == False)
  285.     return;
  286.  
  287.     if (event == NULL)
  288.     XCopyArea(dpy, w->canvas.pixmap, window, w->canvas.normal_gc,
  289.       0, 0, w->core.width, w->core.height, 0, 0);
  290.     else
  291.     XCopyArea(dpy, w->canvas.pixmap, window, w->canvas.normal_gc,
  292.       event->x, event->y, (unsigned) event->width,
  293.       (unsigned) event->height, event->x, event->y);
  294. }
  295.  
  296.  
  297. static void CanvasRegister(widget, resp, offset)
  298. Widget widget;
  299. RegistrationResponse *resp;
  300. unsigned long *offset;
  301. {
  302.     CanvasWidget w = (CanvasWidget) widget;
  303.     Display *dpy = XtDisplay(w);
  304.     Task *task;
  305.     extern Widget menu;
  306.     extern void ByteSwapLong();
  307.     extern void DroneRegister();
  308.     char hostname[32];
  309.  
  310.     /* Copy hostname before we get a chance to byteswap */
  311.     strcpy(hostname, resp->hostname);
  312.  
  313.     if (resp->byte_order != 0x11223344)
  314.     ByteSwapLong((char *) resp, sizeof(RegistrationResponse));
  315.  
  316.     DroneRegister(resp->window, hostname);
  317.  
  318.     task = AllocTask();
  319.     task->window = resp->window;
  320.  
  321.     if (w->canvas.finished == True || w->canvas.paused == True)
  322.     {
  323.     /* If the widget is not currently drawing, add the new drone to the
  324.      * wait list. */
  325.     TaskNQ(waitQ, task);
  326. #ifdef DEBUG
  327.     dprintf("Enqueued new drone (window=0x%x) on wait list\n",
  328.       task->window);
  329. #endif
  330.     }
  331.     else
  332.     {
  333.     /* If the widget is actively drawing, give the drone a task and send
  334.      * it on its way. */
  335.     CanvasFillTask(widget, task);
  336.     TaskNQ(runQ, task);
  337.     if (SendTask(task) == False)
  338.         eprintf("SendTask() failed\n");
  339. #ifdef DEBUG
  340.     dprintf("Enqueued new drone (window=0x%x) on run list\n",
  341.       task->window);
  342. #endif
  343.     }
  344.  
  345.     /* select for DestroyNotify events on the drone's window */
  346.     XSelectInput(dpy, task->window, StructureNotifyMask);
  347.  
  348.     /* unstipple draw & redraw menu items when 1st drone added */
  349.     if (w->canvas.num_drones++ == 0)
  350.     {
  351.     (void) MenuChangeSensitivity(menu, MENU_DRAW_ITEM, True);
  352.     (void) MenuChangeSensitivity(menu, MENU_REDRAW_ITEM, True);
  353.     }
  354.     *offset += PAD(sizeof(RegistrationResponse));
  355. }
  356.  
  357.  
  358. #define CHEEZY_OPTIMIZATION
  359.  
  360. void CanvasImageResponse(widget, resp, offset)
  361. Widget widget;
  362. ImageResponse *resp;
  363. unsigned long *offset;
  364. {
  365.     CanvasWidget w = (CanvasWidget) widget;
  366.     Display *dpy = XtDisplay(w);
  367.     Window window = XtWindow(w);
  368.     Task *task;
  369. #ifdef CHEEZY_OPTIMIZATION
  370.     int ii;
  371.     char *ptr;
  372. #else
  373.     int x, y;
  374. #endif
  375.     unsigned char *value;
  376.     int num_colors = w->canvas.num_colors;
  377.     long width = resp->width;
  378.     long height = resp->height;
  379.  
  380.     if (resp->byte_order != 0x11223344)
  381.     {
  382. #ifdef DEBUG
  383.     dprintf("ImageResponse\n");
  384.     dprintf("  byte_order:   0x%x\n", resp->byte_order);
  385.     dprintf("        type:   %d\n", resp->type);
  386.     dprintf("      serial:   %d\n", resp->serial);
  387.     dprintf("       width:   %d\n", resp->width);
  388.     dprintf("      height:   %d\n", resp->height);
  389.     dprintf("    max_iter:   %d\n", resp->max_iter);
  390. #endif
  391.     eprintf("Incorrect byte ordering (0x%x)\n", resp->byte_order);
  392.     abort();
  393.     }
  394.     if ((task = TaskDQ(runQ, (Window) NULL, resp->serial)) != NULL)
  395.     {
  396. #ifdef DEBUG
  397.     dprintf("Task located on the runQ\n");
  398. #endif
  399.     /* dp points past the ImageResponse header */
  400.     value = (unsigned char *) &resp[1];
  401.  
  402. #ifdef CHEEZY_OPTIMIZATION
  403.     if (num_colors != 256)
  404.         for (ii = height * width, value += ii; ii >= 0; --ii, --value)
  405.         *value %= num_colors;
  406.  
  407.     if (width == w->canvas.image->width)
  408.         (void) memcpy(w->canvas.image->data, (char *) value,
  409.           (int) (height * width));
  410.     else
  411.     {
  412.         /* narrow ImageResponse - copy line at a time */
  413.         ptr = w->canvas.image->data;
  414.         for (ii = height; ii > 0; --ii, value += width,
  415.           ptr += w->canvas.image->width)
  416.         (void) memcpy(ptr, (char *) value, (int) width);
  417.     }
  418. #else
  419.     if (num_colors == 256)
  420.         /* No need to perform modulus */
  421.         for (y = 0; y < height; ++y)
  422.         for (x = 0; x < width; ++x)
  423.             XPutPixel(w->canvas.image, x, y, *(value++));
  424.     else
  425.         for (y = 0; y < height; ++y)
  426.         for (x = 0; x < width; ++x)
  427.             XPutPixel(w->canvas.image, x, y, *(value++) %
  428.               num_colors);
  429. #endif
  430.  
  431.     XPutImage(dpy, w->canvas.pixmap, w->canvas.normal_gc,
  432.       w->canvas.image, 0, 0, task->x, task->y, (unsigned) width,
  433.       (unsigned) height);
  434.  
  435.     XCopyArea(dpy, w->canvas.pixmap, window,
  436.       w->canvas.normal_gc, task->x, task->y, (unsigned) width,
  437.       (unsigned) height, task->x, task->y);
  438.  
  439.     /* We need to save everything on the next zoom */
  440.     w->canvas.needs_saving = True;
  441.     }
  442.     else
  443.     {
  444.     /* Was this a stale response from a drone? */
  445.     if ((task = TaskDQ(staleQ, (Window) NULL, resp->serial)) == NULL)
  446.     {
  447.         eprintf("Received bad ImageResponse\n");
  448.         abort();
  449.     }
  450. #ifdef DEBUG
  451.     dprintf("Task located on the staleQ\n");
  452. #endif
  453.     }
  454.  
  455.     if (w->canvas.finished == False && w->canvas.paused == False)
  456.     {
  457.     CanvasFillTask(widget, task);
  458.  
  459. #ifdef DEBUG
  460.     dprintf("Sending following task to run queue:\n");
  461.     PrintTask(task);
  462. #endif
  463.  
  464.     TaskNQ(runQ, task);
  465.     if (SendTask(task) == False)
  466.         eprintf("SendTask() failed\n");
  467.     }
  468.     else
  469.     {
  470.  
  471. #ifdef DEBUG
  472.     dprintf("Sending drone 0x%x to the wait queue:\n", task->window);
  473. #endif
  474.  
  475.     TaskNQ(waitQ, task);
  476.     }
  477.     *offset += PAD(DATA_SIZE(width, height));
  478. }
  479.  
  480.  
  481. /***********************************************************/
  482. /******************** Action Procedures ********************/
  483. /***********************************************************/
  484.  
  485.  
  486. /*ARGSUSED*/
  487. static void CanvasUnregister(widget, event, params, num_params)
  488. Widget widget;
  489. XEvent *event;
  490. String *params;            /* unused */
  491. Cardinal *num_params;        /* unused */
  492. {
  493.     CanvasWidget w = (CanvasWidget) widget;
  494.     XDestroyWindowEvent *ev = (XDestroyWindowEvent *) event;
  495.     Task *task;
  496.     extern Widget menu;
  497.     extern void DroneUnregister();
  498.  
  499. #ifdef DEBUG
  500.     dprintf("Drone (window=0x%x) has terminated:\n", ev->window);
  501. #endif
  502.  
  503.     /* update the drone control dialogbox */
  504.     DroneUnregister(ev->window);
  505.  
  506.     /* remove task for window which was destroyed. */
  507.  
  508.     if ((task = TaskDQ(runQ, ev->window, (long) NULL)) != NULL)
  509.     {
  510.     /* Window's task structure was on the run list. The task is placed on
  511.      * the zombie list here for later re-assigned to a new drone */
  512.  
  513. #ifdef DEBUG
  514.     dprintf("Task removed from run list\n");
  515. #endif
  516.  
  517.     TaskNQ(zombieQ, task);
  518.     }
  519.     else if ((task = TaskDQ(waitQ, ev->window, (long) NULL)) != NULL)
  520.     {
  521.     /* Window's task structure was on the wait list. Since it wasn't doing
  522.      * anything (but waiting) when it terminated, we can simply purge it
  523.      * from our lists. */
  524.  
  525. #ifdef DEBUG
  526.     dprintf("Task removed from wait list\n");
  527. #endif
  528.  
  529.     FreeTask(task);
  530.     }
  531.     else if ((task = TaskDQ(staleQ, ev->window, (long) NULL)) != NULL)
  532.     {
  533.     /* Window's task structure was on the stale list. Since it was
  534.      * calculating a stale task when it terminated, we can simply purge it
  535.      * from our lists. */
  536.  
  537. #ifdef DEBUG
  538.     dprintf("Task removed from stale list\n");
  539. #endif
  540.  
  541.     FreeTask(task);
  542.     }
  543.     else
  544.     {
  545.     eprintf("Task structure for drone (window=0x%x) not found!\n",
  546.       ev->window);
  547.     abort();
  548.     }
  549.  
  550.     /* stipple draw and redraw menu items when last drone removed */
  551.     if (--w->canvas.num_drones == 0)
  552.     {
  553.     (void) MenuChangeSensitivity(menu, MENU_DRAW_ITEM, False);
  554.     (void) MenuChangeSensitivity(menu, MENU_REDRAW_ITEM, False);
  555.     }
  556. }
  557.  
  558.  
  559. /*ARGSUSED*/
  560. static void CanvasMessage(widget, event, params, num_params)
  561. Widget widget;
  562. XEvent *event;
  563. String *params;            /* unused */
  564. Cardinal *num_params;        /* unused */
  565. {
  566.     XPropertyEvent *ev = (XPropertyEvent *) event;
  567.     char *msg;
  568.     unsigned long msg_len;
  569.     unsigned long offset;
  570.     GenericMessage generic;
  571.     static int read_ahead = 0;
  572.  
  573.     if (ev->atom != MAILBOX)
  574.     {
  575.     /* must have been some other property (handle as usual) */
  576.     return;
  577.     }
  578.  
  579.     if (ev->state != PropertyNewValue)
  580.     {
  581.     /* generally, ignore the PropertyDelete events. they are generated by
  582.      * our destructive reads  on the property and since we can't stop them
  583.      * we must simply ignore them. */
  584.     return;
  585.     }
  586.  
  587.     /* The counter read_ahead will become greater than zero when multiple
  588.      * messages are read from the property in response to a single
  589.      * PropertyNotify event.  By keeping track of how many messages we have
  590.      * "read ahead" of the events, we do not attempt to read from an empty
  591.      * property. */
  592.     if (read_ahead-- > 0)
  593.     return;
  594.  
  595.     if (RecvMsg(&msg, &msg_len) == False)
  596.     {
  597.     eprintf("RecvMsg() failed\n");
  598.     return;
  599.     }
  600.  
  601.     offset = 0;
  602.     do
  603.     {
  604.     (void) memcpy((char *) &generic, &msg[offset], sizeof(GenericMessage));
  605.     if (generic.byte_order != 0x11223344)
  606.         ByteSwapLong((char *) &generic, 2 * sizeof(long));
  607.  
  608.     if (generic.type == REGISTRATION_RESPONSE)
  609.         CanvasRegister(widget, (RegistrationResponse *) & msg[offset],
  610.           &offset);
  611.     else if (generic.type == IMAGE_RESPONSE)
  612.         CanvasImageResponse(widget, (ImageResponse *) & msg[offset],
  613.           &offset);
  614.     else
  615.     {
  616.         eprintf("Unknown message type: %d\n", generic.type);
  617.         abort();
  618.     }
  619.     ++read_ahead;
  620.     } while (offset < msg_len);
  621.     free(msg);
  622. }
  623.  
  624.  
  625. static void CanvasMoveBox(w, x1, y1, x2, y2)
  626. CanvasWidget w;
  627. int x1, y1;
  628. int x2, y2;
  629. {
  630.     Display *dpy = XtDisplay(w);
  631.     Window window = XtWindow(w);
  632.     static int last_x1 = -1, last_y1 = -1;
  633.     static int last_x2 = -1, last_y2 = -1;
  634.     static Boolean prev_box = False;
  635.     int tmp;
  636.  
  637.     if (x1 == last_x1 && y1 == last_y1 && x2 == last_x2 && y2 == last_y2)
  638.     return;
  639.  
  640.     if (prev_box)
  641.     XDrawRectangle(dpy, window, w->canvas.xor_gc, last_x1, last_y1,
  642.       (unsigned) (last_x2 - last_x1), (unsigned) (last_y2 - last_y1));
  643.  
  644.     /* Make sure x1 < x2 */
  645.     if (x1 > x2)
  646.     {
  647.     tmp = x1;
  648.     x1 = x2;
  649.     x2 = tmp;
  650.     }
  651.  
  652.     /* Make sure y1 < y2 */
  653.     if (y1 > y2)
  654.     {
  655.     tmp = y1;
  656.     y1 = y2;
  657.     y2 = tmp;
  658.     }
  659.  
  660.     last_x1 = x1;
  661.     last_y1 = y1;
  662.     last_x2 = x2;
  663.     last_y2 = y2;
  664.  
  665.     if (x1 != x2 && y1 != y2)
  666.     {
  667.     XDrawRectangle(dpy, window, w->canvas.xor_gc, x1, y1,
  668.       (unsigned) (x2 - x1), (unsigned) (y2 - y1));
  669.     prev_box = True;
  670.     }
  671.     else
  672.     {
  673.     prev_box = False;
  674.     }
  675. }
  676.  
  677.  
  678. /*ARGSUSED*/
  679. static void CanvasBtnDown(widget, event, params, num_params)
  680. Widget widget;
  681. XEvent *event;
  682. String *params;            /* unused */
  683. Cardinal *num_params;        /* unused */
  684. {
  685.     CanvasWidget w = (CanvasWidget) widget;
  686.     XButtonEvent *ev = (XButtonEvent *) & event->xbutton;
  687.  
  688.  
  689.     w->canvas.x_from = ev->x;
  690.     w->canvas.y_from = ev->y;
  691.  
  692.     CanvasMoveBox(w, w->canvas.x_from, w->canvas.y_from,
  693.       w->canvas.x_from, w->canvas.y_from);
  694. }
  695.  
  696.  
  697. /*ARGSUSED*/
  698. static void CanvasBtnUp(widget, event, params, num_params)
  699. Widget widget;
  700. XEvent *event;
  701. String *params;            /* unused */
  702. Cardinal *num_params;        /* unused */
  703. {
  704.     CanvasWidget w = (CanvasWidget) widget;
  705.     XButtonEvent *ev = (XButtonEvent *) & event->xbutton;
  706.     int tmp;
  707.     extern Widget menu;
  708.  
  709.     w->canvas.x_to = IN_BOUNDS_X(w, ev->x);
  710.     w->canvas.y_to = IN_BOUNDS_Y(w, ev->y);
  711.     CanvasMoveBox(w, w->canvas.x_from, w->canvas.y_from, w->canvas.x_to,
  712.       w->canvas.y_to);
  713.  
  714.     /* Make sure the final points are in "proper" order */
  715.  
  716.     /* Make sure w->canvas.x_from < w->canvas.x_to */
  717.     if (w->canvas.x_from > w->canvas.x_to)
  718.     {
  719.     tmp = w->canvas.x_from;
  720.     w->canvas.x_from = w->canvas.x_to;
  721.     w->canvas.x_to = tmp;
  722.     }
  723.  
  724.     /* Make sure w->canvas.y_from < w->canvas.y_to */
  725.     if (w->canvas.y_from > w->canvas.y_to)
  726.     {
  727.     tmp = w->canvas.y_from;
  728.     w->canvas.y_from = w->canvas.y_to;
  729.     w->canvas.y_to = tmp;
  730.     }
  731.  
  732.     /* stipple zoom menu item if NULL zoom range */
  733.     if (w->canvas.y_from == w->canvas.y_to
  734.       && w->canvas.x_from == w->canvas.x_to)
  735.     (void) MenuChangeSensitivity(menu, MENU_ZOOM_ITEM, False);
  736.     else
  737.     (void) MenuChangeSensitivity(menu, MENU_ZOOM_ITEM, True);
  738. }
  739.  
  740.  
  741. /*ARGSUSED*/
  742. static void CanvasMotion(widget, event, params, num_params)
  743. Widget widget;
  744. XEvent *event;
  745. String *params;            /* unused */
  746. Cardinal *num_params;        /* unused */
  747. {
  748.     CanvasWidget w = (CanvasWidget) widget;
  749.     XMotionEvent *ev = (XMotionEvent *) & event->xmotion;
  750.  
  751.     CanvasMoveBox(w, w->canvas.x_from, w->canvas.y_from, IN_BOUNDS_X(w, ev->x),
  752.       IN_BOUNDS_Y(w, ev->y));
  753. }
  754.  
  755.  
  756. /*ARGSUSED*/
  757. static void CanvasQuit(widget, event, params, num_params)
  758. Widget widget;            /* unused */
  759. XEvent *event;            /* unused */
  760. String *params;            /* unused */
  761. Cardinal *num_params;        /* unused */
  762. {
  763.     extern void CleanupAndDie();
  764.  
  765.     CleanupAndDie();
  766. }
  767.  
  768.  
  769. /*ARGSUSED*/
  770. static void CanvasReverse(widget, event, params, num_params)
  771. Widget widget;            /* unused */
  772. XEvent *event;            /* unused */
  773. String *params;            /* unused */
  774. Cardinal *num_params;        /* unused */
  775. {
  776.     extern Widget colormap_dialogbox;
  777.     extern Rotation rotation_dir;
  778.  
  779.     if (rotation_dir == forward)
  780.     rotation_dir = backward;
  781.     else
  782.     rotation_dir = forward;
  783. }
  784.  
  785.  
  786. /*ARGSUSED*/
  787. static void CanvasStartStop(widget, event, params, num_params)
  788. Widget widget;            /* unused */
  789. XEvent *event;            /* unused */
  790. String *params;            /* unused */
  791. Cardinal *num_params;        /* unused */
  792. {
  793.     extern Boolean rotating;
  794.  
  795.     rotating = !rotating;
  796. }
  797.  
  798.  
  799. /***********************************************************/
  800. /******************** Public Procedures ********************/
  801. /***********************************************************/
  802.  
  803.  
  804. void CanvasClear(widget)
  805. Widget widget;
  806. {
  807.     CanvasWidget w = (CanvasWidget) widget;
  808.     Display *dpy = XtDisplay(w);
  809.     Window window = XtWindow(w);
  810.     extern int iteration_limit;
  811.     extern int task_width;
  812.     extern int task_height;
  813.  
  814.     /* Reset any previously drawn zoom box */
  815.     CanvasMoveBox(w, -1, -1, -1, -1);
  816.  
  817.     w->canvas.task_x = 0;
  818.     w->canvas.task_y = 0;
  819.     w->canvas.finished = False;
  820.     w->canvas.paused = False;
  821.  
  822.     /* New iteration limit should now be observed */
  823.     w->canvas.curr_limit = iteration_limit;
  824.  
  825.     /* Check and see if a new task size should now be observed */
  826.     CanvasChangeTaskSize(widget, (unsigned int) task_width,
  827.       (unsigned int) task_height);
  828.  
  829.     XFillRectangle(dpy, w->canvas.pixmap, w->canvas.clear_gc,
  830.       0, 0, w->core.width, w->core.height);
  831.  
  832.     XCopyArea(dpy, w->canvas.pixmap, window, w->canvas.normal_gc,
  833.       0, 0, w->core.width, w->core.height, 0, 0);
  834. }
  835.  
  836.  
  837. void CanvasZoom(widget)
  838. Widget widget;
  839. {
  840.     CanvasWidget w = (CanvasWidget) widget;
  841.     double p_mid, q_mid;
  842.     double x_zoom, y_zoom;
  843.     int x_avg, y_avg;
  844.     int width;
  845.     int height;
  846.  
  847. #ifdef DEBUG
  848.     dprintf("p_lo=%.10G p_hi=%.10G p_inc=%.10G\n",
  849.       w->canvas.p_lo, w->canvas.p_hi, w->canvas.p_inc);
  850.  
  851.     dprintf("q_lo=%.10G q_hi=%.10G q_inc=%.10G\n",
  852.       w->canvas.q_lo, w->canvas.q_hi, w->canvas.q_inc);
  853. #endif
  854.  
  855.     x_avg = (w->canvas.x_from + w->canvas.x_to) / 2;
  856.     y_avg = (w->canvas.y_from + w->canvas.y_to) / 2;
  857.  
  858.     p_mid = w->canvas.p_lo + w->canvas.p_inc * x_avg;
  859.     q_mid = w->canvas.q_lo + w->canvas.q_inc * y_avg;
  860.  
  861.     width = w->canvas.x_to - w->canvas.x_from;
  862.     height = w->canvas.y_to - w->canvas.y_from;
  863.  
  864.     x_zoom = (double) w->core.width / width;
  865.     y_zoom = (double) w->core.height / height;
  866.  
  867.     if (w->canvas.retain_aspect_ratio)
  868.     x_zoom = y_zoom = MIN(x_zoom, y_zoom);
  869.  
  870.     w->canvas.p_inc /= x_zoom;
  871.     w->canvas.q_inc /= y_zoom;
  872.  
  873.     w->canvas.p_lo = p_mid - w->canvas.p_inc * w->core.width / 2;
  874.     w->canvas.q_lo = q_mid - w->canvas.q_inc * w->core.height / 2;
  875.  
  876.     w->canvas.p_hi = w->canvas.p_lo + w->canvas.p_inc * (w->core.width - 1);
  877.     w->canvas.q_hi = w->canvas.q_lo + w->canvas.q_inc * (w->core.height - 1);
  878.  
  879. #ifdef DEBUG
  880.     dprintf("p_lo=%.10G p_hi=%.10G p_inc=%.10G\n",
  881.       w->canvas.p_lo, w->canvas.p_hi, w->canvas.p_inc);
  882.  
  883.     dprintf("q_lo=%.10G q_hi=%.10G q_inc=%.10G\n",
  884.       w->canvas.q_lo, w->canvas.q_hi, w->canvas.q_inc);
  885. #endif
  886. }
  887.  
  888.  
  889. void CanvasChangeSize(widget)
  890. Widget widget;
  891. {
  892.     CanvasWidget w = (CanvasWidget) widget;
  893.     double p_mid, q_mid;
  894.  
  895. #ifdef DEBUG
  896.     dprintf("p_lo=%.10G p_hi=%.10G p_inc=%.10G\n",
  897.       w->canvas.p_lo, w->canvas.p_hi, w->canvas.p_inc);
  898.  
  899.     dprintf("q_lo=%.10G q_hi=%.10G q_inc=%.10G\n",
  900.       w->canvas.q_lo, w->canvas.q_hi, w->canvas.q_inc);
  901. #endif
  902.  
  903.     p_mid = (w->canvas.p_hi + w->canvas.p_lo) / 2;
  904.     q_mid = (w->canvas.q_hi + w->canvas.q_lo) / 2;
  905.  
  906.     w->canvas.p_inc = (double) (w->canvas.p_hi - w->canvas.p_lo) /
  907.       (w->core.width - 1);
  908.     w->canvas.q_inc = (double) (w->canvas.q_hi - w->canvas.q_lo) /
  909.       (w->core.height - 1);
  910.  
  911.     if (w->canvas.retain_aspect_ratio)
  912.     w->canvas.p_inc = w->canvas.q_inc =
  913.       MAX(w->canvas.p_inc, w->canvas.q_inc);
  914.  
  915.     w->canvas.p_lo = p_mid - w->canvas.p_inc * w->core.width / 2;
  916.     w->canvas.q_lo = q_mid - w->canvas.q_inc * w->core.height / 2;
  917.  
  918.     w->canvas.p_hi = w->canvas.p_lo + w->canvas.p_inc * (w->core.width - 1);
  919.     w->canvas.q_hi = w->canvas.q_lo + w->canvas.q_inc * (w->core.height - 1);
  920.  
  921. #ifdef DEBUG
  922.     dprintf("p_lo=%.10G p_hi=%.10G p_inc=%.10G\n",
  923.       w->canvas.p_lo, w->canvas.p_hi, w->canvas.p_inc);
  924.  
  925.     dprintf("q_lo=%.10G q_hi=%.10G q_inc=%.10G\n",
  926.       w->canvas.q_lo, w->canvas.q_hi, w->canvas.q_inc);
  927. #endif
  928.  
  929.     w->canvas.task_x = 0;
  930.     w->canvas.task_y = 0;
  931.     w->canvas.finished = True;
  932. }
  933.  
  934.  
  935. void CanvasFillTask(widget, task)
  936. Widget widget;
  937. Task *task;
  938. {
  939.     CanvasWidget w = (CanvasWidget) widget;
  940.     Task *unfinished;
  941.     Task *zombie;
  942.     Window saved_window;
  943.     extern int task_width;
  944.     extern int task_height;
  945.  
  946.     /* We need to save everything on the next zoom */
  947.     w->canvas.needs_saving = True;
  948.  
  949.     /* Check first to see if any unfinished task should be completed */
  950.     if ((unfinished = TaskDQ(unfinishedQ, (Window) NULL, (long) NULL)) != NULL)
  951.     {
  952.     /* save the window from the task structure */
  953.     saved_window = task->window;
  954.  
  955.     /* copy in the info from the unfinished task */
  956.     (void) memcpy((char *) task, (char *) unfinished, sizeof(Task));
  957.  
  958.     /* restore original window to task */
  959.     task->window = saved_window;
  960.  
  961.     /* don't need this task anymore */
  962.     FreeTask(unfinished);
  963.     return;
  964.     }
  965.  
  966.     /* Check next to see if any zombie task should be rejuvinated */
  967.     if ((zombie = TaskDQ(zombieQ, (Window) NULL, (long) NULL)) != NULL)
  968.     {
  969.     /* save the window from the task structure */
  970.     saved_window = task->window;
  971.  
  972.     /* copy in the info from the zombie task */
  973.     (void) memcpy((char *) task, (char *) zombie, sizeof(Task));
  974.  
  975.     /* restore original window to task */
  976.     task->window = saved_window;
  977.  
  978.     /* R.I.P. ex-zombie */
  979.     FreeTask(zombie);
  980.     return;
  981.     }
  982.  
  983.     /* Otherwise, fill in the info for the next task... */
  984.  
  985.     task->serial = ++(w->canvas.serial);
  986.     task->limit = w->canvas.curr_limit;
  987.     task->method = w->canvas.method;
  988.  
  989.     /* If we are at the start of the canvas, check to see if we should update
  990.      * the current task width and height. */
  991.     if (w->canvas.task_x == 0 && w->canvas.task_y == 0)
  992.     CanvasChangeTaskSize(widget, (unsigned int) task_width,
  993.       (unsigned int) task_height);
  994.  
  995.     task->width = MIN(w->canvas.curr_task_width, w->core.width -
  996.       w->canvas.task_x);
  997.     task->height = MIN(w->canvas.curr_task_height, w->core.height -
  998.       w->canvas.task_y);
  999.  
  1000.     task->x = w->canvas.task_x;
  1001.     task->y = w->canvas.task_y;
  1002.     task->p_lo = w->canvas.p_lo + task->x * w->canvas.p_inc;
  1003.     task->p_hi = task->p_lo + (task->width - 1) * w->canvas.p_inc;
  1004.     task->q_lo = w->canvas.q_lo + task->y * w->canvas.q_inc;
  1005.     task->q_hi = task->q_lo + (task->height - 1) * w->canvas.q_inc;
  1006.  
  1007.     w->canvas.finished = False;
  1008.  
  1009.     w->canvas.task_x += w->canvas.curr_task_width;
  1010.     if (w->canvas.task_x >= w->core.width)
  1011.     {
  1012.     w->canvas.task_x = 0;
  1013.     w->canvas.task_y += w->canvas.curr_task_height;
  1014.     if (w->canvas.task_y >= w->core.height)
  1015.     {
  1016.         /* Reset for next drawing session */
  1017.         w->canvas.task_x = 0;
  1018.         w->canvas.task_y = 0;
  1019.         w->canvas.finished = True;
  1020.     }
  1021.     }
  1022. }
  1023.  
  1024.  
  1025. void CanvasChangeTaskSize(widget, width, height)
  1026. Widget widget;
  1027. unsigned int width;
  1028. unsigned int height;
  1029. {
  1030.     CanvasWidget w = (CanvasWidget) widget;
  1031.     Display *dpy = XtDisplay(w);
  1032.     Screen *screen = XtScreen(w);
  1033.  
  1034.     /* no need to update if nothing changed */
  1035.     if (w->canvas.curr_task_width == width &&
  1036.       w->canvas.curr_task_height == height)
  1037.     return;
  1038.  
  1039.     if (w->canvas.image)
  1040.     XDestroyImage(w->canvas.image);
  1041.  
  1042.     w->canvas.image = XCreateImage(dpy, DefaultVisualOfScreen(screen),
  1043.       DefaultDepthOfScreen(screen), ZPixmap, 0, malloc(width * height),
  1044.       width, height, 8, 0);
  1045.  
  1046.     w->canvas.curr_task_width = width;
  1047.     w->canvas.curr_task_height = height;
  1048. }
  1049.  
  1050.  
  1051. void CanvasChangeRetainAspect(widget)
  1052. Widget widget;
  1053. {
  1054.     CanvasWidget w = (CanvasWidget) widget;
  1055.  
  1056.     w->canvas.retain_aspect_ratio = !w->canvas.retain_aspect_ratio;
  1057. }
  1058.  
  1059.  
  1060. void CanvasUnpause(widget)
  1061. Widget widget;
  1062. {
  1063.     CanvasWidget w = (CanvasWidget) widget;
  1064.  
  1065.     w->canvas.paused = False;
  1066. }
  1067.  
  1068.  
  1069. void CanvasTouchCanvas(widget)
  1070. Widget widget;
  1071. {
  1072.     CanvasWidget w = (CanvasWidget) widget;
  1073.  
  1074.     w->canvas.needs_saving = TRUE;
  1075. }
  1076.  
  1077.  
  1078. Boolean CanvasNeedsSaving(widget)
  1079. Widget widget;
  1080. {
  1081.     CanvasWidget w = (CanvasWidget) widget;
  1082.  
  1083.     return (w->canvas.needs_saving);
  1084. }
  1085.  
  1086.  
  1087. Boolean CanvasSaveImage(fd, widget)
  1088. int fd;
  1089. Widget widget;
  1090. {
  1091.     CanvasWidget w = (CanvasWidget) widget;
  1092.     Display *dpy = XtDisplay(w);
  1093.     XImage *image;
  1094.     int image_height, image_width;
  1095.     int curr_y = 0;
  1096.     XWindowAttributes win_attrib;
  1097.     XWDFileHeader header;
  1098.     XColor *colors;
  1099.     char *name = "Chaos-Image";
  1100.     int name_len = strlen(name) + 1;
  1101.     unsigned long swap_test = 1;
  1102.     Boolean header_written = False;
  1103.     int ii;
  1104.     extern void ByteSwapShort();
  1105.     extern void ByteSwapLong();
  1106.  
  1107.     image_width = w->core.width;
  1108.     image_height = MAX_SAVE_IMAGE_SIZE / image_width;
  1109.     image_height = MIN(image_height, w->core.height);
  1110.  
  1111.     if (!XGetWindowAttributes(dpy, XtWindow(w), &win_attrib))
  1112.     {
  1113.     eprintf("CanvasSaveImage: cannot get attributes\n");
  1114.     return (False);
  1115.     }
  1116.  
  1117.     while (curr_y < w->core.height)
  1118.     {
  1119.     image = XGetImage(dpy, w->canvas.pixmap, 0, curr_y, image_width,
  1120.       image_height, AllPlanes, ZPixmap);
  1121.  
  1122.     if (header_written == False)
  1123.     {
  1124.         header_written = True;
  1125.         colors = GetColors(w->canvas.num_colors);
  1126.  
  1127.         header.header_size = (CARD32) sizeof(header) + name_len;
  1128.         header.file_version = (CARD32) XWD_FILE_VERSION;
  1129.         header.pixmap_format = (CARD32) ZPixmap;
  1130.         header.pixmap_depth = (CARD32) image->depth;
  1131.         header.pixmap_width = (CARD32) w->core.width;
  1132.         header.pixmap_height = (CARD32) w->core.height;
  1133.         header.xoffset = (CARD32) image->xoffset;
  1134.         header.byte_order = (CARD32) image->byte_order;
  1135.         header.bitmap_unit = (CARD32) image->bitmap_unit;
  1136.         header.bitmap_bit_order = (CARD32) image->bitmap_bit_order;
  1137.         header.bitmap_pad = (CARD32) image->bitmap_pad;
  1138.         header.bits_per_pixel = (CARD32) image->bits_per_pixel;
  1139.         header.bytes_per_line = (CARD32) image->bytes_per_line;
  1140.         header.visual_class = (CARD32) win_attrib.visual->class;
  1141.         header.red_mask = (CARD32) win_attrib.visual->red_mask;
  1142.         header.green_mask = (CARD32) win_attrib.visual->green_mask;
  1143.         header.blue_mask = (CARD32) win_attrib.visual->blue_mask;
  1144.         header.bits_per_rgb = (CARD32) win_attrib.visual->bits_per_rgb;
  1145.         header.colormap_entries = (CARD32) win_attrib.visual->map_entries;
  1146.         header.ncolors = w->canvas.num_colors;
  1147.         header.window_width = (CARD32) win_attrib.width;
  1148.         header.window_height = (CARD32) win_attrib.height;
  1149.         header.window_x = 0;
  1150.         header.window_y = 0;
  1151.         header.window_bdrwidth = (CARD32) win_attrib.border_width;
  1152.  
  1153.         if (*(char *) &swap_test)
  1154.         {
  1155.         ByteSwapLong((char *) &header, sizeof(XWDFileHeader));
  1156.         for (ii = 0; ii < w->canvas.num_colors; ++ii)
  1157.         {
  1158.             ByteSwapLong((char *) &colors[ii].pixel, sizeof(long));
  1159.             ByteSwapShort((char *) &colors[ii].red,
  1160.               3 * sizeof(short));
  1161.         }
  1162.         }
  1163.  
  1164.         (void) write(fd, (char *) &header, sizeof(XWDFileHeader));
  1165.         (void) write(fd, name, name_len);
  1166.         (void) write(fd, (char *) colors, sizeof(XColor) *
  1167.           w->canvas.num_colors);
  1168.         free((char *) colors);
  1169.     }
  1170.     (void) write(fd, image->data, image->bytes_per_line * image->height);
  1171.     XDestroyImage(image);
  1172.     curr_y += image_height;
  1173.     if (curr_y + image_height > w->core.height)
  1174.         image_height = w->core.height - curr_y;
  1175.     }
  1176.     return (True);
  1177. }
  1178.  
  1179.  
  1180. Boolean CanvasLoadImage(fd, widget)
  1181. int fd;
  1182. Widget widget;
  1183. {
  1184.     CanvasWidget w = (CanvasWidget) widget;
  1185.     Display *dpy = XtDisplay(w);
  1186.     Screen *screen = XtScreen(w);
  1187.     XImage *image;
  1188.     char *image_data;
  1189.     XWDFileHeader header;
  1190.     XColor *colors;
  1191.     XSizeHints hints;
  1192.     unsigned int image_height, image_width, image_size;
  1193.     int curr_y = 0;
  1194.     int ii;
  1195.     unsigned long swap_test = 1;
  1196.     extern Colormap colormap;
  1197.     extern Widget top_level;
  1198.     extern void ColormapResetControls();
  1199.     Boolean resized = False;
  1200.     extern long lseek();
  1201.  
  1202.     (void) read(fd, (char *) &header, sizeof(XWDFileHeader));
  1203.  
  1204.     if (*(char *) &swap_test)
  1205.     ByteSwapLong((char *) &header, sizeof(XWDFileHeader));
  1206.  
  1207.     /* skip past name (at end of header) */
  1208.     (void) lseek(fd, (long) header.header_size, 0);
  1209.  
  1210.     colors = (XColor *) malloc((unsigned) (sizeof(XColor) * header.ncolors));
  1211.     (void) read(fd, (char *) colors, (int) (sizeof(XColor) * header.ncolors));
  1212.  
  1213.     if (*(char *) &swap_test)
  1214.     for (ii = 0; ii < header.ncolors; ++ii)
  1215.     {
  1216.         ByteSwapLong((char *) &colors[ii].pixel, sizeof(long));
  1217.         ByteSwapShort((char *) &colors[ii].red, 3 * sizeof(short));
  1218.     }
  1219.  
  1220.     PutColors(colors, (int) header.ncolors);
  1221.     StoreColormap(dpy, colormap);
  1222.     (void) free((char *) colors);
  1223.  
  1224.     /* since a new colormap has been loaded, remove the text from the colormap
  1225.      * dialogbox text input field and reset listbox. */
  1226.     ColormapResetControls();
  1227.  
  1228.     if (header.pixmap_width != w->core.width ||
  1229.       header.pixmap_height != w->core.height)
  1230.     {
  1231.     /* We need to resize the before we can load in the image */
  1232.     hints.width = header.pixmap_width;
  1233.     hints.height = header.pixmap_height;
  1234.     hints.flags = USSize;
  1235.  
  1236.     XSetNormalHints(dpy, XtWindow(top_level), &hints);
  1237.  
  1238.     XtConfigureWidget(top_level, top_level->core.x, top_level->core.y,
  1239.       header.pixmap_width, header.pixmap_height,
  1240.       top_level->core.border_width);
  1241.  
  1242.     resized = True;
  1243.     }
  1244.  
  1245.     image_width = header.pixmap_width;
  1246.     image_height = MAX_SAVE_IMAGE_SIZE / image_width;
  1247.     image_height = MIN(image_height, header.pixmap_height);
  1248.     image_size = PAD(image_width) * image_height;
  1249.  
  1250.     if ((image_data = malloc(image_size)) == NULL)
  1251.     {
  1252.     eprintf("Cannot malloc %d bytes\n", image_size);
  1253.     return (False);
  1254.     }
  1255.  
  1256.     image = XCreateImage(dpy, DefaultVisualOfScreen(screen),
  1257.       DefaultDepthOfScreen(screen), ZPixmap, 0, image_data,
  1258.       image_width, image_height, 32, 0);
  1259.  
  1260.     while (curr_y < w->core.height)
  1261.     {
  1262.     if (read(fd, image->data, image->bytes_per_line * image->height) < 0)
  1263.     {
  1264.         XDestroyImage(image);
  1265.         return (False);
  1266.     }
  1267.  
  1268.     XPutImage(dpy, w->canvas.pixmap, w->canvas.normal_gc, image,
  1269.       0, 0, 0, curr_y, image_width, image_height);
  1270.  
  1271.     curr_y += image_height;
  1272.     if (curr_y + image_height > w->core.height)
  1273.         image->height = w->core.height - curr_y;
  1274.     }
  1275.     XDestroyImage(image);
  1276.  
  1277.     if (resized == False)
  1278.     /* slap the new pixmap up on the screen */
  1279.     CanvasRedisplay(widget, (XExposeEvent *) NULL, (Region) NULL);
  1280.  
  1281.     return (True);
  1282. }
  1283.  
  1284.  
  1285. Boolean CanvasSaveState(fd, widget)
  1286. int fd;
  1287. Widget widget;
  1288. {
  1289.     CanvasWidget w = (CanvasWidget) widget;
  1290.     CanvasSaveStateStruct package;
  1291.     unsigned long swap_test = 1;
  1292.  
  1293.     package.width = (long) w->core.width;
  1294.     package.height = (long) w->core.height;
  1295.     package.task_x = (long) w->canvas.task_x;
  1296.     package.task_y = (long) w->canvas.task_y;
  1297.     package.limit = (long) w->canvas.curr_limit;
  1298.     package.finished = (long) w->canvas.finished;
  1299.     package.curr_task_width = (long) w->canvas.curr_task_width;
  1300.     package.curr_task_height = (long) w->canvas.curr_task_height;
  1301.  
  1302.     /* do the necessary swapping before laying in the strings */
  1303.     if (*(char *) &swap_test)
  1304.     ByteSwapLong((char *) &package, sizeof(CanvasSaveStateStruct));
  1305.  
  1306.     (void) sprintf(package.p_lo, "%.20G", w->canvas.p_lo);
  1307.     (void) sprintf(package.p_hi, "%.20G", w->canvas.p_hi);
  1308.     (void) sprintf(package.q_lo, "%.20G", w->canvas.q_lo);
  1309.     (void) sprintf(package.q_hi, "%.20G", w->canvas.q_hi);
  1310.     (void) sprintf(package.p_inc, "%.20G", w->canvas.p_inc);
  1311.     (void) sprintf(package.q_inc, "%.20G", w->canvas.q_inc);
  1312.  
  1313.     (void) write(fd, (char *) &package, sizeof(CanvasSaveStateStruct));
  1314.     return (True);
  1315. }
  1316.  
  1317.  
  1318. Boolean CanvasLoadState(fd, widget)
  1319. int fd;
  1320. Widget widget;
  1321. {
  1322.     CanvasWidget w = (CanvasWidget) widget;
  1323.     CanvasSaveStateStruct package;
  1324.     unsigned long swap_test = 1;
  1325.     extern void SettingsSetLimit();
  1326.  
  1327.     (void) read(fd, (char *) &package, sizeof(CanvasSaveStateStruct));
  1328.  
  1329.     /* grab the strings before doing the necessary swapping */
  1330.     w->canvas.p_lo = atof(package.p_lo);
  1331.     w->canvas.p_hi = atof(package.p_hi);
  1332.     w->canvas.q_lo = atof(package.q_lo);
  1333.     w->canvas.q_hi = atof(package.q_hi);
  1334.     w->canvas.p_inc = atof(package.p_inc);
  1335.     w->canvas.q_inc = atof(package.q_inc);
  1336.  
  1337.     if (*(char *) &swap_test)
  1338.     ByteSwapLong((char *) &package, sizeof(CanvasSaveStateStruct));
  1339.  
  1340.     w->core.width = (Dimension) package.width;
  1341.     w->core.height = (Dimension) package.height;
  1342.     w->canvas.task_x = (short) package.task_x;
  1343.     w->canvas.task_y = (short) package.task_y;
  1344.     w->canvas.curr_limit = (short) package.limit;
  1345.     w->canvas.finished = (Boolean) package.finished;
  1346.  
  1347.     /* We don't need to save everything again unless we change something */
  1348.     w->canvas.needs_saving = False;
  1349.  
  1350.     /* Pause until told to restart drawing */
  1351.     w->canvas.paused = True;
  1352.  
  1353.     /* Update the current limit in the settings dialogbox */
  1354.     SettingsSetLimit((int) package.limit);
  1355.  
  1356.     CanvasChangeTaskSize(widget, (unsigned int) package.curr_task_width,
  1357.       (unsigned int) package.curr_task_height);
  1358.  
  1359.     return (True);
  1360. }
  1361.