509 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			509 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /************************************************************************/
 | |
| /*									*/
 | |
| /* 	Nano toolkit for OpenGL for the X Window System			*/
 | |
| /*									*/
 | |
| /************************************************************************/
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <X11/Xlib.h>
 | |
| #include <X11/Xutil.h>
 | |
| #include <X11/keysym.h>
 | |
| #include <GL/glx.h>
 | |
| #include "ctk.h"
 | |
| 
 | |
| #if defined(__cplusplus) || defined(c_plusplus)
 | |
| #define class c_class
 | |
| #endif
 | |
| 
 | |
| /******************************************************************************/
 | |
| 
 | |
| static Display *display = 0;
 | |
| static int screen = 0;
 | |
| static XVisualInfo *visual = 0;
 | |
| static Colormap colorMap;
 | |
| static Window window = 0;
 | |
| static GLXContext context = 0;
 | |
| 
 | |
| static int windType;
 | |
| static TK_EventRec event;
 | |
| 
 | |
| /******************************************************************************/
 | |
| 
 | |
| static long DisplayInit(void)
 | |
| {
 | |
|     int erb, evb;
 | |
| 
 | |
|     if (!display) {
 | |
| 	display = XOpenDisplay(0);
 | |
| 	if (!display) {
 | |
| 	    fprintf(stderr, "Can't connect to display!\n");
 | |
| 	    return 0;
 | |
| 	}
 | |
| 	if (!glXQueryExtension(display, &erb, &evb)) {
 | |
| 	    fprintf(stderr, "No glx extension!\n");
 | |
| 	    return 0;
 | |
| 	}
 | |
| 	screen = DefaultScreen(display);
 | |
|     }
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| static XVisualInfo *FindVisual(long type)
 | |
| {
 | |
|     long list[32];
 | |
|     int i;
 | |
| 
 | |
|     i = 0;
 | |
| 
 | |
|     list[i++] = GLX_LEVEL;
 | |
|     list[i++] = 0;
 | |
| 
 | |
|     if (TK_WIND_IS_DB(type)) {
 | |
| 	list[i++] = GLX_DOUBLEBUFFER;
 | |
|     }
 | |
| 
 | |
|     if (TK_WIND_IS_RGB(type)) {
 | |
| 	list[i++] = GLX_RGBA;
 | |
| 	list[i++] = GLX_RED_SIZE;
 | |
| 	list[i++] = 1;
 | |
| 	list[i++] = GLX_GREEN_SIZE;
 | |
| 	list[i++] = 1;
 | |
| 	list[i++] = GLX_BLUE_SIZE;
 | |
| 	list[i++] = 1;
 | |
| 	/*
 | |
| 	list[i++] = GLX_ALPHA_SIZE;
 | |
| 	list[i++] = 1;
 | |
| 	list[i++] = GLX_ACCUM_RED_SIZE;
 | |
| 	list[i++] = 1;
 | |
| 	list[i++] = GLX_ACCUM_GREEN_SIZE;
 | |
| 	list[i++] = 1;
 | |
| 	list[i++] = GLX_ACCUM_BLUE_SIZE;
 | |
| 	list[i++] = 1;
 | |
| 	list[i++] = GLX_ACCUM_ALPHA_SIZE;
 | |
| 	list[i++] = 1;
 | |
| 	*/
 | |
|     } else {
 | |
| 	list[i++] = GLX_BUFFER_SIZE;
 | |
| 	list[i++] = 1;
 | |
|     }
 | |
| 
 | |
|     list[i++] = GLX_DEPTH_SIZE;
 | |
|     list[i++] = 1;
 | |
| 
 | |
|     list[i++] = GLX_STENCIL_SIZE;
 | |
|     list[i++] = 1;
 | |
| 
 | |
|     list[i] = (int)None;
 | |
| 
 | |
|     return glXChooseVisual(display, screen, (int *)list);
 | |
| }
 | |
| 
 | |
| static long MakeVisualType(XVisualInfo *vi)
 | |
| {
 | |
|     int rgba, doubleBuffer, stencilSize, depthSize;
 | |
|     int accumRed, accumGreen, accumBlue, auxBuffers;
 | |
|     long mask;
 | |
| 
 | |
|     mask = 0;
 | |
| 
 | |
|     glXGetConfig(display, vi, GLX_RGBA, &rgba);
 | |
|     if (rgba) {
 | |
| 	mask |= TK_WIND_RGB;
 | |
|     } else {
 | |
| 	mask |= TK_WIND_CI;
 | |
|     }
 | |
| 
 | |
|     glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &doubleBuffer);
 | |
|     if (doubleBuffer) {
 | |
| 	mask |= TK_WIND_DB;
 | |
|     } else {
 | |
| 	mask |= TK_WIND_SB;
 | |
|     }
 | |
| 
 | |
|     glXGetConfig(display, vi, GLX_ACCUM_RED_SIZE, &accumRed);
 | |
|     glXGetConfig(display, vi, GLX_ACCUM_GREEN_SIZE, &accumGreen);
 | |
|     glXGetConfig(display, vi, GLX_ACCUM_BLUE_SIZE, &accumBlue);
 | |
|     if ((accumRed > 0) && (accumGreen > 0) && (accumBlue > 0)) {
 | |
| 	mask |= TK_WIND_ACCUM;
 | |
|     }
 | |
| 
 | |
|     glXGetConfig(display, vi, GLX_STENCIL_SIZE, &stencilSize);
 | |
|     if (stencilSize > 0) {
 | |
| 	mask |= TK_WIND_STENCIL;
 | |
|     }
 | |
| 
 | |
|     glXGetConfig(display, vi, GLX_DEPTH_SIZE, &depthSize);
 | |
|     if (depthSize > 0) {
 | |
| 	mask |= TK_WIND_Z;
 | |
|     }
 | |
| 
 | |
|     return mask;
 | |
| }
 | |
| 
 | |
| static Bool WaitForMapNotify(Display *d, XEvent *e, char *arg)
 | |
| {
 | |
|     if (e->type == MapNotify && e->xmap.window == window) {
 | |
| 	return GL_TRUE;
 | |
|     }
 | |
|     return GL_FALSE;
 | |
| }
 | |
| 
 | |
| long tkNewWindow(TK_WindowRec *wind)
 | |
| {
 | |
|     XSetWindowAttributes wa;
 | |
|     XTextProperty tp;
 | |
|     XSizeHints sh;
 | |
|     XEvent e;
 | |
|     char *ptr;
 | |
| 
 | |
|     if (!DisplayInit()) {
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|     if (wind->type == TK_WIND_REQUEST) {
 | |
| 	int useGL;
 | |
| 
 | |
| 	visual = FindVisual(wind->info);
 | |
| 	if (visual == 0) {
 | |
| 	    return 0;
 | |
| 	}
 | |
| 	glXGetConfig(display, visual, GLX_USE_GL, &useGL);
 | |
| 	if (!useGL) {
 | |
| 	    return 0;
 | |
| 	}
 | |
|     } else {
 | |
| 	XVisualInfo temp;
 | |
| 	int useGL, count;
 | |
| 
 | |
| 	temp.visualid = wind->info;
 | |
| 	visual = XGetVisualInfo(display, VisualIDMask, &temp, &count);
 | |
| 	if (visual == 0) {
 | |
| 	    return 0;
 | |
| 	}
 | |
| 	glXGetConfig(display, visual, GLX_USE_GL, &useGL);
 | |
| 	if (!useGL) {
 | |
| 	    return 0;
 | |
| 	}
 | |
|     }
 | |
|     wind->type = TK_WIND_REQUEST;
 | |
|     wind->info = MakeVisualType(visual);
 | |
|     windType = wind->info;
 | |
| 
 | |
|     context = glXCreateContext(display, visual, None,
 | |
| 			       (wind->render==TK_WIND_DIRECT)?GL_TRUE:GL_FALSE);
 | |
|     if (!context) {
 | |
| 	fprintf(stderr, "Can't create a context!\n");
 | |
|     }
 | |
|     if (glXIsDirect(display, context)) {
 | |
| 	wind->render = TK_WIND_DIRECT;
 | |
|     } else {
 | |
| 	wind->render = TK_WIND_INDIRECT;
 | |
|     }
 | |
| 
 | |
|     colorMap = XCreateColormap(display, RootWindow(display, screen),
 | |
| 			       visual->visual, AllocNone);
 | |
|     wa.colormap = colorMap;
 | |
|     wa.background_pixmap = None;
 | |
|     wa.border_pixel = 0;
 | |
|     wa.event_mask = StructureNotifyMask | ExposureMask;
 | |
| 
 | |
|     window = XCreateWindow(display, RootWindow(display, screen), (int)wind->x,
 | |
| 			   (int)wind->y, (unsigned int)wind->width,
 | |
| 			   (unsigned int)wind->height, 0, visual->depth,
 | |
| 			   InputOutput, visual->visual,
 | |
| 			   CWBackPixmap|CWBorderPixel|CWEventMask|CWColormap,
 | |
| 			   &wa);
 | |
|     
 | |
|     /*
 | |
|     ** Set up window hints.
 | |
|     */
 | |
|     ptr = &wind->name[0];
 | |
|     XStringListToTextProperty(&ptr, 1, &tp);
 | |
|     sh.flags = USPosition | USSize;
 | |
|     XSetWMProperties(display, window, &tp, &tp, 0, 0, &sh, 0, 0);
 | |
| 
 | |
|     /*
 | |
|     ** Map window and then wait for the window system to get around to it.
 | |
|     */
 | |
|     XMapWindow(display, window);
 | |
|     XIfEvent(display, &e, WaitForMapNotify, 0);
 | |
| 
 | |
|     XSetWMColormapWindows(display, window, &window, 1);
 | |
|     if (!glXMakeCurrent(display, window, context)) {
 | |
| 	return 0;
 | |
|     }
 | |
|     XFlush(display);
 | |
| 
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| void tkCloseWindow(void)
 | |
| {
 | |
|     glFinish();
 | |
|     XDestroyWindow(display, window);
 | |
|     glXDestroyContext(display, context);
 | |
|     XFreeColormap(display, colorMap);
 | |
|     XFree((char *)visual);
 | |
| }
 | |
| 
 | |
| void tkSwapBuffers(void)
 | |
| {
 | |
|     glXSwapBuffers(display, window);
 | |
| }
 | |
| 
 | |
| void tkQuit(void)
 | |
| {
 | |
|     exit(0);
 | |
| }
 | |
| 
 | |
| static void GetNextEvent(void)
 | |
| {
 | |
|     XEvent current, ahead;
 | |
| 
 | |
|     event.event = 0;
 | |
|     event.data[0] = 0;
 | |
|     event.data[1] = 0;
 | |
|     event.data[2] = 0;
 | |
|     event.data[3] = 0;
 | |
| 
 | |
|     XNextEvent(display, ¤t);
 | |
|     switch (current.type) {
 | |
|       case MappingNotify:
 | |
| 	XRefreshKeyboardMapping((XMappingEvent *)¤t);
 | |
| 	break;
 | |
| 
 | |
|       case Expose:
 | |
| 	while (XEventsQueued(current.xexpose.display, QueuedAfterReading) > 0) {
 | |
| 	    XPeekEvent(current.xexpose.display, &ahead);
 | |
| 	    if (ahead.type != Expose) {
 | |
| 		break;
 | |
| 	    }
 | |
| 	    if (ahead.xexpose.window != current.xexpose.window) {
 | |
| 		break;
 | |
| 	    }
 | |
| 	    XNextEvent(display, ¤t);
 | |
| 	}
 | |
| 	if (current.xexpose.count == 0) {
 | |
| 	    event.event = TK_EVENT_EXPOSE;
 | |
| 	    event.data[TK_WINDOWX] = current.xexpose.width;
 | |
| 	    event.data[TK_WINDOWY] = current.xexpose.height;
 | |
| 	}
 | |
| 	break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void tkExec(long (*Func)(TK_EventRec *ptr))
 | |
| {
 | |
| 
 | |
|     while (1) {
 | |
| 	if (XPending(display)) {
 | |
| 	    GetNextEvent();
 | |
| 	    if (event.event == TK_EVENT_EXPOSE) {
 | |
| 		if ((*Func)(&event) == 0) {
 | |
| 		    break;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void ScreenImage(TK_ScreenImageRec *ptr)
 | |
| {
 | |
|     XImage *image;
 | |
|     float *destPtr, *tmpBuf;
 | |
|     unsigned long *srcPtr, c, mask;
 | |
|     long rShift = 0, gShift = 0, bShift = 0;
 | |
|     GLint rBits, gBits, bBits;
 | |
|     float rFactor, gFactor, bFactor;
 | |
|     long indices_per_word, words_per_line, lines;
 | |
|     int i, j;
 | |
|     float *tmpPtr;
 | |
| 
 | |
|     glXWaitGL();
 | |
| 
 | |
|     image = XGetImage(display, window, (int)ptr->x, (int)ptr->y, 
 | |
| 	    (unsigned int)ptr->width, (unsigned int)ptr->height, ~0, ZPixmap);
 | |
|     srcPtr = (unsigned long *)image->data;
 | |
|     destPtr = ptr->data;
 | |
| 
 | |
|     mask = (1 << image->depth) - 1;
 | |
|     indices_per_word = 32 / image->bits_per_pixel;
 | |
|     words_per_line = image->bytes_per_line / 4;
 | |
|     lines = ptr->width * ptr->height / (words_per_line * indices_per_word);
 | |
| 
 | |
|     if (ptr->colorMode == TK_WIND_RGB) {
 | |
| 	mask = image->red_mask;
 | |
| 	while ((mask & 0x1) == 0) {
 | |
| 	    rShift++;
 | |
| 	    mask = mask >> 1;
 | |
| 	}
 | |
| 	mask = image->green_mask;
 | |
| 	while ((mask & 0x1) == 0) {
 | |
| 	    gShift++;
 | |
| 	    mask = mask >> 1;
 | |
| 	}
 | |
| 	mask = image->blue_mask;
 | |
| 	while ((mask & 0x1) == 0) {
 | |
| 	    bShift++;
 | |
| 	    mask = mask >> 1;
 | |
| 	}
 | |
| 	mask = (1 << image->depth) - 1;
 | |
| 	glGetIntegerv(GL_RED_BITS, &rBits);
 | |
| 	glGetIntegerv(GL_GREEN_BITS, &gBits);
 | |
| 	glGetIntegerv(GL_BLUE_BITS, &bBits);
 | |
| 	rFactor = (1 << rBits) - 1;
 | |
| 	gFactor = (1 << gBits) - 1;
 | |
| 	bFactor = (1 << bBits) - 1;
 | |
| 
 | |
|         tmpBuf = (float *)malloc(ptr->width*ptr->height*3*sizeof(float));
 | |
|         tmpPtr = tmpBuf;
 | |
|         for ( j = 0; j < ptr->height; j++) {
 | |
|             for (i = 0; i < ptr->width; i++) {
 | |
|                 c = XGetPixel(image, i, j);
 | |
|                     *tmpPtr++ = ((float)((c & image->red_mask) >> rShift)) / rFactor;
 | |
|                     *tmpPtr++ = ((float)((c & image->green_mask) >> gShift)) / gFactor;
 | |
|                     *tmpPtr++ = ((float)((c & image->blue_mask) >> bShift)) / bFactor;
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 	/*
 | |
| 	** X reads top to bottom, so reverse the line ordering to match
 | |
| 	** GL's bottom to top ordering.
 | |
| 	*/
 | |
| 	tmpPtr = tmpBuf;
 | |
| 	for (i = ptr->height - 1; i >= 0; i--) {
 | |
| 	    for (j = 0; j < ptr->width * 3; j++) {
 | |
| 		destPtr[i*ptr->width*3 + j] = *tmpPtr++;
 | |
| 	    }
 | |
| 	}
 | |
| 	free(tmpBuf);
 | |
|    } else {
 | |
|         for ( j = 0; j < ptr->height; j++) {
 | |
|             for (i = 0; i < ptr->width; i++) {
 | |
|                 destPtr[(ptr->height-j-1)*ptr->width + i] = (float) XGetPixel(image, i, j);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     XDestroyImage(image);
 | |
| }
 | |
| 
 | |
| static void GetVisuals(TK_VisualIDsRec *ptr)
 | |
| {
 | |
|     XVisualInfo vInfoTmp, *vInfoList;
 | |
|     int total, useGL, i;
 | |
| 
 | |
|     ptr->count = 0;
 | |
| 
 | |
|     if (!DisplayInit()) {
 | |
| 	return;
 | |
|     }
 | |
|     vInfoTmp.screen = screen;
 | |
|     vInfoList = XGetVisualInfo(display, VisualScreenMask, &vInfoTmp, &total);
 | |
| 
 | |
|     for (i = 0; i < total; i++) {
 | |
| 	glXGetConfig(display, &vInfoList[i], GLX_USE_GL, &useGL);
 | |
| 	if (useGL) {
 | |
| 	    ptr->IDs[ptr->count++] = vInfoList[i].visualid;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     XFree((char *)vInfoList);
 | |
| }
 | |
| 
 | |
| void tkGet(long item, void *data)
 | |
| {
 | |
| 
 | |
|     if (item == TK_SCREENIMAGE) {
 | |
| 	ScreenImage((TK_ScreenImageRec *)data);
 | |
|     } else if (item == TK_VISUALIDS) {
 | |
| 	GetVisuals((TK_VisualIDsRec *)data);
 | |
|     }
 | |
| }
 | |
| 
 | |
| long tkLoadFont(char *fontName, long *width, long *height)
 | |
| {
 | |
|     XFontStruct *fontInfo;
 | |
|     Font id;
 | |
|     int first, last;
 | |
|     GLuint base;
 | |
| 
 | |
|     fontInfo = XLoadQueryFont(display, fontName);
 | |
|     if (fontInfo == NULL) {
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|     id = fontInfo->fid;
 | |
|     first = (int)fontInfo->min_char_or_byte2;
 | |
|     last = (int)fontInfo->max_char_or_byte2;
 | |
| 
 | |
|     base = glGenLists(last+1);
 | |
|     if (base == 0) {
 | |
| 	return 0;
 | |
|     }
 | |
|     glXUseXFont(id, first, last-first+1, base+first);
 | |
|     *height = fontInfo->ascent + fontInfo->descent;
 | |
|     *width = fontInfo->max_bounds.width;
 | |
|     return base;
 | |
| }
 | |
| 
 | |
| long tkDrawFont(char *fontName, long x, long y, char *string, long len)
 | |
| {
 | |
|     static char pattern1[] = "-adobe-courier-bold-o-normal--14-*-*-*-*-*-*-*";
 | |
|     static char pattern2[] = "-*-*-*-*-*-*-14-*-*-*-*-*-*-*";
 | |
|     static char size[] = "456781";
 | |
|     XFontStruct *fontInfo;
 | |
|     GC gc;
 | |
|     XGCValues values;
 | |
|     XColor exact, green;
 | |
|     char **fontList;
 | |
|     unsigned long mask;
 | |
|     long count, i;
 | |
| 
 | |
|     glXWaitGL();
 | |
| 
 | |
|     /*
 | |
|     ** Look for a common font first, then look for any font in
 | |
|     ** a range of point sizes from 11 to 18.
 | |
|     */
 | |
|     fontList = XListFonts(display, pattern1, 1, (int *)&count);
 | |
|     if (count == 0) {
 | |
| 	for (i = 0; i < 6; i++) {
 | |
| 	    pattern2[14] = size[i];
 | |
| 	    fontList = XListFonts(display, pattern2, 1, (int *)&count);
 | |
| 	    if (count > 0) {
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     if (count == 0) {
 | |
|        return 0;
 | |
|     }
 | |
| 
 | |
|     strcpy(fontName, fontList[0]);
 | |
| 
 | |
|     fontInfo = XLoadQueryFont(display, fontList[0]);
 | |
|     if (fontInfo == NULL) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     mask = GCForeground | GCFont;
 | |
|     values.font = fontInfo->fid;
 | |
| 
 | |
|     if (TK_WIND_IS_RGB(windType)) {
 | |
| 	XAllocNamedColor(display, colorMap, "Green", &exact, &green);
 | |
| 	values.foreground = green.pixel;
 | |
|     } else {
 | |
| 	values.foreground = 2; /* 2 = GREEN. */
 | |
|     }
 | |
| 
 | |
|     gc = XCreateGC(display, window, mask, &values);
 | |
| 
 | |
|     XDrawString(display, window, gc, (int)x, (int)y, string, (int)len);
 | |
| 
 | |
|     XFreeFontNames(fontList);
 | |
|     glXWaitX();
 | |
|     return 1;
 | |
| }
 |