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 "implementation.h"
18
19 #include "types.h"
20
21 #include "pugl/pugl.h"
22
23 #include <assert.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 const char*
puglStrerror(const PuglStatus status)29 puglStrerror(const PuglStatus status)
30 {
31 // clang-format off
32 switch (status) {
33 case PUGL_SUCCESS: return "Success";
34 case PUGL_FAILURE: return "Non-fatal failure";
35 case PUGL_UNKNOWN_ERROR: return "Unknown system error";
36 case PUGL_BAD_BACKEND: return "Invalid or missing backend";
37 case PUGL_BAD_CONFIGURATION: return "Invalid view configuration";
38 case PUGL_BAD_PARAMETER: return "Invalid parameter";
39 case PUGL_BACKEND_FAILED: return "Backend initialisation failed";
40 case PUGL_REGISTRATION_FAILED: return "Class registration failed";
41 case PUGL_REALIZE_FAILED: return "View creation failed";
42 case PUGL_SET_FORMAT_FAILED: return "Failed to set pixel format";
43 case PUGL_CREATE_CONTEXT_FAILED: return "Failed to create drawing context";
44 case PUGL_UNSUPPORTED_TYPE: return "Unsupported data type";
45 }
46 // clang-format on
47
48 return "Unknown error";
49 }
50
51 void
puglSetString(char ** dest,const char * string)52 puglSetString(char** dest, const char* string)
53 {
54 if (*dest != string) {
55 const size_t len = strlen(string);
56
57 *dest = (char*)realloc(*dest, len + 1);
58 strncpy(*dest, string, len + 1);
59 }
60 }
61
62 void
puglSetBlob(PuglBlob * const dest,const void * const data,const size_t len)63 puglSetBlob(PuglBlob* const dest, const void* const data, const size_t len)
64 {
65 if (data) {
66 dest->len = len;
67 dest->data = realloc(dest->data, len + 1);
68 memcpy(dest->data, data, len);
69 ((char*)dest->data)[len] = 0;
70 } else {
71 dest->len = 0;
72 dest->data = NULL;
73 }
74 }
75
76 static void
puglSetDefaultHints(PuglHints hints)77 puglSetDefaultHints(PuglHints hints)
78 {
79 hints[PUGL_USE_COMPAT_PROFILE] = PUGL_TRUE;
80 hints[PUGL_CONTEXT_VERSION_MAJOR] = 2;
81 hints[PUGL_CONTEXT_VERSION_MINOR] = 0;
82 hints[PUGL_RED_BITS] = 8;
83 hints[PUGL_GREEN_BITS] = 8;
84 hints[PUGL_BLUE_BITS] = 8;
85 hints[PUGL_ALPHA_BITS] = 8;
86 hints[PUGL_DEPTH_BITS] = 0;
87 hints[PUGL_STENCIL_BITS] = 0;
88 hints[PUGL_SAMPLES] = 0;
89 hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE;
90 hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE;
91 hints[PUGL_RESIZABLE] = PUGL_FALSE;
92 hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE;
93 hints[PUGL_REFRESH_RATE] = PUGL_DONT_CARE;
94 }
95
96 PuglWorld*
puglNewWorld(PuglWorldType type,PuglWorldFlags flags)97 puglNewWorld(PuglWorldType type, PuglWorldFlags flags)
98 {
99 PuglWorld* world = (PuglWorld*)calloc(1, sizeof(PuglWorld));
100 if (!world || !(world->impl = puglInitWorldInternals(type, flags))) {
101 free(world);
102 return NULL;
103 }
104
105 world->startTime = puglGetTime(world);
106
107 puglSetString(&world->className, "Pugl");
108
109 return world;
110 }
111
112 void
puglFreeWorld(PuglWorld * const world)113 puglFreeWorld(PuglWorld* const world)
114 {
115 puglFreeWorldInternals(world);
116 free(world->className);
117 free(world->views);
118 free(world);
119 }
120
121 void
puglSetWorldHandle(PuglWorld * world,PuglWorldHandle handle)122 puglSetWorldHandle(PuglWorld* world, PuglWorldHandle handle)
123 {
124 world->handle = handle;
125 }
126
127 PuglWorldHandle
puglGetWorldHandle(PuglWorld * world)128 puglGetWorldHandle(PuglWorld* world)
129 {
130 return world->handle;
131 }
132
133 PuglStatus
puglSetClassName(PuglWorld * const world,const char * const name)134 puglSetClassName(PuglWorld* const world, const char* const name)
135 {
136 puglSetString(&world->className, name);
137 return PUGL_SUCCESS;
138 }
139
140 PuglView*
puglNewView(PuglWorld * const world)141 puglNewView(PuglWorld* const world)
142 {
143 PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
144 if (!view || !(view->impl = puglInitViewInternals())) {
145 free(view);
146 return NULL;
147 }
148
149 view->world = world;
150 view->minWidth = 1;
151 view->minHeight = 1;
152
153 puglSetDefaultHints(view->hints);
154
155 // Add to world view list
156 ++world->numViews;
157 world->views =
158 (PuglView**)realloc(world->views, world->numViews * sizeof(PuglView*));
159
160 world->views[world->numViews - 1] = view;
161
162 return view;
163 }
164
165 void
puglFreeView(PuglView * view)166 puglFreeView(PuglView* view)
167 {
168 if (view->eventFunc && view->backend) {
169 puglDispatchSimpleEvent(view, PUGL_DESTROY);
170 }
171
172 // Remove from world view list
173 PuglWorld* world = view->world;
174 for (size_t i = 0; i < world->numViews; ++i) {
175 if (world->views[i] == view) {
176 if (i == world->numViews - 1) {
177 world->views[i] = NULL;
178 } else {
179 memmove(world->views + i,
180 world->views + i + 1,
181 sizeof(PuglView*) * (world->numViews - i - 1));
182 world->views[world->numViews - 1] = NULL;
183 }
184 --world->numViews;
185 }
186 }
187
188 free(view->title);
189 free(view->clipboard.data);
190 puglFreeViewInternals(view);
191 free(view);
192 }
193
194 PuglWorld*
puglGetWorld(PuglView * view)195 puglGetWorld(PuglView* view)
196 {
197 return view->world;
198 }
199
200 PuglStatus
puglSetViewHint(PuglView * view,PuglViewHint hint,int value)201 puglSetViewHint(PuglView* view, PuglViewHint hint, int value)
202 {
203 if (value == PUGL_DONT_CARE) {
204 switch (hint) {
205 case PUGL_USE_COMPAT_PROFILE:
206 case PUGL_USE_DEBUG_CONTEXT:
207 case PUGL_CONTEXT_VERSION_MAJOR:
208 case PUGL_CONTEXT_VERSION_MINOR:
209 case PUGL_SWAP_INTERVAL:
210 return PUGL_BAD_PARAMETER;
211 default:
212 break;
213 }
214 }
215
216 if (hint < PUGL_NUM_VIEW_HINTS) {
217 view->hints[hint] = value;
218 return PUGL_SUCCESS;
219 }
220
221 return PUGL_BAD_PARAMETER;
222 }
223
224 int
puglGetViewHint(const PuglView * view,PuglViewHint hint)225 puglGetViewHint(const PuglView* view, PuglViewHint hint)
226 {
227 return (hint < PUGL_NUM_VIEW_HINTS) ? view->hints[hint] : PUGL_DONT_CARE;
228 }
229
230 PuglStatus
puglSetParentWindow(PuglView * view,PuglNativeView parent)231 puglSetParentWindow(PuglView* view, PuglNativeView parent)
232 {
233 view->parent = parent;
234 return PUGL_SUCCESS;
235 }
236
237 PuglStatus
puglSetBackend(PuglView * view,const PuglBackend * backend)238 puglSetBackend(PuglView* view, const PuglBackend* backend)
239 {
240 view->backend = backend;
241 return PUGL_SUCCESS;
242 }
243
244 void
puglSetHandle(PuglView * view,PuglHandle handle)245 puglSetHandle(PuglView* view, PuglHandle handle)
246 {
247 view->handle = handle;
248 }
249
250 PuglHandle
puglGetHandle(PuglView * view)251 puglGetHandle(PuglView* view)
252 {
253 return view->handle;
254 }
255
256 bool
puglGetVisible(const PuglView * view)257 puglGetVisible(const PuglView* view)
258 {
259 return view->visible;
260 }
261
262 PuglRect
puglGetFrame(const PuglView * view)263 puglGetFrame(const PuglView* view)
264 {
265 return view->frame;
266 }
267
268 void*
puglGetContext(PuglView * view)269 puglGetContext(PuglView* view)
270 {
271 return view->backend->getContext(view);
272 }
273
274 #ifndef PUGL_DISABLE_DEPRECATED
275
276 PuglStatus
puglPollEvents(PuglWorld * world,double timeout)277 puglPollEvents(PuglWorld* world, double timeout)
278 {
279 return puglUpdate(world, timeout);
280 }
281
282 PuglStatus
puglDispatchEvents(PuglWorld * world)283 puglDispatchEvents(PuglWorld* world)
284 {
285 return puglUpdate(world, 0.0);
286 }
287
288 PuglStatus
puglShowWindow(PuglView * view)289 puglShowWindow(PuglView* view)
290 {
291 return puglShow(view);
292 }
293
294 PuglStatus
puglHideWindow(PuglView * view)295 puglHideWindow(PuglView* view)
296 {
297 return puglHide(view);
298 }
299
300 #endif
301
302 PuglStatus
puglSetEventFunc(PuglView * view,PuglEventFunc eventFunc)303 puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc)
304 {
305 view->eventFunc = eventFunc;
306 return PUGL_SUCCESS;
307 }
308
309 /// Return the code point for buf, or the replacement character on error
310 uint32_t
puglDecodeUTF8(const uint8_t * buf)311 puglDecodeUTF8(const uint8_t* buf)
312 {
313 #define FAIL_IF(cond) \
314 do { \
315 if (cond) \
316 return 0xFFFD; \
317 } while (0)
318
319 // http://en.wikipedia.org/wiki/UTF-8
320
321 if (buf[0] < 0x80) {
322 return buf[0];
323 }
324
325 if (buf[0] < 0xC2) {
326 return 0xFFFD;
327 }
328
329 if (buf[0] < 0xE0) {
330 FAIL_IF((buf[1] & 0xC0u) != 0x80);
331 return ((uint32_t)buf[0] << 6u) + buf[1] - 0x3080u;
332 }
333
334 if (buf[0] < 0xF0) {
335 FAIL_IF((buf[1] & 0xC0u) != 0x80);
336 FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0);
337 FAIL_IF((buf[2] & 0xC0u) != 0x80);
338 return ((uint32_t)buf[0] << 12u) + //
339 ((uint32_t)buf[1] << 6u) + //
340 ((uint32_t)buf[2] - 0xE2080u);
341 }
342
343 if (buf[0] < 0xF5) {
344 FAIL_IF((buf[1] & 0xC0u) != 0x80);
345 FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90);
346 FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90);
347 FAIL_IF((buf[2] & 0xC0u) != 0x80u);
348 FAIL_IF((buf[3] & 0xC0u) != 0x80u);
349 return (((uint32_t)buf[0] << 18u) + //
350 ((uint32_t)buf[1] << 12u) + //
351 ((uint32_t)buf[2] << 6u) + //
352 ((uint32_t)buf[3] - 0x3C82080u));
353 }
354
355 return 0xFFFD;
356 }
357
358 static inline bool
puglMustConfigure(PuglView * view,const PuglEventConfigure * configure)359 puglMustConfigure(PuglView* view, const PuglEventConfigure* configure)
360 {
361 return memcmp(configure, &view->lastConfigure, sizeof(PuglEventConfigure));
362 }
363
364 void
puglDispatchSimpleEvent(PuglView * view,const PuglEventType type)365 puglDispatchSimpleEvent(PuglView* view, const PuglEventType type)
366 {
367 assert(type == PUGL_CREATE || type == PUGL_DESTROY || type == PUGL_MAP ||
368 type == PUGL_UNMAP || type == PUGL_UPDATE || type == PUGL_CLOSE ||
369 type == PUGL_LOOP_ENTER || type == PUGL_LOOP_LEAVE);
370
371 const PuglEvent event = {{type, 0}};
372 puglDispatchEvent(view, &event);
373 }
374
375 void
puglConfigure(PuglView * view,const PuglEvent * event)376 puglConfigure(PuglView* view, const PuglEvent* event)
377 {
378 assert(event->type == PUGL_CONFIGURE);
379
380 view->frame.x = event->configure.x;
381 view->frame.y = event->configure.y;
382 view->frame.width = event->configure.width;
383 view->frame.height = event->configure.height;
384
385 if (puglMustConfigure(view, &event->configure)) {
386 view->eventFunc(view, event);
387 view->lastConfigure = event->configure;
388 }
389 }
390
391 void
puglExpose(PuglView * view,const PuglEvent * event)392 puglExpose(PuglView* view, const PuglEvent* event)
393 {
394 if (event->expose.width > 0.0 && event->expose.height > 0.0) {
395 view->eventFunc(view, event);
396 }
397 }
398
399 void
puglDispatchEvent(PuglView * view,const PuglEvent * event)400 puglDispatchEvent(PuglView* view, const PuglEvent* event)
401 {
402 switch (event->type) {
403 case PUGL_NOTHING:
404 break;
405 case PUGL_CREATE:
406 case PUGL_DESTROY:
407 view->backend->enter(view, NULL);
408 view->eventFunc(view, event);
409 view->backend->leave(view, NULL);
410 break;
411 case PUGL_CONFIGURE:
412 if (puglMustConfigure(view, &event->configure)) {
413 view->backend->enter(view, NULL);
414 puglConfigure(view, event);
415 view->backend->leave(view, NULL);
416 }
417 break;
418 case PUGL_MAP:
419 if (!view->visible) {
420 view->visible = true;
421 view->eventFunc(view, event);
422 }
423 break;
424 case PUGL_UNMAP:
425 if (view->visible) {
426 view->visible = false;
427 view->eventFunc(view, event);
428 }
429 break;
430 case PUGL_EXPOSE:
431 view->backend->enter(view, &event->expose);
432 puglExpose(view, event);
433 view->backend->leave(view, &event->expose);
434 break;
435 default:
436 view->eventFunc(view, event);
437 }
438 }
439
440 const void*
puglGetInternalClipboard(const PuglView * const view,const char ** const type,size_t * const len)441 puglGetInternalClipboard(const PuglView* const view,
442 const char** const type,
443 size_t* const len)
444 {
445 if (len) {
446 *len = view->clipboard.len;
447 }
448
449 if (type) {
450 *type = "text/plain";
451 }
452
453 return view->clipboard.data;
454 }
455
456 PuglStatus
puglSetInternalClipboard(PuglView * const view,const char * const type,const void * const data,const size_t len)457 puglSetInternalClipboard(PuglView* const view,
458 const char* const type,
459 const void* const data,
460 const size_t len)
461 {
462 if (type && !!strcmp(type, "text/plain")) {
463 return PUGL_UNSUPPORTED_TYPE;
464 }
465
466 puglSetBlob(&view->clipboard, data, len);
467 return PUGL_SUCCESS;
468 }
469