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, ¶ms->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