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 	free(view->clipboardType.data);
187   puglFreeViewInternals(view);
188   free(view);
189 }
190 
191 PuglWorld*
puglGetWorld(PuglView * view)192 puglGetWorld(PuglView* view)
193 {
194   return view->world;
195 }
196 
197 PuglStatus
puglSetViewHint(PuglView * view,PuglViewHint hint,int value)198 puglSetViewHint(PuglView* view, PuglViewHint hint, int value)
199 {
200   if (value == PUGL_DONT_CARE) {
201     switch (hint) {
202     case PUGL_USE_COMPAT_PROFILE:
203     case PUGL_USE_DEBUG_CONTEXT:
204     case PUGL_CONTEXT_VERSION_MAJOR:
205     case PUGL_CONTEXT_VERSION_MINOR:
206     case PUGL_SWAP_INTERVAL:
207       return PUGL_BAD_PARAMETER;
208     default:
209       break;
210     }
211   }
212 
213   if (hint < PUGL_NUM_VIEW_HINTS) {
214     view->hints[hint] = value;
215     return PUGL_SUCCESS;
216   }
217 
218   return PUGL_BAD_PARAMETER;
219 }
220 
221 int
puglGetViewHint(const PuglView * view,PuglViewHint hint)222 puglGetViewHint(const PuglView* view, PuglViewHint hint)
223 {
224   if (hint < PUGL_NUM_VIEW_HINTS) {
225     return view->hints[hint];
226   }
227 
228   return PUGL_DONT_CARE;
229 }
230 
231 PuglStatus
puglSetParentWindow(PuglView * view,PuglNativeView parent)232 puglSetParentWindow(PuglView* view, PuglNativeView parent)
233 {
234   view->parent = parent;
235   return PUGL_SUCCESS;
236 }
237 
238 PuglStatus
puglSetBackend(PuglView * view,const PuglBackend * backend)239 puglSetBackend(PuglView* view, const PuglBackend* backend)
240 {
241   view->backend = backend;
242   return PUGL_SUCCESS;
243 }
244 
245 void
puglSetHandle(PuglView * view,PuglHandle handle)246 puglSetHandle(PuglView* view, PuglHandle handle)
247 {
248   view->handle = handle;
249 }
250 
251 PuglHandle
puglGetHandle(PuglView * view)252 puglGetHandle(PuglView* view)
253 {
254   return view->handle;
255 }
256 
257 bool
puglGetVisible(const PuglView * view)258 puglGetVisible(const PuglView* view)
259 {
260   return view->visible;
261 }
262 
263 PuglRect
puglGetFrame(const PuglView * view)264 puglGetFrame(const PuglView* view)
265 {
266   return view->frame;
267 }
268 
269 void*
puglGetContext(PuglView * view)270 puglGetContext(PuglView* view)
271 {
272   return view->backend->getContext(view);
273 }
274 
275 #ifndef PUGL_DISABLE_DEPRECATED
276 
277 PuglStatus
puglPollEvents(PuglWorld * world,double timeout)278 puglPollEvents(PuglWorld* world, double timeout)
279 {
280   return puglUpdate(world, timeout);
281 }
282 
283 PuglStatus
puglDispatchEvents(PuglWorld * world)284 puglDispatchEvents(PuglWorld* world)
285 {
286   return puglUpdate(world, 0.0);
287 }
288 
289 PuglStatus
puglShowWindow(PuglView * view)290 puglShowWindow(PuglView* view)
291 {
292   return puglShow(view);
293 }
294 
295 PuglStatus
puglHideWindow(PuglView * view)296 puglHideWindow(PuglView* view)
297 {
298   return puglHide(view);
299 }
300 
301 #endif
302 
303 PuglStatus
puglSetEventFunc(PuglView * view,PuglEventFunc eventFunc)304 puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc)
305 {
306   view->eventFunc = eventFunc;
307   return PUGL_SUCCESS;
308 }
309 
310 /// Return the code point for buf, or the replacement character on error
311 uint32_t
puglDecodeUTF8(const uint8_t * buf)312 puglDecodeUTF8(const uint8_t* buf)
313 {
314 #define FAIL_IF(cond) \
315   do {                \
316     if (cond)         \
317       return 0xFFFD;  \
318   } while (0)
319 
320   // http://en.wikipedia.org/wiki/UTF-8
321 
322   if (buf[0] < 0x80) {
323     return buf[0];
324   }
325 
326   if (buf[0] < 0xC2) {
327     return 0xFFFD;
328   }
329 
330   if (buf[0] < 0xE0) {
331     FAIL_IF((buf[1] & 0xC0u) != 0x80);
332     return ((uint32_t)buf[0] << 6u) + buf[1] - 0x3080u;
333   }
334 
335   if (buf[0] < 0xF0) {
336     FAIL_IF((buf[1] & 0xC0u) != 0x80);
337     FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0);
338     FAIL_IF((buf[2] & 0xC0u) != 0x80);
339     return ((uint32_t)buf[0] << 12u) + //
340            ((uint32_t)buf[1] << 6u) +  //
341            ((uint32_t)buf[2] - 0xE2080u);
342   }
343 
344   if (buf[0] < 0xF5) {
345     FAIL_IF((buf[1] & 0xC0u) != 0x80);
346     FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90);
347     FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90);
348     FAIL_IF((buf[2] & 0xC0u) != 0x80u);
349     FAIL_IF((buf[3] & 0xC0u) != 0x80u);
350     return (((uint32_t)buf[0] << 18u) + //
351             ((uint32_t)buf[1] << 12u) + //
352             ((uint32_t)buf[2] << 6u) +  //
353             ((uint32_t)buf[3] - 0x3C82080u));
354   }
355 
356   return 0xFFFD;
357 }
358 
359 static inline bool
puglMustConfigure(PuglView * view,const PuglEventConfigure * configure)360 puglMustConfigure(PuglView* view, const PuglEventConfigure* configure)
361 {
362   return memcmp(configure, &view->lastConfigure, sizeof(PuglEventConfigure));
363 }
364 
365 void
puglDispatchSimpleEvent(PuglView * view,const PuglEventType type)366 puglDispatchSimpleEvent(PuglView* view, const PuglEventType type)
367 {
368   assert(type == PUGL_CREATE || type == PUGL_DESTROY || type == PUGL_MAP ||
369          type == PUGL_UNMAP || type == PUGL_UPDATE || type == PUGL_CLOSE ||
370          type == PUGL_LOOP_ENTER || type == PUGL_LOOP_LEAVE);
371 
372   const PuglEvent event = {{type, 0}};
373   puglDispatchEvent(view, &event);
374 }
375 
376 void
puglDispatchEventInContext(PuglView * view,const PuglEvent * event)377 puglDispatchEventInContext(PuglView* view, const PuglEvent* event)
378 {
379   if (event->type == PUGL_CONFIGURE) {
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   } else if (event->type == PUGL_EXPOSE) {
390     if (event->expose.width > 0 && event->expose.height > 0) {
391       view->eventFunc(view, event);
392     }
393   } else {
394     view->eventFunc(view, event);
395   }
396 }
397 
398 void
puglDispatchEvent(PuglView * view,const PuglEvent * event)399 puglDispatchEvent(PuglView* view, const PuglEvent* event)
400 {
401   switch (event->type) {
402   case PUGL_NOTHING:
403     break;
404   case PUGL_CREATE:
405   case PUGL_DESTROY:
406     view->backend->enter(view, NULL);
407     view->eventFunc(view, event);
408     view->backend->leave(view, NULL);
409     break;
410   case PUGL_CONFIGURE:
411     if (puglMustConfigure(view, &event->configure)) {
412       view->backend->enter(view, NULL);
413       puglDispatchEventInContext(view, event);
414       view->backend->leave(view, NULL);
415     }
416     break;
417   case PUGL_EXPOSE:
418     view->backend->enter(view, &event->expose);
419     puglDispatchEventInContext(view, event);
420     view->backend->leave(view, &event->expose);
421     break;
422   default:
423     view->eventFunc(view, event);
424   }
425 }
426 
427 const void*
puglGetInternalClipboard(const PuglView * const view,const char ** const type,size_t * const len)428 puglGetInternalClipboard(const PuglView* const view,
429                          const char** const    type,
430                          size_t* const         len)
431 {
432   if (len) {
433     *len = view->clipboard.len;
434   }
435 
436   if (type) {
437 		*type = view->clipboardType.data;
438   }
439 
440   return view->clipboard.data;
441 }
442 
443 PuglStatus
puglSetInternalClipboard(PuglView * const view,const char * const type,const void * const data,const size_t len)444 puglSetInternalClipboard(PuglView* const   view,
445                          const char* const type,
446                          const void* const data,
447                          const size_t      len)
448 {
449 	if (!type) {
450     return PUGL_UNSUPPORTED_TYPE;
451   }
452 
453 	puglSetBlob(&view->clipboardType, type, strlen(type) + 1);
454   puglSetBlob(&view->clipboard, data, len);
455   return PUGL_SUCCESS;
456 }
457