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