1 /* B.Angr
2  * Dynamic distorted bandpass filter plugin
3  *
4  * Copyright (C) 2021 by Sven Jähnichen
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 #include "BAngrGUI.hpp"
22 #include <exception>
23 #include "screen.h"
24 #include "BUtilities/vsystem.hpp"
25 
26 
BAngrGUI(const char * bundle_path,const LV2_Feature * const * features,PuglNativeView parentWindow)27 BAngrGUI::BAngrGUI (const char *bundle_path, const LV2_Feature *const *features, PuglNativeView parentWindow) :
28 	Window (1000, 560, "B.Angr", parentWindow, true, PUGL_MODULE, 0),
29 	controller (NULL),
30 	write_function (NULL),
31 	pluginPath (bundle_path ? std::string (bundle_path) : std::string ("")),
32 	sz (1.0),
33 	bgImageSurface (nullptr),
34 	map (nullptr),
35 
36 	mContainer (0, 0, 1000, 560, "main"),
37 	cursor (480, 260, 40, 40, "dot"),
38 	poweredLabel (720, 540, 250, 20, "rlabel", BANGR_LABEL_POWERED_BY),
39 	helpButton (918, 508, 24, 24, "widget", BANGR_LABEL_HELP),
40 	ytButton (948, 508, 24, 24, "widget", BANGR_LABEL_VIDEO),
41 	bypassButton (900, 30, 20, 20, "dial"),
42 	bypassLabel (880, 60, 60, 20, "label", BANGR_LABEL_BYPASS),
43 	drywetDial (940, 20, 40, 40, "dial", 1.0, 0.0, 1.0, 0.0, ""),
44 	drywetLabel (930, 60, 60, 20, "label", BANGR_LABEL_DRY_WET),
45 	speedDial (370, 460, 60, 60, "dial", 0.5, 0.0, 1.0, 0.0, BIDIRECTIONAL, "%1.2f", ""),
46 	speedLabel (370, 520, 60, 20, "label", BANGR_LABEL_SPEED),
47 	spinDial (570, 460, 60, 60, "dial", 0.0, -1.0, 1.0, 0.0, BIDIRECTIONAL, "%1.2f", ""),
48 	spinLabel (570, 520, 60, 20, "label", BANGR_LABEL_SPIN),
49 	speedScreen (180, 480, 100, 35, "screen"),
50 	speedFlexLabel (220, 520, 100, 20, "label", BANGR_LABEL_FLEXIBILITY),
51 	speedTypeListbox
52 	(
53 		280, 490, 80, 20, 0, -120, 80, 120, "menu",
54 		BItems::ItemList
55 		({
56 			{RANDOM, BANGR_LABEL_RANDOM},
57 			{LEVEL, BANGR_LABEL_LEVEL},
58 			{LOWS, BANGR_LABEL_LOWS},
59 			{MIDS, BANGR_LABEL_MIDS},
60 			{HIGHS, BANGR_LABEL_HIGHS}
61 		}),
62 		0),
63 	speedAmountSlider (200, 485, 80, 20, "slider", 0.0, 0.0, 1.0, 0.0, "%1.2f"),
64 	spinScreen (640, 480, 100, 35, "screen"),
65 	spinFlexLabel (680, 520, 100, 20, "label", BANGR_LABEL_FLEXIBILITY),
66 	spinTypeListbox
67 	(
68 		740, 490, 80, 20, 0, -120, 80, 120, "menu",
69 		BItems::ItemList
70 		({
71 			{RANDOM, BANGR_LABEL_RANDOM},
72 			{LEVEL, BANGR_LABEL_LEVEL},
73 			{LOWS, BANGR_LABEL_LOWS},
74 			{MIDS, BANGR_LABEL_MIDS},
75 			{HIGHS, BANGR_LABEL_HIGHS}
76 		}),
77 		0),
78 	spinAmountSlider (660, 485, 80, 20, "slider", 0.0, 0.0, 1.0, 0.0, "%1.2f")
79 {
80 	// Init param widgets
81 	for (int i = 0; i < NR_FX; ++i)
82 	{
83 		fx[i].container = BWidgets::Widget (20 + int (i / 2) * 660, 100 + int (((i + 1) & 3) / 2) * 200, 300, 160, "widget");
84 		fx[i].paramDials[0] = Dial (0, 10, 60, 60, "dial", 0.5, 0.0, 1.0, 0.0, "%1.2f");
85 		fx[i].paramLabels[0] = BWidgets::Label (0, 70, 60, 20, "label", BANGR_LABEL_GAIN);
86 		fx[i].paramDials[1] = Dial (80, 10, 60, 60, "dial", 0.5, 0.0, 1.0, 0.0, "%1.2f");
87 		fx[i].paramLabels[1] = BWidgets::Label (80, 70, 60, 20, "label", BANGR_LABEL_FIRST);
88 		fx[i].paramDials[2] = Dial (160, 10, 60, 60, "dial", 0.5, 0.0, 1.0, 0.0, "%1.2f");
89 		fx[i].paramLabels[2] = BWidgets::Label (160, 70, 60, 20, "label", BANGR_LABEL_LAST);
90 		fx[i].paramDials[3] = Dial (240, 10, 60, 60, "dial", 0.0, 0.0, 1.0, 0.0, "%1.2f");
91 		fx[i].paramLabels[3] = BWidgets::Label (240, 70, 60, 20, "label", BANGR_LABEL_NUKE);
92 		fx[i].paramDials[4] = Dial (40, 70, 60, 60, "dial", 1.0, 0.0, 1.0, 0.0, "%1.2f");
93 		fx[i].paramLabels[4] = BWidgets::Label (40, 130, 60, 20, "label", BANGR_LABEL_MIX);
94 		fx[i].paramDials[5] = Dial (200, 70, 60, 60, "dial", 0.0, -1.0, 1.0, 0.0, "%1.2f");
95 		fx[i].paramLabels[5] = BWidgets::Label (200, 130, 60, 20, "label", BANGR_LABEL_PAN);
96 	}
97 
98 	// Link controllers
99 	controllerWidgets[BYPASS] = &bypassButton;
100 	controllerWidgets[DRY_WET] = &drywetDial;
101 	controllerWidgets[SPEED] = &speedDial;
102 	controllerWidgets[SPEED_RANGE] = &speedDial.range;
103 	controllerWidgets[SPEED_TYPE] = &speedTypeListbox;
104 	controllerWidgets[SPEED_AMOUNT] = &speedAmountSlider;
105 	controllerWidgets[SPIN] = &spinDial;
106 	controllerWidgets[SPIN_RANGE] = &spinDial.range;
107 	controllerWidgets[SPIN_TYPE] = &spinTypeListbox;
108 	controllerWidgets[SPIN_AMOUNT] = &spinAmountSlider;
109 
110 	for (int i = 0; i < NR_FX; ++i)
111 	{
112 		for (int j = 0; j < NR_PARAMS; ++j)
113 		{
114 			controllerWidgets[FX + i * NR_PARAMS + j] = &fx[i].paramDials[j];
115 		}
116 	}
117 
118 	// Configure widgets
119 	cursor.setDraggable (true);
120 
121 
122 	// Set callbacks
123 	for (BWidgets::ValueWidget* c : controllerWidgets) c->setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BAngrGUI::valueChangedCallback);
124 	cursor.setCallbackFunction (BEvents::EventType::POINTER_DRAG_EVENT, BAngrGUI::cursorDraggedCallback);
125 	cursor.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BAngrGUI::cursorDraggedCallback);
126 	cursor.setCallbackFunction (BEvents::EventType::BUTTON_RELEASE_EVENT, BAngrGUI::cursorReleasedCallback);
127 	poweredLabel.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BAngrGUI::xregionClickedCallback);
128 	helpButton.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BAngrGUI::helpButtonClickedCallback);
129 	ytButton.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BAngrGUI::ytButtonClickedCallback);
130 
131 	// Load background & apply theme
132 	bgImageSurface = cairo_image_surface_create_from_png ((pluginPath + BG_FILE).c_str());
133 	widgetBg.loadFillFromCairoSurface (bgImageSurface);
134 	applyTheme (theme);
135 
136 	// Pack widgets
137 	for (Fx& f : fx)
138 	{
139 		for (Dial& d : f.paramDials) f.container.add (d);
140 		for (BWidgets::Label& l : f.paramLabels) f.container.add (l);
141 		mContainer.add (f.container);
142 	}
143 	mContainer.add (bypassButton);
144 	mContainer.add (bypassLabel);
145 	mContainer.add (drywetDial);
146 	mContainer.add (drywetLabel);
147 	mContainer.add (speedDial);
148 	mContainer.add (speedLabel);
149 	mContainer.add (spinDial);
150 	mContainer.add (spinLabel);
151 	mContainer.add (speedFlexLabel);
152 	mContainer.add (speedTypeListbox);
153 	mContainer.add (speedAmountSlider);
154 	mContainer.add (spinFlexLabel);
155 	mContainer.add (spinTypeListbox);
156 	mContainer.add (spinAmountSlider);
157 	mContainer.add (speedScreen);
158 	mContainer.add (spinScreen);
159 	mContainer.add (cursor);
160 	mContainer.add (poweredLabel);
161 	mContainer.add (helpButton);
162 	mContainer.add (ytButton);
163 	add (mContainer);
164 
165 	//Scan host features for URID map
166 	LV2_URID_Map* m = NULL;
167 	// unmap = NULL;
168 
169 	for (int i = 0; features[i]; ++i)
170 	{
171 		if (strcmp(features[i]->URI, LV2_URID__map) == 0) m = (LV2_URID_Map*) features[i]->data;
172 		// if (strcmp(features[i]->URI, LV2_URID__unmap) == 0) unmap = (LV2_URID_Unmap*) features[i]->data;
173 	}
174 	if ((!m) /*|| (!unmap)*/ ) throw std::invalid_argument ("Host does not support urid:map");
175 
176 	//Map URIS
177 	map = m;
178 	getURIs (map, &urids);
179 
180 	// Initialize forge
181 	lv2_atom_forge_init (&forge,map);
182 }
183 
~BAngrGUI()184 BAngrGUI::~BAngrGUI()
185 {}
186 
portEvent(uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)187 void BAngrGUI::portEvent(uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
188 {
189 	// Notify port
190 	if ((format == urids.atom_eventTransfer) && (port_index == NOTIFY))
191 	{
192 		const LV2_Atom* atom = (const LV2_Atom*) buffer;
193 		if (lv2_atom_forge_is_object_type(&forge, atom->type))
194 		{
195 			const LV2_Atom_Object* obj = (const LV2_Atom_Object*) atom;
196 
197 			if (obj->body.otype == urids.patch_Set)
198 			{
199 				const LV2_Atom* property = NULL;
200       			const LV2_Atom* value    = NULL;
201 				lv2_atom_object_get
202 				(
203 					obj,
204                     urids.patch_property, &property,
205                     urids.patch_value, &value,
206                     NULL
207 				);
208 
209 				if (property && (property->type == urids.atom_URID) && value)
210 				{
211 					const uint32_t key = ((const LV2_Atom_URID*)property)->body;
212 
213 					if ((key == urids.bangr_xcursor) && (value->type == urids.atom_Float))
214 					{
215 						const float xcursor = ((LV2_Atom_Float*)value)->body;
216 						cursor.moveTo ((400.0 + xcursor * 200.0) * sz - 0.5 * cursor.getWidth(), cursor.getPosition().y);
217 					}
218 
219 					else if ((key == urids.bangr_ycursor) && (value->type == urids.atom_Float))
220 					{
221 						const float ycursor = ((LV2_Atom_Float*)value)->body;
222 						cursor.moveTo (cursor.getPosition().x, (180.0 + ycursor * 200.0) * sz - 0.5 * cursor.getHeight());
223 					}
224 				}
225 			}
226 		}
227 	}
228 
229 	// Scan controller ports
230 	else if ((format == 0) && (port_index >= CONTROLLERS) && (port_index < CONTROLLERS + NR_CONTROLLERS))
231 	{
232 		float* pval = (float*) buffer;
233 		controllerWidgets[port_index - CONTROLLERS]->setValue (*pval);
234 	}
235 }
236 
resizeGUI()237 void BAngrGUI::resizeGUI()
238 {
239 	hide ();
240 
241 	// Resize Fonts
242 	defaultFont.setFontSize (12 * sz);
243 	rFont.setFontSize (12 * sz);
244 	lFont.setFontSize (12 * sz);
245 
246 	// Resize Background
247 	cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1000 * sz, 560 * sz);
248 	cairo_t* cr = cairo_create (surface);
249 	cairo_scale (cr, sz, sz);
250 	cairo_set_source_surface(cr, bgImageSurface, 0, 0);
251 	cairo_paint(cr);
252 	widgetBg.loadFillFromCairoSurface(surface);
253 	cairo_destroy (cr);
254 	cairo_surface_destroy (surface);
255 
256 	// Resize widgets
257 	RESIZE (mContainer, 0, 0, 1000, 560, sz);
258 	cursor.resize (40.0 * sz, 40.0 * sz);
259 	RESIZE (poweredLabel, 720, 540, 250, 20, sz);
260 	RESIZE (helpButton, 918, 508, 24, 24, sz);
261 	RESIZE (ytButton, 948, 508, 24, 24, sz);
262 	RESIZE (bypassButton, 900, 30, 20, 20, sz);
263 	RESIZE (bypassLabel, 880, 60, 60, 20, sz);
264 	RESIZE (drywetDial, 940, 20, 40, 40, sz);
265 	RESIZE (drywetLabel, 930, 60, 60, 20, sz);
266 	RESIZE (speedDial, 370, 460, 60, 60, sz);
267 	RESIZE (speedLabel, 370, 520, 60, 20, sz);
268 	RESIZE (spinDial, 570, 460, 60, 60, sz);
269 	RESIZE (spinLabel, 570, 520, 60, 20, sz);
270 	RESIZE (speedScreen, 180, 480, 100, 35, sz);
271 	RESIZE (speedFlexLabel, 220, 520, 100, 20, sz);
272 	RESIZE (speedTypeListbox, 280, 490, 80, 20, sz);
273 	speedTypeListbox.resizeListBox(BUtilities::Point (80 * sz, 120 * sz));
274 	speedTypeListbox.moveListBox(BUtilities::Point (0, -120 * sz));
275 	speedTypeListbox.resizeListBoxItems(BUtilities::Point (80 * sz, 20 * sz));
276 	RESIZE (speedAmountSlider, 200, 485, 80, 20, sz);
277 	RESIZE (spinScreen , 640, 480, 100, 35, sz);
278 	RESIZE (spinFlexLabel, 680, 520, 100, 20, sz);
279 	RESIZE (spinTypeListbox, 740, 490, 80, 20, sz);
280 	spinTypeListbox.resizeListBox(BUtilities::Point (80 * sz, 120 * sz));
281 	spinTypeListbox.moveListBox(BUtilities::Point (0, -120 * sz));
282 	spinTypeListbox.resizeListBoxItems(BUtilities::Point (80 * sz, 20 * sz));
283 	RESIZE (spinAmountSlider, 660, 485, 80, 20, sz);
284 
285 	for (int i = 0; i < NR_FX; ++i)
286 	{
287 		RESIZE (fx[i].container, 20 + int (i / 2) * 660, 100 + int (((i + 1) & 3) / 2) * 200, 300, 160, sz);
288 		RESIZE (fx[i].paramDials[0], 0, 10, 60, 60, sz);
289 		RESIZE (fx[i].paramLabels[0], 0, 70, 60, 20, sz);
290 		RESIZE (fx[i].paramDials[1], 80, 10, 60, 60, sz);
291 		RESIZE (fx[i].paramLabels[1], 80, 70, 60, 20, sz);
292 		RESIZE (fx[i].paramDials[2], 160, 10, 60, 60, sz);
293 		RESIZE (fx[i].paramLabels[2], 160, 70, 60, 20, sz);
294 		RESIZE (fx[i].paramDials[3], 240, 10, 60, 60, sz);
295 		RESIZE (fx[i].paramLabels[3], 240, 70, 60, 20, sz);
296 		RESIZE (fx[i].paramDials[4], 40, 70, 60, 60, sz);
297 		RESIZE (fx[i].paramLabels[4], 40, 130, 60, 20, sz);
298 		RESIZE (fx[i].paramDials[5], 200, 70, 60, 60, sz);
299 		RESIZE (fx[i].paramLabels[5], 200, 130, 60, 20, sz);
300 	}
301 
302 	// Apply changes
303 	applyTheme (theme);
304 	show ();
305 }
306 
applyTheme(BStyles::Theme & theme)307 void BAngrGUI::applyTheme (BStyles::Theme& theme)
308 {
309 	mContainer.applyTheme (theme);
310 	cursor.applyTheme (theme);
311 	poweredLabel.applyTheme (theme);
312 	helpButton.applyTheme (theme);
313 	ytButton.applyTheme (theme);
314 	bypassButton.applyTheme (theme);
315 	bypassLabel.applyTheme (theme);
316 	drywetDial.applyTheme (theme);
317 	drywetLabel.applyTheme (theme);
318 	speedDial.applyTheme (theme);
319 	speedLabel.applyTheme (theme);
320 	spinDial.applyTheme (theme);
321 	spinLabel.applyTheme (theme);
322 	speedScreen.applyTheme (theme);
323 	speedFlexLabel.applyTheme (theme);
324 	speedTypeListbox.applyTheme (theme);
325 	speedAmountSlider.applyTheme (theme);
326 	spinScreen.applyTheme (theme);
327 	spinFlexLabel.applyTheme (theme);
328 	spinTypeListbox.applyTheme (theme);
329 	spinAmountSlider.applyTheme (theme);
330 
331 	for (Fx& f : fx)
332 	{
333 		for (Dial& d : f.paramDials) d.applyTheme (theme);
334 		for (BWidgets::Label& l : f.paramLabels) l.applyTheme (theme);
335 	}
336 }
337 
onConfigureRequest(BEvents::ExposeEvent * event)338 void BAngrGUI::onConfigureRequest (BEvents::ExposeEvent* event)
339 {
340 	Window::onConfigureRequest (event);
341 
342 	sz = (getWidth() / 1000 > getHeight() / 560 ? getHeight() / 560 : getWidth() / 1000);
343 	resizeGUI ();
344 }
345 
sendCursor()346 void BAngrGUI::sendCursor ()
347 {
348 	sendCursorOn();
349 	sendXCursor();
350 	sendYCursor();
351 }
352 
sendXCursor()353 void BAngrGUI::sendXCursor ()
354 {
355 	uint8_t obj_buf[128];
356 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
357 
358 	LV2_Atom_Forge_Frame frame;
359 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, urids.patch_Set);
360 	lv2_atom_forge_key(&forge, urids.patch_property);
361 	lv2_atom_forge_urid(&forge, urids.bangr_xcursor);
362 	lv2_atom_forge_key(&forge, urids.patch_value);
363 	lv2_atom_forge_float(&forge, ((cursor.getPosition().x + 0.5 * cursor.getWidth()) / sz - 400.0) / 200.0);
364 	lv2_atom_forge_pop(&forge, &frame);
365 	write_function(controller, CONTROL, lv2_atom_total_size(msg), urids.atom_eventTransfer, msg);
366 }
367 
sendYCursor()368 void BAngrGUI::sendYCursor ()
369 {
370 	uint8_t obj_buf[128];
371 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
372 
373 	LV2_Atom_Forge_Frame frame;
374 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, urids.patch_Set);
375 	lv2_atom_forge_key(&forge, urids.patch_property);
376 	lv2_atom_forge_urid(&forge, urids.bangr_ycursor);
377 	lv2_atom_forge_key(&forge, urids.patch_value);
378 	lv2_atom_forge_float(&forge, ((cursor.getPosition().y + 0.5 * cursor.getHeight()) / sz - 180.0) / 200.0);
379 	lv2_atom_forge_pop(&forge, &frame);
380 	write_function(controller, CONTROL, lv2_atom_total_size(msg), urids.atom_eventTransfer, msg);
381 }
382 
sendCursorOn()383 void BAngrGUI::sendCursorOn ()
384 {
385 	uint8_t obj_buf[64];
386 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
387 
388 	LV2_Atom_Forge_Frame frame;
389 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, urids.bangr_cursorOn);
390 	lv2_atom_forge_pop(&forge, &frame);
391 	write_function(controller, CONTROL, lv2_atom_total_size(msg), urids.atom_eventTransfer, msg);
392 }
393 
sendCursorOff()394 void BAngrGUI::sendCursorOff ()
395 {
396 	uint8_t obj_buf[64];
397 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
398 
399 	LV2_Atom_Forge_Frame frame;
400 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, urids.bangr_cursorOff);
401 	lv2_atom_forge_pop(&forge, &frame);
402 	write_function(controller, CONTROL, lv2_atom_total_size(msg), urids.atom_eventTransfer, msg);
403 }
404 
valueChangedCallback(BEvents::Event * event)405 void BAngrGUI::valueChangedCallback (BEvents::Event* event)
406 {
407 	if (!event) return;
408 	BWidgets::ValueWidget* widget = (BWidgets::ValueWidget*) event->getWidget ();
409 	if (!widget) return;
410 	float value = widget->getValue();
411 	BAngrGUI* ui = (BAngrGUI*) widget->getMainWindow();
412 	if (!ui) return;
413 
414 	int controllerNr = -1;
415 
416 	// Identify controller
417 	for (int i = 0; i < NR_CONTROLLERS; ++i)
418 	{
419 		if (widget == ui->controllerWidgets[i])
420 		{
421 			controllerNr = i;
422 			break;
423 		}
424 	}
425 
426 	// Controllers
427 	if (controllerNr >= 0)
428 	{
429 		if (controllerNr == SPEED_TYPE)
430 		{
431 			if (value == RANDOM) ui->speedScreen.show();
432 			else ui->speedScreen.hide();
433 		}
434 
435 		if (controllerNr == SPIN_TYPE)
436 		{
437 			if (value == RANDOM) ui->spinScreen.show();
438 			else ui->spinScreen.hide();
439 		}
440 
441 		ui->write_function(ui->controller, CONTROLLERS + controllerNr, sizeof(float), 0, &value);
442 	}
443 }
444 
cursorDraggedCallback(BEvents::Event * event)445 void BAngrGUI::cursorDraggedCallback (BEvents::Event* event)
446 {
447 	if (!event) return;
448 	BEvents::PointerEvent* pev = (BEvents::PointerEvent*)event;
449 	Dot* widget = (Dot*) event->getWidget ();
450 	if (!widget) return;
451 	BAngrGUI* ui = (BAngrGUI*) widget->getMainWindow();
452 	if (!ui) return;
453 	if (widget != &ui->cursor) return;
454 
455 	double x = ui->cursor.getPosition().x + 0.5 * ui->cursor.getWidth() + pev->getDelta().x;
456 	double y = ui->cursor.getPosition().y + 0.5 * ui->cursor.getHeight() + pev->getDelta().y;
457 
458 	if (x < 400 * ui->sz) x = 400 * ui->sz;
459 	if (x > 600 * ui->sz) x = 600 * ui->sz;
460 	if (y < 180 * ui->sz) y = 180 * ui->sz;
461 	if (y > 380 * ui->sz) y = 380 * ui->sz;
462 
463 	ui->cursor.moveTo (x - 0.5 * ui->cursor.getWidth(), y - 0.5 * ui->cursor.getHeight());
464 	ui->sendCursor();
465 }
466 
cursorReleasedCallback(BEvents::Event * event)467 void BAngrGUI::cursorReleasedCallback (BEvents::Event* event)
468 {
469 	if (!event) return;
470 	//BEvents::PointerEvent* pev = (BEvents::PointerEvent*)event;
471 	Dot* widget = (Dot*) event->getWidget ();
472 	if (!widget) return;
473 	BAngrGUI* ui = (BAngrGUI*) widget->getMainWindow();
474 	if (!ui) return;
475 	if (widget != &ui->cursor) return;
476 
477 	ui->sendCursorOff();
478 }
479 
xregionClickedCallback(BEvents::Event * event)480 void BAngrGUI::xregionClickedCallback (BEvents::Event* event)
481 {
482 	char cmd[] = WWW_BROWSER_CMD;
483 	char param[] = XREGION_URL;
484 	char* argv[] = {cmd, param, NULL};
485 	std::cerr << "BAngr.lv2#GUI: Call " << XREGION_URL << " for Airwindows XRegion.\n";
486 	if (BUtilities::vsystem (argv) == -1) std::cerr << "BAngr.lv2#GUI: Couldn't fork.\n";
487 }
488 
helpButtonClickedCallback(BEvents::Event * event)489 void BAngrGUI::helpButtonClickedCallback (BEvents::Event* event)
490 {
491 	char cmd[] = WWW_BROWSER_CMD;
492 	char param[] = HELP_URL;
493 	char* argv[] = {cmd, param, NULL};
494 	std::cerr << "BAngr.lv2#GUI: Call " << HELP_URL << " for help.\n";
495 	if (BUtilities::vsystem (argv) == -1) std::cerr << "BAngr.lv2#GUI: Couldn't fork.\n";
496 }
497 
ytButtonClickedCallback(BEvents::Event * event)498 void BAngrGUI::ytButtonClickedCallback (BEvents::Event* event)
499 {
500 	char cmd[] = WWW_BROWSER_CMD;
501 	char param[] = YT_URL;
502 	char* argv[] = {cmd, param, NULL};
503 	std::cerr << "BAngr.lv2#GUI: Call " << YT_URL << " for preview video.\n";
504 	if (BUtilities::vsystem (argv) == -1) std::cerr << "BAngr.lv2#GUI: Couldn't fork.\n";
505 }
506 
instantiate(const LV2UI_Descriptor * descriptor,const char * plugin_uri,const char * bundle_path,LV2UI_Write_Function write_function,LV2UI_Controller controller,LV2UI_Widget * widget,const LV2_Feature * const * features)507 static LV2UI_Handle instantiate (const LV2UI_Descriptor *descriptor, const char *plugin_uri, const char *bundle_path,
508 						  LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget *widget,
509 						  const LV2_Feature *const *features)
510 {
511 	PuglNativeView parentWindow = 0;
512 	LV2UI_Resize* resize = NULL;
513 
514 	if (strcmp(plugin_uri, BANGR_URI) != 0)
515 	{
516 		std::cerr << "BAngr.lv2#GUI: GUI does not support plugin with URI " << plugin_uri << std::endl;
517 		return NULL;
518 	}
519 
520 	for (int i = 0; features[i]; ++i)
521 	{
522 		if (!strcmp(features[i]->URI, LV2_UI__parent)) parentWindow = (PuglNativeView) features[i]->data;
523 		else if (!strcmp(features[i]->URI, LV2_UI__resize)) resize = (LV2UI_Resize*)features[i]->data;
524 	}
525 	if (parentWindow == 0) std::cerr << "BAngr.lv2#GUI: No parent window.\n";
526 
527 	// New instance
528 	BAngrGUI* ui;
529 	try {ui = new BAngrGUI (bundle_path, features, parentWindow);}
530 	catch (std::exception& exc)
531 	{
532 		std::cerr << "BAngr.lv2#GUI: Instantiation failed. " << exc.what () << std::endl;
533 		return NULL;
534 	}
535 
536 	ui->controller = controller;
537 	ui->write_function = write_function;
538 
539 	// Reduce min GUI size for small displays
540 	double sz = 1.0;
541 	int screenWidth  = getScreenWidth ();
542 	int screenHeight = getScreenHeight ();
543 	if ((screenWidth < 1040) || (screenHeight < 600)) sz = 0.66;
544 
545 	/*
546 	std::cerr << "BAngrGUI.lv2 screen size " << screenWidth << " x " << screenHeight <<
547 			". Set GUI size to " << 800 * sz << " x " << 560 * sz << ".\n";
548 	*/
549 
550 	if (resize) resize->ui_resize(resize->handle, 1000 * sz, 560 * sz);
551 
552 	*widget = (LV2UI_Widget) puglGetNativeWindow (ui->getPuglView ());
553 	return (LV2UI_Handle) ui;
554 }
555 
cleanup(LV2UI_Handle ui)556 static void cleanup(LV2UI_Handle ui)
557 {
558 	BAngrGUI* pluginGui = (BAngrGUI*) ui;
559 	if (pluginGui) delete pluginGui;
560 }
561 
portEvent(LV2UI_Handle ui,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)562 static void portEvent(LV2UI_Handle ui, uint32_t port_index, uint32_t buffer_size,
563 	uint32_t format, const void* buffer)
564 {
565 	BAngrGUI* pluginGui = (BAngrGUI*) ui;
566 	if (pluginGui) pluginGui->portEvent(port_index, buffer_size, format, buffer);
567 }
568 
callIdle(LV2UI_Handle ui)569 static int callIdle (LV2UI_Handle ui)
570 {
571 	BAngrGUI* pluginGui = (BAngrGUI*) ui;
572 	if (pluginGui) pluginGui->handleEvents ();
573 	return 0;
574 }
575 
callResize(LV2UI_Handle ui,int width,int height)576 static int callResize (LV2UI_Handle ui, int width, int height)
577 {
578 	BAngrGUI* self = (BAngrGUI*) ui;
579 	if (!self) return 0;
580 
581 	BEvents::ExposeEvent* ev = new BEvents::ExposeEvent (self, self, BEvents::CONFIGURE_REQUEST_EVENT, self->getPosition().x, self->getPosition().y, width, height);
582 	self->addEventToQueue (ev);
583 	return 0;
584 }
585 
586 static const LV2UI_Idle_Interface idle = {callIdle};
587 static const LV2UI_Resize resize = {nullptr, callResize};
588 
extensionData(const char * uri)589 static const void* extensionData(const char* uri)
590 {
591 	if (!strcmp(uri, LV2_UI__idleInterface)) return &idle;
592 	else if(!strcmp(uri, LV2_UI__resize)) return &resize;
593 	else return NULL;
594 }
595 
596 static const LV2UI_Descriptor guiDescriptor =
597 {
598 		BANGR_GUI_URI,
599 		instantiate,
600 		cleanup,
601 		portEvent,
602 		extensionData
603 };
604 
605 // LV2 Symbol Export
lv2ui_descriptor(uint32_t index)606 LV2_SYMBOL_EXPORT const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index)
607 {
608 	switch (index) {
609 	case 0: return &guiDescriptor;
610 	default:return NULL;
611     }
612 }
613