1 /*
2   Copyright 2015 Nick Bailey
3 */
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <math.h>
9 #include "stegosaurus.h"
10 
11 #include <cairo/cairo.h>
12 
13 #include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
14 #include "deliriumUI/deliriumUI.h"
15 
16 #define stegosaurus_UI_URI "http://nickbailey.co.nr/plugins/stegosaurus#ui"
17 
18 typedef struct {
19 	PuglView*            view;
20 	LV2UI_Write_Function write;
21 	LV2UI_Controller     controller;
22 	int                  width;
23 	int                  height;
24 	deliriumUI	     deliriumUI_window;
25 	float                brightness;
26 	bool                 exit;
27 } stegosaurusUI;
28 
29 static void
30 onReshape(PuglView* view, int width, int height)
31 {
32 
33 	stegosaurusUI* self = (stegosaurusUI*)puglGetHandle(view);
34 
35 	setDeliriumUICurrentWindowSize(&self->deliriumUI_window, width, height);
36 }
37 
38 
39 static void
40 onDisplay(PuglView* view)
41 {
42 	stegosaurusUI* self = (stegosaurusUI*)puglGetHandle(view);
43 	cairo_t* cr = puglGetContext(view);
44 
45 	displayAllDeliriumUIWidgets(&self->deliriumUI_window,cr);
46 }
47 
48 static void
49 onKeyboard(PuglView* view, bool press, uint32_t key)
50 {
51 	stegosaurusUI* self = (stegosaurusUI*)puglGetHandle(view);
52 	cairo_t* cr = puglGetContext(view);
53 
54 	if (press) {
55 
56 		if (key==27)
57 		{
58 			self->exit = true;
59 		}
60 
61 
62 		if (key=='r')
63 		{
64 			displayAllDeliriumUIWidgets(&self->deliriumUI_window,cr);
65 		}
66 
67 		fprintf(stderr, "Keyboard press %c\n", key);
68 	} else {
69 		fprintf(stderr, "Keyboard release %c\n", key);
70 	}
71 }
72 
73 static void
74 onMotion(PuglView* view, int x, int y)
75 {
76 	stegosaurusUI* self = (stegosaurusUI*)puglGetHandle(view);
77 	cairo_t* cr = puglGetContext(view);
78 
79 	// fprintf(stderr, "Motion: %d,%d\n", x, y);
80 	isMouseOverDeliriumUIWidget(&self->deliriumUI_window, cr, x, y);
81 }
82 
83 static void
84 onMouse(PuglView* view, int button, bool press, int x, int y)
85 {
86 	if (press == true && button == 0 || button > 15) return;
87 
88 	stegosaurusUI* self = (stegosaurusUI*)puglGetHandle(view);
89 	cairo_t* cr = puglGetContext(view);
90 
91 	self->deliriumUI_window.mouseButton[button] = press;
92 
93 	if (press == true && button == 3)
94 		zeroValue(&self->deliriumUI_window, cr);
95 
96 	if (press == true && button == 1)
97 		hasMouseClickedDeliriumUIWidget(&self->deliriumUI_window, x, y);
98 		else
99 		hasMouseClickedDeliriumUIWidget(&self->deliriumUI_window, -1,-1);
100 
101 }
102 
103 static void
104 onScroll(PuglView* view, int x, int y, float dx, float dy)
105 {
106 	stegosaurusUI* self = (stegosaurusUI*)puglGetHandle(view);
107 	cairo_t* cr = puglGetContext(view);
108 
109 	if (dy!=0)
110 	{
111 
112 		if (dy == -1)
113 		{
114 			decValue(&self->deliriumUI_window, cr);
115 		}
116 
117 		if (dy == 1)
118 		{
119 			incValue(&self->deliriumUI_window, cr);
120 		}
121 	}
122 }
123 
124 
125 static LV2UI_Handle
126 instantiate(const LV2UI_Descriptor*   descriptor,
127             const char*               plugin_uri,
128             const char*               bundle_path,
129             LV2UI_Write_Function      write_function,
130             LV2UI_Controller          controller,
131             LV2UI_Widget*             widget,
132             const LV2_Feature* const* features)
133 {
134 	stegosaurusUI* self = (stegosaurusUI*)malloc(sizeof(stegosaurusUI));
135 	if (!self) {
136 		return NULL;
137 	}
138 
139 	self->write      = write_function;
140 	self->controller = controller;
141 	self->width      = 800;
142 	self->height     = 360;
143 	self->exit       = false;
144 
145 	self->deliriumUI_window.deliriumUIWidgets = NULL;
146 	self->deliriumUI_window.numberOfUIWidgets = 1;
147 	self->deliriumUI_window.currentWidgetNumber = -1;
148 
149 	for (int b=0; b<16; ++b)
150 	{
151 		self->deliriumUI_window.mouseButton[b] = 0;
152 		self->deliriumUI_window.group_visible[b] = false;
153 		self->deliriumUI_window.group_visible[b+16] = false;
154 	}
155 
156 	setDeliriumUIGridSize(&self->deliriumUI_window, self->width, self->height, 16, 12);
157 
158 
159 	// Get parent window and resize API from features
160 	PuglNativeWindow parent = 0;
161 	LV2UI_Resize*    resize = NULL;
162 	for (int i = 0; features && features[i]; ++i) {
163 		printf("Feature %s\n", features[i]->URI );
164 		if (!strcmp(features[i]->URI, LV2_UI__parent)) {
165 			parent = (PuglNativeWindow)features[i]->data;
166 		} else if (!strcmp(features[i]->URI, LV2_UI__resize)) {
167 			resize = (LV2UI_Resize*)features[i]->data;
168 		}
169 	}
170 
171 	if (!parent) {
172 		fprintf(stderr, "error: stegosaurus_ui: No parent window provided.\n");
173 		free(self);
174 		return NULL;
175 	}
176 
177 
178 	setControllerDiliriumUI(controller, write_function);
179 
180 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Button, 5, 0,0,2,1,"KICK",-1);
181 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Button, 5, 2,0,2,1,"SNARE",-2);
182 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Button, 5, 4,0,2,1,"CLOSED HAT",-3);
183 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Button, 5, 6,0,2,1,"OPEN HAT",-4);
184 
185 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Switch, 1, 0.5,3,1,2,"OSC 1",stegosaurus_KICK_OSC1_ACTIVE);
186 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 1, 1.5,2,1,4,"NOISE",stegosaurus_KICK_OSC1_NOISE);
187 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 1, 2.5,2,1,4,"PITCH",stegosaurus_KICK_OSC1_PITCH);
188 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 1, 4,2,3,3,"AMP",stegosaurus_KICK_OSC1_AMP_ATTACK);
189 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 1, 7.52,2,3,3,"PITCH",stegosaurus_KICK_OSC1_PITCH_ATTACK);
190 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 1, 11,2,1,4,"VOL",stegosaurus_KICK_OSC1_VOLUME);
191 
192 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Switch, 1, 0.5,8,1,2,"OSC 2",stegosaurus_KICK_OSC2_ACTIVE);
193 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 1, 1.5,7,1,4,"NOISE",stegosaurus_KICK_OSC2_NOISE);
194 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 1, 2.5,7,1,4,"PITCH",stegosaurus_KICK_OSC2_PITCH);
195 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 1, 4,7,3,3,"AMP",stegosaurus_KICK_OSC2_AMP_ATTACK);
196 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 1, 7.52,7,3,3,"PITCH",stegosaurus_KICK_OSC2_PITCH_ATTACK);
197 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 1, 11,7,1,4,"VOL", stegosaurus_KICK_OSC2_VOLUME);
198 
199 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Switch, 2, 0.5,3,1,2,"OSC 1",stegosaurus_SNARE_OSC1_ACTIVE);
200 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 2, 1.5,2,1,4,"NOISE",stegosaurus_SNARE_OSC1_NOISE);
201 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 2, 2.5,2,1,4,"PITCH",stegosaurus_SNARE_OSC1_PITCH);
202 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 2, 4,2,3,3,"AMP",stegosaurus_SNARE_OSC1_AMP_ATTACK);
203 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 2, 7.52,2,3,3,"PITCH",stegosaurus_SNARE_OSC1_PITCH_ATTACK);
204 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 2, 11,2,1,4,"VOL",stegosaurus_SNARE_OSC1_VOLUME);
205 
206 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Switch, 2, 0.5,8,1,2,"OSC 2",stegosaurus_SNARE_OSC2_ACTIVE);
207 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 2, 1.5,7,1,4,"NOISE",stegosaurus_SNARE_OSC2_NOISE);
208 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 2, 2.5,7,1,4,"PITCH",stegosaurus_SNARE_OSC2_PITCH);
209 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 2, 4,7,3,3,"AMP",stegosaurus_SNARE_OSC2_AMP_ATTACK);
210 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 2, 7.52,7,3,3,"PITCH",stegosaurus_SNARE_OSC2_PITCH_ATTACK);
211 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 2, 11,7,1,4,"VOL", stegosaurus_SNARE_OSC2_VOLUME);
212 
213 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Switch, 3, 0.5,3,1,2,"OSC 1",stegosaurus_CLHAT_OSC1_ACTIVE);
214 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 3, 1.5,2,1,4,"NOISE",stegosaurus_CLHAT_OSC1_NOISE);
215 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 3, 2.5,2,1,4,"PITCH",stegosaurus_CLHAT_OSC1_PITCH);
216 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 3, 4,2,3,3,"AMP",stegosaurus_CLHAT_OSC1_AMP_ATTACK);
217 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 3, 7.52,2,3,3,"PITCH",stegosaurus_CLHAT_OSC1_PITCH_ATTACK);
218 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 3, 11,2,1,4,"VOL",stegosaurus_CLHAT_OSC1_VOLUME);
219 
220 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Switch, 3, 0.5,8,1,2,"OSC 2",stegosaurus_CLHAT_OSC2_ACTIVE);
221 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 3, 1.5,7,1,4,"NOISE",stegosaurus_CLHAT_OSC2_NOISE);
222 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 3, 2.5,7,1,4,"PITCH",stegosaurus_CLHAT_OSC2_PITCH);
223 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 3, 4,7,3,3,"AMP",stegosaurus_CLHAT_OSC2_AMP_ATTACK);
224 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 3, 7.52,7,3,3,"PITCH",stegosaurus_CLHAT_OSC2_PITCH_ATTACK);
225 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 3, 11,7,1,4,"VOL", stegosaurus_CLHAT_OSC2_VOLUME);
226 
227 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Switch, 4, 0.5,3,1,2,"OSC 1",stegosaurus_OPHAT_OSC1_ACTIVE);
228 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 4, 1.5,2,1,4,"NOISE",stegosaurus_OPHAT_OSC1_NOISE);
229 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 4, 2.5,2,1,4,"PITCH",stegosaurus_OPHAT_OSC1_PITCH);
230 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 4, 4,2,3,3,"AMP",stegosaurus_OPHAT_OSC1_AMP_ATTACK);
231 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 4, 7.52,2,3,3,"PITCH",stegosaurus_OPHAT_OSC1_PITCH_ATTACK);
232 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 4, 11,2,1,4,"VOL",stegosaurus_OPHAT_OSC1_VOLUME);
233 
234 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Switch, 4, 0.5,8,1,2,"OSC 2",stegosaurus_OPHAT_OSC2_ACTIVE);
235 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 4, 1.5,7,1,4,"NOISE",stegosaurus_OPHAT_OSC2_NOISE);
236 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 4, 2.5,7,1,4,"PITCH",stegosaurus_OPHAT_OSC2_PITCH);
237 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 4, 4,7,3,3,"AMP",stegosaurus_OPHAT_OSC2_AMP_ATTACK);
238 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_ADSR, 4, 7.52,7,3,3,"PITCH",stegosaurus_OPHAT_OSC2_PITCH_ATTACK);
239 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Fader, 4, 11,7,1,4,"VOL", stegosaurus_OPHAT_OSC2_VOLUME);
240 
241 	addDeliriumUIWidget(&self->deliriumUI_window, deliriumUI_Knob, 5, 13.5,6,2,4,"VOLUME",stegosaurus_VOLUME);
242 
243 	self->deliriumUI_window.group_visible[1] = true;
244 	self->deliriumUI_window.group_visible[2] = false;
245 	self->deliriumUI_window.group_visible[3] = false;
246 	self->deliriumUI_window.group_visible[4] = false;
247 	self->deliriumUI_window.group_visible[5] = true;
248 
249 	// Set up pugl window
250 
251 	self->view = puglInit(NULL, NULL);
252 	puglInitResizable(self->view, false);
253 	puglInitWindowParent(self->view, parent);
254 	puglInitWindowSize(self->view, 800,360);
255 	puglInitContextType(self->view, PUGL_CAIRO);
256 	puglIgnoreKeyRepeat(self->view, false);
257 
258 	puglSetDisplayFunc(self->view, onDisplay);
259 	puglSetReshapeFunc(self->view, onReshape);
260 	puglSetKeyboardFunc(self->view, onKeyboard);
261 	puglSetMotionFunc(self->view, onMotion);
262 	puglSetMouseFunc(self->view, onMouse);
263 	puglSetScrollFunc(self->view, onScroll);
264 
265 	puglCreateWindow(self->view, "stegosaurus");
266 	puglSetHandle(self->view, self);
267 	puglShowWindow(self->view);
268 
269 	if (resize) {
270 		resize->ui_resize(resize->handle, self->width, self->height);
271 	}
272 
273 	*widget = (void*)puglGetNativeWindow(self->view);
274 	return self;
275 }
276 
277 static int
278 idle(LV2UI_Handle handle)
279 {
280 	stegosaurusUI* self = (stegosaurusUI*)handle;
281 
282 	// Silly pulsing animation to check that idle handler is working
283 	self->brightness = fmod(self->brightness + 0.01, 1.0);
284 	puglPostRedisplay(self->view);
285 	puglProcessEvents(self->view);
286 
287 	return 0;
288 }
289 
290 static void
291 cleanup(LV2UI_Handle handle)
292 {
293 
294 	stegosaurusUI* self = (stegosaurusUI*)handle;
295 	self->exit = true;
296 
297 	fprintf(stderr, "\nstegosaurus shutting down........\n");
298 
299 	puglDestroy(self->view);
300 	free(self);
301 
302 }
303 
304 static void
305 port_event(LV2UI_Handle handle,
306            uint32_t     port_index,
307            uint32_t     buffer_size,
308            uint32_t     format,
309            const void*  buffer)
310 {
311 	stegosaurusUI* self = (stegosaurusUI*)handle;
312 	cairo_t* cr = puglGetContext(self->view);
313 	float value = *(float*)buffer;
314 
315 	setValue(&self->deliriumUI_window, cr, port_index, value);
316 
317 
318 }
319 
320 static const LV2UI_Idle_Interface idle_iface = { idle };
321 
322 static const void*
323 extension_data(const char* uri)
324 {
325 	if (!strcmp(uri, LV2_UI__idleInterface)) {
326 		return &idle_iface;
327 	}
328 	return NULL;
329 }
330 
331 static const LV2UI_Descriptor descriptor = {
332 	stegosaurus_UI_URI,
333 	instantiate,
334 	cleanup,
335 	port_event,
336 	extension_data
337 };
338 
339 LV2_SYMBOL_EXPORT
340 const LV2UI_Descriptor*
341 lv2ui_descriptor(uint32_t index)
342 {
343 	switch (index) {
344 	case 0:
345 		return &descriptor;
346 	default:
347 		return NULL;
348 	}
349 }
350 
351 
352 
353 
354 
355 
356 
357 
358