vndcmds.c revision ed6184df
1/*
2 * Copyright (c) 2016, NVIDIA CORPORATION.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and/or associated documentation files (the
6 * "Materials"), to deal in the Materials without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Materials, and to
9 * permit persons to whom the Materials are furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * unaltered in all copies or substantial portions of the Materials.
14 * Any additions, deletions, or changes to the original source files
15 * must be clearly indicated in accompanying documentation.
16 *
17 * If only executable code is distributed, then the accompanying
18 * documentation must state that "this software is based in part on the
19 * work of the Khronos Group."
20 *
21 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
28 */
29
30#include <dix-config.h>
31
32#include "hashtable.h"
33#include "vndserver.h"
34#include "vndservervendor.h"
35
36/**
37 * The length of the dispatchFuncs array. Every opcode above this is a
38 * X_GLsop_* code, which all can use the same handler.
39 */
40#define OPCODE_ARRAY_LEN 100
41
42// This hashtable is used to keep track of the dispatch stubs for
43// GLXVendorPrivate and GLXVendorPrivateWithReply.
44typedef struct GlxVendorPrivDispatchRec {
45    CARD32 vendorCode;
46    GlxServerDispatchProc proc;
47    HashTable hh;
48} GlxVendorPrivDispatch;
49
50static GlxServerDispatchProc dispatchFuncs[OPCODE_ARRAY_LEN] = {};
51static HashTable vendorPrivHash = NULL;
52static HtGenericHashSetupRec vendorPrivSetup = {
53    .keySize = sizeof(CARD32)
54};
55
56static int DispatchBadRequest(ClientPtr client)
57{
58    return BadRequest;
59}
60
61static GlxVendorPrivDispatch *LookupVendorPrivDispatch(CARD32 vendorCode, Bool create)
62{
63    GlxVendorPrivDispatch *disp = NULL;
64
65    disp = ht_find(vendorPrivHash, &vendorCode);
66    if (disp == NULL && create) {
67        if ((disp = ht_add(vendorPrivHash, &vendorCode))) {
68            disp->vendorCode = vendorCode;
69            disp->proc = NULL;
70        }
71    }
72
73    return disp;
74}
75
76static GlxServerDispatchProc GetVendorDispatchFunc(CARD8 opcode, CARD32 vendorCode)
77{
78    GlxServerVendor *vendor;
79
80    xorg_list_for_each_entry(vendor, &GlxVendorList, entry) {
81        GlxServerDispatchProc proc = vendor->glxvc.getDispatchAddress(opcode, vendorCode);
82        if (proc != NULL) {
83            return proc;
84        }
85    }
86
87    return DispatchBadRequest;
88}
89
90static void SetReplyHeader(ClientPtr client, void *replyPtr)
91{
92    xGenericReply *rep = (xGenericReply *) replyPtr;
93    rep->type = X_Reply;
94    rep->sequenceNumber = client->sequence;
95    if (client->swapped) {
96	swaps(&rep->sequenceNumber);
97    }
98    rep->length = 0;
99}
100
101/* Include the trivial dispatch handlers */
102#include "vnd_dispatch_stubs.c"
103
104static int dispatch_GLXQueryVersion(ClientPtr client)
105{
106    xGLXQueryVersionReply reply;
107    REQUEST_SIZE_MATCH(xGLXQueryVersionReq);
108
109    SetReplyHeader(client, &reply);
110    reply.majorVersion = GlxCheckSwap(client, 1);
111    reply.minorVersion = GlxCheckSwap(client, 4);
112
113    WriteToClient(client, sz_xGLXQueryVersionReply, &reply);
114    return Success;
115}
116
117/* broken header workaround */
118#ifndef X_GLXSetClientInfo2ARB
119#define X_GLXSetClientInfo2ARB X_GLXSetConfigInfo2ARB
120#endif
121
122/**
123 * This function is used for X_GLXClientInfo, X_GLXSetClientInfoARB, and
124 * X_GLXSetClientInfo2ARB.
125 */
126static int dispatch_GLXClientInfo(ClientPtr client)
127{
128    GlxServerVendor *vendor;
129    void *requestCopy = NULL;
130    size_t requestSize = client->req_len * 4;
131
132    if (client->minorOp == X_GLXClientInfo) {
133        REQUEST_AT_LEAST_SIZE(xGLXClientInfoReq);
134    } else if (client->minorOp == X_GLXSetClientInfoARB) {
135        REQUEST_AT_LEAST_SIZE(xGLXSetClientInfoARBReq);
136    } else if (client->minorOp == X_GLXSetClientInfo2ARB) {
137        REQUEST_AT_LEAST_SIZE(xGLXSetClientInfo2ARBReq);
138    } else {
139        return BadImplementation;
140    }
141
142    // We'll forward this request to each vendor library. Since a vendor might
143    // modify the request data in place (e.g., for byte swapping), make a copy
144    // of the request first.
145    requestCopy = malloc(requestSize);
146    if (requestCopy == NULL) {
147        return BadAlloc;
148    }
149    memcpy(requestCopy, client->requestBuffer, requestSize);
150
151    xorg_list_for_each_entry(vendor, &GlxVendorList, entry) {
152        vendor->glxvc.handleRequest(client);
153        // Revert the request buffer back to our copy.
154        memcpy(client->requestBuffer, requestCopy, requestSize);
155    }
156    free(requestCopy);
157    return Success;
158}
159
160static int CommonLoseCurrent(ClientPtr client, GlxContextTagInfo *tagInfo)
161{
162    int ret;
163
164    ret = tagInfo->vendor->glxvc.makeCurrent(client,
165            tagInfo->tag, // No old context tag,
166            None, None, None, 0);
167
168    if (ret == Success) {
169        GlxFreeContextTag(tagInfo);
170    }
171    return ret;
172}
173
174static int CommonMakeNewCurrent(ClientPtr client,
175        GlxServerVendor *vendor,
176        GLXDrawable drawable,
177        GLXDrawable readdrawable,
178        GLXContextID context,
179        GLXContextTag *newContextTag)
180{
181    int ret = BadAlloc;
182    GlxContextTagInfo *tagInfo;
183
184    tagInfo = GlxAllocContextTag(client, vendor);
185
186    if (tagInfo) {
187        ret = vendor->glxvc.makeCurrent(client,
188                0, // No old context tag,
189                drawable, readdrawable, context,
190                tagInfo->tag);
191
192        if (ret == Success) {
193            tagInfo->drawable = drawable;
194            tagInfo->readdrawable = readdrawable;
195            tagInfo->context = context;
196            *newContextTag = tagInfo->tag;
197        } else {
198            GlxFreeContextTag(tagInfo);
199        }
200    }
201
202    return ret;
203}
204
205static int CommonMakeCurrent(ClientPtr client,
206        GLXContextTag oldContextTag,
207        GLXDrawable drawable,
208        GLXDrawable readdrawable,
209        GLXContextID context)
210{
211    xGLXMakeCurrentReply reply = {};
212    GlxContextTagInfo *oldTag = NULL;
213    GlxServerVendor *newVendor = NULL;
214
215    oldContextTag = GlxCheckSwap(client, oldContextTag);
216    drawable = GlxCheckSwap(client, drawable);
217    readdrawable = GlxCheckSwap(client, readdrawable);
218    context = GlxCheckSwap(client, context);
219
220    SetReplyHeader(client, &reply);
221
222    if (oldContextTag != 0) {
223        oldTag = GlxLookupContextTag(client, oldContextTag);
224        if (oldTag == NULL) {
225            return GlxErrorBase + GLXBadContextTag;
226        }
227    }
228    if (context != 0) {
229        newVendor = GlxGetXIDMap(context);
230        if (newVendor == NULL) {
231            return GlxErrorBase + GLXBadContext;
232        }
233    }
234
235    if (oldTag == NULL && newVendor == NULL) {
236        // Nothing to do here. Just send a successful reply.
237        reply.contextTag = 0;
238    } else if (oldTag != NULL && newVendor != NULL
239            && oldTag->context == context
240            && oldTag->drawable == drawable
241            && oldTag->readdrawable == readdrawable)
242    {
243        // The old and new values are all the same, so send a successful reply.
244        reply.contextTag = oldTag->tag;
245    } else {
246        // TODO: For switching contexts in a single vendor, just make one
247        // makeCurrent call?
248
249        // TODO: When changing vendors, would it be better to do the
250        // MakeCurrent(new) first, then the LoseCurrent(old)?
251        // If the MakeCurrent(new) fails, then the old context will still be current.
252        // If the LoseCurrent(old) fails, then we can (probably) undo the MakeCurrent(new) with
253        // a LoseCurrent(old).
254        // But, if the recovery LoseCurrent(old) fails, then we're really in a bad state.
255
256        // Clear the old context first.
257        if (oldTag != NULL) {
258            int ret = CommonLoseCurrent(client, oldTag);
259            if (ret != Success) {
260                return ret;
261            }
262            oldTag = NULL;
263        }
264
265        if (newVendor != NULL) {
266            int ret = CommonMakeNewCurrent(client, newVendor, drawable, readdrawable, context, &reply.contextTag);
267            if (ret != Success) {
268                return ret;
269            }
270        } else {
271            reply.contextTag = 0;
272        }
273    }
274
275    reply.contextTag = GlxCheckSwap(client, reply.contextTag);
276    WriteToClient(client, sz_xGLXMakeCurrentReply, &reply);
277    return Success;
278}
279
280static int dispatch_GLXMakeCurrent(ClientPtr client)
281{
282    REQUEST(xGLXMakeCurrentReq);
283    REQUEST_SIZE_MATCH(*stuff);
284
285    return CommonMakeCurrent(client, stuff->oldContextTag,
286            stuff->drawable, stuff->drawable, stuff->context);
287}
288
289static int dispatch_GLXMakeContextCurrent(ClientPtr client)
290{
291    REQUEST(xGLXMakeContextCurrentReq);
292    REQUEST_SIZE_MATCH(*stuff);
293
294    return CommonMakeCurrent(client, stuff->oldContextTag,
295            stuff->drawable, stuff->readdrawable, stuff->context);
296}
297
298static int dispatch_GLXMakeCurrentReadSGI(ClientPtr client)
299{
300    REQUEST(xGLXMakeCurrentReadSGIReq);
301    REQUEST_SIZE_MATCH(*stuff);
302
303    return CommonMakeCurrent(client, stuff->oldContextTag,
304            stuff->drawable, stuff->readable, stuff->context);
305}
306
307static int dispatch_GLXCopyContext(ClientPtr client)
308{
309    REQUEST(xGLXCopyContextReq);
310    GlxServerVendor *vendor;
311    REQUEST_SIZE_MATCH(*stuff);
312
313    // If we've got a context tag, then we'll use it to select a vendor. If we
314    // don't have a tag, then we'll look up one of the contexts. In either
315    // case, it's up to the vendor library to make sure that the context ID's
316    // are valid.
317    if (stuff->contextTag != 0) {
318        GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
319        if (tagInfo == NULL) {
320            return GlxErrorBase + GLXBadContextTag;
321        }
322        vendor = tagInfo->vendor;
323    } else {
324        vendor = GlxGetXIDMap(GlxCheckSwap(client, stuff->source));
325        if (vendor == NULL) {
326            return GlxErrorBase + GLXBadContext;
327        }
328    }
329    return vendor->glxvc.handleRequest(client);
330}
331
332static int dispatch_GLXSwapBuffers(ClientPtr client)
333{
334    GlxServerVendor *vendor = NULL;
335    REQUEST(xGLXSwapBuffersReq);
336    REQUEST_SIZE_MATCH(*stuff);
337
338    if (stuff->contextTag != 0) {
339        // If the request has a context tag, then look up a vendor from that.
340        // The vendor library is then responsible for validating the drawable.
341        GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
342        if (tagInfo == NULL) {
343            return GlxErrorBase + GLXBadContextTag;
344        }
345        vendor = tagInfo->vendor;
346    } else {
347        // We don't have a context tag, so look up the vendor from the
348        // drawable.
349        vendor = GlxGetXIDMap(GlxCheckSwap(client, stuff->drawable));
350        if (vendor == NULL) {
351            return GlxErrorBase + GLXBadDrawable;
352        }
353    }
354
355    return vendor->glxvc.handleRequest(client);
356}
357
358/**
359 * This is a generic handler for all of the X_GLXsop* requests.
360 */
361static int dispatch_GLXSingle(ClientPtr client)
362{
363    REQUEST(xGLXSingleReq);
364    GlxContextTagInfo *tagInfo;
365    REQUEST_AT_LEAST_SIZE(*stuff);
366
367    tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
368    if (tagInfo != NULL) {
369        return tagInfo->vendor->glxvc.handleRequest(client);
370    } else {
371        return GlxErrorBase + GLXBadContextTag;
372    }
373}
374
375static int dispatch_GLXVendorPriv(ClientPtr client)
376{
377    GlxVendorPrivDispatch *disp;
378    REQUEST(xGLXVendorPrivateReq);
379    REQUEST_AT_LEAST_SIZE(*stuff);
380
381    disp = LookupVendorPrivDispatch(GlxCheckSwap(client, stuff->vendorCode), TRUE);
382    if (disp == NULL) {
383        return BadAlloc;
384    }
385
386    if (disp->proc == NULL) {
387        // We don't have a dispatch function for this request yet. Check with
388        // each vendor library to find one.
389        // Note that even if none of the vendors provides a dispatch stub,
390        // we'll still add an entry to the dispatch table, so that we don't
391        // have to look it up again later.
392
393        disp->proc = GetVendorDispatchFunc(stuff->glxCode,
394                                           GlxCheckSwap(client,
395                                                        stuff->vendorCode));
396    }
397    return disp->proc(client);
398}
399
400Bool GlxDispatchInit(void)
401{
402    GlxVendorPrivDispatch *disp;
403
404    vendorPrivHash = ht_create(sizeof(CARD32), sizeof(GlxVendorPrivDispatch),
405                               ht_generic_hash, ht_generic_compare,
406                               (void *) &vendorPrivSetup);
407    if (!vendorPrivHash) {
408        return FALSE;
409    }
410
411    // Assign a custom dispatch stub GLXMakeCurrentReadSGI. This is the only
412    // vendor private request that we need to deal with in libglvnd itself.
413    disp = LookupVendorPrivDispatch(X_GLXvop_MakeCurrentReadSGI, TRUE);
414    if (disp == NULL) {
415        return FALSE;
416    }
417    disp->proc = dispatch_GLXMakeCurrentReadSGI;
418
419    // Assign the dispatch stubs for requests that need special handling.
420    dispatchFuncs[X_GLXQueryVersion] = dispatch_GLXQueryVersion;
421    dispatchFuncs[X_GLXMakeCurrent] = dispatch_GLXMakeCurrent;
422    dispatchFuncs[X_GLXMakeContextCurrent] = dispatch_GLXMakeContextCurrent;
423    dispatchFuncs[X_GLXCopyContext] = dispatch_GLXCopyContext;
424    dispatchFuncs[X_GLXSwapBuffers] = dispatch_GLXSwapBuffers;
425
426    dispatchFuncs[X_GLXClientInfo] = dispatch_GLXClientInfo;
427    dispatchFuncs[X_GLXSetClientInfoARB] = dispatch_GLXClientInfo;
428    dispatchFuncs[X_GLXSetClientInfo2ARB] = dispatch_GLXClientInfo;
429
430    dispatchFuncs[X_GLXVendorPrivate] = dispatch_GLXVendorPriv;
431    dispatchFuncs[X_GLXVendorPrivateWithReply] = dispatch_GLXVendorPriv;
432
433    // Assign the trivial stubs
434    dispatchFuncs[X_GLXRender] = dispatch_Render;
435    dispatchFuncs[X_GLXRenderLarge] = dispatch_RenderLarge;
436    dispatchFuncs[X_GLXCreateContext] = dispatch_CreateContext;
437    dispatchFuncs[X_GLXDestroyContext] = dispatch_DestroyContext;
438    dispatchFuncs[X_GLXWaitGL] = dispatch_WaitGL;
439    dispatchFuncs[X_GLXWaitX] = dispatch_WaitX;
440    dispatchFuncs[X_GLXUseXFont] = dispatch_UseXFont;
441    dispatchFuncs[X_GLXCreateGLXPixmap] = dispatch_CreateGLXPixmap;
442    dispatchFuncs[X_GLXGetVisualConfigs] = dispatch_GetVisualConfigs;
443    dispatchFuncs[X_GLXDestroyGLXPixmap] = dispatch_DestroyGLXPixmap;
444    dispatchFuncs[X_GLXQueryExtensionsString] = dispatch_QueryExtensionsString;
445    dispatchFuncs[X_GLXQueryServerString] = dispatch_QueryServerString;
446    dispatchFuncs[X_GLXChangeDrawableAttributes] = dispatch_ChangeDrawableAttributes;
447    dispatchFuncs[X_GLXCreateNewContext] = dispatch_CreateNewContext;
448    dispatchFuncs[X_GLXCreatePbuffer] = dispatch_CreatePbuffer;
449    dispatchFuncs[X_GLXCreatePixmap] = dispatch_CreatePixmap;
450    dispatchFuncs[X_GLXCreateWindow] = dispatch_CreateWindow;
451    dispatchFuncs[X_GLXCreateContextAttribsARB] = dispatch_CreateContextAttribsARB;
452    dispatchFuncs[X_GLXDestroyPbuffer] = dispatch_DestroyPbuffer;
453    dispatchFuncs[X_GLXDestroyPixmap] = dispatch_DestroyPixmap;
454    dispatchFuncs[X_GLXDestroyWindow] = dispatch_DestroyWindow;
455    dispatchFuncs[X_GLXGetDrawableAttributes] = dispatch_GetDrawableAttributes;
456    dispatchFuncs[X_GLXGetFBConfigs] = dispatch_GetFBConfigs;
457    dispatchFuncs[X_GLXQueryContext] = dispatch_QueryContext;
458    dispatchFuncs[X_GLXIsDirect] = dispatch_IsDirect;
459
460    return TRUE;
461}
462
463void GlxDispatchReset(void)
464{
465    memset(dispatchFuncs, 0, sizeof(dispatchFuncs));
466
467    ht_destroy(vendorPrivHash);
468    vendorPrivHash = NULL;
469}
470
471int GlxDispatchRequest(ClientPtr client)
472{
473    REQUEST(xReq);
474    int result;
475
476    if (GlxExtensionEntry->base == 0)
477        return BadRequest;
478
479    GlxSetRequestClient(client);
480
481    if (stuff->data < OPCODE_ARRAY_LEN) {
482        if (dispatchFuncs[stuff->data] == NULL) {
483            // Try to find a dispatch stub.
484            dispatchFuncs[stuff->data] = GetVendorDispatchFunc(stuff->data, 0);
485        }
486        result = dispatchFuncs[stuff->data](client);
487    } else {
488        result = dispatch_GLXSingle(client);
489    }
490
491    GlxSetRequestClient(NULL);
492
493    return result;
494}
495