summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli.c59
-rw-r--r--device.c71
-rw-r--r--device.h48
-rw-r--r--gui_app.c26
-rw-r--r--gui_app_window.c60
-rw-r--r--image.c59
-rw-r--r--image.h19
-rw-r--r--moticam.c254
-rw-r--r--options.c2
-rw-r--r--usb_source.c8
-rw-r--r--utils.c18
-rw-r--r--utils.h4
12 files changed, 386 insertions, 242 deletions
diff --git a/cli.c b/cli.c
index 79225c1..a5c20c7 100644
--- a/cli.c
+++ b/cli.c
@@ -20,21 +20,35 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
*/
-#define _GNU_SOURCE
-#include <stdio.h>
+#include <glib.h>
+#include "cli.h"
#include "device.h"
#include "utils.h"
-#include "cli.h"
-void
+static bool
cli_read_images(libusb_context *usb, struct device *device, int count,
- bool raw, const char *out)
+ bool raw, const char *out, GError **error)
{
- device_start(device);
- for (int i = 0; i < count;) {
- struct image *image = device_read(device);
- if (image) {
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
+ if (!device_start(device, error))
+ return false;
+ bool ret = true;
+ for (int i = 0; ret && i < count;) {
+ /* Read image. */
+ GError *image_error = NULL;
+ struct image *image = device_read(device, &image_error);
+ if (!image && image_error) {
+ g_propagate_error(error, image_error);
+ ret = false;
+ } else if (!image) {
+ /* No image yet, handle events. */
+ int r = libusb_handle_events(usb);
+ if (r)
+ utils_fatal("unable to handle libusb events: %s",
+ libusb_strerror(r));
+ } else {
+ /* Image received. */
if (!raw && image->format != IMAGE_FORMAT_XBGR32) {
struct image *cimage = image_new(image->width, image->height,
image->width * 4, IMAGE_FORMAT_XBGR32);
@@ -42,19 +56,18 @@ cli_read_images(libusb_context *usb, struct device *device, int count,
image_unref(image);
image = cimage;
}
- char *name = NULL;
- if (asprintf(&name, out, i) < 0)
- utils_fatal("can not prepare file name");
+ char *name = g_strdup_printf(out, i);
utils_info("write %s", name);
- image_save(image, name);
- free(name);
+ if (!image_save(image, name, error))
+ ret = false;
+ g_free(name);
image_unref(image);
i++;
- } else {
- libusb_handle_events(usb);
}
}
- device_stop(device);
+ if (!device_stop(device, ret ? error : NULL))
+ return false;
+ return ret;
}
int
@@ -64,9 +77,10 @@ cli_run(struct options *options)
int r = libusb_init(&usb);
if (r)
utils_fatal("unable to initialize libusb: %s", libusb_strerror(r));
- struct device *device = device_open(usb);
+ GError *error = NULL;
+ struct device *device = device_open(usb, &error);
if (!device)
- utils_fatal("unable to find device");
+ utils_fatal("unable to find device: %s", error->message);
const struct device_info *info = device_get_info(device);
if (options->exposure_ms > 0.0)
device_set_exposure(device, options->exposure_ms);
@@ -84,8 +98,11 @@ cli_run(struct options *options)
if (width == -1)
utils_fatal("no matching resolution");
device_set_resolution(device, width, height, 0);
- cli_read_images(usb, device, options->count, options->raw, options->out);
- device_close(device);
+ if (!cli_read_images(usb, device, options->count, options->raw,
+ options->out, &error))
+ utils_fatal("unable to read images: %s", error->message);
+ if (!device_close(device, &error))
+ utils_fatal("unable to close device: %s", error->message);
libusb_exit(usb);
return EXIT_SUCCESS;
}
diff --git a/device.c b/device.c
index cfc8783..09a94e3 100644
--- a/device.c
+++ b/device.c
@@ -20,10 +20,7 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
*/
-#include <stdlib.h>
-
#include "device.h"
-#include "utils.h"
#include "moticam.h"
@@ -32,31 +29,38 @@ static const struct device_driver *drivers[] = {
NULL
};
+GQuark
+device_error_quark(void)
+{
+ return g_quark_from_static_string("device-error-quark");
+}
+
struct device *
-device_open(libusb_context *usb)
+device_open(libusb_context *usb, GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, NULL);
libusb_device **list;
libusb_device *found_usb_device = NULL;
char *found_device_name = NULL;
const struct device_driver *found_device_driver = NULL;
ssize_t cnt = libusb_get_device_list(usb, &list);
- if (cnt < 0)
- utils_fatal("can not list devices: %s", libusb_strerror(cnt));
- for (ssize_t i = 0; i < cnt; i++) {
+ if (cnt < 0) {
+ g_set_error(error, DEVICE_ERROR, DEVICE_ERROR_LIST,
+ "can not list devices: %s", libusb_strerror(cnt));
+ return NULL;
+ }
+ for (ssize_t i = 0; !found_usb_device && i < cnt; i++) {
libusb_device *usb_device = list[i];
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(usb_device, &desc);
- if (r)
- utils_fatal("can not get device descriptor: %s",
+ if (r) {
+ g_warning("can not get device descriptor: %s",
libusb_strerror(r));
- for (const struct device_driver **dd = drivers; *dd; dd++) {
- char *device_name = (*dd)->usb_poll(&desc);
- if (device_name) {
- if (found_device_name)
- utils_fatal("more than one matching device, not"
- " supported");
- else
- {
+ } else {
+ for (const struct device_driver **dd = drivers;
+ !found_usb_device && *dd; dd++) {
+ char *device_name = (*dd)->usb_poll(&desc);
+ if (device_name) {
found_usb_device = usb_device;
found_device_name = device_name;
found_device_driver = *dd;
@@ -66,8 +70,11 @@ device_open(libusb_context *usb)
}
struct device *device = NULL;
if (found_usb_device) {
- free(found_device_name);
- device = found_device_driver->usb_open(usb, found_usb_device);
+ g_free(found_device_name);
+ device = found_device_driver->usb_open(usb, found_usb_device, error);
+ } else {
+ g_set_error(error, DEVICE_ERROR, DEVICE_ERROR_LIST,
+ "no device found");
}
libusb_free_device_list(list, 1);
return device;
@@ -98,26 +105,30 @@ device_set_resolution(struct device *device, int width, int height,
device->set_resolution(device, width, height, stride);
}
-void
-device_start(struct device *device)
+bool
+device_start(struct device *device, GError **error)
{
- device->start(device);
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
+ return device->start(device, error);
}
struct image *
-device_read(struct device *device)
+device_read(struct device *device, GError **error)
{
- return device->read(device);
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
+ return device->read(device, error);
}
-void
-device_stop(struct device *device)
+bool
+device_stop(struct device *device, GError **error)
{
- device->stop(device);
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
+ return device->stop(device, error);
}
-void
-device_close(struct device *device)
+bool
+device_close(struct device *device, GError **error)
{
- device->close(device);
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
+ return device->close(device, error);
}
diff --git a/device.h b/device.h
index 01c6336..7028ebb 100644
--- a/device.h
+++ b/device.h
@@ -26,9 +26,13 @@
*/
#include <libusb.h>
#include <stdint.h>
+#include <glib.h>
#include "image.h"
+/* Error domain for device related errors. */
+#define DEVICE_ERROR device_error_quark()
+
struct device;
/* USB poll function. If the device is handled by this driver, it should
@@ -39,7 +43,7 @@ typedef char *(*device_driver_usb_poll_f)(
/* USB open function. The device driver previously declared that it handles
* this device. */
typedef struct device *(*device_driver_usb_open_f)(
- libusb_context *usb, libusb_device *usb_device);
+ libusb_context *usb, libusb_device *usb_device, GError **error);
/* Information on a device driver. */
struct device_driver {
@@ -97,20 +101,20 @@ typedef void (*device_set_resolution_f)(
struct device *device, int width, int height, int stride);
/* Start streaming. */
-typedef void (*device_start_f)(
- struct device *device);
+typedef bool (*device_start_f)(
+ struct device *device, GError **error);
/* Return an image if one is available, else, return NULL. */
typedef struct image *(*device_read_f)(
- struct device *device);
+ struct device *device, GError **error);
/* Stop streaming. */
-typedef void (*device_stop_f)(
- struct device *device);
+typedef bool (*device_stop_f)(
+ struct device *device, GError **error);
/* Close device. */
-typedef void (*device_close_f)(
- struct device *device);
+typedef bool (*device_close_f)(
+ struct device *device, GError **error);
/* Open device. */
struct device {
@@ -132,9 +136,21 @@ struct device {
device_close_f close;
};
+/* Error codes. */
+enum DeviceError {
+ /* Error listing devices. */
+ DEVICE_ERROR_LIST,
+ /* Generic USB error. */
+ DEVICE_ERROR_USB,
+};
+
+/* Return quark of error domain for device related errors. */
+GQuark
+device_error_quark(void);
+
/* Find and open device. */
struct device *
-device_open(libusb_context *usb);
+device_open(libusb_context *usb, GError **error);
/* Retrieve informations. */
const struct device_info *
@@ -154,19 +170,19 @@ device_set_resolution(struct device *device, int width, int height,
int stride);
/* Start streaming. */
-void
-device_start(struct device *device);
+bool
+device_start(struct device *device, GError **error);
/* Read an image. */
struct image *
-device_read(struct device *device);
+device_read(struct device *device, GError **error);
/* Stop streaming. */
-void
-device_stop(struct device *device);
+bool
+device_stop(struct device *device, GError **error);
/* Close device. */
-void
-device_close(struct device *device);
+bool
+device_close(struct device *device, GError **error);
#endif /* device_h */
diff --git a/gui_app.c b/gui_app.c
index 5df8d15..e58e4ae 100644
--- a/gui_app.c
+++ b/gui_app.c
@@ -20,8 +20,6 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
*/
-#include <gtk/gtk.h>
-
#include "gui_app.h"
#include "gui_app_window.h"
#include "options.h"
@@ -49,7 +47,9 @@ gui_app_usb_source_cb(gpointer user_data)
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
- libusb_handle_events_timeout(priv->usb, &tv);
+ int r = libusb_handle_events_timeout(priv->usb, &tv);
+ if (r)
+ g_error("unable to handle libusb events: %s", libusb_strerror(r));
g_signal_emit_by_name(app, "video-ready");
return G_SOURCE_CONTINUE;
}
@@ -70,7 +70,7 @@ gui_app_startup(GApplication *app)
GuiAppPrivate *priv = gui_app_get_instance_private(GUI_APP(app));
int r = libusb_init(&priv->usb);
if (r)
- utils_fatal("unable to initialize libusb: %s", libusb_strerror(r));
+ g_error("unable to initialize libusb: %s", libusb_strerror(r));
priv->usb_source = usb_source_new(priv->usb);
g_source_set_callback(priv->usb_source, gui_app_usb_source_cb, app,
NULL);
@@ -81,13 +81,17 @@ static void
gui_app_activate(GApplication *app)
{
GuiAppPrivate *priv = gui_app_get_instance_private(GUI_APP(app));
- struct device *device = device_open(priv->usb);
- if (!device)
- utils_fatal("unable to find device");
- GuiAppWindow *window;
- window = gui_app_window_new(GUI_APP(app));
- gui_app_window_open(window, device);
- gtk_window_present(GTK_WINDOW(window));
+ GError *error = NULL;
+ struct device *device = device_open(priv->usb, &error);
+ if (!device) {
+ utils_dialog_error(NULL, "unable to find device: %s", error->message);
+ g_error_free(error);
+ } else {
+ GuiAppWindow *window;
+ window = gui_app_window_new(GUI_APP(app));
+ gui_app_window_open(window, device);
+ gtk_window_present(GTK_WINDOW(window));
+ }
}
static void
diff --git a/gui_app_window.c b/gui_app_window.c
index 7c2bedd..b7c0d84 100644
--- a/gui_app_window.c
+++ b/gui_app_window.c
@@ -20,16 +20,11 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
*/
-#include <gtk/gtk.h>
#include <stdbool.h>
-#include <assert.h>
#include <math.h>
-#include "device.h"
-#include "utils.h"
-
-#include "gui_app.h"
#include "gui_app_window.h"
+#include "utils.h"
struct _GuiAppWindow {
GtkApplicationWindow parent;
@@ -38,6 +33,7 @@ struct _GuiAppWindow {
typedef struct _GuiAppWindowPrivate GuiAppWindowPrivate;
struct _GuiAppWindowPrivate {
+ GuiAppWindow *win;
GuiApp *app;
GtkDrawingArea *video;
GtkButton *start_stop_button;
@@ -107,8 +103,17 @@ video_ready_cb(GuiApp *app, gpointer user_data)
{
GuiAppWindowPrivate *priv = user_data;
if (priv->started) {
- struct image *image = device_read(priv->device);
- if (image) {
+ GError *error = NULL;
+ struct image *image = device_read(priv->device, &error);
+ if (!image) {
+ if (error) {
+ utils_dialog_error(GTK_WINDOW(priv->win),
+ "error reading image: %s",
+ error->message);
+ g_error_free(error);
+ gtk_widget_destroy(GTK_WIDGET(priv->win));
+ }
+ } else {
/* Release old data. */
if (priv->surface) {
cairo_surface_destroy(priv->surface);
@@ -165,23 +170,29 @@ video_ready_cb(GuiApp *app, gpointer user_data)
static void
video_start(GuiAppWindowPrivate *priv)
{
- assert(!priv->started);
- priv->started = true;
+ g_assert(!priv->started);
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24,
priv->width);
device_set_resolution(priv->device, priv->width, priv->height, stride);
video_size_update(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");
+ GError *error = NULL;
+ if (!device_start(priv->device, &error)) {
+ utils_dialog_error(GTK_WINDOW(priv->win), "can not start: %s",
+ error->message);
+ g_error_free(error);
+ } else {
+ 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");
+ priv->started = true;
+ }
}
/* Stop video. */
static void
video_stop(GuiAppWindowPrivate *priv)
{
- assert(priv->started);
+ g_assert(priv->started);
if (priv->surface) {
cairo_surface_destroy(priv->surface);
priv->surface = NULL;
@@ -195,7 +206,11 @@ video_stop(GuiAppWindowPrivate *priv)
priv->image_converted = NULL;
}
g_signal_handler_disconnect(priv->app, priv->video_ready_handler_id);
- device_stop(priv->device);
+ GError *error = NULL;
+ if (!device_stop(priv->device, &error)) {
+ g_warning("device_stop: %s", error->message);
+ g_error_free(error);
+ }
priv->started = false;
gtk_widget_queue_draw(GTK_WIDGET(priv->video));
gtk_button_set_label(priv->start_stop_button, "Start");
@@ -238,7 +253,11 @@ destroy_cb(GtkWidget *widget, gpointer user_data)
if (priv->started)
video_stop(priv);
if (priv->device) {
- device_close(priv->device);
+ GError *error = NULL;
+ if (!device_close(priv->device, &error)) {
+ g_warning("device_close: %s", error->message);
+ g_error_free(error);
+ }
priv->device = NULL;
}
}
@@ -263,7 +282,7 @@ resolution_combo_box_changed_cb(GtkComboBox *combo, gpointer user_data)
GuiAppWindowPrivate *priv = gui_app_window_get_instance_private(win);
int sel = gtk_combo_box_get_active(combo);
const struct device_info *info = device_get_info(priv->device);
- assert(sel >= 0 && sel < info->resolutions);
+ g_assert(sel >= 0 && sel < info->resolutions);
priv->width = info->resolution[sel].width;
priv->height = info->resolution[sel].height;
if (priv->started) {
@@ -389,6 +408,7 @@ gui_app_window_init(GuiAppWindow *win)
{
gtk_widget_init_template(GTK_WIDGET(win));
GuiAppWindowPrivate *priv = gui_app_window_get_instance_private(win);
+ priv->win = win;
priv->app = NULL;
priv->allocation.width = -1;
cairo_matrix_init_identity(&priv->transform);
@@ -462,7 +482,7 @@ void
gui_app_window_open(GuiAppWindow *win, struct device *device)
{
GuiAppWindowPrivate *priv = gui_app_window_get_instance_private(win);
- assert(!priv->device);
+ g_assert(!priv->device);
priv->app = GUI_APP(gtk_window_get_application(GTK_WINDOW(win)));
const struct device_info *info = device_get_info(device);
priv->width = info->resolution[0].width;
@@ -485,7 +505,7 @@ gui_app_window_open(GuiAppWindow *win, struct device *device)
const struct device_info_resolution *ir = &info->resolution[i];
char buf[32];
int r = snprintf(buf, sizeof(buf), "%dx%d", ir->width, ir->height);
- assert(r < sizeof(buf));
+ g_assert(r < sizeof(buf));
gtk_combo_box_text_append_text(priv->resolution_combo_box, buf);
}
gtk_combo_box_set_active(GTK_COMBO_BOX(priv->resolution_combo_box), 0);
diff --git a/image.c b/image.c
index ac5387a..ec29751 100644
--- a/image.c
+++ b/image.c
@@ -20,7 +20,6 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
*/
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
@@ -31,6 +30,12 @@
typedef uint8_t u8x16 __attribute__((vector_size(16)));
typedef uint16_t u16x8 __attribute__((vector_size(16)));
+GQuark
+image_error_quark(void)
+{
+ return g_quark_from_static_string("image-error-quark");
+}
+
static void
image_free(struct image *image, void *user_data)
{
@@ -44,9 +49,8 @@ image_new(int width, int height, int stride, enum image_format format)
size_t image_size_aligned = ((height * stride) + 15) / 16 * 16;
uint8_t *m = aligned_alloc(16,
struct_size_aligned + image_size_aligned);
- if (!m) {
- utils_fatal("memory exhausted");
- }
+ if (!m)
+ g_error("aligned_alloc: failed to allocate");
struct image *image = (struct image *) m;
image->width = width;
image->height = height;
@@ -76,9 +80,9 @@ image_unref(struct image *image)
static void
image_histogram_sgrbg8(const struct image *image, uint32_t histogram[3][256])
{
- assert(image->width % 2 == 0);
- assert(image->height % 2 == 0);
- assert(image->stride == image->width);
+ g_assert(image->width % 2 == 0);
+ g_assert(image->height % 2 == 0);
+ g_assert(image->stride == image->width);
memset(histogram, 0, 3 * 256 * sizeof(uint32_t));
const uint8_t *p = image->pixels;
for (int i = 0; i < image->height; i += 2) {
@@ -100,7 +104,7 @@ image_histogram_sgrbg8(const struct image *image, uint32_t histogram[3][256])
static void
image_histogram_xrgb32(const struct image *image, uint32_t histogram[3][256])
{
- assert(image->stride == image->width * 4);
+ g_assert(image->stride == image->width * 4);
memset(histogram, 0, 3 * 256 * sizeof(uint32_t));
const uint8_t *p = image->pixels;
const uint8_t *pend = image->pixels + image->height * image->stride;
@@ -126,7 +130,7 @@ image_histogram(const struct image *image, uint32_t histogram[3][256])
image_histogram_xrgb32(image, histogram);
break;
default:
- assert(0);
+ g_assert_not_reached();
}
}
@@ -178,9 +182,9 @@ static void
image_white_balance_sgrbg8(struct image *image,
const struct image_white_balance_reference *reference)
{
- assert(image->width % 16 == 0);
- assert(image->height % 2 == 0);
- assert(image->stride == image->width);
+ g_assert(image->width % 16 == 0);
+ g_assert(image->height % 2 == 0);
+ g_assert(image->stride == image->width);
uint16_t fr, fg, fb;
image_white_balance_factors(reference, &fr, &fg, &fb);
u8x16 *p = (u8x16 *) image->pixels;
@@ -227,8 +231,8 @@ static void
image_white_balance_xbgr32(struct image *image,
const struct image_white_balance_reference *reference)
{
- assert(image->width % 4 == 0);
- assert(image->stride == image->width * 4);
+ g_assert(image->width % 4 == 0);
+ g_assert(image->stride == image->width * 4);
uint16_t fr, fg, fb;
image_white_balance_factors(reference, &fr, &fg, &fb);
u8x16 *p = (u8x16 *) image->pixels;
@@ -268,7 +272,7 @@ image_white_balance(struct image *image,
image_white_balance_xbgr32(image, reference);
break;
default:
- assert(0);
+ g_assert_not_reached();
}
}
@@ -284,7 +288,7 @@ image_convert_to_xbgr32_from_sgrbg8(struct image *dst,
* G R G R G R
* B G B G B G
*/
- assert(dst->width == src->width
+ g_assert(dst->width == src->width
&& dst->height == src->height);
const int in_stride = src->stride;
const int out_stride = dst->stride;
@@ -361,10 +365,10 @@ image_convert_to_xbgr32_from_sgrbg8(struct image *dst,
void
image_convert(struct image *dst, const struct image *src)
{
- assert(dst->width == src->width
+ g_assert(dst->width == src->width
&& dst->height == src->height);
if (dst->format == src->format) {
- assert(dst->stride == src->stride);
+ g_assert(dst->stride == src->stride);
memcpy(dst->pixels, src->pixels, src->stride * src->height);
} else {
switch (src->format) {
@@ -374,18 +378,19 @@ image_convert(struct image *dst, const struct image *src)
image_convert_to_xbgr32_from_sgrbg8(dst, src);
break;
default:
- assert(0);
+ g_assert_not_reached();
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
}
}
-void
-image_save(const struct image *image, const char *name)
+bool
+image_save(const struct image *image, const char *name, GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
png_image pimage;
memset(&pimage, 0, sizeof(pimage));
pimage.version = PNG_IMAGE_VERSION;
@@ -399,10 +404,14 @@ image_save(const struct image *image, const char *name)
pimage.format = PNG_FORMAT_BGRA;
break;
default:
- assert(0);
+ g_assert_not_reached();
}
int r = png_image_write_to_file(&pimage, name, 0,
image->pixels, image->stride, NULL);
- if (r == 0)
- utils_fatal("can not write image: %s", pimage.message);
+ if (r == 0) {
+ g_set_error(error, IMAGE_ERROR, IMAGE_ERROR_SAVE,
+ "can not write image: %s", pimage.message);
+ return false;
+ }
+ return true;
}
diff --git a/image.h b/image.h
index d854fcc..0e36c02 100644
--- a/image.h
+++ b/image.h
@@ -23,6 +23,11 @@
* Email: <nico at ni.fr.eu.org>
*/
#include <stdint.h>
+#include <stdbool.h>
+#include <glib.h>
+
+/* Error domain for image related errors. */
+#define IMAGE_ERROR image_error_quark()
/* Image format. */
enum image_format
@@ -69,6 +74,16 @@ struct image_white_balance_reference {
uint8_t r, g, b;
};
+/* Error codes. */
+enum ImageError {
+ /* Error writing an image to disk. */
+ IMAGE_ERROR_SAVE,
+};
+
+/* Return quark of error domain for image related errors. */
+GQuark
+image_error_quark(void);
+
/* Allocate an image using system allocator. */
struct image *
image_new(int width, int height, int stride, enum image_format format);
@@ -100,7 +115,7 @@ void
image_convert(struct image *dst, const struct image *src);
/* Save image to PNG. */
-void
-image_save(const struct image *image, const char *name);
+bool
+image_save(const struct image *image, const char *name, GError **error);
#endif /* image_h */
diff --git a/moticam.c b/moticam.c
index 4e83225..232fb77 100644
--- a/moticam.c
+++ b/moticam.c
@@ -22,13 +22,7 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
*/
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include "device.h"
-#include "image.h"
+#include "moticam.h"
#include "utils.h"
#define ID_VENDOR 0x232f
@@ -56,8 +50,13 @@ struct moticam_device {
static char *
moticam_usb_poll(struct libusb_device_descriptor *desc);
+
static struct device *
-moticam_usb_open(libusb_context *usb, libusb_device *usb_device);
+moticam_usb_open(libusb_context *usb, libusb_device *usb_device,
+ GError **error);
+
+static bool
+moticam_stop(struct device *device, GError **error);
const struct device_driver moticam_device_driver = {
"Moticam USB cameras",
@@ -84,46 +83,58 @@ static const struct device_info moticam_device_info = {
.resolution = moticam_device_info_resolution,
};
-static void
-moticam_control_vendor(struct device *device, uint16_t wValue,
- const uint8_t *data, int data_cnt)
+static bool
+moticam_control_vendor(struct moticam_device *mdev, uint16_t wValue,
+ const uint8_t *data, int data_cnt, GError **error)
{
- struct moticam_device *mdev = (struct moticam_device *) device;
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
int r = libusb_control_transfer(mdev->handle, LIBUSB_REQUEST_TYPE_VENDOR,
240, wValue, 0, (uint8_t *) data, data_cnt, 0);
- if (r < 0)
- utils_fatal("can not send vendor control: %s", libusb_strerror(r));
+ if (r < 0) {
+ g_set_error(error, DEVICE_ERROR, DEVICE_ERROR_USB,
+ "can not send vendor control: %s", libusb_strerror(r));
+ return false;
+ }
+ return true;
}
-static void
-moticam_control_vendor_w(struct device *device, uint16_t wValue,
- const uint16_t data0)
+static bool
+moticam_control_vendor_w(struct moticam_device *mdev, uint16_t wValue,
+ const uint16_t data0, GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
uint8_t data[] = { (uint8_t) (data0 >> 8), (uint8_t) data0 };
- moticam_control_vendor(device, wValue, data, sizeof(data));
+ return moticam_control_vendor(mdev, wValue, data, sizeof(data), error);
}
-static void
-moticam_control_reset(struct device *device)
+static bool
+moticam_control_reset(struct moticam_device *mdev, GError **error)
{
- moticam_control_vendor_w(device, 0xba00, 0x0000);
- moticam_control_vendor_w(device, 0xba00, 0x0001);
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
+ if (!moticam_control_vendor_w(mdev, 0xba00, 0x0000, error))
+ return false;
+ if (!moticam_control_vendor_w(mdev, 0xba00, 0x0001, error))
+ return false;
+ return true;
}
-static void
-moticam_control_exposure(struct device *device, double exposure_ms)
+static bool
+moticam_control_exposure(struct moticam_device *mdev, double exposure_ms,
+ GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
int exposure_w = exposure_ms * 12.82;
if (exposure_w < 0x000c)
exposure_w = 0x000c;
else if (exposure_w > 0xffff)
exposure_w = 0xffff;
- moticam_control_vendor_w(device, 0xba09, exposure_w);
+ return moticam_control_vendor_w(mdev, 0xba09, exposure_w, error);
}
-static void
-moticam_control_gain(struct device *device, double gain)
+static bool
+moticam_control_gain(struct moticam_device *mdev, double gain, GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
double gmin, gmax;
int xmin, xmax;
bool up = false;
@@ -151,42 +162,52 @@ moticam_control_gain(struct device *device, double gain)
x = xmax;
if (up)
x = (x << 8) | 0x60;
- moticam_control_vendor_w(device, 0xba2d, x);
- moticam_control_vendor_w(device, 0xba2b, x);
- moticam_control_vendor_w(device, 0xba2e, x);
- moticam_control_vendor_w(device, 0xba2c, x);
+ if (!moticam_control_vendor_w(mdev, 0xba2d, x, error))
+ return false;
+ if (!moticam_control_vendor_w(mdev, 0xba2b, x, error))
+ return false;
+ if (!moticam_control_vendor_w(mdev, 0xba2e, x, error))
+ return false;
+ if (!moticam_control_vendor_w(mdev, 0xba2c, x, error))
+ return false;
+ return true;
}
-static void
-moticam_control_resolution(struct device *device, int width, int height)
+static bool
+moticam_control_resolution(struct moticam_device *mdev, int width, int height,
+ GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
static const uint8_t control_init[] = {
0x00, 0x14, 0x00, 0x20, 0x05, 0xff, 0x07, 0xff };
- moticam_control_vendor(device, 0xba01, control_init,
- sizeof(control_init));
+ if (!moticam_control_vendor(mdev, 0xba01, control_init,
+ sizeof(control_init), error))
+ return false;
static const uint8_t control_512x384[] = { 0x00, 0x03, 0x00, 0x03 };
static const uint8_t control_1024x768[] = { 0x00, 0x11, 0x00, 0x11 };
static const uint8_t control_2048x1536[] = { 0x00, 0x00, 0x00, 0x00 };
+ bool ret;
switch (width)
{
case 512:
- assert(height == 384);
- moticam_control_vendor(device, 0xba22, control_512x384,
- sizeof(control_512x384));
+ g_assert(height == 384);
+ ret = moticam_control_vendor(mdev, 0xba22, control_512x384,
+ sizeof(control_512x384), error);
break;
case 1024:
- assert(height == 768);
- moticam_control_vendor(device, 0xba22, control_1024x768,
- sizeof(control_1024x768));
+ g_assert(height == 768);
+ ret = moticam_control_vendor(mdev, 0xba22, control_1024x768,
+ sizeof(control_1024x768), error);
break;
case 2048:
- assert(height == 1536);
- moticam_control_vendor(device, 0xba22, control_2048x1536,
- sizeof(control_2048x1536));
+ g_assert(height == 1536);
+ ret = moticam_control_vendor(mdev, 0xba22, control_2048x1536,
+ sizeof(control_2048x1536), error);
break;
default:
- assert(0);
+ g_assert_not_reached();
}
+ return ret;
}
static const struct device_info *
@@ -216,7 +237,7 @@ moticam_set_resolution(struct device *device, int width, int height,
int stride)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(!mdev->current_image);
+ g_assert(!mdev->current_image);
/* Ignore stride, store parameters. */
mdev->width = width;
mdev->height = height;
@@ -229,16 +250,21 @@ moticam_transfer_cb(struct libusb_transfer *transfer)
mdev->usb_transfer_done = true;
}
-static void
-moticam_start(struct device *device)
+static bool
+moticam_start(struct device *device, GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(!mdev->current_image);
+ g_assert(!mdev->current_image);
/* Configure camera. */
- moticam_control_exposure(&mdev->device, 30.0);
- moticam_control_gain(&mdev->device, mdev->gain);
- moticam_control_resolution(&mdev->device, mdev->width, mdev->height);
- moticam_control_exposure(&mdev->device, mdev->exposure_ms);
+ if (!moticam_control_exposure(mdev, 30.0, error))
+ return false;
+ if (!moticam_control_gain(mdev, mdev->gain, error))
+ return false;
+ if (!moticam_control_resolution(mdev, mdev->width, mdev->height, error))
+ return false;
+ if (!moticam_control_exposure(mdev, mdev->exposure_ms, error))
+ return false;
mdev->exposure_pending = false;
mdev->gain_pending = false;
utils_delay_us(100000);
@@ -257,8 +283,8 @@ moticam_start(struct device *device)
image->pixels = libusb_dev_mem_alloc(mdev->handle,
mdev->rawbuffer_size);
if (!image->pixels)
- utils_fatal("can not allocate raw buffer");
- assert(image->refs == 0);
+ g_error("can not allocate raw buffer");
+ g_assert(image->refs == 0);
image->release = NULL;
image->release_user_data = NULL;
}
@@ -272,19 +298,27 @@ moticam_start(struct device *device)
mdev->current_image->pixels, mdev->rawbuffer_size,
moticam_transfer_cb, mdev, 30000);
int r = libusb_submit_transfer(mdev->usb_transfer);
- if (r)
- utils_fatal("can not submit transfer: %s", libusb_strerror(r));
+ if (r) {
+ g_set_error(error, DEVICE_ERROR, DEVICE_ERROR_USB,
+ "can not submit transfer: %s", libusb_strerror(r));
+ /* Cancel start. */
+ moticam_stop(device, NULL);
+ return false;
+ }
+ return true;
}
static struct image *
-moticam_read(struct device *device)
+moticam_read(struct device *device, GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, NULL);
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(mdev->current_image);
+ g_assert(mdev->current_image);
if (mdev->usb_transfer_done) {
if (mdev->usb_transfer->status == LIBUSB_TRANSFER_COMPLETED) {
const int image_size = mdev->width * mdev->height;
struct image *image = NULL;
+ bool error_set = false;
/* Image received? Return it, else drop. */
if (!mdev->drop
&& mdev->usb_transfer->actual_length == image_size) {
@@ -296,42 +330,67 @@ moticam_read(struct device *device)
if (mdev->drop)
mdev->drop--;
/* Pending updates? */
- if (mdev->exposure_pending) {
- moticam_control_exposure(device, mdev->exposure_ms);
- mdev->exposure_pending = false;
+ if (image && !error_set && mdev->exposure_pending) {
+ if (!moticam_control_exposure(mdev, mdev->exposure_ms, error))
+ error_set = true;
+ else
+ mdev->exposure_pending = false;
}
- if (mdev->gain_pending) {
- moticam_control_gain(device, mdev->gain);
- mdev->gain_pending = false;
+ if (image && !error_set && mdev->gain_pending) {
+ if (!moticam_control_gain(mdev, mdev->gain, error))
+ error_set = true;
+ else
+ mdev->gain_pending = false;
}
/* Start a new transfer. */
- assert(mdev->current_image->refs == 0);
- mdev->usb_transfer_done = false;
- mdev->usb_transfer->buffer = mdev->current_image->pixels;
- int r = libusb_submit_transfer(mdev->usb_transfer);
- if (r)
- utils_fatal("can not submit transfer: %s",
- libusb_strerror(r));
+ if (!error_set) {
+ g_assert(mdev->current_image->refs == 0);
+ mdev->usb_transfer_done = false;
+ mdev->usb_transfer->buffer = mdev->current_image->pixels;
+ int r = libusb_submit_transfer(mdev->usb_transfer);
+ if (r) {
+ g_set_error(error, DEVICE_ERROR, DEVICE_ERROR_USB,
+ "can not submit transfer: %s", libusb_strerror(r));
+ error_set = true;
+ }
+ }
/* Return image or NULL. */
+ if (error_set && image) {
+ image_unref(image);
+ image = NULL;
+ }
return image;
} else {
- utils_fatal("transfer stopped: TODO");
+ g_set_error(error, DEVICE_ERROR, DEVICE_ERROR_USB,
+ "transfer stopped");
return NULL;
}
} else
return NULL;
}
-static void
-moticam_stop(struct device *device)
+static bool
+moticam_stop(struct device *device, GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(mdev->current_image);
+ g_assert(mdev->current_image);
+ bool ret = true;
/* Cancel transfer if running. */
if (!mdev->usb_transfer_done) {
- libusb_cancel_transfer(mdev->usb_transfer);
- while (!mdev->usb_transfer_done)
- libusb_handle_events(mdev->usb);
+ int r = libusb_cancel_transfer(mdev->usb_transfer);
+ if (r && r != LIBUSB_ERROR_NOT_FOUND) {
+ g_set_error(error, DEVICE_ERROR, DEVICE_ERROR_USB,
+ "can not cancel transfer: %s", libusb_strerror(r));
+ ret = false;
+ }
+ while (!mdev->usb_transfer_done) {
+ int r = libusb_handle_events(mdev->usb);
+ if (r) {
+ g_error("unable to handle libusb events: %s",
+ libusb_strerror(r));
+ }
+ }
}
/* Release. */
libusb_free_transfer(mdev->usb_transfer);
@@ -344,19 +403,22 @@ moticam_stop(struct device *device)
mdev->current_image = NULL;
mdev->other_image = NULL;
/* Stop camera. */
- moticam_control_exposure(device, 0.0);
- moticam_control_exposure(device, 0.0);
- moticam_control_exposure(device, 0.0);
+ for (int i = 0; ret && i < 3; i++)
+ ret = moticam_control_exposure(mdev, 0.0, error);
+ return ret;
}
-static void
-moticam_close(struct device *device)
+static bool
+moticam_close(struct device *device, GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, false);
struct moticam_device *mdev = (struct moticam_device *) device;
+ bool ret = true;
if (mdev->current_image)
- moticam_stop(device);
+ ret = moticam_stop(device, error);
libusb_close(mdev->handle);
free(device);
+ return ret;
}
static char *
@@ -365,17 +427,18 @@ moticam_usb_poll(struct libusb_device_descriptor *desc)
if (desc->idVendor == ID_VENDOR
&& (desc->idProduct == ID_PRODUCT
|| desc->idProduct == ID_PRODUCT_USB3))
- return strdup("Moticam 3+");
+ return g_strdup("Moticam 3+");
else
return NULL;
}
static struct device *
-moticam_usb_open(libusb_context *usb, libusb_device *usb_device)
+moticam_usb_open(libusb_context *usb, libusb_device *usb_device,
+ GError **error)
{
+ g_return_val_if_fail(error == NULL || *error == NULL, NULL);
/* Create context. */
- struct moticam_device *mdev =
- utils_malloc(sizeof(struct moticam_device));
+ struct moticam_device *mdev = g_new(struct moticam_device, 1);
mdev->device.get_info = &moticam_get_info;
mdev->device.set_exposure = &moticam_set_exposure;
mdev->device.set_gain = &moticam_set_gain;
@@ -403,10 +466,17 @@ moticam_usb_open(libusb_context *usb, libusb_device *usb_device)
mdev->drop = 1;
/* Open USB device. */
int r = libusb_open(usb_device, &mdev->handle);
- if (r)
- utils_fatal("can not open device: %s", libusb_strerror(r));
+ if (r) {
+ g_set_error(error, DEVICE_ERROR, DEVICE_ERROR_USB,
+ "can not open device: %s", libusb_strerror(r));
+ g_free(mdev);
+ return NULL;
+ }
/* Reset camera. */
- moticam_control_reset(&mdev->device);
+ if (!moticam_control_reset(mdev, error)) {
+ g_free(mdev);
+ return NULL;
+ }
/* Done. */
return &mdev->device;
}
diff --git a/options.c b/options.c
index c8baaa2..cbdaf3c 100644
--- a/options.c
+++ b/options.c
@@ -23,8 +23,8 @@
#define _GNU_SOURCE
#include <printf.h>
-#include "cli.h"
#include "options.h"
+#include "cli.h"
#include "utils.h"
void
diff --git a/usb_source.c b/usb_source.c
index 1a8174d..761d103 100644
--- a/usb_source.c
+++ b/usb_source.c
@@ -20,11 +20,9 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
*/
-#include <assert.h>
#include <stdbool.h>
#include "usb_source.h"
-#include "utils.h"
struct fd_tag {
int fd;
@@ -102,7 +100,7 @@ usb_source_pollfd_removed(int fd, void *user_data)
found = true;
}
}
- assert(found);
+ g_assert(found);
}
}
@@ -112,7 +110,7 @@ usb_source_new(libusb_context *usb)
/* Get file descriptors. */
const struct libusb_pollfd **pollfds = libusb_get_pollfds(usb);
if (!pollfds)
- utils_fatal("unable to get libusb file descriptors");
+ g_error("unable to get libusb file descriptors");
/* Create source. */
static GSourceFuncs funcs = {
.prepare = usb_source_prepare,
@@ -125,7 +123,7 @@ usb_source_new(libusb_context *usb)
g_source_set_name(source, "usb");
usb_source->usb = usb;
usb_source->fd_tag = g_array_new(FALSE, FALSE, sizeof(struct fd_tag));
- assert(libusb_pollfds_handle_timeouts(usb));
+ g_assert(libusb_pollfds_handle_timeouts(usb));
/* Add existing file descriptors. */
for (const struct libusb_pollfd **pollfd = pollfds; *pollfd; pollfd++)
usb_source_pollfd_added((*pollfd)->fd, (*pollfd)->events, usb_source);
diff --git a/utils.c b/utils.c
index 489c794..ddd044f 100644
--- a/utils.c
+++ b/utils.c
@@ -24,7 +24,6 @@
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
-#include <stdlib.h>
#include <time.h>
#include "utils.h"
@@ -52,15 +51,6 @@ utils_info(const char *fmt, ...)
fputc('\n', stderr);
}
-void *
-utils_malloc(size_t size)
-{
- void *m = malloc(size);
- if (!m)
- utils_fatal("memory exhausted");
- return m;
-}
-
void
utils_delay_us(int us)
{
@@ -71,7 +61,7 @@ utils_delay_us(int us)
if (errno == EINTR)
delay = remaining;
else
- utils_fatal("can not sleep: %m");
+ g_error("can not sleep: %m");
}
}
@@ -79,14 +69,12 @@ void
utils_dialog_error(GtkWindow *parent, const char *fmt, ...)
{
va_list ap;
- char *msg;
va_start(ap, fmt);
- if (vasprintf(&msg, fmt, ap) == -1)
- utils_fatal("memory exhausted");
+ char *msg = g_strdup_vprintf(fmt, ap);
va_end(ap);
GtkWidget *dialog = gtk_message_dialog_new(parent, GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", msg);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
- free(msg);
+ g_free(msg);
}
diff --git a/utils.h b/utils.h
index af43dc9..ef495e5 100644
--- a/utils.h
+++ b/utils.h
@@ -34,10 +34,6 @@ void
utils_info(const char *fmt, ...)
__attribute__((format(printf, 1, 2)));
-/* Allocate memory, exit on error. */
-void *
-utils_malloc(size_t size);
-
/* Sleep for a given number of microseconds. */
void
utils_delay_us(int us);