| /** |
| * \file miniglx.c |
| * \brief Mini GLX interface functions. |
| * \author Brian Paul |
| * |
| * The Mini GLX interface is a subset of the GLX interface, plus a |
| * minimal set of Xlib functions. |
| */ |
| |
| /* |
| * Mesa 3-D graphics library |
| * Version: 6.0.1 |
| * |
| * Copyright (C) 1999-2004 Brian Paul All Rights Reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included |
| * in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
| * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| |
| /** |
| * \mainpage Mini GLX |
| * |
| * \section miniglxIntro Introduction |
| * |
| * The Mini GLX interface facilitates OpenGL rendering on embedded devices. The |
| * interface is a subset of the GLX interface, plus a minimal set of Xlib-like |
| * functions. |
| * |
| * Programs written to the Mini GLX specification should run unchanged |
| * on systems with the X Window System and the GLX extension (after |
| * recompilation). The intention is to allow flexibility for |
| * prototyping and testing. |
| * |
| * The files in the src/miniglx/ directory are compiled to build the |
| * libGL.so library. This is the library which applications link with. |
| * libGL.so in turn, loads the hardware-specific device driver. |
| * |
| * |
| * \section miniglxDoxygen About Doxygen |
| * |
| * For a list of all files, select <b>File List</b>. Choose a file from |
| * the list for a list of all functions in the file. |
| * |
| * For a list of all functions, types, constants, etc. |
| * select <b>File Members</b>. |
| * |
| * |
| * \section miniglxReferences References |
| * |
| * - <A HREF="file:../../docs/MiniGLX.html">Mini GLX Specification</A>, |
| * Tungsten Graphics, Inc. |
| * - OpenGL Graphics with the X Window System, Silicon Graphics, Inc., |
| * ftp://ftp.sgi.com/opengl/doc/opengl1.2/glx1.3.ps |
| * - Xlib - C Language X Interface, X Consortium Standard, X Version 11, |
| * Release 6.4, ftp://ftp.x.org/pub/R6.4/xc/doc/hardcopy/X11/xlib.PS.gz |
| * - XFree86 Man pages, The XFree86 Project, Inc., |
| * http://www.xfree86.org/current/manindex3.html |
| * |
| */ |
| |
| /** |
| * \page datatypes Notes on the XVisualInfo, Visual, and __GLXvisualConfig data types |
| * |
| * -# X (unfortunately) has two (or three) data types which |
| * describe visuals. Ideally, there would just be one. |
| * -# We need the #__GLXvisualConfig type to augment #XVisualInfo and #Visual |
| * because we need to describe the GLX-specific attributes of visuals. |
| * -# In this interface there is a one-to-one-to-one correspondence between |
| * the three types and they're all interconnected. |
| * -# The #XVisualInfo type has a pointer to a #Visual. The #Visual structure |
| * (aka MiniGLXVisualRec) has a pointer to the #__GLXvisualConfig. The |
| * #Visual structure also has a pointer pointing back to the #XVisualInfo. |
| * -# The #XVisualInfo structure is the only one who's contents are public. |
| * -# The glXChooseVisual() and XGetVisualInfo() are the only functions that |
| * return #XVisualInfo structures. They can be freed with XFree(), though |
| * there is a small memory leak. |
| */ |
| |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <dlfcn.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <sys/types.h> |
| #include <sys/time.h> /* for gettimeofday */ |
| #include <linux/kd.h> |
| #include <linux/vt.h> |
| |
| #include "miniglxP.h" |
| #include "dri_util.h" |
| |
| #include "imports.h" |
| #include "glcontextmodes.h" |
| #include "glapi.h" |
| |
| #include "pciaccess.h" |
| |
| static GLboolean __glXCreateContextWithConfig(__DRInativeDisplay *dpy, |
| int screen, int fbconfigID, void *contextID, |
| drm_context_t *hHWContext); |
| |
| static GLboolean __glXGetDrawableInfo(__DRInativeDisplay *dpy, int scrn, |
| __DRIid draw, unsigned int * index, unsigned int * stamp, |
| int * x, int * y, int * width, int * height, |
| int * numClipRects, drm_clip_rect_t ** pClipRects, |
| int * backX, int * backY, |
| int * numBackClipRects, drm_clip_rect_t ** pBackClipRects); |
| |
| static __DRIscreen * __glXFindDRIScreen(__DRInativeDisplay *dpy, int scrn); |
| |
| static GLboolean __glXWindowExists(__DRInativeDisplay *dpy, __DRIid draw); |
| |
| static int __glXGetUST( int64_t * ust ); |
| |
| static GLboolean __glXGetMscRate(__DRInativeDisplay * dpy, __DRIid drawable, |
| int32_t * numerator, int32_t * denominator); |
| |
| static GLboolean xf86DRI_DestroyContext(__DRInativeDisplay *dpy, int screen, |
| __DRIid context_id ); |
| |
| static GLboolean xf86DRI_CreateDrawable(__DRInativeDisplay *dpy, int screen, |
| __DRIid drawable, drm_drawable_t *hHWDrawable ); |
| |
| static GLboolean xf86DRI_DestroyDrawable(__DRInativeDisplay *dpy, int screen, |
| __DRIid drawable); |
| |
| |
| /** Wrapper around either malloc() */ |
| void * |
| _mesa_malloc(size_t bytes) |
| { |
| return malloc(bytes); |
| } |
| |
| /** Wrapper around either calloc() */ |
| void * |
| _mesa_calloc(size_t bytes) |
| { |
| return calloc(1, bytes); |
| } |
| |
| /** Wrapper around either free() */ |
| void |
| _mesa_free(void *ptr) |
| { |
| free(ptr); |
| } |
| |
| |
| /** |
| * \brief Current GLX context. |
| * |
| * \sa glXGetCurrentContext(). |
| */ |
| static GLXContext CurrentContext = NULL; |
| |
| |
| |
| static Display *SignalDisplay = 0; |
| |
| static void SwitchVT(int sig) |
| { |
| fprintf(stderr, "SwitchVT %d dpy %p\n", sig, SignalDisplay); |
| |
| if (SignalDisplay) { |
| SignalDisplay->vtSignalFlag = 1; |
| switch( sig ) |
| { |
| case SIGUSR1: /* vt has been released */ |
| SignalDisplay->haveVT = 0; |
| break; |
| case SIGUSR2: /* vt has been acquired */ |
| SignalDisplay->haveVT = 1; |
| break; |
| } |
| } |
| } |
| |
| /**********************************************************************/ |
| /** \name Framebuffer device functions */ |
| /**********************************************************************/ |
| /*@{*/ |
| |
| /** |
| * \brief Do the first part of setting up the framebuffer device. |
| * |
| * \param dpy the display handle. |
| * \param use_vt use a VT for display or not |
| * |
| * \return GL_TRUE on success, or GL_FALSE on failure. |
| * |
| * \sa This is called during XOpenDisplay(). |
| * |
| * \internal |
| * Gets the VT number, opens the respective console TTY device. Saves its state |
| * to restore when exiting and goes into graphics mode. |
| * |
| * Opens the framebuffer device and make a copy of the original variable screen |
| * information and gets the fixed screen information. Maps the framebuffer and |
| * MMIO region into the process address space. |
| */ |
| static GLboolean |
| OpenFBDev( Display *dpy, int use_vt ) |
| { |
| char ttystr[1000]; |
| int fd, vtnumber, ttyfd; |
| |
| assert(dpy); |
| |
| if (geteuid()) { |
| fprintf(stderr, "error: you need to be root\n"); |
| return GL_FALSE; |
| } |
| |
| if (use_vt) { |
| |
| /* open /dev/tty0 and get the VT number */ |
| if ((fd = open("/dev/tty0", O_WRONLY, 0)) < 0) { |
| fprintf(stderr, "error opening /dev/tty0\n"); |
| return GL_FALSE; |
| } |
| if (ioctl(fd, VT_OPENQRY, &vtnumber) < 0 || vtnumber < 0) { |
| fprintf(stderr, "error: couldn't get a free vt\n"); |
| return GL_FALSE; |
| } |
| |
| fprintf(stderr, "*** got vt nr: %d\n", vtnumber); |
| close(fd); |
| |
| /* open the console tty */ |
| sprintf(ttystr, "/dev/tty%d", vtnumber); /* /dev/tty1-64 */ |
| dpy->ConsoleFD = open(ttystr, O_RDWR | O_NDELAY, 0); |
| if (dpy->ConsoleFD < 0) { |
| fprintf(stderr, "error couldn't open console fd\n"); |
| return GL_FALSE; |
| } |
| |
| /* save current vt number */ |
| { |
| struct vt_stat vts; |
| if (ioctl(dpy->ConsoleFD, VT_GETSTATE, &vts) == 0) |
| dpy->OriginalVT = vts.v_active; |
| } |
| |
| /* disconnect from controlling tty */ |
| ttyfd = open("/dev/tty", O_RDWR); |
| if (ttyfd >= 0) { |
| ioctl(ttyfd, TIOCNOTTY, 0); |
| close(ttyfd); |
| } |
| |
| /* some magic to restore the vt when we exit */ |
| { |
| struct vt_mode vt; |
| struct sigaction sig_tty; |
| |
| /* Set-up tty signal handler to catch the signal we request below */ |
| SignalDisplay = dpy; |
| memset( &sig_tty, 0, sizeof( sig_tty ) ); |
| sig_tty.sa_handler = SwitchVT; |
| sigemptyset( &sig_tty.sa_mask ); |
| if( sigaction( SIGUSR1, &sig_tty, &dpy->OrigSigUsr1 ) || |
| sigaction( SIGUSR2, &sig_tty, &dpy->OrigSigUsr2 ) ) |
| { |
| fprintf(stderr, "error: can't set up signal handler (%s)", |
| strerror(errno) ); |
| return GL_FALSE; |
| } |
| |
| |
| |
| vt.mode = VT_PROCESS; |
| vt.waitv = 0; |
| vt.relsig = SIGUSR1; |
| vt.acqsig = SIGUSR2; |
| if (ioctl(dpy->ConsoleFD, VT_SETMODE, &vt) < 0) { |
| fprintf(stderr, "error: ioctl(VT_SETMODE) failed: %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| |
| if (ioctl(dpy->ConsoleFD, VT_ACTIVATE, vtnumber) != 0) |
| printf("ioctl VT_ACTIVATE: %s\n", strerror(errno)); |
| if (ioctl(dpy->ConsoleFD, VT_WAITACTIVE, vtnumber) != 0) |
| printf("ioctl VT_WAITACTIVE: %s\n", strerror(errno)); |
| |
| if (ioctl(dpy->ConsoleFD, VT_GETMODE, &vt) < 0) { |
| fprintf(stderr, "error: ioctl VT_GETMODE: %s\n", strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| |
| |
| } |
| |
| /* go into graphics mode */ |
| if (ioctl(dpy->ConsoleFD, KDSETMODE, KD_GRAPHICS) < 0) { |
| fprintf(stderr, "error: ioctl(KDSETMODE, KD_GRAPHICS) failed: %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| } |
| |
| /* open the framebuffer device */ |
| dpy->FrameBufferFD = open(dpy->fbdevDevice, O_RDWR); |
| if (dpy->FrameBufferFD < 0) { |
| fprintf(stderr, "Error opening /dev/fb0: %s\n", strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| /* get the original variable screen info */ |
| if (ioctl(dpy->FrameBufferFD, FBIOGET_VSCREENINFO, &dpy->OrigVarInfo)) { |
| fprintf(stderr, "error: ioctl(FBIOGET_VSCREENINFO) failed: %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| /* make copy */ |
| dpy->VarInfo = dpy->OrigVarInfo; /* structure copy */ |
| |
| /* Turn off hw accels (otherwise mmap of mmio region will be |
| * refused) |
| */ |
| dpy->VarInfo.accel_flags = 0; |
| if (ioctl(dpy->FrameBufferFD, FBIOPUT_VSCREENINFO, &dpy->VarInfo)) { |
| fprintf(stderr, "error: ioctl(FBIOPUT_VSCREENINFO) failed: %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| |
| |
| /* Get the fixed screen info */ |
| if (ioctl(dpy->FrameBufferFD, FBIOGET_FSCREENINFO, &dpy->FixedInfo)) { |
| fprintf(stderr, "error: ioctl(FBIOGET_FSCREENINFO) failed: %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| |
| |
| /* mmap the framebuffer into our address space */ |
| dpy->driverContext.FBStart = dpy->FixedInfo.smem_start; |
| dpy->driverContext.FBSize = dpy->FixedInfo.smem_len; |
| dpy->driverContext.shared.fbSize = dpy->FixedInfo.smem_len; |
| dpy->driverContext.FBAddress = (caddr_t) mmap(0, /* start */ |
| dpy->driverContext.shared.fbSize, /* bytes */ |
| PROT_READ | PROT_WRITE, /* prot */ |
| MAP_SHARED, /* flags */ |
| dpy->FrameBufferFD, /* fd */ |
| 0 /* offset */); |
| if (dpy->driverContext.FBAddress == (caddr_t) - 1) { |
| fprintf(stderr, "error: unable to mmap framebuffer: %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| /* mmap the MMIO region into our address space */ |
| dpy->driverContext.MMIOStart = dpy->FixedInfo.mmio_start; |
| dpy->driverContext.MMIOSize = dpy->FixedInfo.mmio_len; |
| dpy->driverContext.MMIOAddress = (caddr_t) mmap(0, /* start */ |
| dpy->driverContext.MMIOSize, /* bytes */ |
| PROT_READ | PROT_WRITE, /* prot */ |
| MAP_SHARED, /* flags */ |
| dpy->FrameBufferFD, /* fd */ |
| dpy->FixedInfo.smem_len /* offset */); |
| if (dpy->driverContext.MMIOAddress == (caddr_t) - 1) { |
| fprintf(stderr, "error: unable to mmap mmio region: %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| fprintf(stderr, "got MMIOAddress %p offset %d\n", |
| dpy->driverContext.MMIOAddress, |
| dpy->FixedInfo.smem_len); |
| |
| return GL_TRUE; |
| } |
| |
| |
| |
| |
| /** |
| * \brief Setup up the desired framebuffer device mode. |
| * |
| * \param dpy the display handle. |
| * |
| * \return GL_TRUE on success, or GL_FALSE on failure. |
| * |
| * \sa This is called during __miniglx_StartServer(). |
| * |
| * \internal |
| * |
| * Bumps the size of the window the the next supported mode. Sets the |
| * variable screen information according to the desired mode and asks |
| * the driver to validate the mode. Certifies that a DirectColor or |
| * TrueColor visual is used from the updated fixed screen information. |
| * In the case of DirectColor visuals, sets up an 'identity' colormap to |
| * mimic a TrueColor visual. |
| * |
| * Calls the driver hooks 'ValidateMode' and 'PostValidateMode' to |
| * allow the driver to make modifications to the chosen mode according |
| * to hardware constraints, or to save and restore videocard registers |
| * that may be clobbered by the fbdev driver. |
| * |
| * \todo Timings are hard-coded in the source for a set of supported modes. |
| */ |
| static GLboolean |
| SetupFBDev( Display *dpy ) |
| { |
| int width, height; |
| |
| assert(dpy); |
| |
| width = dpy->driverContext.shared.virtualWidth; |
| height = dpy->driverContext.shared.virtualHeight; |
| |
| if (width==832) |
| width=800; |
| #if 0 |
| /* Bump size up to next supported mode. |
| */ |
| if (width <= 720 && height <= 480) { |
| width = 720; height = 480; |
| } |
| else if (width <= 960 && height <= 540) { |
| width = 960; height = 540; |
| } |
| else if (width <= 800 && height <= 600) { |
| width = 800; height = 600; |
| } |
| else if (width <= 1024 && height <= 768) { |
| width = 1024; height = 768; |
| } |
| else if (width <= 768 && height <= 1024) { |
| width = 768; height = 1024; |
| } |
| else if (width <= 1280 && height <= 1024) { |
| width = 1280; height = 1024; |
| } |
| #endif |
| |
| dpy->driverContext.shared.fbStride = width * (dpy->driverContext.bpp / 8); |
| |
| /* set the depth, resolution, etc */ |
| dpy->VarInfo = dpy->OrigVarInfo; |
| dpy->VarInfo.bits_per_pixel = dpy->driverContext.bpp; |
| dpy->VarInfo.xres_virtual = dpy->driverContext.shared.virtualWidth; |
| dpy->VarInfo.yres_virtual = dpy->driverContext.shared.virtualHeight; |
| dpy->VarInfo.xres = dpy->driverContext.shared.Width; |
| dpy->VarInfo.yres = height; |
| dpy->VarInfo.xoffset = 0; |
| dpy->VarInfo.yoffset = 0; |
| dpy->VarInfo.nonstd = 0; |
| dpy->VarInfo.vmode &= ~FB_VMODE_YWRAP; /* turn off scrolling */ |
| |
| if (dpy->VarInfo.bits_per_pixel == 32) { |
| dpy->VarInfo.red.offset = 16; |
| dpy->VarInfo.green.offset = 8; |
| dpy->VarInfo.blue.offset = 0; |
| dpy->VarInfo.transp.offset = 24; |
| dpy->VarInfo.red.length = 8; |
| dpy->VarInfo.green.length = 8; |
| dpy->VarInfo.blue.length = 8; |
| dpy->VarInfo.transp.length = 8; |
| } |
| else if (dpy->VarInfo.bits_per_pixel == 16) { |
| dpy->VarInfo.red.offset = 11; |
| dpy->VarInfo.green.offset = 5; |
| dpy->VarInfo.blue.offset = 0; |
| dpy->VarInfo.red.length = 5; |
| dpy->VarInfo.green.length = 6; |
| dpy->VarInfo.blue.length = 5; |
| dpy->VarInfo.transp.offset = 0; |
| dpy->VarInfo.transp.length = 0; |
| } |
| else { |
| fprintf(stderr, "Only 32bpp and 16bpp modes supported at the moment\n"); |
| return 0; |
| } |
| |
| if (!dpy->driver->validateMode( &dpy->driverContext )) { |
| fprintf(stderr, "Driver validateMode() failed\n"); |
| return 0; |
| } |
| |
| /* These should be calculated with the gtf.c program, and then we could |
| remove all this... AlanH. */ |
| if (dpy->VarInfo.xres == 1280 && |
| dpy->VarInfo.yres == 1024) { |
| /* timing values taken from /etc/fb.modes (1280x1024 @ 75Hz) */ |
| dpy->VarInfo.pixclock = 7408; |
| dpy->VarInfo.left_margin = 248; |
| dpy->VarInfo.right_margin = 16; |
| dpy->VarInfo.upper_margin = 38; |
| dpy->VarInfo.lower_margin = 1; |
| dpy->VarInfo.hsync_len = 144; |
| dpy->VarInfo.vsync_len = 3; |
| } |
| else if (dpy->VarInfo.xres == 1024 && |
| dpy->VarInfo.yres == 768) { |
| /* timing values taken from /etc/fb.modes (1024x768 @ 75Hz) */ |
| dpy->VarInfo.pixclock = 12699; |
| dpy->VarInfo.left_margin = 176; |
| dpy->VarInfo.right_margin = 16; |
| dpy->VarInfo.upper_margin = 28; |
| dpy->VarInfo.lower_margin = 1; |
| dpy->VarInfo.hsync_len = 96; |
| dpy->VarInfo.vsync_len = 3; |
| } |
| else if (dpy->VarInfo.xres == 800 && |
| dpy->VarInfo.yres == 600) { |
| /* timing values taken from /etc/fb.modes (800x600 @ 75Hz) */ |
| dpy->VarInfo.pixclock = 27778; |
| dpy->VarInfo.left_margin = 128; |
| dpy->VarInfo.right_margin = 24; |
| dpy->VarInfo.upper_margin = 22; |
| dpy->VarInfo.lower_margin = 1; |
| dpy->VarInfo.hsync_len = 72; |
| dpy->VarInfo.vsync_len = 2; |
| } |
| else if (dpy->VarInfo.xres == 720 && |
| dpy->VarInfo.yres == 480) { |
| dpy->VarInfo.pixclock = 37202; |
| dpy->VarInfo.left_margin = 88; |
| dpy->VarInfo.right_margin = 16; |
| dpy->VarInfo.upper_margin = 14; |
| dpy->VarInfo.lower_margin = 1; |
| dpy->VarInfo.hsync_len = 72; |
| dpy->VarInfo.vsync_len = 3; |
| } |
| else if (dpy->VarInfo.xres == 960 && |
| dpy->VarInfo.yres == 540) { |
| dpy->VarInfo.pixclock = 24273; |
| dpy->VarInfo.left_margin = 128; |
| dpy->VarInfo.right_margin = 32; |
| dpy->VarInfo.upper_margin = 16; |
| dpy->VarInfo.lower_margin = 1; |
| dpy->VarInfo.hsync_len = 96; |
| dpy->VarInfo.vsync_len = 3; |
| } |
| else if (dpy->VarInfo.xres == 768 && |
| dpy->VarInfo.yres == 1024) { |
| /* timing values for 768x1024 @ 75Hz */ |
| dpy->VarInfo.pixclock = 11993; |
| dpy->VarInfo.left_margin = 136; |
| dpy->VarInfo.right_margin = 32; |
| dpy->VarInfo.upper_margin = 41; |
| dpy->VarInfo.lower_margin = 1; |
| dpy->VarInfo.hsync_len = 80; |
| dpy->VarInfo.vsync_len = 3; |
| } |
| else { |
| /* XXX need timings for other screen sizes */ |
| fprintf(stderr, "XXXX screen size %d x %d not supported at this time!\n", |
| dpy->VarInfo.xres, dpy->VarInfo.yres); |
| return GL_FALSE; |
| } |
| |
| fprintf(stderr, "[miniglx] Setting mode: visible %dx%d virtual %dx%dx%d\n", |
| dpy->VarInfo.xres, dpy->VarInfo.yres, |
| dpy->VarInfo.xres_virtual, dpy->VarInfo.yres_virtual, |
| dpy->VarInfo.bits_per_pixel); |
| |
| /* set variable screen info */ |
| if (ioctl(dpy->FrameBufferFD, FBIOPUT_VSCREENINFO, &dpy->VarInfo)) { |
| fprintf(stderr, "error: ioctl(FBIOPUT_VSCREENINFO) failed: %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| /* get the variable screen info, in case it has been modified */ |
| if (ioctl(dpy->FrameBufferFD, FBIOGET_VSCREENINFO, &dpy->VarInfo)) { |
| fprintf(stderr, "error: ioctl(FBIOGET_VSCREENINFO) failed: %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| |
| fprintf(stderr, "[miniglx] Readback mode: visible %dx%d virtual %dx%dx%d\n", |
| dpy->VarInfo.xres, dpy->VarInfo.yres, |
| dpy->VarInfo.xres_virtual, dpy->VarInfo.yres_virtual, |
| dpy->VarInfo.bits_per_pixel); |
| |
| /* Get the fixed screen info */ |
| if (ioctl(dpy->FrameBufferFD, FBIOGET_FSCREENINFO, &dpy->FixedInfo)) { |
| fprintf(stderr, "error: ioctl(FBIOGET_FSCREENINFO) failed: %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| |
| if (dpy->FixedInfo.visual != FB_VISUAL_TRUECOLOR && |
| dpy->FixedInfo.visual != FB_VISUAL_DIRECTCOLOR) { |
| fprintf(stderr, "non-TRUECOLOR visuals not supported.\n"); |
| return GL_FALSE; |
| } |
| |
| if (dpy->FixedInfo.visual == FB_VISUAL_DIRECTCOLOR) { |
| struct fb_cmap cmap; |
| unsigned short red[256], green[256], blue[256]; |
| int rcols = 1 << dpy->VarInfo.red.length; |
| int gcols = 1 << dpy->VarInfo.green.length; |
| int bcols = 1 << dpy->VarInfo.blue.length; |
| int i; |
| |
| cmap.start = 0; |
| cmap.len = gcols; |
| cmap.red = red; |
| cmap.green = green; |
| cmap.blue = blue; |
| cmap.transp = NULL; |
| |
| for (i = 0; i < rcols ; i++) |
| red[i] = (65536/(rcols-1)) * i; |
| |
| for (i = 0; i < gcols ; i++) |
| green[i] = (65536/(gcols-1)) * i; |
| |
| for (i = 0; i < bcols ; i++) |
| blue[i] = (65536/(bcols-1)) * i; |
| |
| if (ioctl(dpy->FrameBufferFD, FBIOPUTCMAP, (void *) &cmap) < 0) { |
| fprintf(stderr, "ioctl(FBIOPUTCMAP) failed [%d]\n", i); |
| exit(1); |
| } |
| } |
| |
| /* May need to restore regs fbdev has clobbered: |
| */ |
| if (!dpy->driver->postValidateMode( &dpy->driverContext )) { |
| fprintf(stderr, "Driver postValidateMode() failed\n"); |
| return 0; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| |
| /** |
| * \brief Restore the framebuffer device to state it was in before we started |
| * |
| * Undoes the work done by SetupFBDev(). |
| * |
| * \param dpy the display handle. |
| * |
| * \return GL_TRUE on success, or GL_FALSE on failure. |
| * |
| * \sa Called from XDestroyWindow(). |
| * |
| * \internal |
| * Restores the original variable screen info. |
| */ |
| static GLboolean |
| RestoreFBDev( Display *dpy ) |
| { |
| /* restore original variable screen info */ |
| if (ioctl(dpy->FrameBufferFD, FBIOPUT_VSCREENINFO, &dpy->OrigVarInfo)) { |
| fprintf(stderr, "ioctl(FBIOPUT_VSCREENINFO failed): %s\n", |
| strerror(errno)); |
| return GL_FALSE; |
| } |
| dpy->VarInfo = dpy->OrigVarInfo; |
| |
| return GL_TRUE; |
| } |
| |
| |
| /** |
| * \brief Close the framebuffer device. |
| * |
| * \param dpy the display handle. |
| * |
| * \sa Called from XCloseDisplay(). |
| * |
| * \internal |
| * Unmaps the framebuffer and MMIO region. Restores the text mode and the |
| * original virtual terminal. Closes the console and framebuffer devices. |
| */ |
| static void |
| CloseFBDev( Display *dpy ) |
| { |
| struct vt_mode VT; |
| |
| munmap(dpy->driverContext.FBAddress, dpy->driverContext.FBSize); |
| munmap(dpy->driverContext.MMIOAddress, dpy->driverContext.MMIOSize); |
| |
| if (dpy->ConsoleFD) { |
| /* restore text mode */ |
| ioctl(dpy->ConsoleFD, KDSETMODE, KD_TEXT); |
| |
| /* set vt */ |
| if (ioctl(dpy->ConsoleFD, VT_GETMODE, &VT) != -1) { |
| VT.mode = VT_AUTO; |
| ioctl(dpy->ConsoleFD, VT_SETMODE, &VT); |
| } |
| |
| /* restore original vt */ |
| if (dpy->OriginalVT >= 0) { |
| ioctl(dpy->ConsoleFD, VT_ACTIVATE, dpy->OriginalVT); |
| dpy->OriginalVT = -1; |
| } |
| |
| close(dpy->ConsoleFD); |
| } |
| close(dpy->FrameBufferFD); |
| } |
| |
| /*@}*/ |
| |
| |
| /**********************************************************************/ |
| /** \name Misc functions needed for DRI drivers */ |
| /**********************************************************************/ |
| /*@{*/ |
| |
| /** |
| * \brief Find the DRI screen dependent methods associated with the display. |
| * |
| * \param dpy a display handle, as returned by XOpenDisplay(). |
| * \param scrn the screen number. Not referenced. |
| * |
| * \returns a pointer to a __DRIscreenRec structure. |
| * |
| * \internal |
| * Returns the MiniGLXDisplayRec::driScreen attribute. |
| */ |
| static __DRIscreen * |
| __glXFindDRIScreen(__DRInativeDisplay *dpy, int scrn) |
| { |
| (void) scrn; |
| return &((Display*)dpy)->driScreen; |
| } |
| |
| /** |
| * \brief Validate a drawable. |
| * |
| * \param dpy a display handle, as returned by XOpenDisplay(). |
| * \param draw drawable to validate. |
| * |
| * \internal |
| * Since Mini GLX only supports one window, compares the specified drawable with |
| * the MiniGLXDisplayRec::TheWindow attribute. |
| */ |
| static GLboolean |
| __glXWindowExists(__DRInativeDisplay *dpy, __DRIid draw) |
| { |
| const Display * const display = (Display*)dpy; |
| if (display->TheWindow == (Window) draw) |
| return True; |
| else |
| return False; |
| } |
| |
| /** |
| * \brief Get current thread ID. |
| * |
| * \return thread ID. |
| * |
| * \internal |
| * Always returns 0. |
| */ |
| /*unsigned long |
| _glthread_GetID(void) |
| { |
| return 0; |
| }*/ |
| |
| /*@}*/ |
| |
| |
| /** |
| * \brief Scan Linux /prog/bus/pci/devices file to determine hardware |
| * chipset based on supplied bus ID. |
| * |
| * \return probed chipset (non-zero) on success, zero otherwise. |
| * |
| * \internal |
| */ |
| static int get_chipset_from_busid( Display *dpy ) |
| { |
| char buf[0x200]; |
| FILE *file; |
| const char *fname = "/proc/bus/pci/devices"; |
| int retval = 0; |
| |
| if (!(file = fopen(fname,"r"))) { |
| fprintf(stderr, "couldn't open %s: %s\n", fname, strerror(errno)); |
| return 0; |
| } |
| |
| while (fgets(buf, sizeof(buf)-1, file)) { |
| unsigned int nr, bus, dev, fn, vendor, device, encode; |
| nr = sscanf(buf, "%04x\t%04x%04x", &encode, |
| &vendor, &device); |
| |
| bus = encode >> 8; |
| dev = (encode & 0xFF) >> 3; |
| fn = encode & 0x7; |
| |
| if (nr != 3) |
| break; |
| |
| if (bus == dpy->driverContext.pciBus && |
| dev == dpy->driverContext.pciDevice && |
| fn == dpy->driverContext.pciFunc) { |
| retval = device; |
| break; |
| } |
| } |
| |
| fclose(file); |
| |
| if (retval) |
| fprintf(stderr, "[miniglx] probed chipset 0x%x\n", retval); |
| else |
| fprintf(stderr, "[miniglx] failed to probe chipset\n"); |
| |
| return retval; |
| } |
| |
| |
| /** |
| * \brief Read settings from a configuration file. |
| * |
| * The configuration file is usually "/etc/miniglx.conf", but can be overridden |
| * with the MINIGLX_CONF environment variable. |
| * |
| * The format consists in \code option = value \endcode lines. The option names |
| * corresponds to the fields in MiniGLXDisplayRec. |
| * |
| * \param dpy the display handle as. |
| * |
| * \return non-zero on success, zero otherwise. |
| * |
| * \internal |
| * Sets some defaults. Opens and parses the the Mini GLX configuration file and |
| * fills in the MiniGLXDisplayRec field that corresponds for each option. |
| */ |
| static int __read_config_file( Display *dpy ) |
| { |
| FILE *file; |
| const char *fname; |
| |
| /* Fallback/defaults |
| */ |
| dpy->fbdevDevice = "/dev/fb0"; |
| dpy->clientDriverName = "fb_dri.so"; |
| dpy->driverContext.pciBus = 0; |
| dpy->driverContext.pciDevice = 0; |
| dpy->driverContext.pciFunc = 0; |
| dpy->driverContext.chipset = 0; |
| dpy->driverContext.pciBusID = 0; |
| dpy->driverContext.shared.virtualWidth = 1280; |
| dpy->driverContext.shared.virtualHeight = 1024; |
| dpy->driverContext.bpp = 32; |
| dpy->driverContext.cpp = 4; |
| dpy->rotateMode = 0; |
| dpy->driverContext.agpmode = 1; |
| dpy->driverContext.isPCI = 0; |
| dpy->driverContext.colorTiling = 0; |
| |
| fname = getenv("MINIGLX_CONF"); |
| if (!fname) fname = "/etc/miniglx.conf"; |
| |
| file = fopen(fname, "r"); |
| if (!file) { |
| fprintf(stderr, "couldn't open config file %s: %s\n", fname, strerror(errno)); |
| return 0; |
| } |
| |
| |
| while (!feof(file)) { |
| char buf[81], *opt = buf, *val, *tmp1, *tmp2; |
| fgets(buf, sizeof(buf), file); |
| |
| /* Parse 'opt = val' -- must be easier ways to do this. |
| */ |
| while (isspace(*opt)) opt++; |
| val = opt; |
| if (*val == '#') continue; /* comment */ |
| while (!isspace(*val) && *val != '=' && *val) val++; |
| tmp1 = val; |
| while (isspace(*val)) val++; |
| if (*val != '=') continue; |
| *tmp1 = 0; |
| val++; |
| while (isspace(*val)) val++; |
| tmp2 = val; |
| while (!isspace(*tmp2) && *tmp2 != '\n' && *tmp2) tmp2++; |
| *tmp2 = 0; |
| |
| |
| if (strcmp(opt, "fbdevDevice") == 0) |
| dpy->fbdevDevice = strdup(val); |
| else if (strcmp(opt, "clientDriverName") == 0) |
| dpy->clientDriverName = strdup(val); |
| else if (strcmp(opt, "rotateMode") == 0) |
| dpy->rotateMode = atoi(val) ? 1 : 0; |
| else if (strcmp(opt, "pciBusID") == 0) { |
| if (sscanf(val, "PCI:%d:%d:%d", |
| &dpy->driverContext.pciBus, |
| &dpy->driverContext.pciDevice, |
| &dpy->driverContext.pciFunc) != 3) { |
| fprintf(stderr, "malformed bus id: %s\n", val); |
| continue; |
| } |
| dpy->driverContext.pciBusID = strdup(val); |
| } |
| else if (strcmp(opt, "chipset") == 0) { |
| if (sscanf(val, "0x%x", &dpy->driverContext.chipset) != 1) |
| fprintf(stderr, "malformed chipset: %s\n", opt); |
| } |
| else if (strcmp(opt, "virtualWidth") == 0) { |
| if (sscanf(val, "%d", &dpy->driverContext.shared.virtualWidth) != 1) |
| fprintf(stderr, "malformed virtualWidth: %s\n", opt); |
| } |
| else if (strcmp(opt, "virtualHeight") == 0) { |
| if (sscanf(val, "%d", &dpy->driverContext.shared.virtualHeight) != 1) |
| fprintf(stderr, "malformed virutalHeight: %s\n", opt); |
| } |
| else if (strcmp(opt, "bpp") == 0) { |
| if (sscanf(val, "%d", &dpy->driverContext.bpp) != 1) |
| fprintf(stderr, "malformed bpp: %s\n", opt); |
| dpy->driverContext.cpp = dpy->driverContext.bpp / 8; |
| } |
| else if (strcmp(opt, "agpmode") == 0) { |
| if (sscanf(val, "%d", &dpy->driverContext.agpmode) != 1) |
| fprintf(stderr, "malformed agpmode: %s\n", opt); |
| } |
| else if (strcmp(opt, "isPCI") == 0) { |
| dpy->driverContext.isPCI = atoi(val) ? 1 : 0; |
| } |
| else if (strcmp(opt, "colorTiling") == 0) { |
| dpy->driverContext.colorTiling = atoi(val) ? 1 : 0; |
| } |
| } |
| |
| fclose(file); |
| |
| if (dpy->driverContext.chipset == 0 && dpy->driverContext.pciBusID != 0) |
| dpy->driverContext.chipset = get_chipset_from_busid( dpy ); |
| |
| return 1; |
| } |
| |
| /** |
| * Versioned name of the expected \c __driCreateNewScreen function. |
| * |
| * The version of the last incompatible loader/driver inteface change is |
| * appended to the name of the \c __driCreateNewScreen function. This |
| * prevents loaders from trying to load drivers that are too old. |
| * |
| * \todo |
| * Create a macro or something so that this is automatically updated. |
| */ |
| static const char createNewScreenName[] = "__driCreateNewScreen_20050727"; |
| |
| |
| static int InitDriver( Display *dpy ) |
| { |
| /* |
| * Begin DRI setup. |
| * We're kind of combining the per-display and per-screen information |
| * which was kept separate in XFree86/DRI's libGL. |
| */ |
| dpy->dlHandle = dlopen(dpy->clientDriverName, RTLD_NOW | RTLD_GLOBAL); |
| if (!dpy->dlHandle) { |
| fprintf(stderr, "Unable to open %s: %s\n", dpy->clientDriverName, |
| dlerror()); |
| goto failed; |
| } |
| |
| /* Pull in Mini GLX specific hooks: |
| */ |
| dpy->driver = (struct DRIDriverRec *) dlsym(dpy->dlHandle, |
| "__driDriver"); |
| if (!dpy->driver) { |
| fprintf(stderr, "Couldn't find __driDriver in %s\n", |
| dpy->clientDriverName); |
| goto failed; |
| } |
| |
| /* Pull in standard DRI client-side driver hooks: |
| */ |
| dpy->createNewScreen = (PFNCREATENEWSCREENFUNC) |
| dlsym(dpy->dlHandle, createNewScreenName); |
| if (!dpy->createNewScreen) { |
| fprintf(stderr, "Couldn't find %s in %s\n", createNewScreenName, |
| dpy->clientDriverName); |
| goto failed; |
| } |
| |
| return GL_TRUE; |
| |
| failed: |
| if (dpy->dlHandle) { |
| dlclose(dpy->dlHandle); |
| dpy->dlHandle = 0; |
| } |
| return GL_FALSE; |
| } |
| |
| |
| /**********************************************************************/ |
| /** \name Public API functions (Xlib and GLX) */ |
| /**********************************************************************/ |
| /*@{*/ |
| |
| |
| /** |
| * \brief Initialize the graphics system. |
| * |
| * \param display_name currently ignored. It is recommended to pass it as NULL. |
| * \return a pointer to a #Display if the function is able to initialize |
| * the graphics system, NULL otherwise. |
| * |
| * Allocates a MiniGLXDisplayRec structure and fills in with information from a |
| * configuration file. |
| * |
| * Calls OpenFBDev() to open the framebuffer device and calls |
| * DRIDriverRec::initFBDev to do the client-side initialization on it. |
| * |
| * Loads the DRI driver and pulls in Mini GLX specific hooks into a |
| * DRIDriverRec structure, and the standard DRI \e __driCreateScreen hook. |
| * Asks the driver for a list of supported visuals. Performs the per-screen |
| * client-side initialization. Also setups the callbacks in the screen private |
| * information. |
| * |
| * Does the framebuffer device setup. Calls __miniglx_open_connections() to |
| * serve clients. |
| */ |
| Display * |
| __miniglx_StartServer( const char *display_name ) |
| { |
| Display *dpy; |
| int use_vt = 0; |
| |
| pci_system_init(); |
| |
| dpy = (Display *)calloc(1, sizeof(Display)); |
| if (!dpy) |
| return NULL; |
| |
| dpy->IsClient = False; |
| |
| if (!__read_config_file( dpy )) { |
| fprintf(stderr, "Couldn't get configuration details\n"); |
| free(dpy); |
| return NULL; |
| } |
| |
| /* Open the fbdev device |
| */ |
| if (!OpenFBDev(dpy, use_vt)) { |
| fprintf(stderr, "OpenFBDev failed\n"); |
| free(dpy); |
| return NULL; |
| } |
| |
| if (!InitDriver(dpy)) { |
| fprintf(stderr, "InitDriver failed\n"); |
| free(dpy); |
| return NULL; |
| } |
| |
| /* Perform the initialization normally done in the X server |
| */ |
| if (!dpy->driver->initFBDev( &dpy->driverContext )) { |
| fprintf(stderr, "%s: __driInitFBDev failed\n", __FUNCTION__); |
| dlclose(dpy->dlHandle); |
| return GL_FALSE; |
| } |
| |
| /* do fbdev setup |
| */ |
| if (!SetupFBDev(dpy)) { |
| fprintf(stderr, "SetupFBDev failed\n"); |
| free(dpy); |
| return NULL; |
| } |
| |
| /* unlock here if not using VT -- JDS */ |
| if (!use_vt) { |
| if (dpy->driver->restoreHardware) |
| dpy->driver->restoreHardware( &dpy->driverContext ); |
| DRM_UNLOCK( dpy->driverContext.drmFD, |
| dpy->driverContext.pSAREA, |
| dpy->driverContext.serverContext ); |
| dpy->hwActive = 1; |
| } |
| |
| /* Ready for clients: |
| */ |
| if (!__miniglx_open_connections(dpy)) { |
| free(dpy); |
| return NULL; |
| } |
| |
| return dpy; |
| } |
| |
| |
| /** |
| * Implement \c __DRIinterfaceMethods::getProcAddress. |
| */ |
| static __DRIfuncPtr get_proc_address( const char * proc_name ) |
| { |
| (void) proc_name; |
| return NULL; |
| } |
| |
| |
| /** |
| * Table of functions exported by the loader to the driver. |
| */ |
| static const __DRIinterfaceMethods interface_methods = { |
| get_proc_address, |
| |
| _gl_context_modes_create, |
| _gl_context_modes_destroy, |
| |
| __glXFindDRIScreen, |
| __glXWindowExists, |
| |
| __glXCreateContextWithConfig, |
| xf86DRI_DestroyContext, |
| |
| xf86DRI_CreateDrawable, |
| xf86DRI_DestroyDrawable, |
| __glXGetDrawableInfo, |
| |
| __glXGetUST, |
| __glXGetMscRate, |
| }; |
| |
| |
| static void * |
| CallCreateNewScreen(Display *dpy, int scrn, __DRIscreen *psc) |
| { |
| void *psp = NULL; |
| drm_handle_t hSAREA; |
| drmAddress pSAREA; |
| const char *BusID; |
| int i; |
| __DRIversion ddx_version; |
| __DRIversion dri_version; |
| __DRIversion drm_version; |
| __DRIframebuffer framebuffer; |
| int fd = -1; |
| int status; |
| const char * err_msg; |
| const char * err_extra; |
| drmVersionPtr version; |
| drm_handle_t hFB; |
| drm_magic_t magic; |
| |
| |
| hSAREA = dpy->driverContext.shared.hSAREA; |
| BusID = dpy->driverContext.pciBusID; |
| |
| fd = drmOpen(NULL, BusID); |
| |
| err_msg = "open DRM"; |
| err_extra = strerror( -fd ); |
| |
| if (fd < 0) goto done; |
| |
| err_msg = "drmGetMagic"; |
| err_extra = NULL; |
| |
| if (drmGetMagic(fd, &magic)) goto done; |
| |
| dpy->authorized = False; |
| send_char_msg( dpy, 0, _Authorize ); |
| send_msg( dpy, 0, &magic, sizeof(magic)); |
| |
| /* force net buffer flush */ |
| while (!dpy->authorized) |
| handle_fd_events( dpy, 0 ); |
| |
| version = drmGetVersion(fd); |
| if (version) { |
| drm_version.major = version->version_major; |
| drm_version.minor = version->version_minor; |
| drm_version.patch = version->version_patchlevel; |
| drmFreeVersion(version); |
| } |
| else { |
| drm_version.major = -1; |
| drm_version.minor = -1; |
| drm_version.patch = -1; |
| } |
| |
| /* |
| * Get device name (like "tdfx") and the ddx version numbers. |
| * We'll check the version in each DRI driver's "createScreen" |
| * function. |
| */ |
| ddx_version.major = -1; |
| ddx_version.minor = 0; |
| ddx_version.patch = 0; |
| |
| /* |
| * Get the DRI X extension version. |
| */ |
| dri_version.major = 4; |
| dri_version.minor = 0; |
| dri_version.patch = 0; |
| |
| /* |
| * Get device-specific info. pDevPriv will point to a struct |
| * (such as DRIRADEONRec in xfree86/driver/ati/radeon_dri.h) |
| * that has information about the screen size, depth, pitch, |
| * ancilliary buffers, DRM mmap handles, etc. |
| */ |
| hFB = dpy->driverContext.shared.hFrameBuffer; |
| framebuffer.size = dpy->driverContext.shared.fbSize; |
| framebuffer.stride = dpy->driverContext.shared.fbStride; |
| framebuffer.dev_priv_size = dpy->driverContext.driverClientMsgSize; |
| framebuffer.dev_priv = dpy->driverContext.driverClientMsg; |
| framebuffer.width = dpy->driverContext.shared.virtualWidth; |
| framebuffer.height = dpy->driverContext.shared.virtualHeight; |
| |
| /* |
| * Map the framebuffer region. |
| */ |
| status = drmMap(fd, hFB, framebuffer.size, |
| (drmAddressPtr)&framebuffer.base); |
| |
| err_msg = "drmMap of framebuffer"; |
| err_extra = strerror( -status ); |
| |
| if ( status != 0 ) goto done; |
| |
| /* |
| * Map the SAREA region. Further mmap regions may be setup in |
| * each DRI driver's "createScreen" function. |
| */ |
| status = drmMap(fd, hSAREA, SAREA_MAX, &pSAREA); |
| |
| err_msg = "drmMap of sarea"; |
| err_extra = strerror( -status ); |
| |
| if ( status == 0 ) { |
| err_msg = "InitDriver"; |
| err_extra = NULL; |
| psp = dpy->createNewScreen(dpy, scrn, psc, NULL, |
| & ddx_version, |
| & dri_version, |
| & drm_version, |
| & framebuffer, |
| pSAREA, |
| fd, |
| 20050727, |
| & interface_methods, |
| (__GLcontextModes **) &dpy->driver_modes); |
| |
| /* fill in dummy visual ids */ |
| { |
| __GLcontextModes *temp; |
| temp = (__GLcontextModes *)dpy->driver_modes; |
| i = 1; |
| while (temp) |
| { |
| temp->visualID = i++; |
| temp=temp->next; |
| } |
| } |
| } |
| |
| done: |
| if ( psp == NULL ) { |
| if ( pSAREA != MAP_FAILED ) { |
| (void)drmUnmap(pSAREA, SAREA_MAX); |
| } |
| |
| if ( framebuffer.base != MAP_FAILED ) { |
| (void)drmUnmap((drmAddress)framebuffer.base, framebuffer.size); |
| } |
| |
| if ( framebuffer.dev_priv != NULL ) { |
| free(framebuffer.dev_priv); |
| } |
| |
| if ( fd >= 0 ) { |
| (void)drmClose(fd); |
| } |
| |
| if ( err_extra != NULL ) { |
| fprintf(stderr, "libGL error: %s failed (%s)\n", err_msg, |
| err_extra); |
| } |
| else { |
| fprintf(stderr, "libGL error: %s failed\n", err_msg ); |
| } |
| |
| fprintf(stderr, "libGL error: reverting to (slow) indirect rendering\n"); |
| } |
| |
| return psp; |
| } |
| |
| /** |
| * \brief Initialize the graphics system. |
| * |
| * \param display_name currently ignored. It is recommended to pass it as NULL. |
| * \return a pointer to a #Display if the function is able to initialize |
| * the graphics system, NULL otherwise. |
| * |
| * Allocates a MiniGLXDisplayRec structure and fills in with information from a |
| * configuration file. |
| * |
| * Calls __miniglx_open_connections() to connect to the server. |
| * |
| * Loads the DRI driver and pulls in Mini GLX specific hooks into a |
| * DRIDriverRec structure, and the standard DRI \e __driCreateScreen hook. |
| * Asks the driver for a list of supported visuals. Performs the per-screen |
| * client-side initialization. Also setups the callbacks in the screen private |
| * information. |
| * |
| * \todo |
| * - read config file |
| * - what about virtualWidth, etc? |
| * - determine dpy->driverClientMsgSize, |
| * - allocate dpy->driverClientMsg |
| */ |
| Display * |
| XOpenDisplay( const char *display_name ) |
| { |
| Display *dpy; |
| |
| dpy = (Display *)calloc(1, sizeof(Display)); |
| if (!dpy) |
| return NULL; |
| |
| dpy->IsClient = True; |
| |
| /* read config file |
| */ |
| if (!__read_config_file( dpy )) { |
| fprintf(stderr, "Couldn't get configuration details\n"); |
| free(dpy); |
| return NULL; |
| } |
| |
| /* Connect to the server and receive driverClientMsg |
| */ |
| if (!__miniglx_open_connections(dpy)) { |
| free(dpy); |
| return NULL; |
| } |
| |
| /* dlopen the driver .so file |
| */ |
| if (!InitDriver(dpy)) { |
| fprintf(stderr, "InitDriver failed\n"); |
| free(dpy); |
| return NULL; |
| } |
| |
| /* Perform the client-side initialization. |
| * |
| * Clearly there is a limit of one on the number of windows in |
| * existence at any time. |
| * |
| * Need to shut down DRM and free DRI data in XDestroyWindow(), too. |
| */ |
| dpy->driScreen.private = CallCreateNewScreen(dpy, 0, &dpy->driScreen); |
| if (!dpy->driScreen.private) { |
| fprintf(stderr, "%s: __driCreateScreen failed\n", __FUNCTION__); |
| dlclose(dpy->dlHandle); |
| free(dpy); |
| return NULL; |
| } |
| |
| /* Anything more to do? |
| */ |
| return dpy; |
| } |
| |
| |
| /** |
| * \brief Release display resources. |
| * |
| * When the application is about to exit, the resources associated with the |
| * graphics system can be released by calling this function. |
| * |
| * \param dpy display handle. It becomes invalid at this point. |
| * |
| * Destroys the window if any, and destroys the per-screen |
| * driver private information. |
| * Calls __miniglx_close_connections(). |
| * |
| * If a server, puts the the framebuffer back into the initial state. |
| * |
| * Finally frees the display structure. |
| */ |
| void |
| XCloseDisplay( Display *dpy ) |
| { |
| glXMakeCurrent( dpy, NULL, NULL); |
| |
| if (dpy->NumWindows) |
| XDestroyWindow( dpy, dpy->TheWindow ); |
| |
| /* As this is done in XOpenDisplay, need to undo it here: |
| */ |
| dpy->driScreen.destroyScreen(dpy, 0, dpy->driScreen.private); |
| |
| __miniglx_close_connections( dpy ); |
| |
| if (!dpy->IsClient) { |
| /* put framebuffer back to initial state |
| */ |
| (*dpy->driver->haltFBDev)( &dpy->driverContext ); |
| RestoreFBDev(dpy); |
| CloseFBDev(dpy); |
| } |
| |
| dlclose(dpy->dlHandle); |
| free(dpy); |
| } |
| |
| |
| /** |
| * \brief Window creation. |
| * |
| * \param display a display handle, as returned by XOpenDisplay(). |
| * \param parent the parent window for the new window. For Mini GLX this should |
| * be |
| * \code RootWindow(display, 0) \endcode |
| * \param x the window abscissa. For Mini GLX, it should be zero. |
| * \param y the window ordinate. For Mini GLX, it should be zero. |
| * \param width the window width. For Mini GLX, this specifies the desired |
| * screen width such as 1024 or 1280. |
| * \param height the window height. For Mini GLX, this specifies the desired |
| * screen height such as 768 or 1024. |
| * \param border_width the border width. For Mini GLX, it should be zero. |
| * \param depth the window pixel depth. For Mini GLX, this should be the depth |
| * found in the #XVisualInfo object returned by glXChooseVisual() |
| * \param winclass the window class. For Mini GLX this value should be |
| * #InputOutput. |
| * \param visual the visual type. It should be the visual field of the |
| * #XVisualInfo object returned by glXChooseVisual(). |
| * \param valuemask which fields of the XSetWindowAttributes() are to be used. |
| * For Mini GLX this is typically the bitmask |
| * \code CWBackPixel | CWBorderPixel | CWColormap \endcode |
| * \param attributes initial window attributes. The |
| * XSetWindowAttributes::background_pixel, XSetWindowAttributes::border_pixel |
| * and XSetWindowAttributes::colormap fields should be set. |
| * |
| * \return a window handle if it succeeds or zero if it fails. |
| * |
| * \note For Mini GLX, windows are full-screen; they cover the entire frame |
| * buffer. Also, Mini GLX imposes a limit of one window. A second window |
| * cannot be created until the first one is destroyed. |
| * |
| * This function creates and initializes a ::MiniGLXWindowRec structure after |
| * ensuring that there is no other window created. Performs the per-drawable |
| * client-side initialization calling the __DRIscreenRec::createDrawable |
| * method. |
| * |
| */ |
| Window |
| XCreateWindow( Display *dpy, Window parent, int x, int y, |
| unsigned int width, unsigned int height, |
| unsigned int border_width, int depth, unsigned int winclass, |
| Visual *visual, unsigned long valuemask, |
| XSetWindowAttributes *attributes ) |
| { |
| const int empty_attribute_list[1] = { None }; |
| |
| Window win; |
| |
| /* ignored */ |
| (void) x; |
| (void) y; |
| (void) border_width; |
| (void) depth; |
| (void) winclass; |
| (void) valuemask; |
| (void) attributes; |
| |
| if (!dpy->IsClient) { |
| fprintf(stderr, "Server process may not create windows (currently)\n"); |
| return NULL; |
| } |
| |
| if (dpy->NumWindows > 0) |
| return NULL; /* only allow one window */ |
| |
| assert(dpy->TheWindow == NULL); |
| |
| win = malloc(sizeof(struct MiniGLXWindowRec)); |
| if (!win) |
| return NULL; |
| |
| /* In rotated mode, translate incoming x,y,width,height into |
| * 'normal' coordinates. |
| */ |
| if (dpy->rotateMode) { |
| int tmp; |
| tmp = width; width = height; height = tmp; |
| tmp = x; x = y; y = tmp; |
| } |
| |
| /* init other per-window fields */ |
| win->x = x; |
| win->y = y; |
| win->w = width; |
| win->h = height; |
| win->visual = visual; /* ptr assignment */ |
| |
| win->bytesPerPixel = dpy->driverContext.cpp; |
| win->rowStride = dpy->driverContext.shared.virtualWidth * win->bytesPerPixel; |
| win->size = win->rowStride * height; |
| win->frontStart = dpy->driverContext.FBAddress + (win->rowStride * win->x) + (win->y * win->bytesPerPixel); |
| win->frontBottom = (GLubyte *) win->frontStart + (height-1) * win->rowStride; |
| |
| /* This is incorrect: the hardware driver could put the backbuffer |
| * just about anywhere. These fields, including the above are |
| * hardware dependent & don't really belong here. |
| */ |
| if (visual->mode->doubleBufferMode) { |
| win->backStart = (GLubyte *) win->frontStart + |
| win->rowStride * dpy->driverContext.shared.virtualHeight; |
| win->backBottom = (GLubyte *) win->backStart |
| + (height - 1) * win->rowStride; |
| win->curBottom = win->backBottom; |
| } |
| else { |
| /* single buffered */ |
| win->backStart = NULL; |
| win->backBottom = NULL; |
| win->curBottom = win->frontBottom; |
| } |
| |
| dpy->driScreen.createNewDrawable(dpy, visual->mode, (int) win, |
| &win->driDrawable, GLX_WINDOW_BIT, empty_attribute_list); |
| |
| if (!win->driDrawable.private) { |
| fprintf(stderr, "%s: dri.createDrawable failed\n", __FUNCTION__); |
| free(win); |
| return NULL; |
| } |
| |
| dpy->NumWindows++; |
| dpy->TheWindow = win; |
| |
| return win; |
| } |
| |
| |
| /** |
| * \brief Destroy window. |
| * |
| * \param display display handle. |
| * \param w window handle. |
| * |
| * This function calls XUnmapWindow() and frees window \p w. |
| * |
| * In case of destroying the current buffer first unbinds the GLX context |
| * by calling glXMakeCurrent() with no drawable. |
| */ |
| void |
| XDestroyWindow( Display *display, Window win ) |
| { |
| if (display && display->IsClient && win) { |
| /* check if destroying the current buffer */ |
| Window curDraw = glXGetCurrentDrawable(); |
| if (win == curDraw) { |
| glXMakeCurrent( display, NULL, NULL); |
| } |
| |
| XUnmapWindow( display, win ); |
| |
| /* Destroy the drawable. */ |
| win->driDrawable.destroyDrawable(display, win->driDrawable.private); |
| free(win); |
| |
| /* unlink window from display */ |
| display->NumWindows--; |
| assert(display->NumWindows == 0); |
| display->TheWindow = NULL; |
| } |
| } |
| |
| |
| |
| |
| /** |
| * \brief Create color map structure. |
| * |
| * \param dpy the display handle as returned by XOpenDisplay(). |
| * \param w the window on whose screen you want to create a color map. This |
| * parameter is ignored by Mini GLX but should be the value returned by the |
| * \code RootWindow(display, 0) \endcode macro. |
| * \param visual a visual type supported on the screen. This parameter is |
| * ignored by Mini GLX but should be the XVisualInfo::visual returned by |
| * glXChooseVisual(). |
| * \param alloc the color map entries to be allocated. This parameter is ignored |
| * by Mini GLX but should be set to #AllocNone. |
| * |
| * \return the color map. |
| * |
| * This function is only provided to ease porting. Practically a no-op - |
| * returns a pointer to a dynamically allocated chunk of memory (one byte). |
| */ |
| Colormap |
| XCreateColormap( Display *dpy, Window w, Visual *visual, int alloc ) |
| { |
| (void) dpy; |
| (void) w; |
| (void) visual; |
| (void) alloc; |
| return (Colormap) malloc(1); |
| } |
| |
| |
| /** |
| * \brief Destroy color map structure. |
| * |
| * \param display The display handle as returned by XOpenDisplay(). |
| * \param colormap the color map to destroy. |
| * |
| * This function is only provided to ease porting. Practically a no-op. |
| * |
| * Frees the memory pointed by \p colormap. |
| */ |
| void |
| XFreeColormap( Display *display, Colormap colormap ) |
| { |
| (void) display; |
| (void) colormap; |
| free(colormap); |
| } |
| |
| |
| /** |
| * \brief Free client data. |
| * |
| * \param data the data that is to be freed. |
| * |
| * Frees the memory pointed by \p data. |
| */ |
| void |
| XFree( void *data ) |
| { |
| free(data); |
| } |
| |
| |
| /** |
| * \brief Query available visuals. |
| * |
| * \param dpy the display handle, as returned by XOpenDisplay(). |
| * \param vinfo_mask a bitmask indicating which fields of the \p vinfo_template |
| * are to be matched. The value must be \c VisualScreenMask. |
| * \param vinfo_template a template whose fields indicate which visual |
| * attributes must be matched by the results. The XVisualInfo::screen field of |
| * this structure must be zero. |
| * \param nitens_return will hold the number of visuals returned. |
| * |
| * \return the address of an array of all available visuals. |
| * |
| * An example of using XGetVisualInfo() to get all available visuals follows: |
| * |
| * \code |
| * XVisualInfo vinfo_template, *results; |
| * int nitens_return; |
| * Display *dpy = XOpenDisplay(NULL); |
| * vinfo_template.screen = 0; |
| * results = XGetVisualInfo(dpy, VisualScreenMask, &vinfo_template, &nitens_return); |
| * \endcode |
| * |
| * Returns the list of all ::XVisualInfo available, one per |
| * ::__GLcontextMode stored in MiniGLXDisplayRec::modes. |
| */ |
| XVisualInfo * |
| XGetVisualInfo( Display *dpy, long vinfo_mask, XVisualInfo *vinfo_template, int *nitens_return ) |
| { |
| const __GLcontextModes *mode; |
| XVisualInfo *results; |
| Visual *visResults; |
| int i, n=0; |
| |
| // ASSERT(vinfo_mask == VisualScreenMask); |
| ASSERT(vinfo_template.screen == 0); |
| |
| if (vinfo_mask == VisualIDMask) |
| { |
| for ( mode = dpy->driver_modes ; mode != NULL ; mode= mode->next ) |
| if (mode->visualID == vinfo_template->visualid) |
| n=1; |
| |
| if (n==0) |
| return NULL; |
| |
| results = (XVisualInfo *)calloc(1, n * sizeof(XVisualInfo)); |
| if (!results) { |
| *nitens_return = 0; |
| return NULL; |
| } |
| |
| visResults = (Visual *)calloc(1, n * sizeof(Visual)); |
| if (!results) { |
| free(results); |
| *nitens_return = 0; |
| return NULL; |
| } |
| |
| for ( mode = dpy->driver_modes ; mode != NULL ; mode= mode->next ) |
| if (mode->visualID == vinfo_template->visualid) |
| { |
| visResults[0].mode=mode; |
| visResults[0].visInfo = results; |
| visResults[0].dpy = dpy; |
| if (dpy->driverContext.bpp == 32) |
| visResults[0].pixelFormat = PF_B8G8R8A8; /* XXX: FIX ME */ |
| else |
| visResults[0].pixelFormat = PF_B5G6R5; /* XXX: FIX ME */ |
| |
| results[0].visual = visResults; |
| results[0].visualid = mode->visualID; |
| #if defined(__cplusplus) || defined(c_plusplus) |
| results[0].c_class = TrueColor; |
| #else |
| results[0].class = TrueColor; |
| #endif |
| results[0].depth = mode->redBits + |
| mode->redBits + |
| mode->redBits + |
| mode->redBits; |
| results[0].bits_per_rgb = dpy->driverContext.bpp; |
| |
| } |
| |
| } |
| else // if (vinfo_mask == VisualScreenMask) |
| { |
| n = 0; |
| for ( mode = dpy->driver_modes ; mode != NULL ; mode = mode->next ) |
| n++; |
| |
| results = (XVisualInfo *)calloc(1, n * sizeof(XVisualInfo)); |
| if (!results) { |
| *nitens_return = 0; |
| return NULL; |
| } |
| |
| visResults = (Visual *)calloc(1, n * sizeof(Visual)); |
| if (!results) { |
| free(results); |
| *nitens_return = 0; |
| return NULL; |
| } |
| |
| for ( mode = dpy->driver_modes, i = 0 ; mode != NULL ; mode = mode->next, i++ ) { |
| visResults[i].mode = mode; |
| visResults[i].visInfo = results + i; |
| visResults[i].dpy = dpy; |
| |
| if (dpy->driverContext.bpp == 32) |
| visResults[i].pixelFormat = PF_B8G8R8A8; /* XXX: FIX ME */ |
| else |
| visResults[i].pixelFormat = PF_B5G6R5; /* XXX: FIX ME */ |
| |
| results[i].visual = visResults + i; |
| results[i].visualid = mode->visualID; |
| #if defined(__cplusplus) || defined(c_plusplus) |
| results[i].c_class = TrueColor; |
| #else |
| results[i].class = TrueColor; |
| #endif |
| results[i].depth = mode->redBits + |
| mode->redBits + |
| mode->redBits + |
| mode->redBits; |
| results[i].bits_per_rgb = dpy->driverContext.bpp; |
| } |
| } |
| *nitens_return = n; |
| return results; |
| } |
| |
| |
| /** |
| * \brief Return a visual that matches specified attributes. |
| * |
| * \param dpy the display handle, as returned by XOpenDisplay(). |
| * \param screen the screen number. It is currently ignored by Mini GLX and |
| * should be zero. |
| * \param attribList a list of GLX attributes which describe the desired pixel |
| * format. It is terminated by the token \c None. |
| * |
| * The attributes are as follows: |
| * \arg GLX_USE_GL: |
| * This attribute should always be present in order to maintain compatibility |
| * with GLX. |
| * \arg GLX_RGBA: |
| * If present, only RGBA pixel formats will be considered. Otherwise, only |
| * color index formats are considered. |
| * \arg GLX_DOUBLEBUFFER: |
| * if present, only double-buffered pixel formats will be chosen. |
| * \arg GLX_RED_SIZE \e n: |
| * Must be followed by a non-negative integer indicating the minimum number of |
| * bits per red pixel component that is acceptable. |
| * \arg GLX_GREEN_SIZE \e n: |
| * Must be followed by a non-negative integer indicating the minimum number of |
| * bits per green pixel component that is acceptable. |
| * \arg GLX_BLUE_SIZE \e n: |
| * Must be followed by a non-negative integer indicating the minimum number of |
| * bits per blue pixel component that is acceptable. |
| * \arg GLX_ALPHA_SIZE \e n: |
| * Must be followed by a non-negative integer indicating the minimum number of |
| * bits per alpha pixel component that is acceptable. |
| * \arg GLX_STENCIL_SIZE \e n: |
| * Must be followed by a non-negative integer indicating the minimum number of |
| * bits per stencil value that is acceptable. |
| * \arg GLX_DEPTH_SIZE \e n: |
| * Must be followed by a non-negative integer indicating the minimum number of |
| * bits per depth component that is acceptable. |
| * \arg None: |
| * This token is used to terminate the attribute list. |
| * |
| * \return a pointer to an #XVisualInfo object which most closely matches the |
| * requirements of the attribute list. If there is no visual which matches the |
| * request, \c NULL will be returned. |
| * |
| * \note Visuals with accumulation buffers are not available. |
| * |
| * This function searches the list of available visual configurations in |
| * MiniGLXDisplayRec::configs for a configuration which best matches the GLX |
| * attribute list parameter. A new ::XVisualInfo object is created which |
| * describes the visual configuration. The match criteria is described in the |
| * specification. |
| */ |
| XVisualInfo* |
| glXChooseVisual( Display *dpy, int screen, int *attribList ) |
| { |
| const __GLcontextModes *mode; |
| Visual *vis; |
| XVisualInfo *visInfo; |
| const int *attrib; |
| GLboolean rgbFlag = GL_FALSE, dbFlag = GL_FALSE, stereoFlag = GL_FALSE; |
| GLint redBits = 0, greenBits = 0, blueBits = 0, alphaBits = 0; |
| GLint indexBits = 0, depthBits = 0, stencilBits = 0; |
| GLint numSamples = 0; |
| int i=0; |
| |
| /* |
| * XXX in the future, <screen> might be interpreted as a VT |
| */ |
| ASSERT(dpy); |
| ASSERT(screen == 0); |
| |
| vis = (Visual *)calloc(1, sizeof(Visual)); |
| if (!vis) |
| return NULL; |
| |
| visInfo = (XVisualInfo *)malloc(sizeof(XVisualInfo)); |
| if (!visInfo) { |
| free(vis); |
| return NULL; |
| } |
| |
| visInfo->visual = vis; |
| vis->visInfo = visInfo; |
| vis->dpy = dpy; |
| |
| /* parse the attribute list */ |
| for (attrib = attribList; attrib && *attrib != None; attrib++) { |
| switch (attrib[0]) { |
| case GLX_DOUBLEBUFFER: |
| dbFlag = GL_TRUE; |
| break; |
| case GLX_RGBA: |
| rgbFlag = GL_TRUE; |
| break; |
| case GLX_RED_SIZE: |
| redBits = attrib[1]; |
| attrib++; |
| break; |
| case GLX_GREEN_SIZE: |
| greenBits = attrib[1]; |
| attrib++; |
| break; |
| case GLX_BLUE_SIZE: |
| blueBits = attrib[1]; |
| attrib++; |
| break; |
| case GLX_ALPHA_SIZE: |
| alphaBits = attrib[1]; |
| attrib++; |
| break; |
| case GLX_STENCIL_SIZE: |
| stencilBits = attrib[1]; |
| attrib++; |
| break; |
| case GLX_DEPTH_SIZE: |
| depthBits = attrib[1]; |
| attrib++; |
| break; |
| #if 0 |
| case GLX_ACCUM_RED_SIZE: |
| accumRedBits = attrib[1]; |
| attrib++; |
| break; |
| case GLX_ACCUM_GREEN_SIZE: |
| accumGreenBits = attrib[1]; |
| attrib++; |
| break; |
| case GLX_ACCUM_BLUE_SIZE: |
| accumBlueBits = attrib[1]; |
| attrib++; |
| break; |
| case GLX_ACCUM_ALPHA_SIZE: |
| accumAlphaBits = attrib[1]; |
| attrib++; |
| break; |
| case GLX_LEVEL: |
| /* ignored for now */ |
| break; |
| #endif |
| default: |
| /* unexpected token */ |
| fprintf(stderr, "unexpected token in glXChooseVisual attrib list\n"); |
| free(vis); |
| free(visInfo); |
| return NULL; |
| } |
| } |
| |
| /* search screen configs for suitable visual */ |
| (void) numSamples; |
| (void) indexBits; |
| (void) redBits; |
| (void) greenBits; |
| (void) blueBits; |
| (void) alphaBits; |
| (void) stereoFlag; |
| for ( mode = dpy->driver_modes ; mode != NULL ; mode = mode->next ) { |
| i++; |
| if (mode->rgbMode == rgbFlag && |
| mode->doubleBufferMode == dbFlag && |
| mode->redBits >= redBits && |
| mode->greenBits >= greenBits && |
| mode->blueBits >= blueBits && |
| mode->alphaBits >= alphaBits && |
| mode->depthBits >= depthBits && |
| mode->stencilBits >= stencilBits) { |
| /* found it */ |
| visInfo->visualid = i; |
| vis->mode = mode; |
| break; |
| } |
| } |
| if (!vis->mode) |
| return NULL; |
| |
| /* compute depth and bpp */ |
| if (rgbFlag) { |
| /* XXX maybe support depth 16 someday */ |
| #if defined(__cplusplus) || defined(c_plusplus) |
| visInfo->c_class = TrueColor; |
| #else |
| visInfo->class = TrueColor; |
| #endif |
| visInfo->depth = dpy->driverContext.bpp; |
| visInfo->bits_per_rgb = dpy->driverContext.bpp; |
| if (dpy->driverContext.bpp == 32) |
| vis->pixelFormat = PF_B8G8R8A8; |
| else |
| vis->pixelFormat = PF_B5G6R5; |
| } |
| else { |
| /* color index mode */ |
| #if defined(__cplusplus) || defined(c_plusplus) |
| visInfo->c_class = PseudoColor; |
| #else |
| visInfo->class = PseudoColor; |
| #endif |
| visInfo->depth = 8; |
| visInfo->bits_per_rgb = 8; /* bits/pixel */ |
| vis->pixelFormat = PF_CI8; |
| } |
| |
| return visInfo; |
| } |
| |
| |
| /** |
| * \brief Return information about GLX visuals. |
| * |
| * \param dpy the display handle, as returned by XOpenDisplay(). |
| * \param vis the visual to be queried, as returned by glXChooseVisual(). |
| * \param attrib the visual attribute to be returned. |
| * \param value pointer to an integer in which the result of the query will be |
| * stored. |
| * |
| * \return zero if no error occurs, \c GLX_INVALID_ATTRIBUTE if the attribute |
| * parameter is invalid, or \c GLX_BAD_VISUAL if the \p vis parameter is |
| * invalid. |
| * |
| * Returns the appropriate attribute of ::__GLXvisualConfig pointed by |
| * MiniGLXVisualRec::glxConfig of XVisualInfo::visual. |
| * |
| * \sa data types. |
| */ |
| int |
| glXGetConfig( Display *dpy, XVisualInfo *vis, int attrib, int *value ) |
| { |
| const __GLcontextModes *mode = vis->visual->mode; |
| if (!mode) { |
| *value = 0; |
| return GLX_BAD_VISUAL; |
| } |
| |
| switch (attrib) { |
| case GLX_USE_GL: |
| *value = True; |
| return 0; |
| case GLX_RGBA: |
| *value = mode->rgbMode; |
| return 0; |
| case GLX_DOUBLEBUFFER: |
| *value = mode->doubleBufferMode; |
| return 0; |
| case GLX_RED_SIZE: |
| *value = mode->redBits; |
| return 0; |
| case GLX_GREEN_SIZE: |
| *value = mode->greenBits; |
| return 0; |
| case GLX_BLUE_SIZE: |
| *value = mode->blueBits; |
| return 0; |
| case GLX_ALPHA_SIZE: |
| *value = mode->alphaBits; |
| return 0; |
| case GLX_DEPTH_SIZE: |
| *value = mode->depthBits; |
| return 0; |
| case GLX_STENCIL_SIZE: |
| *value = mode->stencilBits; |
| return 0; |
| default: |
| *value = 0; |
| return GLX_BAD_ATTRIBUTE; |
| } |
| return 0; |
| } |
| |
| |
| /** |
| * \brief Create a new GLX rendering context. |
| * |
| * \param dpy the display handle, as returned by XOpenDisplay(). |
| * \param vis the visual that defines the frame buffer resources available to |
| * the rendering context, as returned by glXChooseVisual(). |
| * \param shareList If non-zero, texture objects and display lists are shared |
| * with the named rendering context. If zero, texture objects and display lists |
| * will (initially) be private to this context. They may be shared when a |
| * subsequent context is created. |
| * \param direct whether direct or indirect rendering is desired. For Mini GLX |
| * this value is ignored but it should be set to \c True. |
| * |
| * \return a ::GLXContext handle if it succeeds or zero if it fails due to |
| * invalid parameter or insufficient resources. |
| * |
| * This function creates and initializes a ::MiniGLXContextRec structure and |
| * calls the __DRIscreenRec::createContext method to initialize the client |
| * private data. |
| */ |
| GLXContext |
| glXCreateContext( Display *dpy, XVisualInfo *vis, |
| GLXContext shareList, Bool direct ) |
| { |
| GLXContext ctx; |
| void *sharePriv; |
| |
| ASSERT(vis); |
| |
| ctx = (struct MiniGLXContextRec *)calloc(1, sizeof(struct MiniGLXContextRec)); |
| if (!ctx) |
| return NULL; |
| |
| ctx->vid = vis->visualid; |
| |
| if (shareList) |
| sharePriv = shareList->driContext.private; |
| else |
| sharePriv = NULL; |
| |
| ctx->driContext.mode = vis->visual->mode; |
| ctx->driContext.private = dpy->driScreen.createNewContext(dpy, vis->visual->mode, |
| GLX_WINDOW_BIT, sharePriv, &ctx->driContext); |
| |
| if (!ctx->driContext.private) { |
| free(ctx); |
| return NULL; |
| } |
| |
| return ctx; |
| } |
| |
| |
| /** |
| * \brief Destroy a GLX context. |
| * |
| * \param dpy the display handle, as returned by XOpenDisplay(). |
| * \param ctx the GLX context to be destroyed. |
| * |
| * This function frees the \p ctx parameter after unbinding the current context |
| * by calling the __DRIcontextRec::bindContext method with zeros and calling |
| * the __DRIcontextRec::destroyContext method. |
| */ |
| void |
| glXDestroyContext( Display *dpy, GLXContext ctx ) |
| { |
| GLXContext glxctx = glXGetCurrentContext(); |
| |
| if (ctx) { |
| if (glxctx == ctx) { |
| /* destroying current context */ |
| ctx->driContext.bindContext(dpy, 0, 0, 0, 0); |
| CurrentContext = 0; |
| } |
| ctx->driContext.destroyContext(dpy, 0, ctx->driContext.private); |
| free(ctx); |
| } |
| } |
| |
| |
| /** |
| * \brief Bind a GLX context to a window or a pixmap. |
| * |
| * \param dpy the display handle, as returned by XOpenDisplay(). |
| * \param drawable the window or drawable to bind to the rendering context. |
| * This should be the value returned by XCreateWindow(). |
| * \param ctx the GLX context to be destroyed. |
| * |
| * \return \c True if it succeeds, \c False otherwise to indicate an invalid |
| * display, window or context parameter. |
| * |
| * The current rendering context may be unbound by calling glXMakeCurrent() |
| * with the window and context parameters set to zero. |
| * |
| * An application may create any number of rendering contexts and bind them as |
| * needed. Note that binding a rendering context is generally not a |
| * light-weight operation. Most simple OpenGL applications create only one |
| * rendering context. |
| * |
| * This function first unbinds any old context via |
| * __DRIcontextRec::unbindContext and binds the new one via |
| * __DRIcontextRec::bindContext. |
| * |
| * If \p drawable is zero it unbinds the GLX context by calling |
| * __DRIcontextRec::bindContext with zeros. |
| */ |
| Bool |
| glXMakeCurrent( Display *dpy, GLXDrawable drawable, GLXContext ctx) |
| { |
| if (dpy && drawable && ctx) { |
| GLXContext oldContext = glXGetCurrentContext(); |
| GLXDrawable oldDrawable = glXGetCurrentDrawable(); |
| /* unbind old */ |
| if (oldContext) { |
| oldContext->driContext.unbindContext(dpy, 0, |
| (__DRIid) oldDrawable, (__DRIid) oldDrawable, |
| &oldContext->driContext); |
| } |
| /* bind new */ |
| CurrentContext = ctx; |
| ctx->driContext.bindContext(dpy, 0, (__DRIid) drawable, |
| (__DRIid) drawable, &ctx->driContext); |
| ctx->drawBuffer = drawable; |
| ctx->curBuffer = drawable; |
| } |
| else if (ctx && dpy) { |
| /* unbind */ |
| ctx->driContext.bindContext(dpy, 0, 0, 0, 0); |
| } |
| else if (dpy) { |
| CurrentContext = 0; /* kw: this seems to be intended??? */ |
| } |
| |
| return True; |
| } |
| |
| |
| /** |
| * \brief Exchange front and back buffers. |
| * |
| * \param dpy the display handle, as returned by XOpenDisplay(). |
| * \param drawable the drawable whose buffers are to be swapped. |
| * |
| * Any pending rendering commands will be completed before the buffer swap |
| * takes place. |
| * |
| * Calling glXSwapBuffers() on a window which is single-buffered has no effect. |
| * |
| * This function just calls the __DRIdrawableRec::swapBuffers method to do the |
| * work. |
| */ |
| void |
| glXSwapBuffers( Display *dpy, GLXDrawable drawable ) |
| { |
| if (!dpy || !drawable) |
| return; |
| |
| drawable->driDrawable.swapBuffers(dpy, drawable->driDrawable.private); |
| } |
| |
| |
| /** |
| * \brief Return the current context |
| * |
| * \return the current context, as specified by glXMakeCurrent(), or zero if no |
| * context is currently bound. |
| * |
| * \sa glXCreateContext(), glXMakeCurrent() |
| * |
| * Returns the value of the ::CurrentContext global variable. |
| */ |
| GLXContext |
| glXGetCurrentContext( void ) |
| { |
| return CurrentContext; |
| } |
| |
| |
| /** |
| * \brief Return the current drawable. |
| * |
| * \return the current drawable, as specified by glXMakeCurrent(), or zero if |
| * no drawable is currently bound. |
| * |
| * This function gets the current context via glXGetCurrentContext() and |
| * returns the MiniGLXContextRec::drawBuffer attribute. |
| */ |
| GLXDrawable |
| glXGetCurrentDrawable( void ) |
| { |
| GLXContext glxctx = glXGetCurrentContext(); |
| if (glxctx) |
| return glxctx->drawBuffer; |
| else |
| return NULL; |
| } |
| |
| |
| static GLboolean |
| __glXCreateContextWithConfig(__DRInativeDisplay *dpy, int screen, |
| int fbconfigID, void *contextID, drm_context_t *hHWContext) |
| { |
| __DRIscreen *pDRIScreen; |
| __DRIscreen *psp; |
| |
| pDRIScreen = __glXFindDRIScreen(dpy, screen); |
| if ( (pDRIScreen == NULL) || (pDRIScreen->private == NULL) ) { |
| return GL_FALSE; |
| } |
| |
| psp = (__DRIscreen *) pDRIScreen->private; |
| |
| if (psp->fd) { |
| if (drmCreateContext(psp->fd, hHWContext)) { |
| fprintf(stderr, ">>> drmCreateContext failed\n"); |
| return GL_FALSE; |
| } |
| *(void**)contextID = (void*) *hHWContext; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| |
| static GLboolean |
| __glXGetDrawableInfo(__DRInativeDisplay *dpy, int scrn, |
| __DRIid draw, unsigned int * index, unsigned int * stamp, |
| int * x, int * y, int * width, int * height, |
| int * numClipRects, drm_clip_rect_t ** pClipRects, |
| int * backX, int * backY, |
| int * numBackClipRects, drm_clip_rect_t ** pBackClipRects) |
| { |
| GLXDrawable drawable = (GLXDrawable) draw; |
| drm_clip_rect_t * cliprect; |
| Display* display = (Display*)dpy; |
| __DRIscreen *psp = display->driScreen.private; |
| __DRIcontext *pcp = (__DRIcontext *)CurrentContext->driContext.private; |
| __DRIdrawable *pdp = pcp->driDrawablePriv; |
| if (drawable == 0) { |
| return GL_FALSE; |
| } |
| |
| cliprect = (drm_clip_rect_t*) _mesa_malloc(sizeof(drm_clip_rect_t)); |
| cliprect->x1 = drawable->x; |
| cliprect->y1 = drawable->y; |
| cliprect->x2 = drawable->x + drawable->w; |
| cliprect->y2 = drawable->y + drawable->h; |
| |
| /* the drawable index is by client id */ |
| *index = display->clientID; |
| |
| *stamp = pcp->driScreenPriv->pSAREA->drawableTable[display->clientID].stamp; |
| drmUpdateDrawableInfo(psp->fd, pdp->hHWDrawable, DRM_DRAWABLE_CLIPRECTS, 1, cliprect); |
| *x = drawable->x; |
| *y = drawable->y; |
| *width = drawable->w; |
| *height = drawable->h; |
| *numClipRects = 1; |
| *pClipRects = cliprect; |
| |
| *backX = drawable->x; |
| *backY = drawable->y; |
| *numBackClipRects = 0; |
| *pBackClipRects = 0; |
| |
| return GL_TRUE; |
| } |
| |
| |
| static GLboolean |
| xf86DRI_DestroyContext(__DRInativeDisplay *dpy, int screen, __DRIid context_id ) |
| { |
| return GL_TRUE; |
| } |
| |
| |
| static GLboolean |
| xf86DRI_CreateDrawable(__DRInativeDisplay *dpy, int screen, __DRIid drawable, |
| drm_drawable_t *hHWDrawable ) |
| { |
| |
| Display *display = (Display *)dpy; |
| __DRIscreen *psp = display->driScreen.private; |
| int ret; |
| ret = drmCreateDrawable(psp->fd, hHWDrawable); |
| |
| fprintf(stderr, "drawable is %d %08X ret is %d\n", *hHWDrawable, drawable, -ret); |
| if (ret != 0) |
| return GL_FALSE; |
| return GL_TRUE; |
| } |
| |
| |
| static GLboolean |
| xf86DRI_DestroyDrawable(__DRInativeDisplay *dpy, int screen, __DRIid drawable) |
| { |
| return GL_TRUE; |
| } |
| |
| |
| /** |
| * \brief Query function address. |
| * |
| * The glXGetProcAddress() function will return the address of any available |
| * OpenGL or Mini GLX function. |
| * |
| * \param procName name of the function to be returned. |
| * |
| * \return If \p procName is a valid function name, a pointer to that function |
| * will be returned. Otherwise, \c NULL will be returned. |
| * |
| * The purpose of glXGetProcAddress() is to facilitate using future extensions |
| * to OpenGL or Mini GLX. If a future version of the library adds new extension |
| * functions they'll be accessible via glXGetProcAddress(). The alternative is |
| * to hard-code calls to the new functions in the application but doing so will |
| * prevent linking the application with older versions of the library. |
| * |
| * Returns the function address by looking up its name in a static (name, |
| * address) pair list. |
| */ |
| void (*glXGetProcAddress(const GLubyte *procname))( void ) |
| { |
| struct name_address { |
| const char *name; |
| const void *func; |
| }; |
| static const struct name_address functions[] = { |
| { "glXChooseVisual", (void *) glXChooseVisual }, |
| { "glXCreateContext", (void *) glXCreateContext }, |
| { "glXDestroyContext", (void *) glXDestroyContext }, |
| { "glXMakeCurrent", (void *) glXMakeCurrent }, |
| { "glXSwapBuffers", (void *) glXSwapBuffers }, |
| { "glXGetCurrentContext", (void *) glXGetCurrentContext }, |
| { "glXGetCurrentDrawable", (void *) glXGetCurrentDrawable }, |
| { "glXGetProcAddress", (void *) glXGetProcAddress }, |
| { "XOpenDisplay", (void *) XOpenDisplay }, |
| { "XCloseDisplay", (void *) XCloseDisplay }, |
| { "XCreateWindow", (void *) XCreateWindow }, |
| { "XDestroyWindow", (void *) XDestroyWindow }, |
| { "XMapWindow", (void *) XMapWindow }, |
| { "XCreateColormap", (void *) XCreateColormap }, |
| { "XFreeColormap", (void *) XFreeColormap }, |
| { "XFree", (void *) XFree }, |
| { "XGetVisualinfo", (void *) XGetVisualInfo }, |
| { "glXCreatePbuffer", (void *) glXCreatePbuffer }, |
| { "glXDestroyPbuffer", (void *) glXDestroyPbuffer }, |
| { "glXChooseFBConfig", (void *) glXChooseFBConfig }, |
| { "glXGetVisualFromFBConfig", (void *) glXGetVisualFromFBConfig }, |
| { NULL, NULL } |
| }; |
| const struct name_address *entry; |
| for (entry = functions; entry->name; entry++) { |
| if (strcmp(entry->name, (const char *) procname) == 0) { |
| return entry->func; |
| } |
| } |
| return _glapi_get_proc_address((const char *) procname); |
| } |
| |
| void (*glXGetProcAddressARB(const GLubyte *procName))( void ) __attribute__ ((alias ("glXGetProcAddress"))); |
| |
| /** |
| * \brief Query the Mini GLX version. |
| * |
| * \param dpy the display handle. It is currently ignored, but should be the |
| * value returned by XOpenDisplay(). |
| * \param major receives the major version number of Mini GLX. |
| * \param minor receives the minor version number of Mini GLX. |
| * |
| * \return \c True if the function succeeds, \c False if the function fails due |
| * to invalid parameters. |
| * |
| * \sa #MINI_GLX_VERSION_1_0. |
| * |
| * Returns the hard-coded Mini GLX version. |
| */ |
| Bool |
| glXQueryVersion( Display *dpy, int *major, int *minor ) |
| { |
| (void) dpy; |
| *major = 1; |
| *minor = 0; |
| return True; |
| } |
| |
| |
| /** |
| * \brief Create a new pbuffer. |
| */ |
| GLXPbuffer |
| glXCreatePbuffer( Display *dpy, GLXFBConfig config, const int *attribList ) |
| { |
| return NULL; |
| } |
| |
| |
| void |
| glXDestroyPbuffer( Display *dpy, GLXPbuffer pbuf ) |
| { |
| free(pbuf); |
| } |
| |
| |
| GLXFBConfig * |
| glXChooseFBConfig( Display *dpy, int screen, const int *attribList, |
| int *nitems ) |
| { |
| GLXFBConfig *f = (GLXFBConfig *) malloc(sizeof(GLXFBConfig)); |
| f->visInfo = glXChooseVisual( dpy, screen, (int *) attribList ); |
| if (f->visInfo) { |
| *nitems = 1; |
| return f; |
| } |
| else { |
| *nitems = 0; |
| free(f); |
| return NULL; |
| } |
| } |
| |
| |
| XVisualInfo * |
| glXGetVisualFromFBConfig( Display *dpy, GLXFBConfig config ) |
| { |
| /* XVisualInfo and GLXFBConfig are the same structure */ |
| (void) dpy; |
| return config.visInfo; |
| } |
| |
| void *glXAllocateMemoryMESA(Display *dpy, int scrn, |
| size_t size, float readFreq, |
| float writeFreq, float priority) |
| { |
| if (dpy->driScreen.private && dpy->driScreen.allocateMemory) { |
| return (*dpy->driScreen.allocateMemory)( dpy, scrn, size, |
| readFreq, writeFreq, |
| priority ); |
| } |
| |
| return NULL; |
| } |
| |
| void glXFreeMemoryMESA(Display *dpy, int scrn, void *pointer) |
| { |
| if (dpy->driScreen.private && dpy->driScreen.freeMemory) { |
| (*dpy->driScreen.freeMemory)( dpy, scrn, pointer ); |
| } |
| } |
| |
| GLuint glXGetMemoryOffsetMESA( Display *dpy, int scrn, |
| const void *pointer ) |
| { |
| if (dpy->driScreen.private && dpy->driScreen.memoryOffset) { |
| return (*dpy->driScreen.memoryOffset)( dpy, scrn, pointer ); |
| } |
| |
| return 0; |
| } |
| |
| |
| /** |
| * Get the unadjusted system time (UST). Currently, the UST is measured in |
| * microseconds since Epoc. The actual resolution of the UST may vary from |
| * system to system, and the units may vary from release to release. |
| * Drivers should not call this function directly. They should instead use |
| * \c glXGetProcAddress to obtain a pointer to the function. |
| * |
| * \param ust Location to store the 64-bit UST |
| * \returns Zero on success or a negative errno value on failure. |
| * |
| * \note |
| * This function was copied directly from src/glx/x11/glxcmds.c. |
| */ |
| static int __glXGetUST( int64_t * ust ) |
| { |
| struct timeval tv; |
| |
| if ( ust == NULL ) { |
| return -EFAULT; |
| } |
| |
| if ( gettimeofday( & tv, NULL ) == 0 ) { |
| ust[0] = (tv.tv_sec * 1000000) + tv.tv_usec; |
| return 0; |
| } else { |
| return -errno; |
| } |
| } |
| |
| |
| /** |
| * |
| * \bug |
| * This needs to be implemented for miniGlx. |
| */ |
| static GLboolean __glXGetMscRate(__DRInativeDisplay * dpy, __DRIid drawable, |
| int32_t * numerator, int32_t * denominator) |
| { |
| *numerator = 0; |
| *denominator = 0; |
| return False; |
| } |
| /*@}*/ |