1/*
2 * Copyright © 2014 Jon Turney
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24#include "glxclient.h"
25#include "glx_error.h"
26#include "dri_common.h"
27#include "util/macros.h"
28#include "windows/xwindowsdri.h"
29#include "windows/windowsgl.h"
30
31struct driwindows_display
32{
33   __GLXDRIdisplay base;
34   int event_base;
35};
36
37struct driwindows_context
38{
39   struct glx_context base;
40   windowsContext *windowsContext;
41};
42
43struct driwindows_config
44{
45   struct glx_config base;
46   int pxfi;
47};
48
49struct driwindows_screen
50{
51   struct glx_screen base;
52   __DRIscreen *driScreen;
53   __GLXDRIscreen vtable;
54   Bool copySubBuffer;
55};
56
57struct driwindows_drawable
58{
59   __GLXDRIdrawable base;
60   windowsDrawable *windowsDrawable;
61};
62
63/**
64 * GLXDRI functions
65 */
66
67static void
68driwindows_destroy_context(struct glx_context *context)
69{
70   struct driwindows_context *pcp = (struct driwindows_context *) context;
71
72   driReleaseDrawables(&pcp->base);
73
74   free((char *) context->extensions);
75
76   windows_destroy_context(pcp->windowsContext);
77
78   free(pcp);
79}
80
81static int
82driwindows_bind_context(struct glx_context *context, struct glx_context *old,
83                        GLXDrawable draw, GLXDrawable read)
84{
85   struct driwindows_context *pcp = (struct driwindows_context *) context;
86   struct driwindows_drawable *pdraw, *pread;
87
88   pdraw = (struct driwindows_drawable *) driFetchDrawable(context, draw);
89   pread = (struct driwindows_drawable *) driFetchDrawable(context, read);
90
91   driReleaseDrawables(&pcp->base);
92
93   if (pdraw == NULL || pread == NULL)
94      return GLXBadDrawable;
95
96   if (windows_bind_context(pcp->windowsContext,
97                           pdraw->windowsDrawable, pread->windowsDrawable))
98      return Success;
99
100   return GLXBadContext;
101}
102
103static void
104driwindows_unbind_context(struct glx_context *context, struct glx_context *new)
105{
106   struct driwindows_context *pcp = (struct driwindows_context *) context;
107
108   windows_unbind_context(pcp->windowsContext);
109}
110
111static const struct glx_context_vtable driwindows_context_vtable = {
112   .destroy             = driwindows_destroy_context,
113   .bind                = driwindows_bind_context,
114   .unbind              = driwindows_unbind_context,
115   .wait_gl             = NULL,
116   .wait_x              = NULL,
117};
118
119static struct glx_context *
120driwindows_create_context(struct glx_screen *base,
121                          struct glx_config *config_base,
122                          struct glx_context *shareList, int renderType)
123{
124   struct driwindows_context *pcp, *pcp_shared;
125   struct driwindows_config *config = (struct driwindows_config *) config_base;
126   struct driwindows_screen *psc = (struct driwindows_screen *) base;
127   windowsContext *shared = NULL;
128
129   if (!psc->base.driScreen)
130      return NULL;
131
132   /* Check the renderType value */
133   if (!validate_renderType_against_config(config_base, renderType))
134       return NULL;
135
136   if (shareList) {
137      /* If the shareList context is not on this renderer, we cannot possibly
138       * create a context that shares with it.
139       */
140      if (shareList->vtable->destroy != driwindows_destroy_context) {
141         return NULL;
142      }
143
144      pcp_shared = (struct driwindows_context *) shareList;
145      shared = pcp_shared->windowsContext;
146   }
147
148   pcp = calloc(1, sizeof *pcp);
149   if (pcp == NULL)
150      return NULL;
151
152   if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
153      free(pcp);
154      return NULL;
155   }
156
157   pcp->base.renderType = renderType;
158
159   InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi);
160
161   pcp->windowsContext = windows_create_context(config->pxfi, shared);
162
163   if (!pcp->windowsContext) {
164      free(pcp);
165      return NULL;
166   }
167
168   pcp->base.vtable = &driwindows_context_vtable;
169
170   return &pcp->base;
171}
172
173static struct glx_context *
174driwindows_create_context_attribs(struct glx_screen *base,
175                                  struct glx_config *config_base,
176                                  struct glx_context *shareList,
177                                  unsigned num_attribs,
178                                  const uint32_t *attribs,
179                                  unsigned *error)
180{
181   struct driwindows_context *pcp, *pcp_shared;
182   struct driwindows_config *config = (struct driwindows_config *) config_base;
183   struct driwindows_screen *psc = (struct driwindows_screen *) base;
184   windowsContext *shared = NULL;
185
186   int i;
187   uint32_t renderType = GLX_RGBA_TYPE;
188
189   /* Extract renderType from attribs */
190   for (i = 0; i < num_attribs; i++) {
191      switch (attribs[i * 2]) {
192      case GLX_RENDER_TYPE:
193         renderType = attribs[i * 2 + 1];
194         break;
195      }
196   }
197
198   /*
199     Perhaps we should map GLX tokens to WGL tokens, but they appear to have
200     identical values, so far
201   */
202
203   if (!psc->base.driScreen || !config_base)
204      return NULL;
205
206   /* Check the renderType value */
207   if (!validate_renderType_against_config(config_base, renderType)) {
208       return NULL;
209   }
210
211   if (shareList) {
212      /* If the shareList context is not on this renderer, we cannot possibly
213       * create a context that shares with it.
214       */
215      if (shareList->vtable->destroy != driwindows_destroy_context) {
216         return NULL;
217      }
218
219      pcp_shared = (struct driwindows_context *) shareList;
220      shared = pcp_shared->windowsContext;
221   }
222
223   pcp = calloc(1, sizeof *pcp);
224   if (pcp == NULL)
225      return NULL;
226
227   if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
228      free(pcp);
229      return NULL;
230   }
231
232   pcp->base.renderType = renderType;
233
234   InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi);
235
236   pcp->windowsContext = windows_create_context_attribs(config->pxfi,
237                                                      shared,
238                                                      (const int *)attribs);
239   if (pcp->windowsContext == NULL) {
240      free(pcp);
241      return NULL;
242   }
243
244   pcp->base.vtable = &driwindows_context_vtable;
245
246   return &pcp->base;
247}
248
249static void
250driwindowsDestroyDrawable(__GLXDRIdrawable * pdraw)
251{
252   struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
253
254   windows_destroy_drawable(pdp->windowsDrawable);
255
256   free(pdp);
257}
258
259static __GLXDRIdrawable *
260driwindowsCreateDrawable(struct glx_screen *base, XID xDrawable,
261                         GLXDrawable drawable, struct glx_config *modes)
262{
263   struct driwindows_drawable *pdp;
264   struct driwindows_screen *psc = (struct driwindows_screen *) base;
265
266   pdp = calloc(1, sizeof(*pdp));
267   if (!pdp)
268      return NULL;
269
270   pdp->base.xDrawable = xDrawable;
271   pdp->base.drawable = drawable;
272   pdp->base.psc = &psc->base;
273
274   /*
275      By this stage, the X drawable already exists, but the GLX drawable may
276      not.
277
278      Query the server with the XID to find the correct HWND, HPBUFFERARB or
279      HBITMAP
280   */
281
282   unsigned int type;
283   void *handle;
284
285   if (!XWindowsDRIQueryDrawable(psc->base.dpy, base->scr, drawable, &type, &handle))
286   {
287      free(pdp);
288      return NULL;
289   }
290
291   /* No handle found is a failure */
292   if (!handle) {
293      free(pdp);
294      return NULL;
295   }
296
297   /* Create a new drawable */
298   pdp->windowsDrawable = windows_create_drawable(type, handle);
299
300   if (!pdp->windowsDrawable) {
301      free(pdp);
302      return NULL;
303   }
304
305   pdp->base.destroyDrawable = driwindowsDestroyDrawable;
306
307   return &pdp->base;
308}
309
310static int64_t
311driwindowsSwapBuffers(__GLXDRIdrawable * pdraw,
312                 int64_t target_msc, int64_t divisor, int64_t remainder,
313                 Bool flush)
314{
315   struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
316
317   (void) target_msc;
318   (void) divisor;
319   (void) remainder;
320
321   if (flush) {
322      glFlush();
323   }
324
325   windows_swap_buffers(pdp->windowsDrawable);
326
327   return 0;
328}
329
330static void
331driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw,
332                   int x, int y, int width, int height, Bool flush)
333{
334   struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
335
336   if (flush) {
337      glFlush();
338   }
339
340   windows_copy_subbuffer(pdp->windowsDrawable, x, y, width, height);
341}
342
343static void
344driwindowsDestroyScreen(struct glx_screen *base)
345{
346   struct driwindows_screen *psc = (struct driwindows_screen *) base;
347
348   /* Free the direct rendering per screen data */
349   psc->driScreen = NULL;
350   free(psc);
351}
352
353static const struct glx_screen_vtable driwindows_screen_vtable = {
354   .create_context         = driwindows_create_context,
355   .create_context_attribs = driwindows_create_context_attribs,
356   .query_renderer_integer = NULL,
357   .query_renderer_string  = NULL,
358};
359
360static Bool
361driwindowsBindExtensions(struct driwindows_screen *psc)
362{
363   Bool result = 1;
364
365   const struct
366   {
367      char *wglext;
368      char *glxext;
369      Bool mandatory;
370   } extensionMap[] = {
371      { "WGL_ARB_make_current_read", "GLX_SGI_make_current_read", 0 },
372      { "WGL_EXT_swap_control", "GLX_SGI_swap_control", 0 },
373      { "WGL_EXT_swap_control", "GLX_MESA_swap_control", 0 },
374//      { "WGL_ARB_render_texture", "GLX_EXT_texture_from_pixmap", 0 },
375// Not exactly equivalent, needs some more glue to be written
376      { "WGL_ARB_pbuffer", "GLX_SGIX_pbuffer", 1 },
377      { "WGL_ARB_multisample", "GLX_ARB_multisample", 1 },
378      { "WGL_ARB_multisample", "GLX_SGIS_multisample", 1 },
379      { "WGL_ARB_create_context", "GLX_ARB_create_context", 0 },
380      { "WGL_ARB_create_context_profile", "GLX_ARB_create_context_profile", 0 },
381      { "WGL_ARB_create_context_robustness", "GLX_ARB_create_context_robustness", 0 },
382      { "WGL_EXT_create_context_es2_profile", "GLX_EXT_create_context_es2_profile", 0 },
383   };
384
385   char *wgl_extensions;
386   char *gl_extensions;
387   int i;
388
389   windows_extensions(&gl_extensions, &wgl_extensions);
390
391   for (i = 0; i < ARRAY_SIZE(extensionMap); i++) {
392      if (strstr(wgl_extensions, extensionMap[i].wglext)) {
393          __glXEnableDirectExtension(&psc->base, extensionMap[i].glxext);
394          InfoMessageF("enabled %s\n", extensionMap[i].glxext);
395      }
396      else if (extensionMap[i].mandatory) {
397         ErrorMessageF("required WGL extension %s is missing\n", extensionMap[i].wglext);
398         result = 0;
399      }
400   }
401
402   /*
403       Because it pre-dates WGL_EXT_extensions_string, GL_WIN_swap_hint might
404       only be in GL_EXTENSIONS
405   */
406   if (strstr(gl_extensions, "GL_WIN_swap_hint")) {
407      psc->copySubBuffer = 1;
408      __glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer");
409      InfoMessageF("enabled GLX_MESA_copy_sub_buffer\n");
410   }
411
412   free(gl_extensions);
413   free(wgl_extensions);
414
415   return result;
416}
417
418static struct glx_config *
419driwindowsMapConfigs(struct glx_display *priv, int screen, struct glx_config *configs, struct glx_config *fbconfigs)
420{
421   struct glx_config head, *tail, *m;
422
423   tail = &head;
424   head.next = NULL;
425
426   for (m = configs; m; m = m->next) {
427      int fbconfigID = GLX_DONT_CARE;
428      if (fbconfigs) {
429         /*
430           visuals have fbconfigID of GLX_DONT_CARE, so search for a fbconfig
431           with matching visualID and get the fbconfigID from there
432         */
433         struct glx_config *f;
434         for (f = fbconfigs; f; f = f->next) {
435            if (f->visualID == m->visualID)
436               fbconfigID = f->fbconfigID;
437         }
438      }
439      else {
440         fbconfigID = m->fbconfigID;
441      }
442
443      int pxfi;
444      XWindowsDRIFBConfigToPixelFormat(priv->dpy, screen, fbconfigID, &pxfi);
445      if (pxfi == 0)
446         continue;
447
448      struct driwindows_config *config = malloc(sizeof(*config));
449
450      tail->next = &config->base;
451      if (tail->next == NULL)
452         continue;
453
454      config->base = *m;
455      config->pxfi = pxfi;
456
457      tail = tail->next;
458   }
459
460   return head.next;
461}
462
463static struct glx_screen *
464driwindowsCreateScreen(int screen, struct glx_display *priv)
465{
466   __GLXDRIscreen *psp;
467   struct driwindows_screen *psc;
468   struct glx_config *configs = NULL, *visuals = NULL;
469   int directCapable;
470
471   psc = calloc(1, sizeof *psc);
472   if (psc == NULL)
473      return NULL;
474
475   if (!glx_screen_init(&psc->base, screen, priv)) {
476      free(psc);
477      return NULL;
478   }
479
480   if (!XWindowsDRIQueryDirectRenderingCapable(psc->base.dpy, screen, &directCapable) ||
481       !directCapable) {
482      ErrorMessageF("Screen is not Windows-DRI capable\n");
483      goto handle_error;
484   }
485
486   /* discover native supported extensions */
487   if (!driwindowsBindExtensions(psc)) {
488      goto handle_error;
489   }
490
491   /* Augment configs with pxfi information */
492   configs = driwindowsMapConfigs(priv, screen, psc->base.configs, NULL);
493   visuals = driwindowsMapConfigs(priv, screen, psc->base.visuals, configs);
494
495   if (!configs || !visuals) {
496       ErrorMessageF("No fbConfigs or visuals found\n");
497       goto handle_error;
498   }
499
500   glx_config_destroy_list(psc->base.configs);
501   psc->base.configs = configs;
502   glx_config_destroy_list(psc->base.visuals);
503   psc->base.visuals = visuals;
504
505   psc->base.vtable = &driwindows_screen_vtable;
506   psp = &psc->vtable;
507   psc->base.driScreen = psp;
508   psp->destroyScreen = driwindowsDestroyScreen;
509   psp->createDrawable = driwindowsCreateDrawable;
510   psp->swapBuffers = driwindowsSwapBuffers;
511
512   if (psc->copySubBuffer)
513      psp->copySubBuffer = driwindowsCopySubBuffer;
514
515   return &psc->base;
516
517handle_error:
518   glx_screen_cleanup(&psc->base);
519
520   return NULL;
521}
522
523/* Called from __glXFreeDisplayPrivate.
524 */
525static void
526driwindowsDestroyDisplay(__GLXDRIdisplay * dpy)
527{
528   free(dpy);
529}
530
531/*
532 * Allocate, initialize and return a  __GLXDRIdisplay object.
533 * This is called from __glXInitialize() when we are given a new
534 * display pointer.
535 */
536_X_HIDDEN __GLXDRIdisplay *
537driwindowsCreateDisplay(Display * dpy)
538{
539   struct driwindows_display *pdpyp;
540
541   int eventBase, errorBase;
542   int major, minor, patch;
543
544   /* Verify server has Windows-DRI extension */
545   if (!XWindowsDRIQueryExtension(dpy, &eventBase, &errorBase)) {
546      ErrorMessageF("Windows-DRI extension not available\n");
547      return NULL;
548   }
549
550   if (!XWindowsDRIQueryVersion(dpy, &major, &minor, &patch)) {
551      ErrorMessageF("Fetching Windows-DRI extension version failed\n");
552      return NULL;
553   }
554
555   if (!windows_check_renderer()) {
556      ErrorMessageF("Windows-DRI extension disabled for GDI Generic renderer\n");
557      return NULL;
558   }
559
560   pdpyp = malloc(sizeof *pdpyp);
561   if (pdpyp == NULL)
562      return NULL;
563
564   pdpyp->base.destroyDisplay = driwindowsDestroyDisplay;
565   pdpyp->base.createScreen = driwindowsCreateScreen;
566
567   pdpyp->event_base = eventBase;
568
569   return &pdpyp->base;
570}
571