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;
|
|
}
|