/**************************************************************************** * xwindows.c * * This module implements X-windows specific routines. * * from Persistence of Vision Raytracer * Copyright 1993 Persistence of Vision Team *--------------------------------------------------------------------------- * NOTICE: This source code file is provided so that users may experiment * with enhancements to POV-Ray and to port the software to platforms other * than those supported by the POV-Ray Team. There are strict rules under * which you are permitted to use this file. The rules are in the file * named POVLEGAL.DOC which should be distributed with this file. If * POVLEGAL.DOC is not available or for more info please contact the POV-Ray * Team Coordinator by leaving a message in CompuServe's Graphics Developer's * Forum. The latest version of POV-Ray may be found there as well. * * This program is based on the popular DKB raytracer version 2.12. * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * *****************************************************************************/ /* * Here are some routines I wrote which implement +d option on unix computers * running X-windows. For now, only black and white output is supported. If I * get access to a computer with a color monitor, I'll probably add support * for colors to my routines. * * In future I'll probably add some more dithering methods. I have tested these * routines on SUN 3 and SUN 4. I'm using the tvtwm window manager. * * If you have some suggestions to my source code or if you change anything, * please let me know. I can be reached at the following address: Marek * Rzewuski, Avstikkeren 11, 1156 Oslo 11, Norway or marekr@ifi.uio.no on * Internet. */ /* * 91-07-29 Allan H. Wax (Wax.OSBU_South@Xerox.COM) Hacked this module so it * can be used with either a colour or B&W screen. Made all variables local * to this routine (no need for more globals if no one ever uses them). Fixed * bug in refresh routine. Use dither from colorx.c. Use xpov.icon instead of * theIcon. */ #include #include /* some Xlib stuff */ #include #include #include #include "xpov.icn" #include #include "frame.h" #include "povproto.h" #define BORDER_WIDTH 2 #define EV_MASK (ButtonPressMask | \ KeyPressMask | \ ExposureMask | \ StructureNotifyMask) static Display *theDisplay; static int theScreen; static int theDepth; static unsigned long theBlackPixel; static unsigned long theWhitePixel; static XEvent theEvent; static Window theWindow, openWindow(); static GC theGC; static unsigned char *bitmap; /* pointer to our picture bitmap */ static unsigned char *bitmapPos;/* position to the last drawn pixel in our * bitmap */ static char *display_name = 0; static int disp_wide, disp_high; static XImage *xi; static char *data; static XGCValues gcvalues; static long colorpixels[256]; static Bool iscolor; static int screen_open = 0; struct { void (*Init) (); void (*Close) (); void (*Finished) (); void (*Plot) (); } display; /* global POVRay variables */ extern FRAME Frame; extern unsigned int Options; extern char DisplayFormat; extern int First_Line; /* * Sets up a connection to the X server and stores informations about the * enviroment */ static initX() { theDisplay = XOpenDisplay(NULL); if (theDisplay == NULL) { fprintf(stderr, "ERROR: Cannot establish a connection to the X server %s\n", XDisplayName(NULL)); exit(1); } theScreen = DefaultScreen(theDisplay); theDepth = DefaultDepth(theDisplay, theScreen); theWhitePixel = WhitePixel(theDisplay, theScreen); theBlackPixel = BlackPixel(theDisplay, theScreen); } /* * This procedure will do the following things: 1) Set up attributes * desired for the window 2) Set up an icon to our window. 3) Send * hints to the window manager. 4) Open a window on the display 5) Tell * the X to place the window on the screen 6) Flush out all the queued up X * requests to the X server */ static Window openWindow(x, y, width, height, flag, theNewGC) int x, y; int width, height; int flag; GC *theNewGC; { XSetWindowAttributes theWindowAttributes; XSizeHints theSizeHints; unsigned long theWindowMask; Window theNewWindow; Pixmap theIconPixmap; XWMHints theWMHints; /* * Set up some attributes for the window. Override_redirect tells the * window manager to deal width the window or leave it alone */ theWindowAttributes.border_pixel = theBlackPixel; theWindowAttributes.background_pixel = theWhitePixel; theWindowAttributes.override_redirect = False; theWindowMask = CWBackPixel | CWBorderPixel | CWOverrideRedirect; /* Now, open out window */ theNewWindow = XCreateWindow(theDisplay, RootWindow(theDisplay, theScreen), x, y, width, height, BORDER_WIDTH, theDepth, InputOutput, CopyFromParent, theWindowMask, &theWindowAttributes); /* Create one iconbitmap */ theIconPixmap = XCreateBitmapFromData(theDisplay, theNewWindow, xpov_bits, xpov_width, xpov_height); /* * Now tell the window manager where on screen we should place our * window. */ theWMHints.icon_pixmap = theIconPixmap; theWMHints.initial_state = NormalState; /* we don't want an iconized * window when it's created */ theWMHints.flags = IconPixmapHint | StateHint; XSetWMHints(theDisplay, theNewWindow, &theWMHints); theSizeHints.flags = PPosition | PSize; theSizeHints.x = x; theSizeHints.y = y; theSizeHints.width = width; theSizeHints.height = height; /* * theSizeHints.min_width = width; theSizeHints.min_height = * height; theSizeHints.max_width = width; theSizeHints.max_height = * height; */ XSetNormalHints(theDisplay, theNewWindow, &theSizeHints); if (createGC(theNewWindow, theNewGC) == 0) { XDestroyWindow(theDisplay, theNewWindow); return ((Window) 0); } /* make a name for our window */ XStoreName(theDisplay, theNewWindow, "POVRay \0"); /* * Now, could we please see the window on the screen? Until now, we have * dealed with a window which has been created but noe appeared on the * screen. Maping the window places it visibly on the screen */ XMapWindow(theDisplay, theNewWindow); XFlush(theDisplay); return (theNewWindow); } static refreshWindow(theExposedWindow) Window theExposedWindow; { int i = 0, x = 0, y = First_Line; register unsigned char *dummy = bitmap; while (dummy != bitmapPos) { /* draw until most current pixel is drawn */ if (*dummy) XDrawPoint(theDisplay, theWindow, theGC, x, y); if (x == Frame.Screen_Width) { x = 0; y++; } else { dummy++; x++; i++; } } XFlush(theDisplay); } /* Creates a new graphics context */ static int createGC(theNewWindow, theNewGC) Window theNewWindow; GC *theNewGC; { XGCValues theGCValues; *theNewGC = XCreateGC(theDisplay, theNewWindow, (unsigned long) 0, &theGCValues); if (*theNewGC == 0) { return (0); /* error */ } else { /* set foreground and background defaults for * the new GC */ XSetForeground(theDisplay, *theNewGC, theBlackPixel); XSetBackground(theDisplay, *theNewGC, theWhitePixel); return (1); /* OK */ } } static initEvents(theWindow) Window theWindow; { XSelectInput(theDisplay, theWindow, EV_MASK); } void BWFinished() { } void BWInit() { int i; /* * set some room for a bitmap for our picture. I've got to "remember" the * whole picture bacuse of resizing of the window, overlapping etc. Then * I've got to refresh the picture. This should be easy to convert to an * "color version" in future */ bitmap = (unsigned char *) malloc(sizeof(unsigned char) * (Frame.Screen_Width * Frame.Screen_Height)); bitmapPos = bitmap; if (bitmap == NULL) printf("ERROR: Can not allocate the buffer..\n"); for (i = 0; i < (Frame.Screen_Width * Frame.Screen_Height); i++) { *bitmapPos++ = 0; } bitmapPos = bitmap; initX(); theWindow = openWindow(0, 0, Frame.Screen_Width, Frame.Screen_Height, 0, &theGC); initEvents(theWindow); XFlush(theDisplay); XNextEvent(theDisplay, &theEvent); XFlush(theDisplay); } /* end of display initilazation */ void BWClose() { sleep(10); /* an simple delay. 10 seconds. */ XDestroyWindow(theDisplay, theWindow); XFlush(theDisplay); XCloseDisplay(theDisplay); free(bitmap); } void BWPlot(x, y, Red, Green, Blue) int x, y; unsigned char Red, Green, Blue; { unsigned char color; long thePixel; /* lets find if there are some events waiting for us */ while (XPending(theDisplay) > 0) { /* now deal with the events.. */ XNextEvent(theDisplay, &theEvent); switch (theEvent.type) { case Expose: /* printf("Window is exposed.\n"); */ refreshWindow(theEvent.xany.window); break; case MapNotify: /* printf("The window is mapped.\n"); */ refreshWindow(theEvent.xany.window); break; case ButtonPress: /* printf("A mouse button was pressed.\n"); */ break; case ConfigureNotify: /* printf("The window configuration has been changed\n"); */ refreshWindow(theEvent.xany.window); break; } } if (dither(x, y, Red, Green, Blue)) { thePixel = BlackPixel(theDisplay, theScreen); XDrawPoint(theDisplay, theWindow, theGC, x, y); XFlush(theDisplay); } else { thePixel = WhitePixel(theDisplay, theScreen); } *bitmapPos++ = thePixel; } /* so we can get at the dimensions */ extern FRAME Frame; /* x11 stuff */ static Bool evtPred(d, e, a) Display *d; XEvent *e; char *a; { return (True); } static void BuildColormap() { XColor tmp; register unsigned short r, g, b, i; Colormap cmap; /* * build a colormap that will contain 216, or 6^3 colors. We won't do * anything clever like adding extra grays in the leftover space. * Remember, this was written in a few hours. */ cmap = DefaultColormap(theDisplay, theScreen); i = 0; for (r = 0; r < 6; r++) { for (g = 0; g < 6; g++) { for (b = 0; b < 6; b++, i++) { tmp.red = (r * 36) << 8; tmp.green = (g * 36) << 8; tmp.blue = (b * 36) << 8; tmp.flags = DoRed | DoGreen | DoBlue; if (!XAllocColor(theDisplay, cmap, &tmp)) goto crap_out; colorpixels[i] = tmp.pixel; } } } crap_out: /* couldn't get what we wanted. Boo-hoo. */ if (i != 216) fprintf(stderr, "warning: could only get %d colors.\n", i); } void ColourFinished() { XEvent *report; /* do nothing. I suppose we could wait... */ while (1) { XNextEvent(theDisplay, &report); XPutImage(theDisplay, theWindow, theGC, xi, 0, 0, 0, 0, Frame.Screen_Width, Frame.Screen_Height); } } void display_close() { if(screen_open) (display.Close) (); } void display_plot(x, y, Red, Green, Blue) int x, y; unsigned char Red, Green, Blue; { (display.Plot) (x, y, Red, Green, Blue); } void display_finished() { if(screen_open) (display.Finished) (); } void ColourInit() { Pixmap icon; XSizeHints hints; long size, i; XSetWindowAttributes attrib; if ((theDisplay = XOpenDisplay(display_name)) == NULL) { fprintf(stderr, "Can't open X server %s.\n", XDisplayName(display_name)); exit(1); } theScreen = DefaultScreen(theDisplay); /* screen dimensions */ disp_wide = DisplayWidth(theDisplay, theScreen); disp_high = DisplayHeight(theDisplay, theScreen); theWindow = XCreateSimpleWindow(theDisplay, RootWindow(theDisplay, theScreen), (disp_wide - Frame.Screen_Width) / 2, (disp_high - Frame.Screen_Height) / 2, Frame.Screen_Width, Frame.Screen_Height, 2 /* border width */ , BlackPixel(theDisplay, theScreen), WhitePixel(theDisplay, theScreen)); /* create a graphics context for drawing */ gcvalues.function = GXcopy; theGC = XCreateGC(theDisplay, theWindow, GCFunction, &gcvalues); /* make a cute icon */ icon = XCreateBitmapFromData(theDisplay, theWindow, xpov_bits, xpov_width, xpov_height); /* give a few clues about ourself */ hints.flags = PPosition | PSize | PMinSize; hints.x = (disp_wide - Frame.Screen_Width) / 2; hints.y = (disp_high - Frame.Screen_Height) / 2; hints.width = Frame.Screen_Width; hints.height = Frame.Screen_Height; hints.min_width = 32; hints.min_height = 32; XSetStandardProperties(theDisplay, theWindow, "povray", "povray", icon, NULL, 0, &hints); /* only want to know about expose events */ XSelectInput(theDisplay, theWindow, ExposureMask); XMapWindow(theDisplay, theWindow); BuildColormap(); size = Frame.Screen_Width * Frame.Screen_Height; data = (char *) XtMalloc(size); for (i = 0; i < size; i++) data[i] = 0; xi = XCreateImage(theDisplay, DefaultVisual(theDisplay, theScreen), 8, ZPixmap, 0, data, Frame.Screen_Width, Frame.Screen_Height, 8, Frame.Screen_Width); }; void ColourClose() { sleep(10); XDestroyImage(xi); XtFree(data); XDestroyWindow(theDisplay, theWindow); XCloseDisplay(theDisplay); } /* static */ int dither(x, y, Red, Green, Blue) register unsigned int x, y; unsigned char Red, Green, Blue; { /* * Perform ordered dithering for halftone screen. For details, see * Graphics Gems, Ed. Andrew Glassner, p. 176-178 Floyd-Steinberg dither * would look better, but we would need more saved state, making the * coding a lot uglier. Since this is just a display hack and not an * image processing package, I don't bother. */ static unsigned int dm[] = { 0, 192, 48, 240, 12, 204, 60, 252, 128, 64, 176, 112, 140, 76, 188, 124, 32, 224, 16, 208, 44, 236, 28, 220, 160, 96, 144, 80, 172, 108, 156, 92, 8, 200, 56, 248, 4, 196, 52, 244, 136, 72, 184, 120, 132, 68, 180, 116, 40, 232, 24, 216, 36, 228, 20, 212, 168, 104, 152, 88, 164, 100, 148, 84 }; register unsigned long luminence = (307 * Red + 599 * Green + 118 * Blue) / 1024; return (luminence < dm[(x & 7) + ((y & 7) << 3)]); } void ColourPlot(x, y, Red, Green, Blue) int x, y; unsigned char Red, Green, Blue; { long thePixel; int r, g, b; XEvent report; int luminence; /* scale rgb to be in our 216 color space */ r = (Red * 216) / (256 * 36); g = (Green * 216) / (256 * 36); b = (Blue * 216) / (256 * 36); /* find the pixel we got */ thePixel = colorpixels[(r * 36) + (g * 6) + b]; XPutPixel(xi, x, y, thePixel); /* on each pixel, copy a scanline. Inefficient, but so what? See above. */ XPutImage(theDisplay, theWindow, theGC, xi, 0, y, 0, y, Frame.Screen_Width, 1); /* poll for exposure events */ if (XCheckIfEvent(theDisplay, &report, evtPred, "blah")) { XPutImage(theDisplay, theWindow, theGC, xi, 0, 0, 0, 0, Frame.Screen_Width, y + 1); } } void unix_init_POVRAY PARAMS((void)) { } int matherr(x) struct exception *x; { switch (x->type) { case DOMAIN: case OVERFLOW: x->retval = 1.0e17; break; case SING: case UNDERFLOW: x->retval = 0.0; break; default: break; } return (1); } extern void BWInit(), BWClose(), BWFinished(), BWPlot(); void display_init() { Pixmap icon; XSizeHints hints; long size, i; XSetWindowAttributes attrib; if ((theDisplay = XOpenDisplay(display_name)) == NULL) { fprintf(stderr, "Can't open X server %s.\n", XDisplayName(display_name)); exit(1); } screen_open = 1; theScreen = DefaultScreen(theDisplay); iscolor = (XDisplayCells(theDisplay, theScreen) > 2); if (iscolor) { display.Init = ColourInit; display.Close = ColourClose; display.Finished = ColourFinished; display.Plot = ColourPlot; } else { display.Init = BWInit; display.Close = BWClose; display.Finished = BWFinished; display.Plot = BWPlot; } XCloseDisplay(theDisplay); (display.Init) (); }