home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / x / volume14 / xpsringies / part01 / xdisp.c < prev   
Encoding:
C/C++ Source or Header  |  1991-08-27  |  38.4 KB  |  1,442 lines

  1. /* xdisp.c -- Xlib display code for xspringies
  2.  * Copyright (C) 1991  Douglas M. DeCarlo
  3.  *
  4.  * This file is part of XSpringies, a mass and spring simulation system for X
  5.  *
  6.  * XSpringies is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; either version 1, or (at your option)
  9.  * any later version.
  10.  *
  11.  * XSpringies is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  * 
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with XSpringies; see the file COPYING.  If not, write to
  18.  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  *
  20.  */
  21.  
  22. #include "defs.h"
  23. #include <X11/Xlib.h>
  24. #include <X11/Xutil.h>
  25. #include <X11/keysym.h>
  26. #include <X11/cursorfont.h>
  27. #include <sys/time.h>
  28. #include "obj.h"
  29. #include "bitmap.h"
  30. #include "title.h"
  31. #include "bfbm.h"
  32.  
  33. /* Mode values */
  34. #define M_EDIT        0
  35. #define M_MASS        1
  36. #define M_SPRING    2
  37.  
  38. /* Behavior function modes */
  39. #define M_BF        3
  40.  
  41. /* Button values */
  42. #define B_CENTER    0
  43. #define B_RESTLEN       1
  44. #define B_SELECTALL     2
  45. #define B_SAVESTATE    3
  46. #define B_RESSTATE    4
  47. #define B_DUPLICATE    5
  48. #define B_INSERTFILE    6
  49. #define B_LOADFILE    7
  50. #define B_SAVEFILE    8
  51. #define B_RESET            9
  52. #define B_QUIT        10
  53.  
  54. #define C_FIXEDMASS    0
  55. #define C_GRIDSNAP    1
  56. #define C_ADAPTIVE_STEP    2
  57. #define C_ACTION    3
  58. #define C_WTOP        4
  59. #define C_WLEFT        5
  60. #define C_WRIGHT    6
  61. #define C_WBOTTOM    7
  62. #define C_SHOWSPRING    8
  63.  
  64. #define S_MASS        0
  65. #define S_ELAS        1
  66. #define S_KSPR        2
  67. #define S_KDAMP        3
  68. #define S_GRAV        4
  69. #define S_GDIR        5
  70. #define S_VISC        6
  71. #define S_STICK        7
  72. #define S_TIMESTEP    8
  73. #define S_GRIDSNAP    9
  74. #define S_PRECISION    10
  75.  
  76. /* Number of previous mouse state saves */
  77. #define MOUSE_PREV    4
  78.  
  79. #define NAIL_SIZE    4
  80.  
  81. /* Number of milliseconds that each drawing takes (to prevent skips) */
  82. #define MIN_DIFFT    3000
  83.  
  84. /* Default highlight color for color screen */
  85. #define HLCOLOR        "green"
  86.  
  87. /* Just in case someone has a 4.2 BSD system (you have my sympathy) */
  88. #ifndef FD_SET
  89. #define fd_set int
  90. #define FD_SET(fd,fdset) (*(fdset) |= (1 << (fd)))
  91. #define FD_CLR(fd,fdset) (*(fdset) &= ~(1 << (fd)))
  92. #define FD_ISSET(fd, fdset) (*(fdset) & (1 << (fd)))
  93. #define FD_ZERO(fdset) (*(fdset) = 0)
  94. #endif
  95.  
  96. /* X globals */
  97. Display *dpy;
  98. Window main_win, text_win, acts_win, draw_win;
  99. GC fggc, bggc, revgc, hlgc, selectboxgc;
  100. GC drawgc, erasegc, selectgc, selectlinegc;
  101. Pixmap draw_pm, acts_pm, startup_pm;
  102. int fcolor, bcolor, hcolor;
  103. int main_wid, main_ht, draw_wid, draw_ht, planes, xfd;
  104.  
  105. /* Other globals */
  106. static int mode = M_EDIT, slid_dt_num, cur_force = 0;
  107. static boolean quitting = FALSE, action = FALSE;
  108.  
  109. t_state mst = {
  110.     1.0, 1.0,
  111.     1.0, 1.0,
  112.     FALSE, TRUE,
  113.     -1,
  114.     { -1, -1, -1, -1 },
  115.     { 10.0, 5.0, 10.0, 10000.0 },
  116.     { 0.0, 2.0, 0.0, 1.0 },
  117.     0.0, 0.0,
  118.     DEF_TSTEP, 1.0,
  119.     FALSE, FALSE,
  120.     20.0,
  121.     TRUE, TRUE, TRUE, TRUE
  122. }, initst, sst;
  123.  
  124. static double cur_grav_max[BF_NUM] = { 10000000.0, 10000000.0, 10000000.0, 10000000.0 };
  125. static double cur_grav_min[BF_NUM] = { 0.0, -10000000.0, -10000000.0, -10000000.0 };
  126. static double cur_misc_max[BF_NUM] = { 360.0, 10000000.0, 1000.0, 1000.0 };
  127. static double cur_misc_min[BF_NUM] = { -360.0, 0.0, 0.0, -0.0 };
  128.  
  129. /* Position of Gravity/Direction sliders */
  130. int force_liney, grav_slidx, grav_slidy, dir_slidx, dir_slidy;
  131.  
  132. /* Mouse recording */
  133. typedef struct {
  134.     int x, y;
  135.     unsigned long t;
  136. } mouse_info;
  137.  
  138. static mouse_info mprev[MOUSE_PREV];
  139. static int moffset = 0;
  140. static void mouse_event();
  141.  
  142. /* File operations globals */
  143. int file_op = F_NONE;
  144. char filename[MAXPATHLEN];
  145.  
  146. /* clean_up: free all of the X objects created
  147.    like pixmaps and gcs
  148.    */
  149. static void clean_up()
  150. {
  151.     /* Free up X objects */
  152.     XFreePixmap(dpy, draw_pm);
  153.     XFreePixmap(dpy, acts_pm);
  154.     XFreePixmap(dpy, startup_pm);
  155.  
  156.     XFreeGC(dpy, fggc);
  157.     XFreeGC(dpy, bggc);
  158.     XFreeGC(dpy, revgc);
  159.     XFreeGC(dpy, hlgc);
  160.     XFreeGC(dpy, selectboxgc);
  161.  
  162.     XFreeGC(dpy, drawgc);
  163.     XFreeGC(dpy, erasegc);
  164.     XFreeGC(dpy, selectgc);
  165.     XFreeGC(dpy, selectlinegc);
  166.     XCloseDisplay(dpy);
  167. }
  168.  
  169. /* fatal(msg): print out the message msg, clean up,
  170.    and exit
  171.    */
  172. void fatal(msg)
  173. char *msg;
  174. {
  175.     if (msg != NULL)
  176.       fprintf(stderr, msg);
  177.     clean_up();
  178.     exit(-1);
  179. }
  180.  
  181. void disp_filename()
  182. {
  183.     char fbuff[MAXPATHLEN + 256];
  184.     int offset;
  185.  
  186.     switch (file_op) {
  187.       case F_NONE:
  188.     break;
  189.       case F_LOAD:
  190.     strcpy(fbuff, "Load file: ");
  191.     break;
  192.       case F_INSERT:
  193.     strcpy(fbuff, "Insert file: ");
  194.     break;
  195.       case F_SAVE:
  196.     strcpy(fbuff, "Save file: ");
  197.     break;
  198.     }
  199.  
  200.     XFillRectangle(dpy, text_win, bggc, 0, 0, draw_wid, T_HT);
  201.     if (file_op != F_NONE) {
  202.     strcat(fbuff, filename);
  203.     if ((offset = strlen(fbuff) - (draw_wid - F_WID) / F_WID) < 0)
  204.       offset = 0;
  205.     XDrawString(dpy, text_win, fggc, F_WID/2, F_HT, fbuff + offset, strlen(fbuff + offset));
  206.     }
  207.     XFlush(dpy);
  208. }
  209.  
  210. void file_error()
  211. {
  212.     XBell(dpy, 50);
  213. }
  214.  
  215. void draw_startup_picture()
  216. {
  217.     /* Set up draw pixmap with startup picture */
  218.     XFillRectangle(dpy, draw_pm, erasegc, 0, 0, draw_wid, draw_ht);
  219.  
  220.     XCopyArea(dpy, startup_pm, draw_pm, drawgc, 0, 0, startup_width, startup_height, (draw_wid - startup_width) / 2, (draw_ht - startup_height) / 2);
  221. }
  222.  
  223. #define NUM_PARTS    256
  224.  
  225. void redraw_system()
  226. {
  227.     static XArc *arcs = NULL, *selarcs = NULL, *circs = NULL, *selcircs = NULL;
  228.     static XSegment *segs = NULL, *selsegs = NULL;
  229.     static num_mass_alloc = 0, num_spring_alloc = 0;
  230.     XArc *cur_arc;
  231.     XSegment *cur_seg;
  232.     int numarcs, numselarcs, numcircs, numselcircs, numsegs, numselsegs;
  233.     int i, rad, num_spr;
  234.     boolean fixed;
  235.  
  236.     /* Draw springs */
  237.     numsegs = numselsegs = 0;
  238.     if (num_spring_alloc < num_spring) {
  239.     num_spring_alloc = num_spring;
  240.     segs = (XSegment *)xrealloc(segs, num_spring_alloc * sizeof(XSegment));
  241.     selsegs = (XSegment *)xrealloc(selsegs, num_spring_alloc * sizeof(XSegment));
  242.     }
  243.     num_spr = (mst.show_spring) ? num_spring : 1;
  244.  
  245.     for (i = 0; i < num_spr; i++) {
  246.     if (springs[i].status & S_ALIVE) {
  247.         if (springs[i].status & S_SELECTED) {
  248.         cur_seg = selsegs + numselsegs++;
  249.         } else {
  250.         cur_seg = segs + numsegs++;
  251.         }
  252.         
  253.         cur_seg->x1 = COORD_X(masses[springs[i].m1].x);
  254.         cur_seg->y1 = COORD_Y(masses[springs[i].m1].y);
  255.         cur_seg->x2 = COORD_X(masses[springs[i].m2].x);
  256.         cur_seg->y2 = COORD_Y(masses[springs[i].m2].y);
  257.     }
  258.     }
  259.     if (numsegs) {
  260.     XDrawSegments(dpy, draw_pm, drawgc, segs, numsegs);
  261.     }
  262.     if (numselsegs) {
  263.     XDrawSegments(dpy, draw_pm, erasegc, selsegs, numselsegs);
  264.     XDrawSegments(dpy, draw_pm, selectlinegc, selsegs, numselsegs);
  265.     }
  266.  
  267.     /* Draw masses */
  268.     numarcs = numselarcs = numcircs = numselcircs = 0;
  269.     if (num_mass_alloc < num_mass) {
  270.     num_mass_alloc = num_mass;
  271.     arcs = (XArc *)xrealloc(arcs, num_mass_alloc * sizeof(XArc));
  272.     selarcs = (XArc *)xrealloc(selarcs, num_mass_alloc * sizeof(XArc));
  273.     circs = (XArc *)xrealloc(circs, num_mass_alloc * sizeof(XArc));
  274.     selcircs = (XArc *)xrealloc(selcircs, num_mass_alloc * sizeof(XArc));
  275.     }
  276.  
  277.     for (i = 0; i < num_mass; i++) {
  278.     if (masses[i].status & S_ALIVE) {
  279.         /* Check if within bounds */
  280.         fixed = (masses[i].status & S_FIXED) && !(masses[i].status & S_TEMPFIXED);
  281.         rad = fixed ? NAIL_SIZE : masses[i].radius;
  282.  
  283.         if (COORD_X(masses[i].x + rad) >= 0 && COORD_X(masses[i].x - rad) <= draw_wid &&
  284.         COORD_Y(masses[i].y - rad) >= 0 && COORD_Y(masses[i].y + rad) <= draw_ht) {
  285.         /* Get cur_arc value based on if FIXED or SELECTED */
  286.         if (masses[i].status & S_SELECTED) {
  287.             if (fixed) {
  288.             cur_arc = selcircs + numselcircs++;
  289.             } else {
  290.             cur_arc = selarcs + numselarcs++;
  291.             }
  292.         } else {
  293.             if (fixed) {
  294.             cur_arc = circs + numcircs++;
  295.             } else {
  296.             cur_arc = arcs + numarcs++;
  297.             }
  298.         }
  299.  
  300.         cur_arc->x = COORD_X(masses[i].x) - rad;
  301.         cur_arc->y = COORD_Y(masses[i].y) - rad;
  302.         cur_arc->width = cur_arc->height = rad * 2 + 1;
  303.         cur_arc->angle1 = 0;
  304.         cur_arc->angle2 = 64 * 360;
  305.         }
  306.     }
  307.     }
  308.     /* Draw masses and nails */
  309.     if (numarcs) {
  310.     XFillArcs(dpy, draw_pm, drawgc, arcs, numarcs);
  311.     }
  312.     if (numselarcs) {
  313.     XFillArcs(dpy, draw_pm, erasegc, selarcs, numselarcs);
  314.     XFillArcs(dpy, draw_pm, selectgc, selarcs, numselarcs);
  315.     }
  316.     if (numcircs) {
  317.     XFillArcs(dpy, draw_pm, erasegc, circs, numcircs);
  318.     XDrawArcs(dpy, draw_pm, drawgc, circs, numcircs);
  319.     }
  320.     if (numselcircs) {
  321.     XFillArcs(dpy, draw_pm, erasegc, selcircs, numselcircs);
  322.     XDrawArcs(dpy, draw_pm, selectgc, selcircs, numselcircs);
  323.     }
  324.  
  325.     if (mst.center_id != -1) {
  326.     i = mst.center_id;
  327.  
  328.     rad = ((masses[i].status & S_FIXED) && !(masses[i].status & S_TEMPFIXED)) ? NAIL_SIZE : masses[i].radius;
  329.  
  330.     XDrawRectangle(dpy, draw_pm, drawgc, (int)(COORD_X(masses[i].x) - rad), (int)(COORD_Y(masses[i].y) - rad),
  331.                2 * rad + 1, 2 * rad + 1);
  332.     }
  333. }
  334.  
  335. void view_system()
  336. {
  337.     XCopyPlane(dpy, draw_pm, draw_win, fggc, 0, 0, draw_wid, draw_ht, 0, 0, 0x1);
  338. }
  339.  
  340. void view_subsystem(ulx, uly, wid, ht)
  341. int ulx, uly, wid, ht;
  342. {
  343.     if (ulx < 0) {
  344.     wid += ulx;
  345.     ulx = 0;
  346.     }
  347.     if (uly < 0) {
  348.     ht += uly;
  349.     uly = 0;
  350.     }
  351.     if (wid < 0 || ht < 0)
  352.       return;
  353.  
  354.     if (ulx + wid >= draw_wid)
  355.       wid -= (ulx + wid - draw_wid);
  356.     if (uly + ht >= draw_ht)
  357.       ht -= (uly + ht - draw_ht);
  358.  
  359.     if (wid < 0 || ht < 0)
  360.       return;
  361.  
  362.     XCopyPlane(dpy, draw_pm, draw_win, fggc, ulx, uly, wid, ht, ulx, uly, 0x1);
  363. }
  364.  
  365. void review_system()
  366. {
  367.     /* Clear the old pixmap */
  368.     XFillRectangle(dpy, draw_pm, erasegc, 0, 0, draw_wid, draw_ht);
  369.  
  370.     redraw_system();
  371.  
  372.     view_system();
  373.  
  374.     mouse_event(M_REDISPLAY, 0, 0, AnyButton, FALSE);
  375.     if (mst.adaptive_step) {
  376.     update_slider(slid_dt_num);
  377.     }
  378.     XFlush(dpy);
  379. }
  380.  
  381. void update_slidnum(w)
  382. int w;
  383. {
  384.     change_slider_parms(S_GRAV, &(mst.cur_grav_val[w]), cur_grav_max[w], cur_grav_min[w]);
  385.     change_slider_parms(S_GDIR, &(mst.cur_misc_val[w]), cur_misc_max[w], cur_misc_min[w]);
  386.  
  387.     update_slider(S_GRAV);
  388.     update_slider(S_GDIR);
  389. }
  390.  
  391. void redraw_names(w)
  392. int w;
  393. {
  394.     static char *grav_nam[] = { "Gravity", "Magnitude", "Magnitude", "Magnitude" };
  395.     static char *misc_nam[] = { "Direction", "Damping", "Exponent", "Exponent" };
  396.     int offset = 120;
  397.  
  398.     XDrawLine(dpy, acts_win, bggc, 4, force_liney-1, (BF_NUM+1)*(BF_SIZ+4)+5, force_liney-1);
  399.     XDrawLine(dpy, acts_win, bggc, 4, force_liney+BF_SIZ+6, (BF_NUM+1)*(BF_SIZ+4)+5, force_liney+BF_SIZ+6);
  400.     XFillRectangle(dpy, acts_win, bggc, grav_slidx + offset, grav_slidy - F_HT, B_WID - 1 - grav_slidx - offset,
  401.            dir_slidy + F_HT + 3 - grav_slidy);
  402.  
  403.     XDrawLine(dpy, acts_win, fggc, w*(BF_SIZ+4)+4, force_liney-1, (w+1)*(BF_SIZ+4)+5, force_liney-1);
  404.     XDrawLine(dpy, acts_win, fggc, w*(BF_SIZ+4)+4, force_liney+BF_SIZ+6, (w+1)*(BF_SIZ+4)+5, force_liney+BF_SIZ+6);
  405.     XDrawString(dpy, acts_win, fggc, grav_slidx + offset, grav_slidy, grav_nam[w], strlen(grav_nam[w]));
  406.     XDrawString(dpy, acts_win, fggc, dir_slidx + offset, dir_slidy, misc_nam[w], strlen(misc_nam[w]));
  407. }
  408.  
  409. void redisplay_widgets()
  410. {
  411.     XCopyPlane(dpy, acts_pm, acts_win, fggc, 0, 0, B_WID, B_HT, 0, 0, 0x1);
  412.  
  413.     redraw_names(cur_force);
  414.     
  415.     redraw_widgets(TRUE);
  416. }
  417.  
  418. int mass_radius(m)
  419. double m;
  420. {
  421.     int rad;
  422.  
  423.     rad = (int)(2 * log(4.0 * m + 1.0));
  424.  
  425.     if (rad < 1) rad = 1;
  426.     if (rad > 64) rad = 64;
  427.  
  428.     return rad;
  429. }
  430.  
  431. static void draw_mass(w, mx, my, m)
  432. Window w;
  433. int mx, my;
  434. double m;
  435. {
  436.     int rad;
  437.  
  438.     rad = mass_radius(m);
  439.  
  440.     XFillArc(dpy, w, fggc, mx - rad, my - rad, rad * 2 + 1, rad * 2 + 1, 0, 64 * 360);
  441. }
  442.  
  443. static void draw_spring(w, x1, y1, x2, y2)
  444. Window w;
  445. int x1, y1, x2, y2;
  446. {
  447.     XDrawLine(dpy, w, fggc, x1, y1, x2, y2);
  448. }
  449.  
  450. static void mouse_vel(mx, my)
  451. int *mx, *my;
  452. {
  453.     int i, totalx = 0, totaly = 0, scale = 0, dx, dy, dt;
  454.     int fudge = 256;
  455.     
  456.     for (i = 0; i < MOUSE_PREV - 1; i++) {
  457.     dx = mprev[(moffset + 2 + i) % MOUSE_PREV].x - mprev[(moffset + 1 + i) % MOUSE_PREV].x;
  458.     dy = mprev[(moffset + 2 + i) % MOUSE_PREV].y - mprev[(moffset + 1 + i) % MOUSE_PREV].y;
  459.     dt = mprev[(moffset + 2 + i) % MOUSE_PREV].t - mprev[(moffset + 1 + i) % MOUSE_PREV].t;
  460.     
  461.     if (dt) {
  462.         scale += 64 * i * i;
  463.         totalx += 64 * i * i * fudge * dx / dt;
  464.         totaly += 64 * i * i * fudge * dy / dt;
  465.     }
  466.     }
  467.  
  468.     if (scale) {
  469.     totalx /= scale;
  470.     totaly /= scale;
  471.     }
  472.  
  473.     *mx = totalx;
  474.     *my = totaly;
  475. }
  476.  
  477. void widget_notify(type, idno)
  478. int type, idno;
  479. {
  480.     int i;
  481.  
  482.     switch (type) {
  483.  
  484.       case O_BUTTON:
  485.     switch (idno) {
  486.       case B_CENTER:
  487.         set_center();
  488.         review_system();
  489.         break;
  490.       case B_RESTLEN:
  491.         set_sel_restlen();
  492.         break;
  493.       case B_SELECTALL:
  494.         select_all();
  495.         eval_selection();
  496.         review_system();
  497.         break;
  498.       case B_SAVESTATE:
  499.         save_state();
  500.         sst = mst;
  501.         break;
  502.       case B_RESSTATE:
  503.         restore_state();
  504.         mst = sst;
  505.  
  506.         redisplay_widgets();
  507.         review_system();
  508.         mouse_event(M_REDISPLAY, 0, 0, AnyButton, FALSE);
  509.         break;
  510.       case B_DUPLICATE:
  511.         duplicate_selected();
  512.         review_system();
  513.         break;
  514.       case B_INSERTFILE:
  515.         file_op = F_INSERT;
  516.         disp_filename();
  517.         break;
  518.       case B_LOADFILE:
  519.         file_op = F_LOAD;
  520.         disp_filename();
  521.         break;
  522.       case B_SAVEFILE:
  523.         file_op = F_SAVE;
  524.         disp_filename();
  525.         break;
  526.       case B_RESET:
  527.         delete_all();
  528.         mst = initst;
  529.         init_objects();
  530.  
  531.         redisplay_widgets();
  532.         review_system();
  533.         mouse_event(M_REDISPLAY, 0, 0, AnyButton, FALSE);
  534.         break;
  535.       case B_QUIT:
  536.         quitting = TRUE;
  537.         break;
  538.     }
  539.     break;
  540.  
  541.       case O_MBUTTON:
  542.     if (idno >= M_BF && idno < M_BF + BF_NUM && mst.bf_mode[idno - M_BF] > 0) {
  543.         cur_force = idno - M_BF;
  544.         update_slidnum(cur_force);
  545.         redraw_names(cur_force);
  546.     }
  547.     break;
  548.  
  549.       case O_CHECKBOX:
  550.     switch (idno) {        
  551.       case C_FIXEDMASS:
  552.         for (i = 0; i < num_mass; i++) {
  553.         if (masses[i].status & S_SELECTED) {
  554.             if (mst.fix_mass) {
  555.             masses[i].status |= S_FIXED;
  556.             } else {
  557.             masses[i].status &= ~(S_FIXED | S_TEMPFIXED);
  558.             }
  559.         }
  560.         }
  561.         review_system();
  562.         break;
  563.       case C_SHOWSPRING:
  564.         review_system();
  565.         break;
  566.     }
  567.     break;
  568.  
  569.       case O_SLIDER:
  570.     switch (idno) {        
  571.       case S_MASS:
  572.         for (i = 0; i < num_mass; i++) {
  573.         if (masses[i].status & S_SELECTED) {
  574.             masses[i].mass = mst.cur_mass;
  575.             masses[i].radius = mass_radius(mst.cur_mass);
  576.         }
  577.         }
  578.         review_system();
  579.         break;
  580.       case S_ELAS:
  581.         for (i = 0; i < num_mass; i++) {
  582.         if (masses[i].status & S_SELECTED)
  583.           masses[i].elastic = mst.cur_rest;
  584.         }
  585.         break;
  586.       case S_KSPR:
  587.         for (i = 0; i < num_spring; i++) {
  588.         if (springs[i].status & S_SELECTED)
  589.           springs[i].ks = mst.cur_ks;
  590.         }
  591.         break;
  592.       case S_KDAMP:
  593.         for (i = 0; i < num_spring; i++) {
  594.         if (springs[i].status & S_SELECTED)
  595.           springs[i].kd = mst.cur_kd;
  596.         }
  597.         break;
  598.     }
  599.     }
  600. }
  601.  
  602. static void mouse_event(type, mx, my, mbutton, shifted)
  603. int type;
  604. int mx, my, mbutton;
  605. int shifted;
  606. {
  607.     static int startx = 0, prevx = 0, starty = 0, prevy = 0, cur_button = 0, selection = -1;
  608.     static boolean active = FALSE, cur_shift = FALSE, static_spring = FALSE;
  609.  
  610.     /* Skip restarts when active or continuations when inactive */
  611.     if ((type != M_DOWN && !active) || (type == M_DOWN && active))
  612.       return;
  613.  
  614.     /* Do grid snapping operation */
  615.     if (mst.grid_snap && mode != M_EDIT) {
  616.     mx = ((mx + (int)mst.cur_gsnap / 2) / (int)mst.cur_gsnap) * (int)mst.cur_gsnap;
  617.     my = ((my + (int)mst.cur_gsnap / 2) / (int)mst.cur_gsnap) * (int)mst.cur_gsnap;
  618.     }
  619.  
  620.     switch (type) {
  621.       case M_REDISPLAY:
  622.     switch (mode) {
  623.       case M_EDIT:
  624.         switch (cur_button) {
  625.           case Button1:
  626.         if (selection < 0) {
  627.             XDrawRectangle(dpy, draw_win, selectboxgc, MIN(startx, prevx), MIN(starty, prevy),
  628.                    ABS(prevx - startx), ABS(prevy - starty));
  629.         }
  630.         break;
  631.           case Button2:
  632.         break;
  633.           case Button3:
  634.         break;
  635.         }
  636.         break;
  637.       case M_MASS:
  638.         draw_mass(draw_win, prevx, prevy, mst.cur_mass);
  639.         break;
  640.       case M_SPRING:
  641.         if (static_spring) {
  642.         startx = COORD_X(masses[selection].x);
  643.         starty = COORD_Y(masses[selection].y);
  644.         draw_spring(draw_win, startx, starty, prevx, prevy);
  645.         }
  646.         break;
  647.     }
  648.     break;
  649.  
  650.       case M_DOWN:
  651.     review_system();
  652.     startx = prevx = mx;
  653.     starty = prevy = my;
  654.     cur_button = mbutton;
  655.     active = TRUE;
  656.     cur_shift = shifted;
  657.  
  658.     switch (mode) {
  659.       case M_EDIT:
  660.         switch (cur_button) {
  661.           case Button1:
  662.         {
  663.             boolean is_mass = FALSE;
  664.             selection = nearest_object(COORD_X(mx), COORD_Y(my), &is_mass);
  665.             
  666.             /* If not shift clicking, unselect all currently selected items */
  667.             if (!shifted) {
  668.             unselect_all();
  669.             review_system();
  670.             }
  671.         }
  672.         break;
  673.           case Button2:
  674.           case Button3:
  675.         tempfixed_obj(TRUE);
  676.         break;
  677.         }
  678.         break;
  679.       case M_MASS:
  680.         draw_mass(draw_win, mx, my, mst.cur_mass);
  681.         break;
  682.       case M_SPRING:
  683.         {
  684.         boolean is_mass = TRUE;
  685.  
  686.         static_spring = (!action || cur_button == Button3);
  687.  
  688.         selection = nearest_object(COORD_X(mx), COORD_Y(my), &is_mass);
  689.         if (selection >= 0 && is_mass) {
  690.             startx = COORD_X(masses[selection].x);
  691.             starty = COORD_Y(masses[selection].y);
  692.             if (static_spring) {
  693.             draw_spring(draw_win, startx, starty, prevx, prevy);
  694.             } else {
  695.             attach_fake_spring(selection);
  696.             move_fake_mass(COORD_X(mx), COORD_Y(my));
  697.             review_system();
  698.             }
  699.         } else {
  700.             active = FALSE;
  701.         }
  702.         }
  703.         break;
  704.     }
  705.     break;
  706.  
  707.       case M_DRAG:
  708.     switch (mode) {
  709.       case M_EDIT:
  710.         switch (cur_button) {
  711.           case Button1:
  712.         if (selection < 0) {
  713.             view_subsystem(MIN(startx, prevx), MIN(starty, prevy), ABS(prevx - startx) + 1, ABS(prevy - starty) + 1);
  714.             prevx = mx;
  715.             prevy = my;
  716.             XDrawRectangle(dpy, draw_win, selectboxgc, MIN(startx, prevx), MIN(starty, prevy),
  717.                    ABS(prevx - startx), ABS(prevy - starty));
  718.         }
  719.         break;
  720.           case Button2:
  721.           case Button3:
  722.         /* Move objects relative to mouse */
  723.         translate_selobj(COORD_DX(mx - prevx), COORD_DY(my - prevy));
  724.         review_system();
  725.         prevx = mx;
  726.         prevy = my;
  727.         break;
  728.         }        
  729.         break;
  730.       case M_MASS:
  731.         {
  732.         int rad = mass_radius(mst.cur_mass);
  733.         view_subsystem(prevx - rad, prevy - rad, rad * 2 + 1, rad * 2 + 1);
  734.         }
  735.         prevx = mx;
  736.         prevy = my;
  737.         draw_mass(draw_win, prevx, prevy, mst.cur_mass);
  738.         break;
  739.       case M_SPRING:
  740.         if (static_spring) {
  741.         view_subsystem(MIN(startx, prevx), MIN(starty, prevy), ABS(prevx - startx) + 1, ABS(prevy - starty) + 1);
  742.         prevx = mx;
  743.         prevy = my;
  744.         startx = COORD_X(masses[selection].x);
  745.         starty = COORD_Y(masses[selection].y);
  746.         draw_spring(draw_win, startx, starty, prevx, prevy);
  747.         } else {
  748.         move_fake_mass(COORD_X(mx), COORD_Y(my));
  749.         }
  750.         break;
  751.     }
  752.     break;
  753.  
  754.       case M_UP:
  755.     active = FALSE;
  756.     switch (mode) {
  757.       case M_EDIT:
  758.         switch (cur_button) {
  759.           case Button1:
  760.         if (selection < 0) {
  761.             select_objects(MIN(COORD_X(startx), COORD_X(mx)), MIN(COORD_Y(starty), COORD_Y(my)), 
  762.                    MAX(COORD_X(startx), COORD_X(mx)), MAX(COORD_Y(starty), COORD_Y(my)), cur_shift);
  763.             eval_selection();
  764.             review_system();
  765.         } else {
  766.             boolean is_mass = FALSE;
  767.             
  768.             if ((selection = nearest_object(COORD_X(mx), COORD_Y(my), &is_mass)) >= 0) {
  769.             select_object(selection, is_mass, cur_shift);
  770.             eval_selection();
  771.             review_system();
  772.             }
  773.         }
  774.         break;
  775.           case Button2:
  776.         tempfixed_obj(FALSE);
  777.         review_system();
  778.         break;
  779.           case Button3:
  780.         {
  781.             int mvx, mvy;
  782.  
  783.             mouse_vel(&mvx, &mvy);
  784.  
  785.             changevel_selobj(COORD_DX(mvx), COORD_DY(mvy), FALSE);
  786.             tempfixed_obj(FALSE);
  787.             review_system();
  788.         }
  789.         break;
  790.         }
  791.         break;
  792.       case M_MASS:
  793.         {
  794.         int newm;
  795.  
  796.         newm = create_mass();
  797.  
  798.         masses[newm].x = COORD_X((double)mx);
  799.         masses[newm].y = COORD_Y((double)my);
  800.         masses[newm].mass = mst.cur_mass;
  801.         masses[newm].radius = mass_radius(mst.cur_mass);
  802.         masses[newm].elastic = mst.cur_rest;
  803.         if (mst.fix_mass) masses[newm].status |= S_FIXED;
  804.  
  805.         review_system();
  806.         }
  807.         break;
  808.  
  809.       case M_SPRING:
  810.         {
  811.         boolean is_mass = TRUE;
  812.         int start_sel = selection, news, endx, endy;
  813.  
  814.         if (!static_spring) {
  815.             kill_fake_spring();
  816.         }
  817.             
  818.         selection = nearest_object(COORD_X(mx), COORD_Y(my), &is_mass);
  819.         if ((static_spring || !action || cur_button == Button1) && selection >= 0 && is_mass && selection != start_sel) {
  820.             startx = COORD_X(masses[start_sel].x);
  821.             starty = COORD_Y(masses[start_sel].y);
  822.             endx = COORD_X(masses[selection].x);
  823.             endy = COORD_Y(masses[selection].y);
  824.             
  825.             news = create_spring();
  826.             springs[news].m1 = start_sel;
  827.             springs[news].m2 = selection;
  828.             springs[news].ks = mst.cur_ks;
  829.             springs[news].kd = mst.cur_kd;
  830.             springs[news].restlen = sqrt((double)SQR(startx - endx) + (double)SQR(starty - endy));
  831.  
  832.             add_massparent(start_sel, news);
  833.             add_massparent(selection, news);
  834.         }
  835.  
  836.         review_system();
  837.         }
  838.         break;
  839.     }
  840.     break;
  841.     }
  842.     XFlush(dpy);
  843. }
  844.  
  845. static boolean x_event()
  846. {
  847.     XEvent event, peek;
  848.     KeySym ks;
  849.     char keybuf[1];
  850.     
  851.     if (quitting)
  852.       return FALSE;
  853.  
  854.     /* Get next event */
  855.     if (XPending(dpy)) {
  856.     XNextEvent(dpy, &event);
  857.  
  858.     switch (event.type) {
  859.       case ButtonPress:
  860.         if (!check_widgets(event.xbutton.window, event.xbutton.x, event.xbutton.y, event.xbutton.button, M_DOWN)) {
  861.         /* Process button press event */
  862.         if (event.xbutton.window == draw_win) {
  863.             bzero(mprev, sizeof(mprev));
  864.             mouse_event(M_DOWN, event.xbutton.x, event.xbutton.y, event.xbutton.button, event.xbutton.state & ShiftMask);
  865.         } else if (event.xbutton.window == acts_win) {
  866.             /* Show startup picture if clicked on xspringies logo */
  867.             if (event.xbutton.x >= 4 && event.xbutton.x < 4 + title_width && event.xbutton.y >= 4 && event.xbutton.y < 4 + title_height) {
  868.             draw_startup_picture();
  869.             view_system();
  870.             }
  871.         }
  872.         }
  873.         break;
  874.       case ButtonRelease:
  875.         if (!check_widgets(event.xbutton.window, event.xbutton.x, event.xbutton.y, event.xbutton.button, M_UP)) {
  876.         /* Process button release event */
  877.         if (event.xbutton.window == draw_win) {
  878.             mouse_event(M_UP, event.xbutton.x, event.xbutton.y, event.xbutton.button, FALSE);
  879.         }
  880.         }
  881.         break;
  882.       case MotionNotify:
  883.         {
  884.         Window mw = event.xmotion.window;
  885.  
  886.         /* Skip over other motion notify events for this window */
  887.         while (XCheckWindowEvent(dpy, mw, PointerMotionMask, &event));
  888.  
  889.         if (event.xmotion.state & (Button1Mask | Button2Mask | Button3Mask)) {
  890.             if (!check_widgets(event.xmotion.window, event.xmotion.x, event.xmotion.y, 0, M_DRAG)) {
  891.             /* Record mouse info */
  892.             mprev[moffset].x = event.xmotion.x;
  893.             mprev[moffset].y = event.xmotion.y;
  894.             mprev[moffset].t = (unsigned long)(event.xmotion.time);
  895.             moffset = (moffset + 1) % MOUSE_PREV;
  896.  
  897.             /* Process motion notify event */
  898.             if (event.xbutton.window == draw_win) {
  899.                 mouse_event(M_DRAG, event.xbutton.x, event.xbutton.y, AnyButton, FALSE);
  900.             }
  901.             }
  902.         }
  903.         }
  904.         break;
  905.  
  906.       case KeyPress:
  907.         XLookupString (&event.xkey, keybuf, sizeof(keybuf), &ks, NULL);
  908.         
  909.         /* Skip modifier key */
  910.         if (IsModifierKey(ks) || ks == XK_Multi_key)
  911.           break;
  912.  
  913.         switch (ks) {
  914.           case XK_BackSpace:
  915.           case XK_Delete:
  916.         key_press(K_DELETE, event.xkey.window);
  917.         break;
  918.           case XK_Return:
  919.         key_press(K_RETURN, event.xkey.window);
  920.         break;
  921.           case XK_Escape:
  922.         key_press(K_ESCAPE, event.xkey.window);
  923.         break;
  924.           default:
  925.         key_press((int)(keybuf[0]), event.xkey.window);
  926.         break;
  927.         }
  928.         break;
  929.  
  930.       case ConfigureNotify:
  931.         {
  932.         static boolean created = FALSE;
  933.         int new_wid, new_ht;
  934.  
  935.         new_wid = event.xconfigure.width;
  936.         new_ht = event.xconfigure.height;
  937.  
  938.         if (new_wid == main_wid && new_ht == main_ht)
  939.           break;
  940.  
  941.         main_wid = new_wid;
  942.         main_ht = new_ht;
  943.  
  944.         draw_wid = main_wid - B_WID - 1;
  945.         draw_ht = main_ht - T_HT - 1;
  946.  
  947.         if (created) {
  948.             XFreePixmap(dpy, draw_pm);
  949.             created = TRUE;
  950.         }
  951.         draw_pm = XCreatePixmap(dpy, draw_win, draw_wid, draw_ht, 1);
  952.         review_system();
  953.  
  954.         XMoveResizeWindow(dpy, text_win, B_WID + 1, draw_ht + 1, draw_wid, T_HT);
  955.         XMoveResizeWindow(dpy, draw_win, B_WID + 1, 0, draw_wid, draw_ht);
  956.         }
  957.         break;
  958.  
  959.       case Expose:
  960.         /* Skip over additional expose events */
  961.         while (XCheckTypedEvent(dpy, Expose, &peek))
  962.           event = peek;
  963.         
  964.         /* Generic expose event (redraw everything) */
  965.         redisplay_widgets();
  966.  
  967.         view_system();
  968.         mouse_event(M_REDISPLAY, 0, 0, AnyButton, FALSE);
  969.  
  970.         disp_filename();
  971.         break;
  972.         
  973.       default:
  974.         break;
  975.     }
  976.     XFlush(dpy);
  977.     } else {
  978.     /* No events waiting */
  979.     if (scan_flag) {
  980.         static struct timeval tp, tpo;
  981.         static struct timezone tzp;
  982.         int difft;
  983.  
  984.         if (tpo.tv_sec == 0)
  985.           gettimeofday(&tpo, &tzp);
  986.  
  987.         gettimeofday(&tp, &tzp);
  988.         difft = (tp.tv_sec - tpo.tv_sec) * 1000000 + (tp.tv_usec - tpo.tv_usec);
  989.  
  990.         /* Do widget scanning buttons about 30 times / sec */
  991.         if (difft > 30000) {
  992.         tpo = tp;
  993.         (void)check_widgets(acts_win, 0, 0, 0, M_HOLD);
  994.         }
  995.     } else if (!action) {
  996.         /* Sleep until next X event */
  997.         fd_set readfds;
  998.         
  999.         FD_ZERO(&readfds);
  1000.         FD_SET(xfd, &readfds);
  1001.         
  1002.         (void)select(getdtablesize(), &readfds, NULL, NULL, NULL);
  1003.     }
  1004.  
  1005.     if (action && animate_obj()) {
  1006.         static struct timeval tp, tpo;
  1007.         static struct timezone tzp;
  1008.         static int totaldiff = 0;
  1009.         static boolean started = FALSE;
  1010.         int difft;
  1011.  
  1012.         /* Get time the first time through */
  1013.         if (!started) {
  1014.         gettimeofday(&tp, &tzp);
  1015.         started = TRUE;
  1016.         }
  1017.         tpo = tp;
  1018.  
  1019.         gettimeofday(&tp, &tzp);
  1020.  
  1021.         difft = (tp.tv_sec - tpo.tv_sec) * 1000000 + (tp.tv_usec - tpo.tv_usec);
  1022.  
  1023.         if (difft < MIN_DIFFT)
  1024.           difft = MIN_DIFFT;
  1025.  
  1026.         if (difft > 0) {
  1027.         totaldiff += difft;
  1028.  
  1029.         /* Do updates about 30 frames per second */
  1030.         if (totaldiff > 30000) {
  1031.             review_system();
  1032.             XSync(dpy, False);
  1033.         }
  1034.         }
  1035.     }
  1036.     }
  1037.  
  1038.     return TRUE;
  1039. }
  1040.  
  1041. Pixmap get_pixmap(bits, width, height, inv)
  1042. char *bits;
  1043. int width, height;
  1044. boolean inv;
  1045. {
  1046.     int black, white;
  1047.  
  1048.     black = inv ? 1 : 0;
  1049.     white = inv ? 0 : 1;
  1050.  
  1051.     return XCreatePixmapFromBitmapData(dpy, main_win, bits, width, height, white, black, 1);
  1052. }
  1053.  
  1054. unsigned long GetColor(cname, cmap)
  1055. char *cname;
  1056. Colormap cmap;
  1057. {
  1058.     /* Color stuff */
  1059.     XColor xc;
  1060.  
  1061.     if (XParseColor(dpy, cmap, cname, &xc) == 0) {
  1062.     fprintf(stderr, "Unknown color: %s\n", cname);
  1063.     exit(-1);
  1064.     } else {
  1065.     if (XAllocColor(dpy, cmap, &xc) == 0) {
  1066.         fprintf(stderr, "Cannot allocate color: %s\n", cname);
  1067.         exit(-1);
  1068.     }
  1069.     }
  1070.  
  1071.     return xc.pixel;
  1072. }
  1073.  
  1074. static void init_x(argc, argv, displayname, geometry, fgcolor, bgcolor, hlcolor)
  1075. int argc;
  1076. char *argv[];
  1077. char *displayname, *geometry, *fgcolor, *bgcolor, *hlcolor;
  1078. {
  1079.     XGCValues gcv, gcmv;
  1080.     XSetWindowAttributes xswa;
  1081.     XSizeHints hints;
  1082.     XFontStruct *font;
  1083.     Cursor cross;
  1084.     Colormap cmap;
  1085.     Pixmap icon_p;
  1086.     Visual *vis;
  1087.     int scn;
  1088.     int mask = 0;
  1089.  
  1090.     /* Open display */
  1091.     if ((dpy = XOpenDisplay(displayname)) == NULL) {
  1092.     fprintf(stderr, "Could not open display\n");
  1093.     exit(-1);
  1094.     }
  1095.  
  1096.     /* Get screen and colors */
  1097.     scn = DefaultScreen(dpy);
  1098.     planes = DisplayPlanes(dpy, scn);
  1099.     cmap = DefaultColormap(dpy, scn);
  1100.     vis = DefaultVisual(dpy, scn);
  1101.     xfd = ConnectionNumber(dpy);
  1102.  
  1103.     if (hlcolor == NULL) {
  1104.     if (vis->class == StaticGray || vis->class == GrayScale) {
  1105.         hlcolor = fgcolor;
  1106.     } else {
  1107.         XColor xc;
  1108.  
  1109.         if (XParseColor(dpy, cmap, HLCOLOR, &xc) == 0) {
  1110.         hlcolor = fgcolor;
  1111.         } else {
  1112.         hlcolor = HLCOLOR;
  1113.         }
  1114.     }
  1115.     }
  1116.  
  1117.     fcolor = GetColor(fgcolor, cmap);
  1118.     bcolor = GetColor(bgcolor, cmap);
  1119.     hcolor = GetColor(hlcolor, cmap);
  1120.  
  1121.     /* Set up main window */
  1122.     main_wid = hints.width = M_WID + 2;
  1123.     hints.min_width = B_WID * 2;
  1124.     main_ht = hints.height = hints.min_height = M_HT + 2;
  1125.     hints.flags = PSize | PMinSize;
  1126.  
  1127.     /* Get geometry info */
  1128.     if (geometry != NULL) {
  1129.     hints.x = hints.y = 0;
  1130.  
  1131.     if (mask = XParseGeometry(geometry, &(hints.x), &(hints.y), &(hints.width), &(hints.height))) {
  1132.         hints.flags |= ((mask & (XValue | YValue)) ? USPosition: PPosition) |
  1133.           ((mask & (WidthValue | HeightValue)) ? USSize : PSize);
  1134.  
  1135.         if (hints.width < hints.min_width)
  1136.           hints.width = hints.min_width;
  1137.         if (hints.height < hints.min_height)
  1138.           hints.height = hints.min_height;
  1139.     }
  1140.     }
  1141.  
  1142.     /* Create main window */
  1143.     main_win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), hints.x, hints.y, hints.width, hints.height, 1, fcolor, bcolor);
  1144.  
  1145.     icon_p = XCreatePixmapFromBitmapData(dpy, DefaultRootWindow(dpy), icon_bits, icon_width, icon_height, 1, 0, planes);
  1146.     XSetStandardProperties(dpy, main_win, "XSpringies", "XSpringies", icon_p, argv, argc, &hints);
  1147.  
  1148.     if (mask)
  1149.       XSetNormalHints(dpy, main_win, &hints);
  1150.  
  1151.     /* Map main window to display */
  1152.     XMapWindow(dpy, main_win);
  1153.  
  1154.     XFlush(dpy);
  1155.     XSync(dpy, False);
  1156.  
  1157.     {
  1158.     Window tmp_win;
  1159.     int xpos, ypos;
  1160.     unsigned int bw, dep;
  1161.     
  1162.     XGetGeometry(dpy, main_win, &tmp_win, &xpos, &ypos, &main_wid, &main_ht, &bw, &dep);
  1163.     }
  1164.  
  1165.     /* Make subwindows */
  1166.     acts_win = XCreateSimpleWindow(dpy, main_win, 0, 0, B_WID, B_HT, 1, fcolor, bcolor);
  1167.  
  1168.     draw_wid = main_wid - B_WID - 1;
  1169.     draw_ht = main_ht - T_HT - 1;
  1170.     draw_win = XCreateSimpleWindow(dpy, main_win, B_WID + 1, 0, draw_wid, draw_ht, 1, fcolor, bcolor);
  1171.     text_win = XCreateSimpleWindow(dpy, main_win, B_WID + 1, draw_ht + 1, draw_wid, T_HT, 1, fcolor, bcolor);
  1172.  
  1173.     /* Make pixmaps for drawing and actions areas (to make redraw trivial) */
  1174.     draw_pm = XCreatePixmap(dpy, draw_win, draw_wid, draw_ht, 1);
  1175.  
  1176.     acts_pm = XCreatePixmap(dpy, acts_win, B_WID, B_HT, 1);
  1177.  
  1178.     /* Get a font */
  1179.     if ((font = XLoadQueryFont(dpy, F_NAME)) == NULL) {
  1180.     fatal("Could not load font");
  1181.     }
  1182.  
  1183.     /* Create GCs */
  1184.     gcmv.font = gcv.font = font->fid;
  1185.     gcmv.plane_mask = gcv.plane_mask = 1;
  1186.  
  1187.     gcmv.foreground = 1;
  1188.     gcmv.background = 0;
  1189.  
  1190.     gcv.foreground = bcolor;
  1191.     gcv.background = hcolor;
  1192.     revgc = XCreateGC(dpy, main_win, GCForeground | GCBackground | GCFont, &gcv);
  1193.  
  1194.     gcv.foreground = hcolor;
  1195.     gcv.background = bcolor;
  1196.     hlgc = XCreateGC(dpy, main_win, GCForeground | GCBackground | GCFont, &gcv);
  1197.  
  1198.     gcv.foreground = fcolor;
  1199.     drawgc = XCreateGC(dpy, draw_pm, GCForeground | GCBackground | GCFont, &gcmv);
  1200.     fggc = XCreateGC(dpy, main_win, GCForeground | GCBackground | GCFont, &gcv);
  1201.  
  1202.     {
  1203.     static char stipple_bits[] = { 0x01, 0x02 };
  1204.     static char dot_line[2] = { 1, 1 };
  1205.     Pixmap stip;
  1206.  
  1207.     stip = XCreateBitmapFromData(dpy, main_win, stipple_bits, 2, 2);
  1208.     gcmv.stipple = gcv.stipple = stip;
  1209.     gcmv.fill_style = gcv.fill_style = FillStippled;
  1210.        
  1211.     gcmv.line_style = gcv.line_style = LineOnOffDash;
  1212.     selectgc = XCreateGC(dpy, draw_pm, GCForeground | GCBackground | GCFont | GCFillStyle | GCStipple, &gcmv);
  1213.      selectlinegc = XCreateGC(dpy, draw_pm, GCForeground | GCBackground | GCFont | GCLineStyle, &gcmv);
  1214.  
  1215.     gcv.foreground = hcolor;
  1216.  
  1217.     if (hcolor != fcolor && hcolor != bcolor) {
  1218.         selectboxgc = XCreateGC(dpy, main_win, GCForeground | GCBackground | GCFont, &gcv);
  1219.     } else {
  1220.         selectboxgc = XCreateGC(dpy, main_win, GCForeground | GCBackground | GCFont | GCLineStyle, &gcv);
  1221.         XSetDashes(dpy, selectboxgc, 0, dot_line, 2);
  1222.     }        
  1223.  
  1224.     XSetDashes(dpy, selectlinegc, 0, dot_line, 2);
  1225.     }
  1226.  
  1227.     gcv.foreground = gcv.background = bcolor;
  1228.     gcmv.foreground = gcmv.background = 0;
  1229.     erasegc = XCreateGC(dpy, draw_pm, GCForeground | GCBackground | GCFont, &gcmv);
  1230.     bggc = XCreateGC(dpy, main_win, GCForeground | GCBackground | GCFont, &gcv);
  1231.  
  1232.     /* Clear out action pixmap */
  1233.     XFillRectangle(dpy, acts_pm, erasegc, 0, 0, B_WID, B_HT);
  1234.  
  1235.     startup_pm = get_pixmap(startup_bits, startup_width, startup_height, FALSE);
  1236.     draw_startup_picture();
  1237.  
  1238.     /* Use a cross cursor for the draw window */
  1239.     cross = XCreateFontCursor(dpy, XC_tcross);
  1240.  
  1241.     /* Set window event masks and other attributes */
  1242.     xswa.event_mask = ExposureMask | StructureNotifyMask;
  1243.     XChangeWindowAttributes(dpy, main_win, CWEventMask, &xswa);
  1244.  
  1245.     xswa.event_mask = KeyPressMask | ExposureMask;
  1246.     XChangeWindowAttributes(dpy, text_win, CWEventMask, &xswa);
  1247.  
  1248.     xswa.event_mask = ButtonPressMask | ButtonReleaseMask | Button1MotionMask | Button2MotionMask | Button3MotionMask | KeyPressMask | ExposureMask;
  1249.     XChangeWindowAttributes(dpy, acts_win, CWEventMask, &xswa);
  1250.     xswa.cursor = cross;
  1251.     XChangeWindowAttributes(dpy, draw_win, CWEventMask | CWCursor, &xswa);
  1252.  
  1253.     XMapWindow(dpy, draw_win);
  1254.     XMapWindow(dpy, text_win);
  1255.     XMapWindow(dpy, acts_win);
  1256.  
  1257.     XFlush(dpy);
  1258. }
  1259.  
  1260. static void make_widgets()
  1261. {
  1262.     int i, y;
  1263.  
  1264.     {    
  1265.     Pixmap title_pm = get_pixmap(title_bits, title_width, title_height, FALSE);
  1266.     XCopyArea(dpy, title_pm, acts_pm, drawgc, 0, 0, title_width, title_height, 4, 4);
  1267.     XFreePixmap(dpy, title_pm);
  1268.     }
  1269.  
  1270.     add_modebutton(acts_pm, acts_win, B_WID/2, 4, B_WID-6, 54, "Edit", edit_bits, edit_width, edit_height, M_EDIT, &mode, FALSE);
  1271.     add_modebutton(acts_pm, acts_win, 4, 54, B_WID/2, 104, "Mass", mass_bits, mass_width, mass_height, M_MASS, &mode, FALSE);
  1272.     add_modebutton(acts_pm, acts_win, B_WID/2, 54, B_WID-6, 104, "Spring", spring_bits, spring_width, spring_height, M_SPRING, &mode, FALSE);
  1273.  
  1274.     add_slider(acts_pm, acts_win, 4, 110, B_WID-6, 125, "Mass", "%10.2lf", S_MASS, &(mst.cur_mass), 10000000.0, 0.01, 0.01);
  1275.     add_slider(acts_pm, acts_win, 4, 127, B_WID-6, 142, "Elasticity", "%10.3lf", S_ELAS, &(mst.cur_rest), 1.0, 0.0, 0.001);
  1276.     add_slider(acts_pm, acts_win, 4, 144, B_WID-6, 159, "Kspring", "%10.2lf", S_KSPR, &(mst.cur_ks), 10000000.0, 0.01, 0.01);
  1277.     add_slider(acts_pm, acts_win, 4, 161, B_WID-6, 176, "Kdamp", "%10.2lf", S_KDAMP, &(mst.cur_kd), 10000000.0, 0.0, 0.01);
  1278.  
  1279.     add_checkbox(acts_pm, acts_win, 4, 178, B_WID/2-16, 194, "Fixed Mass", C_FIXEDMASS, &(mst.fix_mass));
  1280.     add_checkbox(acts_pm, acts_win, B_WID/2, 178, B_WID-6, 194, "Show Springs", C_SHOWSPRING, &(mst.show_spring));
  1281.  
  1282.     add_button(acts_pm, acts_win, 4, 197, B_WID/2 + 10, 217, "Set Rest Length", B_RESTLEN);
  1283.     add_button(acts_pm, acts_win, B_WID/2 + 16, 197, B_WID-6, 217, "Set Center", B_CENTER);
  1284.  
  1285.     y = 228;
  1286.     XDrawLine(dpy, acts_pm, drawgc, 0, y-4, B_WID-1, y-4);
  1287.     y++;
  1288.  
  1289.     for (i = 0; i < BF_NUM; i++) {
  1290.     mst.bf_mode[i] = -1;
  1291.     add_modebutton(acts_pm, acts_win, i*(BF_SIZ+4)+4, y, 4+(i+1)*(BF_SIZ+4),y+BF_SIZ+4,"", b_bits[i], BF_SIZ, BF_SIZ, M_BF + i, &(mst.bf_mode[i]), TRUE);
  1292.     }
  1293.     force_liney = y;
  1294.  
  1295.     y += 36;
  1296.     add_slider(acts_pm, acts_win, 4, y, B_WID-6, y+15, "", "%10.2lf", S_GRAV, &(mst.cur_grav_val[0]), cur_grav_max[0], cur_grav_min[0], 0.01);
  1297.     grav_slidx = 4;
  1298.     grav_slidy = (y + y+15 + F_HT) / 2 - 2;
  1299.  
  1300.     add_slider(acts_pm, acts_win, 4, y+17, B_WID-6, y+32, "", "%10.2lf", S_GDIR, &(mst.cur_grav_val[0]), cur_misc_max[0], cur_misc_min[0], 0.05);
  1301.  
  1302.     dir_slidx = 4;
  1303.     dir_slidy = (y+17 + y+32 + F_HT) / 2 - 2;
  1304.     update_slidnum(0);
  1305.  
  1306.     y += 38;
  1307.     XDrawLine(dpy, acts_pm, drawgc, 0, y - 4, B_WID-1, y - 4);
  1308.  
  1309.     add_slider(acts_pm, acts_win, 4, y, B_WID-6, y+15, "Viscosity", "%10.2lf", S_VISC, &(mst.cur_visc), 10000000.0, 0.0, 0.01);
  1310.     add_slider(acts_pm, acts_win, 4, y+17, B_WID-6, y+32, "Stickiness", "%10.2lf", S_STICK, &(mst.cur_stick), 10000000.0, 0.0, 0.01);
  1311.  
  1312.     y = y + 37;
  1313.  
  1314.     XDrawLine(dpy, acts_pm, drawgc, 0, y - 4, B_WID-1, y - 4);
  1315.  
  1316.     add_slider(acts_pm, acts_win, 4, y, B_WID-6, y+15, "Time Step", "%10.4lf", S_TIMESTEP, &(mst.cur_dt), 1.0, 0.0001, 0.0001);
  1317.     add_slider(acts_pm, acts_win, 4, y+17, B_WID-6, y+32, "Precision", "%10.4lf", S_PRECISION, &(mst.cur_prec), 1000.0, 0.0001, 0.0001);
  1318.     add_checkbox(acts_pm, acts_win, 4, y+33, B_WID/2+32, y+49, "Adaptive Time Step", C_ADAPTIVE_STEP, &(mst.adaptive_step));
  1319.  
  1320.     y = y + 56;
  1321.     XDrawLine(dpy, acts_pm, drawgc, 0, y-4, B_WID-1, y-4);
  1322.  
  1323.     add_slider(acts_pm, acts_win, 4, y, B_WID/2 + 20, y+16, "", "%10.0lf", S_GRIDSNAP, &(mst.cur_gsnap), 200.0, 1.0, 1.0);
  1324.     add_checkbox(acts_pm, acts_win, B_WID/2 + 24, y, B_WID - 4, y+16, "Grid Snap", C_GRIDSNAP, &(mst.grid_snap));
  1325.  
  1326.     add_checkbox(acts_pm, acts_win, B_WID/2 + 24, y+20, B_WID - 16, y+33, "Action", C_ACTION, &action);
  1327.  
  1328.     XDrawRectangle(dpy, acts_pm, drawgc, 16, y+24, 64, 30);
  1329.     add_checkbox(acts_pm, acts_win, 40, y+18, 56, y+33, "", C_WTOP, &(mst.w_top));
  1330.     add_checkbox(acts_pm, acts_win, 8, y+33, 24, y+48, " Walls", C_WLEFT, &(mst.w_left));
  1331.     add_checkbox(acts_pm, acts_win, 72, y+33, 88, y+48, "", C_WRIGHT, &(mst.w_right));
  1332.     add_checkbox(acts_pm, acts_win, 40, y+48, 56, y+63, "", C_WBOTTOM, &(mst.w_bottom));
  1333.  
  1334.     add_button(acts_pm, acts_win, B_WID/2+3, y+38, B_WID-6, y+58, "Select all", B_SELECTALL);
  1335.  
  1336.     y = y + 68;
  1337.     XDrawLine(dpy, acts_pm, drawgc, 0, y-4, B_WID-1, y-4);
  1338.  
  1339.     add_button(acts_pm, acts_win, 4, y, B_WID/2-5, y+20, "Save State", B_SAVESTATE);
  1340.     add_button(acts_pm, acts_win, B_WID/2+3, y, B_WID-6, y+20, "Restore State", B_RESSTATE);
  1341.     add_button(acts_pm, acts_win, 4, y+25, B_WID/2-5, y+45, "Duplicate", B_DUPLICATE);
  1342.     add_button(acts_pm, acts_win, B_WID/2+3, y+25, B_WID-6, y+45, "Insert File", B_INSERTFILE);
  1343.  
  1344.     add_button(acts_pm, acts_win, 4, y+50, B_WID/2-5, y+70, "Load File", B_LOADFILE);
  1345.     add_button(acts_pm, acts_win, B_WID/2+3, y+50, B_WID-6, y+70, "Save File", B_SAVEFILE);
  1346.     add_button(acts_pm, acts_win, 4, y+75, B_WID/2-5, y+95, "Reset", B_RESET);
  1347.     add_button(acts_pm, acts_win, B_WID/2+3, y+75, B_WID-6, y+95, "Quit", B_QUIT);
  1348.  
  1349.     slid_dt_num = slider_valno(S_TIMESTEP);
  1350. }
  1351.  
  1352. void usage()
  1353. {
  1354.     static char *msg1 = "Usage: xspringies [-d|display displayname] [-geometry geom] [-rv]\n";
  1355.     static char *msg2 = "                  [-bg color] [-fg color] [-hl color]\n";
  1356.  
  1357.     fprintf(stderr, msg1);
  1358.     fprintf(stderr, msg2);
  1359.     exit(-1);
  1360. }
  1361.  
  1362. main(argc, argv)
  1363. int argc;
  1364. char *argv[];
  1365. {
  1366.     char *swch, *displayname = NULL, *geometry = NULL, *bgcolor = "black", *fgcolor = "white", *hlcolor = NULL;
  1367.     boolean rev_vid = FALSE;
  1368.     extern char *getenv();
  1369.     char *path;
  1370.  
  1371.     initst = sst = mst;
  1372.  
  1373.     if ((path = getenv("SPRINGDIR")) != NULL) {
  1374.     strcpy(filename, path);
  1375.     } else {
  1376.     strcpy(filename, DEF_PATH);
  1377.     }
  1378.  
  1379.     while (--argc > 0) {
  1380.     if (**++argv == '-') {
  1381.         swch = (*argv) + 1;
  1382.  
  1383.         if (!strcmp(swch, "display") || !strcmp(swch, "d")) {
  1384.         if (--argc > 0) {
  1385.             displayname = *++argv;
  1386.         } else {
  1387.             usage();
  1388.         }
  1389.         } else if (!strcmp(swch, "geometry")) {
  1390.         if (--argc > 0) {
  1391.             geometry = *++argv;
  1392.         } else {
  1393.             usage();
  1394.         }
  1395.         } else if (!strcmp(swch, "rv")) {
  1396.         rev_vid = TRUE;
  1397.         } else if (!strcmp(swch, "bg")) {
  1398.         if (--argc > 0) {
  1399.             bgcolor = *++argv;
  1400.         } else {
  1401.             usage();
  1402.         }
  1403.         } else if (!strcmp(swch, "fg")) {
  1404.         if (--argc > 0) {
  1405.             fgcolor = *++argv;
  1406.         } else {
  1407.             usage();
  1408.         }
  1409.         } else if (!strcmp(swch, "hl")) {
  1410.         if (--argc > 0) {
  1411.             hlcolor = *++argv;
  1412.         } else {
  1413.             usage();
  1414.         }
  1415.         } else {
  1416.         usage();
  1417.         }
  1418.     } else {
  1419.         usage();
  1420.     }
  1421.     }
  1422.  
  1423.     if (rev_vid) {
  1424.     char *swap = fgcolor;
  1425.  
  1426.     fgcolor = bgcolor;
  1427.     bgcolor = swap;
  1428.     }
  1429.  
  1430.     init_x(argc, argv, displayname, geometry, fgcolor, bgcolor, hlcolor);
  1431.  
  1432.     init_widgets(widget_notify);
  1433.     make_widgets();
  1434.  
  1435.     init_objects();
  1436.  
  1437.     while (x_event());
  1438.  
  1439.     clean_up();
  1440.     return 0;
  1441. }
  1442.