1 
2 #include "lv2/lv2plug.in/ns/lv2core/lv2.h"
3 #include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
4 
5 // xwidgets.h includes xputty.h and all defined widgets from Xputty
6 #include "xwidgets.h"
7 
8 #include "gx_fuzz.h"
9 
10 /*---------------------------------------------------------------------
11 -----------------------------------------------------------------------
12                 define controller numbers
13 -----------------------------------------------------------------------
14 ----------------------------------------------------------------------*/
15 
16 #define CONTROLS 5
17 
18 /*---------------------------------------------------------------------
19 -----------------------------------------------------------------------
20                 the main LV2 handle->XWindow
21 -----------------------------------------------------------------------
22 ----------------------------------------------------------------------*/
23 
24 
25 // main window struct
26 typedef struct {
27     void *parentXwindow;
28     Xputty main;
29     Widget_t *win;
30     Widget_t *widget[CONTROLS];
31     cairo_surface_t *screw;
32     int block_event;
33     float db_zero;
34 
35     void *controller;
36     LV2UI_Write_Function write_function;
37     LV2UI_Resize* resize;
38 } X11_UI;
39 
40 // setup a color theme
set_my_theme(Xputty * main)41 static void set_my_theme(Xputty *main) {
42     main->color_scheme->normal = (Colors) {
43          /* cairo    / r  / g  / b  / a  /  */
44         /*fg */       { 0.45, 0.45, 0.45, 1.0},
45         /*bg */       { 0.349, 0.0235, 0.011, 1.0},
46         /*base */     { 0.1, 0.1, 0.2, 1.0},
47         /*text */     { 0.45, 0.45, 0.45, 1.0},
48         /*shadow */   { 0.0, 0.0, 0.0, 0.2},
49         /*frame */    { 0.0, 0.0, 0.0, 1.0},
50         /*light */    { 0.1, 0.1, 0.2, 1.0}
51     };
52 
53     main->color_scheme->prelight = (Colors) {
54         /*fg */       { 1.0, 1.0, 1.0, 1.0},
55         /*bg */       { 0.25, 0.25, 0.25, 1.0},
56         /*base */     { 0.2, 0.2, 0.3, 1.0},
57         /*text */     { 0.7, 0.7, 0.7, 1.0},
58         /*shadow */   { 0.1, 0.1, 0.1, 0.4},
59         /*frame */    { 0.3, 0.3, 0.3, 1.0},
60         /*light */    { 0.3, 0.3, 0.3, 1.0}
61     };
62 
63     main->color_scheme->selected = (Colors) {
64         /*fg */       { 0.9, 0.9, 0.9, 1.0},
65         /*bg */       { 0.349, 0.313, 0.243, 1.0},
66         /*base */     { 0.18, 0.18, 0.28, 1.0},
67         /*text */     { 1.0, 1.0, 1.0, 1.0},
68         /*shadow */   { 0.18, 0.18, 0.18, 0.2},
69         /*frame */    { 0.18, 0.18, 0.18, 1.0},
70         /*light */    { 0.18, 0.18, 0.28, 1.0}
71     };
72 }
73 
74 // draw the window
draw_window(void * w_,void * user_data)75 static void draw_window(void *w_, void* user_data) {
76     Widget_t *w = (Widget_t*)w_;
77     X11_UI* ui = (X11_UI*)w->parent_struct;
78     set_pattern(w,&w->app->color_scheme->selected,&w->app->color_scheme->normal,BACKGROUND_);
79     cairo_paint (w->crb);
80     set_pattern(w,&w->app->color_scheme->normal,&w->app->color_scheme->selected,BACKGROUND_);
81     cairo_rectangle (w->crb,4,4,w->width-8,w->height-8);
82     cairo_set_line_width(w->crb,4);
83     cairo_stroke(w->crb);
84 
85     cairo_set_source_surface (w->crb, ui->screw,5,5);
86     cairo_paint (w->crb);
87     cairo_set_source_surface (w->crb, ui->screw,5,w->height-37);
88     cairo_paint (w->crb);
89     cairo_set_source_surface (w->crb, ui->screw,w->width-37,w->height-37);
90     cairo_paint (w->crb);
91     cairo_set_source_surface (w->crb, ui->screw,w->width-37,5);
92     cairo_paint (w->crb);
93     cairo_new_path (w->crb);
94 
95     cairo_text_extents_t extents;
96     use_text_color_scheme(w, get_color_state(w));
97     cairo_set_source_rgb (w->crb,0.45, 0.45, 0.45);
98     float font_size = min(20.0,((w->height/2.2 < (w->width*0.5)/3) ? w->height/2.2 : (w->width*0.5)/3));
99     cairo_set_font_size (w->crb, font_size);
100     cairo_text_extents(w->crb,w->label , &extents);
101     double tw = extents.width/2.0;
102 
103     widget_set_scale(w);
104     cairo_move_to (w->crb, 255-tw, 230 );
105     cairo_show_text(w->crb, w->label);
106     cairo_new_path (w->crb);
107     cairo_scale (w->crb, 0.95, 0.95);
108     cairo_set_source_surface (w->crb, w->image,410,10);
109     cairo_paint (w->crb);
110     cairo_scale (w->crb, 1.05, 1.05);
111     widget_reset_scale(w);
112 }
113 
114 // draw the knobs
draw_my_knob(void * w_,void * user_data)115 static void draw_my_knob(void *w_, void* user_data) {
116     Widget_t *w = (Widget_t*)w_;
117     int width = w->width-2;
118     int height = w->height-2;
119 
120     const double scale_zero = 20 * (M_PI/180); // defines "dead zone" for knobs
121     int arc_offset = 2;
122     int knob_x = 0;
123     int knob_y = 0;
124 
125     int grow = (width > height) ? height:width;
126     knob_x = grow-1;
127     knob_y = grow-1;
128     /** get values for the knob **/
129 
130     int knobx = (width - knob_x) * 0.5;
131     int knobx1 = width* 0.5;
132 
133     int knoby = (height - knob_y) * 0.5;
134     int knoby1 = height * 0.5;
135 
136     double knobstate = adj_get_state(w->adj_y);
137     double angle = scale_zero + knobstate * 2 * (M_PI - scale_zero);
138 
139     double pointer_off =knob_x/3.5;
140     double radius = min(knob_x-pointer_off, knob_y-pointer_off) / 2;
141     double lengh_x = (knobx+radius+pointer_off/2) - radius * sin(angle);
142     double lengh_y = (knoby+radius+pointer_off/2) + radius * cos(angle);
143     double radius_x = (knobx+radius+pointer_off/2) - radius/ 1.18 * sin(angle);
144     double radius_y = (knoby+radius+pointer_off/2) + radius/ 1.18 * cos(angle);
145     cairo_pattern_t* pat;
146     cairo_new_path (w->crb);
147 
148     pat = cairo_pattern_create_linear (0, 0, 0, knob_y);
149     cairo_pattern_add_color_stop_rgba (pat, 1,  0.349, 0.313, 0.243, 1.0);
150     cairo_pattern_add_color_stop_rgba (pat, 0.75,  0.349, 0.235, 0.011, 1.0);
151     cairo_pattern_add_color_stop_rgba (pat, 0.5,  0.15, 0.15, 0.15, 1.0);
152     cairo_pattern_add_color_stop_rgba (pat, 0.25,  0.1, 0.1, 0.1, 1.0);
153     cairo_pattern_add_color_stop_rgba (pat, 0,  0.05, 0.05, 0.05, 1.0);
154 
155     cairo_scale (w->crb, 0.95, 1.05);
156     cairo_arc(w->crb,knobx1+arc_offset/2, knoby1-arc_offset, knob_x/2.2, 0, 2 * M_PI );
157     cairo_set_source (w->crb, pat);
158     cairo_fill_preserve (w->crb);
159     cairo_set_source_rgb (w->crb, 0.1, 0.1, 0.1);
160     cairo_set_line_width(w->crb,1);
161     cairo_stroke(w->crb);
162     cairo_scale (w->crb, 1.05, 0.95);
163     cairo_new_path (w->crb);
164     cairo_pattern_destroy (pat);
165     pat = NULL;
166 
167     pat = cairo_pattern_create_linear (0, 0, 0, knob_y);
168     cairo_pattern_add_color_stop_rgba (pat, 0,  0.349, 0.213, 0.143, 1.0);
169     cairo_pattern_add_color_stop_rgba (pat, 0.25,  0.349, 0.235, 0.011, 1.0);
170     cairo_pattern_add_color_stop_rgba (pat, 0.5,  0.15, 0.15, 0.15, 1.0);
171     cairo_pattern_add_color_stop_rgba (pat, 0.75,  0.1, 0.1, 0.1, 1.0);
172     cairo_pattern_add_color_stop_rgba (pat, 1,  0.05, 0.05, 0.05, 1.0);
173 
174     cairo_arc(w->crb,knobx1, knoby1, knob_x/2.6, 0, 2 * M_PI );
175     cairo_set_source (w->crb, pat);
176     cairo_fill_preserve (w->crb);
177     cairo_set_source_rgb (w->crb, 0.1, 0.1, 0.1);
178     cairo_set_line_width(w->crb,1);
179     cairo_stroke(w->crb);
180     cairo_new_path (w->crb);
181     cairo_pattern_destroy (pat);
182 
183     use_text_color_scheme(w, get_color_state(w));
184 
185     /** create a rotating pointer on the kob**/
186     cairo_set_line_cap(w->crb, CAIRO_LINE_CAP_ROUND);
187     cairo_set_line_join(w->crb, CAIRO_LINE_JOIN_BEVEL);
188     cairo_move_to(w->crb, radius_x, radius_y);
189     cairo_line_to(w->crb,lengh_x,lengh_y);
190     cairo_set_line_width(w->crb,3);
191     cairo_stroke(w->crb);
192     cairo_new_path (w->crb);
193 
194     cairo_text_extents_t extents;
195     /** show value on the kob**/
196     if (w->state>0.0 && w->state<4.0) {
197         char s[64];
198         const char* format[] = {"%.1f", "%.2f", "%.3f"};
199         snprintf(s, 63, format[2-1], w->adj_y->value);
200         cairo_set_font_size (w->crb, min(11.0,knobx1/3));
201         cairo_text_extents(w->crb, s, &extents);
202         cairo_move_to (w->crb, knobx1-extents.width/2, knoby1+extents.height/2);
203         cairo_show_text(w->crb, s);
204         cairo_new_path (w->crb);
205     }
206 
207     /** show label below the knob**/
208     float font_size = min(12.0,((height/2.2 < (width*0.5)/3) ? height/2.2 : (width*0.5)/3));
209     cairo_set_font_size (w->crb, font_size);
210     cairo_text_extents(w->crb,w->label , &extents);
211 
212     cairo_move_to (w->crb, knobx1-extents.width/2, height );
213     cairo_show_text(w->crb, w->label);
214     cairo_new_path (w->crb);
215 }
216 
217 // if controller value changed send notify to host
value_changed(void * w_,void * user_data)218 static void value_changed(void *w_, void* user_data) {
219     Widget_t *w = (Widget_t*)w_;
220     X11_UI* ui = (X11_UI*)w->parent_struct;
221     if (ui->block_event != w->data)
222         ui->write_function(ui->controller,w->data,sizeof(float),0,&w->adj->value);
223     ui->block_event = -1;
224 }
225 
226 // shortcut to create knobs with portindex binding
add_my_knob(Widget_t * w,PortIndex index,const char * label,X11_UI * ui,int x,int y,int width,int height)227 Widget_t* add_my_knob(Widget_t *w, PortIndex index, const char * label,
228                                 X11_UI* ui, int x, int y, int width, int height) {
229     w = add_knob(ui->win, label, x, y, width, height);
230     w->func.expose_callback = draw_my_knob;
231     w->parent_struct = ui;
232     w->data = index;
233     w->func.value_changed_callback = value_changed;
234     return w;
235 }
236 
237 // init the xwindow and return the LV2UI handle
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)238 static LV2UI_Handle instantiate(const LV2UI_Descriptor * descriptor,
239             const char * plugin_uri, const char * bundle_path,
240             LV2UI_Write_Function write_function,
241             LV2UI_Controller controller, LV2UI_Widget * widget,
242             const LV2_Feature * const * features) {
243 
244     X11_UI* ui = (X11_UI*)malloc(sizeof(X11_UI));
245 
246     if (!ui) {
247         fprintf(stderr,"ERROR: failed to instantiate plugin with URI %s\n", plugin_uri);
248         return NULL;
249     }
250 
251     ui->parentXwindow = 0;
252     LV2UI_Resize* resize = NULL;
253     ui->block_event = -1;
254     ui->db_zero = 20.*log10(0.0000003); // -130db
255 
256     int i = 0;
257     for (; features[i]; ++i) {
258         if (!strcmp(features[i]->URI, LV2_UI__parent)) {
259             ui->parentXwindow = features[i]->data;
260         } else if (!strcmp(features[i]->URI, LV2_UI__resize)) {
261             resize = (LV2UI_Resize*)features[i]->data;
262         }
263     }
264 
265     if (ui->parentXwindow == NULL)  {
266         fprintf(stderr, "ERROR: Failed to open parentXwindow for %s\n", plugin_uri);
267         free(ui);
268         return NULL;
269     }
270     // init Xputty
271     main_init(&ui->main);
272     set_my_theme(&ui->main);
273     // create the toplevel Window on the parentXwindow provided by the host
274     ui->win = create_window(&ui->main, (Window)ui->parentXwindow, 0, 0, 510, 240);
275     ui->win->parent_struct = ui;
276     ui->win->label = "GxFuzz";
277     widget_get_png(ui->win, LDVAR(guitarix_png));
278     ui->screw = surface_get_png(ui->win, ui->screw, LDVAR(screw_png));
279     // connect the expose func
280     ui->win->func.expose_callback = draw_window;
281     // create knob widgets
282     ui->widget[0] = add_my_knob(ui->widget[0], INPUT,"Input", ui,40, 25, 100, 125);
283     set_adjustment(ui->widget[0]->adj,0.0, 0.0, -20.0, 10.0, 0.1, CL_CONTINUOS);
284 
285     ui->widget[1] = add_my_knob(ui->widget[1], DRIVE,"Drive", ui,150, 25, 100, 125);
286     set_adjustment(ui->widget[1]->adj,1.0, 1.0, -3.0, 100.0, 1.0, CL_CONTINUOS);
287 
288     ui->widget[2] = add_my_knob(ui->widget[2], TONE,"Tone", ui,260, 25, 100, 125);
289     set_adjustment(ui->widget[2]->adj,0.5, 0.5, 0.0, 1.0, 0.01, CL_CONTINUOS);
290 
291     ui->widget[3] = add_my_knob(ui->widget[3], OUTPUT,"Output", ui,370, 25, 100, 125);
292     set_adjustment(ui->widget[3]->adj,100.0, 100.0, 10.0, 100.0, 1.0, CL_CONTINUOS);
293 
294     ui->widget[4] = add_hmeter(ui->win, "Meter", true, 50, 160, 400, 20);
295     ui->widget[4]->parent_struct = ui;
296     ui->widget[4]->data = V1;
297 
298     // map all widgets into the toplevel Widget_t
299     widget_show_all(ui->win);
300     // set the widget pointer to the X11 Window from the toplevel Widget_t
301     *widget = (void*)ui->win->widget;
302     // request to resize the parentXwindow to the size of the toplevel Widget_t
303     if (resize){
304         ui->resize = resize;
305         resize->ui_resize(resize->handle, 510, 240);
306     }
307     // store pointer to the host controller
308     ui->controller = controller;
309     // store pointer to the host write function
310     ui->write_function = write_function;
311 
312     return (LV2UI_Handle)ui;
313 }
314 
315 // cleanup after usage
cleanup(LV2UI_Handle handle)316 static void cleanup(LV2UI_Handle handle) {
317     X11_UI* ui = (X11_UI*)handle;
318     cairo_surface_destroy(ui->screw);
319     // Xputty free all memory used
320     main_quit(&ui->main);
321     free(ui);
322 }
323 
324 /*---------------------------------------------------------------------
325 -----------------------------------------------------------------------
326                         LV2 interface
327 -----------------------------------------------------------------------
328 ----------------------------------------------------------------------*/
329 
330 // port value change message from host
port_event(LV2UI_Handle handle,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)331 static void port_event(LV2UI_Handle handle, uint32_t port_index,
332                         uint32_t buffer_size, uint32_t format,
333                         const void * buffer) {
334     X11_UI* ui = (X11_UI*)handle;
335     float value = *(float*)buffer;
336     int i=0;
337     for (;i<CONTROLS;i++) {
338         if (port_index == (uint32_t)ui->widget[i]->data) {
339             // prevent event loop between host and plugin
340             ui->block_event = (int)port_index;
341             if (port_index == V1) value = power2db(ui->widget[4], 20.*log10(max(ui->db_zero,value)));
342             // Xputty check if the new value differs from the old one
343             // and set new one, when needed
344             check_value_changed(ui->widget[i]->adj, &value);
345         }
346    }
347 }
348 
349 // LV2 idle interface to host
ui_idle(LV2UI_Handle handle)350 static int ui_idle(LV2UI_Handle handle) {
351     X11_UI* ui = (X11_UI*)handle;
352     // Xputty event loop setup to run one cycle when called
353     run_embedded(&ui->main);
354     return 0;
355 }
356 
357 // LV2 resize interface to host
ui_resize(LV2UI_Feature_Handle handle,int w,int h)358 static int ui_resize(LV2UI_Feature_Handle handle, int w, int h) {
359     X11_UI* ui = (X11_UI*)handle;
360     // Xputty sends configure event to the toplevel widget to resize itself
361     if (ui) send_configure_event(ui->win,0, 0, w, h);
362     return 0;
363 }
364 
365 // connect idle and resize functions to host
extension_data(const char * uri)366 static const void* extension_data(const char* uri) {
367     static const LV2UI_Idle_Interface idle = { ui_idle };
368     static const LV2UI_Resize resize = { 0 ,ui_resize };
369     if (!strcmp(uri, LV2_UI__idleInterface)) {
370         return &idle;
371     }
372     if (!strcmp(uri, LV2_UI__resize)) {
373         return &resize;
374     }
375     return NULL;
376 }
377 
378 static const LV2UI_Descriptor descriptor = {
379     GXPLUGIN_UI_URI,
380     instantiate,
381     cleanup,
382     port_event,
383     extension_data
384 };
385 
386 
387 
388 extern "C"
389 LV2_SYMBOL_EXPORT
lv2ui_descriptor(uint32_t index)390 const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) {
391     switch (index) {
392         case 0:
393             return &descriptor;
394         default:
395         return NULL;
396     }
397 }
398 
399