/* X Window plotting code for Ingrid Jakobsens partimatrix program. Allows matrix to displayed on suitable screen instead of generating PostScript code. The main function is ShowXPlot, equivalent to WritePostScript in the original program. The other functions break the code into neater chunks and may be useful for other purposes. To do: add labels to the display. Written by Hugh Fisher Jan 95. Subsequently modified by Ingrid Jakobsen for use in partimatrix.c Mar 95 - Oct 95 June 1996 Modifications to provide a neater exit. */ #include #include #include #include "XPlot.h" /**** Where we're displaying things. ****/ static Display * gDisplay; static Window gWindow; static GC gDrawGC, gWinGC; static Pixmap gBacking; /**** Error reporting ****/ void Fail (String message) { fprintf(stderr, "%s\n", message); exit(-1); } /**** Initialisation and shutdown ****/ Boolean OpenXWindow (String name, int width, int height) { long black, white; int screen; XGCValues values; /* Can we open a display ? */ gDisplay = XOpenDisplay(NULL); if (gDisplay == NULL) return False; /* We're OK. Remember some things */ screen = XDefaultScreen(gDisplay); black = XBlackPixel(gDisplay, screen); white = XWhitePixel(gDisplay, screen); /* Sanity check - but don't abort altogether. Return control to the main program */ if (width > XDisplayWidth(gDisplay, screen) || height > XDisplayHeight(gDisplay, screen)) { fprintf(stderr, "Window size requested is bigger than screen\n"); return False; } /* the program had this but I don't want to kill the program completely if this happens - the user should be allowed to continue Fail("Window size requested is bigger than screen"); */ /* Create a window */ gWindow = XCreateSimpleWindow(gDisplay, DefaultRootWindow(gDisplay), 0, 0, width, height, 1, black, white); if (gWindow == None) Fail("XCreateSimpleWindow"); XStoreName(gDisplay, gWindow, name); /* We accept mouse clicks to end the program and expose events */ XSelectInput(gDisplay, gWindow, ButtonPressMask | ExposureMask); /* And bring it to the front */ XMapRaised(gDisplay, gWindow); /* We draw the points offscreen and use this to update the window as required. Since there are a lot of points but they're only on or off, creating a bitmap saves memory. (Although compared to the 16M genetic data array...still, every bit helps) */ gBacking = XCreatePixmap(gDisplay, gWindow, width, height, 1); if (gBacking == None) Fail("XCreatePixmap"); /* We need separate contexts for updating the window and drawing the points since the window may be deeper */ values.foreground = black; values.background = white; values.function = GXcopy; gWinGC = XCreateGC(gDisplay, gWindow, GCForeground | GCBackground | GCFunction, &values); if (gWinGC == NULL) Fail("XCreateGC - window"); values.foreground = 1; values.background = 0; gDrawGC = XCreateGC(gDisplay, gBacking, GCForeground | GCBackground, &values); if (gDrawGC == NULL) Fail("XCreateGC - offscreen"); /* Clear the window, just to be neat */ XClearWindow(gDisplay, gWindow); /* Clear the offscreen bitmap */ XSetFunction(gDisplay, gDrawGC, GXclear); XFillRectangle(gDisplay, gBacking, gDrawGC, 0, 0, width, height); /* From now on we want to draw normally */ XSetFunction(gDisplay, gDrawGC, GXcopy); /* If we get here, everything has worked. */ return True; } void CloseXWindow () { XFreePixmap(gDisplay, gBacking); XDestroyWindow(gDisplay, gWindow); XCloseDisplay(gDisplay); } /**** Waiting for input and handling screen updates ****/ void WaitMouseClicked () { Boolean done; XEvent event; /* Just sit in event loop until we get a click */ done = False; while (! done) { XNextEvent(gDisplay, &event); switch(event.type) { case ButtonPress: done = True; break; case Expose: XCopyPlane(gDisplay, gBacking, gWindow, gWinGC, event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height, event.xexpose.x, event.xexpose.y, 1); break; /* Compulsory ritual for XLib programs */ case MappingNotify: XRefreshKeyboardMapping((XMappingEvent *)(&event)); break; default: break; } } } /**** At last, the real thing ****/ int ShowXPlot (int length, int width, char **matrix) { #define kMagic 'm' char name[64]; int row, col, sz = 4; int colspace = 16; int sheight, blocks = 1; int showrow, showcol; int screen, i; /* Decide how many blocks are needed to fit the matrix onto the screen */ gDisplay = XOpenDisplay(NULL); if (gDisplay == NULL) return 1; screen = XDefaultScreen(gDisplay); while ( (int)(length*sz/blocks)+1 > XDisplayHeight(gDisplay, screen)) blocks++; sheight = (int)(length/blocks)+1; /* Prompt user for name, and warning them in advance if the matrix is too long to fit on the screen in one column */ if (blocks > 1) printf("The partimatrix will be displayed %d columns wide\n\n", blocks); printf("Name for window > "); fgets(name, sizeof(name), stdin); /* the following two lines remove the and replace it with a null, which looks nicer under eXcursion. 02 Nov 1995 */ for (i = 0; name[i] != '\n' ; i++); name[i] = '\0'; /* If we can't open the window, give up and let the main program know. */ if (! OpenXWindow(name, width*blocks*sz + (blocks-1)*colspace, sheight*sz)) return 1; /* Now we plot all the points... */ for (col = 0; col < length; col ++) { for (row = 0; row < width; row ++) { showcol = ((int)(col%sheight))*sz; showrow = row*sz + ((int)(col/sheight))*(width*sz + colspace); if (matrix[row][col] == kMagic) XFillRectangle(gDisplay, gBacking, gDrawGC, showrow, showcol, sz, sz); } } /* ...and wait for the user to dismiss the window */ WaitMouseClicked(); /* Done */ return 0; #undef kMagic }