Commit | Line | Data |
---|---|---|
a09e091a JB |
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 "glxserver.h" | |
37 | #include <windowstr.h> | |
38 | #include <propertyst.h> | |
39 | #include <registry.h> | |
40 | #include "privates.h" | |
41 | #include <os.h> | |
42 | #include "extinit.h" | |
43 | #include "glx_extinit.h" | |
44 | #include "unpack.h" | |
45 | #include "glxutil.h" | |
46 | #include "glxext.h" | |
47 | #include "indirect_table.h" | |
48 | #include "indirect_util.h" | |
49 | ||
50 | /* | |
51 | ** The last context used by the server. It is the context that is current | |
52 | ** from the server's perspective. | |
53 | */ | |
54 | __GLXcontext *__glXLastContext; | |
55 | ||
56 | /* | |
57 | ** X resources. | |
58 | */ | |
59 | RESTYPE __glXContextRes; | |
60 | RESTYPE __glXDrawableRes; | |
61 | ||
62 | /* | |
63 | ** Reply for most singles. | |
64 | */ | |
65 | xGLXSingleReply __glXReply; | |
66 | ||
67 | static DevPrivateKeyRec glxClientPrivateKeyRec; | |
68 | ||
69 | #define glxClientPrivateKey (&glxClientPrivateKeyRec) | |
70 | ||
71 | /* | |
72 | ** Forward declarations. | |
73 | */ | |
74 | static int __glXDispatch(ClientPtr); | |
75 | ||
76 | /* | |
77 | ** Called when the extension is reset. | |
78 | */ | |
79 | static void | |
80 | ResetExtension(ExtensionEntry * extEntry) | |
81 | { | |
82 | __glXFlushContextCache(); | |
83 | } | |
84 | ||
85 | /* | |
86 | ** Reset state used to keep track of large (multi-request) commands. | |
87 | */ | |
88 | void | |
89 | __glXResetLargeCommandStatus(__GLXclientState * cl) | |
90 | { | |
91 | cl->largeCmdBytesSoFar = 0; | |
92 | cl->largeCmdBytesTotal = 0; | |
93 | cl->largeCmdRequestsSoFar = 0; | |
94 | cl->largeCmdRequestsTotal = 0; | |
95 | } | |
96 | ||
97 | /* | |
98 | * This procedure is called when the client who created the context goes away | |
99 | * OR when glXDestroyContext is called. In either case, all we do is flag that | |
100 | * the ID is no longer valid, and (maybe) free the context. | |
101 | */ | |
102 | static int | |
103 | ContextGone(__GLXcontext * cx, XID id) | |
104 | { | |
105 | cx->idExists = GL_FALSE; | |
106 | if (!cx->currentClient) { | |
107 | __glXFreeContext(cx); | |
108 | } | |
109 | ||
110 | return True; | |
111 | } | |
112 | ||
113 | static __GLXcontext *glxPendingDestroyContexts; | |
114 | static __GLXcontext *glxAllContexts; | |
115 | static int glxServerLeaveCount; | |
116 | static int glxBlockClients; | |
117 | ||
118 | /* | |
119 | ** Destroy routine that gets called when a drawable is freed. A drawable | |
120 | ** contains the ancillary buffers needed for rendering. | |
121 | */ | |
122 | static Bool | |
123 | DrawableGone(__GLXdrawable * glxPriv, XID xid) | |
124 | { | |
125 | __GLXcontext *c, *next; | |
126 | ||
127 | if (glxPriv->type == GLX_DRAWABLE_WINDOW) { | |
128 | /* If this was created by glXCreateWindow, free the matching resource */ | |
129 | if (glxPriv->drawId != glxPriv->pDraw->id) { | |
130 | if (xid == glxPriv->drawId) | |
131 | FreeResourceByType(glxPriv->pDraw->id, __glXDrawableRes, TRUE); | |
132 | else | |
133 | FreeResourceByType(glxPriv->drawId, __glXDrawableRes, TRUE); | |
134 | } | |
135 | /* otherwise this window was implicitly created by MakeCurrent */ | |
136 | } | |
137 | ||
138 | for (c = glxAllContexts; c; c = next) { | |
139 | next = c->next; | |
140 | if (c->currentClient && | |
141 | (c->drawPriv == glxPriv || c->readPriv == glxPriv)) { | |
142 | /* just force a re-bind the next time through */ | |
143 | (*c->loseCurrent) (c); | |
144 | if (c == __glXLastContext) | |
145 | __glXFlushContextCache(); | |
146 | } | |
147 | if (c->drawPriv == glxPriv) | |
148 | c->drawPriv = NULL; | |
149 | if (c->readPriv == glxPriv) | |
150 | c->readPriv = NULL; | |
151 | } | |
152 | ||
153 | /* drop our reference to any backing pixmap */ | |
154 | if (glxPriv->type == GLX_DRAWABLE_PIXMAP) | |
155 | glxPriv->pDraw->pScreen->DestroyPixmap((PixmapPtr) glxPriv->pDraw); | |
156 | ||
157 | glxPriv->destroy(glxPriv); | |
158 | ||
159 | return True; | |
160 | } | |
161 | ||
162 | Bool | |
163 | __glXAddContext(__GLXcontext * cx) | |
164 | { | |
165 | /* Register this context as a resource. | |
166 | */ | |
167 | if (!AddResource(cx->id, __glXContextRes, (pointer)cx)) { | |
168 | return False; | |
169 | } | |
170 | ||
171 | cx->next = glxAllContexts; | |
172 | glxAllContexts = cx; | |
173 | return True; | |
174 | } | |
175 | ||
176 | static void | |
177 | __glXRemoveFromContextList(__GLXcontext * cx) | |
178 | { | |
179 | __GLXcontext *c, *prev; | |
180 | ||
181 | if (cx == glxAllContexts) | |
182 | glxAllContexts = cx->next; | |
183 | else { | |
184 | prev = glxAllContexts; | |
185 | for (c = glxAllContexts; c; c = c->next) { | |
186 | if (c == cx) | |
187 | prev->next = c->next; | |
188 | prev = c; | |
189 | } | |
190 | } | |
191 | } | |
192 | ||
193 | /* | |
194 | ** Free a context. | |
195 | */ | |
196 | GLboolean | |
197 | __glXFreeContext(__GLXcontext * cx) | |
198 | { | |
199 | if (cx->idExists || cx->currentClient) | |
200 | return GL_FALSE; | |
201 | ||
202 | __glXRemoveFromContextList(cx); | |
203 | ||
204 | free(cx->feedbackBuf); | |
205 | free(cx->selectBuf); | |
206 | if (cx == __glXLastContext) { | |
207 | __glXFlushContextCache(); | |
208 | } | |
209 | ||
210 | /* We can get here through both regular dispatching from | |
211 | * __glXDispatch() or as a callback from the resource manager. In | |
212 | * the latter case we need to lift the DRI lock manually. */ | |
213 | ||
214 | if (!glxBlockClients) { | |
215 | __glXleaveServer(GL_FALSE); | |
216 | cx->destroy(cx); | |
217 | __glXenterServer(GL_FALSE); | |
218 | } | |
219 | else { | |
220 | cx->next = glxPendingDestroyContexts; | |
221 | glxPendingDestroyContexts = cx; | |
222 | } | |
223 | ||
224 | return GL_TRUE; | |
225 | } | |
226 | ||
227 | /************************************************************************/ | |
228 | ||
229 | /* | |
230 | ** These routines can be used to check whether a particular GL command | |
231 | ** has caused an error. Specifically, we use them to check whether a | |
232 | ** given query has caused an error, in which case a zero-length data | |
233 | ** reply is sent to the client. | |
234 | */ | |
235 | ||
236 | static GLboolean errorOccured = GL_FALSE; | |
237 | ||
238 | /* | |
239 | ** The GL was will call this routine if an error occurs. | |
240 | */ | |
241 | void | |
242 | __glXErrorCallBack(GLenum code) | |
243 | { | |
244 | errorOccured = GL_TRUE; | |
245 | } | |
246 | ||
247 | /* | |
248 | ** Clear the error flag before calling the GL command. | |
249 | */ | |
250 | void | |
251 | __glXClearErrorOccured(void) | |
252 | { | |
253 | errorOccured = GL_FALSE; | |
254 | } | |
255 | ||
256 | /* | |
257 | ** Check if the GL command caused an error. | |
258 | */ | |
259 | GLboolean | |
260 | __glXErrorOccured(void) | |
261 | { | |
262 | return errorOccured; | |
263 | } | |
264 | ||
265 | static int __glXErrorBase; | |
266 | int __glXEventBase; | |
267 | ||
268 | int | |
269 | __glXError(int error) | |
270 | { | |
271 | return __glXErrorBase + error; | |
272 | } | |
273 | ||
274 | __GLXclientState * | |
275 | glxGetClient(ClientPtr pClient) | |
276 | { | |
277 | return dixLookupPrivate(&pClient->devPrivates, glxClientPrivateKey); | |
278 | } | |
279 | ||
280 | static void | |
281 | glxClientCallback(CallbackListPtr *list, pointer closure, pointer data) | |
282 | { | |
283 | NewClientInfoRec *clientinfo = (NewClientInfoRec *) data; | |
284 | ClientPtr pClient = clientinfo->client; | |
285 | __GLXclientState *cl = glxGetClient(pClient); | |
286 | __GLXcontext *c, *next; | |
287 | ||
288 | switch (pClient->clientState) { | |
289 | case ClientStateRunning: | |
290 | cl->client = pClient; | |
291 | break; | |
292 | ||
293 | case ClientStateGone: | |
294 | /* detach from all current contexts */ | |
295 | for (c = glxAllContexts; c; c = next) { | |
296 | next = c->next; | |
297 | if (c->currentClient == pClient) { | |
298 | c->loseCurrent(c); | |
299 | c->currentClient = NULL; | |
300 | __glXFreeContext(c); | |
301 | } | |
302 | } | |
303 | ||
304 | free(cl->returnBuf); | |
305 | free(cl->largeCmdBuf); | |
306 | free(cl->GLClientextensions); | |
307 | break; | |
308 | ||
309 | default: | |
310 | break; | |
311 | } | |
312 | } | |
313 | ||
314 | /************************************************************************/ | |
315 | ||
316 | static __GLXprovider *__glXProviderStack; | |
317 | ||
318 | void | |
319 | GlxPushProvider(__GLXprovider * provider) | |
320 | { | |
321 | provider->next = __glXProviderStack; | |
322 | __glXProviderStack = provider; | |
323 | } | |
324 | ||
325 | /* | |
326 | ** Initialize the GLX extension. | |
327 | */ | |
328 | void | |
329 | GlxExtensionInit(void) | |
330 | { | |
331 | ExtensionEntry *extEntry; | |
332 | ScreenPtr pScreen; | |
333 | int i; | |
334 | __GLXprovider *p, **stack; | |
335 | Bool glx_provided = False; | |
336 | ||
337 | if (serverGeneration == 1) { | |
338 | for (stack = &__glXProviderStack; *stack; stack = &(*stack)->next) | |
339 | ; | |
340 | *stack = &__glXDRISWRastProvider; | |
341 | } | |
342 | ||
343 | __glXContextRes = CreateNewResourceType((DeleteType) ContextGone, | |
344 | "GLXContext"); | |
345 | __glXDrawableRes = CreateNewResourceType((DeleteType) DrawableGone, | |
346 | "GLXDrawable"); | |
347 | if (!__glXContextRes || !__glXDrawableRes) | |
348 | return; | |
349 | ||
350 | if (!dixRegisterPrivateKey | |
351 | (&glxClientPrivateKeyRec, PRIVATE_CLIENT, sizeof(__GLXclientState))) | |
352 | return; | |
353 | if (!AddCallback(&ClientStateCallback, glxClientCallback, 0)) | |
354 | return; | |
355 | ||
356 | for (i = 0; i < screenInfo.numScreens; i++) { | |
357 | pScreen = screenInfo.screens[i]; | |
358 | ||
359 | for (p = __glXProviderStack; p != NULL; p = p->next) { | |
360 | __GLXscreen *glxScreen; | |
361 | ||
362 | glxScreen = p->screenProbe(pScreen); | |
363 | if (glxScreen != NULL) { | |
364 | if (glxScreen->GLXminor < glxMinorVersion) | |
365 | glxMinorVersion = glxScreen->GLXminor; | |
366 | LogMessage(X_INFO, | |
367 | "GLX: Initialized %s GL provider for screen %d\n", | |
368 | p->name, i); | |
369 | break; | |
370 | } | |
371 | ||
372 | } | |
373 | ||
374 | if (!p) | |
375 | LogMessage(X_INFO, | |
376 | "GLX: no usable GL providers found for screen %d\n", i); | |
377 | else | |
378 | glx_provided = True; | |
379 | } | |
380 | ||
381 | /* don't register extension if GL is not provided on any screen */ | |
382 | if (!glx_provided) | |
383 | return; | |
384 | ||
385 | /* | |
386 | ** Add extension to server extensions. | |
387 | */ | |
388 | extEntry = AddExtension(GLX_EXTENSION_NAME, __GLX_NUMBER_EVENTS, | |
389 | __GLX_NUMBER_ERRORS, __glXDispatch, | |
390 | __glXDispatch, ResetExtension, StandardMinorOpcode); | |
391 | if (!extEntry) { | |
392 | FatalError("__glXExtensionInit: AddExtensions failed\n"); | |
393 | return; | |
394 | } | |
395 | if (!AddExtensionAlias(GLX_EXTENSION_ALIAS, extEntry)) { | |
396 | ErrorF("__glXExtensionInit: AddExtensionAlias failed\n"); | |
397 | return; | |
398 | } | |
399 | ||
400 | __glXErrorBase = extEntry->errorBase; | |
401 | __glXEventBase = extEntry->eventBase; | |
402 | #if PRESENT | |
403 | __glXregisterPresentCompleteNotify(); | |
404 | #endif | |
405 | } | |
406 | ||
407 | /************************************************************************/ | |
408 | ||
409 | void | |
410 | __glXFlushContextCache(void) | |
411 | { | |
412 | __glXLastContext = 0; | |
413 | } | |
414 | ||
415 | /* | |
416 | ** Make a context the current one for the GL (in this implementation, there | |
417 | ** is only one instance of the GL, and we use it to serve all GL clients by | |
418 | ** switching it between different contexts). While we are at it, look up | |
419 | ** a context by its tag and return its (__GLXcontext *). | |
420 | */ | |
421 | __GLXcontext * | |
422 | __glXForceCurrent(__GLXclientState * cl, GLXContextTag tag, int *error) | |
423 | { | |
424 | __GLXcontext *cx; | |
425 | ||
426 | /* | |
427 | ** See if the context tag is legal; it is managed by the extension, | |
428 | ** so if it's invalid, we have an implementation error. | |
429 | */ | |
430 | cx = __glXLookupContextByTag(cl, tag); | |
431 | if (!cx) { | |
432 | cl->client->errorValue = tag; | |
433 | *error = __glXError(GLXBadContextTag); | |
434 | return 0; | |
435 | } | |
436 | ||
437 | if (!cx->isDirect) { | |
438 | if (cx->drawPriv == NULL) { | |
439 | /* | |
440 | ** The drawable has vanished. It must be a window, because only | |
441 | ** windows can be destroyed from under us; GLX pixmaps are | |
442 | ** refcounted and don't go away until no one is using them. | |
443 | */ | |
444 | *error = __glXError(GLXBadCurrentWindow); | |
445 | return 0; | |
446 | } | |
447 | } | |
448 | ||
449 | if (cx->wait && (*cx->wait) (cx, cl, error)) | |
450 | return NULL; | |
451 | ||
452 | if (cx == __glXLastContext) { | |
453 | /* No need to re-bind */ | |
454 | return cx; | |
455 | } | |
456 | ||
457 | /* Make this context the current one for the GL. */ | |
458 | if (!cx->isDirect) { | |
459 | if (!(*cx->makeCurrent) (cx)) { | |
460 | /* Bind failed, and set the error code. Bummer */ | |
461 | cl->client->errorValue = cx->id; | |
462 | *error = __glXError(GLXBadContextState); | |
463 | return 0; | |
464 | } | |
465 | } | |
466 | __glXLastContext = cx; | |
467 | return cx; | |
468 | } | |
469 | ||
470 | /************************************************************************/ | |
471 | ||
472 | void | |
473 | glxSuspendClients(void) | |
474 | { | |
475 | int i; | |
476 | ||
477 | for (i = 1; i < currentMaxClients; i++) { | |
478 | if (clients[i] && glxGetClient(clients[i])->inUse) | |
479 | IgnoreClient(clients[i]); | |
480 | } | |
481 | ||
482 | glxBlockClients = TRUE; | |
483 | } | |
484 | ||
485 | void | |
486 | glxResumeClients(void) | |
487 | { | |
488 | __GLXcontext *cx, *next; | |
489 | int i; | |
490 | ||
491 | glxBlockClients = FALSE; | |
492 | ||
493 | for (i = 1; i < currentMaxClients; i++) { | |
494 | if (clients[i] && glxGetClient(clients[i])->inUse) | |
495 | AttendClient(clients[i]); | |
496 | } | |
497 | ||
498 | __glXleaveServer(GL_FALSE); | |
499 | for (cx = glxPendingDestroyContexts; cx != NULL; cx = next) { | |
500 | next = cx->next; | |
501 | ||
502 | cx->destroy(cx); | |
503 | } | |
504 | glxPendingDestroyContexts = NULL; | |
505 | __glXenterServer(GL_FALSE); | |
506 | } | |
507 | ||
508 | static void | |
509 | __glXnopEnterServer(GLboolean rendering) | |
510 | { | |
511 | } | |
512 | ||
513 | static void | |
514 | __glXnopLeaveServer(GLboolean rendering) | |
515 | { | |
516 | } | |
517 | ||
518 | static void (*__glXenterServerFunc) (GLboolean) = __glXnopEnterServer; | |
519 | static void (*__glXleaveServerFunc) (GLboolean) = __glXnopLeaveServer; | |
520 | ||
521 | void | |
522 | __glXsetEnterLeaveServerFuncs(void (*enter) (GLboolean), | |
523 | void (*leave) (GLboolean)) | |
524 | { | |
525 | __glXenterServerFunc = enter; | |
526 | __glXleaveServerFunc = leave; | |
527 | } | |
528 | ||
529 | void | |
530 | __glXenterServer(GLboolean rendering) | |
531 | { | |
532 | glxServerLeaveCount--; | |
533 | ||
534 | if (glxServerLeaveCount == 0) | |
535 | (*__glXenterServerFunc) (rendering); | |
536 | } | |
537 | ||
538 | void | |
539 | __glXleaveServer(GLboolean rendering) | |
540 | { | |
541 | if (glxServerLeaveCount == 0) | |
542 | (*__glXleaveServerFunc) (rendering); | |
543 | ||
544 | glxServerLeaveCount++; | |
545 | } | |
546 | ||
547 | static glx_gpa_proc _get_proc_address; | |
548 | ||
549 | void | |
550 | __glXsetGetProcAddress(glx_gpa_proc get_proc_address) | |
551 | { | |
552 | _get_proc_address = get_proc_address; | |
553 | } | |
554 | ||
555 | void *__glGetProcAddress(const char *proc) | |
556 | { | |
557 | void *ret = _get_proc_address(proc); | |
558 | ||
559 | return ret ? ret : NoopDDA; | |
560 | } | |
561 | ||
562 | /* | |
563 | ** Top level dispatcher; all commands are executed from here down. | |
564 | */ | |
565 | static int | |
566 | __glXDispatch(ClientPtr client) | |
567 | { | |
568 | REQUEST(xGLXSingleReq); | |
569 | CARD8 opcode; | |
570 | __GLXdispatchSingleProcPtr proc; | |
571 | __GLXclientState *cl; | |
572 | int retval; | |
573 | ||
574 | opcode = stuff->glxCode; | |
575 | cl = glxGetClient(client); | |
576 | /* Mark it in use so we suspend it on VT switch. */ | |
577 | cl->inUse = TRUE; | |
578 | ||
579 | /* | |
580 | ** If we're expecting a glXRenderLarge request, this better be one. | |
581 | */ | |
582 | if ((cl->largeCmdRequestsSoFar != 0) && (opcode != X_GLXRenderLarge)) { | |
583 | client->errorValue = stuff->glxCode; | |
584 | return __glXError(GLXBadLargeRequest); | |
585 | } | |
586 | ||
587 | /* If we're currently blocking GLX clients, just put this guy to | |
588 | * sleep, reset the request and return. */ | |
589 | if (glxBlockClients) { | |
590 | ResetCurrentRequest(client); | |
591 | client->sequence--; | |
592 | IgnoreClient(client); | |
593 | return Success; | |
594 | } | |
595 | ||
596 | /* | |
597 | ** Use the opcode to index into the procedure table. | |
598 | */ | |
599 | proc = __glXGetProtocolDecodeFunction(&Single_dispatch_info, opcode, | |
600 | client->swapped); | |
601 | if (proc != NULL) { | |
602 | GLboolean rendering = opcode <= X_GLXRenderLarge; | |
603 | ||
604 | __glXleaveServer(rendering); | |
605 | ||
606 | retval = (*proc) (cl, (GLbyte *) stuff); | |
607 | ||
608 | __glXenterServer(rendering); | |
609 | } | |
610 | else { | |
611 | retval = BadRequest; | |
612 | } | |
613 | ||
614 | return retval; | |
615 | } |