1 // License: CC0 / Public Domain
2 
3 #if defined(USE_GL) + defined(USE_VK) + defined(USE_D3D11) != 1
4 #error Specify exactly one of -DUSE_GL, -DUSE_VK or -DUSE_D3D11 when compiling!
5 #endif
6 
7 #include <string.h>
8 
9 #include "common.h"
10 #include "window.h"
11 
12 #ifdef USE_VK
13 #define VK_NO_PROTOTYPES
14 #include <libplacebo/vulkan.h>
15 #define GLFW_INCLUDE_VULKAN
16 #define IMPL win_impl_glfw_vk
17 #define IMPL_NAME "GLFW (vulkan)"
18 #endif
19 
20 #ifdef USE_GL
21 #include <libplacebo/opengl.h>
22 #define IMPL win_impl_glfw_gl
23 #define IMPL_NAME "GLFW (opengl)"
24 #endif
25 
26 #ifdef USE_D3D11
27 #include <libplacebo/d3d11.h>
28 #define IMPL win_impl_glfw_d3d11
29 #define IMPL_NAME "GLFW (D3D11)"
30 #endif
31 
32 #include <GLFW/glfw3.h>
33 
34 #ifdef USE_D3D11
35 #define GLFW_EXPOSE_NATIVE_WIN32
36 #include <GLFW/glfw3native.h>
37 #endif
38 
39 #ifdef NDEBUG
40 #define DEBUG false
41 #else
42 #define DEBUG true
43 #endif
44 
45 const struct window_impl IMPL;
46 
47 struct priv {
48     struct window w;
49     GLFWwindow *win;
50 
51 #ifdef USE_VK
52     VkSurfaceKHR surf;
53     pl_vulkan vk;
54     pl_vk_inst vk_inst;
55 #endif
56 
57 #ifdef USE_GL
58     pl_opengl gl;
59 #endif
60 
61 #ifdef USE_D3D11
62     pl_d3d11 d3d11;
63 #endif
64 
65     float scroll_dx, scroll_dy;
66     char **files;
67     size_t files_num;
68     size_t files_size;
69     bool file_seen;
70 };
71 
err_cb(int code,const char * desc)72 static void err_cb(int code, const char *desc)
73 {
74     fprintf(stderr, "GLFW err %d: %s\n", code, desc);
75 }
76 
close_cb(GLFWwindow * win)77 static void close_cb(GLFWwindow *win)
78 {
79     struct priv *p = glfwGetWindowUserPointer(win);
80     p->w.window_lost = true;
81 }
82 
resize_cb(GLFWwindow * win,int width,int height)83 static void resize_cb(GLFWwindow *win, int width, int height)
84 {
85     struct priv *p = glfwGetWindowUserPointer(win);
86     if (!pl_swapchain_resize(p->w.swapchain, &width, &height)) {
87         fprintf(stderr, "libplacebo: Failed resizing swapchain? Exiting...\n");
88         p->w.window_lost = true;
89     }
90 }
91 
scroll_cb(GLFWwindow * win,double dx,double dy)92 static void scroll_cb(GLFWwindow *win, double dx, double dy)
93 {
94     struct priv *p = glfwGetWindowUserPointer(win);
95     p->scroll_dx += dx;
96     p->scroll_dy += dy;
97 }
98 
drop_cb(GLFWwindow * win,int num,const char * files[])99 static void drop_cb(GLFWwindow *win, int num, const char *files[])
100 {
101     struct priv *p = glfwGetWindowUserPointer(win);
102 
103     for (int i = 0; i < num; i++) {
104         if (p->files_num == p->files_size) {
105             size_t new_size = p->files_size ? p->files_size * 2 : 16;
106             char **new_files = realloc(p->files, new_size * sizeof(char *));
107             if (!new_files)
108                 return;
109             p->files = new_files;
110             p->files_size = new_size;
111         }
112 
113         char *file = strdup(files[i]);
114         if (!file)
115             return;
116 
117         p->files[p->files_num++] = file;
118     }
119 }
120 
121 #ifdef USE_GL
make_current(void * priv)122 static bool make_current(void *priv)
123 {
124     GLFWwindow *win = priv;
125     glfwMakeContextCurrent(win);
126     return true;
127 }
128 
release_current(void * priv)129 static void release_current(void *priv)
130 {
131     glfwMakeContextCurrent(NULL);
132 }
133 #endif
134 
135 #ifdef USE_VK
get_vk_proc_addr(VkInstance instance,const char * pName)136 static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_vk_proc_addr(VkInstance instance, const char* pName)
137 {
138     return (PFN_vkVoidFunction) glfwGetInstanceProcAddress(instance, pName);
139 }
140 #endif
141 
glfw_create(pl_log log,const struct window_params * params)142 static struct window *glfw_create(pl_log log, const struct window_params *params)
143 {
144     struct priv *p = calloc(1, sizeof(struct priv));
145     if (!p)
146         return NULL;
147 
148     p->w.impl = &IMPL;
149     if (!glfwInit()) {
150         fprintf(stderr, "GLFW: Failed initializing?\n");
151         goto error;
152     }
153 
154     glfwSetErrorCallback(&err_cb);
155 
156 #ifdef USE_VK
157     if (!glfwVulkanSupported()) {
158         fprintf(stderr, "GLFW: No vulkan support! Perhaps recompile with -DUSE_GL\n");
159         goto error;
160     }
161 
162     glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
163 #endif // USE_VK
164 
165 #ifdef USE_D3D11
166     glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
167 #endif // USE_D3D11
168 
169 #ifdef USE_GL
170     glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
171 
172     // Request OpenGL 3.2 (or higher) core profile
173     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
174     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
175     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
176     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
177 #endif // USE_GL
178 
179     if (params->alpha)
180         glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
181 
182     printf("Creating %dx%d window%s...\n", params->width, params->height,
183            params->alpha ? " (with alpha)" : "");
184 
185     p->win = glfwCreateWindow(params->width, params->height, params->title, NULL, NULL);
186     if (!p->win) {
187         fprintf(stderr, "GLFW: Failed creating window\n");
188         goto error;
189     }
190 
191     // Set up GLFW event callbacks
192     glfwSetWindowUserPointer(p->win, p);
193     glfwSetFramebufferSizeCallback(p->win, resize_cb);
194     glfwSetWindowCloseCallback(p->win, close_cb);
195     glfwSetScrollCallback(p->win, scroll_cb);
196     glfwSetDropCallback(p->win, drop_cb);
197 
198 #ifdef USE_VK
199     VkResult err;
200 
201     struct pl_vk_inst_params iparams = pl_vk_inst_default_params;
202     iparams.get_proc_addr = get_vk_proc_addr,
203     iparams.debug = DEBUG;
204 
205     // Load all extensions required for WSI
206     uint32_t num;
207     iparams.extensions = glfwGetRequiredInstanceExtensions(&num);
208     iparams.num_extensions = num;
209 
210     p->vk_inst = pl_vk_inst_create(log, &iparams);
211     if (!p->vk_inst) {
212         fprintf(stderr, "libplacebo: Failed creating vulkan instance\n");
213         goto error;
214     }
215 
216     err = glfwCreateWindowSurface(p->vk_inst->instance, p->win, NULL, &p->surf);
217     if (err != VK_SUCCESS) {
218         fprintf(stderr, "GLFW: Failed creating vulkan surface\n");
219         goto error;
220     }
221 
222     struct pl_vulkan_params vkparams = pl_vulkan_default_params;
223     vkparams.instance = p->vk_inst->instance;
224     vkparams.get_proc_addr = p->vk_inst->get_proc_addr;
225     vkparams.surface = p->surf;
226     vkparams.allow_software = true;
227     p->vk = pl_vulkan_create(log, &vkparams);
228     if (!p->vk) {
229         fprintf(stderr, "libplacebo: Failed creating vulkan device\n");
230         goto error;
231     }
232 
233     p->w.swapchain = pl_vulkan_create_swapchain(p->vk, &(struct pl_vulkan_swapchain_params) {
234         .surface = p->surf,
235         .present_mode = VK_PRESENT_MODE_FIFO_KHR,
236     });
237 
238     if (!p->w.swapchain) {
239         fprintf(stderr, "libplacebo: Failed creating vulkan swapchain\n");
240         goto error;
241     }
242 
243     p->w.gpu = p->vk->gpu;
244 #endif // USE_VK
245 
246 #ifdef USE_GL
247     struct pl_opengl_params glparams = pl_opengl_default_params;
248     glparams.allow_software = true;
249     glparams.debug = DEBUG;
250     glparams.make_current = make_current;
251     glparams.release_current = release_current;
252     glparams.priv = p->win;
253 
254     p->gl = pl_opengl_create(log, &glparams);
255     if (!p->gl) {
256         fprintf(stderr, "libplacebo: Failed creating opengl device\n");
257         goto error;
258     }
259 
260     p->w.swapchain = pl_opengl_create_swapchain(p->gl, &(struct pl_opengl_swapchain_params) {
261         .swap_buffers = (void (*)(void *)) glfwSwapBuffers,
262         .priv = p->win,
263     });
264 
265     if (!p->w.swapchain) {
266         fprintf(stderr, "libplacebo: Failed creating opengl swapchain\n");
267         goto error;
268     }
269 
270     p->w.gpu = p->gl->gpu;
271 #endif // USE_GL
272 
273 #ifdef USE_D3D11
274     struct pl_d3d11_params d3dparams = pl_d3d11_default_params;
275     d3dparams.debug = DEBUG;
276 
277     p->d3d11 = pl_d3d11_create(log, &d3dparams);
278     if (!p->d3d11) {
279         fprintf(stderr, "libplacebo: Failed creating D3D11 device\n");
280         goto error;
281     }
282 
283     p->w.swapchain = pl_d3d11_create_swapchain(p->d3d11,
284                                                &(struct pl_d3d11_swapchain_params) {
285         .window = glfwGetWin32Window(p->win),
286     });
287     if (!p->w.swapchain) {
288         fprintf(stderr, "libplacebo: Failed creating D3D11 swapchain\n");
289         goto error;
290     }
291 
292     p->w.gpu = p->d3d11->gpu;
293 #endif // USE_D3D11
294 
295     int w = params->width, h = params->height;
296     pl_swapchain_colorspace_hint(p->w.swapchain, &params->colors);
297     if (!pl_swapchain_resize(p->w.swapchain, &w, &h)) {
298         fprintf(stderr, "libplacebo: Failed initializing swapchain\n");
299         goto error;
300     }
301 
302     return &p->w;
303 
304 error:
305     window_destroy((struct window **) &p);
306     return NULL;
307 }
308 
glfw_destroy(struct window ** window)309 static void glfw_destroy(struct window **window)
310 {
311     struct priv *p = (struct priv *) *window;
312     if (!p)
313         return;
314 
315     pl_swapchain_destroy(&p->w.swapchain);
316 
317 #ifdef USE_VK
318     pl_vulkan_destroy(&p->vk);
319     if (p->surf) {
320         PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)
321             p->vk_inst->get_proc_addr(p->vk_inst->instance, "vkDestroySurfaceKHR");
322         vkDestroySurfaceKHR(p->vk_inst->instance, p->surf, NULL);
323     }
324     pl_vk_inst_destroy(&p->vk_inst);
325 #endif
326 
327 #ifdef USE_GL
328     pl_opengl_destroy(&p->gl);
329 #endif
330 
331 #ifdef USE_D3D11
332     pl_d3d11_destroy(&p->d3d11);
333 #endif
334 
335     for (int i = 0; i < p->files_num; i++)
336         free(p->files[i]);
337     free(p->files);
338 
339     glfwTerminate();
340     free(p);
341     *window = NULL;
342 }
343 
glfw_poll(struct window * window,bool block)344 static void glfw_poll(struct window *window, bool block)
345 {
346     if (block) {
347         glfwWaitEvents();
348     } else {
349         glfwPollEvents();
350     }
351 }
352 
glfw_get_cursor(const struct window * window,int * x,int * y)353 static void glfw_get_cursor(const struct window *window, int *x, int *y)
354 {
355     struct priv *p = (struct priv *) window;
356     double dx, dy;
357     glfwGetCursorPos(p->win, &dx, &dy);
358     *x = dx;
359     *y = dy;
360 }
361 
glfw_get_button(const struct window * window,enum button btn)362 static bool glfw_get_button(const struct window *window, enum button btn)
363 {
364     static const int button_map[] = {
365         [BTN_LEFT] = GLFW_MOUSE_BUTTON_LEFT,
366         [BTN_RIGHT] = GLFW_MOUSE_BUTTON_RIGHT,
367         [BTN_MIDDLE] = GLFW_MOUSE_BUTTON_MIDDLE,
368     };
369 
370     struct priv *p = (struct priv *) window;
371     return glfwGetMouseButton(p->win, button_map[btn]) == GLFW_PRESS;
372 }
373 
glfw_get_key(const struct window * window,enum key key)374 static bool glfw_get_key(const struct window *window, enum key key)
375 {
376     static const int key_map[] = {
377         [KEY_ESC] = GLFW_KEY_ESCAPE,
378     };
379 
380     struct priv *p = (struct priv *) window;
381     return glfwGetKey(p->win, key_map[key]) == GLFW_PRESS;
382 }
383 
glfw_get_scroll(const struct window * window,float * dx,float * dy)384 static void glfw_get_scroll(const struct window *window, float *dx, float *dy)
385 {
386     struct priv *p = (struct priv *) window;
387     *dx = p->scroll_dx;
388     *dy = p->scroll_dy;
389     p->scroll_dx = p->scroll_dy = 0.0;
390 }
391 
glfw_get_file(const struct window * window)392 static char *glfw_get_file(const struct window *window)
393 {
394     struct priv *p = (struct priv *) window;
395     if (p->file_seen) {
396         assert(p->files_num);
397         free(p->files[0]);
398         memmove(&p->files[0], &p->files[1], --p->files_num * sizeof(char *));
399         p->file_seen = false;
400     }
401 
402     if (!p->files_num)
403         return NULL;
404 
405     p->file_seen = true;
406     return p->files[0];
407 }
408 
409 const struct window_impl IMPL = {
410     .name = IMPL_NAME,
411     .create = glfw_create,
412     .destroy = glfw_destroy,
413     .poll = glfw_poll,
414     .get_cursor = glfw_get_cursor,
415     .get_button = glfw_get_button,
416     .get_key = glfw_get_key,
417     .get_scroll = glfw_get_scroll,
418     .get_file = glfw_get_file,
419 };
420