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