1 /*
2   Copyright 2012-2019 David Robillard <http://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 /**
18    @file implementation.c Platform-independent implementation.
19 */
20 
21 #include "pugl/detail/implementation.h"
22 #include "pugl/pugl.h"
23 
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 	switch (status) {
32 	case PUGL_SUCCESS:               return "Success";
33 	case PUGL_FAILURE:               return "Non-fatal failure";
34 	case PUGL_UNKNOWN_ERROR:         return "Unknown system error";
35 	case PUGL_BAD_BACKEND:           return "Invalid or missing backend";
36 	case PUGL_BACKEND_FAILED:        return "Backend initialisation failed";
37 	case PUGL_REGISTRATION_FAILED:   return "Window class registration failed";
38 	case PUGL_CREATE_WINDOW_FAILED:  return "Window creation failed";
39 	case PUGL_SET_FORMAT_FAILED:     return "Failed to set pixel format";
40 	case PUGL_CREATE_CONTEXT_FAILED: return "Failed to create drawing context";
41 	case PUGL_UNSUPPORTED_TYPE:      return "Unsupported data type";
42 	}
43 
44 	return "Unknown error";
45 }
46 
47 void
puglSetString(char ** dest,const char * string)48 puglSetString(char** dest, const char* string)
49 {
50 	const size_t len = strlen(string);
51 
52 	*dest = (char*)realloc(*dest, len + 1);
53 	strncpy(*dest, string, len + 1);
54 }
55 
56 void
puglSetBlob(PuglBlob * const dest,const void * const data,const size_t len)57 puglSetBlob(PuglBlob* const dest, const void* const data, const size_t len)
58 {
59 	if (data) {
60 		dest->len  = len;
61 		dest->data = realloc(dest->data, len + 1);
62 		memcpy(dest->data, data, len);
63 		((char*)dest->data)[len] = 0;
64 	} else {
65 		dest->len  = 0;
66 		dest->data = NULL;
67 	}
68 }
69 
70 static void
puglSetDefaultHints(PuglHints hints)71 puglSetDefaultHints(PuglHints hints)
72 {
73 	hints[PUGL_USE_COMPAT_PROFILE]    = PUGL_TRUE;
74 	hints[PUGL_CONTEXT_VERSION_MAJOR] = 2;
75 	hints[PUGL_CONTEXT_VERSION_MINOR] = 0;
76 	hints[PUGL_RED_BITS]              = 4;
77 	hints[PUGL_GREEN_BITS]            = 4;
78 	hints[PUGL_BLUE_BITS]             = 4;
79 	hints[PUGL_ALPHA_BITS]            = 4;
80 	hints[PUGL_DEPTH_BITS]            = 24;
81 	hints[PUGL_STENCIL_BITS]          = 8;
82 	hints[PUGL_SAMPLES]               = 0;
83 	hints[PUGL_DOUBLE_BUFFER]         = PUGL_FALSE;
84 	hints[PUGL_SWAP_INTERVAL]         = 0;
85 	hints[PUGL_RESIZABLE]             = PUGL_FALSE;
86 	hints[PUGL_IGNORE_KEY_REPEAT]     = PUGL_FALSE;
87 }
88 
89 PuglWorld*
puglNewWorld(void)90 puglNewWorld(void)
91 {
92 	PuglWorld* world = (PuglWorld*)calloc(1, sizeof(PuglWorld));
93 	if (!world || !(world->impl = puglInitWorldInternals())) {
94 		free(world);
95 		return NULL;
96 	}
97 
98 	world->startTime = puglGetTime(world);
99 	puglSetString(&world->className, "Pugl");
100 
101 	return world;
102 }
103 
104 void
puglFreeWorld(PuglWorld * const world)105 puglFreeWorld(PuglWorld* const world)
106 {
107 	puglFreeWorldInternals(world);
108 	free(world->className);
109 	free(world->views);
110 	free(world);
111 }
112 
113 PuglStatus
puglSetClassName(PuglWorld * const world,const char * const name)114 puglSetClassName(PuglWorld* const world, const char* const name)
115 {
116 	puglSetString(&world->className, name);
117 	return PUGL_SUCCESS;
118 }
119 
120 PuglView*
puglNewView(PuglWorld * const world)121 puglNewView(PuglWorld* const world)
122 {
123 	PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
124 	if (!view || !(view->impl = puglInitViewInternals())) {
125 		free(view);
126 		return NULL;
127 	}
128 
129 	view->world        = world;
130 	view->frame.width  = 640;
131 	view->frame.height = 480;
132 
133 	puglSetDefaultHints(view->hints);
134 
135 	// Add to world view list
136 	++world->numViews;
137 	world->views = (PuglView**)realloc(world->views,
138 	                                   world->numViews * sizeof(PuglView*));
139 	world->views[world->numViews - 1] = view;
140 
141 	return view;
142 }
143 
144 void
puglFreeView(PuglView * view)145 puglFreeView(PuglView* view)
146 {
147 	// Remove from world view list
148 	PuglWorld* world = view->world;
149 	for (size_t i = 0; i < world->numViews; ++i) {
150 		if (world->views[i] == view) {
151 			if (i == world->numViews - 1) {
152 				world->views[i] = NULL;
153 			} else {
154 				memmove(world->views + i, world->views + i + 1,
155 				        sizeof(PuglView*) * (world->numViews - i - 1));
156 				world->views[world->numViews - 1] = NULL;
157 			}
158 			--world->numViews;
159 		}
160 	}
161 
162 	free(view->title);
163 	free(view->clipboard.data);
164 	puglFreeViewInternals(view);
165 	free(view);
166 }
167 
168 PuglWorld*
puglGetWorld(PuglView * view)169 puglGetWorld(PuglView* view)
170 {
171 	return view->world;
172 }
173 
174 PuglStatus
puglSetViewHint(PuglView * view,PuglViewHint hint,int value)175 puglSetViewHint(PuglView* view, PuglViewHint hint, int value)
176 {
177 	if (hint < PUGL_NUM_WINDOW_HINTS) {
178 		view->hints[hint] = value;
179 	}
180 
181 	return PUGL_SUCCESS;
182 }
183 
184 PuglStatus
puglSetParentWindow(PuglView * view,PuglNativeWindow parent)185 puglSetParentWindow(PuglView* view, PuglNativeWindow parent)
186 {
187 	view->parent = parent;
188 	return PUGL_SUCCESS;
189 }
190 
191 PuglStatus
puglSetBackend(PuglView * view,const PuglBackend * backend)192 puglSetBackend(PuglView* view, const PuglBackend* backend)
193 {
194 	view->backend = backend;
195 	return PUGL_SUCCESS;
196 }
197 
198 void
puglSetHandle(PuglView * view,PuglHandle handle)199 puglSetHandle(PuglView* view, PuglHandle handle)
200 {
201 	view->handle = handle;
202 }
203 
204 PuglHandle
puglGetHandle(PuglView * view)205 puglGetHandle(PuglView* view)
206 {
207 	return view->handle;
208 }
209 
210 bool
puglGetVisible(PuglView * view)211 puglGetVisible(PuglView* view)
212 {
213 	return view->visible;
214 }
215 
216 PuglRect
puglGetFrame(const PuglView * view)217 puglGetFrame(const PuglView* view)
218 {
219 	return view->frame;
220 }
221 
222 void*
puglGetContext(PuglView * view)223 puglGetContext(PuglView* view)
224 {
225 	return view->backend->getContext(view);
226 }
227 
228 PuglStatus
puglEnterContext(PuglView * view,bool drawing)229 puglEnterContext(PuglView* view, bool drawing)
230 {
231 	view->backend->enter(view, drawing);
232 	return PUGL_SUCCESS;
233 }
234 
235 PuglStatus
puglLeaveContext(PuglView * view,bool drawing)236 puglLeaveContext(PuglView* view, bool drawing)
237 {
238 	view->backend->leave(view, drawing);
239 	return PUGL_SUCCESS;
240 }
241 
242 PuglStatus
puglSetEventFunc(PuglView * view,PuglEventFunc eventFunc)243 puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc)
244 {
245 	view->eventFunc = eventFunc;
246 	return PUGL_SUCCESS;
247 }
248 
249 /** Return the code point for buf, or the replacement character on error. */
250 uint32_t
puglDecodeUTF8(const uint8_t * buf)251 puglDecodeUTF8(const uint8_t* buf)
252 {
253 #define FAIL_IF(cond) do { if (cond) return 0xFFFD; } while (0)
254 
255 	// http://en.wikipedia.org/wiki/UTF-8
256 
257 	if (buf[0] < 0x80) {
258 		return buf[0];
259 	} else if (buf[0] < 0xC2) {
260 		return 0xFFFD;
261 	} else if (buf[0] < 0xE0) {
262 		FAIL_IF((buf[1] & 0xC0u) != 0x80);
263 		return (buf[0] << 6u) + buf[1] - 0x3080u;
264 	} else if (buf[0] < 0xF0) {
265 		FAIL_IF((buf[1] & 0xC0u) != 0x80);
266 		FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0);
267 		FAIL_IF((buf[2] & 0xC0u) != 0x80);
268 		return (buf[0] << 12u) + (buf[1] << 6u) + buf[2] - 0xE2080u;
269 	} else if (buf[0] < 0xF5) {
270 		FAIL_IF((buf[1] & 0xC0u) != 0x80);
271 		FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90);
272 		FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90);
273 		FAIL_IF((buf[2] & 0xC0u) != 0x80u);
274 		FAIL_IF((buf[3] & 0xC0u) != 0x80u);
275 		return ((buf[0] << 18u) +
276 		        (buf[1] << 12u) +
277 		        (buf[2] << 6u) +
278 		        buf[3] - 0x3C82080u);
279 	}
280 	return 0xFFFD;
281 }
282 
283 void
puglDispatchEvent(PuglView * view,const PuglEvent * event)284 puglDispatchEvent(PuglView* view, const PuglEvent* event)
285 {
286 	switch (event->type) {
287 	case PUGL_NOTHING:
288 		break;
289 	case PUGL_CONFIGURE:
290 		puglEnterContext(view, false);
291 		view->eventFunc(view, event);
292 		puglLeaveContext(view, false);
293 		break;
294 	case PUGL_EXPOSE:
295 		if (event->expose.count == 0) {
296 			puglEnterContext(view, true);
297 			view->eventFunc(view, event);
298 			puglLeaveContext(view, true);
299 		}
300 		break;
301 	default:
302 		view->eventFunc(view, event);
303 	}
304 }
305 
306 const void*
puglGetInternalClipboard(const PuglView * const view,const char ** const type,size_t * const len)307 puglGetInternalClipboard(const PuglView* const view,
308                          const char** const    type,
309                          size_t* const         len)
310 {
311 	if (len) {
312 		*len = view->clipboard.len;
313 	}
314 
315 	if (type) {
316 		*type = "text/plain";
317 	}
318 
319 	return view->clipboard.data;
320 }
321 
322 PuglStatus
puglSetInternalClipboard(PuglView * const view,const char * const type,const void * const data,const size_t len)323 puglSetInternalClipboard(PuglView* const   view,
324                          const char* const type,
325                          const void* const data,
326                          const size_t      len)
327 {
328 	if (type && strcmp(type, "text/plain")) {
329 		return PUGL_UNSUPPORTED_TYPE;
330 	}
331 
332 	puglSetBlob(&view->clipboard, data, len);
333 	return PUGL_SUCCESS;
334 }
335 
336