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