1/*
2 * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
3 * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice including the dates of first publication and
13 * either this permission notice or a reference to
14 * http://oss.sgi.com/projects/FreeB/
15 * shall be included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
22 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * Except as contained in this notice, the name of Silicon Graphics, Inc.
26 * shall not be used in advertising or otherwise to promote the sale, use or
27 * other dealings in this Software without prior written authorization from
28 * Silicon Graphics, Inc.
29 */
30
31#ifdef HAVE_DIX_CONFIG_H
32#include <dix-config.h>
33#endif
34
35#include <string.h>
36#include <stdio.h>
37#include <stdlib.h>
38
39#include "glxserver.h"
40#include "glxutil.h"
41#include "glxext.h"
42#include "indirect_dispatch.h"
43#include "unpack.h"
44#include "glapitable.h"
45#include "glapi.h"
46#include "glthread.h"
47#include "dispatch.h"
48
49int __glXDisp_FeedbackBuffer(__GLXclientState *cl, GLbyte *pc)
50{
51    ClientPtr client = cl->client;
52    GLsizei size;
53    GLenum type;
54    __GLXcontext *cx;
55    int error;
56
57    REQUEST_FIXED_SIZE(xGLXSingleReq, 8);
58
59    cx = __glXForceCurrent(cl, __GLX_GET_SINGLE_CONTEXT_TAG(pc), &error);
60    if (!cx) {
61	return error;
62    }
63
64    pc += __GLX_SINGLE_HDR_SIZE;
65    size = *(GLsizei *)(pc+0);
66    type = *(GLenum *)(pc+4);
67    if (cx->feedbackBufSize < size) {
68	cx->feedbackBuf = (GLfloat *) realloc(cx->feedbackBuf,
69						   (size_t)size
70						   * __GLX_SIZE_FLOAT32);
71	if (!cx->feedbackBuf) {
72	    cl->client->errorValue = size;
73	    return BadAlloc;
74	}
75	cx->feedbackBufSize = size;
76    }
77    CALL_FeedbackBuffer( GET_DISPATCH(), (size, type, cx->feedbackBuf) );
78    __GLX_NOTE_UNFLUSHED_CMDS(cx);
79    return Success;
80}
81
82int __glXDisp_SelectBuffer(__GLXclientState *cl, GLbyte *pc)
83{
84    ClientPtr client = cl->client;
85    __GLXcontext *cx;
86    GLsizei size;
87    int error;
88
89    REQUEST_FIXED_SIZE(xGLXSingleReq, 4);
90
91    cx = __glXForceCurrent(cl, __GLX_GET_SINGLE_CONTEXT_TAG(pc), &error);
92    if (!cx) {
93	return error;
94    }
95
96    pc += __GLX_SINGLE_HDR_SIZE;
97    size = *(GLsizei *)(pc+0);
98    if (cx->selectBufSize < size) {
99	cx->selectBuf = (GLuint *) realloc(cx->selectBuf,
100						(size_t) size
101						* __GLX_SIZE_CARD32);
102	if (!cx->selectBuf) {
103	    cl->client->errorValue = size;
104	    return BadAlloc;
105	}
106	cx->selectBufSize = size;
107    }
108    CALL_SelectBuffer( GET_DISPATCH(), (size, cx->selectBuf) );
109    __GLX_NOTE_UNFLUSHED_CMDS(cx);
110    return Success;
111}
112
113int __glXDisp_RenderMode(__GLXclientState *cl, GLbyte *pc)
114{
115    ClientPtr client = cl->client;
116    xGLXRenderModeReply reply;
117    __GLXcontext *cx;
118    GLint nitems=0, retBytes=0, retval, newModeCheck;
119    GLubyte *retBuffer = NULL;
120    GLenum newMode;
121    int error;
122
123    REQUEST_FIXED_SIZE(xGLXSingleReq, 4);
124
125    cx = __glXForceCurrent(cl, __GLX_GET_SINGLE_CONTEXT_TAG(pc), &error);
126    if (!cx) {
127	return error;
128    }
129
130    pc += __GLX_SINGLE_HDR_SIZE;
131    newMode = *(GLenum*) pc;
132    retval = CALL_RenderMode( GET_DISPATCH(), (newMode) );
133
134    /* Check that render mode worked */
135    CALL_GetIntegerv( GET_DISPATCH(), (GL_RENDER_MODE, &newModeCheck) );
136    if (newModeCheck != newMode) {
137	/* Render mode change failed.  Bail */
138	newMode = newModeCheck;
139	goto noChangeAllowed;
140    }
141
142    /*
143    ** Render mode might have still failed if we get here.  But in this
144    ** case we can't really tell, nor does it matter.  If it did fail, it
145    ** will return 0, and thus we won't send any data across the wire.
146    */
147
148    switch (cx->renderMode) {
149      case GL_RENDER:
150	cx->renderMode = newMode;
151	break;
152      case GL_FEEDBACK:
153	if (retval < 0) {
154	    /* Overflow happened. Copy the entire buffer */
155	    nitems = cx->feedbackBufSize;
156	} else {
157	    nitems = retval;
158	}
159	retBytes = nitems * __GLX_SIZE_FLOAT32;
160	retBuffer = (GLubyte*) cx->feedbackBuf;
161	cx->renderMode = newMode;
162	break;
163      case GL_SELECT:
164	if (retval < 0) {
165	    /* Overflow happened.  Copy the entire buffer */
166	    nitems = cx->selectBufSize;
167	} else {
168	    GLuint *bp = cx->selectBuf;
169	    GLint i;
170
171	    /*
172	    ** Figure out how many bytes of data need to be sent.  Parse
173	    ** the selection buffer to determine this fact as the
174	    ** return value is the number of hits, not the number of
175	    ** items in the buffer.
176	    */
177	    nitems = 0;
178	    i = retval;
179	    while (--i >= 0) {
180		GLuint n;
181
182		/* Parse select data for this hit */
183		n = *bp;
184		bp += 3 + n;
185	    }
186	    nitems = bp - cx->selectBuf;
187	}
188	retBytes = nitems * __GLX_SIZE_CARD32;
189	retBuffer = (GLubyte*) cx->selectBuf;
190	cx->renderMode = newMode;
191	break;
192    }
193
194    /*
195    ** First reply is the number of elements returned in the feedback or
196    ** selection array, as per the API for glRenderMode itself.
197    */
198  noChangeAllowed:;
199    reply.length = nitems;
200    reply.type = X_Reply;
201    reply.sequenceNumber = client->sequence;
202    reply.retval = retval;
203    reply.size = nitems;
204    reply.newMode = newMode;
205    WriteToClient(client, sz_xGLXRenderModeReply, (char *)&reply);
206    if (retBytes) {
207	WriteToClient(client, retBytes, (char *)retBuffer);
208    }
209    return Success;
210}
211
212int __glXDisp_Flush(__GLXclientState *cl, GLbyte *pc)
213{
214	ClientPtr client = cl->client;
215	__GLXcontext *cx;
216	int error;
217
218	REQUEST_SIZE_MATCH(xGLXSingleReq);
219
220	cx = __glXForceCurrent(cl, __GLX_GET_SINGLE_CONTEXT_TAG(pc), &error);
221	if (!cx) {
222		return error;
223	}
224
225	CALL_Flush( GET_DISPATCH(), () );
226	__GLX_NOTE_FLUSHED_CMDS(cx);
227	return Success;
228}
229
230int __glXDisp_Finish(__GLXclientState *cl, GLbyte *pc)
231{
232    ClientPtr client = cl->client;
233    __GLXcontext *cx;
234    int error;
235
236    REQUEST_SIZE_MATCH(xGLXSingleReq);
237
238    cx = __glXForceCurrent(cl, __GLX_GET_SINGLE_CONTEXT_TAG(pc), &error);
239    if (!cx) {
240	return error;
241    }
242
243    /* Do a local glFinish */
244    CALL_Finish( GET_DISPATCH(), () );
245    __GLX_NOTE_FLUSHED_CMDS(cx);
246
247    /* Send empty reply packet to indicate finish is finished */
248    client = cl->client;
249    __GLX_BEGIN_REPLY(0);
250    __GLX_SEND_HEADER();
251    return Success;
252}
253
254#define SEPARATOR " "
255
256char *__glXcombine_strings(const char *cext_string, const char *sext_string)
257{
258   size_t clen, slen;
259   char *combo_string, *token, *s1;
260   const char *s2, *end;
261
262   /* safeguard to prevent potentially fatal errors in the string functions */
263   if (!cext_string)
264      cext_string = "";
265   if (!sext_string)
266      sext_string = "";
267
268   /*
269   ** String can't be longer than min(cstring, sstring)
270   ** pull tokens out of shortest string
271   ** include space in combo_string for final separator and null terminator
272   */
273   clen = strlen(cext_string);
274   slen = strlen(sext_string);
275   if (clen > slen) {
276	combo_string = (char *) malloc(slen + 2);
277	s1 = (char *) malloc(slen + 2);
278	if (s1) strcpy(s1, sext_string);
279	s2 = cext_string;
280   } else {
281	combo_string = (char *) malloc(clen + 2);
282	s1 = (char *) malloc(clen + 2);
283	if (s1) strcpy(s1, cext_string);
284	s2 = sext_string;
285   }
286   if (!combo_string || !s1) {
287	free(combo_string);
288	free(s1);
289	return NULL;
290   }
291   combo_string[0] = '\0';
292
293   /* Get first extension token */
294   token = strtok( s1, SEPARATOR);
295   while ( token != NULL ) {
296
297	/*
298	** if token in second string then save it
299	** beware of extension names which are prefixes of other extension names
300	*/
301	const char *p = s2;
302	end = p + strlen(p);
303	while (p < end) {
304	    size_t n = strcspn(p, SEPARATOR);
305	    if ((strlen(token) == n) && (strncmp(token, p, n) == 0)) {
306		combo_string = strcat(combo_string, token);
307		combo_string = strcat(combo_string, SEPARATOR);
308	    }
309	    p += (n + 1);
310	}
311
312	/* Get next extension token */
313	token = strtok( NULL, SEPARATOR);
314   }
315   free(s1);
316   return combo_string;
317}
318
319int DoGetString(__GLXclientState *cl, GLbyte *pc, GLboolean need_swap)
320{
321    ClientPtr client = cl->client;
322    __GLXcontext *cx;
323    GLenum name;
324    const char *string;
325    __GLX_DECLARE_SWAP_VARIABLES;
326    int error;
327    char *buf = NULL, *buf1 = NULL;
328    GLint length = 0;
329
330    REQUEST_FIXED_SIZE(xGLXSingleReq, 4);
331
332    /* If the client has the opposite byte order, swap the contextTag and
333     * the name.
334     */
335    if ( need_swap ) {
336	__GLX_SWAP_INT(pc + 4);
337	__GLX_SWAP_INT(pc + __GLX_SINGLE_HDR_SIZE);
338    }
339
340    cx = __glXForceCurrent(cl, __GLX_GET_SINGLE_CONTEXT_TAG(pc), &error);
341    if (!cx) {
342	return error;
343    }
344
345    pc += __GLX_SINGLE_HDR_SIZE;
346    name = *(GLenum *)(pc + 0);
347    string = (const char *) CALL_GetString( GET_DISPATCH(), (name) );
348
349    if (string == NULL)
350      string = "";
351
352    /*
353    ** Restrict extensions to those that are supported by both the
354    ** implementation and the connection.  That is, return the
355    ** intersection of client, server, and core extension strings.
356    */
357    if (name == GL_EXTENSIONS) {
358	buf1 = __glXcombine_strings(string,
359				      cl->GLClientextensions);
360	buf = __glXcombine_strings(buf1,
361				      cx->pGlxScreen->GLextensions);
362	free(buf1);
363	string = buf;
364    }
365    else if ( name == GL_VERSION ) {
366	if ( atof( string ) > atof( GLServerVersion ) ) {
367	    buf = malloc( strlen( string ) + strlen( GLServerVersion ) + 4 );
368	    if ( buf == NULL ) {
369		string = GLServerVersion;
370	    }
371	    else {
372		sprintf( buf, "%s (%s)", GLServerVersion, string );
373		string = buf;
374	    }
375	}
376    }
377    if (string) {
378	length = strlen((const char *) string) + 1;
379    }
380
381    __GLX_BEGIN_REPLY(length);
382    __GLX_PUT_SIZE(length);
383
384    if ( need_swap ) {
385	__GLX_SWAP_REPLY_SIZE();
386	__GLX_SWAP_REPLY_HEADER();
387    }
388
389    __GLX_SEND_HEADER();
390    WriteToClient(client, length, (char *) string);
391    free(buf);
392
393    return Success;
394}
395
396int __glXDisp_GetString(__GLXclientState *cl, GLbyte *pc)
397{
398    return DoGetString(cl, pc, GL_FALSE);
399}
400