1 /* -*- c++ -*-
2 FILE: MachineX.cpp
3 RCS REVISION: $Revision: 1.42 $
4
5 COPYRIGHT: (c) 1999 -- 2003 Melinda Green, Don Hatch, and Jay Berkenbilt - Superliminal Software
6
7 LICENSE: Free to use and modify for non-commercial purposes as long as the
8 following conditions are adhered to:
9 1) Obvious credit for the source of this code and the designs it embodies
10 are clearly made, and
11 2) Ports and derived versions of 4D Magic Cube programs are not distributed
12 without the express written permission of the authors.
13
14 DESCRIPTION:
15 This is the Xt/Xlib Machine class.
16 */
17
18 #include "MachineX.h"
19
20 #include <stdio.h>
21 #include <iostream>
22 #include <X11/keysym.h>
23
24 #include "MagicCube.h"
25 #include "Machine.h"
26 #include "WidgetsX.h"
27 #include "Puzzlest.h"
28 #include "EventHandler.h"
29 #include "BitmapsX.h"
30 #include "Preferences.h"
31
32 char* MachineX::default_face_colors[] = {
33 "#0007FFFFF",
34 "#7FF000FFF",
35 "#FFF7FF000",
36 "#FFF0007FF",
37 "#FFF7FFFFF",
38 "#7FFFFFFFF",
39 "#FFFFFF7FF",
40 "#000FFF7FF",
41 };
42
43 XrmOptionDescRec MachineX::options[] = {
44 {"-outline", "*outline", XrmoptionNoArg, "TRUE"},
45 {"-redraw", "*redraw", XrmoptionNoArg, "TRUE"},
46 {"-rightmacros", "*rightmacros", XrmoptionNoArg, "TRUE"},
47 {"-fastautomoves", "*fastautomoves", XrmoptionNoArg, "TRUE"},
48 {"-no-buttons", "*nobuttons", XrmoptionNoArg, "TRUE"},
49 {"-faceshrink", "*faceshrink", XrmoptionSepArg, NULL},
50 {"-degrees", "*degrees", XrmoptionSepArg, NULL},
51 {"-nframes180", "*nframes180", XrmoptionSepArg, NULL},
52 {"-nframes120", "*nframes120", XrmoptionSepArg, NULL},
53 {"-nframes90", "*nframes90", XrmoptionSepArg, NULL},
54 {"-eyew", "*eyew", XrmoptionSepArg, NULL},
55 {"-eyez", "*eyez", XrmoptionSepArg, NULL},
56 {"-nshades", "*nshades", XrmoptionSepArg, NULL},
57 {"-tilt", "*tilt", XrmoptionSepArg, NULL},
58 {"-twirl", "*twirl", XrmoptionSepArg, NULL},
59 {"-nscramblechen", "*nscramblechen", XrmoptionSepArg, NULL},
60 {"-logfile", "*logfile", XrmoptionSepArg, NULL},
61 {"-stickershrink", "*stickershrink", XrmoptionSepArg, NULL},
62 {"-face1", "*faceColor1", XrmoptionSepArg, NULL},
63 {"-face2", "*faceColor2", XrmoptionSepArg, NULL},
64 {"-face3", "*faceColor3", XrmoptionSepArg, NULL},
65 {"-face4", "*faceColor4", XrmoptionSepArg, NULL},
66 {"-face5", "*faceColor5", XrmoptionSepArg, NULL},
67 {"-face6", "*faceColor6", XrmoptionSepArg, NULL},
68 {"-face7", "*faceColor7", XrmoptionSepArg, NULL},
69 {"-face8", "*faceColor8", XrmoptionSepArg, NULL},
70 };
71
72 XtResource MachineX::resources[] = {
73 { "outline", "Outline", XtRBoolean, sizeof(Boolean),
74 XtOffsetOf(AppRes, outline),
75 XtRImmediate, (XtPointer)False },
76 { "redraw", "Redraw", XtRBoolean, sizeof(Boolean),
77 XtOffsetOf(AppRes, redraw),
78 XtRImmediate, (XtPointer)False },
79 { "rightmacros", "Rightmacros", XtRBoolean, sizeof(Boolean),
80 XtOffsetOf(AppRes, rightmacros),
81 XtRImmediate, (XtPointer)False },
82 { "fastautomoves", "Fastautomoves", XtRBoolean, sizeof(Boolean),
83 XtOffsetOf(AppRes, fastautomoves),
84 XtRImmediate, (XtPointer)False },
85 { "nobuttons", "NoButtons", XtRBoolean, sizeof(Boolean),
86 XtOffsetOf(AppRes, nobuttons),
87 XtRImmediate, (XtPointer)False },
88 { "faceshrink", "Faceshrink", XtRString, sizeof(String),
89 XtOffsetOf(AppRes, faceshrink),
90 XtRImmediate, NULL },
91 { "eyew", "Eyew", XtRString, sizeof(String),
92 XtOffsetOf(AppRes, eyew),
93 XtRImmediate, NULL },
94 { "eyez", "Eyez", XtRString, sizeof(String),
95 XtOffsetOf(AppRes, eyez),
96 XtRImmediate, NULL },
97 { "nshades", "Nshades", XtRString, sizeof(String),
98 XtOffsetOf(AppRes, nshades),
99 XtRImmediate, NULL },
100 { "tilt", "Tilt", XtRString, sizeof(String),
101 XtOffsetOf(AppRes, tilt),
102 XtRImmediate, NULL },
103 { "twirl", "Twirl", XtRString, sizeof(String),
104 XtOffsetOf(AppRes, twirl),
105 XtRImmediate, NULL },
106 { "degrees", "Degrees", XtRString, sizeof(String),
107 XtOffsetOf(AppRes, degrees),
108 XtRImmediate, NULL },
109 { "nframes180", "Nframes180", XtRString, sizeof(String),
110 XtOffsetOf(AppRes, nframes180),
111 XtRImmediate, NULL },
112 { "nframes120", "Nframes120", XtRString, sizeof(String),
113 XtOffsetOf(AppRes, nframes120),
114 XtRImmediate, NULL },
115 { "nframes90", "Nframes90", XtRString, sizeof(String),
116 XtOffsetOf(AppRes, nframes90),
117 XtRImmediate, NULL },
118 { "nscramblechen", "Nscramblechen", XtRString, sizeof(String),
119 XtOffsetOf(AppRes, nscramblechen),
120 XtRImmediate, NULL },
121 { "logfile", "Logfile", XtRString, sizeof(String),
122 XtOffsetOf(AppRes, logfile),
123 XtRImmediate, NULL },
124 { "stickershrink", "Stickershrink", XtRString, sizeof(String),
125 XtOffsetOf(AppRes, stickershrink),
126 XtRImmediate, NULL },
127 { "faceColor1", "FaceColor1", XtRString, sizeof(String),
128 XtOffsetOf(AppRes, face_colors[0]),
129 XtRImmediate, default_face_colors[0] },
130 { "faceColor2", "FaceColor2", XtRString, sizeof(String),
131 XtOffsetOf(AppRes, face_colors[1]),
132 XtRImmediate, default_face_colors[1] },
133 { "faceColor3", "FaceColor3", XtRString, sizeof(String),
134 XtOffsetOf(AppRes, face_colors[2]),
135 XtRImmediate, default_face_colors[2] },
136 { "faceColor4", "FaceColor4", XtRString, sizeof(String),
137 XtOffsetOf(AppRes, face_colors[3]),
138 XtRImmediate, default_face_colors[3] },
139 { "faceColor5", "FaceColor5", XtRString, sizeof(String),
140 XtOffsetOf(AppRes, face_colors[4]),
141 XtRImmediate, default_face_colors[4] },
142 { "faceColor6", "FaceColor6", XtRString, sizeof(String),
143 XtOffsetOf(AppRes, face_colors[5]),
144 XtRImmediate, default_face_colors[5] },
145 { "faceColor7", "FaceColor7", XtRString, sizeof(String),
146 XtOffsetOf(AppRes, face_colors[6]),
147 XtRImmediate, default_face_colors[6] },
148 { "faceColor8", "FaceColor8", XtRString, sizeof(String),
149 XtOffsetOf(AppRes, face_colors[7]),
150 XtRImmediate, default_face_colors[7] },
151 };
152
MachineX(EventHandler * event_handler,int & argc,char ** argv,Preferences & prefs)153 MachineX::MachineX(EventHandler* event_handler,
154 int& argc, char** argv, Preferences& prefs) :
155 preferences(prefs),
156 event_handler(event_handler),
157 puzzle_state(0),
158 widgets_x(0),
159 window_title(0),
160 Xverbose(false),
161 do_outline(false),
162 backbuffer(None),
163 current(None)
164 {
165 int length = preferences.getIntProperty(M4D_LENGTH, LENGTH);
166
167 toplevel = XtInitialize(argv[0], "MagicCube4D", options,
168 XtNumber(options), &argc, argv);
169
170 if (argc != 1)
171 {
172 std::cerr << "Some comandline options were ignored" << std::endl;
173 }
174
175 this->resourcesToEnvironment();
176
177 // Expand twiddles in M4D_LOGFILE once and for all now.
178 char* logfile = preferences.getStringProperty(
179 M4D_LOGFILE, expandTwiddles(LOGFILE));
180 putenvFromString("M4D_LOGFILE", logfile);
181
182 // If a logfile exists, try to get the length from there, overriding
183 // any other value.
184 int file_length = getFileLength(logfile);
185 if (file_length)
186 {
187 length = file_length;
188 }
189
190 if (!INRANGE(2 <=, length, <=MAXLENGTH))
191 {
192 std::cerr << "LENGTH must be between 2 and " << MAXLENGTH
193 << " inclusive." << std::endl;
194 exit(2);
195 }
196
197 prefs.setLength(length);
198 }
199
~MachineX()200 MachineX::~MachineX()
201 {
202 // event_handler is under external control
203 delete this->widgets_x;
204 delete [] this->window_title;
205 }
206
207 void
init(PuzzleState * puzzle_state)208 MachineX::init(PuzzleState* puzzle_state)
209 {
210 this->puzzle_state = puzzle_state;
211 static char* title_base = "MagicCube4D version " VERSION " - ";
212
213 if (this->window_title)
214 {
215 XtVaSetValues(toplevel, XtNtitle, title_base, NULL);
216 delete this->window_title;
217 }
218
219 char* logfile = this->preferences.getStringProperty(M4D_LOGFILE);
220 this->window_title = new char[strlen(title_base) + strlen(logfile) + 1];
221 strcpy(this->window_title, title_base);
222 strcat(this->window_title, logfile);
223 XtVaSetValues(toplevel, XtNtitle, this->window_title, NULL);
224
225 setColors();
226
227 // this->widget is the drawing window
228 widgets_x = new WidgetsX(preferences, event_handler,
229 toplevel, this->widget);
230
231 // backbuffer will be created at the first configurenotify or
232 // mapnotify event
233 XtAddEventHandler(widget, StructureNotifyMask, FALSE,
234 (XtEventHandler) MachineX::dispatchEvent,
235 (XtPointer) new EventClosure(this, 0, 0));
236
237 this->Xverbose = preferences.getBoolProperty(M4D_XVERBOSE);
238 this->do_outline = preferences.getBoolProperty(M4D_OUTLINE);
239
240 // NOTE-- this doesn't seem to work
241 // after everything is realized.
242 // Oh well, 512x512 isn't so bad, and the user can stretch
243 // it if she wants
244 int length = preferences.getLength();
245 XtVaSetValues(widget, XtNwidth,
246 MIN(554 * length / 3, 800), // XXX FUDGE for unruly sgi
247 XtNheight, MIN(512 * length / 3, 800), NULL);
248 }
249
250 char*
expandTwiddles(char * s)251 MachineX::expandTwiddles(char *s)
252 {
253 // expand_twiddles on a NULL pointer is a no-op
254 if (s == 0)
255 {
256 return 0;
257 }
258
259 static char buf[200]; // bounds checks implemented
260 unsigned int len = 0;
261
262 for (; *s; ++s)
263 {
264 if (*s == '~')
265 {
266 char *home = getenv("HOME");
267 if (home == 0)
268 {
269 std::cerr << "No home??? Using /tmp" << std::endl;
270 home = "/tmp";
271 }
272 if ((strlen(home) + len + 1) >= sizeof(buf))
273 {
274 std::cerr << "Recompile with bigger buf ("
275 << __FILE__ << ", " << __LINE__ << ")"
276 << std::endl;
277 }
278 strcpy(buf + len, home);
279 len += strlen(home);
280 }
281 else
282 {
283 if ((len + 2) >= sizeof(buf))
284 {
285 std::cerr << "Recompile with bigger buf ("
286 << __FILE__ << ", " << __LINE__ << ")"
287 << std::endl;
288 }
289 buf[len++] = *s;
290 }
291 }
292 buf[len] = 0;
293 return buf;
294 }
295
296 Widgets*
getWidgets()297 MachineX::getWidgets()
298 {
299 return this->widgets_x;
300 }
301
302 void
toggleOutline()303 MachineX::toggleOutline()
304 {
305 do_outline = !do_outline;
306 }
307
308
309 void
dispatchEvent(Widget,XtPointer arg,XEvent * xevent,Boolean *)310 MachineX::dispatchEvent(Widget, XtPointer arg, XEvent* xevent, Boolean*)
311 {
312 EventClosure *closure = (EventClosure *) arg;
313 MachineX* machine = closure->machine;
314 EventHandler::Event event;
315 if (! machine->xeventToEvent(xevent, &event))
316 {
317 return;
318 }
319 if (closure->handler)
320 {
321 (machine->event_handler->*(closure->handler))(&event, closure->arg);
322 }
323 }
324
325 void
putenvFromString(char * variable,char * val)326 MachineX::putenvFromString(char* variable, char* val)
327 {
328 // Do not free allocated space -- putenv needs it
329 if (val && (!getenv(variable)))
330 {
331 // Allocate enough space for VARIABLE=VALUE\0
332 char* t = new char[strlen(variable) + strlen(val) + 2];
333 sprintf(t, "%s=%s", variable, val);
334 putenv(t);
335 }
336 }
337
338 void
putenvFromBool(char * variable,Boolean val)339 MachineX::putenvFromBool(char* variable, Boolean val)
340 {
341 // Do not free allocated space -- putenv needs it
342 if (val && (!getenv(variable)))
343 {
344 // Allocate enough space for VARIABLE=1\0
345 char* t = new char[strlen(variable) + 3];
346 sprintf(t, "%s=1", variable);
347 putenv(t);
348 }
349 }
350
351 void
resourcesToEnvironment()352 MachineX::resourcesToEnvironment()
353 {
354 XtGetApplicationResources(toplevel, (XtPointer) & app_res,
355 resources, XtNumber(resources), NULL, 0);
356
357 // Note: expand_twiddles is safe to call on a null pointer.
358 putenvFromBool(M4D_OUTLINE, app_res.outline);
359 putenvFromBool(M4D_DRAW_NEW_STATE, app_res.redraw);
360 putenvFromBool(M4D_MACROS_ON_RIGHT, app_res.rightmacros);
361 putenvFromBool(M4D_FAST_AUTOMOVES, app_res.fastautomoves);
362 putenvFromBool(M4D_NO_BUTTONS, app_res.nobuttons);
363 putenvFromString(M4D_FACESHRINK, app_res.faceshrink);
364 putenvFromString(M4D_INC, app_res.degrees);
365 putenvFromString(M4D_NFRAMES_120, app_res.nframes120);
366 putenvFromString(M4D_NFRAMES_180, app_res.nframes180);
367 putenvFromString(M4D_NFRAMES_90, app_res.nframes90);
368 putenvFromString(M4D_EYEW, app_res.eyew);
369 putenvFromString(M4D_EYEZ, app_res.eyez);
370 putenvFromString(M4D_NSHADES, app_res.nshades);
371 putenvFromString(M4D_TILT, app_res.tilt);
372 putenvFromString(M4D_TWIRL, app_res.twirl);
373 putenvFromString(M4D_NSCRAMBLECHEN, app_res.nscramblechen);
374 putenvFromString(M4D_LOGFILE, app_res.logfile);
375 putenvFromString(M4D_STICKERSHRINK, app_res.stickershrink);
376 }
377
378 int
getFileLength(char * filename)379 MachineX::getFileLength(char *filename)
380 {
381 // Attempt to guess the value of M4D_LENGTH based on the logfile.
382 // This code makes only a very crude attempt. at doing this.
383
384 int result = 0;
385 FILE* fp;
386 char buf[1024];
387 if ((fp = fopen(filename, "r")))
388 {
389 /* skip the first line */
390 char* p = fgets(buf, sizeof(buf), fp);
391 /* read the second line */
392 if (p)
393 {
394 p = fgets(buf, sizeof(buf), fp);
395 }
396 if (p)
397 {
398 while (strlen(p) &&
399 ((p[strlen(p) - 1] == '\n') || (p[strlen(p) - 1] == '\r')))
400 {
401 p[strlen(p) - 1] = '\0';
402 }
403 int attempt = strlen(p);
404 /*
405 * See if attempt == n^3 for n in valid range of lengths.
406 * If so, assume that we are reading a valid file and get
407 * the length this way. If not, any file errors will be
408 * picked up later.
409 */
410 int i;
411 for (i = 2; i <= MAXLENGTH; ++i)
412 {
413 if (attempt == (i * i * i))
414 {
415 result = i;
416 break;
417 }
418 }
419 }
420 fclose(fp);
421 }
422 return result;
423 }
424
425 void
makeSureTheGCsExistAndEverything()426 MachineX::makeSureTheGCsExistAndEverything()
427 {
428 XGCValues gcvalues;
429 static int called_already = 0;
430 if (called_already)
431 return;
432 called_already = 1;
433
434 gcvalues.function = GXcopy;
435 gcvalues.foreground = white_pixel;
436 gcvalues.background = black_pixel;
437 gc = XCreateGC(XtDisplay(widget), XtWindow(widget),
438 GCFunction | GCForeground | GCBackground, &gcvalues);
439
440 gcvalues.function = GXcopy;
441 gcvalues.foreground = black_pixel;
442 gcvalues.background = white_pixel;
443 bggc = XCreateGC(XtDisplay(widget), XtWindow(widget),
444 GCFunction | GCForeground | GCBackground, &gcvalues);
445 outlinegc = XCreateGC(XtDisplay(widget), XtWindow(widget),
446 GCFunction | GCForeground | GCBackground,
447 &gcvalues);
448
449 /* Set the cursor for the drawing window */
450 Cursor cursor = XCreateFontCursor(XtDisplay(widget), XC_crosshair);
451 XColor black;
452 XColor white;
453 XColor dummy;
454 Colormap cmap;
455 XtVaGetValues(widget, XtNcolormap, &cmap, NULL);
456 XAllocNamedColor(XtDisplay(widget), cmap, "black", &black, &dummy);
457 XAllocNamedColor(XtDisplay(widget), cmap, "white", &white, &dummy);
458 XRecolorCursor(XtDisplay(widget), cursor, &white, &black);
459 XDefineCursor(XtDisplay(widget), XtWindowOfObject(widget), cursor);
460 XFreeCursor(XtDisplay(widget), cursor);
461 }
462
463 int
numberOfAvailableColormapEntries()464 MachineX::numberOfAvailableColormapEntries()
465 {
466 int result = 1;
467 int ddos =
468 DefaultDepthOfScreen(XDefaultScreenOfDisplay(XtDisplay(toplevel)));
469 int i;
470 for (i = 0; (i < ddos) && (result <= 123); ++i)
471 {
472 result <<= 1;
473 }
474 if (result > 123)
475 {
476 result = 123;
477 }
478 return result;
479 }
480
481 // FIX THIS -- currently, this must be called after XtInitialize
482 // but before XtRealizeWidget.
483 void
setColors()484 MachineX::setColors()
485 {
486 static int const min_acceptable_shades = 2;
487
488 real colors[8][3];
489 int blewit, i, shade;
490 XColor xcolor;
491 Colormap cmap;
492 Pixel colors_allocated[2 + 8 * MAXSHADES]; /* for freeing on
493 error */
494 int ncolors_allocated;
495
496 nshades = preferences.getIntProperty(
497 M4D_NSHADES, numberOfAvailableColormapEntries() / 8);
498 if (nshades > MAXSHADES)
499 nshades = MAXSHADES;
500
501 cmap =
502 DefaultColormapOfScreen(XDefaultScreenOfDisplay(XtDisplay(toplevel)));
503
504
505 for (i = 0; i < 8; ++i)
506 {
507 XColor screen_color, exact_color;
508 if (XAllocNamedColor(XtDisplay(toplevel), cmap,
509 app_res.face_colors[i],
510 &screen_color, &exact_color))
511 {
512 colors[i][0] = (real) ((double)exact_color.red / (1L << 16));
513 colors[i][1] = (real) ((double)exact_color.green / (1L << 16));
514 colors[i][2] = (real) ((double)exact_color.blue / (1L << 16));
515 }
516 else
517 {
518 fprintf(stderr, "Color %s is undefined\n", app_res.face_colors[i]);
519 // FIX THIS -- now what?
520 colors[i][0] = colors[i][1] = colors[i][2] = 0.5;
521 }
522 }
523
524
525 if (nshades > 0)
526 {
527 /*
528 * Try once using the default colormap,
529 * and if that fails, try to make a private colormap for the
530 * application's window.
531 */
532
533 /*
534 * FIX THIS-- should try the default colormap
535 * using default values with lower and lower nshades
536 * until it becomes "unacceptably low" (user-defined),
537 * and only then try using a new colormap.
538 */
539
540 for (; nshades >= min_acceptable_shades; --nshades)
541 {
542 ncolors_allocated = 0;
543
544 xcolor.red = xcolor.green = xcolor.blue = (1L << 16) - 1;
545 xcolor.flags = DoRed | DoGreen | DoBlue;
546 if (!XAllocColor(XtDisplay(toplevel), cmap, &xcolor))
547 {
548 fprintf(stderr, "Couldn't XallocColor white??\n");
549 break;
550 }
551 colors_allocated[ncolors_allocated++] = xcolor.pixel;
552 white_pixel = xcolor.pixel;
553
554 xcolor.red = xcolor.green = xcolor.blue = 0;
555 xcolor.flags = DoRed | DoGreen | DoBlue;
556 if (!XAllocColor(XtDisplay(toplevel), cmap, &xcolor))
557 {
558 fprintf(stderr, "Couldn't XallocColor black??\n");
559 break;
560 }
561 colors_allocated[ncolors_allocated++] = xcolor.pixel;
562 black_pixel = xcolor.pixel;
563
564 for (i = 0; i < 8; ++i)
565 {
566 for (shade = nshades - 1; shade >= 0; --shade)
567 {
568 xcolor.red = (unsigned short)
569 (((1L << 16) - 1) * colors[i][0] * (shade + 1)
570 / nshades);
571 xcolor.green = (unsigned short)
572 (((1L << 16) - 1) * colors[i][1] * (shade + 1)
573 / nshades);
574 xcolor.blue = (unsigned short)
575 (((1L << 16) - 1) * colors[i][2] * (shade + 1)
576 / nshades);
577 xcolor.flags = DoRed | DoGreen | DoBlue;
578 if (!XAllocColor(XtDisplay(toplevel), cmap, &xcolor))
579 {
580 fprintf(stderr,
581 "tried nshades = %d: Could only allocate %d out of %d colors.\n",
582 nshades, ncolors_allocated, 2 + 8 * nshades);
583 XFreeColors(XtDisplay(toplevel), cmap,
584 colors_allocated,
585 ncolors_allocated, (Pixel) 0);
586 goto continue_next_nshades;
587 }
588 colors_allocated[ncolors_allocated++] = xcolor.pixel;
589 colormap_entries[i][shade] = xcolor.pixel;
590 }
591 }
592 break; /* success! */
593 continue_next_nshades:;
594 }
595 if (nshades < min_acceptable_shades)
596 nshades = 0;
597 }
598
599 xcolor.red = xcolor.green = xcolor.blue = 0;
600 xcolor.flags = DoRed | DoGreen | DoBlue;
601 if (!XAllocColor(XtDisplay(toplevel), cmap, &xcolor))
602 {
603 fprintf(stderr, "Couldn't XallocColor black??\n");
604 blewit = 1;
605 }
606 black_pixel = xcolor.pixel;
607
608 xcolor.red = xcolor.green = xcolor.blue = (1L << 16) - 1;
609 xcolor.flags = DoRed | DoGreen | DoBlue;
610 if (!XAllocColor(XtDisplay(toplevel), cmap, &xcolor))
611 {
612 fprintf(stderr, "Couldn't XallocColor black??\n");
613 blewit = 1;
614 }
615 white_pixel = xcolor.pixel;
616
617 if (nshades <= 0)
618 {
619 for (i = 0; i < 8; ++i)
620 {
621 stipple_pixmaps[i] = XCreateBitmapFromData(XtDisplay(toplevel),
622 RootWindowOfScreen
623 (XDefaultScreenOfDisplay
624 (XtDisplay
625 (toplevel))),
626 (char *)stippleinfo[i].
627 bits,
628 stippleinfo[i].width,
629 stippleinfo[i].height);
630 }
631 do_outline = 1;
632 }
633 }
634
635 void
drawFrame(struct frame * frame)636 MachineX::drawFrame(struct frame* frame)
637 {
638 assert (this->puzzle_state != 0);
639
640 int i, j, shade;
641 XPoint xpoints[4];
642 XGCValues gcvalues;
643 int minsiz, xoff, yoff;
644
645 if (preferences.getBoolProperty(M4D_DONT_DRAW_FRAME)) /* FIX THIS */
646 return;
647
648 makeSureTheGCsExistAndEverything();
649
650 XFillRectangle(XtDisplay(widget),
651 current, bggc, 0, 0, window_width, window_height);
652
653 /*
654 * Calculate some info to handle non-square windows
655 */
656 minsiz = MIN(window_width, window_height);
657 xoff = (window_width - minsiz) / 2;
658 yoff = (window_height - minsiz) / 2;
659
660 for (i = 0; i < frame->nquads; ++i)
661 {
662 for (j = 0; j < 4; ++j)
663 {
664 xpoints[j].x = xoff + minsiz * frame->verts[frame->quads[i][j]][X]
665 / (1L << 16);
666 xpoints[j].y = yoff + minsiz * frame->verts[frame->quads[i][j]][Y]
667 / (1L << 16);
668 }
669 /*
670 * Daniel says I should add a couple more comments HERE.
671 * So, here's one. I think The Player was an OK movie.
672 */
673 if (nshades)
674 {
675 shade = (int)ROUND(frame->brightnesses[i] * (nshades - 1));
676 if (!INRANGE(0 <=, shade, <nshades))
677 {
678 fprintf(stderr, "Error: got shade %d out of %d\n", shade,
679 nshades);
680 shade = MAX(MIN(shade, nshades - 1), 0);
681 }
682 gcvalues.foreground = colormap_entries
683 [puzzle_state->idToColor((int)frame->quadids[i] / 6)][shade];
684 XChangeGC(XtDisplay(widget), gc, GCForeground, &gcvalues);
685 }
686 else
687 {
688 gcvalues.fill_style = FillOpaqueStippled;
689 gcvalues.stipple = stipple_pixmaps
690 [puzzle_state->idToColor((int)frame->quadids[i] / 6)];
691 XChangeGC(XtDisplay(widget), gc, GCFillStyle | GCStipple,
692 &gcvalues);
693 }
694 XFillPolygon(XtDisplay(widget),
695 current, gc, xpoints, 4, Convex, CoordModeOrigin);
696 /*
697 * Outline them in the background color
698 */
699 if (do_outline)
700 for (j = 0; j < 4; ++j)
701 {
702 XDrawLine(XtDisplay(widget),
703 current,
704 outlinegc,
705 xpoints[j].x, xpoints[j].y,
706 xpoints[(j + 1) % 4].x, xpoints[(j + 1) % 4].y);
707 }
708 }
709
710 if (current == backbuffer)
711 swapBuffers();
712 }
713
714 void
frontBuffer()715 MachineX::frontBuffer()
716 {
717 current = XtWindow(widget);
718 }
719
720 void
backBuffer()721 MachineX::backBuffer()
722 {
723 current = backbuffer;
724 }
725
726 void
swapBuffers()727 MachineX::swapBuffers()
728 {
729 // actually just copies back to front
730 XCopyArea(XtDisplay(widget),
731 backbuffer,
732 XtWindow(widget), gc, 0, 0, window_width, window_height, 0, 0);
733 XFlush(XtDisplay(widget));
734 }
735
736 void
turnBackgroundBlack()737 MachineX::turnBackgroundBlack()
738 {
739 XGCValues gcvalues;
740 gcvalues.foreground = black_pixel;
741 XChangeGC(XtDisplay(widget), bggc, GCForeground, &gcvalues);
742 }
743
744 void
turnBackgroundWhite()745 MachineX::turnBackgroundWhite()
746 {
747 XGCValues gcvalues;
748 gcvalues.foreground = white_pixel;
749 XChangeGC(XtDisplay(widget), bggc, GCForeground, &gcvalues);
750 }
751
752 void
makeNewBackbuffer()753 MachineX::makeNewBackbuffer()
754 {
755 unsigned int dummy;
756 unsigned long ldummy;
757 int idummy;
758 XGetGeometry(XtDisplay(widget), XtWindow(widget),
759 &ldummy, &idummy, &idummy,
760 &window_width, &window_height, &dummy, &window_depth);
761 if (backbuffer != None)
762 XFreePixmap(XtDisplay(widget), backbuffer);
763 backbuffer = XCreatePixmap(XtDisplay(widget), XtWindow(widget),
764 window_width, window_height, window_depth);
765 if (current != XtWindow(widget))
766 backBuffer();
767 }
768
769 // Event stuff
770
771 // Return true on success, false if it translates to no event.
772 // FIX THIS--- has side effects which should not be here.
773 bool
xeventToEvent(XEvent * xevent,EventHandler::Event * event)774 MachineX::xeventToEvent(XEvent* xevent, EventHandler::Event* event)
775 {
776 static int ox = 0, oy = 0;
777 int minsiz, xoff, yoff;
778
779 // Convert the x event to our event type
780 switch (xevent->type)
781 {
782 case Expose:
783 if (backbuffer == None)
784 makeNewBackbuffer();
785 event->type = EventHandler::EXPOSE;
786 event->x = xevent->xexpose.x;
787 event->y = xevent->xexpose.y;
788 event->w = xevent->xexpose.width;
789 event->h = xevent->xexpose.height;
790 if (Xverbose)
791 printf("Got Expose X Event, exposed %dx%d+%d+%d\n",
792 event->w, event->h, event->x, event->y);
793 if (xevent->xexpose.count)
794 return 0; /* throw away all but last */
795 return 1;
796 case KeyPress:
797 case KeyRelease:
798 {
799 char buf[20];
800 int len;
801 len = XLookupString(&xevent->xkey, buf, sizeof(buf),
802 (KeySym *) NULL, (XComposeStatus *) NULL);
803 buf[MIN(len, (int)(sizeof(buf) - 1))] = 0;
804 if (Xverbose)
805 printf("Got KeyPress X Event, string = \"%s\"\n", buf);
806 if (len != 1)
807 return 0;
808 event->type = (xevent->type == KeyPress ?
809 EventHandler::KEYPRESS : EventHandler::KEYRELEASE);
810 event->key = buf[0];
811 event->x = xevent->xkey.x;
812 event->y = xevent->xkey.y;
813 }
814
815 return 1;
816 case ButtonPress:
817 if (xevent->type == ButtonPress) /* this is ridiculous */
818 if (Xverbose)
819 printf("Got ButtonPress X Event\n");
820 /* fall through */
821 case ButtonRelease:
822 if (xevent->type == ButtonRelease) /* this is ridiculous */
823 if (Xverbose)
824 printf("Got ButtonRelease X Event\n");
825 event->type = (xevent->type == ButtonPress ?
826 EventHandler::BUTTONDOWN : EventHandler::BUTTONUP);
827 event->shift_is_down = (xevent->xbutton.state & ShiftMask) != 0;
828 event->control_is_down = (xevent->xbutton.state & ControlMask) != 0;
829 switch (xevent->xbutton.button)
830 {
831 case Button1:
832 event->button = EventHandler::LEFTBUTTON;
833 break;
834 case Button2:
835 event->button = EventHandler::MIDDLEBUTTON;
836 break;
837 case Button3:
838 event->button = EventHandler::RIGHTBUTTON;
839 break;
840 default:
841 fprintf(stderr,
842 "machinex_dispatch_event: don't recognize X button %d\n",
843 (int)xevent->xbutton.button);
844 break;
845 }
846
847 /*
848 * adjust for non-square window
849 */
850 minsiz = MIN(window_width, window_height);
851 xoff = (window_width - minsiz) / 2;
852 yoff = (window_height - minsiz) / 2;
853
854 event->x = (1L << 16) * (xevent->xbutton.x - xoff) / minsiz;
855 event->y = (1L << 16) * (xevent->xbutton.y - yoff) / minsiz;
856 ox = xevent->xbutton.x; /* FIX THIS-- this is neurotic */
857 oy = xevent->xbutton.y;
858 return 1;
859 case MotionNotify:
860 if (Xverbose)
861 printf("Got PointerMotion X event at %d,%d\n", xevent->xmotion.x,
862 xevent->xmotion.y);
863 if (xevent->xmotion.state & Button1Mask)
864 event->button = EventHandler::LEFTBUTTON;
865 else if (xevent->xmotion.state & Button2Mask)
866 event->button = EventHandler::MIDDLEBUTTON;
867 else if (xevent->xmotion.state & Button3Mask)
868 event->button = EventHandler::RIGHTBUTTON;
869 else
870 return 0; /* no button was down; we don't care about
871 such events */
872 event->type = EventHandler::DRAG;
873 event->shift_is_down = (xevent->xmotion.state & ShiftMask) != 0;
874 event->control_is_down = (xevent->xmotion.state & ControlMask) != 0;
875
876 /* FIX THIS-- the following does not belong in this function */
877 if (!preferences.getBoolProperty(M4D_NOCOMPRESSMOTION))
878 while (XCheckMaskEvent(xevent->xmotion.display,
879 PointerMotionMask, xevent))
880 ;
881
882 event->x = (1L << 16) * xevent->xmotion.x / window_width;
883 event->y = (1L << 16) * xevent->xmotion.y / window_height;
884
885 event->dx = xevent->xmotion.x - ox;
886 event->dy = xevent->xmotion.y - oy;
887 ox = xevent->xmotion.x; /* FIX THIS-- this is neurotic */
888 oy = xevent->xmotion.y;
889
890 return 1;
891 case MapNotify:
892 if (Xverbose)
893 printf("Got MapNotify X event\n");
894 /* fall through */
895 case ConfigureNotify:
896 {
897 if (xevent->type == ConfigureNotify)
898 { /* this is ridiculous */
899 if (Xverbose)
900 printf("Got ConfigureNotify X event\n");
901 event->type = EventHandler::RESIZE;
902 if (Xverbose)
903 printf("Changing from %dx%d ", (int)window_width,
904 (int)window_height);
905 }
906
907 /*
908 * Make a new back buffer
909 */
910 makeNewBackbuffer();
911
912 if (xevent->type == ConfigureNotify) /* this is ridiculous */
913 if (Xverbose)
914 printf("to %dx%d\n", (int)window_width,
915 (int)window_height);
916
917
918 event->w = window_width;
919 event->h = window_height;
920 if (xevent->type == MapNotify)
921 {
922 event->type = EventHandler::RESIZE;
923 /* FIX THIS-- we need to notify the application, since if
924 there is no wm running, two MapNotify events will be the
925 only events generated on startup. We need to intelligently
926 figure out what the situation is.. */
927 }
928
929 return 1;
930 }
931 case UnmapNotify:
932 if (Xverbose)
933 printf("Got UnmapNotify X event\n");
934 return 0;
935 case ReparentNotify:
936 if (Xverbose)
937 printf("Got ReparentNotify X event\n");
938 return 0;
939 default:
940 fprintf(stderr,
941 "xevent_to_event: don't recognize X event type %d\n",
942 xevent->type);
943 return 0;
944 }
945 // return 0; /* sgi compiler thinks this is reachable. yeah, right. */
946 }
947
948 EventMask
eventMaskToXEventMask(unsigned int eventmask)949 MachineX::eventMaskToXEventMask(unsigned int eventmask)
950 {
951 EventMask xeventmask = 0;
952 if (BIT(&eventmask, EventHandler::EXPOSE))
953 xeventmask |= ExposureMask;
954 if (BIT(&eventmask, EventHandler::RESIZE))
955 xeventmask |= StructureNotifyMask;
956 if (BIT(&eventmask, EventHandler::BUTTONDOWN))
957 xeventmask |= ButtonPressMask;
958 if (BIT(&eventmask, EventHandler::BUTTONUP))
959 xeventmask |= ButtonReleaseMask;
960 if (BIT(&eventmask, EventHandler::KEYPRESS))
961 xeventmask |= KeyPressMask;
962 if (BIT(&eventmask, EventHandler::KEYRELEASE))
963 xeventmask |= KeyReleaseMask;
964 if (BIT(&eventmask, EventHandler::DRAG))
965 xeventmask |=
966 (Button1MotionMask | Button2MotionMask | Button3MotionMask);
967 return xeventmask;
968 }
969
970 void
addEventHandler(unsigned int eventmask,Machine::event_handler func,void * arg)971 MachineX::addEventHandler(unsigned int eventmask,
972 Machine::event_handler func,
973 void *arg)
974 {
975 EventMask xeventmask = eventMaskToXEventMask(eventmask);
976 XtAddEventHandler(widget, xeventmask, FALSE,
977 MachineX::dispatchEvent,
978 new EventClosure(this, func, arg));
979 }
980
981 // Return true and fill the event if there is one, false if not
982 bool
getEventIfThereIsOne(int eventmask,EventHandler::Event * event)983 MachineX::getEventIfThereIsOne(int eventmask, EventHandler::Event *event)
984 {
985 bool result = false;
986 XEvent xevent;
987 EventMask xeventmask = eventMaskToXEventMask(eventmask);
988 XFlush(XtDisplay(widget));
989 if (XCheckMaskEvent(XtDisplay(widget), xeventmask, &xevent))
990 {
991 if (!xeventToEvent(&xevent, event))
992 {
993 std::cout << "busy loop: ARGH!" << std::endl;
994 }
995 else
996 {
997 result = true;
998 }
999 }
1000 return result;
1001 }
1002
1003 void
bell()1004 MachineX::bell()
1005 {
1006 XBell(XtDisplay(widget), 100);
1007 }
1008
1009 void
eventLoop()1010 MachineX::eventLoop()
1011 {
1012 XtRealizeWidget(toplevel);
1013
1014 XtMainLoop();
1015 }
1016
1017 void
XtFPrintWidgetName(FILE * fp,Widget wid)1018 MachineX::XtFPrintWidgetName(FILE *fp, Widget wid)
1019 {
1020 if (XtParent(wid))
1021 {
1022 XtFPrintWidgetName(fp, XtParent(wid));
1023 fprintf(fp, ".");
1024 }
1025 fprintf(fp, "%s", XtName(wid));
1026 }
1027
1028 void
XtPrintWidgetName(Widget wid)1029 MachineX::XtPrintWidgetName(Widget wid)
1030 {
1031 XtFPrintWidgetName(stdout, wid);
1032 }
1033
1034 // Local Variables:
1035 // c-basic-offset: 4
1036 // c-comment-only-line-offset: 0
1037 // c-file-offsets: ((defun-block-intro . +) (block-open . 0) (substatement-open . 0) (statement-cont . +) (statement-case-open . +4) (arglist-intro . +) (arglist-close . +) (inline-open . 0))
1038 // indent-tabs-mode: nil
1039 // End:
1040