summaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c377
1 files changed, 317 insertions, 60 deletions
diff --git a/src/main.c b/src/main.c
index 8caf117..74cb96b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -19,6 +19,7 @@
#include <sys/time.h>
#include <til.h>
+#include <til_args.h>
/* glimmer is a GTK+-3.0 frontend for rototiller */
@@ -27,16 +28,21 @@ extern til_fb_ops_t gtk_fb_ops;
#define DEFAULT_WIDTH 320
#define DEFAULT_HEIGHT 480
-#define BOX_SPACING 4
+#define DEFAULT_MODULE "rtv"
+#define BOX_SPACING 1
+#define FRAME_MARGIN 8
+#define LABEL_MARGIN 4
#define NUM_FB_PAGES 3
static struct glimmer_t {
- GtkWidget *modules_list;
+ GtkComboBox *modules_combobox;
+ GtkWidget *module_box, *module_frame;
- til_fb_t *fb;
+ til_args_t args;
til_settings_t *video_settings;
-
til_settings_t *module_settings;
+
+ til_fb_t *fb;
const til_module_t *module;
void *module_context;
pthread_t thread;
@@ -45,12 +51,44 @@ static struct glimmer_t {
} glimmer;
-static unsigned get_ticks(const struct timeval *start, const struct timeval *now, unsigned offset)
+static void glimmer_module_setup(const til_module_t *module, til_settings_t *settings);
+
+
+static unsigned glimmer_get_ticks(const struct timeval *start, const struct timeval *now, unsigned offset)
{
return (unsigned)((now->tv_sec - start->tv_sec) * 1000 + (now->tv_usec - start->tv_usec) / 1000) + offset;
}
+static void glimmer_active_module(const til_module_t **res_module, til_settings_t **res_settings)
+{
+ GtkTreeIter iter;
+
+ if (gtk_combo_box_get_active_iter(glimmer.modules_combobox, &iter)) {
+ GtkTreeModel *model = gtk_combo_box_get_model(glimmer.modules_combobox);
+ char *name;
+
+ gtk_tree_model_get(model, &iter,
+ 0, &name,
+ 1, res_module,
+ 2, res_settings,
+ -1);
+
+ g_free(name);
+ }
+}
+
+
+static void glimmer_active_module_setup(void)
+{
+ const til_module_t *module;
+ til_settings_t *settings;
+
+ glimmer_active_module(&module, &settings);
+ glimmer_module_setup(module, settings);
+}
+
+
/* TODO: this should probably move into libtil */
static void * glimmer_thread(void *foo)
{
@@ -62,7 +100,7 @@ static void * glimmer_thread(void *foo)
page = til_fb_page_get(glimmer.fb);
gettimeofday(&now, NULL);
- ticks = get_ticks(&glimmer.start_tv, &now, glimmer.ticks_offset);
+ ticks = glimmer_get_ticks(&glimmer.start_tv, &now, glimmer.ticks_offset);
til_module_render(glimmer.module, glimmer.module_context, ticks, &page->fragment);
til_fb_page_put(glimmer.fb, page);
}
@@ -71,7 +109,8 @@ static void * glimmer_thread(void *foo)
static void glimmer_go(GtkButton *button, gpointer user_data)
{
- int r;
+ til_settings_t *settings;
+ int r;
if (glimmer.fb) {
pthread_cancel(glimmer.thread);
@@ -79,21 +118,10 @@ static void glimmer_go(GtkButton *button, gpointer user_data)
til_quiesce();
glimmer.fb = til_fb_free(glimmer.fb);
- glimmer.video_settings = til_settings_free(glimmer.video_settings);
- glimmer.module_settings = til_settings_free(glimmer.module_settings);
+ glimmer.module_context = til_module_destroy_context(glimmer.module, glimmer.module_context);
}
- /* TODO: translate the GTK+ settings panel values into
- * glimmer.{fb,module}_settings
- */
-
- /* For now, construct a simple 640x480 non-fullscreen fb, and
- * simply don't do any module setup (those *should* have static builtin
- * defaults that at least work on some level.
- */
- glimmer.video_settings = til_settings_new("fullscreen=off,size=640x480");
- glimmer.module_settings = til_settings_new("TODO");
-
+ /* TODO: prolly stop recreating fb on every go, or maybe only if the settings changed */
r = til_fb_new(&gtk_fb_ops, glimmer.video_settings, NUM_FB_PAGES, &glimmer.fb);
if (r < 0) {
puts("fb no go!");
@@ -101,10 +129,10 @@ static void glimmer_go(GtkButton *button, gpointer user_data)
}
gettimeofday(&glimmer.start_tv, NULL);
- glimmer.module = til_lookup_module(gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(glimmer.modules_list)));
+ glimmer_active_module(&glimmer.module, &settings);
r = til_module_create_context(
glimmer.module,
- get_ticks(
+ glimmer_get_ticks(
&glimmer.start_tv,
&glimmer.start_tv,
glimmer.ticks_offset),
@@ -118,13 +146,186 @@ static void glimmer_go(GtkButton *button, gpointer user_data)
}
-static void activate(GtkApplication *app, gpointer user_data)
+static void glimmer_combobox_setting_changed_cb(GtkComboBoxText *combobox, gpointer user_data)
{
- GtkWidget *window, *vbox, *settings, *button;
- const til_module_t **modules;
- size_t n_modules;
+ til_setting_t *setting = user_data;
+
+ /* XXX FIXME FIXME XXX */
+ /* XXX FIXME FIXME XXX */
+ /* I don't know gtk+ well enough to know what's the non-leaky way to do this:
+ * glimmer_active_module_setup() will destroy the module frame which encompasses the
+ * widget this signal emitted from. It appears that there isn't a reference held across
+ * the signal callbacks so they can safely perform a queued destroy of the originating widget
+ * within the callback to then become realized at the end of all the signal deliveries and
+ * callback processing when the final reference gets removed.
+ *
+ * for now I'm working around this by simply adding a ref, leaking the memory, until I find
+ * the Right Way.
+ */
+ g_object_ref(combobox);
+ /* XXX FIXME FIXME XXX */
+ /* XXX FIXME FIXME XXX */
- til_get_modules(&modules, &n_modules);
+ setting->value = gtk_combo_box_text_get_active_text(combobox);
+ glimmer_active_module_setup();
+}
+
+static void glimmer_entry_setting_changed_cb(GtkEntry *entry, gpointer user_data)
+{
+ til_setting_t *setting = user_data;
+
+ /* XXX FIXME: see above comment for combobox, but oddly I'm only seeing
+ * errors printed for the combobox case. I'm just assuming the problem exists here as well.
+ */
+ g_object_ref(entry);
+
+ /* FIXME TODO there needs to be some validation of the free-form input against setting->desc->regex,
+ * though that probably shouldn't happen here.
+ */
+ setting->value = strdup(gtk_entry_get_text(entry));
+ glimmer_active_module_setup();
+}
+
+
+/* (re)construct the gui settings pane to reflect *module and *settings */
+static void glimmer_module_setup(const til_module_t *module, til_settings_t *settings)
+{
+ GtkWidget *vbox, *label;
+
+ if (glimmer.module_frame)
+ gtk_widget_destroy(glimmer.module_frame);
+
+ glimmer.module_frame = g_object_new( GTK_TYPE_FRAME,
+ "parent", GTK_CONTAINER(glimmer.module_box),
+ "label", module->name,
+ "label-xalign", .01f,
+ "margin", FRAME_MARGIN,
+ "visible", TRUE,
+ NULL);
+ gtk_box_set_child_packing( GTK_BOX(glimmer.module_box),
+ GTK_WIDGET(glimmer.module_frame),
+ TRUE,
+ FALSE,
+ BOX_SPACING,
+ GTK_PACK_START);
+
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, BOX_SPACING);
+ gtk_container_add(GTK_CONTAINER(glimmer.module_frame), vbox);
+
+ label = g_object_new( GTK_TYPE_LABEL,
+ "parent", GTK_CONTAINER(vbox),
+ "label", module->description,
+ "halign", GTK_ALIGN_START,
+ "margin", LABEL_MARGIN,
+ "visible", TRUE,
+ NULL);
+
+ label = g_object_new( GTK_TYPE_LABEL,
+ "parent", GTK_CONTAINER(vbox),
+ "label", module->author,
+ "halign", GTK_ALIGN_START,
+ "margin", LABEL_MARGIN,
+ "visible", TRUE,
+ NULL);
+
+ if (module->setup) {
+ GtkWidget *frame, *svbox;
+ til_setting_t *setting;
+ const til_setting_desc_t *desc;
+
+ frame = g_object_new( GTK_TYPE_FRAME,
+ "parent", GTK_CONTAINER(vbox),
+ "label", "Settings",
+ "label-xalign", .01f,
+ "margin", FRAME_MARGIN,
+ "visible", TRUE,
+ NULL);
+ gtk_box_set_child_packing( GTK_BOX(vbox),
+ GTK_WIDGET(frame),
+ TRUE,
+ TRUE,
+ BOX_SPACING,
+ GTK_PACK_START);
+
+ svbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, BOX_SPACING);
+ gtk_container_add(GTK_CONTAINER(frame), svbox);
+
+ til_settings_reset_descs(settings);
+ while (module->setup(settings, &setting, &desc) > 0) {
+ GtkWidget *shbox;
+
+ if (!setting) {
+ til_settings_add_value(settings, desc->key, desc->preferred, NULL);
+ continue;
+ }
+
+ shbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, BOX_SPACING);
+ gtk_container_add(GTK_CONTAINER(svbox), shbox);
+ gtk_widget_set_halign(GTK_WIDGET(shbox), GTK_ALIGN_START);
+
+ label = g_object_new( GTK_TYPE_LABEL,
+ "parent", GTK_CONTAINER(shbox),
+ "label", desc->name,
+ "halign", GTK_ALIGN_START,
+ "margin", LABEL_MARGIN,
+ "visible", TRUE,
+ NULL);
+
+ if (desc->values) {
+ GtkWidget *combobox;
+
+ /* combo box */
+ combobox = gtk_combo_box_text_new();
+ gtk_container_add(GTK_CONTAINER(shbox), combobox);
+ gtk_widget_set_halign(GTK_WIDGET(combobox), GTK_ALIGN_END);
+ for (int i = 0; desc->values[i]; i++) {
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combobox), NULL, desc->values[i]);
+ if (!strcmp(setting->value, desc->values[i]))
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), i);
+ }
+ g_signal_connect(combobox, "changed", G_CALLBACK(glimmer_combobox_setting_changed_cb), setting);
+
+ } else {
+ GtkWidget *entry;
+
+ /* plain unstructured text input box */
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), setting->value);
+ gtk_container_add(GTK_CONTAINER(shbox), entry);
+ gtk_widget_set_halign(GTK_WIDGET(entry), GTK_ALIGN_END);
+
+ /* XXX FIXME */
+ /* XXX FIXME */
+ /* XXX FIXME */
+ /* "activate" only occurs on hitting Enter in the GtkEntry. So we'll miss
+ * edits that are visible but not Entered before hitting Go! We likely need
+ * to catch more signals...
+ */
+ /* XXX FIXME */
+ /* XXX FIXME */
+ /* XXX FIXME */
+
+ g_signal_connect(entry, "activate", G_CALLBACK(glimmer_entry_setting_changed_cb), setting);
+ }
+
+ if (!setting->desc)
+ setting->desc = desc;
+ }
+ }
+
+ gtk_widget_show_all(glimmer.module_frame);
+}
+
+
+static void glimmer_module_changed_cb(GtkComboBox *box, G_GNUC_UNUSED gpointer user_data)
+{
+ glimmer_active_module_setup();
+}
+
+
+static void glimmer_activate(GtkApplication *app, gpointer user_data)
+{
+ GtkWidget *window, *vbox, *button;
window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "glimmer");
@@ -133,54 +334,110 @@ static void activate(GtkApplication *app, gpointer user_data)
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, BOX_SPACING);
gtk_container_add(GTK_CONTAINER(window), vbox);
- glimmer.modules_list = gtk_combo_box_text_new();
- for (size_t i = 0; i < n_modules; i++) {
- gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(glimmer.modules_list), NULL, modules[i]->name);
-
- /* like rototiller, default to rtv */
- if (!strcmp(modules[i]->name, "rtv"))
- gtk_combo_box_set_active(GTK_COMBO_BOX(glimmer.modules_list), i);
+ { /* construct modules list combobox, associating a name, module, and settings per entry */
+ const til_module_t **modules;
+ size_t n_modules;
+ const char *module;
+ GtkComboBox *combobox;
+ GtkListStore *store;
+ GtkCellRenderer *text;
+
+ til_get_modules(&modules, &n_modules);
+ module = til_settings_get_key(glimmer.module_settings, 0, NULL);
+
+ combobox = g_object_new(GTK_TYPE_COMBO_BOX, "visible", TRUE, NULL);
+ store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER);
+ for (size_t i = 0; i < n_modules; i++) {
+ GtkTreeIter iter;
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set( store, &iter,
+ 0, modules[i]->name,
+ 1, modules[i],
+ 2, (module && !strcmp(module, modules[i]->name)) ? glimmer.module_settings : til_settings_new(NULL),
+ -1);
+ }
+
+ gtk_combo_box_set_model(combobox, GTK_TREE_MODEL(store));
+ gtk_combo_box_set_id_column(combobox, 0);
+ gtk_combo_box_set_active_id(combobox, module ? : DEFAULT_MODULE);
+
+ g_signal_connect(combobox, "changed", G_CALLBACK(glimmer_module_changed_cb), NULL);
+
+ text = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), text, TRUE);
+ gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combobox), text, "text", 0);
+
+ glimmer.modules_combobox = combobox;
}
- gtk_container_add(GTK_CONTAINER(vbox), glimmer.modules_list);
-
- gtk_box_set_child_packing(
- GTK_BOX(vbox),
- glimmer.modules_list,
- FALSE,
- FALSE,
- BOX_SPACING * 4, /* FIXME: having the combo box too near the window edge puts the pointer into the scroll-up arrow on click :/ */
- GTK_PACK_START);
-
- /* TODO: below the combobox, present framebuffer and the selected module's settings */
- settings = gtk_label_new("TODO: fb/module settings here");
- gtk_container_add(GTK_CONTAINER(vbox), settings);
- gtk_box_set_child_packing(
- GTK_BOX(vbox),
- settings,
- TRUE,
- TRUE,
- BOX_SPACING,
- GTK_PACK_START);
+
+ gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(glimmer.modules_combobox));
+ gtk_box_set_child_packing( GTK_BOX(vbox),
+ GTK_WIDGET(glimmer.modules_combobox),
+ FALSE,
+ FALSE,
+ BOX_SPACING * 4,
+ GTK_PACK_START);
+
+ glimmer.module_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, BOX_SPACING);
+ gtk_container_add(GTK_CONTAINER(vbox), glimmer.module_box);
+ gtk_box_set_child_packing( GTK_BOX(vbox),
+ glimmer.module_box,
+ TRUE,
+ TRUE,
+ BOX_SPACING,
+ GTK_PACK_START);
+
+ glimmer_active_module_setup();
/* button to rototill as configured */
- button = gtk_button_new_with_label("Go!");
- gtk_container_add(GTK_CONTAINER(vbox), button);
+ button = g_object_new( GTK_TYPE_BUTTON,
+ "parent", GTK_CONTAINER(vbox),
+ "label", "Go!",
+ "visible", TRUE,
+ NULL);
g_signal_connect(button, "clicked", G_CALLBACK(glimmer_go), NULL);
gtk_widget_show_all(window);
}
-int main(int argc, char **argv)
+int main(int argc, const char *argv[])
{
+ int r, status, pruned_argc;
+ const char **pruned_argv;
GtkApplication *app;
- int status;
til_init();
+
+ r = til_args_pruned_parse(argc, argv, &glimmer.args, &pruned_argc, &pruned_argv);
+ if (r < 0) {
+ fprintf(stderr, "Unable to parse args: %s\n", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ glimmer.module_settings = til_settings_new(glimmer.args.module);
+ /* TODO: glimmer doesn't currently handle video settings, gtk_fb doesn't even
+ * implement a .setup() method. It would be an interesting exercise to bring
+ * in support for rototiller's sdl and drm fb backends, but it immediately
+ * becomes awkward with obvious conflicts like drm wanting to own the display
+ * implicitly being shared when glimmer's already using gtk if not on distinct
+ * devices.
+ *
+ * But it'd be nice to at least support window sizing/fullscreen
+ * startup via args w/gtk_fb, so at some point I should add a
+ * gtk_fb.setup() method for filling in the blanks of what the args omit.
+ * For now these statically defined comprehensive settings simply skirt
+ * the issue.
+ */
+ //glimmer.video_settings = til_settings_new(glimmer.args.video);
+ glimmer.video_settings = til_settings_new("fullscreen=off,size=640x480");
+
app = gtk_application_new("com.pengaru.glimmer", G_APPLICATION_FLAGS_NONE);
- g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
- status = g_application_run(G_APPLICATION(app), argc, argv);
+ g_signal_connect(app, "activate", G_CALLBACK(glimmer_activate), NULL);
+ status = g_application_run(G_APPLICATION(app), pruned_argc, (char **)pruned_argv);
g_object_unref(app);
+
til_shutdown();
return status;
© All Rights Reserved