/* -*- c++ -*- FILE: MachineX.cpp RCS REVISION: $Revision: 1.42 $ COPYRIGHT: (c) 1999 -- 2003 Melinda Green, Don Hatch, and Jay Berkenbilt - Superliminal Software LICENSE: Free to use and modify for non-commercial purposes as long as the following conditions are adhered to: 1) Obvious credit for the source of this code and the designs it embodies are clearly made, and 2) Ports and derived versions of 4D Magic Cube programs are not distributed without the express written permission of the authors. DESCRIPTION: This is the Xt/Xlib Machine class. */ #include "MachineX.h" #include #include #include #include "MagicCube.h" #include "Machine.h" #include "WidgetsX.h" #include "Puzzlest.h" #include "EventHandler.h" #include "BitmapsX.h" #include "Preferences.h" char* MachineX::default_face_colors[] = { "#0007FFFFF", "#7FF000FFF", "#FFF7FF000", "#FFF0007FF", "#FFF7FFFFF", "#7FFFFFFFF", "#FFFFFF7FF", "#000FFF7FF", }; XrmOptionDescRec MachineX::options[] = { {"-outline", "*outline", XrmoptionNoArg, "TRUE"}, {"-redraw", "*redraw", XrmoptionNoArg, "TRUE"}, {"-rightmacros", "*rightmacros", XrmoptionNoArg, "TRUE"}, {"-fastautomoves", "*fastautomoves", XrmoptionNoArg, "TRUE"}, {"-no-buttons", "*nobuttons", XrmoptionNoArg, "TRUE"}, {"-faceshrink", "*faceshrink", XrmoptionSepArg, NULL}, {"-degrees", "*degrees", XrmoptionSepArg, NULL}, {"-nframes180", "*nframes180", XrmoptionSepArg, NULL}, {"-nframes120", "*nframes120", XrmoptionSepArg, NULL}, {"-nframes90", "*nframes90", XrmoptionSepArg, NULL}, {"-eyew", "*eyew", XrmoptionSepArg, NULL}, {"-eyez", "*eyez", XrmoptionSepArg, NULL}, {"-nshades", "*nshades", XrmoptionSepArg, NULL}, {"-tilt", "*tilt", XrmoptionSepArg, NULL}, {"-twirl", "*twirl", XrmoptionSepArg, NULL}, {"-nscramblechen", "*nscramblechen", XrmoptionSepArg, NULL}, {"-logfile", "*logfile", XrmoptionSepArg, NULL}, {"-stickershrink", "*stickershrink", XrmoptionSepArg, NULL}, {"-face1", "*faceColor1", XrmoptionSepArg, NULL}, {"-face2", "*faceColor2", XrmoptionSepArg, NULL}, {"-face3", "*faceColor3", XrmoptionSepArg, NULL}, {"-face4", "*faceColor4", XrmoptionSepArg, NULL}, {"-face5", "*faceColor5", XrmoptionSepArg, NULL}, {"-face6", "*faceColor6", XrmoptionSepArg, NULL}, {"-face7", "*faceColor7", XrmoptionSepArg, NULL}, {"-face8", "*faceColor8", XrmoptionSepArg, NULL}, }; XtResource MachineX::resources[] = { { "outline", "Outline", XtRBoolean, sizeof(Boolean), XtOffsetOf(AppRes, outline), XtRImmediate, (XtPointer)False }, { "redraw", "Redraw", XtRBoolean, sizeof(Boolean), XtOffsetOf(AppRes, redraw), XtRImmediate, (XtPointer)False }, { "rightmacros", "Rightmacros", XtRBoolean, sizeof(Boolean), XtOffsetOf(AppRes, rightmacros), XtRImmediate, (XtPointer)False }, { "fastautomoves", "Fastautomoves", XtRBoolean, sizeof(Boolean), XtOffsetOf(AppRes, fastautomoves), XtRImmediate, (XtPointer)False }, { "nobuttons", "NoButtons", XtRBoolean, sizeof(Boolean), XtOffsetOf(AppRes, nobuttons), XtRImmediate, (XtPointer)False }, { "faceshrink", "Faceshrink", XtRString, sizeof(String), XtOffsetOf(AppRes, faceshrink), XtRImmediate, NULL }, { "eyew", "Eyew", XtRString, sizeof(String), XtOffsetOf(AppRes, eyew), XtRImmediate, NULL }, { "eyez", "Eyez", XtRString, sizeof(String), XtOffsetOf(AppRes, eyez), XtRImmediate, NULL }, { "nshades", "Nshades", XtRString, sizeof(String), XtOffsetOf(AppRes, nshades), XtRImmediate, NULL }, { "tilt", "Tilt", XtRString, sizeof(String), XtOffsetOf(AppRes, tilt), XtRImmediate, NULL }, { "twirl", "Twirl", XtRString, sizeof(String), XtOffsetOf(AppRes, twirl), XtRImmediate, NULL }, { "degrees", "Degrees", XtRString, sizeof(String), XtOffsetOf(AppRes, degrees), XtRImmediate, NULL }, { "nframes180", "Nframes180", XtRString, sizeof(String), XtOffsetOf(AppRes, nframes180), XtRImmediate, NULL }, { "nframes120", "Nframes120", XtRString, sizeof(String), XtOffsetOf(AppRes, nframes120), XtRImmediate, NULL }, { "nframes90", "Nframes90", XtRString, sizeof(String), XtOffsetOf(AppRes, nframes90), XtRImmediate, NULL }, { "nscramblechen", "Nscramblechen", XtRString, sizeof(String), XtOffsetOf(AppRes, nscramblechen), XtRImmediate, NULL }, { "logfile", "Logfile", XtRString, sizeof(String), XtOffsetOf(AppRes, logfile), XtRImmediate, NULL }, { "stickershrink", "Stickershrink", XtRString, sizeof(String), XtOffsetOf(AppRes, stickershrink), XtRImmediate, NULL }, { "faceColor1", "FaceColor1", XtRString, sizeof(String), XtOffsetOf(AppRes, face_colors[0]), XtRImmediate, default_face_colors[0] }, { "faceColor2", "FaceColor2", XtRString, sizeof(String), XtOffsetOf(AppRes, face_colors[1]), XtRImmediate, default_face_colors[1] }, { "faceColor3", "FaceColor3", XtRString, sizeof(String), XtOffsetOf(AppRes, face_colors[2]), XtRImmediate, default_face_colors[2] }, { "faceColor4", "FaceColor4", XtRString, sizeof(String), XtOffsetOf(AppRes, face_colors[3]), XtRImmediate, default_face_colors[3] }, { "faceColor5", "FaceColor5", XtRString, sizeof(String), XtOffsetOf(AppRes, face_colors[4]), XtRImmediate, default_face_colors[4] }, { "faceColor6", "FaceColor6", XtRString, sizeof(String), XtOffsetOf(AppRes, face_colors[5]), XtRImmediate, default_face_colors[5] }, { "faceColor7", "FaceColor7", XtRString, sizeof(String), XtOffsetOf(AppRes, face_colors[6]), XtRImmediate, default_face_colors[6] }, { "faceColor8", "FaceColor8", XtRString, sizeof(String), XtOffsetOf(AppRes, face_colors[7]), XtRImmediate, default_face_colors[7] }, }; MachineX::MachineX(EventHandler* event_handler, int& argc, char** argv, Preferences& prefs) : preferences(prefs), event_handler(event_handler), puzzle_state(0), widgets_x(0), window_title(0), Xverbose(false), do_outline(false), backbuffer(None), current(None) { int length = preferences.getIntProperty(M4D_LENGTH, LENGTH); toplevel = XtInitialize(argv[0], "MagicCube4D", options, XtNumber(options), &argc, argv); if (argc != 1) { std::cerr << "Some comandline options were ignored" << std::endl; } this->resourcesToEnvironment(); // Expand twiddles in M4D_LOGFILE once and for all now. char* logfile = preferences.getStringProperty( M4D_LOGFILE, expandTwiddles(LOGFILE)); putenvFromString("M4D_LOGFILE", logfile); // If a logfile exists, try to get the length from there, overriding // any other value. int file_length = getFileLength(logfile); if (file_length) { length = file_length; } if (!INRANGE(2 <=, length, <=MAXLENGTH)) { std::cerr << "LENGTH must be between 2 and " << MAXLENGTH << " inclusive." << std::endl; exit(2); } prefs.setLength(length); } MachineX::~MachineX() { // event_handler is under external control delete this->widgets_x; delete [] this->window_title; } void MachineX::init(PuzzleState* puzzle_state) { this->puzzle_state = puzzle_state; static char* title_base = "MagicCube4D version " VERSION " - "; if (this->window_title) { XtVaSetValues(toplevel, XtNtitle, title_base, NULL); delete this->window_title; } char* logfile = this->preferences.getStringProperty(M4D_LOGFILE); this->window_title = new char[strlen(title_base) + strlen(logfile) + 1]; strcpy(this->window_title, title_base); strcat(this->window_title, logfile); XtVaSetValues(toplevel, XtNtitle, this->window_title, NULL); setColors(); // this->widget is the drawing window widgets_x = new WidgetsX(preferences, event_handler, toplevel, this->widget); // backbuffer will be created at the first configurenotify or // mapnotify event XtAddEventHandler(widget, StructureNotifyMask, FALSE, (XtEventHandler) MachineX::dispatchEvent, (XtPointer) new EventClosure(this, 0, 0)); this->Xverbose = preferences.getBoolProperty(M4D_XVERBOSE); this->do_outline = preferences.getBoolProperty(M4D_OUTLINE); // NOTE-- this doesn't seem to work // after everything is realized. // Oh well, 512x512 isn't so bad, and the user can stretch // it if she wants int length = preferences.getLength(); XtVaSetValues(widget, XtNwidth, MIN(554 * length / 3, 800), // XXX FUDGE for unruly sgi XtNheight, MIN(512 * length / 3, 800), NULL); } char* MachineX::expandTwiddles(char *s) { // expand_twiddles on a NULL pointer is a no-op if (s == 0) { return 0; } static char buf[200]; // bounds checks implemented unsigned int len = 0; for (; *s; ++s) { if (*s == '~') { char *home = getenv("HOME"); if (home == 0) { std::cerr << "No home??? Using /tmp" << std::endl; home = "/tmp"; } if ((strlen(home) + len + 1) >= sizeof(buf)) { std::cerr << "Recompile with bigger buf (" << __FILE__ << ", " << __LINE__ << ")" << std::endl; } strcpy(buf + len, home); len += strlen(home); } else { if ((len + 2) >= sizeof(buf)) { std::cerr << "Recompile with bigger buf (" << __FILE__ << ", " << __LINE__ << ")" << std::endl; } buf[len++] = *s; } } buf[len] = 0; return buf; } Widgets* MachineX::getWidgets() { return this->widgets_x; } void MachineX::toggleOutline() { do_outline = !do_outline; } void MachineX::dispatchEvent(Widget, XtPointer arg, XEvent* xevent, Boolean*) { EventClosure *closure = (EventClosure *) arg; MachineX* machine = closure->machine; EventHandler::Event event; if (! machine->xeventToEvent(xevent, &event)) { return; } if (closure->handler) { (machine->event_handler->*(closure->handler))(&event, closure->arg); } } void MachineX::putenvFromString(char* variable, char* val) { // Do not free allocated space -- putenv needs it if (val && (!getenv(variable))) { // Allocate enough space for VARIABLE=VALUE\0 char* t = new char[strlen(variable) + strlen(val) + 2]; sprintf(t, "%s=%s", variable, val); putenv(t); } } void MachineX::putenvFromBool(char* variable, Boolean val) { // Do not free allocated space -- putenv needs it if (val && (!getenv(variable))) { // Allocate enough space for VARIABLE=1\0 char* t = new char[strlen(variable) + 3]; sprintf(t, "%s=1", variable); putenv(t); } } void MachineX::resourcesToEnvironment() { XtGetApplicationResources(toplevel, (XtPointer) & app_res, resources, XtNumber(resources), NULL, 0); // Note: expand_twiddles is safe to call on a null pointer. putenvFromBool(M4D_OUTLINE, app_res.outline); putenvFromBool(M4D_DRAW_NEW_STATE, app_res.redraw); putenvFromBool(M4D_MACROS_ON_RIGHT, app_res.rightmacros); putenvFromBool(M4D_FAST_AUTOMOVES, app_res.fastautomoves); putenvFromBool(M4D_NO_BUTTONS, app_res.nobuttons); putenvFromString(M4D_FACESHRINK, app_res.faceshrink); putenvFromString(M4D_INC, app_res.degrees); putenvFromString(M4D_NFRAMES_120, app_res.nframes120); putenvFromString(M4D_NFRAMES_180, app_res.nframes180); putenvFromString(M4D_NFRAMES_90, app_res.nframes90); putenvFromString(M4D_EYEW, app_res.eyew); putenvFromString(M4D_EYEZ, app_res.eyez); putenvFromString(M4D_NSHADES, app_res.nshades); putenvFromString(M4D_TILT, app_res.tilt); putenvFromString(M4D_TWIRL, app_res.twirl); putenvFromString(M4D_NSCRAMBLECHEN, app_res.nscramblechen); putenvFromString(M4D_LOGFILE, app_res.logfile); putenvFromString(M4D_STICKERSHRINK, app_res.stickershrink); } int MachineX::getFileLength(char *filename) { // Attempt to guess the value of M4D_LENGTH based on the logfile. // This code makes only a very crude attempt. at doing this. int result = 0; FILE* fp; char buf[1024]; if ((fp = fopen(filename, "r"))) { /* skip the first line */ char* p = fgets(buf, sizeof(buf), fp); /* read the second line */ if (p) { p = fgets(buf, sizeof(buf), fp); } if (p) { while (strlen(p) && ((p[strlen(p) - 1] == '\n') || (p[strlen(p) - 1] == '\r'))) { p[strlen(p) - 1] = '\0'; } int attempt = strlen(p); /* * See if attempt == n^3 for n in valid range of lengths. * If so, assume that we are reading a valid file and get * the length this way. If not, any file errors will be * picked up later. */ int i; for (i = 2; i <= MAXLENGTH; ++i) { if (attempt == (i * i * i)) { result = i; break; } } } fclose(fp); } return result; } void MachineX::makeSureTheGCsExistAndEverything() { XGCValues gcvalues; static int called_already = 0; if (called_already) return; called_already = 1; gcvalues.function = GXcopy; gcvalues.foreground = white_pixel; gcvalues.background = black_pixel; gc = XCreateGC(XtDisplay(widget), XtWindow(widget), GCFunction | GCForeground | GCBackground, &gcvalues); gcvalues.function = GXcopy; gcvalues.foreground = black_pixel; gcvalues.background = white_pixel; bggc = XCreateGC(XtDisplay(widget), XtWindow(widget), GCFunction | GCForeground | GCBackground, &gcvalues); outlinegc = XCreateGC(XtDisplay(widget), XtWindow(widget), GCFunction | GCForeground | GCBackground, &gcvalues); /* Set the cursor for the drawing window */ Cursor cursor = XCreateFontCursor(XtDisplay(widget), XC_crosshair); XColor black; XColor white; XColor dummy; Colormap cmap; XtVaGetValues(widget, XtNcolormap, &cmap, NULL); XAllocNamedColor(XtDisplay(widget), cmap, "black", &black, &dummy); XAllocNamedColor(XtDisplay(widget), cmap, "white", &white, &dummy); XRecolorCursor(XtDisplay(widget), cursor, &white, &black); XDefineCursor(XtDisplay(widget), XtWindowOfObject(widget), cursor); XFreeCursor(XtDisplay(widget), cursor); } int MachineX::numberOfAvailableColormapEntries() { int result = 1; int ddos = DefaultDepthOfScreen(XDefaultScreenOfDisplay(XtDisplay(toplevel))); int i; for (i = 0; (i < ddos) && (result <= 123); ++i) { result <<= 1; } if (result > 123) { result = 123; } return result; } // FIX THIS -- currently, this must be called after XtInitialize // but before XtRealizeWidget. void MachineX::setColors() { static int const min_acceptable_shades = 2; real colors[8][3]; int blewit, i, shade; XColor xcolor; Colormap cmap; Pixel colors_allocated[2 + 8 * MAXSHADES]; /* for freeing on error */ int ncolors_allocated; nshades = preferences.getIntProperty( M4D_NSHADES, numberOfAvailableColormapEntries() / 8); if (nshades > MAXSHADES) nshades = MAXSHADES; cmap = DefaultColormapOfScreen(XDefaultScreenOfDisplay(XtDisplay(toplevel))); for (i = 0; i < 8; ++i) { XColor screen_color, exact_color; if (XAllocNamedColor(XtDisplay(toplevel), cmap, app_res.face_colors[i], &screen_color, &exact_color)) { colors[i][0] = (real) ((double)exact_color.red / (1L << 16)); colors[i][1] = (real) ((double)exact_color.green / (1L << 16)); colors[i][2] = (real) ((double)exact_color.blue / (1L << 16)); } else { fprintf(stderr, "Color %s is undefined\n", app_res.face_colors[i]); // FIX THIS -- now what? colors[i][0] = colors[i][1] = colors[i][2] = 0.5; } } if (nshades > 0) { /* * Try once using the default colormap, * and if that fails, try to make a private colormap for the * application's window. */ /* * FIX THIS-- should try the default colormap * using default values with lower and lower nshades * until it becomes "unacceptably low" (user-defined), * and only then try using a new colormap. */ for (; nshades >= min_acceptable_shades; --nshades) { ncolors_allocated = 0; xcolor.red = xcolor.green = xcolor.blue = (1L << 16) - 1; xcolor.flags = DoRed | DoGreen | DoBlue; if (!XAllocColor(XtDisplay(toplevel), cmap, &xcolor)) { fprintf(stderr, "Couldn't XallocColor white??\n"); break; } colors_allocated[ncolors_allocated++] = xcolor.pixel; white_pixel = xcolor.pixel; xcolor.red = xcolor.green = xcolor.blue = 0; xcolor.flags = DoRed | DoGreen | DoBlue; if (!XAllocColor(XtDisplay(toplevel), cmap, &xcolor)) { fprintf(stderr, "Couldn't XallocColor black??\n"); break; } colors_allocated[ncolors_allocated++] = xcolor.pixel; black_pixel = xcolor.pixel; for (i = 0; i < 8; ++i) { for (shade = nshades - 1; shade >= 0; --shade) { xcolor.red = (unsigned short) (((1L << 16) - 1) * colors[i][0] * (shade + 1) / nshades); xcolor.green = (unsigned short) (((1L << 16) - 1) * colors[i][1] * (shade + 1) / nshades); xcolor.blue = (unsigned short) (((1L << 16) - 1) * colors[i][2] * (shade + 1) / nshades); xcolor.flags = DoRed | DoGreen | DoBlue; if (!XAllocColor(XtDisplay(toplevel), cmap, &xcolor)) { fprintf(stderr, "tried nshades = %d: Could only allocate %d out of %d colors.\n", nshades, ncolors_allocated, 2 + 8 * nshades); XFreeColors(XtDisplay(toplevel), cmap, colors_allocated, ncolors_allocated, (Pixel) 0); goto continue_next_nshades; } colors_allocated[ncolors_allocated++] = xcolor.pixel; colormap_entries[i][shade] = xcolor.pixel; } } break; /* success! */ continue_next_nshades:; } if (nshades < min_acceptable_shades) nshades = 0; } xcolor.red = xcolor.green = xcolor.blue = 0; xcolor.flags = DoRed | DoGreen | DoBlue; if (!XAllocColor(XtDisplay(toplevel), cmap, &xcolor)) { fprintf(stderr, "Couldn't XallocColor black??\n"); blewit = 1; } black_pixel = xcolor.pixel; xcolor.red = xcolor.green = xcolor.blue = (1L << 16) - 1; xcolor.flags = DoRed | DoGreen | DoBlue; if (!XAllocColor(XtDisplay(toplevel), cmap, &xcolor)) { fprintf(stderr, "Couldn't XallocColor black??\n"); blewit = 1; } white_pixel = xcolor.pixel; if (nshades <= 0) { for (i = 0; i < 8; ++i) { stipple_pixmaps[i] = XCreateBitmapFromData(XtDisplay(toplevel), RootWindowOfScreen (XDefaultScreenOfDisplay (XtDisplay (toplevel))), (char *)stippleinfo[i]. bits, stippleinfo[i].width, stippleinfo[i].height); } do_outline = 1; } } void MachineX::drawFrame(struct frame* frame) { assert (this->puzzle_state != 0); int i, j, shade; XPoint xpoints[4]; XGCValues gcvalues; int minsiz, xoff, yoff; if (preferences.getBoolProperty(M4D_DONT_DRAW_FRAME)) /* FIX THIS */ return; makeSureTheGCsExistAndEverything(); XFillRectangle(XtDisplay(widget), current, bggc, 0, 0, window_width, window_height); /* * Calculate some info to handle non-square windows */ minsiz = MIN(window_width, window_height); xoff = (window_width - minsiz) / 2; yoff = (window_height - minsiz) / 2; for (i = 0; i < frame->nquads; ++i) { for (j = 0; j < 4; ++j) { xpoints[j].x = xoff + minsiz * frame->verts[frame->quads[i][j]][X] / (1L << 16); xpoints[j].y = yoff + minsiz * frame->verts[frame->quads[i][j]][Y] / (1L << 16); } /* * Daniel says I should add a couple more comments HERE. * So, here's one. I think The Player was an OK movie. */ if (nshades) { shade = (int)ROUND(frame->brightnesses[i] * (nshades - 1)); if (!INRANGE(0 <=, shade, idToColor((int)frame->quadids[i] / 6)][shade]; XChangeGC(XtDisplay(widget), gc, GCForeground, &gcvalues); } else { gcvalues.fill_style = FillOpaqueStippled; gcvalues.stipple = stipple_pixmaps [puzzle_state->idToColor((int)frame->quadids[i] / 6)]; XChangeGC(XtDisplay(widget), gc, GCFillStyle | GCStipple, &gcvalues); } XFillPolygon(XtDisplay(widget), current, gc, xpoints, 4, Convex, CoordModeOrigin); /* * Outline them in the background color */ if (do_outline) for (j = 0; j < 4; ++j) { XDrawLine(XtDisplay(widget), current, outlinegc, xpoints[j].x, xpoints[j].y, xpoints[(j + 1) % 4].x, xpoints[(j + 1) % 4].y); } } if (current == backbuffer) swapBuffers(); } void MachineX::frontBuffer() { current = XtWindow(widget); } void MachineX::backBuffer() { current = backbuffer; } void MachineX::swapBuffers() { // actually just copies back to front XCopyArea(XtDisplay(widget), backbuffer, XtWindow(widget), gc, 0, 0, window_width, window_height, 0, 0); XFlush(XtDisplay(widget)); } void MachineX::turnBackgroundBlack() { XGCValues gcvalues; gcvalues.foreground = black_pixel; XChangeGC(XtDisplay(widget), bggc, GCForeground, &gcvalues); } void MachineX::turnBackgroundWhite() { XGCValues gcvalues; gcvalues.foreground = white_pixel; XChangeGC(XtDisplay(widget), bggc, GCForeground, &gcvalues); } void MachineX::makeNewBackbuffer() { unsigned int dummy; unsigned long ldummy; int idummy; XGetGeometry(XtDisplay(widget), XtWindow(widget), &ldummy, &idummy, &idummy, &window_width, &window_height, &dummy, &window_depth); if (backbuffer != None) XFreePixmap(XtDisplay(widget), backbuffer); backbuffer = XCreatePixmap(XtDisplay(widget), XtWindow(widget), window_width, window_height, window_depth); if (current != XtWindow(widget)) backBuffer(); } // Event stuff // Return true on success, false if it translates to no event. // FIX THIS--- has side effects which should not be here. bool MachineX::xeventToEvent(XEvent* xevent, EventHandler::Event* event) { static int ox = 0, oy = 0; int minsiz, xoff, yoff; // Convert the x event to our event type switch (xevent->type) { case Expose: if (backbuffer == None) makeNewBackbuffer(); event->type = EventHandler::EXPOSE; event->x = xevent->xexpose.x; event->y = xevent->xexpose.y; event->w = xevent->xexpose.width; event->h = xevent->xexpose.height; if (Xverbose) printf("Got Expose X Event, exposed %dx%d+%d+%d\n", event->w, event->h, event->x, event->y); if (xevent->xexpose.count) return 0; /* throw away all but last */ return 1; case KeyPress: case KeyRelease: { char buf[20]; int len; len = XLookupString(&xevent->xkey, buf, sizeof(buf), (KeySym *) NULL, (XComposeStatus *) NULL); buf[MIN(len, (int)(sizeof(buf) - 1))] = 0; if (Xverbose) printf("Got KeyPress X Event, string = \"%s\"\n", buf); if (len != 1) return 0; event->type = (xevent->type == KeyPress ? EventHandler::KEYPRESS : EventHandler::KEYRELEASE); event->key = buf[0]; event->x = xevent->xkey.x; event->y = xevent->xkey.y; } return 1; case ButtonPress: if (xevent->type == ButtonPress) /* this is ridiculous */ if (Xverbose) printf("Got ButtonPress X Event\n"); /* fall through */ case ButtonRelease: if (xevent->type == ButtonRelease) /* this is ridiculous */ if (Xverbose) printf("Got ButtonRelease X Event\n"); event->type = (xevent->type == ButtonPress ? EventHandler::BUTTONDOWN : EventHandler::BUTTONUP); event->shift_is_down = (xevent->xbutton.state & ShiftMask) != 0; event->control_is_down = (xevent->xbutton.state & ControlMask) != 0; switch (xevent->xbutton.button) { case Button1: event->button = EventHandler::LEFTBUTTON; break; case Button2: event->button = EventHandler::MIDDLEBUTTON; break; case Button3: event->button = EventHandler::RIGHTBUTTON; break; default: fprintf(stderr, "machinex_dispatch_event: don't recognize X button %d\n", (int)xevent->xbutton.button); break; } /* * adjust for non-square window */ minsiz = MIN(window_width, window_height); xoff = (window_width - minsiz) / 2; yoff = (window_height - minsiz) / 2; event->x = (1L << 16) * (xevent->xbutton.x - xoff) / minsiz; event->y = (1L << 16) * (xevent->xbutton.y - yoff) / minsiz; ox = xevent->xbutton.x; /* FIX THIS-- this is neurotic */ oy = xevent->xbutton.y; return 1; case MotionNotify: if (Xverbose) printf("Got PointerMotion X event at %d,%d\n", xevent->xmotion.x, xevent->xmotion.y); if (xevent->xmotion.state & Button1Mask) event->button = EventHandler::LEFTBUTTON; else if (xevent->xmotion.state & Button2Mask) event->button = EventHandler::MIDDLEBUTTON; else if (xevent->xmotion.state & Button3Mask) event->button = EventHandler::RIGHTBUTTON; else return 0; /* no button was down; we don't care about such events */ event->type = EventHandler::DRAG; event->shift_is_down = (xevent->xmotion.state & ShiftMask) != 0; event->control_is_down = (xevent->xmotion.state & ControlMask) != 0; /* FIX THIS-- the following does not belong in this function */ if (!preferences.getBoolProperty(M4D_NOCOMPRESSMOTION)) while (XCheckMaskEvent(xevent->xmotion.display, PointerMotionMask, xevent)) ; event->x = (1L << 16) * xevent->xmotion.x / window_width; event->y = (1L << 16) * xevent->xmotion.y / window_height; event->dx = xevent->xmotion.x - ox; event->dy = xevent->xmotion.y - oy; ox = xevent->xmotion.x; /* FIX THIS-- this is neurotic */ oy = xevent->xmotion.y; return 1; case MapNotify: if (Xverbose) printf("Got MapNotify X event\n"); /* fall through */ case ConfigureNotify: { if (xevent->type == ConfigureNotify) { /* this is ridiculous */ if (Xverbose) printf("Got ConfigureNotify X event\n"); event->type = EventHandler::RESIZE; if (Xverbose) printf("Changing from %dx%d ", (int)window_width, (int)window_height); } /* * Make a new back buffer */ makeNewBackbuffer(); if (xevent->type == ConfigureNotify) /* this is ridiculous */ if (Xverbose) printf("to %dx%d\n", (int)window_width, (int)window_height); event->w = window_width; event->h = window_height; if (xevent->type == MapNotify) { event->type = EventHandler::RESIZE; /* FIX THIS-- we need to notify the application, since if there is no wm running, two MapNotify events will be the only events generated on startup. We need to intelligently figure out what the situation is.. */ } return 1; } case UnmapNotify: if (Xverbose) printf("Got UnmapNotify X event\n"); return 0; case ReparentNotify: if (Xverbose) printf("Got ReparentNotify X event\n"); return 0; default: fprintf(stderr, "xevent_to_event: don't recognize X event type %d\n", xevent->type); return 0; } // return 0; /* sgi compiler thinks this is reachable. yeah, right. */ } EventMask MachineX::eventMaskToXEventMask(unsigned int eventmask) { EventMask xeventmask = 0; if (BIT(&eventmask, EventHandler::EXPOSE)) xeventmask |= ExposureMask; if (BIT(&eventmask, EventHandler::RESIZE)) xeventmask |= StructureNotifyMask; if (BIT(&eventmask, EventHandler::BUTTONDOWN)) xeventmask |= ButtonPressMask; if (BIT(&eventmask, EventHandler::BUTTONUP)) xeventmask |= ButtonReleaseMask; if (BIT(&eventmask, EventHandler::KEYPRESS)) xeventmask |= KeyPressMask; if (BIT(&eventmask, EventHandler::KEYRELEASE)) xeventmask |= KeyReleaseMask; if (BIT(&eventmask, EventHandler::DRAG)) xeventmask |= (Button1MotionMask | Button2MotionMask | Button3MotionMask); return xeventmask; } void MachineX::addEventHandler(unsigned int eventmask, Machine::event_handler func, void *arg) { EventMask xeventmask = eventMaskToXEventMask(eventmask); XtAddEventHandler(widget, xeventmask, FALSE, MachineX::dispatchEvent, new EventClosure(this, func, arg)); } // Return true and fill the event if there is one, false if not bool MachineX::getEventIfThereIsOne(int eventmask, EventHandler::Event *event) { bool result = false; XEvent xevent; EventMask xeventmask = eventMaskToXEventMask(eventmask); XFlush(XtDisplay(widget)); if (XCheckMaskEvent(XtDisplay(widget), xeventmask, &xevent)) { if (!xeventToEvent(&xevent, event)) { std::cout << "busy loop: ARGH!" << std::endl; } else { result = true; } } return result; } void MachineX::bell() { XBell(XtDisplay(widget), 100); } void MachineX::eventLoop() { XtRealizeWidget(toplevel); XtMainLoop(); } void MachineX::XtFPrintWidgetName(FILE *fp, Widget wid) { if (XtParent(wid)) { XtFPrintWidgetName(fp, XtParent(wid)); fprintf(fp, "."); } fprintf(fp, "%s", XtName(wid)); } void MachineX::XtPrintWidgetName(Widget wid) { XtFPrintWidgetName(stdout, wid); } // Local Variables: // c-basic-offset: 4 // c-comment-only-line-offset: 0 // 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)) // indent-tabs-mode: nil // End: