1 /*
2  * Copyright (C) 2007-2020 Kim Woelders
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies of the Software, its documentation and marketing & publicity
13  * materials, and acknowledgment shall be given in the documentation, materials
14  * and software packages that this Software was used.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 #include "config.h"
24 
25 #include <stdlib.h>
26 #define GLX_GLXEXT_PROTOTYPES 1
27 #include <GL/gl.h>
28 #include <GL/glu.h>
29 #include <GL/glx.h>
30 #include <X11/extensions/Xcomposite.h>
31 #include <X11/extensions/Xrender.h>
32 
33 #include "E.h"
34 #include "eglx.h"
35 #include "eimage.h"
36 #include "xwin.h"
37 
38 #define ENABLE_DEBUG   1
39 #if ENABLE_DEBUG
40 #define Dprintf(fmt...)  do { if(EDebug(EDBUG_TYPE_GLX))Eprintf(fmt); } while(0)
41 #define D2printf(fmt...) do { if(EDebug(EDBUG_TYPE_GLX)>1)Eprintf(fmt); } while(0)
42 #else
43 #define Dprintf(fmt...)
44 #define D2printf(fmt...)
45 #endif /* ENABLE_DEBUG */
46 
47 #define ETEX_TYPE_IMAGE      1
48 #define ETEX_TYPE_PIXMAP     2
49 
50 #if 1
51 #define TEXTURE_TARGET GL_TEXTURE_2D
52 #else
53 #define TEXTURE_TARGET GLX_TEXTURE_RECTANGLE_EXT
54 #endif
55 
56 #ifdef HAVE_GLX_glXBindTexImageEXT
57 
58 #define _glXBindTexImageEXT    glXBindTexImageEXT
59 #define _glXReleaseTexImageEXT glXReleaseTexImageEXT
60 
61 #else
62 
63 #include <dlfcn.h>
64 
65 /* GL functions and helper */
66 typedef void        (*glXBindTexImageEXT_func)(Display * dpy,
67 					       GLXDrawable drawable,
68 					       int buffer,
69 					       const int *attrib_list);
70 typedef void        (*glXReleaseTexImageEXT_func)(Display * dpy,
71 						  GLXDrawable drawable,
72 						  int buffer);
73 typedef void        (*glXFuncPtr)(void);
74 typedef             glXFuncPtr(*glXGetProcAddress_func) (const GLubyte *);
75 
76 static glXBindTexImageEXT_func _glXBindTexImageEXT;
77 static glXReleaseTexImageEXT_func _glXReleaseTexImageEXT;
78 static glXGetProcAddress_func glx_get_proc_address;
79 
80 static              glXFuncPtr
get_func_addr(const char * name)81 get_func_addr(const char *name)
82 {
83    glXFuncPtr          ret = NULL;
84 
85    if (glx_get_proc_address)
86       ret = glx_get_proc_address((const GLubyte *)name);
87    if (!ret)
88       ret = (glXFuncPtr) dlsym(RTLD_DEFAULT, name);
89 
90    return ret;
91 }
92 
93 static int
glx_funcs_init(void)94 glx_funcs_init(void)
95 {
96    glx_get_proc_address = (glXGetProcAddress_func)
97       get_func_addr("glXGetProcAddress");
98    if (!glx_get_proc_address)
99       glx_get_proc_address = (glXGetProcAddress_func)
100 	 get_func_addr("glXGetProcAddressARB");
101 
102    _glXBindTexImageEXT = (glXBindTexImageEXT_func)
103       get_func_addr("glXBindTexImageEXT");
104 
105    _glXReleaseTexImageEXT = (glXReleaseTexImageEXT_func)
106       get_func_addr("glXReleaseTexImageEXT");
107 
108    return !_glXBindTexImageEXT || !_glXReleaseTexImageEXT;
109 }
110 
111 #endif /* HAVE_GLX_glXBindTexImageEXT */
112 
113 static void         EobjTexturesFree(void);
114 
115 typedef struct {
116    XVisualInfo        *vi;
117    GLXContext          ctx;
118    GLXFBConfig         fbc;
119    unsigned            ctx_initialised:1;
120 } EGlContext;
121 
122 static EGlContext   egl;
123 
124 #define FBCATTR(fbc, attr, want) _EGlFbcAttrib(fbc, #attr, attr, want)
125 static int
_EGlFbcAttrib(GLXFBConfig fbc,const char * name,int attr,int want)126 _EGlFbcAttrib(GLXFBConfig fbc, const char *name, int attr, int want)
127 {
128    int                 err, value;
129 
130    value = 0xabbabeef;
131    err = glXGetFBConfigAttrib(disp, fbc, attr, &value);
132    if (err)
133       Eprintf("  %s *** Error %d ***\n", name, err);
134    else if (want > 0)
135       D2printf("  %s=%#x (want %#x)\n", name, value, want);
136    else
137       D2printf("  %s=%#x\n", name, value);
138 
139    return value;
140 }
141 
142 int
EGlInit(void)143 EGlInit(void)
144 {
145 /* From NV's README.txt (AddARGBGLXVisuals) */
146    static const int    attrs[] = {
147       GLX_VISUAL_CAVEAT_EXT, GLX_NONE_EXT,
148       GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT,
149       GLX_RENDER_TYPE, GLX_RGBA_BIT,
150       GLX_RED_SIZE, 1,
151       GLX_GREEN_SIZE, 1,
152       GLX_BLUE_SIZE, 1,
153       GLX_ALPHA_SIZE, 1,
154       GLX_DOUBLEBUFFER, True,
155       GLX_DEPTH_SIZE, 1,
156       0
157    };
158    XVisualInfo        *vi;
159    GLXFBConfig        *fbc;
160    int                 i, ix, num;
161    int                 value;
162    char               *s;
163    XID                 vid = NoXID;
164    XRenderPictFormat  *pictFormat;
165 
166    Dprintf("EGlInit\n");
167 
168    memset(&egl, 0, sizeof(EGlContext));
169 
170    s = getenv("EVISUAL");
171    if (s)
172      {
173 	vid = strtoul(s, NULL, 0);
174 	Eprintf("Want Visual Id=%#lx\n", vid);
175      }
176 
177    /* Create a GLX context */
178    fbc = glXChooseFBConfig(disp, DefaultScreen(disp), attrs, &num);
179    if (!fbc)
180      {
181 	Eprintf("No FB configs\n");
182 	return -1;
183      }
184 
185    D2printf("Visuals found: %d\n", num);
186    ix = -1;
187    for (i = 0; i < num; i++)
188      {
189 	vi = glXGetVisualFromFBConfig(disp, fbc[i]);
190 	if (!vi)
191 	   continue;
192 
193 	D2printf("Checking Visual ID=%#lx depth=%d\n", vi->visualid, vi->depth);
194 	if (vid && vi->visualid != vid)
195 	   continue;
196 
197 #if 1
198 	value = FBCATTR(fbc[i], GLX_FBCONFIG_ID, -1);
199 	value = FBCATTR(fbc[i], GLX_CONFIG_CAVEAT, GLX_NONE);
200 	value = FBCATTR(fbc[i], GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT);
201 	value = FBCATTR(fbc[i], GLX_RENDER_TYPE, -1);
202 	value = FBCATTR(fbc[i], GLX_X_VISUAL_TYPE, -1);
203 	value = FBCATTR(fbc[i], GLX_X_RENDERABLE, -1);
204 	value = FBCATTR(fbc[i], GLX_BUFFER_SIZE, -1);
205 	value = FBCATTR(fbc[i], GLX_LEVEL, -1);
206 	value = FBCATTR(fbc[i], GLX_TRANSPARENT_TYPE, -1);
207 #endif
208 
209 #if 1
210 	value = FBCATTR(fbc[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, 1);
211 	value = FBCATTR(fbc[i], GLX_BIND_TO_TEXTURE_RGB_EXT, 1);
212 	if (!value)
213 	   continue;
214 	value = FBCATTR(fbc[i], GLX_BIND_TO_MIPMAP_TEXTURE_EXT, -1);
215 	value = FBCATTR(fbc[i], GLX_BIND_TO_TEXTURE_TARGETS_EXT,
216 			GLX_TEXTURE_2D_BIT_EXT);
217 #if 0
218 	if (!(value & GLX_TEXTURE_2D_BIT_EXT))
219 	   continue;
220 #endif
221 	value = FBCATTR(fbc[i], GLX_Y_INVERTED_EXT, -1);
222 #endif
223 
224 #if 1
225 	/* We want an ARGB visual */
226 	pictFormat = XRenderFindVisualFormat(disp, vi->visual);
227 	if (!pictFormat)
228 	   continue;
229 	if (pictFormat->direct.alphaMask == 0)
230 	   continue;
231 #endif
232 
233 	D2printf(" - passed\n");
234 	if (ix < 0)
235 	   ix = i;
236 
237 	XFree(vi);
238      }
239 
240    if (ix >= 0)
241       egl.fbc = fbc[ix];
242    XFree(fbc);
243 
244    if (ix < 0)
245      {
246 	Eprintf("No FB config match\n");
247 	return -1;
248      }
249 
250 #ifndef HAVE_GLX_glXBindTexImageEXT
251    if (glx_funcs_init())
252      {
253 	Eprintf("glXBindTexImageEXT or glXReleaseTexImageEXT not available\n");
254 	return -1;
255      }
256 #endif
257 
258    egl.vi = glXGetVisualFromFBConfig(disp, egl.fbc);
259 
260    egl.ctx = glXCreateNewContext(disp, egl.fbc, GLX_RGBA_TYPE, NULL, True);
261 
262    Dprintf("Direct Rendering %s\n",
263 	   glXIsDirect(disp, egl.ctx) ? "enabled" : "not available");
264    Dprintf("Visual ID=%#lx  depth %d\n", egl.vi->visualid, egl.vi->depth);
265 
266    return 0;
267 }
268 
269 void
EGlExit(void)270 EGlExit(void)
271 {
272    Dprintf("EGlExit\n");
273 
274    EobjTexturesFree();
275 
276    if (egl.vi)
277      {
278 	XFree(egl.vi);
279 	egl.vi = NULL;
280      }
281 
282    if (egl.ctx)
283      {
284 	EGlWindowDisconnect();
285 	glXDestroyContext(disp, egl.ctx);
286 	egl.ctx = NULL;
287      }
288 }
289 
290 Visual             *
EGlGetVisual(void)291 EGlGetVisual(void)
292 {
293    if (!egl.vi)
294       EGlInit();
295    return (egl.vi) ? egl.vi->visual : NULL;
296 }
297 
298 unsigned int
EGlGetDepth(void)299 EGlGetDepth(void)
300 {
301    if (!egl.vi)
302       EGlInit();
303    return (egl.vi) ? egl.vi->depth : 0;
304 }
305 
306 void
EGlWindowConnect(EX_Window xwin)307 EGlWindowConnect(EX_Window xwin)
308 {
309    glXMakeContextCurrent(disp, xwin, xwin, egl.ctx);
310 
311    if (egl.ctx_initialised)
312       return;
313 
314    /* First time */
315    glEnable(TEXTURE_TARGET);
316 
317    glShadeModel(GL_SMOOTH);
318    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
319    glClearDepth(1.0f);
320    glEnable(GL_DEPTH_TEST);
321    glDepthFunc(GL_LEQUAL);
322    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
323 
324    egl.ctx_initialised = 1;
325 }
326 
327 void
EGlWindowDisconnect(void)328 EGlWindowDisconnect(void)
329 {
330    if (!glXMakeContextCurrent(disp, NoXID, NoXID, NULL))
331      {
332 	Eprintf("Failed to release GL context.\n");
333      }
334 }
335 
336 ETexture           *
EGlTextureFromImage(EImage * im,int mode)337 EGlTextureFromImage(EImage * im, int mode)
338 {
339    ETexture           *et;
340    int                 w, h;
341    unsigned char      *data;
342 
343    if (!im)
344       return NULL;
345 
346    et = ECALLOC(ETexture, 1);
347    if (!et)
348       return NULL;
349 
350    et->type = ETEX_TYPE_IMAGE;
351    et->target = TEXTURE_TARGET;
352    glGenTextures(1, &et->texture);
353    glBindTexture(et->target, et->texture);
354 
355    EImageGetSize(im, &w, &h);
356    data = (unsigned char *)EImageGetData(im);
357 
358    switch (mode)
359      {
360      case 0:			/* No filtering */
361 	glTexImage2D(et->target, 0, GL_RGB8, w, h, 0, GL_BGRA,
362 		     GL_UNSIGNED_BYTE, data);
363 	glTexParameteri(et->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
364 	glTexParameteri(et->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
365 	break;
366      case 1:			/* Linear filtering */
367 	glTexImage2D(et->target, 0, GL_RGB8, w, h, 0, GL_BGRA,
368 		     GL_UNSIGNED_BYTE, data);
369 	glTexParameteri(et->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
370 	glTexParameteri(et->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
371 	break;
372      case 2:			/* Mipmapping */
373 	gluBuild2DMipmaps(et->target, GL_RGB8, w, h, GL_BGRA,
374 			  GL_UNSIGNED_BYTE, data);
375 	glTexParameteri(et->target, GL_TEXTURE_MIN_FILTER,
376 			GL_LINEAR_MIPMAP_NEAREST);
377 	glTexParameteri(et->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
378 	break;
379      }
380 
381    return et;
382 }
383 
384 static              GLXPixmap
GetGlPixmap(EX_Window xwin,EX_Drawable draw)385 GetGlPixmap(EX_Window xwin, EX_Drawable draw)
386 {
387    static const int    attrs[] = {
388       GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
389       GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT,
390       0
391    };
392    EX_Pixmap           pixmap;
393    GLXPixmap           glxpixmap;
394 
395    if (xwin == NoXID && draw == NoXID)
396       return 0;
397 
398    pixmap = (draw) ? draw : XCompositeNameWindowPixmap(disp, xwin);
399    glxpixmap = glXCreatePixmap(disp, egl.fbc, pixmap, attrs);
400    Dprintf("GetGlPixmap: Window=%#x Drawable=%#x glxpixmap=%#lx\n",
401 	   xwin, draw, glxpixmap);
402 
403    return glxpixmap;
404 }
405 
406 static void
_EGlTextureFromDrawable(ETexture * et,EX_Drawable draw,int mode)407 _EGlTextureFromDrawable(ETexture * et, EX_Drawable draw, int mode)
408 {
409    if (!et || draw == NoXID)
410       return;
411 
412    glBindTexture(et->target, et->texture);
413    et->glxpmap = GetGlPixmap(draw, (mode & 0x100) ? NoXID : draw);
414    if (et->glxpmap == NoXID)
415       return;
416 
417    _glXBindTexImageEXT(disp, et->glxpmap, GLX_FRONT_LEFT_EXT, NULL);
418 #if 0				/* No! */
419    glXDestroyPixmap(disp, et->glxpmap);
420 #endif
421 }
422 
423 ETexture           *
EGlTextureFromDrawable(EX_Drawable draw,int mode)424 EGlTextureFromDrawable(EX_Drawable draw, int mode)
425 {
426    ETexture           *et;
427 
428    if (draw == NoXID)
429       return NULL;
430 
431    et = ECALLOC(ETexture, 1);
432    if (!et)
433       return NULL;
434 
435    et->type = ETEX_TYPE_PIXMAP;
436    et->target = TEXTURE_TARGET;
437    glGenTextures(1, &et->texture);
438    glBindTexture(et->target, et->texture);
439 
440    _EGlTextureFromDrawable(et, draw, mode);
441 
442    return et;
443 }
444 
445 void
EGlTextureDestroy(ETexture * et)446 EGlTextureDestroy(ETexture * et)
447 {
448    if (!et)
449       return;
450 
451    Dprintf("EGlTextureDestroy %d type=%u pmap=%#x\n", et->texture, et->type,
452 	   et->glxpmap);
453 
454    EGlTextureInvalidate(et);
455    glDeleteTextures(1, &et->texture);
456    Efree(et);
457 }
458 
459 void
EGlTextureInvalidate(ETexture * et)460 EGlTextureInvalidate(ETexture * et)
461 {
462    if (!et)
463       return;
464 
465    Dprintf("EGlTextureInvalidate %d type=%u pmap=%#x\n", et->texture, et->type,
466 	   et->glxpmap);
467 
468    switch (et->type)
469      {
470      case ETEX_TYPE_IMAGE:
471 	break;
472      case ETEX_TYPE_PIXMAP:
473 	if (!et->glxpmap)
474 	   break;
475 	_glXReleaseTexImageEXT(disp, et->glxpmap, GLX_FRONT_LEFT_EXT);
476 	glXDestroyPixmap(disp, et->glxpmap);
477 	et->glxpmap = NoXID;
478 	break;
479      }
480 }
481 
482 #include "eobj.h"
483 
484 static void
EobjTexturesFree(void)485 EobjTexturesFree(void)
486 {
487    int                 i, num;
488    EObj               *const *eol;
489 
490    eol = EobjListStackGet(&num);
491    for (i = 0; i < num; i++)
492       EobjTextureDestroy(eol[i]);
493 }
494 
495 ETexture           *
EobjGetTexture(EObj * eo)496 EobjGetTexture(EObj * eo)
497 {
498    if (eo->glhook)
499      {
500 	if (eo->glhook->glxpmap == NoXID)
501 	   _EGlTextureFromDrawable(eo->glhook, EobjGetPixmap(eo), 0);
502      }
503    else
504      {
505 	EobjTextureCreate(eo);
506      }
507 
508    return eo->glhook;
509 }
510 
511 void
EobjTextureCreate(EObj * eo)512 EobjTextureCreate(EObj * eo)
513 {
514    EX_Pixmap           pmap;
515 
516    pmap = EobjGetPixmap(eo);
517    if (pmap == NoXID)
518       return;
519 
520    eo->glhook = EGlTextureFromDrawable(pmap, 0);
521 }
522 
523 void
EobjTextureDestroy(EObj * eo)524 EobjTextureDestroy(EObj * eo)
525 {
526    EGlTextureDestroy(eo->glhook);
527    eo->glhook = NULL;
528 }
529 
530 void
EobjTextureInvalidate(EObj * eo)531 EobjTextureInvalidate(EObj * eo)
532 {
533    EGlTextureInvalidate(eo->glhook);
534 }
535