414 lines
10 KiB
C
414 lines
10 KiB
C
/* simple glx driver for TinyGL */
|
|
#include <GL/glx.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/shm.h>
|
|
#include <X11/extensions/XShm.h>
|
|
#include "zgl.h"
|
|
|
|
typedef struct {
|
|
GLContext *gl_context;
|
|
Display *display;
|
|
XVisualInfo visual_info;
|
|
int xsize,ysize;
|
|
XImage *ximage;
|
|
GC gc;
|
|
Colormap cmap;
|
|
Drawable drawable;
|
|
int do_convert; /* true if must do convertion to X11 format */
|
|
/* shared memory */
|
|
int shm_use;
|
|
XShmSegmentInfo *shm_info;
|
|
int CompletionType;
|
|
} TinyGLXContext;
|
|
|
|
Bool glXQueryExtension( Display *dpy, int *errorb, int *event )
|
|
{
|
|
return True;
|
|
}
|
|
|
|
|
|
XVisualInfo* glXChooseVisual( Display *dpy, int screen,
|
|
int *attribList )
|
|
{
|
|
XVisualInfo vinfo;
|
|
int n;
|
|
|
|
/* the attribList is ignored : we consider only RGBA rendering (no
|
|
direct color) */
|
|
|
|
if (XMatchVisualInfo (dpy, screen, 16, TrueColor, &vinfo)) {
|
|
/* 16 bit visual (fastest with TinyGL) */
|
|
} else if (XMatchVisualInfo (dpy, screen, 24, TrueColor, &vinfo)) {
|
|
/* 24 bit visual */
|
|
} else if (XMatchVisualInfo (dpy, screen, 32, TrueColor, &vinfo)) {
|
|
/* 32 bit visual */
|
|
} else if (XMatchVisualInfo (dpy, screen, 8, PseudoColor, &vinfo)) {
|
|
/* 8 bit visual */
|
|
} else {
|
|
/* no suitable visual */
|
|
return NULL;
|
|
}
|
|
|
|
return XGetVisualInfo(dpy,VisualAllMask,&vinfo,&n);
|
|
}
|
|
|
|
|
|
|
|
GLXContext glXCreateContext( Display *dpy, XVisualInfo *vis,
|
|
GLXContext shareList, Bool direct )
|
|
{
|
|
TinyGLXContext *ctx;
|
|
|
|
if (shareList != NULL) {
|
|
gl_fatal_error("No sharing available in TinyGL");
|
|
}
|
|
ctx=gl_malloc(sizeof(TinyGLXContext));
|
|
ctx->gl_context=NULL;
|
|
ctx->visual_info=*vis;
|
|
return (GLXContext) ctx;
|
|
}
|
|
|
|
|
|
void glXDestroyContext( Display *dpy, GLXContext ctx1 )
|
|
{
|
|
TinyGLXContext *ctx = (TinyGLXContext *) ctx1;
|
|
if (ctx->gl_context != NULL) {
|
|
glClose();
|
|
}
|
|
gl_free(ctx);
|
|
}
|
|
|
|
|
|
static int glxXErrorFlag=0;
|
|
|
|
static int glxHandleXError(Display *dpy,XErrorEvent *event)
|
|
{
|
|
glxXErrorFlag=1;
|
|
return 0;
|
|
}
|
|
|
|
static int bits_per_pixel(Display *dpy, XVisualInfo *visinfo)
|
|
{
|
|
XImage *img;
|
|
int bpp;
|
|
char *data;
|
|
|
|
data = gl_malloc(8);
|
|
if (data == NULL)
|
|
return visinfo->depth;
|
|
|
|
img = XCreateImage(dpy, visinfo->visual, visinfo->depth,
|
|
ZPixmap, 0, data, 1, 1, 32, 0);
|
|
if (img == NULL) {
|
|
gl_free(data);
|
|
return visinfo->depth;
|
|
}
|
|
bpp = img->bits_per_pixel;
|
|
gl_free(data);
|
|
img->data = NULL;
|
|
XDestroyImage(img);
|
|
return bpp;
|
|
}
|
|
|
|
static int create_ximage(TinyGLXContext *ctx,
|
|
int xsize,int ysize,int depth)
|
|
{
|
|
int major,minor;
|
|
Bool pixmaps;
|
|
unsigned char *framebuffer;
|
|
int (*old_handler)(Display *,XErrorEvent *);
|
|
|
|
if (XShmQueryVersion(ctx->display,&major,&minor,&pixmaps))
|
|
ctx->shm_use=1;
|
|
else
|
|
ctx->shm_use=0;
|
|
|
|
if (!ctx->shm_use) goto no_shm;
|
|
|
|
ctx->shm_info=gl_malloc(sizeof(XShmSegmentInfo));
|
|
ctx->ximage=XShmCreateImage(ctx->display,None,depth,ZPixmap,NULL,
|
|
ctx->shm_info,xsize,ysize);
|
|
if (ctx->ximage == NULL) {
|
|
fprintf(stderr,"XShm: error: XShmCreateImage\n");
|
|
ctx->shm_use=0;
|
|
gl_free(ctx->shm_info);
|
|
goto no_shm;
|
|
}
|
|
ctx->shm_info->shmid=shmget(IPC_PRIVATE,
|
|
ctx->ysize*ctx->ximage->bytes_per_line,
|
|
IPC_CREAT | 0777);
|
|
if (ctx->shm_info->shmid < 0) {
|
|
fprintf(stderr,"XShm: error: shmget\n");
|
|
no_shm1:
|
|
ctx->shm_use=0;
|
|
XDestroyImage(ctx->ximage);
|
|
goto no_shm;
|
|
}
|
|
ctx->ximage->data=shmat(ctx->shm_info->shmid,0,0);
|
|
if (ctx->ximage->data == (char *) -1) {
|
|
fprintf(stderr,"XShm: error: shmat\n");
|
|
no_shm2:
|
|
shmctl(ctx->shm_info->shmid,IPC_RMID,0);
|
|
goto no_shm1;
|
|
}
|
|
ctx->shm_info->shmaddr=ctx->ximage->data;
|
|
|
|
ctx->shm_info->readOnly=False;
|
|
|
|
/* attach & test X errors */
|
|
|
|
glxXErrorFlag=0;
|
|
old_handler=XSetErrorHandler(glxHandleXError);
|
|
XShmAttach(ctx->display,ctx->shm_info);
|
|
XSync(ctx->display, False);
|
|
|
|
if (glxXErrorFlag) {
|
|
XFlush(ctx->display);
|
|
shmdt(ctx->shm_info->shmaddr);
|
|
XSetErrorHandler(old_handler);
|
|
goto no_shm2;
|
|
}
|
|
|
|
/* the shared memory will be automatically deleted */
|
|
shmctl(ctx->shm_info->shmid,IPC_RMID,0);
|
|
|
|
/* test with a dummy XShmPutImage */
|
|
XShmPutImage(ctx->display,ctx->drawable,ctx->gc,
|
|
ctx->ximage,0,0,0,0,1,1,
|
|
False);
|
|
|
|
XSync(ctx->display, False);
|
|
XSetErrorHandler(old_handler);
|
|
|
|
if (glxXErrorFlag) {
|
|
fprintf(stderr,"XShm: error: XShmPutImage\n");
|
|
XFlush(ctx->display);
|
|
shmdt(ctx->shm_info->shmaddr);
|
|
goto no_shm2;
|
|
}
|
|
|
|
ctx->CompletionType=XShmGetEventBase(ctx->display) + ShmCompletion;
|
|
/* shared memory is OK !! */
|
|
|
|
return 0;
|
|
|
|
no_shm:
|
|
ctx->ximage=XCreateImage(ctx->display, None, depth, ZPixmap, 0,
|
|
NULL,xsize,ysize, 8, 0);
|
|
framebuffer=gl_malloc(ysize * ctx->ximage->bytes_per_line);
|
|
ctx->ximage->data = framebuffer;
|
|
return 0;
|
|
}
|
|
|
|
static void free_ximage(TinyGLXContext *ctx)
|
|
{
|
|
if (ctx->shm_use)
|
|
{
|
|
XShmDetach(ctx->display, ctx->shm_info);
|
|
XDestroyImage(ctx->ximage);
|
|
shmdt(ctx->shm_info->shmaddr);
|
|
gl_free(ctx->shm_info);
|
|
} else {
|
|
gl_free(ctx->ximage->data);
|
|
XDestroyImage(ctx->ximage);
|
|
}
|
|
}
|
|
|
|
/* resize the glx viewport : we try to use the xsize and ysize
|
|
given. We return the effective size which is guaranted to be smaller */
|
|
|
|
int glX_resize_viewport(GLContext *c,int *xsize_ptr,int *ysize_ptr)
|
|
{
|
|
TinyGLXContext *ctx;
|
|
int xsize,ysize;
|
|
|
|
ctx=(TinyGLXContext *)c->opaque;
|
|
|
|
xsize=*xsize_ptr;
|
|
ysize=*ysize_ptr;
|
|
|
|
/* we ensure that xsize and ysize are multiples of 2 for the zbuffer.
|
|
TODO: find a better solution */
|
|
xsize&=~3;
|
|
ysize&=~3;
|
|
|
|
if (xsize == 0 || ysize == 0) return -1;
|
|
|
|
*xsize_ptr=xsize;
|
|
*ysize_ptr=ysize;
|
|
|
|
if (ctx->ximage != NULL) free_ximage(ctx);
|
|
|
|
ctx->xsize=xsize;
|
|
ctx->ysize=ysize;
|
|
|
|
if (create_ximage(ctx,ctx->xsize,ctx->ysize,ctx->visual_info.depth) != 0)
|
|
return -1;
|
|
|
|
/* resize the Z buffer */
|
|
if (ctx->do_convert) {
|
|
ZB_resize(c->zb,NULL,xsize,ysize);
|
|
} else {
|
|
ZB_resize(c->zb,ctx->ximage->data,xsize,ysize);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* we assume here that drawable is a window */
|
|
Bool glXMakeCurrent( Display *dpy, GLXDrawable drawable,
|
|
GLXContext ctx1)
|
|
{
|
|
TinyGLXContext *ctx = (TinyGLXContext *) ctx1;
|
|
XWindowAttributes attr;
|
|
int i,xsize,ysize;
|
|
unsigned int palette[ZB_NB_COLORS];
|
|
unsigned char color_indexes[ZB_NB_COLORS];
|
|
ZBuffer *zb;
|
|
XColor xcolor;
|
|
unsigned long pixel[ZB_NB_COLORS],tmp_plane;
|
|
|
|
if (ctx->gl_context == NULL) {
|
|
/* create the TinyGL context */
|
|
|
|
ctx->display=dpy;
|
|
ctx->drawable=drawable;
|
|
|
|
XGetWindowAttributes(ctx->display,drawable,&attr);
|
|
|
|
xsize=attr.width;
|
|
ysize=attr.height;
|
|
|
|
if (attr.depth != ctx->visual_info.depth) return False;
|
|
|
|
/* ximage structure */
|
|
ctx->ximage=NULL;
|
|
ctx->shm_use=1; /* use shm */
|
|
|
|
if (attr.depth == 8) {
|
|
/* get the colormap from the window */
|
|
ctx->cmap = attr.colormap;
|
|
|
|
if ( XAllocColorCells(ctx->display,ctx->cmap,True,&tmp_plane,0,
|
|
pixel,ZB_NB_COLORS) == 0) {
|
|
/* private cmap */
|
|
ctx->cmap = XCreateColormap(ctx->display, drawable,
|
|
ctx->visual_info.visual, AllocAll);
|
|
XSetWindowColormap(ctx->display, drawable, ctx->cmap);
|
|
for(i=0;i<ZB_NB_COLORS;i++) pixel[i]=i;
|
|
}
|
|
|
|
for(i=0;i<ZB_NB_COLORS;i++) color_indexes[i]=pixel[i];
|
|
|
|
/* Open the Z Buffer - 256 colors */
|
|
zb=ZB_open(xsize,ysize,ZB_MODE_INDEX,ZB_NB_COLORS,
|
|
color_indexes,palette,NULL);
|
|
if (zb == NULL) {
|
|
fprintf(stderr, "Error while initializing Z buffer\n");
|
|
exit(1);
|
|
}
|
|
|
|
for (i=0; i<ZB_NB_COLORS; i++) {
|
|
xcolor.flags = DoRed | DoGreen | DoBlue;
|
|
|
|
xcolor.red = (palette[i]>>8) & 0xFF00;
|
|
xcolor.green = (palette[i] & 0xFF00);
|
|
xcolor.blue = (palette[i] << 8) & 0xFF00;
|
|
xcolor.pixel = pixel[i];
|
|
XStoreColor(ctx->display,ctx->cmap,&xcolor);
|
|
}
|
|
ctx->do_convert = 1;
|
|
} else {
|
|
int mode,bpp;
|
|
/* RGB 16/24/32 */
|
|
bpp = bits_per_pixel(ctx->display,&ctx->visual_info);
|
|
switch(bpp) {
|
|
case 24:
|
|
mode = ZB_MODE_RGB24;
|
|
ctx->do_convert = (TGL_FEATURE_RENDER_BITS != 16);
|
|
break;
|
|
case 32:
|
|
mode = ZB_MODE_RGBA;
|
|
ctx->do_convert = (TGL_FEATURE_RENDER_BITS != 16);
|
|
break;
|
|
default:
|
|
mode = ZB_MODE_5R6G5B;
|
|
ctx->do_convert = (TGL_FEATURE_RENDER_BITS != 16);
|
|
break;
|
|
}
|
|
zb=ZB_open(xsize,ysize,mode,0,NULL,NULL,NULL);
|
|
if (zb == NULL) {
|
|
fprintf(stderr, "Error while initializing Z buffer\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* create a gc */
|
|
ctx->gc = XCreateGC(ctx->display, drawable, 0, 0);
|
|
|
|
/* initialisation of the TinyGL interpreter */
|
|
glInit(zb);
|
|
ctx->gl_context=gl_get_context();
|
|
ctx->gl_context->opaque=(void *) ctx;
|
|
ctx->gl_context->gl_resize_viewport=glX_resize_viewport;
|
|
|
|
/* set the viewport : we force a call to glX_resize_viewport */
|
|
ctx->gl_context->viewport.xsize=-1;
|
|
ctx->gl_context->viewport.ysize=-1;
|
|
|
|
glViewport(0, 0, xsize, ysize);
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
static Bool WaitForShmCompletion(Display *dpy, XEvent *event, char *arg)
|
|
{
|
|
TinyGLXContext *ctx=(TinyGLXContext *) arg;
|
|
|
|
return (event->type == ctx->CompletionType) &&
|
|
( ((XShmCompletionEvent *)event)->drawable == (Window)ctx->drawable);
|
|
}
|
|
|
|
void glXSwapBuffers( Display *dpy, GLXDrawable drawable )
|
|
{
|
|
GLContext *gl_context;
|
|
TinyGLXContext *ctx;
|
|
|
|
/* retrieve the current GLXContext */
|
|
gl_context=gl_get_context();
|
|
ctx=(TinyGLXContext *)gl_context->opaque;
|
|
|
|
/* for non 16 bits visuals, a conversion is required */
|
|
|
|
|
|
if (ctx->do_convert) {
|
|
ZB_copyFrameBuffer(ctx->gl_context->zb,
|
|
ctx->ximage->data,
|
|
ctx->ximage->bytes_per_line);
|
|
|
|
}
|
|
|
|
/* draw the ximage */
|
|
if (ctx->shm_use) {
|
|
XEvent event;
|
|
|
|
XShmPutImage(dpy,drawable,ctx->gc,
|
|
ctx->ximage,0,0,0,0,ctx->ximage->width, ctx->ximage->height,
|
|
True);
|
|
XIfEvent(dpy, &event, WaitForShmCompletion, (char*)ctx);
|
|
} else {
|
|
XPutImage(dpy, drawable, ctx->gc,
|
|
ctx->ximage, 0, 0, 0, 0, ctx->ximage->width, ctx->ximage->height);
|
|
}
|
|
XFlush(dpy);
|
|
}
|
|
|
|
|
|
void glXWaitGL( void )
|
|
{
|
|
}
|
|
|
|
void glXWaitX( void )
|
|
{
|
|
}
|