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 void
112driwindows_bind_tex_image(Display * dpy,
113                    GLXDrawable drawable,
114                    int buffer, const int *attrib_list)
115{
116   struct glx_context *gc = __glXGetCurrentContext();
117   struct driwindows_context *pcp = (struct driwindows_context *) gc;
118   __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
119   struct driwindows_drawable *pdraw = (struct driwindows_drawable *) base;
120
121   __glXInitialize(dpy);
122
123   if (pdraw != NULL) {
124      windows_setTexBuffer(pcp->windowsContext,
125                          pdraw->base.textureTarget,
126                          pdraw->base.textureFormat,
127                          pdraw->windowsDrawable);
128   }
129}
130
131static void
132driwindows_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer)
133{
134   struct glx_context *gc = __glXGetCurrentContext();
135   struct driwindows_context *pcp = (struct driwindows_context *) gc;
136   __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
137   struct glx_display *dpyPriv = __glXInitialize(dpy);
138   struct driwindows_drawable *pdraw = (struct driwindows_drawable *) base;
139
140   if (dpyPriv != NULL && pdraw != NULL) {
141      windows_releaseTexBuffer(pcp->windowsContext,
142                              pdraw->base.textureTarget,
143                              pdraw->windowsDrawable);
144      }
145}
146
147static const struct glx_context_vtable driwindows_context_vtable = {
148   .destroy             = driwindows_destroy_context,
149   .bind                = driwindows_bind_context,
150   .unbind              = driwindows_unbind_context,
151   .wait_gl             = NULL,
152   .wait_x              = NULL,
153   .use_x_font          = DRI_glXUseXFont,
154   .bind_tex_image      = driwindows_bind_tex_image,
155   .release_tex_image   = driwindows_release_tex_image,
156   .get_proc_address    = NULL,
157};
158
159static struct glx_context *
160driwindows_create_context(struct glx_screen *base,
161                          struct glx_config *config_base,
162                          struct glx_context *shareList, int renderType)
163{
164   struct driwindows_context *pcp, *pcp_shared;
165   struct driwindows_config *config = (struct driwindows_config *) config_base;
166   struct driwindows_screen *psc = (struct driwindows_screen *) base;
167   windowsContext *shared = NULL;
168
169   if (!psc->base.driScreen)
170      return NULL;
171
172   /* Check the renderType value */
173   if (!validate_renderType_against_config(config_base, renderType))
174       return NULL;
175
176   if (shareList) {
177      /* If the shareList context is not on this renderer, we cannot possibly
178       * create a context that shares with it.
179       */
180      if (shareList->vtable->destroy != driwindows_destroy_context) {
181         return NULL;
182      }
183
184      pcp_shared = (struct driwindows_context *) shareList;
185      shared = pcp_shared->windowsContext;
186   }
187
188   pcp = calloc(1, sizeof *pcp);
189   if (pcp == NULL)
190      return NULL;
191
192   if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
193      free(pcp);
194      return NULL;
195   }
196
197   pcp->base.renderType = renderType;
198
199   InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi);
200
201   pcp->windowsContext = windows_create_context(config->pxfi, shared);
202
203   if (!pcp->windowsContext) {
204      free(pcp);
205      return NULL;
206   }
207
208   pcp->base.vtable = &driwindows_context_vtable;
209
210   return &pcp->base;
211}
212
213static struct glx_context *
214driwindows_create_context_attribs(struct glx_screen *base,
215                                  struct glx_config *config_base,
216                                  struct glx_context *shareList,
217                                  unsigned num_attribs,
218                                  const uint32_t *attribs,
219                                  unsigned *error)
220{
221   struct driwindows_context *pcp, *pcp_shared;
222   struct driwindows_config *config = (struct driwindows_config *) config_base;
223   struct driwindows_screen *psc = (struct driwindows_screen *) base;
224   windowsContext *shared = NULL;
225
226   int i;
227   uint32_t renderType = GLX_RGBA_TYPE;
228
229   /* Extract renderType from attribs */
230   for (i = 0; i < num_attribs; i++) {
231      switch (attribs[i * 2]) {
232      case GLX_RENDER_TYPE:
233         renderType = attribs[i * 2 + 1];
234         break;
235      }
236   }
237
238   /*
239     Perhaps we should map GLX tokens to WGL tokens, but they appear to have
240     identical values, so far
241   */
242
243   if (!psc->base.driScreen)
244      return NULL;
245
246   /* Check the renderType value */
247   if (!validate_renderType_against_config(config_base, renderType)) {
248       return NULL;
249   }
250
251   if (shareList) {
252      /* If the shareList context is not on this renderer, we cannot possibly
253       * create a context that shares with it.
254       */
255      if (shareList->vtable->destroy != driwindows_destroy_context) {
256         return NULL;
257      }
258
259      pcp_shared = (struct driwindows_context *) shareList;
260      shared = pcp_shared->windowsContext;
261   }
262
263   pcp = calloc(1, sizeof *pcp);
264   if (pcp == NULL)
265      return NULL;
266
267   if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
268      free(pcp);
269      return NULL;
270   }
271
272   pcp->base.renderType = renderType;
273
274   InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi);
275
276   pcp->windowsContext = windows_create_context_attribs(config->pxfi,
277                                                      shared,
278                                                      (const int *)attribs);
279   if (pcp->windowsContext == NULL) {
280      free(pcp);
281      return NULL;
282   }
283
284   pcp->base.vtable = &driwindows_context_vtable;
285
286   return &pcp->base;
287}
288
289static void
290driwindowsDestroyDrawable(__GLXDRIdrawable * pdraw)
291{
292   struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
293
294   windows_destroy_drawable(pdp->windowsDrawable);
295
296   free(pdp);
297}
298
299static __GLXDRIdrawable *
300driwindowsCreateDrawable(struct glx_screen *base, XID xDrawable,
301                         GLXDrawable drawable, struct glx_config *modes)
302{
303   struct driwindows_drawable *pdp;
304   struct driwindows_screen *psc = (struct driwindows_screen *) base;
305
306   pdp = calloc(1, sizeof(*pdp));
307   if (!pdp)
308      return NULL;
309
310   pdp->base.xDrawable = xDrawable;
311   pdp->base.drawable = drawable;
312   pdp->base.psc = &psc->base;
313
314   /*
315      By this stage, the X drawable already exists, but the GLX drawable may
316      not.
317
318      Query the server with the XID to find the correct HWND, HPBUFFERARB or
319      HBITMAP
320   */
321
322   unsigned int type;
323   void *handle;
324
325   if (!XWindowsDRIQueryDrawable(psc->base.dpy, base->scr, drawable, &type, &handle))
326   {
327      free(pdp);
328      return NULL;
329   }
330
331   /* No handle found is a failure */
332   if (!handle) {
333      free(pdp);
334      return NULL;
335   }
336
337   /* Create a new drawable */
338   pdp->windowsDrawable = windows_create_drawable(type, handle);
339
340   if (!pdp->windowsDrawable) {
341      free(pdp);
342      return NULL;
343   }
344
345   pdp->base.destroyDrawable = driwindowsDestroyDrawable;
346
347   return &pdp->base;
348}
349
350static int64_t
351driwindowsSwapBuffers(__GLXDRIdrawable * pdraw,
352                 int64_t target_msc, int64_t divisor, int64_t remainder,
353                 Bool flush)
354{
355   struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
356
357   (void) target_msc;
358   (void) divisor;
359   (void) remainder;
360
361   if (flush) {
362      glFlush();
363   }
364
365   windows_swap_buffers(pdp->windowsDrawable);
366
367   return 0;
368}
369
370static void
371driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw,
372                   int x, int y, int width, int height, Bool flush)
373{
374   struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
375
376   if (flush) {
377      glFlush();
378   }
379
380   windows_copy_subbuffer(pdp->windowsDrawable, x, y, width, height);
381}
382
383static void
384driwindowsDestroyScreen(struct glx_screen *base)
385{
386   struct driwindows_screen *psc = (struct driwindows_screen *) base;
387
388   /* Free the direct rendering per screen data */
389   psc->driScreen = NULL;
390   free(psc);
391}
392
393static const struct glx_screen_vtable driwindows_screen_vtable = {
394   .create_context         = driwindows_create_context,
395   .create_context_attribs = driwindows_create_context_attribs,
396   .query_renderer_integer = NULL,
397   .query_renderer_string  = NULL,
398};
399
400static Bool
401driwindowsBindExtensions(struct driwindows_screen *psc)
402{
403   Bool result = 1;
404
405   const struct
406   {
407      char *wglext;
408      char *glxext;
409      Bool mandatory;
410   } extensionMap[] = {
411      { "WGL_ARB_make_current_read", "GLX_SGI_make_current_read", 0 },
412      { "WGL_EXT_swap_control", "GLX_SGI_swap_control", 0 },
413      { "WGL_EXT_swap_control", "GLX_MESA_swap_control", 0 },
414//      { "WGL_ARB_render_texture", "GLX_EXT_texture_from_pixmap", 0 },
415// Not exactly equivalent, needs some more glue to be written
416      { "WGL_ARB_pbuffer", "GLX_SGIX_pbuffer", 1 },
417      { "WGL_ARB_multisample", "GLX_ARB_multisample", 1 },
418      { "WGL_ARB_multisample", "GLX_SGIS_multisample", 1 },
419      { "WGL_ARB_create_context", "GLX_ARB_create_context", 0 },
420      { "WGL_ARB_create_context_profile", "GLX_ARB_create_context_profile", 0 },
421      { "WGL_ARB_create_context_robustness", "GLX_ARB_create_context_robustness", 0 },
422      { "WGL_EXT_create_context_es2_profile", "GLX_EXT_create_context_es2_profile", 0 },
423   };
424
425   char *wgl_extensions;
426   char *gl_extensions;
427   int i;
428
429   windows_extensions(&gl_extensions, &wgl_extensions);
430
431   for (i = 0; i < ARRAY_SIZE(extensionMap); i++) {
432      if (strstr(wgl_extensions, extensionMap[i].wglext)) {
433          __glXEnableDirectExtension(&psc->base, extensionMap[i].glxext);
434          InfoMessageF("enabled %s\n", extensionMap[i].glxext);
435      }
436      else if (extensionMap[i].mandatory) {
437         ErrorMessageF("required WGL extension %s is missing\n", extensionMap[i].wglext);
438         result = 0;
439      }
440   }
441
442   /*
443       Because it pre-dates WGL_EXT_extensions_string, GL_WIN_swap_hint might
444       only be in GL_EXTENSIONS
445   */
446   if (strstr(gl_extensions, "GL_WIN_swap_hint")) {
447      psc->copySubBuffer = 1;
448      __glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer");
449      InfoMessageF("enabled GLX_MESA_copy_sub_buffer\n");
450   }
451
452   free(gl_extensions);
453   free(wgl_extensions);
454
455   return result;
456}
457
458static struct glx_config *
459driwindowsMapConfigs(struct glx_display *priv, int screen, struct glx_config *configs, struct glx_config *fbconfigs)
460{
461   struct glx_config head, *tail, *m;
462
463   tail = &head;
464   head.next = NULL;
465
466   for (m = configs; m; m = m->next) {
467      int fbconfigID = GLX_DONT_CARE;
468      if (fbconfigs) {
469         /*
470           visuals have fbconfigID of GLX_DONT_CARE, so search for a fbconfig
471           with matching visualID and get the fbconfigID from there
472         */
473         struct glx_config *f;
474         for (f = fbconfigs; f; f = f->next) {
475            if (f->visualID == m->visualID)
476               fbconfigID = f->fbconfigID;
477         }
478      }
479      else {
480         fbconfigID = m->fbconfigID;
481      }
482
483      int pxfi;
484      XWindowsDRIFBConfigToPixelFormat(priv->dpy, screen, fbconfigID, &pxfi);
485      if (pxfi == 0)
486         continue;
487
488      struct driwindows_config *config = malloc(sizeof(*config));
489
490      tail->next = &config->base;
491      if (tail->next == NULL)
492         continue;
493
494      config->base = *m;
495      config->pxfi = pxfi;
496
497      tail = tail->next;
498   }
499
500   return head.next;
501}
502
503static struct glx_screen *
504driwindowsCreateScreen(int screen, struct glx_display *priv)
505{
506   __GLXDRIscreen *psp;
507   struct driwindows_screen *psc;
508   struct glx_config *configs = NULL, *visuals = NULL;
509   int directCapable;
510
511   psc = calloc(1, sizeof *psc);
512   if (psc == NULL)
513      return NULL;
514
515   if (!glx_screen_init(&psc->base, screen, priv)) {
516      free(psc);
517      return NULL;
518   }
519
520   if (!XWindowsDRIQueryDirectRenderingCapable(psc->base.dpy, screen, &directCapable) ||
521       !directCapable) {
522      ErrorMessageF("Screen is not Windows-DRI capable\n");
523      goto handle_error;
524   }
525
526   /* discover native supported extensions */
527   if (!driwindowsBindExtensions(psc)) {
528      goto handle_error;
529   }
530
531   /* Augment configs with pxfi information */
532   configs = driwindowsMapConfigs(priv, screen, psc->base.configs, NULL);
533   visuals = driwindowsMapConfigs(priv, screen, psc->base.visuals, configs);
534
535   if (!configs || !visuals) {
536       ErrorMessageF("No fbConfigs or visuals found\n");
537       goto handle_error;
538   }
539
540   glx_config_destroy_list(psc->base.configs);
541   psc->base.configs = configs;
542   glx_config_destroy_list(psc->base.visuals);
543   psc->base.visuals = visuals;
544
545   psc->base.vtable = &driwindows_screen_vtable;
546   psp = &psc->vtable;
547   psc->base.driScreen = psp;
548   psp->destroyScreen = driwindowsDestroyScreen;
549   psp->createDrawable = driwindowsCreateDrawable;
550   psp->swapBuffers = driwindowsSwapBuffers;
551
552   if (psc->copySubBuffer)
553      psp->copySubBuffer = driwindowsCopySubBuffer;
554
555   return &psc->base;
556
557handle_error:
558   glx_screen_cleanup(&psc->base);
559
560   return NULL;
561}
562
563/* Called from __glXFreeDisplayPrivate.
564 */
565static void
566driwindowsDestroyDisplay(__GLXDRIdisplay * dpy)
567{
568   free(dpy);
569}
570
571/*
572 * Allocate, initialize and return a  __GLXDRIdisplay object.
573 * This is called from __glXInitialize() when we are given a new
574 * display pointer.
575 */
576_X_HIDDEN __GLXDRIdisplay *
577driwindowsCreateDisplay(Display * dpy)
578{
579   struct driwindows_display *pdpyp;
580
581   int eventBase, errorBase;
582   int major, minor, patch;
583
584   /* Verify server has Windows-DRI extension */
585   if (!XWindowsDRIQueryExtension(dpy, &eventBase, &errorBase)) {
586      ErrorMessageF("Windows-DRI extension not available\n");
587      return NULL;
588   }
589
590   if (!XWindowsDRIQueryVersion(dpy, &major, &minor, &patch)) {
591      ErrorMessageF("Fetching Windows-DRI extension version failed\n");
592      return NULL;
593   }
594
595   if (!windows_check_renderer()) {
596      ErrorMessageF("Windows-DRI extension disabled for GDI Generic renderer\n");
597      return NULL;
598   }
599
600   pdpyp = malloc(sizeof *pdpyp);
601   if (pdpyp == NULL)
602      return NULL;
603
604   pdpyp->base.destroyDisplay = driwindowsDestroyDisplay;
605   pdpyp->base.createScreen = driwindowsCreateScreen;
606
607   pdpyp->event_base = eventBase;
608
609   return &pdpyp->base;
610}
611