1 
2 /*
3  *  Diverse Bristol audio routines.
4  *  Copyright (c) by Nick Copeland <nickycopeland@hotmail.com> 1996,2012
5  *
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 3 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 /*
23  * This will be a rotary potmeter. Takes a bitmap and rotates it according to
24  * input from the mouse/keyboard. We need a few different parameters, and a
25  * hefty include file. Where possible will try and keep X11 requests in a
26  * separate set of files.
27  */
28 
29 #include <math.h>
30 
31 #include "brightoninternals.h"
32 /*#include "brightonX11.h" */
33 
34 extern void brightonPanelLocation(brightonWindow *, int, int, int, int, int, int);
35 
36 int
destroyRotary(brightonDevice * dev)37 destroyRotary(brightonDevice *dev)
38 {
39 	printf("destroyRotary()\n");
40 
41 	if (dev->image)
42 		brightonFreeBitmap(dev->bwin, dev->image);
43 	dev->image = NULL;
44 
45 	return(0);
46 }
47 
48 static int
displayrotary(brightonDevice * dev)49 displayrotary(brightonDevice *dev)
50 {
51 	if (dev->bwin->app->resources[dev->panel].flags & BRIGHTON_WITHDRAWN)
52 		return(0);
53 
54 	if (dev->bwin->app->resources[dev->panel].devlocn[dev->index].flags
55 		& BRIGHTON_WITHDRAWN)
56 		return(0);
57 
58 	/*
59 	 * Build up a smooth position for the pot. We may need to adjust this based
60 	 * on the to/from values.
61 	 */
62 	if (dev->bwin->app->resources[dev->panel].devlocn[dev->index].flags
63 		& BRIGHTON_STEPPED)
64 	{
65 		dev->position = dev->value < 0.5?
66 			1 * M_PI * (1 - 2 * dev->value) / 3:
67 			M_PI * (7 - dev->value * 2) / 3;
68 	} else
69 		dev->position = dev->value < 0.5?
70 			7 * M_PI * (1 - 2 * dev->value) / 8:
71 			M_PI * (23 - dev->value * 14) / 8;
72 
73 	/*
74 	 * Only draw fixed number of steps.
75 	 */
76 	if ((int) (dev->value * 360 / M_PI) != (int) (dev->lastvalue * 360 / M_PI))
77 	{
78 		brightonIResource *panel;
79 
80 		if (dev->bwin->app->resources[dev->panel].devlocn[dev->index].flags
81 			& BRIGHTON_REDRAW)
82 		{
83 			brightonDevUndraw(dev->bwin, dev->bwin->dlayer,
84 				dev->x + dev->bwin->app->resources[dev->panel].sx,
85 				dev->y + dev->bwin->app->resources[dev->panel].sy,
86 				dev->width, dev->height);
87 		}
88 
89 		panel = &dev->bwin->app->resources[dev->panel];
90 
91 		/*
92 		 * Rotate my image onto the parents canvas. For rotaries that have blue
93 		 * bitmaps its needs to be made sure that the background is clear
94 		 * before this rotation is done. This means we have to copy the image
95 		 * background into place first.
96 		 */
97 		brightonRender(dev->bwin, dev->bwin->dlayer, dev->bwin->dlayer,
98 			dev->x + dev->bwin->app->resources[dev->panel].sx,
99 			dev->y + dev->bwin->app->resources[dev->panel].sy,
100 			dev->width, dev->height, 0);
101 		brightonRotate(dev->bwin, dev->image,
102 			dev->bwin->dlayer,
103 			dev->x + dev->bwin->app->resources[dev->panel].sx,
104 			dev->y + dev->bwin->app->resources[dev->panel].sy,
105 			dev->width, dev->height,
106 			dev->position);
107 
108 		/*
109 		 * We can consider the alpha layer here.
110 		 */
111 		if (dev->image2 != 0)
112 		{
113 			brightonAlphaLayer(dev->bwin, dev->image2,
114 				dev->bwin->dlayer,
115 				dev->x + dev->bwin->app->resources[dev->panel].sx,
116 				dev->y + dev->bwin->app->resources[dev->panel].sy,
117 				dev->width, dev->height,
118 				dev->position);
119 		}
120 
121 		/*
122 		 * And request the panel to put this onto the respective image.
123 		 */
124 		brightonFinalRender(dev->bwin,
125 			dev->x + dev->bwin->app->resources[dev->panel].sx,
126 			dev->y + dev->bwin->app->resources[dev->panel].sy,
127 			dev->width, dev->height);
128 	}
129 
130 	dev->lastvalue = dev->value;
131 	dev->lastposition = dev->position;
132 
133 	return(0);
134 }
135 
136 /*
137  * This will go into brighton render
138  */
139 static void
renderHighlights(brightonWindow * bwin,brightonDevice * dev)140 renderHighlights(brightonWindow *bwin, brightonDevice *dev)
141 {
142 	float d, ho2, streak;
143 	float ox, oy, dx, dy;
144 
145 	if (dev->shadow.coords == 0)
146 		dev->shadow.coords = brightonmalloc(7 * sizeof(brightonCoord));
147 	dev->shadow.ccount = 7;
148 	dev->shadow.flags = BRIGHTON_STATIC;
149 
150 	ho2 = dev->width / 2;
151 	ox = dev->x + ho2;
152 	oy = dev->y + ho2;
153 
154 	/*
155 	 * We are going to render the shadow directly onto the background bitmap.
156 	 * We have X and Y for the source of the shadow, plus its height and
157 	 * intensity. For now we will take a default relief, and highlight the
158 	 * background accordingly. This should be a 3D transform.....
159 	 *
160 	 * This can all be done with fractional distances, since we are going to
161 	 * be dealing with a number of similar triangles.
162 	 */
163 	dx = ox - bwin->lightX;
164 	dy = oy - bwin->lightY;
165 	d = sqrt((double) (dx * dx + dy * dy));
166 
167 	dev->shadow.coords[0].x = ox + dy * ho2 / d;
168 	dev->shadow.coords[0].y = oy - dx * ho2 / d;
169 	dev->shadow.coords[1].x = ox - dy * ho2 / d;
170 	dev->shadow.coords[1].y = oy + dx * ho2 / d;
171 
172 	streak = (dev->width * 1.6 * d / bwin->lightH)
173 		/ (1 - dev->width * 1.6 / bwin->lightH);
174 
175 	dev->shadow.coords[2].x = dev->shadow.coords[1].x
176 		+ streak * dx / d + dy * ho2 * 0.4 / d;
177 	dev->shadow.coords[2].y = dev->shadow.coords[1].y
178 		+ streak * dy / d - dx * ho2 * 0.4 / d;
179 
180 	dev->shadow.coords[6].x = dev->shadow.coords[0].x
181 		+ streak * dx / d - dy * ho2 * 0.4 / d;
182 	dev->shadow.coords[6].y = dev->shadow.coords[0].y
183 		+ streak * dy / d + dx * ho2 * 0.4 / d;
184 
185 	streak = (dev->width * 2.07 * d / bwin->lightH)
186 		/ (1 - dev->width * 2.07 / bwin->lightH);
187 
188 	dev->shadow.coords[3].x = ox + (streak * dx / d) - dy * ho2 * 0.5 / d;
189 	dev->shadow.coords[3].y = oy + (streak * dy / d) + dx * ho2 * 0.5 / d;
190 	dev->shadow.coords[5].x = ox + (streak * dx / d) + dy * ho2 * 0.5 / d;
191 	dev->shadow.coords[5].y = oy + (streak * dy / d) - dx * ho2 * 0.5 / d;
192 
193 	streak = (dev->width * 2.2 * d / bwin->lightH)
194 		/ (1 - dev->width * 2.2 / bwin->lightH);
195 
196 	dev->shadow.coords[4].x = ox + (streak * dx / d);
197 	dev->shadow.coords[4].y = oy + (streak * dy / d);
198 
199 	/*
200 	printf("renderHighlights(%i) %i,%i-%i,%i-%i,%i-%i,%i-%i,%i-%i,%i-%i,%i\n",
201 		dev->index, dev->shadow.coords[0].x,dev->shadow.coords[0].y,
202 		dev->shadow.coords[1].x,dev->shadow.coords[1].y,
203 		dev->shadow.coords[2].x,dev->shadow.coords[2].y,
204 		dev->shadow.coords[3].x,dev->shadow.coords[3].y,
205 		dev->shadow.coords[4].x,dev->shadow.coords[4].y,
206 		dev->shadow.coords[5].x,dev->shadow.coords[5].y,
207 		dev->shadow.coords[6].x,dev->shadow.coords[6].y);
208 	 */
209 
210 	dev->shadow.ccount = 7;
211 	/*
212 	 * rather than fill the polygon, we need to generate this shape, and lower
213 	 * the shading of pixels that are within this area.
214 	 */
215 }
216 
217 static void
considercallback(brightonDevice * dev)218 considercallback(brightonDevice *dev)
219 {
220 	brightonIResource *panel = &dev->bwin->app->resources[dev->panel];
221 
222 	if (dev->bwin->flags & BRIGHTON_NO_DRAW)
223 		return;
224 
225 	if (dev->value < 0)
226 		dev->value = 0;
227 
228 	/*
229 	 * We now need to consider rounding this to the resolution of this
230 	 * device. If the max value is not 1.0 then we need to put fixed steps
231 	 * into our new device value.
232 	 */
233 	if (dev->bwin->app->resources[dev->panel].devlocn[dev->index].to != 1.0)
234 	{
235 		dev->value =
236 			(dev->value
237 			* dev->bwin->app->resources[dev->panel].devlocn[dev->index].to);
238 
239 		if ((dev->value - ((int) dev->value)) > 0.5)
240 			dev->value = ((float) ((int) dev->value) + 1)
241 				/ dev->bwin->app->resources[dev->panel].devlocn[dev->index].to;
242 		else
243 			dev->value = ((float) ((int) dev->value))
244 				/ dev->bwin->app->resources[dev->panel].devlocn[dev->index].to;
245 	}
246 
247 	/*
248 	 * I am not sure this is desired functionality. Yes, it reduces the total
249 	 * number of events sent, but there are issues with multifunction panels
250 	 * and memory loading.
251 	if (dev->lastvalue != dev->value)
252 	 */
253 	{
254 		if (panel->devlocn[dev->index].callback)
255 		{
256 			panel->devlocn[dev->index].callback(dev->bwin,
257 				dev->panel, dev->index,
258 				dev->value * panel->devlocn[dev->index].to);
259 		} else if (panel->callback) {
260 			panel->callback(dev->bwin, dev->panel, dev->index,
261 				dev->value * panel->devlocn[dev->index].to);
262 		}
263 	}
264 }
265 
266 static int cx, cy;
267 static float sval;
268 
269 static int
configure(brightonDevice * dev,brightonEvent * event)270 configure(brightonDevice *dev, brightonEvent *event)
271 {
272 /*printf("configureRotary(%i)\n", event->command); */
273 
274 	if (event->command == -1)
275 		return(-1);
276 
277 	if (event->command == BRIGHTON_BUTTONPRESS)
278 	{
279 		/*
280 		 * This is hard coded, it calls back to the GUI. This is incorrect as
281 		 * the callback dispatcher should be requested by the GUI.
282 		 *
283 		 * Perhaps the MIDI code should actually be in the same library? Why
284 		 * does the GUI need to know about this?
285 		 */
286 		if (event->key == BRIGHTON_BUTTON2)
287 			brightonRegisterController(dev);
288 
289 		return(0);
290 	}
291 
292 	if (event->command == BRIGHTON_BUTTONRELEASE)
293 	{
294 		cx = cy = sval = -1;
295 		dev->flags &= ~BRIGHTON_CONTROLKEY;
296 		return(0);
297 	}
298 
299 	if (event->command == BRIGHTON_RESIZE)
300 	{
301 		dev->originx = event->x;
302 		dev->originy = event->y;
303 
304 		if (event->w < event->h)
305 		{
306 			/*
307 			 * If width is less than height, then we need to configure
308 			 * some offsets. Also, we only want even number of pixel areas.
309 			 */
310 			dev->x = event->x;
311 			dev->y = event->y;
312 			dev->width = event->w & ~0x01;
313 			dev->height = event->w & ~0x01;
314 		} else if (event->w > event->h) {
315 			dev->x = event->x;
316 			dev->y = event->y;
317 			dev->width = event->h & ~0x01;
318 			dev->height = event->h & ~0x01;
319 		} else {
320 			dev->x = event->x;
321 			dev->y = event->y;
322 			dev->width = event->w & ~0x01;
323 			dev->height = event->h & ~0x01;
324 		}
325 
326 		/*
327 		 * We should now rework our parent understanding of our window, since
328 		 * it will have altered.
329 		 */
330 		brightonPanelLocation(dev->bwin,
331 			dev->panel, dev->index, dev->x, dev->y, dev->width, dev->height);
332 
333 		considercallback(dev);
334 
335 		if (dev->bwin->app->resources[dev->panel].devlocn[dev->index].flags
336 			& BRIGHTON_WITHDRAWN)
337 			return(0);
338 
339 		/*
340 		 * We need to build in some shadow, to prevent the rotary from looking
341 		 * like it is hanging in mid air.
342 		 */
343 		brightonRenderShadow(dev, 0);
344 
345 		dev->lastvalue = -1;
346 		displayrotary(dev);
347 
348 		return(0);
349 	}
350 
351 	if (event->command == BRIGHTON_KEYRELEASE)
352 	{
353 		switch(event->key) {
354 			default:
355 				break;
356 			case 37:
357 			case 109:
358 			case 65508:
359 			case 65507:
360 				dev->flags &= ~BRIGHTON_CONTROLKEY;
361 				cx = cy = sval = -1;
362 				break;
363 			case 50:
364 			case 62:
365 			case 65505:
366 				dev->flags &= ~BRIGHTON_SHIFTKEY;
367 				break;
368 		}
369 	}
370 
371 	if (event->command == BRIGHTON_KEYPRESS)
372 	{
373 		switch(event->key) {
374 			default:
375 				break;
376 			case 37:
377 			case 109:
378 			case 65508:
379 			case 65507:
380 				cx = event->x;
381 				cy = event->y;
382 				sval = dev->value;
383 				dev->flags |= BRIGHTON_CONTROLKEY;
384 				break;
385 			case 50:
386 			case 62:
387 			case 65505:
388 				dev->flags |= BRIGHTON_SHIFTKEY;
389 				break;
390 			case 0x6b:
391 			case 0xff52:
392 				/*
393 				 * UP arrow - we need to put some bristol keysyms in here. For
394 				 * portability we should not include the X11 headers but map
395 				 * our own translations.
396 				 */
397 				if (dev->flags & BRIGHTON_SHIFTKEY)
398 				{
399 					if ((dev->value += ((float) 256) / 16384) > 1.0)
400 						dev->value = 1.0;
401 				} else {
402 					if ((dev->value += ((float) 1) / 16384) > 1.0)
403 						dev->value = 1.0;
404 				}
405 				break;
406 			case 0x6a:
407 			case 0xff54:
408 				/*
409 				 * Down arrow
410 				 * We should have this consider stepped rotary motion where a
411 				 * single up or down would give a whole step rather than a
412 				 * micromovement
413 				 */
414 				if (dev->flags & BRIGHTON_SHIFTKEY)
415 				{
416 					if ((dev->value -= ((float) 256) / 16384) < 0)
417 						dev->value = 0;
418 				} else {
419 					if ((dev->value -= ((float) 1) / 16384) < 0)
420 						dev->value = 0;
421 				}
422 				break;
423 			case 0xff51:
424 				/* Left arrow */
425 				if (dev->flags & BRIGHTON_SHIFTKEY)
426 				{
427 					if ((dev->value -= ((float) 2048) / 16384) < 0)
428 						dev->value = 0;
429 				} else {
430 					if ((dev->value -= ((float) 32) / 16384) < 0)
431 						dev->value = 0;
432 				}
433 				break;
434 			case 0xff53:
435 				/* Right arrow */
436 				if (dev->flags & BRIGHTON_SHIFTKEY)
437 				{
438 					if ((dev->value += ((float) 2048) / 16384) > 1.0)
439 						dev->value = 1.0;
440 				} else {
441 					if ((dev->value += ((float) 32) / 16384) > 1.0)
442 						dev->value = 1.0;
443 				}
444 				break;
445 		}
446 
447 		considercallback(dev);
448 
449 		displayrotary(dev);
450 	}
451 
452 	/*
453 	 * Adding a fine adjustment control, and adding a notch into the motion
454 	 * control under option
455 	 */
456 	if (event->command == BRIGHTON_MOTION)
457 	{
458 		if (dev->flags & BRIGHTON_CONTROLKEY)
459 		{
460 			float deltax;
461 
462 			/*
463 			 * We should have this consider stepped rotary motion where a single
464 			 * up or down would give a whole step rather than a micromovement
465 			 */
466 			if (cx == -1)
467 			{
468 				sval = dev->value;
469 				cx = event->x;
470 				cy = event->y;
471 			}
472 
473 			deltax = ((float) (event->x - cx)) / 16383.0f;
474 
475 			dev->value = sval + deltax;
476 
477 /*printf("Controlled motion from (%i, %i) to (%i, %i): %f\n", */
478 /*cx, cy, event->x, event->y, deltax); */
479 		} else if (dev->bwin->flags & BRIGHTON_ROTARY_UD) {
480 			if (dev->bwin->app->resources[dev->panel].devlocn[dev->index].flags
481 				& BRIGHTON_NOTCH)
482 			{
483 				dev->value = 1.0f - ((float) (event->y)) * 1.1f
484 					/ (dev->bwin->height);
485 
486 				/* If in range 0.45/0.55 - interpret 0.5 */
487 				if (dev->value > 0.55)
488 					dev->value -= 0.05;
489 				else if (dev->value < 0.45)
490 					dev->value += 0.05;
491 				else
492 					dev->value = 0.5;
493 
494 				if (dev->value < 0.0f)
495 					dev->value = 0.0f;
496 				else if (dev->value > 1.0f)
497 					dev->value = 1.0f;
498 			} else {
499 				if ((dev->value = 1.0f - ((float) event->y)
500 					/ (dev->bwin->height)) < 0)
501 					dev->value = 0.0f;
502 				else if (dev->value > 1.0f)
503 					dev->value = 1.0f;
504 			}
505 			//printf("%f %i %i, %i %i\n", dev->value,
506 				//event->x, event->y, event->ox, event->oy);
507 		} else {
508 			double angle, diffx, diffy;
509 
510 			diffx = event->x - (dev->width / 2 + dev->x);
511 			diffy = event->y - (dev->height / 2 + dev->y);
512 			angle = atan(diffy / diffx);
513 
514 			/*
515 			 * Adjust this so that we get counterclock rotating angles from
516 			 * mid top.
517 			 */
518 			if (((diffx < 0) && (diffy < 0)) || ((diffx < 0) && (diffy >= 0)))
519 				angle = M_PI / 2 - angle;
520 			else
521 				angle = 3 * M_PI / 2 - angle;
522 
523 			/*
524 			 * To correct to clock rotating angles, with stop points.
525 			 */
526 			if (dev->bwin->app->resources[dev->panel].devlocn[dev->index].flags
527 				& BRIGHTON_STEPPED)
528 			{
529 				/*
530 				 * Stepped controllers have limited motion, not from 145 to
531 				 * -145 but from 60 to -60
532 				 *
533 				 * The '7' is 2M_PI + M_PI/3
534 				 */
535 				if (angle < M_PI)
536 					dev->value = (M_PI / 3 - angle) / 2;
537 				else
538 					dev->value = (7 * M_PI / 3 - angle) / 2;
539 			} else {
540 				/*
541 				 * If the angle is negative then we are PM on clockface and
542 				 * we have motion up to 7/8 of M_PI.
543 				 *
544 				 * If the angle is positive then we are AM on clockface and
545 				 * we have motion from 7/8.
546 				 *
547 				 * The '23' is 2M_PI + 7M_PI/8
548 				 */
549 				if (angle < M_PI)
550 					dev->value = (7 * M_PI / 8 - angle) * 4 / (M_PI * 7);
551 				else
552 					dev->value = (23 * M_PI / 8 - angle) * 4 / (M_PI * 7);
553 			}
554 
555 			if (dev->bwin->app->resources[dev->panel].devlocn[dev->index].flags
556 				& BRIGHTON_NOTCH)
557 			{
558 				if (dev->value > 0.55)
559 					dev->value -= 0.05;
560 				else if (dev->value < 0.45)
561 					dev->value += 0.05;
562 				else
563 					dev->value = 0.5;
564 			}
565 		}
566 
567 		if (dev->value < 0)
568 			dev->value = 0;
569 		else if (dev->value > 1.0)
570 			dev->value = 1.0;
571 
572 		considercallback(dev);
573 
574 		displayrotary(dev);
575 
576 		return(0);
577 	}
578 
579 	if (event->command == BRIGHTON_PARAMCHANGE)
580 	{
581 		dev->value = event->value
582 			/ dev->bwin->app->resources[dev->panel].devlocn[dev->index].to;
583 
584 		considercallback(dev);
585 
586 		displayrotary(dev);
587 
588 		return(0);
589 	}
590 
591 	return(0);
592 }
593 
594 int *
createRotary(brightonWindow * bwin,brightonDevice * dev,int index,char * bitmap)595 createRotary(brightonWindow *bwin, brightonDevice *dev, int index, char *bitmap)
596 {
597 	/*printf("createRotary(%s)\n", bitmap); */
598 
599 	dev->destroy = destroyRotary;
600 	dev->configure = configure;
601 	dev->bwin = bwin;
602 	dev->index = index;
603 
604 	if (bitmap == 0)
605 	{
606 		if (dev->image)
607 			brightonFreeBitmap(bwin, dev->image);
608 		/*
609 		 * Open the default bitmap
610 		 */
611 		if (bwin->app->resources[dev->panel].devlocn[dev->index].image != 0)
612 			dev->image =
613 				bwin->app->resources[dev->panel].devlocn[dev->index].image;
614 		else
615 			dev->image = brightonReadImage(bwin, "bitmaps/knobs/knob.xpm");
616 	} else {
617 		if (dev->image)
618 			brightonFreeBitmap(bwin, dev->image);
619 		dev->image = brightonReadImage(bwin, bitmap);
620 	}
621 
622 	/*
623 	 * We should take a peek at the second image resource. A rotary only uses
624 	 * a single bitmap, the second may be used as an alpha layer.
625 	 */
626 	if (bwin->app->resources[dev->panel].devlocn[dev->index].image2 != 0)
627 		dev->image2 =
628 			bwin->app->resources[dev->panel].devlocn[dev->index].image2;
629 
630 	/*
631 	 * These will force an update when we first display ourselves.
632 	 */
633 	dev->value = 0;
634 	dev->lastvalue = -1;
635 	dev->lastposition = -1;
636 
637 	return(0);
638 }
639 
640