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