From ea90facf4674046e0a58969a6e0eb23054c550de Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Wed, 13 Nov 2019 22:35:11 +0100 Subject: Use GLib for command line too --- Makefile | 4 +- cli.c | 93 ++++++++++++++++++++++++++++++++ cli.h | 31 +++++++++++ gui.c | 37 ------------- gui.h | 29 ---------- gui_app.c | 3 ++ gui_app_window.c | 10 ++-- main.c | 87 +++--------------------------- options.c | 157 ++++++++++++++++++++++--------------------------------- options.h | 16 +++--- 10 files changed, 214 insertions(+), 253 deletions(-) create mode 100644 cli.c create mode 100644 cli.h delete mode 100644 gui.c delete mode 100644 gui.h diff --git a/Makefile b/Makefile index e80a611..83675d2 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,9 @@ GLIB_COMPILE_RESOURCES = \ $(shell pkg-config --variable=glib_compile_resources gio-2.0) SOURCES := \ + cli.c \ device.c \ image.c \ - gui.c \ gui_app.c \ gui_app_window.c \ gui_resources.c \ @@ -27,6 +27,6 @@ camicro: $(SOURCES:%.c=%.o) $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@ clean: - rm -f camicro $(SOURCES:%.c=%.o) gui_resources.c + rm -f camicro $(SOURCES:%.c=%.o) $(SOURCES:%.c=%.d) gui_resources.c -include $(SOURCES:%.c=%.d) diff --git a/cli.c b/cli.c new file mode 100644 index 0000000..33edcee --- /dev/null +++ b/cli.c @@ -0,0 +1,93 @@ +/* Camicro - Microscope camera viewer. + * + * Copyright (C) 2019 Nicolas Schodet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contact : + * Web: http://ni.fr.eu.org/ + * Email: + */ +#define _GNU_SOURCE +#include +#include + +#include "device.h" +#include "utils.h" +#include "cli.h" + +void +cli_read_images(libusb_context *usb, struct device *device, int count, + const char *out) +{ + device_start(device); + for (int i = 0; i < count;) { + const struct device_image *device_image = device_read(device); + if (device_image) { + char *name = NULL; + if (asprintf(&name, out, i) < 0) + utils_fatal("can not prepare file name"); + utils_info("write %s", name); + png_image image; + memset(&image, 0, sizeof(image)); + image.version = PNG_IMAGE_VERSION; + image.width = device_image->width; + image.height = device_image->height; + image.format = PNG_FORMAT_BGRA; + int r = png_image_write_to_file(&image, name, 0, + device_image->pixels, device_image->stride, NULL); + if (r == 0) + utils_fatal("can not write image: %s", image.message); + free(name); + i++; + } else { + libusb_handle_events(usb); + } + } + device_stop(device); +} + +int +cli_run(struct options *options) +{ + libusb_context *usb; + int r = libusb_init(&usb); + if (r) + utils_fatal("unable to initialize libusb: %s", libusb_strerror(r)); + struct device *device = device_open(usb); + if (!device) + utils_fatal("unable to find device"); + const struct device_info *info = device_get_info(device); + if (options->exposure_ms > 0.0) + device_set_exposure(device, options->exposure_ms); + if (options->gain > 0.0) + device_set_gain(device, options->gain); + int width = -1, height = -1; + for (int i = 0; width == -1 && i < info->resolutions; i++) { + const struct device_info_resolution *ir = &info->resolution[i]; + if ((options->width == -1 || options->width == ir->width) + && (options->height == -1 || options->height == ir->height)) { + width = ir->width; + height = ir->height; + } + } + if (width == -1) + utils_fatal("no matching resolution"); + device_set_resolution(device, width, height, 0); + cli_read_images(usb, device, options->count, options->out); + device_close(device); + libusb_exit(usb); + return EXIT_SUCCESS; +} diff --git a/cli.h b/cli.h new file mode 100644 index 0000000..01f2d8a --- /dev/null +++ b/cli.h @@ -0,0 +1,31 @@ +#ifndef cli_h +#define cli_h +/* Camicro - Microscope camera viewer. + * + * Copyright (C) 2019 Nicolas Schodet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contact : + * Web: http://ni.fr.eu.org/ + * Email: + */ +#include "options.h" + +/* Run command line application, return status code. */ +int +cli_run(struct options *options); + +#endif /* cli_h */ diff --git a/gui.c b/gui.c deleted file mode 100644 index bce344a..0000000 --- a/gui.c +++ /dev/null @@ -1,37 +0,0 @@ -/* Camicro - Microscope camera viewer. - * - * Copyright (C) 2019 Nicolas Schodet - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Contact : - * Web: http://ni.fr.eu.org/ - * Email: - */ -#include - -#include "gui.h" -#include "gui_app.h" - -int -gui_run(int argc, char **argv) -{ - int status; - GuiApp *app; - app = gui_app_new(); - status = g_application_run(G_APPLICATION(app), argc, argv); - g_object_unref(app); - return status; -} diff --git a/gui.h b/gui.h deleted file mode 100644 index b244717..0000000 --- a/gui.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef gui_h -#define gui_h -/* Camicro - Microscope camera viewer. - * - * Copyright (C) 2019 Nicolas Schodet - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Contact : - * Web: http://ni.fr.eu.org/ - * Email: - */ - -int -gui_run(int argc, char **argv); - -#endif /* gui_h */ diff --git a/gui_app.c b/gui_app.c index d29ba2a..5df8d15 100644 --- a/gui_app.c +++ b/gui_app.c @@ -24,6 +24,7 @@ #include "gui_app.h" #include "gui_app_window.h" +#include "options.h" #include "usb_source.h" #include "utils.h" @@ -59,6 +60,7 @@ gui_app_init(GuiApp *app) GuiAppPrivate *priv = gui_app_get_instance_private(app); priv->usb = NULL; priv->usb_source = NULL; + options_add(G_APPLICATION(app)); } static void @@ -107,6 +109,7 @@ gui_app_class_init(GuiAppClass *class) G_APPLICATION_CLASS(class)->startup = gui_app_startup; G_APPLICATION_CLASS(class)->activate = gui_app_activate; G_APPLICATION_CLASS(class)->shutdown = gui_app_shutdown; + G_APPLICATION_CLASS(class)->handle_local_options = options_handle; g_signal_new("video-ready", GUI_APP_TYPE, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } diff --git a/gui_app_window.c b/gui_app_window.c index 0acdaaa..8403d8e 100644 --- a/gui_app_window.c +++ b/gui_app_window.c @@ -124,6 +124,7 @@ video_start(GuiAppWindowPrivate *priv) device_start(priv->device); priv->video_ready_handler_id = g_signal_connect(priv->app, "video-ready", G_CALLBACK(video_ready_cb), priv); + gtk_button_set_label(priv->start_stop_button, "Stop"); } /* Stop video. */ @@ -139,6 +140,7 @@ video_stop(GuiAppWindowPrivate *priv) device_stop(priv->device); priv->started = false; gtk_widget_queue_draw(GTK_WIDGET(priv->video)); + gtk_button_set_label(priv->start_stop_button, "Start"); } static gboolean @@ -188,13 +190,10 @@ start_stop_button_clicked_cb(GtkButton *button, gpointer user_data) GuiAppWindow *win = GUI_APP_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))); GuiAppWindowPrivate *priv = gui_app_window_get_instance_private(win); - if (!priv->started) { - gtk_button_set_label(button, "Stop"); + if (!priv->started) video_start(priv); - } else { - gtk_button_set_label(button, "Start"); + else video_stop(priv); - } } static void @@ -327,4 +326,5 @@ gui_app_window_open(GuiAppWindow *win, struct device *device) gtk_combo_box_text_append_text(priv->resolution_combo_box, buf); } gtk_combo_box_set_active(GTK_COMBO_BOX(priv->resolution_combo_box), 0); + video_start(priv); } diff --git a/main.c b/main.c index 9ffd6f2..2abeeda 100644 --- a/main.c +++ b/main.c @@ -20,88 +20,17 @@ * Web: http://ni.fr.eu.org/ * Email: */ -#define _GNU_SOURCE -#include -#include -#include -#include +#include -#include "device.h" -#include "gui.h" -#include "options.h" -#include "utils.h" - -void -cli_read_images(libusb_context *usb, struct device *device, int count, - const char *out) -{ - device_start(device); - for (int i = 0; i < count;) { - const struct device_image *device_image = device_read(device); - if (device_image) { - char *name = NULL; - if (asprintf(&name, out, i) < 0) - utils_fatal("can not prepare file name"); - utils_info("write %s", name); - png_image image; - memset(&image, 0, sizeof(image)); - image.version = PNG_IMAGE_VERSION; - image.width = device_image->width; - image.height = device_image->height; - image.format = PNG_FORMAT_BGRA; - int r = png_image_write_to_file(&image, name, 0, - device_image->pixels, device_image->stride, NULL); - if (r == 0) - utils_fatal("can not write image: %s", image.message); - free(name); - i++; - } else { - libusb_handle_events(usb); - } - } - device_stop(device); -} - -int -cli_run(struct options *options) -{ - libusb_context *usb; - int r = libusb_init(&usb); - if (r) - utils_fatal("unable to initialize libusb: %s", libusb_strerror(r)); - struct device *device = device_open(usb); - if (!device) - utils_fatal("unable to find device"); - const struct device_info *info = device_get_info(device); - if (options->exposure_ms > 0.0) - device_set_exposure(device, options->exposure_ms); - if (options->gain > 0.0) - device_set_gain(device, options->gain); - int width = -1, height = -1; - for (int i = 0; width == -1 && i < info->resolutions; i++) { - const struct device_info_resolution *ir = &info->resolution[i]; - if ((options->width == -1 || options->width == ir->width) - && (options->height == -1 || options->height == ir->height)) { - width = ir->width; - height = ir->height; - } - } - if (width == -1) - utils_fatal("no matching resolution"); - device_set_resolution(device, width, height, 0); - cli_read_images(usb, device, options->count, options->out); - device_close(device); - libusb_exit(usb); - return EXIT_SUCCESS; -} +#include "gui_app.h" int main(int argc, char **argv) { - struct options options; - options_parse(argc, argv, &options); - if (!options.count) - return gui_run(argc, argv); - else - return cli_run(&options); + int status; + GuiApp *app; + app = gui_app_new(); + status = g_application_run(G_APPLICATION(app), argc, argv); + g_object_unref(app); + return status; } diff --git a/options.c b/options.c index 388402a..bd9f82b 100644 --- a/options.c +++ b/options.c @@ -21,108 +21,75 @@ * Email: */ #define _GNU_SOURCE -#include -#include #include -#include -#include -#include +#include "cli.h" #include "options.h" +#include "utils.h" -/* Report command line usage, optionally prefixed with an error message. */ -static void -usage(int status, const char *msg) +void +options_add(GApplication *app) { - if (msg) - fprintf(stderr, "%s: %s\n", program_invocation_short_name, msg); - fprintf(status == EXIT_SUCCESS ? stdout : stderr, - "usage: %s [options]\n" - "\n" - "Microscope camera viewer.\n" - "\n" - "optional arguments:\n" - " -h, --help show this help message and exit\n" - " -w, --width VALUE image width\n" - " -e, --exposure MS exposure value\n" - " -g, --gain VALUE gain value\n" - " -o, --output FILE output file pattern (default:" - " out%%02d.png)\n" - " -n, --count N number of image to take" - " (default: gui)\n" - , program_invocation_name); - exit(status); + g_application_set_option_context_summary(app, "Microscope camera viewer"); + g_application_set_option_context_description(app, + "Without options, run the GUI, else switch to batch mode to dump" + " image from the camera."); + g_application_add_main_option(app, "width", 'w', 0, + G_OPTION_ARG_INT, "image width", "VALUE"); + g_application_add_main_option(app, "exposure", 'e', 0, + G_OPTION_ARG_DOUBLE, "exposure value", "MS"); + g_application_add_main_option(app, "gain", 'g', 0, + G_OPTION_ARG_DOUBLE, "gain value", "VALUE"); + g_application_add_main_option(app, "output", 'o', 0, + G_OPTION_ARG_FILENAME, + "output file pattern (default: out%02d.png)", "PATTERN"); + g_application_add_main_option(app, "count", 'n', 0, + G_OPTION_ARG_INT, "number of image to take", "N"); } -void -options_parse(int argc, char **argv, struct options *options) +gint +options_handle(GApplication *app, GVariantDict *options_dict) { - options->width = -1; - options->height = -1; - options->exposure_ms = -1.0; - options->gain = -1.0; - options->count = 0; - options->out = NULL; - char *tail; - while (1) { - static struct option long_options[] = { - { "help", no_argument, 0, 'h' }, - { "width", required_argument, 0, 'w' }, - { "exposure", required_argument, 0, 'e' }, - { "gain", required_argument, 0, 'g' }, - { "output", required_argument, 0, 'o' }, - { "count", required_argument, 0, 'n' }, - { NULL }, - }; - int option_index = 0; - int c = getopt_long(argc, argv, "hw:e:g:o:n:", long_options, - &option_index); - if (c == -1) - break; - switch (c) { - case 'h': - usage(EXIT_SUCCESS, NULL); - break; - case 'w': - errno = 0; - options->width = strtol(optarg, &tail, 10); - if (*tail != '\0' || errno || options->width < 1) - usage(EXIT_FAILURE, "bad width value"); - break; - case 'e': - errno = 0; - options->exposure_ms = strtod(optarg, &tail); - if (*tail != '\0' || errno || options->exposure_ms < 1) - usage(EXIT_FAILURE, "bad exposure value"); - break; - case 'g': - errno = 0; - options->gain = strtod(optarg, &tail); - if (*tail != '\0' || errno || options->gain <= 0.0) - usage(EXIT_FAILURE, "bad gain value"); - break; - case 'o': - options->out = optarg; - break; - case 'n': - errno = 0; - options->count = strtoul(optarg, &tail, 10); - if (*tail != '\0' || errno) - usage(EXIT_FAILURE, "bad count value"); - break; - case '?': - usage(EXIT_FAILURE, NULL); - break; - default: - abort(); - } + struct options options; + options.width = -1; + options.height = -1; + options.exposure_ms = -1.0; + options.gain = -1.0; + options.count = 1; + options.out = NULL; + bool option_set = false; + if (g_variant_dict_lookup(options_dict, "width", "i", &options.width)) { + option_set = true; + if (options.width <= 0) + utils_fatal("invalid width"); + } + if (g_variant_dict_lookup(options_dict, "exposure", "d", + &options.exposure_ms)) { + option_set = true; + if (options.exposure_ms < 1.0) + utils_fatal("invalid exposure"); + } + if (g_variant_dict_lookup(options_dict, "gain", "d", &options.gain)) { + option_set = true; + if (options.gain <= 0.0) + utils_fatal("invalid gain"); + } + if (g_variant_dict_lookup(options_dict, "count", "i", &options.count)) { + option_set = true; + if (options.count <= 0) + utils_fatal("invalid count"); + } + if (g_variant_dict_lookup(options_dict, "out", "^&ay", &options.out)) { + option_set = true; + int argtypes[1]; + int formats = parse_printf_format(options.out, 1, argtypes); + if (formats != 1 || argtypes[0] != PA_INT) + utils_fatal("bad file pattern, use one %%d"); + } else { + options.out = "out%02d.png"; } - if (optind < argc) - usage(EXIT_FAILURE, "too many arguments"); - if (!options->out) - options->out = "out%02d.png"; - int argtypes[1]; - int formats = parse_printf_format(options->out, 1, argtypes); - if (formats != 1 || argtypes[0] != PA_INT) - usage(EXIT_FAILURE, "bad file pattern, use one %d"); + if (option_set) + return cli_run(&options); + else + return -1; } diff --git a/options.h b/options.h index 3cc0681..86741cf 100644 --- a/options.h +++ b/options.h @@ -23,6 +23,7 @@ * Email: */ #include +#include /* Runtime options. */ struct options { @@ -34,16 +35,19 @@ struct options { double exposure_ms; /* Digital gain after image acquisition, -1.0 for default. */ double gain; - /* When zero, run interactive mode, else number of frame to dump. */ + /* Number of frame to dump. */ int count; - /* Output file name or pattern, only used in dump mode. This should - * include a printf like pattern (%d) used to name the files. Only used in - * dump mode. */ + /* Output file name or pattern. This should include a printf like pattern + * (%d) used to name the files. */ const char *out; }; -/* Parse command line options and fill provided options structure. */ +/* Add options to application. */ void -options_parse(int argc, char **argv, struct options *options); +options_add(GApplication *app); + +/* Handle options. */ +gint +options_handle(GApplication *app, GVariantDict *options_dict); #endif /* options_h */ -- cgit v1.2.3