1 /*
2 * Copyright (C) 2018-2020 Oleg Kapitonov
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 * --------------------------------------------------------------------------
18 */
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <libgen.h>
23 #include <limits.h>
24 #include <math.h>
25 #include <stdint.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include "lv2/lv2plug.in/ns/lv2core/lv2.h"
30 #include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
31 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
32 #include "lv2/lv2plug.in/ns/ext/atom/forge.h"
33 #include "lv2/lv2plug.in/ns/ext/patch/patch.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37
38 #include <cairo.h>
39 #include <cairo-xcb.h>
40
41 #include <xcb/xcb.h>
42 #include <xcb/xcb_aux.h>
43 #include <xcb/xcb_icccm.h>
44
45 #include "kpp_file_picker.h"
46
47 // Port numbers for communication with the main LV2 plugin module
48 enum {PORT_BASS, PORT_MIDDLE, PORT_TREBLE, PORT_DRIVE, PORT_MASTERGAIN, PORT_VOLUME,
49 PORT_CABINET, PORT_IN0, PORT_IN1, PORT_OUT0, PORT_OUT1, PORT_CONTROL, PORT_NOTIFY};
50
51 // Dial GUI element structure
52 typedef struct
53 {
54 int value; // 0 - 100
55 int start_value; // Holds value at the start of dialing
56 int base_x; // Coordinates of Dial in main window
57 int base_y; //
58 } st_dial;
59
60 // Slider GUI element structure
61 typedef struct
62 {
63 int value; // 0 - 100
64 int start_value; // Holds value at the start of sliding
65 int base_x; // Coordinates of Slider in main window
66 int base_y; //
67 } st_slider;
68
69 // Point
70 typedef struct
71 {
72 int x;
73 int y;
74 }st_point;
75
76 // Color
77 typedef struct
78 {
79 double r;
80 double g;
81 double b;
82 }st_rgb;
83
84 typedef struct {
85 LV2_URID atom_eventTransfer;
86 LV2_URID patch_Get;
87 LV2_URID patch_Set;
88 LV2_URID profile;
89 LV2_URID patch_property;
90 LV2_URID patch_value;
91 LV2_URID atom_Path;
92 } tubeAmpURIs;
93
94 // GUI window structure
95 typedef struct
96 {
97 xcb_connection_t *connection;
98
99 xcb_window_t win;
100
101 int width, height;
102
103 void *controller;
104
105 LV2UI_Write_Function write_function;
106 LV2UI_Resize* resize;
107 LV2_URID_Map* map;
108
109 LV2_Atom_Forge forge; // Forge for writing atoms in run thread
110
111 uint8_t forge_buf[4096];
112
113 tubeAmpURIs uris;
114
115 // Main window
116 //win_t win;
117
118 // Dials
119 st_dial driveDial;
120
121 st_dial bassDial;
122 st_dial middleDial;
123 st_dial trebleDial;
124
125 st_dial mastergainDial;
126
127 // Sliders
128
129 st_slider levelSlider;
130 st_slider cabinetSlider;
131
132 // Hold mouse coordinates at the start of dialing
133 int pos_x;
134 int pos_y;
135
136 // Cairo objects
137 cairo_t *cr;
138 cairo_surface_t *surface;
139 xcb_visualtype_t *visual;
140
141 cairo_surface_t *image, *image2;
142 cairo_device_t *device;
143
144 // Port number (from enum) of the Dial, which is now
145 // adjusted by the user
146 // -1 means that no dial is adjusted
147 int active_dial;
148
149 int profile_select_fd;
150
151 char profile_path[PATH_MAX];
152 char new_profile_path[PATH_MAX];
153 size_t new_profile_path_len;
154
155 KPPFilePicker::Win *fp_win;
156
157 } win_t;
158
159 // Create main window
win_init(win_t * win,xcb_screen_t * screen,xcb_window_t parentXwindow)160 static void win_init(win_t *win, xcb_screen_t *screen,
161 xcb_window_t parentXwindow)
162 {
163 win->fp_win = NULL;
164 win->width = 1000;
165 win->height = 376;
166
167 win->win = xcb_generate_id(win->connection);
168
169 uint32_t mask = XCB_CW_EVENT_MASK;
170 uint32_t mask_values[1] = {
171 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
172 XCB_EVENT_MASK_EXPOSURE |
173 XCB_EVENT_MASK_BUTTON_PRESS |
174 XCB_EVENT_MASK_BUTTON_1_MOTION };
175
176 xcb_create_window(win->connection, XCB_COPY_FROM_PARENT,
177 win->win, parentXwindow,
178 0, 0, win->width, win->height, 0, XCB_WINDOW_CLASS_COPY_FROM_PARENT,
179 XCB_COPY_FROM_PARENT, mask, mask_values);
180
181
182 xcb_size_hints_t size_hints;
183 memset(&size_hints, 0, sizeof(size_hints));
184 xcb_icccm_size_hints_set_size(&size_hints, 1, win->width, win->height);
185 xcb_icccm_size_hints_set_min_size(&size_hints, win->width, win->height);
186 xcb_icccm_size_hints_set_max_size(&size_hints, win->width, win->height);
187
188 xcb_icccm_set_wm_normal_hints(win->connection, win->win, &size_hints);
189
190 xcb_map_window(win->connection, win->win);
191 xcb_flush(win->connection);
192 }
193
194 // LV2 initialization function
195 static LV2UI_Handle
instantiate(const struct _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)196 instantiate(const struct _LV2UI_Descriptor * descriptor,
197 const char * plugin_uri,
198 const char * bundle_path,
199 LV2UI_Write_Function write_function,
200 LV2UI_Controller controller,
201 LV2UI_Widget * widget,
202 const LV2_Feature * const * features)
203 {
204 // Quit if GUI module loaded with wrong main module
205 if (strcmp(plugin_uri, PLUGIN_URI))
206 {
207 fprintf(stderr,
208 "%s: ERROR: this GUI does not support plugin with URI %s\n",
209 PLUGIN_URI, plugin_uri);
210 return NULL;
211 }
212
213 win_t *win = (win_t *) malloc(sizeof(win_t));
214
215 win->active_dial = -1;
216
217 win->profile_select_fd = -1;
218
219 win->driveDial.value = 0;
220 win->driveDial.start_value = 0;
221 win->driveDial.base_x = 215;
222 win->driveDial.base_y = 235;
223
224 win->bassDial.value = 0;
225 win->bassDial.start_value = 0;
226 win->bassDial.base_x = 377;
227 win->bassDial.base_y = 235;
228
229 win->middleDial.value = 0;
230 win->middleDial.start_value = 0;
231 win->middleDial.base_x = 479;
232 win->middleDial.base_y = 235;
233
234 win->trebleDial.value = 0;
235 win->trebleDial.start_value = 0;
236 win->trebleDial.base_x = 583;
237 win->trebleDial.base_y = 235;
238
239 win->mastergainDial.value = 0;
240 win->mastergainDial.start_value = 0;
241 win->mastergainDial.base_x = 808;
242 win->mastergainDial.base_y = 235;
243
244 win->levelSlider.value = 0;
245 win->levelSlider.start_value = 0;
246 win->levelSlider.base_x = 511;
247 win->levelSlider.base_y = 354;
248
249 win->cabinetSlider.value = 0;
250 win->cabinetSlider.start_value = 0;
251 win->cabinetSlider.base_x = 812;
252 win->cabinetSlider.base_y = 354;
253
254 win->profile_path[0] = '\0';
255
256 // X11 handle of host window, in which plugin GUI
257 // window must be embedded
258 void * parentXwindow = 0;
259
260 // Resize function provided by LV2 host
261 LV2UI_Resize* resize = NULL;
262
263 // Obtain this pointers
264 for (int i = 0; features[i]; ++i)
265 {
266 if (!strcmp(features[i]->URI, LV2_UI__parent))
267 {
268 parentXwindow = features[i]->data;
269 }
270 else if (!strcmp(features[i]->URI, LV2_UI__resize))
271 {
272 resize = (LV2UI_Resize*)features[i]->data;
273 }
274 if (!strcmp(features[i]->URI, LV2_URID__map))
275 {
276 win->map = (LV2_URID_Map*)features[i]->data;
277 }
278 }
279
280 win->uris.atom_eventTransfer = win->map->map(win->map->handle, LV2_ATOM__eventTransfer);
281 win->uris.patch_Get = win->map->map(win->map->handle, LV2_PATCH__Get);
282 win->uris.patch_Set = win->map->map(win->map->handle, LV2_PATCH__Set);
283 win->uris.profile = win->map->map(win->map->handle, "https://faustlv2.bitbucket.io/kpp_tubeamp#profile");
284 win->uris.patch_property = win->map->map(win->map->handle, LV2_PATCH__property);
285 win->uris.patch_value = win->map->map(win->map->handle, LV2_PATCH__value);
286 win->uris.atom_Path = win->map->map(win->map->handle, LV2_ATOM__Path);
287
288 lv2_atom_forge_init(&win->forge, win->map);
289
290 win->connection = xcb_connect(NULL, NULL);
291
292 if (win->connection == NULL)
293 {
294 fprintf(stderr, "Failed to open display\n");
295 return NULL;
296 }
297
298 xcb_screen_t *screen =
299 xcb_setup_roots_iterator(xcb_get_setup(win->connection)).data;
300
301 win_init(win, screen, (xcb_window_t) (size_t) parentXwindow);
302
303 win->visual = xcb_aux_find_visual_by_id(screen, screen->root_visual);
304 xcb_clear_area(win->connection, 0, win->win, 0, 0, 0, 0);
305 win->surface = cairo_xcb_surface_create(win->connection, win->win, win->visual,
306 win->width, win->height);
307
308 win->device = cairo_device_reference(cairo_surface_get_device(win->surface));
309
310 win->cr = cairo_create(win->surface);
311
312 char image_path[PATH_MAX];
313
314 // Load background image
315 snprintf(image_path, sizeof(image_path), "%s/base_scale.png", bundle_path);
316 image_path[sizeof(image_path) - 1] = '\0';
317 win->image = cairo_image_surface_create_from_png (image_path);
318
319 // Load dial point image
320 snprintf(image_path, sizeof(image_path), "%s/light.png", bundle_path);
321 image_path[sizeof(image_path) - 1] = '\0';
322 win->image2 = cairo_image_surface_create_from_png (image_path);
323
324 // Return to host the X11 handle of created window
325 //*widget = (void*) (size_t) win->win;
326 *(uintptr_t *)widget = (uintptr_t)win->win;
327
328 if (resize)
329 {
330 win->resize = resize;
331 // Ask the host to properly resize the plugin host window
332 resize->ui_resize(resize->handle, win->width, win->height);
333 }
334
335 win->controller = controller;
336 win->write_function = write_function;
337
338
339 lv2_atom_forge_set_buffer(&win->forge, win->forge_buf, sizeof(win->forge_buf));
340 LV2_Atom_Forge_Frame frame;
341 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&win->forge,
342 &frame,
343 0,
344 win->uris.patch_Get);
345 lv2_atom_forge_pop(&win->forge, &frame);
346
347 win->write_function(win->controller, PORT_CONTROL, lv2_atom_total_size(msg),
348 win->uris.atom_eventTransfer,
349 msg);
350
351 return (LV2UI_Handle)win;
352 }
353
win_deinit(win_t * win)354 static void win_deinit(win_t *win)
355 {
356 if (win->fp_win != NULL)
357 {
358 delete win->fp_win;
359 }
360 xcb_destroy_window(win->connection, win->win);
361 }
362
363 static void
cleanup(LV2UI_Handle ui)364 cleanup(LV2UI_Handle ui)
365 {
366 win_t *win = (win_t *)ui;
367 win_deinit(win);
368 cairo_destroy(win->cr);
369 cairo_surface_destroy(win->surface);
370 cairo_surface_destroy(win->image);
371 cairo_surface_destroy(win->image2);
372 cairo_device_finish(win->device);
373 cairo_device_destroy(win->device);
374 xcb_disconnect(win->connection);
375 free(win);
376 }
377
378 // Limit value by range 0 - 100
clamp(int value)379 int clamp(int value)
380 {
381 if (value < 0) return 0;
382 if (value > 100) return 100;
383 return value;
384 }
385
386 // Convert a value in dB to the linear range 0 - 100
387 // 0 corresponds to `-range` dB, 100 corresponds to `range` dB
db_to_value(float db,float range)388 int db_to_value(float db, float range)
389 {
390 int value = clamp((int)((db + range)/2.0/range*100.0));
391 return value;
392 }
393
394 // Calculate RGB color of Dial's LED for given value
value_to_color(int value)395 st_rgb value_to_color(int value)
396 {
397 st_rgb color;
398 color.r = 0.01*pow(value,1.0/3.0)*100.0/pow(100,1.0/3.0);
399 color.g = 0.7874*(1.0-pow(value,3)/1000000.0);
400 color.b = 0;
401 return color;
402 }
403
404 // Calculate coordinates of Dial's LED for given value
value_to_xy(int value)405 st_point value_to_xy(int value)
406 {
407 int pointerAngle = value/100.0*(360.0-80.0-80.0) - 105.0;
408 st_point p;
409 p.x = 25 + 15*sin(pointerAngle/180.0*M_PI);
410 p.y = 25 - 15*cos(pointerAngle/180.0*M_PI);
411 return p;
412 }
413
draw_dial(cairo_t * cr,cairo_surface_t * image,int value,int base_x,int base_y)414 void draw_dial(cairo_t *cr, cairo_surface_t *image, int value, int base_x, int base_y)
415 {
416 st_rgb color = value_to_color(value);
417 st_point p = value_to_xy(value);
418
419 cairo_set_source_rgb(cr, color.r, color.g, color.b);
420 cairo_arc(cr, base_x+p.x+10.5, base_y+p.y+10, 5.5, 0, 2 * M_PI);
421 cairo_fill(cr);
422 cairo_set_source_surface (cr, image, base_x+p.x, base_y+p.y);
423 cairo_paint (cr);
424 }
425
draw_slider(cairo_t * cr,int value,int base_x,int base_y)426 void draw_slider(cairo_t *cr, int value, int base_x, int base_y)
427 {
428 cairo_set_source_rgb(cr, 0, 0.35, 0);
429
430 int length = (int)(1.83 * value);
431
432 cairo_rectangle(cr, base_x, base_y, length, 19);
433 cairo_fill(cr);
434 }
435
win_draw(win_t * win)436 static void win_draw(win_t *win)
437 {
438 cairo_push_group (win->cr);
439
440 // File select dialog is pending
441 if (win->profile_select_fd != -1)
442 {
443 // Draw profile name element
444 cairo_set_source_rgb(win->cr, 1, 0, 0);
445 cairo_rectangle(win->cr, 75, 354, 439 - 75, 373 - 354);
446 cairo_fill(win->cr);
447
448 // Draw profile name
449 cairo_set_source_rgb(win->cr, 0, 0, 0);
450 cairo_select_font_face(win->cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
451 cairo_set_font_size(win->cr, 18);
452 cairo_move_to(win->cr, 77, 370);
453 }
454 // Draw profile name element in normal state
455 else
456 {
457 // Draw profile name element
458 cairo_set_source_rgb(win->cr, 0, 0, 0);
459 cairo_rectangle(win->cr, 75, 354, 439 - 75, 373 - 354);
460 cairo_fill(win->cr);
461
462 // Draw profile name
463 cairo_set_source_rgb(win->cr, 1, 1, 1);
464 cairo_select_font_face(win->cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
465 cairo_set_font_size(win->cr, 18);
466 cairo_move_to(win->cr, 77, 370);
467 }
468
469 char profile_name[PATH_MAX];
470
471 strncpy(profile_name, win->profile_path, sizeof(profile_name));
472 size_t profile_name_len = strlen(profile_name);
473 if (profile_name_len >= 5)
474 {
475 profile_name[strlen(profile_name) - 5] = '\0';
476 char *base = basename(profile_name);
477 cairo_show_text(win->cr, base);
478 }
479
480 // Draw background image
481 cairo_set_source_surface (win->cr, win->image, 0, 0);
482 cairo_paint (win->cr);
483
484 draw_slider(win->cr,
485 win->levelSlider.value,
486 win->levelSlider.base_x,
487 win->levelSlider.base_y);
488
489 draw_slider(win->cr,
490 win->cabinetSlider.value,
491 win->cabinetSlider.base_x,
492 win->cabinetSlider.base_y);
493
494 draw_dial(win->cr,
495 win->image2,
496 win->driveDial.value,
497 win->driveDial.base_x,
498 win->driveDial.base_y);
499 draw_dial(win->cr,
500 win->image2,
501 win->bassDial.value,
502 win->bassDial.base_x,
503 win->bassDial.base_y);
504 draw_dial(win->cr,
505 win->image2,
506 win->middleDial.value,
507 win->middleDial.base_x,
508 win->middleDial.base_y);
509 draw_dial(win->cr,
510 win->image2,
511 win->trebleDial.value,
512 win->trebleDial.base_x,
513 win->trebleDial.base_y);
514 draw_dial(win->cr,
515 win->image2,
516 win->mastergainDial.value,
517 win->mastergainDial.base_x,
518 win->mastergainDial.base_y);
519
520 cairo_pop_group_to_source (win->cr);
521 cairo_paint (win->cr);
522 }
523
524 // Callback from host to change Dial values
port_event(LV2UI_Handle ui,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)525 static void port_event(LV2UI_Handle ui,
526 uint32_t port_index,
527 uint32_t buffer_size,
528 uint32_t format,
529 const void * buffer)
530 {
531 win_t *win = (win_t *)ui;
532
533 // Atom event received
534 if (format == win->uris.atom_eventTransfer)
535 {
536 const LV2_Atom* atom = (const LV2_Atom*)buffer;
537
538 if (lv2_atom_forge_is_object_type(&win->forge, atom->type))
539 {
540 const LV2_Atom_Object* obj = (const LV2_Atom_Object*)atom;
541 if (obj->body.otype == win->uris.patch_Set)
542 {
543 // Received patch_Set message
544 const LV2_Atom* property = NULL;
545 lv2_atom_object_get(obj, win->uris.patch_property, &property, 0);
546
547 if (((const LV2_Atom_URID*)property)->body == win->uris.profile)
548 {
549 const LV2_Atom* value = NULL;
550
551 lv2_atom_object_get(obj, win->uris.patch_value, &value, 0);
552
553 if (value->type == win->uris.atom_Path)
554 {
555 // Received new profile path
556 strncpy(win->profile_path, (const char*)LV2_ATOM_BODY_CONST(value),
557 sizeof(win->profile_path));
558 win->profile_path[sizeof(win->profile_path) - 1] = '\0';
559 }
560 }
561 }
562 }
563 }
564 // Port event received
565 else if (format == 0)
566 {
567 switch (port_index)
568 {
569 case PORT_BASS:
570 win->bassDial.value = db_to_value(*(float*)buffer, 10.0);
571 break;
572 case PORT_DRIVE:
573 win->driveDial.value = clamp((int)(*(float*)buffer));
574 break;
575 case PORT_MASTERGAIN:
576 win->mastergainDial.value = clamp((int)(*(float*)buffer));
577 break;
578 case PORT_MIDDLE:
579 win->middleDial.value = db_to_value(*(float*)buffer, 10.0);
580 break;
581 case PORT_TREBLE:
582 win->trebleDial.value = db_to_value(*(float*)buffer, 10.0);
583 break;
584 case PORT_VOLUME:
585 win->levelSlider.value = clamp((int)((*(float*)buffer)*100.0));
586 break;
587 case PORT_CABINET:
588 win->cabinetSlider.value = clamp((int)((*(float*)buffer)*100.0));
589 break;
590 }
591 }
592 // Redraw window due to value change
593 win_draw(win);
594 }
595
596 // Determine if the point with given coordinates
597 // belongs to the given Dial
is_point_in_dial_area(int x,int y,st_dial * dial)598 bool is_point_in_dial_area(int x, int y, st_dial *dial)
599 {
600 if ((x >= dial->base_x + 5) && (x <= dial->base_x + 65)
601 && (y >= dial->base_y + 5) && (y <= dial->base_y + 65))
602 {
603 return true;
604 }
605 return false;
606 }
607
608 // Determine if the point with given coordinates
609 // belongs to the given Slider
is_point_in_slider_area(int x,int y,st_slider * slider)610 bool is_point_in_slider_area(int x, int y, st_slider *slider)
611 {
612 if ((x >= slider->base_x) && (x <= slider->base_x + 183)
613 && (y >= slider->base_y) && (y <= slider->base_y + 19))
614 {
615 return true;
616 }
617 return false;
618 }
619
620 // Determine if the point with given coordinates
621 // belongs to the Profile choose control
is_point_in_profile_area(int x,int y)622 bool is_point_in_profile_area(int x, int y)
623 {
624 if ((x >= 75) && (x <= 439)
625 && (y >= 354) && (y <= 373))
626 {
627 return true;
628 }
629 return false;
630 }
631
632 // Determine if the point with given coordinates
633 // belongs to the given area
is_point_in_area(int x,int y,int topleft_x,int topleft_y,int bottomright_x,int bottomright_y)634 bool is_point_in_area(int x, int y, int topleft_x, int topleft_y,
635 int bottomright_x, int bottomright_y)
636 {
637 if ((x >= topleft_x) && (x <= bottomright_x)
638 && (y >= topleft_y) && (y <= bottomright_y))
639 {
640 return true;
641 }
642 return false;
643 }
644
645 // Convert the linear value 0 - 100 to the dB value,
646 // 0 corresponds to `-range` dB, 100 corresponds to `range` dB
value_to_db(int value,float range)647 float value_to_db(int value, float range)
648 {
649 float db = value / 100.0 * 2.0 * range - range;
650 return db;
651 }
652
653 // Shows file select dialog and sends Atom message
654 // to change profile.
select_new_profile_file(win_t * win)655 static int select_new_profile_file(win_t *win)
656 {
657 if (win->fp_win != NULL)
658 {
659 delete win->fp_win;
660 win->fp_win = NULL;
661 }
662 win->fp_win = new KPPFilePicker::Win();
663 win->fp_win->init();
664 win->fp_win->list.filters.push_back("tapf");
665
666 std::string currentDir = win->profile_path;
667 currentDir = currentDir.substr(0, currentDir.find_last_of('/'));
668 win->fp_win->setTitle("Open *.tapf Profile File");
669 //win->fp_win->list.showHidden = true;
670 win->fp_win->list.readDir(currentDir);
671
672 return 0;
673 }
674
675 // Process X11 events
676 static void
win_handle_events(win_t * win)677 win_handle_events(win_t *win)
678 {
679 if (win->fp_win != NULL)
680 {
681 if (win->fp_win->isInited)
682 {
683 xcb_flush(win->fp_win->connection);
684 xcb_generic_event_t *event;
685 while ((event = xcb_poll_for_event(win->fp_win->connection)) != NULL)
686 {
687 win->fp_win->handle_events(event);
688 free(event);
689 }
690 xcb_flush(win->fp_win->connection);
691 }
692 }
693
694 xcb_flush(win->connection);
695 xcb_generic_event_t *event;
696 while ((event = xcb_poll_for_event(win->connection)) != NULL)
697 {
698 switch(event->response_type & ~0x80)
699 {
700 case XCB_EXPOSE: // the window must be redrawn
701 {
702 xcb_expose_event_t *eev = (xcb_expose_event_t *) event;
703
704 if (eev->count == 0)
705 {
706 win_draw(win);
707 }
708 }
709 break;
710 case XCB_BUTTON_PRESS: // Left mouse button pressed
711 {
712 xcb_button_press_event_t *bev = (xcb_button_press_event_t *) event;
713 if (bev->detail == XCB_BUTTON_INDEX_1)
714 {
715 // Save position
716 win->pos_x = bev->event_x;
717 win->pos_y = bev->event_y;
718
719 // Find on which dial the button was pressed
720 if (is_point_in_dial_area(win->pos_x, win->pos_y, &(win->driveDial)))
721 {
722 win->driveDial.start_value = win->driveDial.value;
723 win->active_dial = PORT_DRIVE;
724 }
725 else if (is_point_in_dial_area(win->pos_x, win->pos_y, &(win->bassDial)))
726 {
727 win->bassDial.start_value = win->bassDial.value;
728 win->active_dial = PORT_BASS;
729 }
730 else if (is_point_in_dial_area(win->pos_x, win->pos_y, &(win->middleDial)))
731 {
732 win->middleDial.start_value = win->middleDial.value;
733 win->active_dial = PORT_MIDDLE;
734 }
735 else if (is_point_in_dial_area(win->pos_x, win->pos_y, &(win->trebleDial)))
736 {
737 win->trebleDial.start_value = win->trebleDial.value;
738 win->active_dial = PORT_TREBLE;
739 }
740 else if (is_point_in_dial_area(win->pos_x, win->pos_y, &(win->mastergainDial)))
741 {
742 win->mastergainDial.start_value = win->mastergainDial.value;
743 win->active_dial = PORT_MASTERGAIN;
744 }
745 else if (is_point_in_slider_area(win->pos_x, win->pos_y, &(win->levelSlider)))
746 {
747 win->levelSlider.start_value = win->levelSlider.value;
748 win->active_dial = PORT_VOLUME;
749 }
750 else if (is_point_in_slider_area(win->pos_x, win->pos_y, &(win->cabinetSlider)))
751 {
752 win->cabinetSlider.start_value = win->cabinetSlider.value;
753 win->active_dial = PORT_CABINET;
754 }
755 else if (is_point_in_profile_area(win->pos_x, win->pos_y))
756 {
757 select_new_profile_file(win);
758 win->active_dial = -1;
759 win_draw(win);
760 }
761 else
762 {
763 win->active_dial = -1;
764 }
765 }
766 }
767 break;
768 case XCB_MOTION_NOTIFY: // Mouse move while left button is pressed
769 {
770 xcb_motion_notify_event_t *mev = (xcb_motion_notify_event_t *) event;
771
772 if (win->active_dial != -1)
773 {
774 // Recalculate active Dial value and send new value to the
775 // main LV2 plugin module
776 switch (win->active_dial)
777 {
778 float value;
779 case PORT_DRIVE:
780 win->driveDial.value = clamp(win->driveDial.start_value + win->pos_y - mev->event_y);
781 value = win->driveDial.value;
782 win->write_function(win->controller,PORT_DRIVE,sizeof(float),0,&value);
783 break;
784 case PORT_BASS:
785 win->bassDial.value = clamp(win->bassDial.start_value + win->pos_y - mev->event_y);
786 value = value_to_db(win->bassDial.value, 10.0);
787 win->write_function(win->controller,PORT_BASS,sizeof(float),0,&value);
788 break;
789 case PORT_MIDDLE:
790 win->middleDial.value = clamp(win->middleDial.start_value + win->pos_y - mev->event_y);
791 value = value_to_db(win->middleDial.value, 10.0);
792 win->write_function(win->controller,PORT_MIDDLE,sizeof(float),0,&value);
793 break;
794 case PORT_TREBLE:
795 win->trebleDial.value = clamp(win->trebleDial.start_value + win->pos_y - mev->event_y);
796 value = value_to_db(win->trebleDial.value, 10.0);
797 win->write_function(win->controller,PORT_TREBLE,sizeof(float),0,&value);
798 break;
799 case PORT_MASTERGAIN:
800 win->mastergainDial.value = clamp(win->mastergainDial.start_value + win->pos_y - mev->event_y);
801 value = win->mastergainDial.value;
802 win->write_function(win->controller,PORT_MASTERGAIN,sizeof(float),0,&value);
803 break;
804 case PORT_VOLUME:
805 win->levelSlider.value = clamp(win->levelSlider.start_value - win->pos_x + mev->event_x);
806 value = win->levelSlider.value / 100.0;
807 win->write_function(win->controller,PORT_VOLUME,sizeof(float),0,&value);
808 break;
809 case PORT_CABINET:
810 win->cabinetSlider.value = clamp(win->cabinetSlider.start_value - win->pos_x + mev->event_x);
811 value = win->cabinetSlider.value / 100.0;
812 win->write_function(win->controller,PORT_CABINET,sizeof(float),0,&value);
813 }
814 win_draw(win);
815 }
816 }
817 break;
818 }
819 free(event);
820 }
821 xcb_flush(win->connection);
822
823 if (win->fp_win != NULL)
824 {
825 if (win->fp_win->isInited)
826 {
827 if (win->fp_win->isFilenameUpdated)
828 {
829 strncpy(win->profile_path, win->fp_win->newFileName.c_str(), win->fp_win->newFileName.size());
830 win->profile_path[win->fp_win->newFileName.size()] = '\0';
831
832 lv2_atom_forge_set_buffer(&win->forge, win->forge_buf, sizeof(win->forge_buf));
833 LV2_Atom_Forge_Frame frame;
834 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&win->forge,
835 &frame,
836 0,
837 win->uris.patch_Set);
838
839 lv2_atom_forge_key(&win->forge, win->uris.patch_property);
840 lv2_atom_forge_urid(&win->forge, win->uris.profile);
841 lv2_atom_forge_key(&win->forge, win->uris.patch_value);
842 lv2_atom_forge_path(&win->forge, win->profile_path, win->fp_win->newFileName.size());
843
844 lv2_atom_forge_pop(&win->forge, &frame);
845
846 win->write_function(win->controller, PORT_CONTROL, lv2_atom_total_size(msg),
847 win->uris.atom_eventTransfer,
848 msg);
849 win_draw(win);
850 win->fp_win->isFilenameUpdated = false;
851 delete win->fp_win;
852 win->fp_win = NULL;
853 }
854 }
855 }
856 }
857
858 // LV2 callback function to organize event handling
859 static int
idle(LV2UI_Handle handle)860 idle(LV2UI_Handle handle)
861 {
862 win_t *win = (win_t *)handle;
863 win_handle_events(win);
864
865 return 0;
866 }
867
868
869 // LV2 interfaces and descriptors
870 static const LV2UI_Idle_Interface idle_iface = { idle };
871
872 static const void*
extension_data(const char * uri)873 extension_data(const char* uri)
874 {
875 if (!strcmp(uri, LV2_UI__idleInterface))
876 {
877 return &idle_iface;
878 }
879 return NULL;
880 }
881
882 static const LV2UI_Descriptor descriptor =
883 {
884 PLUGIN_URI "ui",
885 (void* (*)(const LV2UI_Descriptor*, const char*, const char*, void (*)(void*, unsigned int, unsigned int, unsigned int, const void*), void*, void**, const LV2_Feature* const*))instantiate,
886 cleanup,
887 port_event,
888 extension_data
889 };
890
891 LV2_SYMBOL_EXPORT
892 const LV2UI_Descriptor*
lv2ui_descriptor(uint32_t index)893 lv2ui_descriptor(uint32_t index)
894 {
895 switch (index)
896 {
897 case 0:
898 return &descriptor;
899 default:
900 return NULL;
901 }
902 }
903