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