/************************************************************************/ /* */ /* Nano toolkit for OpenGL for the X Window System */ /* */ /************************************************************************/ #include #include #include #include #include #include #include #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; }