diff options
Diffstat (limited to 'nx-X11/extras/Mesa/src/glx/mini')
-rw-r--r-- | nx-X11/extras/Mesa/src/glx/mini/Makefile | 86 | ||||
-rw-r--r-- | nx-X11/extras/Mesa/src/glx/mini/NOTES | 115 | ||||
-rw-r--r-- | nx-X11/extras/Mesa/src/glx/mini/dispatch.c | 64 | ||||
-rw-r--r-- | nx-X11/extras/Mesa/src/glx/mini/driver.h | 169 | ||||
-rw-r--r-- | nx-X11/extras/Mesa/src/glx/mini/example.miniglx.conf | 36 | ||||
-rw-r--r-- | nx-X11/extras/Mesa/src/glx/mini/miniglx.c | 2563 | ||||
-rw-r--r-- | nx-X11/extras/Mesa/src/glx/mini/miniglxP.h | 205 | ||||
-rw-r--r-- | nx-X11/extras/Mesa/src/glx/mini/miniglx_events.c | 978 |
8 files changed, 4216 insertions, 0 deletions
diff --git a/nx-X11/extras/Mesa/src/glx/mini/Makefile b/nx-X11/extras/Mesa/src/glx/mini/Makefile new file mode 100644 index 000000000..e87905acb --- /dev/null +++ b/nx-X11/extras/Mesa/src/glx/mini/Makefile @@ -0,0 +1,86 @@ +# Build a subset DRI-based libGL.so library. +# Indirect rendering not supported, etc. + +TOP = ../../.. +include $(TOP)/configs/current + + +DEFINES += -DGLX_DIRECT_RENDERING -DIN_MINI_GLX -UIN_DRI_DRIVER + +C_SOURCES = \ + $(TOP)/src/mesa/main/dispatch.c \ + $(TOP)/src/mesa/glapi/glapi.c \ + $(TOP)/src/mesa/glapi/glthread.c \ + $(TOP)/src/mesa/drivers/dri/common/glcontextmodes.c \ + $(DRM_SOURCE_PATH)/libdrm/xf86drm.c \ + $(DRM_SOURCE_PATH)/libdrm/xf86drmHash.c \ + $(DRM_SOURCE_PATH)/libdrm/xf86drmRandom.c \ + miniglx.c \ + miniglx_events.c + +X86_SOURCES = $(TOP)/src/mesa/x86/glapi_x86.S + +OBJECTS = $(C_SOURCES:.c=.o) \ + $(ASM_SOURCES:.S=.o) + +INCLUDES = -I. $(INCLUDE_DIRS) + +INCLUDE_DIRS = \ + -I$(TOP)/include \ + -I$(TOP)/src/mesa \ + -I$(TOP)/src/mesa/main \ + -I$(TOP)/src/mesa/glapi \ + -I$(TOP)/src/mesa/math \ + -I$(TOP)/src/mesa/transform \ + -I$(TOP)/src/mesa/swrast \ + -I$(TOP)/src/mesa/swrast_setup \ + -I$(TOP)/src/mesa/drivers/dri/common \ + -I$(DRM_SOURCE_PATH)/libdrm \ + -I$(DRM_SOURCE_PATH)/shared + + +##### RULES ##### + +.c.o: + $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $< -o $@ + +.S.o: + $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $< -o $@ + + +##### TARGETS ##### + +default: depend $(LIB_DIR)/$(GL_LIB_NAME) + + +# Make libGL +$(LIB_DIR)/$(GL_LIB_NAME): $(OBJECTS) Makefile + $(TOP)/bin/mklib -o $(GL_LIB) -linker '$(CC)' \ + -major 1 -minor 2 $(MKLIB_OPTIONS) \ + -install $(LIB_DIR) $(GL_LIB_DEPS) $(OBJECTS) + rm -f $(LIB_DIR)/miniglx.conf + install example.miniglx.conf $(LIB_DIR)/miniglx.conf + + +drmtest: xf86drm.o drmtest.o + rm -f drmtest && $(CC) -o drmtest xf86drm.o drmtest.o + + +depend: $(C_SOURCES) $(ASM_SOURCES) + touch depend + $(MKDEP) $(MKDEP_OPTIONS) $(INCLUDES) $(C_SOURCES) $(ASM_SOURCES) \ + > /dev/null + + +# Emacs tags +tags: + etags `find . -name \*.[ch]` `find ../include` + + +# Remove .o and backup files +clean: + -rm -f drmtest $(LIB_DIR)/libGL.so* + -rm -f *.o *~ + -rm -f depend + +include depend diff --git a/nx-X11/extras/Mesa/src/glx/mini/NOTES b/nx-X11/extras/Mesa/src/glx/mini/NOTES new file mode 100644 index 000000000..1774107d6 --- /dev/null +++ b/nx-X11/extras/Mesa/src/glx/mini/NOTES @@ -0,0 +1,115 @@ + + +Getting MiniGLX up and running +------------------------------ + +It's necessary to do a bit of work to set up an environment to run miniglx. + +For the radeon driver, it's necessary to get the right set of kernel +modules installed before attempting to run any programs: + + rmmod radeon agpgart; + insmod agpgart; + insmod $(MESA)/src/kernel/radeonfb/radeonfb.o; + insmod $(MESA)/src/kernel/radeon/radeon.o; + +For all drivers, its necessary to reach the compiled libraries, and +tell MiniGLX where to find it's configuration file: + + export LD_LIBRARY_PATH=$(MESA)/lib; + export MINIGLX_CONF=$(MESA)/lib/miniglx.conf + +------------------------------------------------------------ + +MiniGLX Example Programs +------------------------ + +The following programs will work with miniglx: + + $(MESA)/tests/miniglx + $(MESA)/xdemos/glxgears + +Thanks to the miniglut stub library, most of the mesa glut demos will +work. In particular, the following have been tested. (Note there is +no keyboard or mouse interaction with these demos). + + $(MESA)/demos/gears + $(MESA)/demos/geartrain + $(MESA)/demos/morph3d + $(MESA)/demos/isosurf + $(MESA)/demos/texobj + $(MESA)/demos/texcyl + $(MESA)/demos/gloss + $(MESA)/demos/fire + $(MESA)/demos/tunnel + $(MESA)/demos/teapot + $(MESA)/samples/prim + $(MESA)/samples/olympic + $(MESA)/samples/star + $(MESA)/samples/wave + ...etc + +In fact most of the glut demos seem to work within the constraints of +having no keyboard/mouse interactivity. Furthermore, the use of the +glut wrapper means that these programs don't require recompilation to +run under MiniGLX -- the same binary works with both regular GLX and +MiniGLX. + + +------------------------------------------------------------ + +Porting GLX apps to MiniGLX +--------------------------- + +A quick list of issues encountered in porting existing GLX apps to +MiniGLX. Listed in no particular order. + +1) No input events + +MiniGLX doesn't provide an input layer, so any X11 input event +handling in the existing app will have to be redone for whatever +input devices exist on the target. + +2) No configuration, expose events + +Many GLX and Xlib programs wait on an event to ensure the window has +become visible after being mapped. MiniGLX provides no equivalent +facility. + +3) Different headers + +X11/Xlib.h, GL/GLX.h, etc must not be used if the program is being +compiled against MiniGLX. + +The equivalent header is GL/MiniGLX.h. + +4) Different library + +It may be necessary to link directly against the minGLX libGL.so. + +5) Reduced number of Xlib and GLX entrypoints. + +By definition (MiniGLX is a subset of GLX), many Xlib and GLX +entrypoints, structures and macros are not present in MiniGLX. It +will be necessary to find and eliminate all references to +non-supported entrypoints. + + +--------------------------------------------------------------- + +Bugs in radeonfb.o -- the radeon framebuffer driver. +---------------------------------------------------- + +Several bugs have been found in the radeonfb.o framebuffer driver. +Most of these are resolved in the version included in the MiniGLX +sources, but some remain: + +1) Occasionally, after entering graphics mode, colors appear 'shifted' +or 'translated', particularly in higher resolution modes. This is +definitely a bug in radeonfb.o as this can be provoked even when using +the software dri driver (fb_dri.so). Importance: High. Workaround: +Use 800x600 as it seems to be less frequent at this resolution, +otherwise, restart the application. + + + diff --git a/nx-X11/extras/Mesa/src/glx/mini/dispatch.c b/nx-X11/extras/Mesa/src/glx/mini/dispatch.c new file mode 100644 index 000000000..ac24df9e7 --- /dev/null +++ b/nx-X11/extras/Mesa/src/glx/mini/dispatch.c @@ -0,0 +1,64 @@ +/** + * \file miniglx/dispatch.c + * + * \brief C-based dispatch of the OpenGL entry points (glAccum(), glBegin(), + * etc). + * + * \author Brian Paul <brian@precisioninsight.com> + * + * \note This code IS NOT USED if we're compiling on an x86 system and using + * the glapi_x86.S assembly code. + */ + +/* + * Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas. + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + */ + +#include "glheader.h" +#include "glapi.h" +#include "glapitable.h" + + +#if !(defined(USE_X86_ASM) || defined(USE_SPARC_ASM)) + +#define KEYWORD1 + +#define KEYWORD2 + +#define NAME(func) gl##func + +#define DISPATCH(func, args, msg) \ + const struct _glapi_table *dispatch; \ + dispatch = _glapi_Dispatch ? _glapi_Dispatch : _glapi_get_dispatch();\ + (dispatch->func) args + +#define RETURN_DISPATCH(func, args, msg) \ + const struct _glapi_table *dispatch; \ + dispatch = _glapi_Dispatch ? _glapi_Dispatch : _glapi_get_dispatch();\ + return (dispatch->func) args + + +#include "glapitemp.h" + +#endif /* USE_X86_ASM */ diff --git a/nx-X11/extras/Mesa/src/glx/mini/driver.h b/nx-X11/extras/Mesa/src/glx/mini/driver.h new file mode 100644 index 000000000..27402641b --- /dev/null +++ b/nx-X11/extras/Mesa/src/glx/mini/driver.h @@ -0,0 +1,169 @@ +/** + * \file driver.h + * \brief DRI utility functions definitions. + * + * This module acts as glue between GLX and the actual hardware driver. A DRI + * driver doesn't really \e have to use any of this - it's optional. But, some + * useful stuff is done here that otherwise would have to be duplicated in most + * drivers. + * + * Basically, these utility functions take care of some of the dirty details of + * screen initialization, context creation, context binding, DRM setup, etc. + * + * These functions are compiled into each DRI driver so libGL.so knows nothing + * about them. + * + * Look for more comments in the dri_util.c file. + * + * \author Kevin E. Martin <kevin@precisioninsight.com> + * \author Brian Paul <brian@precisioninsight.com> + */ + +/* + * Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas. + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _driver_H_ +#define _driver_H_ + +#define CAPI /* XXX this should be globally defined somewhere */ + +#include "GL/gl.h" +#include "GL/internal/glcore.h" + +#include "drm.h" +#include "drm_sarea.h" + +/** + * \brief DRIDriverContext type. + */ +typedef struct DRIDriverContextRec { + const char *pciBusID; + int pciBus; + int pciDevice; + int pciFunc; + int chipset; + int bpp; + int cpp; + int agpmode; + int isPCI; + + int colorTiling; /**< \brief color tiling is enabled */ + + unsigned long FBStart; /**< \brief physical address of the framebuffer */ + unsigned long MMIOStart; /**< \brief physical address of the MMIO region */ + + int FBSize; /**< \brief size of the mmap'd framebuffer in bytes */ + int MMIOSize; /**< \brief size of the mmap'd MMIO region in bytes */ + + void *FBAddress; /**< \brief start of the mmap'd framebuffer */ + void *MMIOAddress; /**< \brief start of the mmap'd MMIO region */ + + /** + * \brief Client configuration details + * + * These are computed on the server and sent to clients as part of + * the initial handshaking. + */ + struct { + unsigned long hSAREA; + int SAREASize; + unsigned long hFrameBuffer; + int fbOrigin; + int fbSize; + int fbStride; + int virtualWidth; + int virtualHeight; + } shared; + + /** + * \name From DRIInfoRec + */ + /*@{*/ + int drmFD; /**< \brief DRM device file descriptor */ + drm_sarea_t *pSAREA; + unsigned int serverContext; /**< \brief DRM context only active on server */ + /*@}*/ + + + /** + * \name Driver private + * + * Populated by __driInitFBDev() + */ + /*@{*/ + void *driverPrivate; + void *driverClientMsg; + int driverClientMsgSize; + /*@}*/ +} DRIDriverContext; + +/** + * \brief Interface to the DRI driver. + * + * This structure is retrieved from the loadable driver by the \e + * __driDriver symbol to access the Mini GLX specific hardware + * initialization and take down routines. + */ +typedef struct DRIDriverRec { + /** + * \brief Validate the framebuffer device mode + */ + int (*validateMode)( const DRIDriverContext *context ); + + /** + * \brief Examine mode returned by fbdev (may differ from the one + * requested), restore any hw regs clobbered by fbdev. + */ + int (*postValidateMode)( const DRIDriverContext *context ); + + /** + * \brief Initialize the framebuffer device. + */ + int (*initFBDev)( DRIDriverContext *context ); + + /** + * \brief Halt the framebuffer device. + */ + void (*haltFBDev)( DRIDriverContext *context ); + + + /** + * \brief Idle and shutdown hardware in preparation for a VT switch. + */ + int (*shutdownHardware)( const DRIDriverContext *context ); + + /** + * \brief Restore hardware state after regaining the VT. + */ + int (*restoreHardware)( const DRIDriverContext *context ); + + /** + * \brief Notify hardware driver of gain/loose focus. May be zero + * as this is of limited utility for most drivers. + */ + void (*notifyFocus)( int have_focus ); +} DRIDriver; + +#endif /* _driver_H_ */ diff --git a/nx-X11/extras/Mesa/src/glx/mini/example.miniglx.conf b/nx-X11/extras/Mesa/src/glx/mini/example.miniglx.conf new file mode 100644 index 000000000..62dd4f65e --- /dev/null +++ b/nx-X11/extras/Mesa/src/glx/mini/example.miniglx.conf @@ -0,0 +1,36 @@ +# Example miniglx configuration file (/etc/miniglx.conf) +# + +# Framebuffer device to open: Might need to change this on dual-head +# systems. +fbdevDevice=/dev/fb0 + +# Which driver? +# radeon_dri.so -- HW accelerated radeon driver +# fb_dri.so -- Software rasterizer +clientDriverName=radeon_dri.so + +# The pci bus id of the video card. Find this with scanpci, lspci or +# look in /proc/pci. +pciBusID=PCI:1:0:0 + +# Is the card PCI or AGP ? +isPCI=0 + +# Virtual screen dimensions. Can reduce this to save videocard memory +# at the expense of maximum window size available. +virtualWidth=1280 +virtualHeight=1024 + +# Screen depth. Only 16 & 32bpp supported. +bpp=32 + +# AGP Mode. Not all cards supported (1, 2 or 4) +agpmode=1 + +# Rotated monitor? -- NOTE: only works with subsetted radeon driver! +rotateMode=0 + +# Do we want to use color tiling ? +colorTiling=0 + diff --git a/nx-X11/extras/Mesa/src/glx/mini/miniglx.c b/nx-X11/extras/Mesa/src/glx/mini/miniglx.c new file mode 100644 index 000000000..c1c4446ab --- /dev/null +++ b/nx-X11/extras/Mesa/src/glx/mini/miniglx.c @@ -0,0 +1,2563 @@ +/** + * \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" + + +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; + /* 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; + } + + + 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 = 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; + + 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 = 4; + 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 = 0; + win->y = 0; + 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->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; + __DRIscreenPrivate *psp; + + pDRIScreen = __glXFindDRIScreen(dpy, screen); + if ( (pDRIScreen == NULL) || (pDRIScreen->private == NULL) ) { + return GL_FALSE; + } + + psp = (__DRIscreenPrivate *) 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; + __DRIcontextPrivate *pcp = (__DRIcontextPrivate *)CurrentContext->driContext.private; + 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; + *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 ) +{ + 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); +} + + +/** + * \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; +} +/*@}*/ diff --git a/nx-X11/extras/Mesa/src/glx/mini/miniglxP.h b/nx-X11/extras/Mesa/src/glx/mini/miniglxP.h new file mode 100644 index 000000000..96ed0e81c --- /dev/null +++ b/nx-X11/extras/Mesa/src/glx/mini/miniglxP.h @@ -0,0 +1,205 @@ +/** + * \file miniglxP.h + * \brief Define replacements for some X data types and define the DRI-related + * data structures. + * + * \note Cut down version of glxclient.h. + * + */ + +#ifndef _mini_GLX_client_h_ +#define _mini_GLX_client_h_ + +#include <signal.h> +#include <linux/fb.h> + +#include <GL/miniglx.h> +#include "glheader.h" +#include "mtypes.h" + +#include "driver.h" +#include "GL/internal/dri_interface.h" + +/** + * \brief Supported pixel formats. + */ +enum PixelFormat { + PF_B8G8R8, /**< \brief 24-bit BGR */ + PF_B8G8R8A8, /**< \brief 32-bit BGRA */ + PF_B5G6R5, /**< \brief 16-bit BGR */ + PF_B5G5R5, /**< \brief 15-bit BGR */ + PF_CI8 /**< \brief 8-bit color index */ +}; + +/** + * \brief X Visual type. + * + * \sa ::Visual, \ref datatypes. + */ +struct MiniGLXVisualRec { + /** \brief GLX visual information */ + const __GLcontextModes *mode; + + /** \brief pointer back to corresponding ::XVisualInfo */ + XVisualInfo *visInfo; + + /** \brief display handle */ + Display *dpy; + + /** \brief pixel format */ + enum PixelFormat pixelFormat; +}; + + + +/** + * \brief X Window type. + * + * \sa ::Window, \ref datatypes. + */ +struct MiniGLXWindowRec { + Visual *visual; + /** \brief position (always 0,0) */ + int x, y; + /** \brief size */ + unsigned int w, h; + void *frontStart; /**< \brief start of front color buffer */ + void *backStart; /**< \brief start of back color buffer */ + size_t size; /**< \brief color buffer size, in bytes */ + GLuint bytesPerPixel; + GLuint rowStride; /**< \brief in bytes */ + GLubyte *frontBottom; /**< \brief pointer to last row */ + GLubyte *backBottom; /**< \brief pointer to last row */ + GLubyte *curBottom; /**< = frontBottom or backBottom */ + __DRIdrawable driDrawable; + GLuint ismapped; +}; + + +/** + * \brief GLXContext type. + * + * \sa ::GLXContext, \ref datatypes. + */ +struct MiniGLXContextRec { + Window drawBuffer; /**< \brief drawing buffer */ + Window curBuffer; /**< \brief current buffer */ + VisualID vid; /**< \brief visual ID */ + __DRIcontext driContext; /**< \brief context dependent methods */ +}; + +#define MINIGLX_BUF_SIZE 512 +#define MINIGLX_MAX_SERVER_FDS 10 +#define MINIGLX_MAX_CLIENT_FDS 1 +#define MINIGLX_EVENT_QUEUE_SZ 16 +#define MINIGLX_EVENT_QUEUE_MASK (MINIGLX_EVENT_QUEUE_SZ-1) + +/** + * A connection to/from the server + * + * All information is to/from the server is buffered and then dispatched by + * __miniglx_Select() to avoid blocking the server. + */ +struct MiniGLXConnection { + int fd; /**< \brief file descriptor */ + char readbuf[MINIGLX_BUF_SIZE]; /**< \brief read buffer */ + char writebuf[MINIGLX_BUF_SIZE]; /**< \brief write buffer */ + int readbuf_count; /**< \brief count of bytes waiting to be read */ + int writebuf_count; /**< \brief count of bytes waiting to be written */ +}; + + +/** + * \brief X Display type + * + * \sa ::Display, \ref datatypes. + */ +struct MiniGLXDisplayRec { + /** \brief fixed framebuffer screen info */ + struct fb_fix_screeninfo FixedInfo; + /** \brief original and current variable framebuffer screen info */ + struct fb_var_screeninfo OrigVarInfo, VarInfo; + struct sigaction OrigSigUsr1; + struct sigaction OrigSigUsr2; + int OriginalVT; + int ConsoleFD; /**< \brief console TTY device file descriptor */ + int FrameBufferFD; /**< \brief framebuffer device file descriptor */ + int NumWindows; /**< \brief number of open windows */ + Window TheWindow; /**< \brief open window - only allow one window for now */ + int rotateMode; + + + volatile int vtSignalFlag; + volatile int haveVT; /**< \brief whether the VT is hold */ + int hwActive; /**< \brief whether the hardware is active -- mimics + the variations of MiniGLXDisplayRec::haveVT */ + + + int IsClient; /**< \brief whether it's a client or the server */ + int clientID; + int nrFds; /**< \brief number of connections (usually just one for the clients) */ + struct MiniGLXConnection *fd; /**< \brief connections */ + int drmFd; /**< \brief handle to drm device */ + int authorized; /**< \brief has server authorized this process? */ + + struct { + int nr, head, tail; + XEvent queue[MINIGLX_EVENT_QUEUE_SZ]; + } eventqueue; + + /** + * \name Visuals + * + * Visuals (configs) in this screen. + */ + /*@{*/ + const __GLcontextModes *driver_modes; /**< \brief Modes filtered by driver. */ + /*@}*/ + + /** + * \name From __GLXdisplayPrivate + */ + /*@{*/ + PFNCREATENEWSCREENFUNC createNewScreen; /**< \brief \e __driCreateScreen hook */ + __DRIscreen driScreen; /**< \brief Screen dependent methods */ + void *dlHandle; /**< + * \brief handle to the client dynamic + * library + */ + /*@}*/ + + /** + * \brief Mini GLX specific driver hooks + */ + struct DRIDriverRec *driver; + struct DRIDriverContextRec driverContext; + + /** + * \name Configuration details + * + * They are read from a configuration file by __read_config_file(). + */ + /*@{*/ + const char *fbdevDevice; + const char *clientDriverName; + /*@}*/ +}; + +/** Character messages. */ +enum msgs { + _CanIHaveFocus, + _IDontWantFocus, + _YouveGotFocus, + _YouveLostFocus, + _RepaintPlease, + _Authorize, +}; +extern int send_msg( Display *dpy, int i, const void *msg, size_t sz ); +extern int send_char_msg( Display *dpy, int i, char msg ); +extern int blocking_read( Display *dpy, int connection, char *msg, size_t msg_size ); +extern int handle_fd_events( Display *dpy, int nonblock ); + +extern int __miniglx_open_connections( Display *dpy ); +extern void __miniglx_close_connections( Display *dpy ); + +#endif /* !_mini_GLX_client_h_ */ diff --git a/nx-X11/extras/Mesa/src/glx/mini/miniglx_events.c b/nx-X11/extras/Mesa/src/glx/mini/miniglx_events.c new file mode 100644 index 000000000..7e54ec491 --- /dev/null +++ b/nx-X11/extras/Mesa/src/glx/mini/miniglx_events.c @@ -0,0 +1,978 @@ +/** + * \file miniglx_events.c + * \brief Mini GLX client/server communication functions. + * \author Keith Whitwell + * + * The Mini GLX interface is a subset of the GLX interface, plus a + * minimal set of Xlib functions. This file adds interfaces to + * arbitrate a single cliprect between multiple direct rendering + * clients. + * + * A fairly complete client/server non-blocking communication + * mechanism. Probably overkill given that none of our messages + * currently exceed 1 byte in length and take place over the + * relatively benign channel provided by a Unix domain socket. + */ + +/* + * Mesa 3-D graphics library + * Version: 5.0 + * + * Copyright (C) 1999-2003 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. + */ + +/* $Id: miniglx_events.c,v 1.1.1.3 2005/10/18 02:51:50 kem Exp $ */ + + +#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/stat.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> + +#include <linux/kd.h> +#include <linux/vt.h> + +#include "xf86drm.h" +#include "miniglxP.h" + + +#define MINIGLX_FIFO_NAME "/tmp/miniglx.fifo" + +/** + * \brief Allocate an XEvent structure on the event queue. + * + * \param dpy the display handle. + * + * \return Pointer to the queued event structure or NULL on failure. + * + * \internal + * If there is space on the XEvent queue, return a pointer + * to the next free event and increment the eventqueue tail value. + * Otherwise return null. + */ +static XEvent *queue_event( Display *dpy ) +{ + int incr = (dpy->eventqueue.tail + 1) & MINIGLX_EVENT_QUEUE_MASK; + if (incr == dpy->eventqueue.head) { + return 0; + } + else { + XEvent *ev = &dpy->eventqueue.queue[dpy->eventqueue.tail]; + dpy->eventqueue.tail = incr; + return ev; + } +} + +/** + * \brief Dequeue an XEvent and copy it into provided storage. + * + * \param dpy the display handle. + * \param event_return pointer to copy the queued event to. + * + * \return True or False depending on success. + * + * \internal + * If there is a queued XEvent on the queue, copy it to the provided + * pointer and increment the eventqueue head value. Otherwise return + * null. + */ +static int dequeue_event( Display *dpy, XEvent *event_return ) +{ + if (dpy->eventqueue.tail == dpy->eventqueue.head) { + return False; + } + else { + *event_return = dpy->eventqueue.queue[dpy->eventqueue.head]; + dpy->eventqueue.head += 1; + dpy->eventqueue.head &= MINIGLX_EVENT_QUEUE_MASK; + return True; + } +} + +/** + * \brief Shutdown a socket connection. + * + * \param dpy the display handle. + * \param i the index in dpy->fd of the socket connection. + * + * \internal + * Shutdown and close the file descriptor. If this is the special + * connection in fd[0], issue an error message and exit - there's been + * some sort of failure somewhere. Otherwise, let the application + * know about whats happened by issuing a DestroyNotify event. + */ +static void shut_fd( Display *dpy, int i ) +{ + if (dpy->fd[i].fd < 0) + return; + + shutdown (dpy->fd[i].fd, SHUT_RDWR); + close (dpy->fd[i].fd); + dpy->fd[i].fd = -1; + dpy->fd[i].readbuf_count = 0; + dpy->fd[i].writebuf_count = 0; + + if (i == 0) { + fprintf(stderr, "server connection lost\n"); + exit(1); + } + else { + /* Pass this to the application as a DestroyNotify event. + */ + XEvent *er = queue_event(dpy); + if (!er) return; + er->xdestroywindow.type = DestroyNotify; + er->xdestroywindow.serial = 0; + er->xdestroywindow.send_event = 0; + er->xdestroywindow.display = dpy; + er->xdestroywindow.window = (Window)i; + } +} + +/** + * \brief Send a message to a socket connection. + * + * \param dpy the display handle. + * \param i the index in dpy->fd of the socket connection. + * \param msg the message to send. + * \param sz the size of the message + * + * \internal + * Copy the message to the write buffer for the nominated connection. + * This will be actually sent to that file descriptor from + * __miniglx_Select(). + */ +int send_msg( Display *dpy, int i, + const void *msg, size_t sz ) +{ + int cnt = dpy->fd[i].writebuf_count; + if (MINIGLX_BUF_SIZE - cnt < sz) { + fprintf(stderr, "client %d: writebuf overflow\n", i); + return False; + } + + memcpy( dpy->fd[i].writebuf + cnt, msg, sz ); cnt += sz; + dpy->fd[i].writebuf_count = cnt; + return True; +} + +/** + * \brief Send a message to a socket connection. + * + * \param dpy the display handle. + * \param i the index in dpy->fd of the socket connection. + * \param msg the message to send. + * + * \internal + * Use send_msg() to send a one-byte message to a socket. + */ +int send_char_msg( Display *dpy, int i, char msg ) +{ + return send_msg( dpy, i, &msg, sizeof(char)); +} + + +/** + * \brief Block and receive a message from a socket connection. + * + * \param dpy the display handle. + * \param connection the index in dpy->fd of the socket connection. + * \param msg storage for the received message. + * \param msg_size the number of bytes to read. + * + * \internal + * Block and read from the connection's file descriptor + * until msg_size bytes have been received. + * + * Only called from welcome_message_part(). + */ +int blocking_read( Display *dpy, int connection, + char *msg, size_t msg_size ) +{ + int i, r; + + for (i = 0 ; i < msg_size ; i += r) { + r = read(dpy->fd[connection].fd, msg + i, msg_size - i); + if (r < 1) { + fprintf(stderr, "blocking_read: %d %s\n", r, strerror(errno)); + shut_fd(dpy,connection); + return False; + } + } + + return True; +} + +/** + * \brief Send/receive a part of the welcome message. + * + * \param dpy the display handle. + * \param i the index in dpy->fd of the socket connection. + * \param msg storage for the sent/received message. + * \param sz the number of bytes to write/read. + * + * \return True on success, or False on failure. + * + * This function is called by welcome_message_part(), to either send or receive + * (via blocking_read()) part of the welcome message, according to whether + * Display::IsClient is set. + * + * Each part of the welcome message on the wire consists of a count and then the + * actual message data with that number of bytes. + */ +static int welcome_message_part( Display *dpy, int i, void **msg, int sz ) +{ + if (dpy->IsClient) { + int sz; + if (!blocking_read( dpy, i, (char *)&sz, sizeof(sz))) return False; + if (!*msg) *msg = malloc(sz); + if (!*msg) return False; + if (!blocking_read( dpy, i, *msg, sz )) return False; + return sz; + } + else { + if (!send_msg( dpy, i, &sz, sizeof(sz))) return False; + if (!send_msg( dpy, i, *msg, sz )) return False; + } + + return True; +} + +/** + * \brief Send/receive the welcome message. + * + * \param dpy the display handle. + * \param i the index in dpy->fd of the socket connection. + * + * \return True on success, or False on failure. + * + * Using welcome_message_part(), sends/receives the client ID, the client + * configuration details in DRIDriverContext::shared, and the driver private + * message in DRIDriverContext::driverClientMsg. + */ +static int welcome_message( Display *dpy, int i ) +{ + void *tmp = &dpy->driverContext.shared; + int *clientid = dpy->IsClient ? &dpy->clientID : &i; + int size; + if (!welcome_message_part( dpy, i, (void **)&clientid, sizeof(*clientid))) + return False; + + if (!welcome_message_part( dpy, i, &tmp, sizeof(dpy->driverContext.shared))) + return False; + + size=welcome_message_part( dpy, i, + (void **)&dpy->driverContext.driverClientMsg, + dpy->driverContext.driverClientMsgSize ); + if (!size) + return False; + if (dpy->IsClient) { + dpy->driverContext.driverClientMsgSize = size; + } + return True; +} + + +/** + * \brief Handle a new client connection. + * + * \param dpy the display handle. + * + * \return True on success or False on failure. + * + * Accepts the connection, sets it in non-blocking operation, and finds a free + * slot in Display::fd for it. + */ +static int handle_new_client( Display *dpy ) +{ + struct sockaddr_un client_address; + unsigned int l = sizeof(client_address); + int r, i; + + r = accept(dpy->fd[0].fd, (struct sockaddr *) &client_address, &l); + if (r < 0) { + perror ("accept()"); + shut_fd(dpy,0); + return False; + } + + if (fcntl(r, F_SETFL, O_NONBLOCK) != 0) { + perror("fcntl"); + close(r); + return False; + } + + + /* Some rough & ready adaption of the XEvent semantics. + */ + for (i = 1 ; i < dpy->nrFds ; i++) { + if (dpy->fd[i].fd < 0) { + XEvent *er = queue_event(dpy); + if (!er) { + close(r); + return False; + } + + dpy->fd[i].fd = r; + er->xcreatewindow.type = CreateNotify; + er->xcreatewindow.serial = 0; + er->xcreatewindow.send_event = 0; + er->xcreatewindow.display = dpy; + er->xcreatewindow.window = (Window)i; /* fd slot == window, now? */ + + /* Send the driver client message - this is expected as the + * first message on a new connection. The recpient already + * knows the size of the message. + */ + welcome_message( dpy, i ); + return True; + } + } + + + fprintf(stderr, "[miniglx] %s: Max nr clients exceeded\n", __FUNCTION__); + close(r); + return False; +} + +/** + * This routine "puffs out" the very basic communications between + * client and server to full-sized X Events that can be handled by the + * application. + * + * \param dpy the display handle. + * \param i the index in dpy->fd of the socket connection. + * + * \return True on success or False on failure. + * + * \internal + * Interprets the message (see msg) into a XEvent and advances the file FIFO + * buffer. + */ +static int +handle_fifo_read( Display *dpy, int i ) +{ + drm_magic_t magic; + int err; + + while (dpy->fd[i].readbuf_count) { + char id = dpy->fd[i].readbuf[0]; + XEvent *er; + int count = 1; + + if (dpy->IsClient) { + switch (id) { + /* The server has called XMapWindow on a client window */ + case _YouveGotFocus: + er = queue_event(dpy); + if (!er) return False; + er->xmap.type = MapNotify; + er->xmap.serial = 0; + er->xmap.send_event = False; + er->xmap.display = dpy; + er->xmap.event = dpy->TheWindow; + er->xmap.window = dpy->TheWindow; + er->xmap.override_redirect = False; + if (dpy->driver->notifyFocus) + dpy->driver->notifyFocus( 1 ); + break; + + /* The server has called XMapWindow on a client window */ + case _RepaintPlease: + er = queue_event(dpy); + if (!er) return False; + er->xexpose.type = Expose; + er->xexpose.serial = 0; + er->xexpose.send_event = False; + er->xexpose.display = dpy; + er->xexpose.window = dpy->TheWindow; + if (dpy->rotateMode) { + er->xexpose.x = dpy->TheWindow->y; + er->xexpose.y = dpy->TheWindow->x; + er->xexpose.width = dpy->TheWindow->h; + er->xexpose.height = dpy->TheWindow->w; + } + else { + er->xexpose.x = dpy->TheWindow->x; + er->xexpose.y = dpy->TheWindow->y; + er->xexpose.width = dpy->TheWindow->w; + er->xexpose.height = dpy->TheWindow->h; + } + er->xexpose.count = 0; + break; + + /* The server has called 'XUnmapWindow' on a client + * window. + */ + case _YouveLostFocus: + er = queue_event(dpy); + if (!er) return False; + er->xunmap.type = UnmapNotify; + er->xunmap.serial = 0; + er->xunmap.send_event = False; + er->xunmap.display = dpy; + er->xunmap.event = dpy->TheWindow; + er->xunmap.window = dpy->TheWindow; + er->xunmap.from_configure = False; + if (dpy->driver->notifyFocus) + dpy->driver->notifyFocus( 0 ); + break; + + case _Authorize: + dpy->authorized = True; + break; + + default: + fprintf(stderr, "Client received unhandled message type %d\n", id); + shut_fd(dpy, i); /* Actually shuts down the client */ + return False; + } + } + else { + switch (id) { + /* Lets the server know that the client is ready to render + * (having called 'XMapWindow' locally). + */ + case _CanIHaveFocus: + er = queue_event(dpy); + if (!er) return False; + er->xmaprequest.type = MapRequest; + er->xmaprequest.serial = 0; + er->xmaprequest.send_event = False; + er->xmaprequest.display = dpy; + er->xmaprequest.parent = 0; + er->xmaprequest.window = (Window)i; + break; + + /* Both _YouveLostFocus and _IDontWantFocus generate unmap + * events. The idea is that _YouveLostFocus lets the client + * know that it has had focus revoked by the server, whereas + * _IDontWantFocus lets the server know that the client has + * unmapped its own window. + */ + case _IDontWantFocus: + er = queue_event(dpy); + if (!er) return False; + er->xunmap.type = UnmapNotify; + er->xunmap.serial = 0; + er->xunmap.send_event = False; + er->xunmap.display = dpy; + er->xunmap.event = (Window)i; + er->xunmap.window = (Window)i; + er->xunmap.from_configure = False; + break; + + case _Authorize: + /* is full message here yet? */ + if (dpy->fd[i].readbuf_count < count + sizeof(magic)) { + count = 0; + break; + } + memcpy(&magic, dpy->fd[i].readbuf + count, sizeof(magic)); + fprintf(stderr, "Authorize - magic %d\n", magic); + + err = drmAuthMagic(dpy->driverContext.drmFD, magic); + count += sizeof(magic); + + send_char_msg( dpy, i, _Authorize ); + break; + + default: + fprintf(stderr, "Server received unhandled message type %d\n", id); + shut_fd(dpy, i); /* Generates DestroyNotify event */ + return False; + } + } + + dpy->fd[i].readbuf_count -= count; + + if (dpy->fd[i].readbuf_count) { + memmove(dpy->fd[i].readbuf, + dpy->fd[i].readbuf + count, + dpy->fd[i].readbuf_count); + } + } + + return True; +} + +/** + * Handle a VT signal + * + * \param dpy display handle. + * + * The VT switches is detected by comparing Display::haveVT and + * Display::hwActive. When loosing the VT the hardware lock is acquired, the + * hardware is shutdown via a call to DRIDriverRec::shutdownHardware(), and the + * VT released. When acquiring the VT back the hardware state is restored via a + * call to DRIDriverRec::restoreHardware() and the hardware lock released. + */ +static void __driHandleVtSignals( Display *dpy ) +{ + dpy->vtSignalFlag = 0; + + fprintf(stderr, "%s: haveVT %d hwActive %d\n", __FUNCTION__, + dpy->haveVT, dpy->hwActive); + + if (!dpy->haveVT && dpy->hwActive) { + /* Need to get lock and shutdown hardware */ + DRM_LIGHT_LOCK( dpy->driverContext.drmFD, + dpy->driverContext.pSAREA, + dpy->driverContext.serverContext ); + dpy->driver->shutdownHardware( &dpy->driverContext ); + + /* Can now give up control of the VT */ + ioctl( dpy->ConsoleFD, VT_RELDISP, 1 ); + dpy->hwActive = 0; + } + else if (dpy->haveVT && !dpy->hwActive) { + /* Get VT (wait??) */ + ioctl( dpy->ConsoleFD, VT_RELDISP, VT_ACTIVATE ); + + /* restore HW state, release lock */ + dpy->driver->restoreHardware( &dpy->driverContext ); + DRM_UNLOCK( dpy->driverContext.drmFD, + dpy->driverContext.pSAREA, + dpy->driverContext.serverContext ); + dpy->hwActive = 1; + } +} + + +#undef max +#define max(x,y) ((x) > (y) ? (x) : (y)) + +/** + * Logic for the select() call. + * + * \param dpy display handle. + * \param n highest fd in any set plus one. + * \param rfds fd set to be watched for reading, or NULL to create one. + * \param wfds fd set to be watched for writing, or NULL to create one. + * \param xfds fd set to be watched for exceptions or error, or NULL to create one. + * \param tv timeout value, or NULL for no timeout. + * + * \return number of file descriptors contained in the sets, or a negative number on failure. + * + * \note + * This all looks pretty complex, but is necessary especially on the + * server side to prevent a poorly-behaved client from causing the + * server to block in a read or write and hence not service the other + * clients. + * + * \sa + * See select_tut in the Linux manual pages for more discussion. + * + * \internal + * Creates and initializes the file descriptor sets by inspecting Display::fd + * if these aren't passed in the function call. Calls select() and fulfill the + * demands by trying to fill MiniGLXConnection::readbuf and draining + * MiniGLXConnection::writebuf. + * The server fd[0] is handled specially for new connections, by calling + * handle_new_client(). + * + */ +int +__miniglx_Select( Display *dpy, int n, fd_set *rfds, fd_set *wfds, fd_set *xfds, + struct timeval *tv ) +{ + int i; + int retval; + fd_set my_rfds, my_wfds; + struct timeval my_tv; + + if (!rfds) { + rfds = &my_rfds; + FD_ZERO(rfds); + } + + if (!wfds) { + wfds = &my_wfds; + FD_ZERO(wfds); + } + + /* Don't block if there are events queued. Review this if the + * flush in XMapWindow is changed to blocking. (Test case: + * miniglxtest). + */ + if (dpy->eventqueue.head != dpy->eventqueue.tail) { + my_tv.tv_sec = my_tv.tv_usec = 0; + tv = &my_tv; + } + + for (i = 0 ; i < dpy->nrFds; i++) { + if (dpy->fd[i].fd < 0) + continue; + + if (dpy->fd[i].writebuf_count) + FD_SET(dpy->fd[i].fd, wfds); + + if (dpy->fd[i].readbuf_count < MINIGLX_BUF_SIZE) + FD_SET(dpy->fd[i].fd, rfds); + + n = max(n, dpy->fd[i].fd + 1); + } + + if (dpy->vtSignalFlag) + __driHandleVtSignals( dpy ); + + retval = select( n, rfds, wfds, xfds, tv ); + + if (dpy->vtSignalFlag) { + int tmp = errno; + __driHandleVtSignals( dpy ); + errno = tmp; + } + + if (retval < 0) { + FD_ZERO(rfds); + FD_ZERO(wfds); + return retval; + } + + /* Handle server fd[0] specially on the server - accept new client + * connections. + */ + if (!dpy->IsClient) { + if (FD_ISSET(dpy->fd[0].fd, rfds)) { + FD_CLR(dpy->fd[0].fd, rfds); + handle_new_client( dpy ); + } + } + + /* Otherwise, try and fill readbuffer and drain writebuffer: + */ + for (i = 0 ; i < dpy->nrFds ; i++) { + if (dpy->fd[i].fd < 0) + continue; + + /* If there aren't any event slots left, don't examine + * any more file events. This will prevent lost events. + */ + if (dpy->eventqueue.head == + ((dpy->eventqueue.tail + 1) & MINIGLX_EVENT_QUEUE_MASK)) { + fprintf(stderr, "leaving event loop as event queue is full\n"); + return retval; + } + + if (FD_ISSET(dpy->fd[i].fd, wfds)) { + int r = write(dpy->fd[i].fd, + dpy->fd[i].writebuf, + dpy->fd[i].writebuf_count); + + if (r < 1) + shut_fd(dpy,i); + else { + dpy->fd[i].writebuf_count -= r; + if (dpy->fd[i].writebuf_count) { + memmove(dpy->fd[i].writebuf, + dpy->fd[i].writebuf + r, + dpy->fd[i].writebuf_count); + } + } + } + + if (FD_ISSET(dpy->fd[i].fd, rfds)) { + int r = read(dpy->fd[i].fd, + dpy->fd[i].readbuf + dpy->fd[i].readbuf_count, + MINIGLX_BUF_SIZE - dpy->fd[i].readbuf_count); + + if (r < 1) + shut_fd(dpy,i); + else { + dpy->fd[i].readbuf_count += r; + + handle_fifo_read( dpy, i ); + } + } + } + + return retval; +} + +/** + * \brief Handle socket events. + * + * \param dpy the display handle. + * \param nonblock whether to return immediately or wait for an event. + * + * \return True on success, False on failure. Aborts on critical error. + * + * \internal + * This function is the select() main loop. + */ +int handle_fd_events( Display *dpy, int nonblock ) +{ + while (1) { + struct timeval tv = {0, 0}; + int r = __miniglx_Select( dpy, 0, 0, 0, 0, nonblock ? &tv : 0 ); + if (r >= 0) + return True; + if (errno == EINTR || errno == EAGAIN) + continue; + perror ("select()"); + exit (1); + } +} + +/** + * Initializes the connections. + * + * \param dpy the display handle. + * + * \return True on success or False on failure. + * + * Allocates and initializes the Display::fd array and create a Unix socket on + * the first entry. For a server binds the socket to a filename and listen for + * connections. For a client connects to the server and waits for a welcome + * message. Sets the socket in nonblocking mode. + */ +int __miniglx_open_connections( Display *dpy ) +{ + struct sockaddr_un sa; + int i; + + dpy->nrFds = dpy->IsClient ? 1 : MINIGLX_MAX_SERVER_FDS; + dpy->fd = calloc(1, dpy->nrFds * sizeof(struct MiniGLXConnection)); + if (!dpy->fd) + return False; + + for (i = 0 ; i < dpy->nrFds ; i++) + dpy->fd[i].fd = -1; + + if (!dpy->IsClient) { + if (unlink(MINIGLX_FIFO_NAME) != 0 && errno != ENOENT) { + perror("unlink " MINIGLX_FIFO_NAME); + return False; + } + + } + + /* Create a Unix socket -- Note this is *not* a network connection! + */ + dpy->fd[0].fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (dpy->fd[0].fd < 0) { + perror("socket " MINIGLX_FIFO_NAME); + return False; + } + + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, MINIGLX_FIFO_NAME); + + if (dpy->IsClient) { + /* Connect to server + */ + if (connect(dpy->fd[0].fd, (struct sockaddr *)&sa, sizeof(sa)) != 0) { + perror("connect"); + shut_fd(dpy,0); + return False; + } + + /* Wait for configuration messages from the server. + */ + welcome_message( dpy, 0 ); + } + else { + mode_t tmp = umask( 0000 ); /* open to everybody ? */ + + /* Bind socket to our filename + */ + if (bind(dpy->fd[0].fd, (struct sockaddr *)&sa, sizeof(sa)) != 0) { + perror("bind"); + shut_fd(dpy,0); + return False; + } + + umask( tmp ); + + /* Listen for connections + */ + if (listen(dpy->fd[0].fd, 5) != 0) { + perror("listen"); + shut_fd(dpy,0); + return False; + } + } + + if (fcntl(dpy->fd[0].fd, F_SETFL, O_NONBLOCK) != 0) { + perror("fcntl"); + shut_fd(dpy,0); + return False; + } + + + return True; +} + + +/** + * Frees the connections initialized by __miniglx_open_connections(). + * + * \param dpy the display handle. + */ +void __miniglx_close_connections( Display *dpy ) +{ + int i; + + for (i = 0 ; i < dpy->nrFds ; i++) { + if (dpy->fd[i].fd >= 0) { + shutdown (dpy->fd[i].fd, SHUT_RDWR); + close (dpy->fd[i].fd); + } + } + + dpy->nrFds = 0; + free(dpy->fd); +} + + +/** + * Set a drawable flag. + * + * \param dpy the display handle. + * \param w drawable (window). + * \param flag flag. + * + * Sets the specified drawable flag in the SAREA and increment its stamp while + * holding the light hardware lock. + */ +static void set_drawable_flag( Display *dpy, int w, int flag ) +{ + if (dpy->driverContext.pSAREA) { + if (dpy->hwActive) + DRM_LIGHT_LOCK( dpy->driverContext.drmFD, + dpy->driverContext.pSAREA, + dpy->driverContext.serverContext ); + + dpy->driverContext.pSAREA->drawableTable[w].stamp++; + dpy->driverContext.pSAREA->drawableTable[w].flags = flag; + + if (dpy->hwActive) + DRM_UNLOCK( dpy->driverContext.drmFD, + dpy->driverContext.pSAREA, + dpy->driverContext.serverContext ); + } +} + + + +/** + * \brief Map Window. + * + * \param dpy the display handle as returned by XOpenDisplay(). + * \param w the window handle. + * + * If called by a client, sends a request for focus to the server. If + * called by the server, will generate a MapNotify and Expose event at + * the client. + * + */ +void +XMapWindow( Display *dpy, Window w ) +{ + if (dpy->IsClient) + send_char_msg( dpy, 0, _CanIHaveFocus ); + else { + set_drawable_flag( dpy, (int)w, 1 ); + send_char_msg( dpy, (int)w, _YouveGotFocus ); + send_char_msg( dpy, (int)w, _RepaintPlease ); + dpy->TheWindow = w; + } + handle_fd_events( dpy, 0 ); /* flush write queue */ +} + +/** + * \brief Unmap Window. + * + * \param dpy the display handle as returned by XOpenDisplay(). + * \param w the window handle. + * + * Called from the client: Lets the server know that the window won't + * be updated anymore. + * + * Called from the server: Tells the specified client that it no longer + * holds the focus. + */ +void +XUnmapWindow( Display *dpy, Window w ) +{ + if (dpy->IsClient) { + send_char_msg( dpy, 0, _IDontWantFocus ); + } + else { + dpy->TheWindow = 0; + set_drawable_flag( dpy, (int)w, 0 ); + send_char_msg( dpy, (int)w, _YouveLostFocus ); + } + handle_fd_events( dpy, 0 ); /* flush write queue */ +} + + +/** + * \brief Block and wait for next X event. + * + * \param dpy the display handle as returned by XOpenDisplay(). + * \param event_return a pointer to an XEvent structure for the returned data. + * + * Wait until there is a new XEvent pending. + */ +int XNextEvent(Display *dpy, XEvent *event_return) +{ + for (;;) { + if ( dpy->eventqueue.head != dpy->eventqueue.tail ) + return dequeue_event( dpy, event_return ); + + handle_fd_events( dpy, 0 ); + } +} + +/** + * \brief Non-blocking check for next X event. + * + * \param dpy the display handle as returned by XOpenDisplay(). + * \param event_mask ignored. + * \param event_return a pointer to an XEvent structure for the returned data. + * + * Check if there is a new XEvent pending. Note that event_mask is + * ignored and any pending event will be returned. + */ +Bool XCheckMaskEvent(Display *dpy, long event_mask, XEvent *event_return) +{ + if ( dpy->eventqueue.head != dpy->eventqueue.tail ) + return dequeue_event( dpy, event_return ); + + handle_fd_events( dpy, 1 ); + + return dequeue_event( dpy, event_return ); +} |