vndcmds.c revision 25da500f
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    rep->length = 0;
96}
97
98/* Include the trivial dispatch handlers */
99#include "vnd_dispatch_stubs.c"
100
101static int dispatch_GLXQueryVersion(ClientPtr client)
102{
103    xGLXQueryVersionReply reply;
104    REQUEST_SIZE_MATCH(xGLXQueryVersionReq);
105
106    SetReplyHeader(client, &reply);
107    reply.majorVersion = GlxCheckSwap(client, 1);
108    reply.minorVersion = GlxCheckSwap(client, 4);
109
110    WriteToClient(client, sz_xGLXQueryVersionReply, &reply);
111    return Success;
112}
113
114/* broken header workaround */
115#ifndef X_GLXSetClientInfo2ARB
116#define X_GLXSetClientInfo2ARB X_GLXSetConfigInfo2ARB
117#endif
118
119/**
120 * This function is used for X_GLXClientInfo, X_GLXSetClientInfoARB, and
121 * X_GLXSetClientInfo2ARB.
122 */
123static int dispatch_GLXClientInfo(ClientPtr client)
124{
125    GlxServerVendor *vendor;
126    void *requestCopy = NULL;
127    size_t requestSize = client->req_len * 4;
128
129    if (client->minorOp == X_GLXClientInfo) {
130        REQUEST_AT_LEAST_SIZE(xGLXClientInfoReq);
131    } else if (client->minorOp == X_GLXSetClientInfoARB) {
132        REQUEST_AT_LEAST_SIZE(xGLXSetClientInfoARBReq);
133    } else if (client->minorOp == X_GLXSetClientInfo2ARB) {
134        REQUEST_AT_LEAST_SIZE(xGLXSetClientInfo2ARBReq);
135    } else {
136        return BadImplementation;
137    }
138
139    // We'll forward this request to each vendor library. Since a vendor might
140    // modify the request data in place (e.g., for byte swapping), make a copy
141    // of the request first.
142    requestCopy = malloc(requestSize);
143    if (requestCopy == NULL) {
144        return BadAlloc;
145    }
146    memcpy(requestCopy, client->requestBuffer, requestSize);
147
148    xorg_list_for_each_entry(vendor, &GlxVendorList, entry) {
149        vendor->glxvc.handleRequest(client);
150        // Revert the request buffer back to our copy.
151        memcpy(client->requestBuffer, requestCopy, requestSize);
152    }
153    free(requestCopy);
154    return Success;
155}
156
157static int CommonLoseCurrent(ClientPtr client, GlxContextTagInfo *tagInfo)
158{
159    int ret;
160
161    ret = tagInfo->vendor->glxvc.makeCurrent(client,
162            tagInfo->tag, // No old context tag,
163            None, None, None, 0);
164
165    if (ret == Success) {
166        GlxFreeContextTag(tagInfo);
167    }
168    return ret;
169}
170
171static int CommonMakeNewCurrent(ClientPtr client,
172        GlxServerVendor *vendor,
173        GLXDrawable drawable,
174        GLXDrawable readdrawable,
175        GLXContextID context,
176        GLXContextTag *newContextTag)
177{
178    int ret = BadAlloc;
179    GlxContextTagInfo *tagInfo;
180
181    tagInfo = GlxAllocContextTag(client, vendor);
182
183    if (tagInfo) {
184        ret = vendor->glxvc.makeCurrent(client,
185                0, // No old context tag,
186                drawable, readdrawable, context,
187                tagInfo->tag);
188
189        if (ret == Success) {
190            tagInfo->drawable = drawable;
191            tagInfo->readdrawable = readdrawable;
192            tagInfo->context = context;
193            *newContextTag = tagInfo->tag;
194        } else {
195            GlxFreeContextTag(tagInfo);
196        }
197    }
198
199    return ret;
200}
201
202static int CommonMakeCurrent(ClientPtr client,
203        GLXContextTag oldContextTag,
204        GLXDrawable drawable,
205        GLXDrawable readdrawable,
206        GLXContextID context)
207{
208    xGLXMakeCurrentReply reply = {};
209    GlxContextTagInfo *oldTag = NULL;
210    GlxServerVendor *newVendor = NULL;
211
212    oldContextTag = GlxCheckSwap(client, oldContextTag);
213    drawable = GlxCheckSwap(client, drawable);
214    readdrawable = GlxCheckSwap(client, readdrawable);
215    context = GlxCheckSwap(client, context);
216
217    SetReplyHeader(client, &reply);
218
219    if (oldContextTag != 0) {
220        oldTag = GlxLookupContextTag(client, oldContextTag);
221        if (oldTag == NULL) {
222            return GlxErrorBase + GLXBadContextTag;
223        }
224    }
225    if (context != 0) {
226        newVendor = GlxGetXIDMap(context);
227        if (newVendor == NULL) {
228            return GlxErrorBase + GLXBadContext;
229        }
230    }
231
232    if (oldTag == NULL && newVendor == NULL) {
233        // Nothing to do here. Just send a successful reply.
234        reply.contextTag = 0;
235    } else if (oldTag != NULL && newVendor != NULL
236            && oldTag->context == context
237            && oldTag->drawable == drawable
238            && oldTag->readdrawable == readdrawable)
239    {
240        // The old and new values are all the same, so send a successful reply.
241        reply.contextTag = oldTag->tag;
242    } else {
243        // TODO: For switching contexts in a single vendor, just make one
244        // makeCurrent call?
245
246        // TODO: When changing vendors, would it be better to do the
247        // MakeCurrent(new) first, then the LoseCurrent(old)?
248        // If the MakeCurrent(new) fails, then the old context will still be current.
249        // If the LoseCurrent(old) fails, then we can (probably) undo the MakeCurrent(new) with
250        // a LoseCurrent(old).
251        // But, if the recovery LoseCurrent(old) fails, then we're really in a bad state.
252
253        // Clear the old context first.
254        if (oldTag != NULL) {
255            int ret = CommonLoseCurrent(client, oldTag);
256            if (ret != Success) {
257                return ret;
258            }
259            oldTag = NULL;
260        }
261
262        if (newVendor != NULL) {
263            int ret = CommonMakeNewCurrent(client, newVendor, drawable, readdrawable, context, &reply.contextTag);
264            if (ret != Success) {
265                return ret;
266            }
267        } else {
268            reply.contextTag = 0;
269        }
270    }
271
272    reply.contextTag = GlxCheckSwap(client, reply.contextTag);
273    WriteToClient(client, sz_xGLXMakeCurrentReply, &reply);
274    return Success;
275}
276
277static int dispatch_GLXMakeCurrent(ClientPtr client)
278{
279    REQUEST(xGLXMakeCurrentReq);
280    REQUEST_SIZE_MATCH(*stuff);
281
282    return CommonMakeCurrent(client, stuff->oldContextTag,
283            stuff->drawable, stuff->drawable, stuff->context);
284}
285
286static int dispatch_GLXMakeContextCurrent(ClientPtr client)
287{
288    REQUEST(xGLXMakeContextCurrentReq);
289    REQUEST_SIZE_MATCH(*stuff);
290
291    return CommonMakeCurrent(client, stuff->oldContextTag,
292            stuff->drawable, stuff->readdrawable, stuff->context);
293}
294
295static int dispatch_GLXMakeCurrentReadSGI(ClientPtr client)
296{
297    REQUEST(xGLXMakeCurrentReadSGIReq);
298    REQUEST_SIZE_MATCH(*stuff);
299
300    return CommonMakeCurrent(client, stuff->oldContextTag,
301            stuff->drawable, stuff->readable, stuff->context);
302}
303
304static int dispatch_GLXCopyContext(ClientPtr client)
305{
306    REQUEST(xGLXCopyContextReq);
307    GlxServerVendor *vendor;
308    REQUEST_SIZE_MATCH(*stuff);
309
310    // If we've got a context tag, then we'll use it to select a vendor. If we
311    // don't have a tag, then we'll look up one of the contexts. In either
312    // case, it's up to the vendor library to make sure that the context ID's
313    // are valid.
314    if (stuff->contextTag != 0) {
315        GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
316        if (tagInfo == NULL) {
317            return GlxErrorBase + GLXBadContextTag;
318        }
319        vendor = tagInfo->vendor;
320    } else {
321        vendor = GlxGetXIDMap(GlxCheckSwap(client, stuff->source));
322        if (vendor == NULL) {
323            return GlxErrorBase + GLXBadContext;
324        }
325    }
326    return vendor->glxvc.handleRequest(client);
327}
328
329static int dispatch_GLXSwapBuffers(ClientPtr client)
330{
331    GlxServerVendor *vendor = NULL;
332    REQUEST(xGLXSwapBuffersReq);
333    REQUEST_SIZE_MATCH(*stuff);
334
335    if (stuff->contextTag != 0) {
336        // If the request has a context tag, then look up a vendor from that.
337        // The vendor library is then responsible for validating the drawable.
338        GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
339        if (tagInfo == NULL) {
340            return GlxErrorBase + GLXBadContextTag;
341        }
342        vendor = tagInfo->vendor;
343    } else {
344        // We don't have a context tag, so look up the vendor from the
345        // drawable.
346        vendor = GlxGetXIDMap(GlxCheckSwap(client, stuff->drawable));
347        if (vendor == NULL) {
348            return GlxErrorBase + GLXBadDrawable;
349        }
350    }
351
352    return vendor->glxvc.handleRequest(client);
353}
354
355/**
356 * This is a generic handler for all of the X_GLXsop* requests.
357 */
358static int dispatch_GLXSingle(ClientPtr client)
359{
360    REQUEST(xGLXSingleReq);
361    GlxContextTagInfo *tagInfo;
362    REQUEST_AT_LEAST_SIZE(*stuff);
363
364    tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
365    if (tagInfo != NULL) {
366        return tagInfo->vendor->glxvc.handleRequest(client);
367    } else {
368        return GlxErrorBase + GLXBadContextTag;
369    }
370}
371
372static int dispatch_GLXVendorPriv(ClientPtr client)
373{
374    GlxVendorPrivDispatch *disp;
375    REQUEST(xGLXVendorPrivateReq);
376    REQUEST_AT_LEAST_SIZE(*stuff);
377
378    disp = LookupVendorPrivDispatch(GlxCheckSwap(client, stuff->vendorCode), TRUE);
379    if (disp == NULL) {
380        return BadAlloc;
381    }
382
383    if (disp->proc == NULL) {
384        // We don't have a dispatch function for this request yet. Check with
385        // each vendor library to find one.
386        // Note that even if none of the vendors provides a dispatch stub,
387        // we'll still add an entry to the dispatch table, so that we don't
388        // have to look it up again later.
389
390        disp->proc = GetVendorDispatchFunc(stuff->glxCode,
391                                           GlxCheckSwap(client,
392                                                        stuff->vendorCode));
393    }
394    return disp->proc(client);
395}
396
397Bool GlxDispatchInit(void)
398{
399    GlxVendorPrivDispatch *disp;
400
401    vendorPrivHash = ht_create(sizeof(CARD32), sizeof(GlxVendorPrivDispatch),
402                               ht_generic_hash, ht_generic_compare,
403                               (void *) &vendorPrivSetup);
404    if (!vendorPrivHash) {
405        return FALSE;
406    }
407
408    // Assign a custom dispatch stub GLXMakeCurrentReadSGI. This is the only
409    // vendor private request that we need to deal with in libglvnd itself.
410    disp = LookupVendorPrivDispatch(X_GLXvop_MakeCurrentReadSGI, TRUE);
411    if (disp == NULL) {
412        return FALSE;
413    }
414    disp->proc = dispatch_GLXMakeCurrentReadSGI;
415
416    // Assign the dispatch stubs for requests that need special handling.
417    dispatchFuncs[X_GLXQueryVersion] = dispatch_GLXQueryVersion;
418    dispatchFuncs[X_GLXMakeCurrent] = dispatch_GLXMakeCurrent;
419    dispatchFuncs[X_GLXMakeContextCurrent] = dispatch_GLXMakeContextCurrent;
420    dispatchFuncs[X_GLXCopyContext] = dispatch_GLXCopyContext;
421    dispatchFuncs[X_GLXSwapBuffers] = dispatch_GLXSwapBuffers;
422
423    dispatchFuncs[X_GLXClientInfo] = dispatch_GLXClientInfo;
424    dispatchFuncs[X_GLXSetClientInfoARB] = dispatch_GLXClientInfo;
425    dispatchFuncs[X_GLXSetClientInfo2ARB] = dispatch_GLXClientInfo;
426
427    dispatchFuncs[X_GLXVendorPrivate] = dispatch_GLXVendorPriv;
428    dispatchFuncs[X_GLXVendorPrivateWithReply] = dispatch_GLXVendorPriv;
429
430    // Assign the trivial stubs
431    dispatchFuncs[X_GLXRender] = dispatch_Render;
432    dispatchFuncs[X_GLXRenderLarge] = dispatch_RenderLarge;
433    dispatchFuncs[X_GLXCreateContext] = dispatch_CreateContext;
434    dispatchFuncs[X_GLXDestroyContext] = dispatch_DestroyContext;
435    dispatchFuncs[X_GLXWaitGL] = dispatch_WaitGL;
436    dispatchFuncs[X_GLXWaitX] = dispatch_WaitX;
437    dispatchFuncs[X_GLXUseXFont] = dispatch_UseXFont;
438    dispatchFuncs[X_GLXCreateGLXPixmap] = dispatch_CreateGLXPixmap;
439    dispatchFuncs[X_GLXGetVisualConfigs] = dispatch_GetVisualConfigs;
440    dispatchFuncs[X_GLXDestroyGLXPixmap] = dispatch_DestroyGLXPixmap;
441    dispatchFuncs[X_GLXQueryExtensionsString] = dispatch_QueryExtensionsString;
442    dispatchFuncs[X_GLXQueryServerString] = dispatch_QueryServerString;
443    dispatchFuncs[X_GLXChangeDrawableAttributes] = dispatch_ChangeDrawableAttributes;
444    dispatchFuncs[X_GLXCreateNewContext] = dispatch_CreateNewContext;
445    dispatchFuncs[X_GLXCreatePbuffer] = dispatch_CreatePbuffer;
446    dispatchFuncs[X_GLXCreatePixmap] = dispatch_CreatePixmap;
447    dispatchFuncs[X_GLXCreateWindow] = dispatch_CreateWindow;
448    dispatchFuncs[X_GLXCreateContextAttribsARB] = dispatch_CreateContextAttribsARB;
449    dispatchFuncs[X_GLXDestroyPbuffer] = dispatch_DestroyPbuffer;
450    dispatchFuncs[X_GLXDestroyPixmap] = dispatch_DestroyPixmap;
451    dispatchFuncs[X_GLXDestroyWindow] = dispatch_DestroyWindow;
452    dispatchFuncs[X_GLXGetDrawableAttributes] = dispatch_GetDrawableAttributes;
453    dispatchFuncs[X_GLXGetFBConfigs] = dispatch_GetFBConfigs;
454    dispatchFuncs[X_GLXQueryContext] = dispatch_QueryContext;
455    dispatchFuncs[X_GLXIsDirect] = dispatch_IsDirect;
456
457    return TRUE;
458}
459
460void GlxDispatchReset(void)
461{
462    memset(dispatchFuncs, 0, sizeof(dispatchFuncs));
463
464    ht_destroy(vendorPrivHash);
465    vendorPrivHash = NULL;
466}
467
468int GlxDispatchRequest(ClientPtr client)
469{
470    REQUEST(xReq);
471    int result;
472
473    if (GlxExtensionEntry->base == 0)
474        return BadRequest;
475
476    GlxSetRequestClient(client);
477
478    if (stuff->data < OPCODE_ARRAY_LEN) {
479        if (dispatchFuncs[stuff->data] == NULL) {
480            // Try to find a dispatch stub.
481            dispatchFuncs[stuff->data] = GetVendorDispatchFunc(stuff->data, 0);
482        }
483        result = dispatchFuncs[stuff->data](client);
484    } else {
485        result = dispatch_GLXSingle(client);
486    }
487
488    GlxSetRequestClient(NULL);
489
490    return result;
491}
492