From 35e38f95feed56dab2e5b1b10a7939ab640e3163 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 29 Jul 2012 23:00:12 +0000 Subject: Add Gtk3 UI. git-svn-id: http://svn.drobilla.net/lad/trunk/jalv@4572 a436a847-0d15-0410-975c-d299462d15a1 --- NEWS | 1 + src/jalv_gtk.c | 638 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/jalv_gtk2.c | 597 ---------------------------------------------------- wscript | 18 +- 4 files changed, 655 insertions(+), 599 deletions(-) create mode 100644 src/jalv_gtk.c delete mode 100644 src/jalv_gtk2.c diff --git a/NEWS b/NEWS index 76c7dde..7fbb883 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ jalv (9999) unstable; * Tolerate loading presets with port values that aren't xsd:decimal * Notify plugins of Jack transport changes by sending events (an atom:Blank with properties from the LV2 time extension) + * Add Gtk3 UI -- David Robillard diff --git a/src/jalv_gtk.c b/src/jalv_gtk.c new file mode 100644 index 0000000..f385efa --- /dev/null +++ b/src/jalv_gtk.c @@ -0,0 +1,638 @@ +/* + Copyright 2007-2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include + +#include + +#include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" + +#include "jalv_internal.h" + +static GtkWidget* +new_box(gboolean horizontal, gint spacing) +{ + #if GTK_MAJOR_VERSION == 3 + return gtk_box_new( + horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, + spacing); + #else + return (horizontal + ? gtk_hbox_new(FALSE, spacing) + : gtk_vbox_new(FALSE, spacing)); + #endif +} + +static GtkWidget* +new_hscale(gdouble min, gdouble max, gdouble step) +{ + #if GTK_MAJOR_VERSION == 3 + return gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, min, max, step); + #else + return gtk_hscale_new_with_range(min, max, step); + #endif +} + +static void +size_request(GtkWidget* widget, GtkRequisition* req) +{ + #if GTK_MAJOR_VERSION == 3 + gtk_widget_get_preferred_size(widget, NULL, req); + #else + gtk_widget_size_request(widget, req); + #endif +} + +static void +on_window_destroy(GtkWidget* widget, + gpointer data) +{ + Jalv* jalv = (Jalv*)data; + suil_instance_free(jalv->ui_instance); + jalv->ui_instance = NULL; + gtk_main_quit(); +} + +int +jalv_init(int* argc, char*** argv, JalvOptions* opts) +{ + GOptionEntry entries[] = { + { "uuid", 'u', 0, G_OPTION_ARG_STRING, &opts->uuid, + "UUID for Jack session restoration", "UUID" }, + { "load", 'l', 0, G_OPTION_ARG_STRING, &opts->load, + "Load state from save directory", "DIR" }, + { "dump", 'd', 0, G_OPTION_ARG_NONE, &opts->dump, + "Dump plugin <=> UI communication", NULL }, + { "generic-ui", 'g', 0, G_OPTION_ARG_NONE, &opts->generic_ui, + "Use Jalv generic UI and not the plugin UI", NULL}, + { "buffer-size", 'b', 0, G_OPTION_ARG_INT, &opts->buffer_size, + "Buffer size for plugin <=> UI communication", "SIZE"}, + { 0, 0, 0, 0, 0, 0, 0 } }; + GError* error = NULL; + const int err = gtk_init_with_args( + argc, argv, + "PLUGIN_URI - Run an LV2 plugin as a Jack application", + entries, NULL, &error); + + if (!err) { + fprintf(stderr, "%s\n", error->message); + } + + return !err; +} + +LilvNode* +jalv_native_ui_type(Jalv* jalv) +{ +#if GTK_MAJOR_VERSION == 2 + return lilv_new_uri(jalv->world, + "http://lv2plug.in/ns/extensions/ui#GtkUI"); +#elif GTK_MAJOR_VERSION == 3 + return lilv_new_uri(jalv->world, + "http://lv2plug.in/ns/extensions/ui#Gtk3UI"); +#else + return NULL; +#endif +} + +static void +on_save_activate(GtkWidget* widget, void* ptr) +{ + Jalv* jalv = (Jalv*)ptr; + GtkWidget* dialog = gtk_file_chooser_dialog_new( + "Save State", + jalv->window, + GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + char* base = g_build_filename(path, "/", NULL); + jalv_save(jalv, base); + g_free(path); + g_free(base); + } + + gtk_widget_destroy(dialog); +} + +static void +on_quit_activate(GtkWidget* widget, gpointer data) +{ + GtkWidget* window = (GtkWidget*)data; + gtk_widget_destroy(window); +} + +typedef struct { + Jalv* jalv; + LilvNode* preset; +} PresetRecord; + +char* +symbolify(const char* in) +{ + const size_t len = strlen(in); + char* out = calloc(len + 1, 1); + for (size_t i = 0; i < len; ++i) { + if (g_ascii_isalnum(in[i])) { + out[i] = in[i]; + } else { + out[i] = '_'; + } + } + return out; +} + +static void +on_save_preset_activate(GtkWidget* widget, void* ptr) +{ + Jalv* jalv = (Jalv*)ptr; + + GtkWidget* dialog = gtk_file_chooser_dialog_new( + "Save Preset", + jalv->window, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + char* dot_lv2 = g_build_filename(g_get_home_dir(), ".lv2", NULL); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dot_lv2); + free(dot_lv2); + + GtkWidget* content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkBox* box = GTK_BOX(new_box(true, 8)); + GtkWidget* uri_label = gtk_label_new("URI (Optional):"); + GtkWidget* uri_entry = gtk_entry_new(); + + gtk_box_pack_start(box, uri_label, FALSE, TRUE, 2); + gtk_box_pack_start(box, uri_entry, TRUE, TRUE, 2); + gtk_box_pack_start(GTK_BOX(content), GTK_WIDGET(box), FALSE, FALSE, 6); + + gtk_widget_show_all(GTK_WIDGET(dialog)); + gtk_entry_set_activates_default(GTK_ENTRY(uri_entry), TRUE); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + const char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + const char* uri = gtk_entry_get_text(GTK_ENTRY(uri_entry)); + + char* dirname = g_path_get_dirname(path); + char* basename = g_path_get_basename(path); + char* sym = symbolify(basename); + char* bundle = g_strjoin(NULL, sym, ".lv2", NULL); + char* file = g_strjoin(NULL, sym, ".ttl", NULL); + char* dir = g_build_filename(dirname, bundle, NULL); + + jalv_save_preset(jalv, dir, (strlen(uri) ? uri : NULL), basename, file); + + g_free(dir); + g_free(file); + g_free(bundle); + free(sym); + g_free(basename); + g_free(dirname); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); +} + +static void +on_preset_activate(GtkWidget* widget, gpointer data) +{ + PresetRecord* record = (PresetRecord*)data; + jalv_apply_preset(record->jalv, record->preset); +} + +static void +on_preset_destroy(gpointer data, GClosure* closure) +{ + PresetRecord* record = (PresetRecord*)data; + lilv_node_free(record->preset); + free(record); +} + +static int +add_preset_to_menu(Jalv* jalv, + const LilvNode* node, + const LilvNode* title, + void* data) +{ + GtkWidget* presets_menu = GTK_WIDGET(data); + const char* label = lilv_node_as_string(title); + GtkWidget* item = gtk_menu_item_new_with_label(label); + + PresetRecord* record = (PresetRecord*)malloc(sizeof(PresetRecord)); + record->jalv = jalv; + record->preset = lilv_node_duplicate(node); + + g_signal_connect_data(G_OBJECT(item), "activate", + G_CALLBACK(on_preset_activate), + record, on_preset_destroy, + 0); + + gtk_menu_shell_append(GTK_MENU_SHELL(presets_menu), item); + return 0; +} + +void +jalv_ui_port_event(Jalv* jalv, + uint32_t port_index, + uint32_t buffer_size, + uint32_t protocol, + const void* buffer) +{ + GtkWidget* widget = jalv->ports[port_index].widget; + if (!widget) { + return; + } + + if (GTK_IS_COMBO_BOX(widget)) { + GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); + GValue value = { 0, { { 0 } } }; + GtkTreeIter i; + bool valid = gtk_tree_model_get_iter_first(model, &i); + while (valid) { + gtk_tree_model_get_value(model, &i, 0, &value); + const double v = g_value_get_double(&value); + g_value_unset(&value); + if (fabs(v - *(float*)buffer) < FLT_EPSILON) { + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &i); + return; + } + valid = gtk_tree_model_iter_next(model, &i); + } + } else if (GTK_IS_TOGGLE_BUTTON(widget)) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), + *(float*)buffer > 0.0f); + } else if (GTK_IS_RANGE(widget)) { + gtk_range_set_value(GTK_RANGE(widget), *(float*)buffer); + } else { + fprintf(stderr, "Unknown widget type for port %d\n", port_index); + } +} + +static gboolean +slider_changed(GtkRange* range, gpointer data) +{ + ((struct Port*)data)->control = gtk_range_get_value(range); + return FALSE; +} + +static gboolean +log_slider_changed(GtkRange* range, gpointer data) +{ + ((struct Port*)data)->control = expf(gtk_range_get_value(range)); + return FALSE; +} + +static void +combo_changed(GtkComboBox* box, gpointer data) +{ + GtkTreeIter iter; + if (gtk_combo_box_get_active_iter(box, &iter)) { + GtkTreeModel* model = gtk_combo_box_get_model(box); + GValue value = { 0, { { 0 } } }; + + gtk_tree_model_get_value(model, &iter, 0, &value); + const double v = g_value_get_double(&value); + g_value_unset(&value); + + ((struct Port*)data)->control = v; + } +} + +static gboolean +toggle_changed(GtkToggleButton* button, gpointer data) +{ + float fval = gtk_toggle_button_get_active(button) ? 1.0f : 0.0f; + ((struct Port*)data)->control = fval; + return FALSE; +} + +static gchar* +scale_format(GtkScale* scale, gdouble value, gpointer user_data) +{ + gpointer hval = g_hash_table_lookup(user_data, &value); + return hval ? g_strdup(hval) : + g_strdup_printf("%0.*f", gtk_scale_get_digits(scale), value); +} + +static gchar* +log_scale_format(GtkScale* scale, gdouble value, gpointer user_data) +{ + return g_strdup_printf("%0.6g", exp(gtk_range_get_value(GTK_RANGE(scale)))); +} + +static gint +dcmp(gconstpointer a, gconstpointer b) +{ + double y = *(double*)a; + double z = *(double*)b; + return y < z ? -1 : z < y ? 1 : 0; +} + +static GtkWidget* +make_combo(struct Port* port, GHashTable* points, int deft) +{ + GList* list = g_hash_table_get_keys(points); + GtkListStore* list_store = gtk_list_store_new( + 2, G_TYPE_DOUBLE, G_TYPE_STRING); + for (GList* cur = g_list_sort(list, dcmp); cur; cur = cur->next) { + GtkTreeIter iter; + gtk_list_store_append(list_store, &iter); + gtk_list_store_set(list_store, &iter, + 0, *(double*)cur->data, + 1, g_hash_table_lookup(points, cur->data), + -1); + } + g_list_free(list); + + GtkWidget* combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store)); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), deft); + g_object_unref(list_store); + + GtkCellRenderer* cell = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", 1, NULL); + + g_signal_connect(G_OBJECT(combo), + "changed", G_CALLBACK(combo_changed), port); + + return combo; +} + +static GtkWidget* +make_log_slider(struct Port* port, GHashTable* points, + float min, float max, float deft) +{ + float lmin = logf(min); + float lmax = logf(max); + float ldft = logf(deft); + GtkWidget* slider = new_hscale(lmin, lmax, 0.001); + gtk_scale_set_digits(GTK_SCALE(slider), 6); + gtk_range_set_value(GTK_RANGE(slider), ldft); + g_signal_connect(G_OBJECT(slider), + "format-value", G_CALLBACK(log_scale_format), points); + g_signal_connect(G_OBJECT(slider), + "value-changed", G_CALLBACK(log_slider_changed), port); + + return slider; +} + +static GtkWidget* +make_slider(struct Port* port, GHashTable* points, + bool is_int, float min, float max, float deft) +{ + const double step = is_int ? 1.0 : ((max - min) / 100.0); + GtkWidget* slider = new_hscale(min, max, step); + gtk_range_set_value(GTK_RANGE(slider), deft); + if (points) { + g_signal_connect(G_OBJECT(slider), + "format-value", G_CALLBACK(scale_format), points); + } + + g_signal_connect(G_OBJECT(slider), + "value-changed", G_CALLBACK(slider_changed), port); + + return slider; +} + +static GtkWidget* +make_toggle(struct Port* port, float deft) +{ + GtkWidget* check = gtk_check_button_new(); + if (deft) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE); + } + g_signal_connect(G_OBJECT(check), + "toggled", G_CALLBACK(toggle_changed), port); + return check; +} + +static GtkWidget* +build_control_widget(Jalv* jalv, GtkWidget* window) +{ + LilvNode* lv2_sampleRate = lilv_new_uri(jalv->world, LV2_CORE__sampleRate); + LilvNode* lv2_integer = lilv_new_uri(jalv->world, LV2_CORE__integer); + LilvNode* lv2_toggled = lilv_new_uri(jalv->world, LV2_CORE__toggled); + LilvNode* lv2_enum = lilv_new_uri(jalv->world, LV2_CORE__enumeration); + LilvNode* lv2_log = lilv_new_uri(jalv->world, LV2_PORT_PROPS__logarithmic); + LilvNode* rdfs_comment = lilv_new_uri(jalv->world, LILV_NS_RDFS "comment"); + GtkWidget* port_table = gtk_table_new(jalv->num_ports, 2, false); + float* defaults = calloc(jalv->num_ports, sizeof(float)); + float* mins = calloc(jalv->num_ports, sizeof(float)); + float* maxs = calloc(jalv->num_ports, sizeof(float)); + int num_controls = 0; + lilv_plugin_get_port_ranges_float(jalv->plugin, mins, maxs, defaults); + for (unsigned i = 0; i < jalv->num_ports; i++) { + if (jalv->ports[i].type != TYPE_CONTROL) { + continue; + } + ++num_controls; + + const LilvPort* port = jalv->ports[i].lilv_port; + LilvNode* name = lilv_port_get_name(jalv->plugin, port); + + if (lilv_port_has_property(jalv->plugin, port, lv2_sampleRate)) { + mins[i] *= jalv->sample_rate; + maxs[i] *= jalv->sample_rate; + } + + /* Get scale points */ + LilvScalePoints* sp = lilv_port_get_scale_points(jalv->plugin, port); + GHashTable* points = NULL; + if (sp) { + points = g_hash_table_new(g_double_hash, g_double_equal); + gdouble* values = malloc(lilv_scale_points_size(sp) * sizeof(gdouble)); + int idx = 0; + LILV_FOREACH(scale_points, i, sp) { + const LilvScalePoint* p = lilv_scale_points_get(sp, i); + values[idx] = lilv_node_as_float(lilv_scale_point_get_value(p)); + char* label = g_strdup( + lilv_node_as_string(lilv_scale_point_get_label(p))); + g_hash_table_insert(points, values + idx, label); + ++idx; + } + lilv_scale_points_free(sp); + } + + /* Make control */ + GtkWidget* control = NULL; + bool is_integer = lilv_port_has_property( + jalv->plugin, port, lv2_integer); + if (lilv_port_has_property(jalv->plugin, port, lv2_toggled)) { + control = make_toggle(&jalv->ports[i], defaults[i]); + } else if (lilv_port_has_property(jalv->plugin, port, lv2_enum) + || (is_integer && points && + (g_hash_table_size(points) + == (unsigned)(maxs[i] - mins[i] + 1)))) { + control = make_combo(&jalv->ports[i], points, defaults[i]); + } else if (lilv_port_has_property(jalv->plugin, port, lv2_log)) { + control = make_log_slider(&jalv->ports[i], points, + mins[i], maxs[i], defaults[i]); + } else { + control = make_slider(&jalv->ports[i], points, is_integer, + mins[i], maxs[i], defaults[i]); + } + jalv->ports[i].widget = control; + + /* Set tooltip text */ + LilvNodes* comments = lilv_port_get_value( + jalv->plugin, port, rdfs_comment); + if (comments) { + gtk_widget_set_tooltip_text( + control, lilv_node_as_string(lilv_nodes_get_first(comments))); + } + lilv_nodes_free(comments); + + /* Add control table row */ + GtkWidget* label = gtk_label_new(NULL); + gchar* markup = g_markup_printf_escaped( + "%s:", + lilv_node_as_string(name)); + gtk_label_set_markup(GTK_LABEL(label), markup); + g_free(markup); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(port_table), + label, + 0, 1, i, i + 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 15, 15); + gtk_table_attach(GTK_TABLE(port_table), control, + 1, 2, i, i + 1, + GTK_FILL | GTK_EXPAND, 0, 0, 0); + lilv_node_free(name); + } + + free(defaults); + free(mins); + free(maxs); + lilv_node_free(lv2_sampleRate); + lilv_node_free(lv2_integer); + lilv_node_free(lv2_toggled); + lilv_node_free(lv2_enum); + lilv_node_free(rdfs_comment); + + if (num_controls > 0) { + gtk_window_set_resizable(GTK_WINDOW(window), TRUE); + GtkWidget* alignment = gtk_alignment_new(0.5, 0.0, 1.0, 0.0); + gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 8, 8); + gtk_container_add(GTK_CONTAINER(alignment), port_table); + return alignment; + } else { + gtk_widget_destroy(port_table); + GtkWidget* button = gtk_button_new_with_label("Close"); + g_signal_connect_swapped( + button, "clicked", G_CALLBACK(gtk_widget_destroy), window); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + return button; + } +} + +int +jalv_open_ui(Jalv* jalv, + SuilInstance* instance) +{ + GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + jalv->window = window; + + g_signal_connect(window, "destroy", + G_CALLBACK(on_window_destroy), jalv); + + LilvNode* name = lilv_plugin_get_name(jalv->plugin); + gtk_window_set_title(GTK_WINDOW(window), lilv_node_as_string(name)); + lilv_node_free(name); + + GtkWidget* vbox = new_box(false, 0); + GtkWidget* menu_bar = gtk_menu_bar_new(); + GtkWidget* file = gtk_menu_item_new_with_mnemonic("_File"); + GtkWidget* file_menu = gtk_menu_new(); + + GtkAccelGroup* ag = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(window), ag); + + GtkWidget* save = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, ag); + GtkWidget* quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, ag); + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), file_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), save); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit); + gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file); + + GtkWidget* presets = gtk_menu_item_new_with_mnemonic("_Presets"); + GtkWidget* presets_menu = gtk_menu_new(); + GtkWidget* save_preset = gtk_menu_item_new_with_mnemonic( + "_Save Preset..."); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(presets), presets_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(presets_menu), save_preset); + gtk_menu_shell_append(GTK_MENU_SHELL(presets_menu), + gtk_separator_menu_item_new()); + gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), presets); + + jalv_load_presets(jalv, add_preset_to_menu, presets_menu); + + gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(quit), "activate", + G_CALLBACK(on_quit_activate), window); + + g_signal_connect(G_OBJECT(save), "activate", + G_CALLBACK(on_save_activate), jalv); + + g_signal_connect(G_OBJECT(save_preset), "activate", + G_CALLBACK(on_save_preset_activate), jalv); + + if (instance && !jalv->opts.generic_ui) { + GtkWidget* alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0); + GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(instance); + + gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(alignment), widget); + gtk_window_set_resizable(GTK_WINDOW(window), jalv_ui_is_resizable(jalv)); + gtk_widget_show_all(vbox); + } else { + GtkWidget* controls = build_control_widget(jalv, window); + GtkWidget* scroll_win = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_add_with_viewport( + GTK_SCROLLED_WINDOW(scroll_win), controls); + gtk_scrolled_window_set_policy( + GTK_SCROLLED_WINDOW(scroll_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0); + gtk_widget_show_all(vbox); + + GtkRequisition controls_size, box_size; + size_request(GTK_WIDGET(controls), &controls_size); + size_request(GTK_WIDGET(vbox), &box_size); + + gtk_window_set_default_size( + GTK_WINDOW(window), + MAX(MAX(box_size.width, controls_size.width) + 24, 640), + box_size.height + controls_size.height); + } + + g_timeout_add(1000 / jalv->ui_update_hz, + (GSourceFunc)jalv_emit_ui_events, jalv); + + jalv->has_ui = TRUE; + gtk_window_present(GTK_WINDOW(window)); + + gtk_main(); + zix_sem_post(jalv->done); + return 0; +} diff --git a/src/jalv_gtk2.c b/src/jalv_gtk2.c deleted file mode 100644 index 9ff2013..0000000 --- a/src/jalv_gtk2.c +++ /dev/null @@ -1,597 +0,0 @@ -/* - Copyright 2007-2012 David Robillard - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#include - -#include - -#include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" - -#include "jalv_internal.h" - -static void -on_window_destroy(GtkWidget* widget, - gpointer data) -{ - Jalv* jalv = (Jalv*)data; - suil_instance_free(jalv->ui_instance); - jalv->ui_instance = NULL; - gtk_main_quit(); -} - -int -jalv_init(int* argc, char*** argv, JalvOptions* opts) -{ - GOptionEntry entries[] = { - { "uuid", 'u', 0, G_OPTION_ARG_STRING, &opts->uuid, - "UUID for Jack session restoration", "UUID" }, - { "load", 'l', 0, G_OPTION_ARG_STRING, &opts->load, - "Load state from save directory", "DIR" }, - { "dump", 'd', 0, G_OPTION_ARG_NONE, &opts->dump, - "Dump plugin <=> UI communication", NULL }, - { "generic-ui", 'g', 0, G_OPTION_ARG_NONE, &opts->generic_ui, - "Use Jalv generic UI and not the plugin UI", NULL}, - { "buffer-size", 'b', 0, G_OPTION_ARG_INT, &opts->buffer_size, - "Buffer size for plugin <=> UI communication", "SIZE"}, - { 0, 0, 0, 0, 0, 0, 0 } }; - GError* error = NULL; - const int err = gtk_init_with_args( - argc, argv, - "PLUGIN_URI - Run an LV2 plugin as a Jack application", - entries, NULL, &error); - - if (!err) { - fprintf(stderr, "%s\n", error->message); - } - - return !err; -} - -LilvNode* -jalv_native_ui_type(Jalv* jalv) -{ - return lilv_new_uri(jalv->world, - "http://lv2plug.in/ns/extensions/ui#GtkUI"); -} - -static void -on_save_activate(GtkWidget* widget, void* ptr) -{ - Jalv* jalv = (Jalv*)ptr; - GtkWidget* dialog = gtk_file_chooser_dialog_new( - "Save State", - jalv->window, - GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - NULL); - - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - char* base = g_build_filename(path, "/", NULL); - jalv_save(jalv, base); - g_free(path); - g_free(base); - } - - gtk_widget_destroy(dialog); -} - -static void -on_quit_activate(GtkWidget* widget, gpointer data) -{ - GtkWidget* window = (GtkWidget*)data; - gtk_widget_destroy(window); -} - -typedef struct { - Jalv* jalv; - LilvNode* preset; -} PresetRecord; - -char* -symbolify(const char* in) -{ - const size_t len = strlen(in); - char* out = calloc(len + 1, 1); - for (size_t i = 0; i < len; ++i) { - if (g_ascii_isalnum(in[i])) { - out[i] = in[i]; - } else { - out[i] = '_'; - } - } - return out; -} - -static void -on_save_preset_activate(GtkWidget* widget, void* ptr) -{ - Jalv* jalv = (Jalv*)ptr; - - GtkWidget* dialog = gtk_file_chooser_dialog_new( - "Save Preset", - jalv->window, - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - NULL); - - char* dot_lv2 = g_build_filename(g_get_home_dir(), ".lv2", NULL); - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dot_lv2); - free(dot_lv2); - - GtkWidget* content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - GtkBox* box = GTK_BOX(gtk_hbox_new(FALSE, 8)); - GtkWidget* uri_label = gtk_label_new("URI (Optional):"); - GtkWidget* uri_entry = gtk_entry_new(); - - gtk_box_pack_start(box, uri_label, FALSE, TRUE, 2); - gtk_box_pack_start(box, uri_entry, TRUE, TRUE, 2); - gtk_box_pack_start(GTK_BOX(content), GTK_WIDGET(box), FALSE, FALSE, 6); - - gtk_widget_show_all(GTK_WIDGET(dialog)); - gtk_entry_set_activates_default(GTK_ENTRY(uri_entry), TRUE); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - const char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - const char* uri = gtk_entry_get_text(GTK_ENTRY(uri_entry)); - - char* dirname = g_path_get_dirname(path); - char* basename = g_path_get_basename(path); - char* sym = symbolify(basename); - char* bundle = g_strjoin(NULL, sym, ".lv2", NULL); - char* file = g_strjoin(NULL, sym, ".ttl", NULL); - char* dir = g_build_filename(dirname, bundle, NULL); - - jalv_save_preset(jalv, dir, (strlen(uri) ? uri : NULL), basename, file); - - g_free(dir); - g_free(file); - g_free(bundle); - free(sym); - g_free(basename); - g_free(dirname); - } - - gtk_widget_destroy(GTK_WIDGET(dialog)); -} - -static void -on_preset_activate(GtkWidget* widget, gpointer data) -{ - PresetRecord* record = (PresetRecord*)data; - jalv_apply_preset(record->jalv, record->preset); -} - -static void -on_preset_destroy(gpointer data, GClosure* closure) -{ - PresetRecord* record = (PresetRecord*)data; - lilv_node_free(record->preset); - free(record); -} - -static int -add_preset_to_menu(Jalv* jalv, - const LilvNode* node, - const LilvNode* title, - void* data) -{ - GtkWidget* presets_menu = GTK_WIDGET(data); - const char* label = lilv_node_as_string(title); - GtkWidget* item = gtk_menu_item_new_with_label(label); - - PresetRecord* record = (PresetRecord*)malloc(sizeof(PresetRecord)); - record->jalv = jalv; - record->preset = lilv_node_duplicate(node); - - g_signal_connect_data(G_OBJECT(item), "activate", - G_CALLBACK(on_preset_activate), - record, on_preset_destroy, - 0); - - gtk_menu_shell_append(GTK_MENU_SHELL(presets_menu), item); - return 0; -} - -void -jalv_ui_port_event(Jalv* jalv, - uint32_t port_index, - uint32_t buffer_size, - uint32_t protocol, - const void* buffer) -{ - GtkWidget* widget = jalv->ports[port_index].widget; - if (!widget) { - return; - } - - if (GTK_IS_COMBO_BOX(widget)) { - GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); - GValue value = { 0, { { 0 } } }; - GtkTreeIter i; - bool valid = gtk_tree_model_get_iter_first(model, &i); - while (valid) { - gtk_tree_model_get_value(model, &i, 0, &value); - const double v = g_value_get_double(&value); - g_value_unset(&value); - if (fabs(v - *(float*)buffer) < FLT_EPSILON) { - gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &i); - return; - } - valid = gtk_tree_model_iter_next(model, &i); - } - } else if (GTK_IS_TOGGLE_BUTTON(widget)) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), - *(float*)buffer > 0.0f); - } else if (GTK_IS_RANGE(widget)) { - gtk_range_set_value(GTK_RANGE(widget), *(float*)buffer); - } else { - fprintf(stderr, "Unknown widget type for port %d\n", port_index); - } -} - -static gboolean -slider_changed(GtkRange* range, gpointer data) -{ - ((struct Port*)data)->control = gtk_range_get_value(range); - return FALSE; -} - -static gboolean -log_slider_changed(GtkRange* range, gpointer data) -{ - ((struct Port*)data)->control = expf(gtk_range_get_value(range)); - return FALSE; -} - -static void -combo_changed(GtkComboBox* box, gpointer data) -{ - GtkTreeIter iter; - if (gtk_combo_box_get_active_iter(box, &iter)) { - GtkTreeModel* model = gtk_combo_box_get_model(box); - GValue value = { 0, { { 0 } } }; - - gtk_tree_model_get_value(model, &iter, 0, &value); - const double v = g_value_get_double(&value); - g_value_unset(&value); - - ((struct Port*)data)->control = v; - } -} - -static gboolean -toggle_changed(GtkToggleButton* button, gpointer data) -{ - float fval = gtk_toggle_button_get_active(button) ? 1.0f : 0.0f; - ((struct Port*)data)->control = fval; - return FALSE; -} - -static gchar* -scale_format(GtkScale* scale, gdouble value, gpointer user_data) -{ - gpointer hval = g_hash_table_lookup(user_data, &value); - return hval ? g_strdup(hval) : - g_strdup_printf("%0.*f", gtk_scale_get_digits(scale), value); -} - -static gchar* -log_scale_format(GtkScale* scale, gdouble value, gpointer user_data) -{ - return g_strdup_printf("%0.6g", exp(gtk_range_get_value(GTK_RANGE(scale)))); -} - -static gint -dcmp(gconstpointer a, gconstpointer b) -{ - double y = *(double*)a; - double z = *(double*)b; - return y < z ? -1 : z < y ? 1 : 0; -} - -static GtkWidget* -make_combo(struct Port* port, GHashTable* points, int deft) -{ - GList* list = g_hash_table_get_keys(points); - GtkListStore* list_store = gtk_list_store_new( - 2, G_TYPE_DOUBLE, G_TYPE_STRING); - for (GList* cur = g_list_sort(list, dcmp); cur; cur = cur->next) { - GtkTreeIter iter; - gtk_list_store_append(list_store, &iter); - gtk_list_store_set(list_store, &iter, - 0, *(double*)cur->data, - 1, g_hash_table_lookup(points, cur->data), - -1); - } - g_list_free(list); - - GtkWidget* combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store)); - gtk_combo_box_set_active(GTK_COMBO_BOX(combo), deft); - g_object_unref(list_store); - - GtkCellRenderer* cell = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", 1, NULL); - - g_signal_connect(G_OBJECT(combo), - "changed", G_CALLBACK(combo_changed), port); - - return combo; -} - -static GtkWidget* -make_log_slider(struct Port* port, GHashTable* points, - float min, float max, float deft) -{ - float lmin = logf(min); - float lmax = logf(max); - float ldft = logf(deft); - GtkWidget* slider = gtk_hscale_new_with_range(lmin, lmax, 0.001); - gtk_scale_set_digits(GTK_SCALE(slider), 6); - gtk_range_set_value(GTK_RANGE(slider), ldft); - g_signal_connect(G_OBJECT(slider), - "format-value", G_CALLBACK(log_scale_format), points); - g_signal_connect(G_OBJECT(slider), - "value-changed", G_CALLBACK(log_slider_changed), port); - - return slider; -} - -static GtkWidget* -make_slider(struct Port* port, GHashTable* points, - bool is_int, float min, float max, float deft) -{ - const double step = is_int ? 1.0 : ((max - min) / 100.0); - GtkWidget* slider = gtk_hscale_new_with_range(min, max, step); - gtk_range_set_value(GTK_RANGE(slider), deft); - if (points) { - g_signal_connect(G_OBJECT(slider), - "format-value", G_CALLBACK(scale_format), points); - } - - g_signal_connect(G_OBJECT(slider), - "value-changed", G_CALLBACK(slider_changed), port); - - return slider; -} - -static GtkWidget* -make_toggle(struct Port* port, float deft) -{ - GtkWidget* check = gtk_check_button_new(); - if (deft) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE); - } - g_signal_connect(G_OBJECT(check), - "toggled", G_CALLBACK(toggle_changed), port); - return check; -} - -static GtkWidget* -build_control_widget(Jalv* jalv, GtkWidget* window) -{ - LilvNode* lv2_sampleRate = lilv_new_uri(jalv->world, LV2_CORE__sampleRate); - LilvNode* lv2_integer = lilv_new_uri(jalv->world, LV2_CORE__integer); - LilvNode* lv2_toggled = lilv_new_uri(jalv->world, LV2_CORE__toggled); - LilvNode* lv2_enum = lilv_new_uri(jalv->world, LV2_CORE__enumeration); - LilvNode* lv2_log = lilv_new_uri(jalv->world, LV2_PORT_PROPS__logarithmic); - LilvNode* rdfs_comment = lilv_new_uri(jalv->world, LILV_NS_RDFS "comment"); - GtkWidget* port_table = gtk_table_new(jalv->num_ports, 2, false); - float* defaults = calloc(jalv->num_ports, sizeof(float)); - float* mins = calloc(jalv->num_ports, sizeof(float)); - float* maxs = calloc(jalv->num_ports, sizeof(float)); - int num_controls = 0; - lilv_plugin_get_port_ranges_float(jalv->plugin, mins, maxs, defaults); - for (unsigned i = 0; i < jalv->num_ports; i++) { - if (jalv->ports[i].type != TYPE_CONTROL) { - continue; - } - ++num_controls; - - const LilvPort* port = jalv->ports[i].lilv_port; - LilvNode* name = lilv_port_get_name(jalv->plugin, port); - - if (lilv_port_has_property(jalv->plugin, port, lv2_sampleRate)) { - mins[i] *= jalv->sample_rate; - maxs[i] *= jalv->sample_rate; - } - - /* Get scale points */ - LilvScalePoints* sp = lilv_port_get_scale_points(jalv->plugin, port); - GHashTable* points = NULL; - if (sp) { - points = g_hash_table_new(g_double_hash, g_double_equal); - gdouble* values = malloc(lilv_scale_points_size(sp) * sizeof(gdouble)); - int idx = 0; - LILV_FOREACH(scale_points, i, sp) { - const LilvScalePoint* p = lilv_scale_points_get(sp, i); - values[idx] = lilv_node_as_float(lilv_scale_point_get_value(p)); - char* label = g_strdup( - lilv_node_as_string(lilv_scale_point_get_label(p))); - g_hash_table_insert(points, values + idx, label); - ++idx; - } - lilv_scale_points_free(sp); - } - - /* Make control */ - GtkWidget* control = NULL; - bool is_integer = lilv_port_has_property( - jalv->plugin, port, lv2_integer); - if (lilv_port_has_property(jalv->plugin, port, lv2_toggled)) { - control = make_toggle(&jalv->ports[i], defaults[i]); - } else if (lilv_port_has_property(jalv->plugin, port, lv2_enum) - || (is_integer && points && - (g_hash_table_size(points) - == (unsigned)(maxs[i] - mins[i] + 1)))) { - control = make_combo(&jalv->ports[i], points, defaults[i]); - } else if (lilv_port_has_property(jalv->plugin, port, lv2_log)) { - control = make_log_slider(&jalv->ports[i], points, - mins[i], maxs[i], defaults[i]); - } else { - control = make_slider(&jalv->ports[i], points, is_integer, - mins[i], maxs[i], defaults[i]); - } - jalv->ports[i].widget = control; - - /* Set tooltip text */ - LilvNodes* comments = lilv_port_get_value( - jalv->plugin, port, rdfs_comment); - if (comments) { - gtk_widget_set_tooltip_text( - control, lilv_node_as_string(lilv_nodes_get_first(comments))); - } - lilv_nodes_free(comments); - - /* Add control table row */ - GtkWidget* label = gtk_label_new(NULL); - gchar* markup = g_markup_printf_escaped( - "%s:", - lilv_node_as_string(name)); - gtk_label_set_markup(GTK_LABEL(label), markup); - g_free(markup); - gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); - gtk_table_attach(GTK_TABLE(port_table), - label, - 0, 1, i, i + 1, - GTK_FILL, GTK_FILL | GTK_EXPAND, 15, 15); - gtk_table_attach(GTK_TABLE(port_table), control, - 1, 2, i, i + 1, - GTK_FILL | GTK_EXPAND, 0, 0, 0); - lilv_node_free(name); - } - - free(defaults); - free(mins); - free(maxs); - lilv_node_free(lv2_sampleRate); - lilv_node_free(lv2_integer); - lilv_node_free(lv2_toggled); - lilv_node_free(lv2_enum); - lilv_node_free(rdfs_comment); - - if (num_controls > 0) { - gtk_window_set_resizable(GTK_WINDOW(window), TRUE); - GtkWidget* alignment = gtk_alignment_new(0.5, 0.0, 1.0, 0.0); - gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 8, 8); - gtk_container_add(GTK_CONTAINER(alignment), port_table); - return alignment; - } else { - gtk_widget_destroy(port_table); - GtkWidget* button = gtk_button_new_with_label("Close"); - g_signal_connect_swapped( - button, "clicked", G_CALLBACK(gtk_widget_destroy), window); - gtk_window_set_resizable(GTK_WINDOW(window), FALSE); - return button; - } -} - -int -jalv_open_ui(Jalv* jalv, - SuilInstance* instance) -{ - GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - jalv->window = window; - - g_signal_connect(window, "destroy", - G_CALLBACK(on_window_destroy), jalv); - - LilvNode* name = lilv_plugin_get_name(jalv->plugin); - gtk_window_set_title(GTK_WINDOW(window), lilv_node_as_string(name)); - lilv_node_free(name); - - GtkWidget* vbox = gtk_vbox_new(FALSE, 0); - GtkWidget* menu_bar = gtk_menu_bar_new(); - GtkWidget* file = gtk_menu_item_new_with_mnemonic("_File"); - GtkWidget* file_menu = gtk_menu_new(); - - GtkAccelGroup* ag = gtk_accel_group_new(); - gtk_window_add_accel_group(GTK_WINDOW(window), ag); - - GtkWidget* save = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, ag); - GtkWidget* quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, ag); - - gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), file_menu); - gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), save); - gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit); - gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file); - - GtkWidget* presets = gtk_menu_item_new_with_mnemonic("_Presets"); - GtkWidget* presets_menu = gtk_menu_new(); - GtkWidget* save_preset = gtk_menu_item_new_with_mnemonic( - "_Save Preset..."); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(presets), presets_menu); - gtk_menu_shell_append(GTK_MENU_SHELL(presets_menu), save_preset); - gtk_menu_shell_append(GTK_MENU_SHELL(presets_menu), - gtk_separator_menu_item_new()); - gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), presets); - - jalv_load_presets(jalv, add_preset_to_menu, presets_menu); - - gtk_container_add(GTK_CONTAINER(window), vbox); - gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(quit), "activate", - G_CALLBACK(on_quit_activate), window); - - g_signal_connect(G_OBJECT(save), "activate", - G_CALLBACK(on_save_activate), jalv); - - g_signal_connect(G_OBJECT(save_preset), "activate", - G_CALLBACK(on_save_preset_activate), jalv); - - if (instance && !jalv->opts.generic_ui) { - GtkWidget* alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0); - GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(instance); - - gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0); - gtk_container_add(GTK_CONTAINER(alignment), widget); - gtk_window_set_resizable(GTK_WINDOW(window), jalv_ui_is_resizable(jalv)); - gtk_widget_show_all(vbox); - } else { - GtkWidget* controls = build_control_widget(jalv, window); - GtkWidget* scroll_win = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_add_with_viewport( - GTK_SCROLLED_WINDOW(scroll_win), controls); - gtk_scrolled_window_set_policy( - GTK_SCROLLED_WINDOW(scroll_win), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0); - gtk_widget_show_all(vbox); - - GtkRequisition controls_size, box_size; - gtk_widget_size_request(GTK_WIDGET(controls), &controls_size); - gtk_widget_size_request(GTK_WIDGET(vbox), &box_size); - - gtk_window_set_default_size( - GTK_WINDOW(window), - MAX(MAX(box_size.width, controls_size.width) + 24, 640), - box_size.height + controls_size.height); - } - - g_timeout_add(1000 / jalv->ui_update_hz, - (GSourceFunc)jalv_emit_ui_events, jalv); - - jalv->has_ui = TRUE; - gtk_window_present(GTK_WINDOW(window)); - - gtk_main(); - zix_sem_post(jalv->done); - return 0; -} diff --git a/wscript b/wscript index ffad7f6..971ec43 100644 --- a/wscript +++ b/wscript @@ -46,6 +46,8 @@ def configure(conf): atleast_version='0.120.0', mandatory=True) autowaf.check_pkg(conf, 'gtk+-2.0', uselib_store='GTK2', atleast_version='2.18.0', mandatory=False) + autowaf.check_pkg(conf, 'gtk+-3.0', uselib_store='GTK3', + atleast_version='3.0.0', mandatory=False) autowaf.check_pkg(conf, 'gtkmm-2.4', uselib_store='GTKMM2', atleast_version='2.20.0', mandatory=False) autowaf.check_pkg(conf, 'QtGui', uselib_store='QT4', @@ -66,6 +68,8 @@ def configure(conf): autowaf.display_msg(conf, "Gtk 2.0 support", conf.is_defined('HAVE_GTK2')) + autowaf.display_msg(conf, "Gtk 3.0 support", + conf.is_defined('HAVE_GTK3')) autowaf.display_msg(conf, "Gtkmm 2.0 support", conf.is_defined('HAVE_GTKMM2')) autowaf.display_msg(conf, "Qt 4.0 support", @@ -86,16 +90,26 @@ def build(bld): install_path = '${BINDIR}') autowaf.use_lib(bld, obj, libs) - # Gtk version + # Gtk2 version if bld.is_defined('HAVE_GTK2'): obj = bld(features = 'c cprogram', - source = source + ' src/jalv_gtk2.c', + source = source + ' src/jalv_gtk.c', target = 'jalv.gtk', includes = ['.', 'src'], lib = ['pthread', 'm'], install_path = '${BINDIR}') autowaf.use_lib(bld, obj, libs + ' GTK2') + # Gtk3 version + if bld.is_defined('HAVE_GTK3'): + obj = bld(features = 'c cprogram', + source = source + ' src/jalv_gtk.c', + target = 'jalv.gtk3', + includes = ['.', 'src'], + lib = ['pthread', 'm'], + install_path = '${BINDIR}') + autowaf.use_lib(bld, obj, libs + ' GTK3') + # Gtkmm version if bld.is_defined('HAVE_GTKMM2'): obj = bld(features = 'c cxx cxxprogram', -- cgit v1.2.1