summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Vogelgesang <matthias.vogelgesang@kit.edu>2016-02-16 11:53:28 +0100
committerMatthias Vogelgesang <matthias.vogelgesang@kit.edu>2016-02-16 11:53:28 +0100
commitf431158ae2412ed23bd4d2336af00d2b5c170d31 (patch)
tree52c5fbbc7ba26f7444edbe7d0e8d80ed67796384
downloaduca-net-f431158ae2412ed23bd4d2336af00d2b5c170d31.tar.gz
uca-net-f431158ae2412ed23bd4d2336af00d2b5c170d31.tar.bz2
uca-net-f431158ae2412ed23bd4d2336af00d2b5c170d31.tar.xz
uca-net-f431158ae2412ed23bd4d2336af00d2b5c170d31.zip
Initial commit
-rw-r--r--CMakeLists.txt34
-rw-r--r--README.md22
-rw-r--r--cmake/ConfigurePaths.cmake90
-rw-r--r--cmake/PkgConfigVars.cmake30
-rw-r--r--uca-net-camera.c296
-rw-r--r--uca-net-camera.h76
-rw-r--r--uca-net-client.c207
-rw-r--r--uca-net-protocol.h85
-rw-r--r--uca-net-server.c174
-rw-r--r--ucad.c223
10 files changed, 1237 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..bfd96d6
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,34 @@
+cmake_minimum_required(VERSION 2.6)
+project(ucanet C)
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
+
+include(FindPackageHandleStandardArgs)
+include(PkgConfigVars)
+include(ConfigurePaths)
+
+find_package(PkgConfig)
+
+add_definitions("-std=c99 -Wall")
+add_definitions(-DG_LOG_DOMAIN="Uca-Net")
+
+pkg_check_modules(GIO gio-2.0>=2.22 REQUIRED)
+pkg_check_modules(UCA libuca>=2.1.0 REQUIRED)
+pkg_check_variable(libuca plugindir)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${UCA_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${MSGPACK_INCLUDE_DIRS})
+link_directories(${UCA_LIBRARY_DIRS} ${GIO_LIBRARY_DIRS})
+
+# uca-net client camera
+add_library(ucanet SHARED uca-net-camera.c uca-net-client.c)
+target_link_libraries(ucanet ${UCA_LIBRARIES} ${MSGPACK_LIBRARIES})
+
+install(TARGETS ucanet LIBRARY DESTINATION ${LIBUCA_PLUGINDIR})
+
+# uca-net server
+configure_paths(UCAD)
+add_executable(ucad ucad.c uca-net-server.c)
+
+target_link_libraries(ucad ${UCA_LIBRARIES} ${GIO_LIBRARIES})
+
+install(TARGETS ucad RUNTIME DESTINATION ${UCAD_BINDIR} COMPONENT executables)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2352ac1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,22 @@
+## uca-net
+
+A transparent TCP-based network bridge camera for remote access of libuca
+cameras.
+
+### Installation and usage
+
+The only dependency is libuca itself and any camera you wish to access. Build
+the server and client with
+
+ $ mkdir build && cd build
+ $ cmake ..
+ $ make && (sudo) make install
+
+Now, you can start a server on a remote machine with
+
+ $ ucad mock
+
+and connect to it from any other machine, e.g.
+
+ $ uca-grab -n 10 net # grab ten frames
+ $ uca-camera-control -c net # control graphically
diff --git a/cmake/ConfigurePaths.cmake b/cmake/ConfigurePaths.cmake
new file mode 100644
index 0000000..df9c03c
--- /dev/null
+++ b/cmake/ConfigurePaths.cmake
@@ -0,0 +1,90 @@
+# - pre-configured paths for CMake
+#
+# Usage:
+# configure_paths(<PREFIX>)
+#
+# Checks if configure-like prefix and installation paths were passed by the user
+# and sets up corresponding variables for use in install() commands and to fill
+# out .pc files:
+#
+# PREFIX_PREFIX defaults to ... CMAKE_INSTALL_PREFIX
+# PREFIX_EPREFIX PREFIX_PREFIX
+# PREFIX_SBINDIR PREFIX_EPREFIX/sbin
+# PREFIX_SYSCONFDIR PREFIX_PREFIX/etc
+# PREFIX_LOCALSTATEDIR PREFIX_PREFIX/var
+# PREFIX_BINDIR PREFIX_EPREFIX/bin
+# PREFIX_LIBDIR PREFIX_EPREFIX/lib
+# PREFIX_INCLUDEDIR PREFIX_PREFIX/include
+# PREFIX_PKGCONFIGDIR PREFIX_LIBDIR/pkgconfig
+# PREFIX_TYPELIBDIR PREFIX_LIBDIR/girepository-1.0
+# PREFIX_DATAROOTDIR PREFIX_PREFIX/share
+# PREFIX_DATADIR PREFIX_DATAROOTDIR
+# PREFIX_INFODIR PREFIX_DATAROOTDIR/info
+# PREFIX_MANDIR PREFIX_DATAROOTDIR/man
+# PREFIX_LOCALEDIR PREFIX_DATAROOTDIR/locale
+# PREFIX_GIRDIR PREFIX_DATAROOTDIR/gir-1.0
+
+# Copyright (C) 2013 Matthias Vogelgesang <matthias.vogelgesang@gmail.com>
+#
+# Redistribution and use, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# 1. Redistributions must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+# 2. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+if(__configure_paths)
+ return()
+endif()
+
+set(__configure_paths YES)
+
+macro(_set_var _prefix _var _user _override _description)
+ set(_name "${_prefix}_${_var}")
+
+ set("${_name}" "${_user}")
+
+ if("${_name}" STREQUAL "")
+ set("${_name}" "${_override}")
+ endif()
+
+ set(${_name} "${${_name}}" CACHE PATH "${_description}")
+ mark_as_advanced(${_name})
+endmacro()
+
+function(configure_paths _prefix)
+ _set_var("${_prefix}" "PREFIX" "${PREFIX}" "${CMAKE_INSTALL_PREFIX}" "install architecture-independent files in PREFIX")
+ _set_var("${_prefix}" "EPREFIX" "${EXEC_PREFIX}" "${${_prefix}_PREFIX}" "install architecture-dependent files in EPREFIX")
+
+ _set_var("${_prefix}" "SBINDIR" "${SBINDIR}" "${${_prefix}_EPREFIX}/sbin" "system admin executabls")
+ _set_var("${_prefix}" "SYSCONFDIR" "${SYSCONFDIR}" "${${_prefix}_PREFIX}/etc" "read-only single-machine data")
+ _set_var("${_prefix}" "LOCALSTATEDIR" "${LOCALSTATEDIR}" "${${_prefix}_PREFIX}/var" "modifiable single-machine data")
+ _set_var("${_prefix}" "BINDIR" "${BINDIR}" "${${_prefix}_EPREFIX}/bin" "user executables")
+ _set_var("${_prefix}" "LIBDIR" "${LIBDIR}" "${${_prefix}_EPREFIX}/lib" "object code libraries")
+ _set_var("${_prefix}" "INCLUDEDIR" "${INCLUDEDIR}" "${${_prefix}_PREFIX}/include" "C header files")
+ _set_var("${_prefix}" "PKGCONFIGDIR" "${PKGCONFIGDIR}" "${${_prefix}_LIBDIR}/pkgconfig" "pkg-config files")
+ _set_var("${_prefix}" "TYPELIBDIR" "${TYPELIBDIR}" "${${_prefix}_LIBDIR}/girepository-1.0" "GObject run-time introspection data")
+ _set_var("${_prefix}" "DATAROOTDIR" "${DATAROOTDIR}" "${${_prefix}_PREFIX}/share" "read-only arch.-independent data root")
+ _set_var("${_prefix}" "DATADIR" "${DATADIR}" "${${_prefix}_DATAROOTDIR}" "read-only architecture-independent data")
+ _set_var("${_prefix}" "INFODIR" "${INFODIR}" "${${_prefix}_DATAROOTDIR}/info" "info documentation")
+ _set_var("${_prefix}" "MANDIR" "${MANDIR}" "${${_prefix}_DATAROOTDIR}/man" "man documentation")
+ _set_var("${_prefix}" "LOCALEDIR" "${LOCALEDIR}" "${${_prefix}_DATAROOTDIR}/locale" "locale-dependent data")
+ _set_var("${_prefix}" "GIRDIR" "${GIRDIR}" "${${_prefix}_DATAROOTDIR}/gir-1.0" "GObject introspection data")
+endfunction()
+
+# vim: tw=0:
diff --git a/cmake/PkgConfigVars.cmake b/cmake/PkgConfigVars.cmake
new file mode 100644
index 0000000..f295457
--- /dev/null
+++ b/cmake/PkgConfigVars.cmake
@@ -0,0 +1,30 @@
+# - determine variables defined in pkg-config files
+#
+# Usage:
+# pkg_check_variable(<PKG_NAME> <VARIABLE_NAME>)
+#
+# Checks for a variable in the given package and translates to a call such as
+# `pkg-config --variable=<VARIABLE_NAME> <PKG_NAME>`. The output is a cached
+# variable named
+#
+# <PKG_NAME>_<VARIABLE_NAME>
+#
+# Note that both names are uppercased and any dashes replaced by underscores.
+#
+
+find_package(PkgConfig REQUIRED)
+
+function(pkg_check_variable _pkg _name)
+ string(TOUPPER ${_pkg} _pkg_upper)
+ string(TOUPPER ${_name} _name_upper)
+ string(REPLACE "-" "_" _pkg_upper ${_pkg_upper})
+ string(REPLACE "-" "_" _name_upper ${_name_upper})
+ set(_output_name "${_pkg_upper}_${_name_upper}")
+
+ execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=${_name} ${_pkg}
+ OUTPUT_VARIABLE _pkg_result
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ set("${_output_name}" "${_pkg_result}" CACHE STRING "pkg-config variable
+ ${_name} of ${_pkg}")
+endfunction()
diff --git a/uca-net-camera.c b/uca-net-camera.c
new file mode 100644
index 0000000..6088ef3
--- /dev/null
+++ b/uca-net-camera.c
@@ -0,0 +1,296 @@
+/* Copyright (C) 2011-2013 Matthias Vogelgesang <matthias.vogelgesang@kit.edu>
+ (Karlsruhe Institute of Technology)
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+
+ This library 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 Lesser General Public License for more
+ details.
+
+ You should have received a copy of the GNU Lesser General Public License along
+ with this library; if not, write to the Free Software Foundation, Inc., 51
+ Franklin St, Fifth Floor, Boston, MA 02110, USA */
+
+#include <stdlib.h>
+#include <gio/gio.h>
+#include <gmodule.h>
+#include <string.h>
+
+#include <uca/uca-camera.h>
+#include "uca-net-camera.h"
+#include "uca-net-protocol.h"
+
+
+#define UCA_NET_CAMERA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_NET_CAMERA, UcaNetCameraPrivate))
+
+static void uca_net_camera_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (UcaNetCamera, uca_net_camera, UCA_TYPE_CAMERA,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ uca_net_camera_initable_iface_init))
+
+GQuark uca_net_camera_error_quark ()
+{
+ return g_quark_from_static_string("uca-net-camera-error-quark");
+}
+
+enum {
+ PROP_HOST = N_BASE_PROPERTIES,
+ N_PROPERTIES
+};
+
+static GParamSpec *net_properties[N_PROPERTIES] = { NULL, };
+
+struct _UcaNetCameraPrivate {
+ GError *construct_error;
+ gchar *host;
+ GSocketConnection *connection;
+ GSocketClient *client;
+ gsize size;
+};
+
+static void
+uca_net_camera_start_recording (UcaCamera *camera,
+ GError **error)
+{
+ UcaNetCameraPrivate *priv;
+ guint width;
+ guint height;
+ guint bits;
+
+ g_return_if_fail (UCA_IS_NET_CAMERA (camera));
+
+ g_object_get (G_OBJECT (camera),
+ "roi-width", &width,
+ "roi-height", &height,
+ "sensor-bitdepth", &bits,
+ NULL);
+
+ priv = UCA_NET_CAMERA_GET_PRIVATE (camera);
+ priv->size = width * height * (bits > 8 ? 2 : 1);
+
+ uca_net_client_start_recording (priv->connection, error);
+}
+
+static void
+uca_net_camera_stop_recording (UcaCamera *camera,
+ GError **error)
+{
+ g_return_if_fail (UCA_IS_NET_CAMERA (camera));
+ uca_net_client_stop_recording (UCA_NET_CAMERA_GET_PRIVATE (camera)->connection, error);
+}
+
+static void
+uca_net_camera_start_readout (UcaCamera *camera,
+ GError **error)
+{
+ g_return_if_fail (UCA_IS_NET_CAMERA (camera));
+}
+
+static void
+uca_net_camera_stop_readout (UcaCamera *camera,
+ GError **error)
+{
+ g_return_if_fail (UCA_IS_NET_CAMERA (camera));
+}
+
+static void
+uca_net_camera_write (UcaCamera *camera,
+ const gchar *name,
+ gpointer data,
+ gsize size,
+ GError **error)
+{
+ g_return_if_fail (UCA_IS_NET_CAMERA (camera));
+}
+
+static gboolean
+uca_net_camera_grab (UcaCamera *camera,
+ gpointer data,
+ GError **error)
+{
+ UcaNetCameraPrivate *priv;
+
+ g_return_val_if_fail (UCA_IS_NET_CAMERA (camera), FALSE);
+ priv = UCA_NET_CAMERA_GET_PRIVATE (camera);
+ return uca_net_client_grab (priv->connection, data, priv->size, error);
+}
+
+static void
+uca_net_camera_trigger (UcaCamera *camera,
+ GError **error)
+{
+ g_return_if_fail (UCA_IS_NET_CAMERA (camera));
+}
+
+static void
+uca_net_camera_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ UcaNetCameraPrivate *priv;
+ const gchar *name;
+ GError *error = NULL;
+
+ priv = UCA_NET_CAMERA_GET_PRIVATE (object);
+
+ if (property_id == PROP_HOST) {
+ g_free (priv->host);
+ priv->host = g_value_dup_string (value);
+ return;
+ }
+
+ name = g_param_spec_get_name (pspec);
+
+ if (!uca_net_client_set_property (priv->connection, name, value, &error))
+ g_warning ("Could not set property: %s", error->message);
+}
+
+static void
+uca_net_camera_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ UcaNetCameraPrivate *priv;
+ const gchar *name;
+ GError *error = NULL;
+
+ priv = UCA_NET_CAMERA_GET_PRIVATE (object);
+
+ if (property_id == PROP_HOST) {
+ g_value_set_string (value, priv->host);
+ return;
+ }
+
+ name = g_param_spec_get_name (pspec);
+
+ if (!uca_net_client_get_property (priv->connection, name, value, &error))
+ g_warning ("Could not get property: %s", error->message);
+}
+
+static void
+uca_net_camera_dispose (GObject *object)
+{
+ UcaNetCameraPrivate *priv;
+
+ priv = UCA_NET_CAMERA_GET_PRIVATE (object);
+
+ if (priv->connection != NULL) {
+ uca_net_client_close (priv->connection, NULL);
+ g_object_unref (priv->connection);
+ }
+
+ g_object_unref (priv->client);
+ G_OBJECT_CLASS (uca_net_camera_parent_class)->dispose (object);
+}
+
+static void
+uca_net_camera_finalize (GObject *object)
+{
+ UcaNetCameraPrivate *priv;
+
+ priv = UCA_NET_CAMERA_GET_PRIVATE (object);
+ g_clear_error (&priv->construct_error);
+
+ g_free (priv->host);
+
+ G_OBJECT_CLASS (uca_net_camera_parent_class)->finalize (object);
+}
+
+static gboolean
+ufo_net_camera_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ UcaNetCamera *camera;
+ UcaNetCameraPrivate *priv;
+
+ g_return_val_if_fail (UCA_IS_NET_CAMERA (initable), FALSE);
+
+ if (cancellable != NULL) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Cancellable initialization not supported");
+ return FALSE;
+ }
+
+ camera = UCA_NET_CAMERA (initable);
+ priv = camera->priv;
+
+ if (priv->construct_error != NULL) {
+ if (error)
+ *error = g_error_copy (priv->construct_error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+uca_net_camera_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = ufo_net_camera_initable_init;
+}
+
+static void
+uca_net_camera_class_init (UcaNetCameraClass *klass)
+{
+ GObjectClass *oclass = G_OBJECT_CLASS (klass);
+ UcaCameraClass *camera_class = UCA_CAMERA_CLASS (klass);
+
+ oclass->set_property = uca_net_camera_set_property;
+ oclass->get_property = uca_net_camera_get_property;
+ oclass->dispose = uca_net_camera_dispose;
+ oclass->finalize = uca_net_camera_finalize;
+
+ camera_class->start_recording = uca_net_camera_start_recording;
+ camera_class->stop_recording = uca_net_camera_stop_recording;
+ camera_class->start_readout = uca_net_camera_start_readout;
+ camera_class->stop_readout = uca_net_camera_stop_readout;
+ camera_class->write = uca_net_camera_write;
+ camera_class->grab = uca_net_camera_grab;
+ camera_class->trigger = uca_net_camera_trigger;
+
+ net_properties[PROP_HOST] =
+ g_param_spec_string ("host",
+ "Host name and optional port",
+ "Host name and optional port",
+ "localhost",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+ for (guint i = PROP_0 + 1; i < N_BASE_PROPERTIES; i++)
+ g_object_class_override_property (oclass, i, uca_camera_props[i]);
+
+ for (guint id = N_BASE_PROPERTIES; id < N_PROPERTIES; id++)
+ g_object_class_install_property (oclass, id, net_properties[id]);
+
+ g_type_class_add_private (klass, sizeof(UcaNetCameraPrivate));
+}
+
+static void
+uca_net_camera_init (UcaNetCamera *self)
+{
+ UcaNetCameraPrivate *priv;
+
+ self->priv = priv = UCA_NET_CAMERA_GET_PRIVATE (self);
+
+ priv->construct_error = NULL;
+ priv->client = g_socket_client_new ();
+
+ if (priv->host == NULL)
+ priv->host = g_strdup ("localhost");
+
+ priv->connection = g_socket_client_connect_to_host (priv->client, priv->host, 8989, NULL, &priv->construct_error);
+}
+
+G_MODULE_EXPORT GType
+uca_camera_get_type (void)
+{
+ return UCA_TYPE_NET_CAMERA;
+}
diff --git a/uca-net-camera.h b/uca-net-camera.h
new file mode 100644
index 0000000..865fb4b
--- /dev/null
+++ b/uca-net-camera.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu>
+ (Karlsruhe Institute of Technology)
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+
+ This library 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 Lesser General Public License for more
+ details.
+
+ You should have received a copy of the GNU Lesser General Public License along
+ with this library; if not, write to the Free Software Foundation, Inc., 51
+ Franklin St, Fifth Floor, Boston, MA 02110, USA */
+
+#ifndef __UCA_NET_CAMERA_H
+#define __UCA_NET_CAMERA_H
+
+#include <glib-object.h>
+#include <uca/uca-camera.h>
+
+G_BEGIN_DECLS
+
+#define UCA_TYPE_NET_CAMERA (uca_net_camera_get_type())
+#define UCA_NET_CAMERA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), UCA_TYPE_NET_CAMERA, UcaNetCamera))
+#define UCA_IS_NET_CAMERA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), UCA_TYPE_NET_CAMERA))
+#define UCA_NET_CAMERA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), UCA_TYPE_NET_CAMERA, UcaNetCameraClass))
+#define UCA_IS_NET_CAMERA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), UCA_TYPE_NET_CAMERA))
+#define UCA_NET_CAMERA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), UCA_TYPE_NET_CAMERA, UcaNetCameraClass))
+
+#define UCA_NET_CAMERA_ERROR uca_net_camera_error_quark()
+typedef enum {
+ UCA_NET_CAMERA_ERROR_INIT,
+ UCA_NET_CAMERA_ERROR_START_RECORDING,
+ UCA_NET_CAMERA_ERROR_STOP_RECORDING,
+ UCA_NET_CAMERA_ERROR_TRIGGER,
+ UCA_NET_CAMERA_ERROR_NEXT_EVENT,
+ UCA_NET_CAMERA_ERROR_NO_DATA,
+ UCA_NET_CAMERA_ERROR_MAYBE_CORRUPTED
+} UcaNetCameraError;
+
+typedef struct _UcaNetCamera UcaNetCamera;
+typedef struct _UcaNetCameraClass UcaNetCameraClass;
+typedef struct _UcaNetCameraPrivate UcaNetCameraPrivate;
+
+/**
+ * UcaNetCamera:
+ *
+ * Creates #UcaNetCamera instances by loading corresponding shared objects. The
+ * contents of the #UcaNetCamera structure are private and should only be
+ * accessed via the provided API.
+ */
+struct _UcaNetCamera {
+ /*< private >*/
+ UcaCamera parent;
+
+ UcaNetCameraPrivate *priv;
+};
+
+/**
+ * UcaNetCameraClass:
+ *
+ * #UcaNetCamera class
+ */
+struct _UcaNetCameraClass {
+ /*< private >*/
+ UcaCameraClass parent;
+};
+
+GType uca_net_camera_get_type(void);
+
+G_END_DECLS
+
+#endif
diff --git a/uca-net-client.c b/uca-net-client.c
new file mode 100644
index 0000000..c92eedd
--- /dev/null
+++ b/uca-net-client.c
@@ -0,0 +1,207 @@
+#include <string.h>
+#include <stdlib.h>
+#include "uca-net-protocol.h"
+
+static gboolean
+send_default_message (GSocketConnection *connection, UcaNetMessageType type, GError **error)
+{
+ GOutputStream *output;
+ UcaNetMessageDefault request;
+
+ output = g_io_stream_get_output_stream (G_IO_STREAM (connection));
+ request.type = type;
+
+ if (!g_output_stream_write_all (output, &request, sizeof (request), NULL, NULL, error))
+ return FALSE;
+
+ if (!g_output_stream_flush (output, NULL, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+handle_default_reply (GSocketConnection *connection, UcaNetMessageType type, GError **error)
+{
+ GInputStream *input;
+ UcaNetDefaultReply reply;
+
+ input = g_io_stream_get_input_stream (G_IO_STREAM (connection));
+
+ if (g_input_stream_read_all (input, &reply, sizeof (reply), NULL, NULL, error)) {
+ g_assert (reply.type == type);
+
+ if (reply.error.occurred) {
+ g_set_error_literal (error, g_quark_from_string (reply.error.domain), reply.error.code, reply.error.message);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+uca_net_client_get_property (GSocketConnection *connection, const gchar *name, GValue *value, GError **error)
+{
+ UcaNetMessageGetPropertyRequest request;
+ UcaNetMessageGetPropertyReply reply;
+ GInputStream *input;
+ GOutputStream *output;
+
+ input = g_io_stream_get_input_stream (G_IO_STREAM (connection));
+ output = g_io_stream_get_output_stream (G_IO_STREAM (connection));
+
+ if (g_input_stream_has_pending (input))
+ g_input_stream_clear_pending (input);
+
+ /* request */
+ request.type = UCA_NET_MESSAGE_GET_PROPERTY;
+ strncpy (request.property_name, name, sizeof (request.property_name));
+
+ if (!g_output_stream_write_all (output, &request, sizeof (request), NULL, NULL, error))
+ return FALSE;
+
+ /* reply */
+ if (g_input_stream_read (input, &reply, sizeof (reply), NULL, error) < 0)
+ return FALSE;
+
+ if (reply.type != request.type) {
+ if (*error != NULL)
+ /* FIXME: replace with correct error codes */
+ *error = g_error_new_literal (G_FILE_ERROR, G_FILE_ERROR_NOENT, "Reply does not match request");
+ return FALSE;
+ }
+
+ if (g_type_is_a (G_VALUE_TYPE (value), G_TYPE_ENUM)) {
+ g_value_set_enum (value, atoi (reply.property_value));
+ }
+ else {
+ /* XXX: I'd like to avoid this and rather use g_value_transform(), however
+ * that call fails with Python and uca-camera-control but succeeds with
+ * uca-grab ... */
+ switch (G_VALUE_TYPE (value)) {
+ case G_TYPE_UINT:
+ g_value_set_uint (value, atol (reply.property_value));
+ break;
+ case G_TYPE_DOUBLE:
+ g_value_set_double (value, atof (reply.property_value));
+ break;
+ case G_TYPE_BOOLEAN:
+ g_value_set_boolean (value, g_strcmp0 (reply.property_value, "TRUE"));
+ break;
+ case G_TYPE_STRING:
+ g_value_set_string (value, reply.property_value);
+ break;
+ default:
+ g_warning ("Unsupported property type %s", G_VALUE_TYPE_NAME (value));
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+uca_net_client_set_property (GSocketConnection *connection, const gchar *name, const GValue *value, GError **error)
+{
+ GOutputStream *output;
+ const gchar *str;
+ GValue str_value = {0};
+ UcaNetMessageSetPropertyRequest request = { .type = UCA_NET_MESSAGE_SET_PROPERTY };
+
+ output = g_io_stream_get_output_stream (G_IO_STREAM (connection));
+ g_value_init (&str_value, G_TYPE_STRING);
+
+ if (g_type_is_a (G_VALUE_TYPE (value), G_TYPE_ENUM)) {
+ GValue int_value = {0};
+
+ g_value_init (&int_value, G_TYPE_INT);
+ g_value_transform (value, &int_value);
+ g_value_transform (&int_value, &str_value);
+ }
+ else {
+ g_value_transform (value, &str_value);
+ }
+
+ str = g_value_get_string (&str_value);
+ strncpy (request.property_name, name, sizeof (request.property_name));
+ strncpy (request.property_value, str, sizeof (request.property_value));
+
+ if (!g_output_stream_write_all (output, &request, sizeof (request), NULL, NULL, error))
+ return FALSE;
+
+ return handle_default_reply (connection, UCA_NET_MESSAGE_SET_PROPERTY, error);
+}
+
+void
+uca_net_client_start_recording (GSocketConnection *connection, GError **error)
+{
+ if (!send_default_message (connection, UCA_NET_MESSAGE_START_RECORDING, error))
+ return;
+
+ handle_default_reply (connection, UCA_NET_MESSAGE_START_RECORDING, error);
+}
+
+void
+uca_net_client_stop_recording (GSocketConnection *connection, GError **error)
+{
+ if (!send_default_message (connection, UCA_NET_MESSAGE_STOP_RECORDING, error))
+ return;
+
+ handle_default_reply (connection, UCA_NET_MESSAGE_STOP_RECORDING, error);
+}
+
+gboolean
+uca_net_client_grab (GSocketConnection *connection, gpointer data, gsize size, GError **error)
+{
+ GInputStream *input;
+ GOutputStream *output;
+ gsize transmitted;
+ gsize bytes_left;
+ UcaNetMessageGrabRequest request = { .type = UCA_NET_MESSAGE_GRAB, .size = size };
+
+ input = g_io_stream_get_input_stream (G_IO_STREAM (connection));
+ output = g_io_stream_get_output_stream (G_IO_STREAM (connection));
+
+ /* request */
+ if (!g_output_stream_write_all (output, &request, sizeof (request), &transmitted, NULL, error)) {
+ return FALSE;
+ }
+
+ /* error reply */
+ if (handle_default_reply (connection, UCA_NET_MESSAGE_GRAB, error)) {
+ bytes_left = size;
+
+ while (bytes_left > 0) {
+ gssize read;
+ gchar *buffer;
+
+ buffer = (gchar *) data;
+ read = g_input_stream_read (input, &buffer[size - bytes_left], bytes_left, NULL, error);
+
+ if (read < 0)
+ return FALSE;
+
+ bytes_left -= read;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+uca_net_client_close (GSocketConnection *connection, GError **error)
+{
+ GOutputStream *output;
+ UcaNetMessageDefault request = { .type = UCA_NET_MESSAGE_CLOSE_CONNECTION };
+
+ output = g_io_stream_get_output_stream (G_IO_STREAM (connection));
+
+ if (!g_output_stream_write_all (output, &request, sizeof (request), NULL, NULL, error))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/uca-net-protocol.h b/uca-net-protocol.h
new file mode 100644
index 0000000..3df8c37
--- /dev/null
+++ b/uca-net-protocol.h
@@ -0,0 +1,85 @@
+#ifndef PROTOCOL_H
+#define PROTOCOL_H
+
+#include <gio/gio.h>
+
+typedef enum {
+ UCA_NET_MESSAGE_GET_PROPERTY = 0,
+ UCA_NET_MESSAGE_SET_PROPERTY,
+ UCA_NET_MESSAGE_START_RECORDING,
+ UCA_NET_MESSAGE_STOP_RECORDING,
+ UCA_NET_MESSAGE_GRAB,
+ UCA_NET_MESSAGE_CLOSE_CONNECTION,
+} UcaNetMessageType;
+
+typedef struct {
+ gboolean occurred;
+ gchar domain[64];
+ gint code;
+ gchar message[512];
+} UcaNetErrorReply;
+
+typedef struct {
+ UcaNetMessageType type;
+ UcaNetErrorReply error;
+} UcaNetDefaultReply;
+
+typedef struct {
+ UcaNetMessageType type;
+} UcaNetMessageDefault;
+
+typedef struct {
+ UcaNetMessageType type;
+ gchar property_name[128];
+} UcaNetMessageGetPropertyRequest;
+
+typedef struct {
+ UcaNetMessageType type;
+ gchar property_value[128];
+} UcaNetMessageGetPropertyReply;
+
+typedef struct {
+ UcaNetMessageType type;
+ gchar property_name[128];
+ gchar property_value[128];
+} UcaNetMessageSetPropertyRequest;
+
+typedef struct {
+ UcaNetMessageType type;
+ gsize size;
+} UcaNetMessageGrabRequest;
+
+
+typedef struct {
+ gpointer user_data;
+
+ void (*get_property) (gpointer user_data, const gchar *name, gchar *value);
+ void (*set_property) (gpointer user_data, const gchar *name, const gchar *value, GError **error);
+ void (*start_recording) (gpointer user_data, GError **error);
+ void (*stop_recording) (gpointer user_data, GError **error);
+ gboolean (*grab) (gpointer data, gpointer user_data, GError **error);
+} UcaNetHandlers;
+
+gboolean uca_net_client_get_property (GSocketConnection *connection,
+ const gchar *name,
+ GValue *value,
+ GError **error);
+gboolean uca_net_client_set_property (GSocketConnection *connection,
+ const gchar *name,
+ const GValue *value,
+ GError **error);
+void uca_net_client_start_recording (GSocketConnection *connection,
+ GError **error);
+void uca_net_client_stop_recording (GSocketConnection *connection,
+ GError **error);
+gboolean uca_net_client_grab (GSocketConnection *connection,
+ gpointer data,
+ gsize size,
+ GError **error);
+gboolean uca_net_client_close (GSocketConnection *connection,
+ GError **error);
+
+void uca_net_server_register_handlers (UcaNetHandlers *handlers);
+void uca_net_server_handle (GSocketConnection *connection);
+
+#endif
diff --git a/uca-net-server.c b/uca-net-server.c
new file mode 100644
index 0000000..f038cd6
--- /dev/null
+++ b/uca-net-server.c
@@ -0,0 +1,174 @@
+#include <string.h>
+#include "uca-net-protocol.h"
+
+static UcaNetHandlers handlers;
+
+static void
+send_reply (GOutputStream *output, gpointer data, gsize size, GError **error)
+{
+ gsize written;
+
+ if (!g_output_stream_write_all (output, data, size, &written, NULL, error))
+ return;
+
+ if (!g_output_stream_flush (output, NULL, error))
+ return;
+}
+
+static void
+prepare_error_reply (GError *error, UcaNetErrorReply *reply)
+{
+ if (error != NULL) {
+ reply->occurred = TRUE;
+ reply->code = error->code;
+ strncpy (reply->domain, g_quark_to_string (error->domain), sizeof (error->domain));
+ strncpy (reply->message, error->message, sizeof (reply->message));
+ g_error_free (error);
+ }
+ else {
+ reply->occurred = FALSE;
+ }
+}
+
+static void
+uca_net_server_handle_get_property (GOutputStream *output, UcaNetMessageGetPropertyRequest *request, GError **error)
+{
+ UcaNetMessageGetPropertyReply reply = { .type = UCA_NET_MESSAGE_GET_PROPERTY };
+
+ handlers.get_property (handlers.user_data, request->property_name, reply.property_value);
+ send_reply (output, &reply, sizeof (reply), error);
+}
+
+static void
+uca_net_server_handle_set_property (GOutputStream *output, UcaNetMessageSetPropertyRequest *request, GError **stream_error)
+{
+ UcaNetDefaultReply reply = { .type = UCA_NET_MESSAGE_SET_PROPERTY };
+ GError *error = NULL;
+
+ handlers.set_property (handlers.user_data, request->property_name, request->property_value, &error);
+ prepare_error_reply (error, &reply.error);
+ send_reply (output, &reply, sizeof (reply), stream_error);
+}
+
+static void
+uca_net_server_handle_start_recording (GOutputStream *output, GError **stream_error)
+{
+ UcaNetDefaultReply reply = { .type = UCA_NET_MESSAGE_START_RECORDING };
+ GError *error = NULL;
+
+ handlers.start_recording (handlers.user_data, &error);
+ prepare_error_reply (error, &reply.error);
+ send_reply (output, &reply, sizeof (reply), stream_error);
+}
+
+static void
+uca_net_server_handle_stop_recording (GOutputStream *output, GError **stream_error)
+{
+ UcaNetDefaultReply reply = { .type = UCA_NET_MESSAGE_STOP_RECORDING };
+ GError *error = NULL;
+
+ handlers.stop_recording (handlers.user_data, &error);
+ prepare_error_reply (error, &reply.error);
+ send_reply (output, &reply, sizeof (reply), stream_error);
+}
+
+static void
+uca_net_server_handle_grab (GOutputStream *output, UcaNetMessageGrabRequest *request, GError **stream_error)
+{
+ UcaNetDefaultReply reply = { .type = UCA_NET_MESSAGE_GRAB };
+ gsize bytes_left;
+ GError *error = NULL;
+ static gsize size = 0;
+ static gchar *buffer = NULL;
+
+ if (buffer == NULL || size != request->size) {
+ buffer = g_realloc (buffer, request->size);
+ size = request->size;
+ }
+
+ handlers.grab (buffer, handlers.user_data, &error);
+ prepare_error_reply (error, &reply.error);
+ send_reply (output, &reply, sizeof (reply), stream_error);
+
+ /* send data if no error occured during grab */
+ if (!reply.error.occurred) {
+ bytes_left = size;
+
+ while (bytes_left > 0) {
+ gssize written;
+
+ written = g_output_stream_write (output, &buffer[size - bytes_left], bytes_left, NULL, stream_error);
+
+ if (written < 0)
+ return;
+
+ bytes_left -= written;
+ }
+ }
+}
+
+void
+uca_net_server_register_handlers (UcaNetHandlers *new_handlers)
+{
+ memcpy (&handlers, new_handlers, sizeof (UcaNetHandlers));
+}
+
+void
+uca_net_server_handle (GSocketConnection *connection)
+{
+ GInputStream *input;
+ GOutputStream *output;
+ gchar *buffer;
+ gboolean active;
+
+ buffer = g_malloc0 (4096);
+ input = g_io_stream_get_input_stream (G_IO_STREAM (connection));
+ output = g_io_stream_get_output_stream (G_IO_STREAM (connection));
+ active = TRUE;
+
+ while (active) {
+ UcaNetMessageDefault *message;
+ GError *error = NULL;
+
+ /* looks dangerous */
+ g_input_stream_read (input, buffer, 4096, NULL, &error);
+ message = (UcaNetMessageDefault *) buffer;
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE)) {
+ g_error_free (error);
+ active = FALSE;
+ break;
+ }
+
+ switch (message->type) {
+ case UCA_NET_MESSAGE_GET_PROPERTY:
+ uca_net_server_handle_get_property (output, (UcaNetMessageGetPropertyRequest *) buffer, &error);
+ break;
+ case UCA_NET_MESSAGE_SET_PROPERTY:
+ uca_net_server_handle_set_property (output, (UcaNetMessageSetPropertyRequest *) buffer, &error);
+ break;
+ case UCA_NET_MESSAGE_START_RECORDING:
+ uca_net_server_handle_start_recording (output, &error);
+ break;
+ case UCA_NET_MESSAGE_STOP_RECORDING:
+ uca_net_server_handle_stop_recording (output, &error);
+ break;
+ case UCA_NET_MESSAGE_GRAB:
+ uca_net_server_handle_grab (output, (UcaNetMessageGrabRequest *) buffer, &error);
+ break;
+ case UCA_NET_MESSAGE_CLOSE_CONNECTION:
+ active = FALSE;
+ break;
+ default:
+ g_warning ("Message type not known");
+ }
+
+ if (error != NULL) {
+ g_warning ("Error handling requests: %s", error->message);
+ g_error_free (error);
+ active = FALSE;
+ }
+ }
+
+ g_free (buffer);
+}
diff --git a/ucad.c b/ucad.c
new file mode 100644
index 0000000..de3d79b
--- /dev/null
+++ b/ucad.c
@@ -0,0 +1,223 @@
+/* Copyright (C) 2011-2016 Matthias Vogelgesang <matthias.vogelgesang@kit.edu>
+ (Karlsruhe Institute of Technology)
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+
+ This library 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 Lesser General Public License for more
+ details.
+
+ You should have received a copy of the GNU Lesser General Public License along
+ with this library; if not, write to the Free Software Foundation, Inc., 51
+ Franklin St, Fifth Floor, Boston, MA 02110, USA */
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <string.h>
+#include <uca/uca-camera.h>
+#include <uca/uca-plugin-manager.h>
+#include "uca-net-protocol.h"
+
+static gchar *
+get_camera_list (UcaPluginManager *manager)
+{
+ GList *types;
+ GString *str;
+
+ manager = uca_plugin_manager_new ();
+ types = uca_plugin_manager_get_available_cameras (manager);
+ str = g_string_new ("[ ");
+
+ if (types != NULL) {
+ for (GList *it = g_list_first (types); it != NULL; it = g_list_next (it)) {
+ gchar *name = (gchar *) it->data;
+
+ if (g_list_next (it) == NULL)
+ g_string_append_printf (str, "%s ]", name);
+ else
+ g_string_append_printf (str, "%s, ", name);
+ }
+ }
+ else {
+ g_string_append (str, "]");
+ }
+
+ g_list_free_full (types, g_free);
+ g_object_unref (manager);
+ return g_string_free (str, FALSE);
+}
+
+static GOptionContext *
+uca_option_context_new (UcaPluginManager *manager)
+{
+ GOptionContext *context;
+ gchar *camera_list;
+
+ camera_list = get_camera_list (manager);
+ context = g_option_context_new (camera_list);
+ g_free (camera_list);
+ return context;
+}
+
+static void
+handle_get_property_request (gpointer user_data, const gchar *name, gchar *value)
+{
+ UcaCamera *camera;
+ GParamSpec *pspec;
+ GValue prop_value = {0};
+ GValue str_value = {0};
+
+ camera = user_data;
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (camera), name);
+
+ g_value_init (&prop_value, g_type_is_a (pspec->value_type, G_TYPE_ENUM) ? G_TYPE_INT : pspec->value_type);
+ g_object_get_property (G_OBJECT (camera), name, &prop_value);
+
+ g_value_init (&str_value, G_TYPE_STRING);
+ g_value_transform (&prop_value, &str_value);
+
+ strncpy (value, g_value_get_string (&str_value), sizeof (g_value_get_string (&str_value)));
+}
+
+static void
+handle_set_property_request (gpointer user_data, const gchar *name, const gchar *value, GError **error)
+{
+ UcaCamera *camera;
+ GParamSpec *pspec;
+ GValue prop_value = {0};
+ GValue str_value = {0};
+
+ camera = user_data;
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (camera), name);
+
+ g_value_init (&prop_value, g_type_is_a (pspec->value_type, G_TYPE_ENUM) ? G_TYPE_INT : pspec->value_type);
+ g_value_init (&str_value, G_TYPE_STRING);
+ g_value_set_string (&str_value, value);
+ g_value_transform (&str_value, &prop_value);
+
+ g_object_set_property (G_OBJECT (camera), name, &prop_value);
+}
+
+static void
+handle_start_recording_request (gpointer user_data, GError **error)
+{
+ uca_camera_start_recording (UCA_CAMERA (user_data), error);
+}
+
+static void
+handle_stop_recording_request (gpointer user_data, GError **error)
+{
+ uca_camera_stop_recording (UCA_CAMERA (user_data), error);
+}
+
+static gboolean
+handle_grab_request (gpointer data, gpointer user_data, GError **error)
+{
+ return uca_camera_grab (UCA_CAMERA (user_data), data, error);
+}
+
+static gboolean
+run_callback (GSocketService *service, GSocketConnection *connection, GObject *source, gpointer user_data)
+{
+ GInetSocketAddress *sock_address;
+ GInetAddress *address;
+ gchar *address_string;
+
+ UcaNetHandlers handlers = {
+ .user_data = user_data,
+ .get_property = handle_get_property_request,
+ .set_property = handle_set_property_request,
+ .start_recording = handle_start_recording_request,
+ .stop_recording = handle_stop_recording_request,
+ .grab = handle_grab_request,
+ };
+
+ sock_address = G_INET_SOCKET_ADDRESS (g_socket_connection_get_remote_address (connection, NULL));
+ address = g_inet_socket_address_get_address (sock_address);
+ address_string = g_inet_address_to_string (address);
+ g_message ("Connection accepted from %s:%u", address_string, g_inet_socket_address_get_port (sock_address));
+
+ g_free (address_string);
+ g_object_unref (sock_address);
+
+ uca_net_server_register_handlers (&handlers);
+ uca_net_server_handle (connection);
+
+ return FALSE;
+}
+
+static void
+serve (UcaCamera *camera, GError **error)
+{
+ GMainLoop *loop;
+ GSocketService *service;
+
+ service = g_threaded_socket_service_new (1);
+
+ if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (service), 8989, NULL, error))
+ return;
+
+ g_signal_connect (service, "run", G_CALLBACK (run_callback), camera);
+
+ loop = g_main_loop_new (NULL, TRUE);
+ g_main_loop_run (loop);
+}
+
+int
+main (int argc, char **argv)
+{
+ GOptionContext *context;
+ UcaPluginManager *manager;
+ UcaCamera *camera;
+ GError *error = NULL;
+
+ static GOptionEntry entries[] = { { NULL } };
+
+#if !(GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init();
+#endif
+
+ manager = uca_plugin_manager_new ();
+ context = uca_option_context_new (manager);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_print ("Failed parsing arguments: %s\n", error->message);
+ goto cleanup_manager;
+ }
+
+ if (argc < 2) {
+ g_print ("%s\n", g_option_context_get_help (context, TRUE, NULL));
+ goto cleanup_manager;
+ }
+
+ camera = uca_plugin_manager_get_camera (manager, argv[argc - 1], &error, NULL);
+
+ if (camera == NULL) {
+ g_print ("Error during initialization: %s\n", error->message);
+ goto cleanup_camera;
+ }
+
+ if (!uca_camera_parse_arg_props (camera, argv, argc - 1, &error)) {
+ g_print ("Error setting properties: %s\n", error->message);
+ goto cleanup_manager;
+ }
+ if (error != NULL)
+ g_print ("Error: %s\n", error->message);
+
+ g_option_context_free (context);
+
+ serve (camera, &error);
+
+cleanup_camera:
+ g_object_unref (camera);
+
+cleanup_manager:
+ g_object_unref (manager);
+
+ return error != NULL ? 1 : 0;
+}