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 "pugl/pugl.h"
20 
21 #include <assert.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 const char*
puglStrerror(const PuglStatus status)27 puglStrerror(const PuglStatus status)
28 {
29   // clang-format off
30   switch (status) {
31   case PUGL_SUCCESS:               return "Success";
32   case PUGL_FAILURE:               return "Non-fatal failure";
33   case PUGL_UNKNOWN_ERROR:         return "Unknown system error";
34   case PUGL_BAD_BACKEND:           return "Invalid or missing backend";
35   case PUGL_BAD_CONFIGURATION:     return "Invalid view configuration";
36   case PUGL_BAD_PARAMETER:         return "Invalid parameter";
37   case PUGL_BACKEND_FAILED:        return "Backend initialisation failed";
38   case PUGL_REGISTRATION_FAILED:   return "Class registration failed";
39   case PUGL_REALIZE_FAILED:        return "View creation failed";
40   case PUGL_SET_FORMAT_FAILED:     return "Failed to set pixel format";
41   case PUGL_CREATE_CONTEXT_FAILED: return "Failed to create drawing context";
42   case PUGL_UNSUPPORTED_TYPE:      return "Unsupported data type";
43   }
44   // clang-format on
45 
46   return "Unknown error";
47 }
48 
49 void
puglSetString(char ** dest,const char * string)50 puglSetString(char** dest, const char* string)
51 {
52   if (*dest != string) {
53     const size_t len = strlen(string);
54 
55     *dest = (char*)realloc(*dest, len + 1);
56     strncpy(*dest, string, len + 1);
57   }
58 }
59 
60 void
puglSetBlob(PuglBlob * const dest,const void * const data,const size_t len)61 puglSetBlob(PuglBlob* const dest, const void* const data, const size_t len)
62 {
63   if (data) {
64     dest->len  = len;
65     dest->data = realloc(dest->data, len + 1);
66     memcpy(dest->data, data, len);
67     ((char*)dest->data)[len] = 0;
68   } else {
69     dest->len  = 0;
70     dest->data = NULL;
71   }
72 }
73 
74 static void
puglSetDefaultHints(PuglHints hints)75 puglSetDefaultHints(PuglHints hints)
76 {
77   hints[PUGL_USE_COMPAT_PROFILE]    = PUGL_TRUE;
78   hints[PUGL_CONTEXT_VERSION_MAJOR] = 2;
79   hints[PUGL_CONTEXT_VERSION_MINOR] = 0;
80   hints[PUGL_RED_BITS]              = 8;
81   hints[PUGL_GREEN_BITS]            = 8;
82   hints[PUGL_BLUE_BITS]             = 8;
83   hints[PUGL_ALPHA_BITS]            = 8;
84   hints[PUGL_DEPTH_BITS]            = 0;
85   hints[PUGL_STENCIL_BITS]          = 0;
86   hints[PUGL_SAMPLES]               = 0;
87   hints[PUGL_DOUBLE_BUFFER]         = PUGL_TRUE;
88   hints[PUGL_SWAP_INTERVAL]         = PUGL_DONT_CARE;
89   hints[PUGL_RESIZABLE]             = PUGL_FALSE;
90   hints[PUGL_IGNORE_KEY_REPEAT]     = PUGL_FALSE;
91   hints[PUGL_REFRESH_RATE]          = PUGL_DONT_CARE;
92 }
93 
94 PuglWorld*
puglNewWorld(PuglWorldType type,PuglWorldFlags flags)95 puglNewWorld(PuglWorldType type, PuglWorldFlags flags)
96 {
97   PuglWorld* world = (PuglWorld*)calloc(1, sizeof(PuglWorld));
98   if (!world || !(world->impl = puglInitWorldInternals(type, flags))) {
99     free(world);
100     return NULL;
101   }
102 
103   world->startTime = puglGetTime(world);
104 
105   puglSetString(&world->className, "Pugl");
106 
107   return world;
108 }
109 
110 void
puglFreeWorld(PuglWorld * const world)111 puglFreeWorld(PuglWorld* const world)
112 {
113   puglFreeWorldInternals(world);
114   free(world->className);
115   free(world->views);
116   free(world);
117 }
118 
119 void
puglSetWorldHandle(PuglWorld * world,PuglWorldHandle handle)120 puglSetWorldHandle(PuglWorld* world, PuglWorldHandle handle)
121 {
122   world->handle = handle;
123 }
124 
125 PuglWorldHandle
puglGetWorldHandle(PuglWorld * world)126 puglGetWorldHandle(PuglWorld* world)
127 {
128   return world->handle;
129 }
130 
131 PuglStatus
puglSetClassName(PuglWorld * const world,const char * const name)132 puglSetClassName(PuglWorld* const world, const char* const name)
133 {
134   puglSetString(&world->className, name);
135   return PUGL_SUCCESS;
136 }
137 
138 PuglView*
puglNewView(PuglWorld * const world)139 puglNewView(PuglWorld* const world)
140 {
141   PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
142   if (!view || !(view->impl = puglInitViewInternals())) {
143     free(view);
144     return NULL;
145   }
146 
147   view->world     = world;
148   view->minWidth  = 1;
149   view->minHeight = 1;
150 
151   puglSetDefaultHints(view->hints);
152 
153   // Add to world view list
154   ++world->numViews;
155   world->views =
156     (PuglView**)realloc(world->views, world->numViews * sizeof(PuglView*));
157 
158   world->views[world->numViews - 1] = view;
159 
160   return view;
161 }
162 
163 void
puglFreeView(PuglView * view)164 puglFreeView(PuglView* view)
165 {
166   puglDispatchSimpleEvent(view, PUGL_DESTROY);
167 
168   // Remove from world view list
169   PuglWorld* world = view->world;
170   for (size_t i = 0; i < world->numViews; ++i) {
171     if (world->views[i] == view) {
172       if (i == world->numViews - 1) {
173         world->views[i] = NULL;
174       } else {
175         memmove(world->views + i,
176                 world->views + i + 1,
177                 sizeof(PuglView*) * (world->numViews - i - 1));
178         world->views[world->numViews - 1] = NULL;
179       }
180       --world->numViews;
181     }
182   }
183 
184   free(view->title);
185   free(view->clipboard.data);
186   puglFreeViewInternals(view);
187   free(view);
188 }
189 
190 PuglWorld*
puglGetWorld(PuglView * view)191 puglGetWorld(PuglView* view)
192 {
193   return view->world;
194 }
195 
196 PuglStatus
puglSetViewHint(PuglView * view,PuglViewHint hint,int value)197 puglSetViewHint(PuglView* view, PuglViewHint hint, int value)
198 {
199   if (value == PUGL_DONT_CARE) {
200     switch (hint) {
201     case PUGL_USE_COMPAT_PROFILE:
202     case PUGL_USE_DEBUG_CONTEXT:
203     case PUGL_CONTEXT_VERSION_MAJOR:
204     case PUGL_CONTEXT_VERSION_MINOR:
205     case PUGL_SWAP_INTERVAL:
206       return PUGL_BAD_PARAMETER;
207     default:
208       break;
209     }
210   }
211 
212   if (hint < PUGL_NUM_VIEW_HINTS) {
213     view->hints[hint] = value;
214     return PUGL_SUCCESS;
215   }
216 
217   return PUGL_BAD_PARAMETER;
218 }
219 
220 int
puglGetViewHint(const PuglView * view,PuglViewHint hint)221 puglGetViewHint(const PuglView* view, PuglViewHint hint)
222 {
223   if (hint < PUGL_NUM_VIEW_HINTS) {
224     return view->hints[hint];
225   }
226 
227   return 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
puglDispatchEventInContext(PuglView * view,const PuglEvent * event)376 puglDispatchEventInContext(PuglView* view, const PuglEvent* event)
377 {
378   if (event->type == PUGL_CONFIGURE) {
379     view->frame.x      = event->configure.x;
380     view->frame.y      = event->configure.y;
381     view->frame.width  = event->configure.width;
382     view->frame.height = event->configure.height;
383 
384     if (puglMustConfigure(view, &event->configure)) {
385       view->eventFunc(view, event);
386       view->lastConfigure = event->configure;
387     }
388   } else if (event->type == PUGL_EXPOSE) {
389     if (event->expose.width > 0 && event->expose.height > 0) {
390       view->eventFunc(view, event);
391     }
392   } else {
393     view->eventFunc(view, event);
394   }
395 }
396 
397 void
puglDispatchEvent(PuglView * view,const PuglEvent * event)398 puglDispatchEvent(PuglView* view, const PuglEvent* event)
399 {
400   switch (event->type) {
401   case PUGL_NOTHING:
402     break;
403   case PUGL_CREATE:
404   case PUGL_DESTROY:
405     view->backend->enter(view, NULL);
406     view->eventFunc(view, event);
407     view->backend->leave(view, NULL);
408     break;
409   case PUGL_CONFIGURE:
410     if (puglMustConfigure(view, &event->configure)) {
411       view->backend->enter(view, NULL);
412       puglDispatchEventInContext(view, event);
413       view->backend->leave(view, NULL);
414     }
415     break;
416   case PUGL_EXPOSE:
417     view->backend->enter(view, &event->expose);
418     puglDispatchEventInContext(view, event);
419     view->backend->leave(view, &event->expose);
420     break;
421   default:
422     view->eventFunc(view, event);
423   }
424 }
425 
426 const void*
puglGetInternalClipboard(const PuglView * const view,const char ** const type,size_t * const len)427 puglGetInternalClipboard(const PuglView* const view,
428                          const char** const    type,
429                          size_t* const         len)
430 {
431   if (len) {
432     *len = view->clipboard.len;
433   }
434 
435   if (type) {
436     *type = "text/plain";
437   }
438 
439   return view->clipboard.data;
440 }
441 
442 PuglStatus
puglSetInternalClipboard(PuglView * const view,const char * const type,const void * const data,const size_t len)443 puglSetInternalClipboard(PuglView* const   view,
444                          const char* const type,
445                          const void* const data,
446                          const size_t      len)
447 {
448   if (type && strcmp(type, "text/plain")) {
449     return PUGL_UNSUPPORTED_TYPE;
450   }
451 
452   puglSetBlob(&view->clipboard, data, len);
453   return PUGL_SUCCESS;
454 }
455