1 /*
2   Copyright 2012-2020 David Robillard <d@drobilla.net>
3 
4   Permission to use, copy, modify, and/or distribute this software for any
5   purpose with or without fee is hereby granted, provided that the above
6   copyright notice and this permission notice appear in all copies.
7 
8   THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 
17 #include "stub.h"
18 #include "types.h"
19 #include "x11.h"
20 
21 #include "pugl/gl.h"
22 #include "pugl/pugl.h"
23 
24 #include <GL/glx.h>
25 #include <X11/X.h>
26 #include <X11/Xlib.h>
27 
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 
32 typedef struct {
33   GLXFBConfig fb_config;
34   GLXContext  ctx;
35 } PuglX11GlSurface;
36 
37 static int
puglX11GlHintValue(const int value)38 puglX11GlHintValue(const int value)
39 {
40   return value == PUGL_DONT_CARE ? (int)GLX_DONT_CARE : value;
41 }
42 
43 static int
puglX11GlGetAttrib(Display * const display,GLXFBConfig fb_config,const int attrib)44 puglX11GlGetAttrib(Display* const display,
45                    GLXFBConfig    fb_config,
46                    const int      attrib)
47 {
48   int value = 0;
49   glXGetFBConfigAttrib(display, fb_config, attrib, &value);
50   return value;
51 }
52 
53 static PuglStatus
puglX11GlConfigure(PuglView * view)54 puglX11GlConfigure(PuglView* view)
55 {
56   PuglInternals* const impl    = view->impl;
57   const int            screen  = impl->screen;
58   Display* const       display = impl->display;
59 
60   PuglX11GlSurface* const surface =
61     (PuglX11GlSurface*)calloc(1, sizeof(PuglX11GlSurface));
62   impl->surface = surface;
63 
64   // clang-format off
65   const int attrs[] = {
66     GLX_X_RENDERABLE,  True,
67     GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
68     GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
69     GLX_RENDER_TYPE,   GLX_RGBA_BIT,
70     GLX_SAMPLES,       puglX11GlHintValue(view->hints[PUGL_SAMPLES]),
71     GLX_RED_SIZE,      puglX11GlHintValue(view->hints[PUGL_RED_BITS]),
72     GLX_GREEN_SIZE,    puglX11GlHintValue(view->hints[PUGL_GREEN_BITS]),
73     GLX_BLUE_SIZE,     puglX11GlHintValue(view->hints[PUGL_BLUE_BITS]),
74     GLX_ALPHA_SIZE,    puglX11GlHintValue(view->hints[PUGL_ALPHA_BITS]),
75     GLX_DEPTH_SIZE,    puglX11GlHintValue(view->hints[PUGL_DEPTH_BITS]),
76     GLX_STENCIL_SIZE,  puglX11GlHintValue(view->hints[PUGL_STENCIL_BITS]),
77     GLX_DOUBLEBUFFER,  puglX11GlHintValue(view->hints[PUGL_DOUBLE_BUFFER]),
78     None
79   };
80   // clang-format on
81 
82   int          n_fbc = 0;
83   GLXFBConfig* fbc   = glXChooseFBConfig(display, screen, attrs, &n_fbc);
84   if (n_fbc <= 0) {
85     return PUGL_CREATE_CONTEXT_FAILED;
86   }
87 
88   surface->fb_config = fbc[0];
89   impl->vi           = glXGetVisualFromFBConfig(impl->display, fbc[0]);
90 
91   view->hints[PUGL_RED_BITS] =
92     puglX11GlGetAttrib(display, fbc[0], GLX_RED_SIZE);
93   view->hints[PUGL_GREEN_BITS] =
94     puglX11GlGetAttrib(display, fbc[0], GLX_GREEN_SIZE);
95   view->hints[PUGL_BLUE_BITS] =
96     puglX11GlGetAttrib(display, fbc[0], GLX_BLUE_SIZE);
97   view->hints[PUGL_ALPHA_BITS] =
98     puglX11GlGetAttrib(display, fbc[0], GLX_ALPHA_SIZE);
99   view->hints[PUGL_DEPTH_BITS] =
100     puglX11GlGetAttrib(display, fbc[0], GLX_DEPTH_SIZE);
101   view->hints[PUGL_STENCIL_BITS] =
102     puglX11GlGetAttrib(display, fbc[0], GLX_STENCIL_SIZE);
103   view->hints[PUGL_SAMPLES] = puglX11GlGetAttrib(display, fbc[0], GLX_SAMPLES);
104   view->hints[PUGL_DOUBLE_BUFFER] =
105     puglX11GlGetAttrib(display, fbc[0], GLX_DOUBLEBUFFER);
106 
107   XFree(fbc);
108 
109   return PUGL_SUCCESS;
110 }
111 
112 static PuglStatus
puglX11GlEnter(PuglView * view,const PuglEventExpose * PUGL_UNUSED (expose))113 puglX11GlEnter(PuglView* view, const PuglEventExpose* PUGL_UNUSED(expose))
114 {
115   PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
116   glXMakeCurrent(view->impl->display, view->impl->win, surface->ctx);
117   return PUGL_SUCCESS;
118 }
119 
120 static PuglStatus
puglX11GlLeave(PuglView * view,const PuglEventExpose * expose)121 puglX11GlLeave(PuglView* view, const PuglEventExpose* expose)
122 {
123   if (expose && view->hints[PUGL_DOUBLE_BUFFER]) {
124     glXSwapBuffers(view->impl->display, view->impl->win);
125   }
126 
127   glXMakeCurrent(view->impl->display, None, NULL);
128 
129   return PUGL_SUCCESS;
130 }
131 
132 static PuglStatus
puglX11GlCreate(PuglView * view)133 puglX11GlCreate(PuglView* view)
134 {
135   PuglInternals* const    impl      = view->impl;
136   PuglX11GlSurface* const surface   = (PuglX11GlSurface*)impl->surface;
137   Display* const          display   = impl->display;
138   GLXFBConfig             fb_config = surface->fb_config;
139 
140   const int ctx_attrs[] = {
141     GLX_CONTEXT_MAJOR_VERSION_ARB,
142     view->hints[PUGL_CONTEXT_VERSION_MAJOR],
143 
144     GLX_CONTEXT_MINOR_VERSION_ARB,
145     view->hints[PUGL_CONTEXT_VERSION_MINOR],
146 
147     GLX_CONTEXT_FLAGS_ARB,
148     (view->hints[PUGL_USE_DEBUG_CONTEXT] ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
149 
150     GLX_CONTEXT_PROFILE_MASK_ARB,
151     (view->hints[PUGL_USE_COMPAT_PROFILE]
152        ? GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
153        : GLX_CONTEXT_CORE_PROFILE_BIT_ARB),
154     0};
155 
156   PFNGLXCREATECONTEXTATTRIBSARBPROC create_context =
157     (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress(
158       (const uint8_t*)"glXCreateContextAttribsARB");
159 
160   PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT =
161     (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress(
162       (const uint8_t*)"glXSwapIntervalEXT");
163 
164   surface->ctx = create_context(display, fb_config, 0, True, ctx_attrs);
165   if (!surface->ctx) {
166     surface->ctx =
167       glXCreateNewContext(display, fb_config, GLX_RGBA_TYPE, 0, True);
168   }
169 
170   if (!surface->ctx) {
171     return PUGL_CREATE_CONTEXT_FAILED;
172   }
173 
174   const int swapInterval = view->hints[PUGL_SWAP_INTERVAL];
175   if (glXSwapIntervalEXT && swapInterval != PUGL_DONT_CARE) {
176     puglX11GlEnter(view, NULL);
177     glXSwapIntervalEXT(display, impl->win, swapInterval);
178     puglX11GlLeave(view, NULL);
179   }
180 
181   glXGetConfig(impl->display,
182                impl->vi,
183                GLX_DOUBLEBUFFER,
184                &view->hints[PUGL_DOUBLE_BUFFER]);
185 
186   glXQueryDrawable(display,
187                    impl->win,
188                    GLX_SWAP_INTERVAL_EXT,
189                    (unsigned int*)&view->hints[PUGL_SWAP_INTERVAL]);
190 
191   return PUGL_SUCCESS;
192 }
193 
194 static PuglStatus
puglX11GlDestroy(PuglView * view)195 puglX11GlDestroy(PuglView* view)
196 {
197   PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
198   if (surface) {
199     glXDestroyContext(view->impl->display, surface->ctx);
200     free(surface);
201     view->impl->surface = NULL;
202   }
203   return PUGL_SUCCESS;
204 }
205 
206 PuglGlFunc
puglGetProcAddress(const char * name)207 puglGetProcAddress(const char* name)
208 {
209   return glXGetProcAddress((const uint8_t*)name);
210 }
211 
212 PuglStatus
puglEnterContext(PuglView * view)213 puglEnterContext(PuglView* view)
214 {
215   return view->backend->enter(view, NULL);
216 }
217 
218 PuglStatus
puglLeaveContext(PuglView * view)219 puglLeaveContext(PuglView* view)
220 {
221   return view->backend->leave(view, NULL);
222 }
223 
224 const PuglBackend*
puglGlBackend(void)225 puglGlBackend(void)
226 {
227   static const PuglBackend backend = {puglX11GlConfigure,
228                                       puglX11GlCreate,
229                                       puglX11GlDestroy,
230                                       puglX11GlEnter,
231                                       puglX11GlLeave,
232                                       puglStubGetContext};
233 
234   return &backend;
235 }
236