[cheese/wip/camera-service: 2/2] Import basic camera D-Bus service



commit 91d1db75d01ca5ecf9c16b6737f4b425b9cad65b
Author: David King <amigadave amigadave com>
Date:   Mon Aug 18 17:27:07 2014 +0100

    Import basic camera D-Bus service

 Makefile.am                      |   52 ++++++-
 configure.ac                     |    4 +-
 data/org.gnome.Camera.service.in |    3 +
 data/org.gnome.Camera.xml        |   50 ++++++
 service/gc-application.c         |  116 ++++++++++++++
 service/gc-application.h         |   45 ++++++
 service/gc-camera-client.c       |  298 ++++++++++++++++++++++++++++++++++++
 service/gc-camera-client.h       |   46 ++++++
 service/gc-camera-manager.c      |  295 ++++++++++++++++++++++++++++++++++++
 service/gc-camera-manager.h      |   45 ++++++
 service/gc-chooser.c             |  141 +++++++++++++++++
 service/gc-chooser.h             |   46 ++++++
 service/gc-client-monitor.c      |  311 ++++++++++++++++++++++++++++++++++++++
 service/gc-client-monitor.h      |   48 ++++++
 service/gc-main.c                |   45 ++++++
 15 files changed, 1542 insertions(+), 3 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index fbcd4de..0e093f6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -251,6 +251,48 @@ cheese_LDADD = \
        $(CHEESE_LIBS) \
        $(CHEESE_GTK_LIBS)
 
+libexec_PROGRAMS = \
+       gnome-camera-service
+
+service/camera.c: service/camera.h
+service/camera.h: Makefile data/org.gnome.Camera.xml
+       $(AM_V_GEN) $(GDBUS_CODEGEN) --interface-prefix org.gnome.Camera. \
+               --generate-c-code service/camera \
+               --c-namespace Gc $(srcdir)/data/org.gnome.Camera.xml
+
+BUILT_SOURCES = \
+       $(nodist_gnome_camera_service_SOURCES)
+
+gnome_camera_service_SOURCES = \
+       service/gc-application.c \
+       service/gc-camera-client.c \
+       service/gc-camera-manager.c \
+       service/gc-client-monitor.c \
+       service/gc-main.c
+
+noinst_gnome_camera_service_headers = \
+       service/gc-application.h \
+       service/gc-camera-client.h \
+       service/gc-camera-manager.h \
+       service/gc-client-monitor.h
+
+nodist_gnome_camera_service_headers = \
+       service/camera.h
+
+nodist_gnome_camera_service_SOURCES = \
+       service/camera.c
+
+gnome_camera_service_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       -I$(top_builddir)/service \
+       -I$(top_srcdir)/libcheese
+
+gnome_camera_service_CFLAGS = \
+       $(CHEESE_GTK_CFLAGS)
+
+gnome_camera_service_LDADD = \
+       libcheese-gtk.la
+
 EXAMPLES = \
        tests/cheese-test-camera \
        tests/cheese-test-chooser \
@@ -322,7 +364,9 @@ appdata_in_files = data/org.gnome.Cheese.appdata.xml.in
 appdata_XML = $(appdata_in_files:.appdata.xml.in=.appdata.xml)
 
 servicedir = $(datadir)/dbus-1/services
-service_DATA = data/org.gnome.Cheese.service
+service_DATA = \
+       data/org.gnome.Camera.service \
+       data/org.gnome.Cheese.service
 
 @GSETTINGS_RULES@
 gsettings_SCHEMAS = data/org.gnome.Cheese.gschema.xml
@@ -487,9 +531,11 @@ dist_noinst_DATA = \
        ChangeLog.pre-git \
        $(desktop_in_files) \
        $(gsettings_SCHEMAS) \
+       $(noinst_gnome_camera_service_headers) \
        $(noinst_resource_files) \
        $(appdata_in_files) \
        data/org.gnome.Cheese.gresource.xml \
+       data/org.gnome.Camera.xml \
        build-aux/test-driver \
        $(gtkdoc_srcdir)/cheese-docs.xml \
        $(gtkdoc_srcdir)/cheese-sections.txt \
@@ -519,8 +565,10 @@ CLEANFILES = \
        $(enum_data) \
        $(gir_DATA) \
        $(typelib_DATA) \
+       $(nodist_gnome_camera_service_headers) \
        src/cheese-resource.c \
-       src/cheese-resource.h
+       src/cheese-resource.h \
+       service/camera.h
 
 DISTCLEANFILES = \
        libcheese_la_vala.stamp \
diff --git a/configure.ac b/configure.ac
index c2fbdce..b904858 100644
--- a/configure.ac
+++ b/configure.ac
@@ -119,8 +119,9 @@ PKG_CHECK_MODULES([CHEESE_GTK],
    $UDEV_REQUIRED
    $GNOME_VIDEO_EFFECTS_REQUIRED])
 
-# Resources.
+# GLib programs.
 AC_SUBST([GLIB_COMPILE_RESOURCES], [`$PKG_CONFIG --variable glib_compile_resources gio-2.0`])
+AC_SUBST([GDBUS_CODEGEN], [`$PKG_CONFIG --variable gdbus_codegen gio-2.0`])
 
 # Recommend some runtime GStreamer plugins.
 AC_PATH_PROGS([GST_INSPECT], [gst-inspect-1.0], [notfound])
@@ -217,6 +218,7 @@ docs/reference/Makefile
 docs/reference/version.xml
 data/cheese.pc
 data/cheese-gtk.pc
+data/org.gnome.Camera.service
 data/org.gnome.Cheese.desktop.in
 data/org.gnome.Cheese.service
 help/Makefile
diff --git a/data/org.gnome.Camera.service.in b/data/org.gnome.Camera.service.in
new file mode 100644
index 0000000..de474f0
--- /dev/null
+++ b/data/org.gnome.Camera.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Camera
+Exec= libexecdir@/gnome-camera-service
diff --git a/data/org.gnome.Camera.xml b/data/org.gnome.Camera.xml
new file mode 100644
index 0000000..4a6926c
--- /dev/null
+++ b/data/org.gnome.Camera.xml
@@ -0,0 +1,50 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" 
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";>
+<node name="/org/gnome/Camera">
+    <!-- org.gnome.Camera:
+         @short_description: Use the available camera devices
+
+         The camera interface is used to take photos with the available camera
+         devices.
+    -->
+    <interface name="org.gnome.Camera.Manager">
+        <!-- GetClient:
+             @chooser: The object path of a new client object
+             @short_description: Get a client object for showing a photo dialog
+
+             Use the obtained client object to show a dialog for taking photos
+             and videos using a webcam.
+        -->
+        <method name="GetClient">
+            <arg name="client" type="o" direction="out"/>
+        </method>
+    </interface>
+
+    <!-- org.gnome.Camera.Client:
+         @short_description: Play images and videos from a camera
+    -->
+    <interface name="org.gnome.Camera.Client">
+        <!-- ImageData:
+             @short_description: Image data for photo from webcam
+
+             The PNG image data, Base64-encoded, from the photo taken by the
+             webcam, and cropped by the user.
+        -->
+        <property name="ImageData" type="s" access="read">
+            <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="invalidates"/>
+        </property>
+        <!-- ShowChooser:
+             @short_description: Show the webcam chooser dialog
+        -->
+        <method name="ShowChooser">
+        </method>
+        <!-- UserDone:
+             @photo_taken: Whether a photo was taken or not
+             @image_data: The image data as a Base64-encoded PNG, or empty
+             @short_description: Indicates that the dialog was closed
+        -->
+        <signal name="UserDone">
+            <arg name="photo_taken" type="b" />
+            <arg name="image_data" type="s" />
+        </signal>
+    </interface>
+</node>
diff --git a/service/gc-application.c b/service/gc-application.c
new file mode 100644
index 0000000..7e0d086
--- /dev/null
+++ b/service/gc-application.c
@@ -0,0 +1,116 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gc-application.h"
+
+#include "gc-camera-manager.h"
+
+typedef struct
+{
+    GcCameraManager *manager;
+} GcApplicationPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GcApplication, gc_application, G_TYPE_APPLICATION)
+
+static gboolean
+gc_application_dbus_register (GApplication *application,
+                              GDBusConnection *connection,
+                              const gchar *object_path,
+                              GError **error)
+{
+    GcApplicationPrivate *priv;
+
+    priv = gc_application_get_instance_private (GC_APPLICATION (application));
+
+    if (!G_APPLICATION_CLASS (gc_application_parent_class)->dbus_register (application,
+                                                                           connection,
+                                                                           object_path,
+                                                                           error))
+    {
+        return FALSE;
+    }
+
+    priv->manager = gc_camera_manager_new (connection, error);
+
+    if (priv->manager == NULL)
+    {
+        g_warning ("Error exporting camera interface skeleton: %s",
+                   (*error)->message);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+gc_application_dbus_unregister (GApplication *application,
+                                GDBusConnection *connection,
+                                const gchar *object_path)
+{
+    GcApplicationPrivate *priv;
+
+    priv = gc_application_get_instance_private (GC_APPLICATION (application));
+
+    /* TODO: Not necessary? */
+    g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (priv->manager));
+
+    G_APPLICATION_CLASS (gc_application_parent_class)->dbus_unregister (application,
+                                                                        connection,
+                                                                        object_path);
+}
+
+static void
+gc_application_finalize (GObject *object)
+{
+    GcApplication *application;
+    GcApplicationPrivate *priv;
+
+    application = GC_APPLICATION (object);
+    priv = gc_application_get_instance_private (application);
+
+    g_clear_object (&priv->manager);
+}
+
+static void
+gc_application_init (GcApplication *application)
+{
+}
+
+static void
+gc_application_class_init (GcApplicationClass *klass)
+{
+    GObjectClass *gobject_class;
+    GApplicationClass *app_class;
+
+    gobject_class = G_OBJECT_CLASS (klass);
+    gobject_class->finalize = gc_application_finalize;
+
+    app_class = G_APPLICATION_CLASS (klass);
+    app_class->dbus_register = gc_application_dbus_register;
+    app_class->dbus_unregister = gc_application_dbus_unregister;
+}
+
+GcApplication *
+gc_application_new (void)
+{
+    return g_object_new (GC_TYPE_APPLICATION, "application-id",
+                         "org.gnome.Camera", "flags", G_APPLICATION_IS_SERVICE,
+                         NULL);
+}
diff --git a/service/gc-application.h b/service/gc-application.h
new file mode 100644
index 0000000..a74a6ea
--- /dev/null
+++ b/service/gc-application.h
@@ -0,0 +1,45 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GC_APPLICATION_H_
+#define GC_APPLICATION_H_
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+    /*< private >*/
+    GApplication parent_instance;
+} GcApplication;
+
+typedef struct
+{
+    GApplicationClass parent_class;
+} GcApplicationClass;
+
+#define GC_TYPE_APPLICATION (gc_application_get_type ())
+#define GC_APPLICATION(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GC_TYPE_APPLICATION, GcApplication))
+
+GType gc_application_get_type (void);
+GcApplication * gc_application_new (void);
+
+G_END_DECLS
+
+#endif /* GC_APPLICATION_H_ */
diff --git a/service/gc-camera-client.c b/service/gc-camera-client.c
new file mode 100644
index 0000000..5436564
--- /dev/null
+++ b/service/gc-camera-client.c
@@ -0,0 +1,298 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gc-camera-client.h"
+
+#include <cheese/cheese-avatar-chooser.h>
+
+typedef struct
+{
+    GDBusConnection *connection;
+    gchar *path;
+    GtkWidget *chooser;
+    gchar *image_data;
+} GcCameraClientPrivate;
+
+static void gc_camera_client_client_iface_init (GcClientIface *iface);
+static void gc_camera_client_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcCameraClient, gc_camera_client,
+                         GC_TYPE_CLIENT_SKELETON,
+                         G_ADD_PRIVATE (GcCameraClient)
+                         G_IMPLEMENT_INTERFACE (GC_TYPE_CLIENT, gc_camera_client_client_iface_init)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gc_camera_client_initable_iface_init))
+
+enum
+{
+    PROP_0,
+    PROP_CONNECTION,
+    PROP_PATH,
+    PROP_IMAGE_DATA,
+    N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+static void
+gc_camera_client_set_image_data_from_pixbuf (GcCameraClient *self,
+                                             GdkPixbuf *pixbuf)
+{
+    GcCameraClientPrivate *priv;
+    gchar *buffer;
+    gsize length;
+
+    priv = gc_camera_client_get_instance_private (self);
+
+    /* FIXME: Check for errors. */
+    if (!gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &length, "png", NULL, NULL))
+    {
+        g_error ("%s", "GdkPixbuf could not be saved to PNG format");
+    }
+
+    g_free (priv->image_data);
+    priv->image_data = g_base64_encode (buffer, length);
+    g_free (buffer);
+
+    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_IMAGE_DATA]);
+}
+
+static void
+on_chooser_response (CheeseAvatarChooser *chooser,
+                     gint response,
+                     GcCameraClient *self)
+{
+    GcCameraClientPrivate *priv;
+
+    priv = gc_camera_client_get_instance_private (self);
+
+    if (response == GTK_RESPONSE_ACCEPT)
+    {
+        gc_camera_client_set_image_data_from_pixbuf (self,
+                                                     cheese_avatar_chooser_get_picture (chooser));
+        gc_client_emit_user_done (GC_CLIENT (self), TRUE, priv->image_data);
+    }
+    else
+    {
+        gc_client_emit_user_done (GC_CLIENT (self), FALSE, priv->image_data);
+    }
+
+    /* FIXME: Destroy the chooser, as there is no way to stop the webcam
+     * viewfinder. */
+    gtk_widget_hide (GTK_WIDGET (chooser));
+}
+
+static gboolean
+gc_camera_client_handle_show_chooser (GcClient *client,
+                                      GDBusMethodInvocation *invocation)
+{
+    GcCameraClientPrivate *priv;
+
+    priv = gc_camera_client_get_instance_private (GC_CAMERA_CLIENT (client));
+
+    gtk_widget_show (priv->chooser);
+
+    gc_client_complete_show_chooser (client, invocation);
+
+    return TRUE;
+}
+
+static const gchar *
+gc_camera_client_get_image_data (GcClient *client)
+{
+    GcCameraClient *self;
+    GcCameraClientPrivate *priv;
+
+    self = GC_CAMERA_CLIENT (client);
+    priv = gc_camera_client_get_instance_private (self);
+
+    return priv->image_data;
+}
+
+const gchar *
+gc_camera_client_get_path (GcCameraClient *client)
+{
+    GcCameraClientPrivate *priv;
+
+    g_return_val_if_fail (GC_CAMERA_CLIENT (client), NULL);
+
+    priv = gc_camera_client_get_instance_private (client);
+
+    return priv->path;
+}
+
+static void
+gc_camera_client_finalize (GObject *object)
+{
+    GcCameraClientPrivate *priv;
+
+    priv = gc_camera_client_get_instance_private (GC_CAMERA_CLIENT (object));
+
+    g_clear_object (&priv->chooser);
+    g_clear_pointer (&priv->image_data, g_free);
+
+    g_free (priv->path);
+
+    G_OBJECT_CLASS (gc_camera_client_parent_class)->finalize (object);
+}
+
+static void
+gc_camera_client_get_property (GObject *object,
+                               guint prop_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+    GcCameraClientPrivate *priv;
+
+    priv = gc_camera_client_get_instance_private (GC_CAMERA_CLIENT (object));
+
+    switch (prop_id)
+    {
+        case PROP_CONNECTION:
+            g_value_set_object (value, priv->connection);
+            break;
+        case PROP_PATH:
+            g_value_set_string (value, priv->path);
+            break;
+        case PROP_IMAGE_DATA:
+            g_value_set_string (value, priv->image_data);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gc_camera_client_set_property (GObject *object,
+                               guint prop_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+    GcCameraClientPrivate *priv;
+
+    priv = gc_camera_client_get_instance_private (GC_CAMERA_CLIENT (object));
+
+    switch (prop_id)
+    {
+        case PROP_CONNECTION:
+            priv->connection = g_value_dup_object (value);
+            break;
+        case PROP_PATH:
+            g_free (priv->path);
+            priv->path = g_value_dup_string (value);
+            break;
+        case PROP_IMAGE_DATA:
+            g_free (priv->image_data);
+            priv->image_data = g_value_dup_string (value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gc_camera_client_class_init (GcCameraClientClass *klass)
+{
+    GObjectClass *object_class;
+
+    object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = gc_camera_client_finalize;
+    object_class->get_property = gc_camera_client_get_property;
+    object_class->set_property = gc_camera_client_set_property;
+
+    properties[PROP_CONNECTION] = g_param_spec_object ("connection",
+                                                       "Connection",
+                                                       "DBus Connection",
+                                                       G_TYPE_DBUS_CONNECTION,
+                                                       G_PARAM_READWRITE |
+                                                       G_PARAM_CONSTRUCT_ONLY |
+                                                       G_PARAM_STATIC_STRINGS);
+    g_object_class_install_property (object_class, PROP_CONNECTION,
+                                     properties[PROP_CONNECTION]);
+
+    properties[PROP_PATH] = g_param_spec_string ("path",
+                                                 "Path",
+                                                 "D-Bus object path",
+                                                 NULL,
+                                                 G_PARAM_READWRITE |
+                                                 G_PARAM_CONSTRUCT_ONLY |
+                                                 G_PARAM_STATIC_STRINGS);
+    g_object_class_install_property (object_class, PROP_PATH,
+                                     properties[PROP_PATH]);
+
+    properties[PROP_IMAGE_DATA] = g_param_spec_string ("image-data",
+                                                       "Image data",
+                                                       "PNG image data, Base64-encoded",
+                                                       NULL,
+                                                       G_PARAM_READWRITE |
+                                                       G_PARAM_STATIC_STRINGS);
+    g_object_class_install_property (object_class, PROP_IMAGE_DATA,
+                                     properties[PROP_IMAGE_DATA]);
+}
+
+static void
+gc_camera_client_init (GcCameraClient *self)
+{
+    GcCameraClientPrivate *priv;
+
+    priv = gc_camera_client_get_instance_private (self);
+
+    priv->chooser = cheese_avatar_chooser_new ();
+
+    g_signal_connect (priv->chooser, "response",
+                      G_CALLBACK (on_chooser_response), self);
+}
+
+static gboolean
+gc_camera_client_initable_init (GInitable *initable,
+                                GCancellable *cancellable,
+                                GError **error)
+{
+    GcCameraClientPrivate *priv;
+
+    priv = gc_camera_client_get_instance_private (GC_CAMERA_CLIENT (initable));
+
+    return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (initable),
+                                             priv->connection,
+                                             priv->path,
+                                             error);
+}
+
+static void
+gc_camera_client_client_iface_init (GcClientIface *iface)
+{
+    iface->handle_show_chooser = gc_camera_client_handle_show_chooser;
+    iface->get_image_data = gc_camera_client_get_image_data;
+}
+
+static void
+gc_camera_client_initable_iface_init (GInitableIface *iface)
+{
+    iface->init = gc_camera_client_initable_init;
+}
+
+GcCameraClient *
+gc_camera_client_new (GDBusConnection *connection,
+                      const gchar *path,
+                      GError **error)
+{
+    return g_initable_new (GC_TYPE_CAMERA_CLIENT, NULL, error, "connection",
+                           connection, "path", path, NULL);
+}
diff --git a/service/gc-camera-client.h b/service/gc-camera-client.h
new file mode 100644
index 0000000..d611451
--- /dev/null
+++ b/service/gc-camera-client.h
@@ -0,0 +1,46 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GC_CAMERA_CLIENT_H_
+#define GC_CAMERA_CLIENT_H_
+
+#include "camera.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+    /*< private >*/
+    GcClientSkeleton parent_instance;
+} GcCameraClient;
+
+typedef struct
+{
+    GcClientSkeletonClass parent_class;
+} GcCameraClientClass;
+
+#define GC_TYPE_CAMERA_CLIENT (gc_camera_client_get_type ())
+#define GC_CAMERA_CLIENT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GC_TYPE_CAMERA_CLIENT, 
GcCameraClient))
+
+GType gc_camera_client_get_type (void);
+GcCameraClient * gc_camera_client_new (GDBusConnection *connection, const gchar *path, GError **error);
+const gchar * gc_camera_client_get_path (GcCameraClient *client);
+
+G_END_DECLS
+
+#endif /* GC_CAMERA_CLIENT_H_ */
diff --git a/service/gc-camera-manager.c b/service/gc-camera-manager.c
new file mode 100644
index 0000000..94b18ea
--- /dev/null
+++ b/service/gc-camera-manager.c
@@ -0,0 +1,295 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gc-camera-manager.h"
+
+#include "gc-camera-client.h"
+#include "gc-client-monitor.h"
+
+typedef struct
+{
+    GDBusConnection *connection;
+    GStrv cameras;
+    GHashTable *clients;
+    guint client_ids;
+} GcCameraManagerPrivate;
+
+static void gc_camera_manager_manager_iface_init (GcManagerIface *iface);
+static void gc_camera_manager_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcCameraManager, gc_camera_manager,
+                         GC_TYPE_MANAGER_SKELETON,
+                         G_ADD_PRIVATE (GcCameraManager)
+                         G_IMPLEMENT_INTERFACE (GC_TYPE_MANAGER, gc_camera_manager_manager_iface_init)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gc_camera_manager_initable_iface_init))
+
+typedef struct
+{
+    GcCameraManager *manager;
+    GDBusMethodInvocation *invocation;
+    GcClientMonitor *monitor;
+} OnClientMonitorNewReadyData;
+
+enum
+{
+    PROP_0,
+    PROP_CONNECTION,
+    PROP_CAMERAS,
+    N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+static void
+on_client_monitor_new_ready (GObject *source_object,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+    OnClientMonitorNewReadyData *data;
+    GcClientMonitor *monitor;
+    GError *error = NULL;
+    GcCameraManager *self;
+    GcCameraManagerPrivate *priv;
+    gchar *path;
+    GcCameraClient *client;
+
+    data = (OnClientMonitorNewReadyData *)user_data;
+    monitor = gc_client_monitor_new_finish (result, &error);
+
+    if (monitor == NULL)
+    {
+        g_dbus_method_invocation_return_error (data->invocation,
+                                               G_DBUS_ERROR,
+                                               G_DBUS_ERROR_FAILED,
+                                               "%s",
+                                               error->message);
+        g_error_free (error);
+        g_slice_free (OnClientMonitorNewReadyData, data);
+
+        return;
+    }
+
+    self = data->manager;
+    priv = gc_camera_manager_get_instance_private (self);
+
+    path = g_strdup_printf ("/org/gnome/Camera/Client/%d", ++priv->client_ids);
+
+    /* FIXME: Do this properly when adding multiple client handling. */
+    g_application_hold (g_application_get_default ());
+
+    client = gc_camera_client_new (priv->connection, path, &error);
+
+    if (client == NULL)
+    {
+        goto error_out;
+    }
+
+    g_hash_table_insert (priv->clients, path, client);
+
+    gc_manager_complete_get_client (GC_MANAGER (self), data->invocation, path);
+
+    goto out;
+
+error_out:
+    g_dbus_method_invocation_return_error (data->invocation,
+                                           G_DBUS_ERROR,
+                                           G_DBUS_ERROR_FAILED,
+                                           "%s",
+                                           error->message);
+    g_error_free (error);
+
+out:
+    g_clear_object (&monitor);
+    g_slice_free (OnClientMonitorNewReadyData, data);
+    g_free (path);
+}
+
+static gboolean
+gc_camera_manager_handle_get_client (GcManager *manager,
+                                     GDBusMethodInvocation *invocation)
+{
+    GcCameraManager *self;
+    GcCameraManagerPrivate *priv;
+    const gchar *peer;
+    GcCameraClient *client;
+    OnClientMonitorNewReadyData *data;
+
+    self = GC_CAMERA_MANAGER (manager);
+    priv = gc_camera_manager_get_instance_private (self);
+
+    peer = g_dbus_method_invocation_get_sender (invocation);
+    client = g_hash_table_lookup (priv->clients, peer);
+
+    if (client != NULL)
+    {
+        const gchar *path;
+
+        path = gc_camera_client_get_path (client);
+        gc_manager_complete_get_client (manager, invocation, path);
+        return TRUE;
+    }
+
+    /* FIXME: Monitor when the associated peer disappears from the bus. */
+    data = g_slice_new (OnClientMonitorNewReadyData);
+    data->manager = self;
+    data->invocation = invocation;
+    gc_client_monitor_new_async (peer,
+                                 priv->connection,
+                                 NULL,
+                                 on_client_monitor_new_ready,
+                                 data);
+
+    return TRUE;
+}
+
+static void
+gc_camera_manager_finalize (GObject *object)
+{
+    GcCameraManagerPrivate *priv;
+
+    priv = gc_camera_manager_get_instance_private (GC_CAMERA_MANAGER (object));
+
+    g_clear_object (&priv->connection);
+    g_clear_pointer (&priv->clients, g_hash_table_unref);
+
+    G_OBJECT_CLASS (gc_camera_manager_parent_class)->finalize (object);
+}
+
+static void
+gc_camera_manager_get_property (GObject *object,
+                                guint prop_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+    GcCameraManagerPrivate *priv;
+
+    priv = gc_camera_manager_get_instance_private (GC_CAMERA_MANAGER (object));
+
+    switch (prop_id)
+    {
+        case PROP_CONNECTION:
+            g_value_set_object (value, priv->connection);
+            break;
+        case PROP_CAMERAS:
+            g_value_set_boxed (value, priv->cameras);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gc_camera_manager_set_property (GObject *object,
+                                guint prop_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+    GcCameraManagerPrivate *priv;
+
+    priv = gc_camera_manager_get_instance_private (GC_CAMERA_MANAGER (object));
+
+    switch (prop_id)
+    {
+        case PROP_CONNECTION:
+            priv->connection = g_value_dup_object (value);
+            break;
+        case PROP_CAMERAS:
+            g_strfreev (priv->cameras);
+            priv->cameras = g_value_dup_boxed (value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gc_camera_manager_class_init (GcCameraManagerClass *klass)
+{
+    GObjectClass *object_class;
+
+    object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = gc_camera_manager_finalize;
+    object_class->get_property = gc_camera_manager_get_property;
+    object_class->set_property = gc_camera_manager_set_property;
+
+    properties[PROP_CONNECTION] = g_param_spec_object ("connection",
+                                                       "Connection",
+                                                       "DBus Connection",
+                                                       G_TYPE_DBUS_CONNECTION,
+                                                       G_PARAM_READWRITE |
+                                                       G_PARAM_CONSTRUCT_ONLY |
+                                                       G_PARAM_STATIC_STRINGS);
+    g_object_class_install_property (object_class, PROP_CONNECTION,
+                                     properties[PROP_CONNECTION]);
+
+    properties[PROP_CAMERAS] = g_param_spec_boxed ("cameras",
+                                                   "Cameras",
+                                                   "List of connected cameras",
+                                                   G_TYPE_STRV,
+                                                   G_PARAM_READWRITE |
+                                                   G_PARAM_STATIC_STRINGS);
+    g_object_class_install_property (object_class, PROP_CAMERAS,
+                                     properties[PROP_CAMERAS]);
+}
+
+static void
+gc_camera_manager_init (GcCameraManager *self)
+{
+}
+
+static gboolean
+gc_camera_manager_initable_init (GInitable *initable,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+    GcCameraManagerPrivate *priv;
+
+    priv = gc_camera_manager_get_instance_private (GC_CAMERA_MANAGER (initable));
+
+    priv->clients = g_hash_table_new_full (g_str_hash,
+                                           g_str_equal,
+                                           g_free,
+                                           g_object_unref);
+
+    return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (initable),
+                                             priv->connection,
+                                             "/org/gnome/Camera/Manager",
+                                             error);
+}
+
+static void
+gc_camera_manager_manager_iface_init (GcManagerIface *iface)
+{
+    iface->handle_get_client = gc_camera_manager_handle_get_client;
+}
+
+static void
+gc_camera_manager_initable_iface_init (GInitableIface *iface)
+{
+    iface->init = gc_camera_manager_initable_init;
+}
+
+GcCameraManager *
+gc_camera_manager_new (GDBusConnection *connection, GError **error)
+{
+    return g_initable_new (GC_TYPE_CAMERA_MANAGER, NULL, error, "connection",
+                           connection, NULL);
+}
diff --git a/service/gc-camera-manager.h b/service/gc-camera-manager.h
new file mode 100644
index 0000000..00965c3
--- /dev/null
+++ b/service/gc-camera-manager.h
@@ -0,0 +1,45 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GC_CAMERA_MANAGER_H_
+#define GC_CAMERA_MANAGER_H_
+
+#include "camera.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+    /*< private >*/
+    GcManagerSkeleton parent_instance;
+} GcCameraManager;
+
+typedef struct
+{
+    GcManagerSkeletonClass parent_class;
+} GcCameraManagerClass;
+
+#define GC_TYPE_CAMERA_MANAGER (gc_camera_manager_get_type ())
+#define GC_CAMERA_MANAGER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GC_TYPE_CAMERA_MANAGER, 
GcCameraManager))
+
+GType gc_camera_manager_get_type (void);
+GcCameraManager * gc_camera_manager_new (GDBusConnection *connection, GError **error);
+
+G_END_DECLS
+
+#endif /* GC_CAMERA_MANAGER_H_ */
diff --git a/service/gc-chooser.c b/service/gc-chooser.c
new file mode 100644
index 0000000..9cbdb43
--- /dev/null
+++ b/service/gc-chooser.c
@@ -0,0 +1,141 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gc-chooser.h"
+
+#include <glib/gi18n.h>
+#include <clutter-gst/clutter-gst.h>
+#include <clutter-gtk/clutter-gtk.h>
+
+typedef struct
+{
+    GtkWidget *camera_embed;
+    ClutterActor *stage;
+    ClutterGstCamera *camera;
+    ClutterActor *camera_actor;
+    const GPtrArray *devices;
+} GcChooserPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GcChooser, gc_chooser, GTK_TYPE_DIALOG)
+
+static void
+on_gc_chooser_response (GtkDialog *dialog,
+                        gint response_id,
+                        gpointer user_data)
+{
+    switch (response_id)
+    {
+        default:
+            return;
+    }
+}
+
+static void
+set_default_camera_resolutions (GcChooser *chooser)
+{
+    GcChooserPrivate *priv;
+    guint i;
+
+    priv = gc_chooser_get_instance_private (chooser);
+
+    priv->devices = clutter_gst_camera_manager_get_camera_devices (clutter_gst_camera_manager_get_default 
());
+
+    for (i = 0; i < priv->devices->len; i++)
+    {
+        ClutterGstCameraDevice *device;
+
+        device = g_ptr_array_index (priv->devices, i);
+
+        clutter_gst_camera_device_set_capture_resolution (device, 640, 480);
+    }
+}
+
+static void
+add_default_camera_device (GcChooser *chooser)
+{
+    GcChooserPrivate *priv;
+    ClutterContent *ratio;
+
+    priv = gc_chooser_get_instance_private (chooser);
+
+    priv->camera = clutter_gst_camera_new ();
+    priv->camera_actor = clutter_actor_new ();
+
+    ratio = clutter_gst_aspectratio_new ();
+    clutter_gst_content_set_player (CLUTTER_GST_CONTENT (ratio),
+                                    CLUTTER_GST_PLAYER (priv->camera));
+
+    clutter_actor_set_content (priv->camera_actor, ratio);
+    clutter_actor_add_child (priv->stage, priv->camera_actor);
+
+    /* TODO: Use something other than the default camera device. */
+    set_default_camera_resolutions (chooser);
+
+    clutter_gst_player_set_playing (CLUTTER_GST_PLAYER (priv->camera), TRUE);
+    clutter_actor_show (priv->stage);
+}
+
+static void
+gc_chooser_class_init (GcChooserClass *klass)
+{
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+    gtk_widget_class_set_template_from_resource (widget_class,
+                                                 "/org/gnome/Camera/gc-chooser.ui");
+
+    gtk_widget_class_bind_template_child_private (widget_class, GcChooser,
+                                                  camera_embed);
+    gtk_widget_class_bind_template_callback (widget_class,
+                                             on_gc_chooser_response);
+}
+
+static void
+gc_chooser_init (GcChooser *chooser)
+{
+    GcChooserPrivate *priv;
+    GtkWidget *button;
+    ClutterColor stage_colour = { 0x00, 0x00, 0x00, 0x00 };
+
+    priv = gc_chooser_get_instance_private (chooser);
+
+    gtk_widget_init_template (GTK_WIDGET (chooser));
+
+    /* FIXME: Translatable strings. */
+    gtk_dialog_add_buttons (GTK_DIALOG (chooser), "Select",
+                            GTK_RESPONSE_ACCEPT, "Cancel", GTK_RESPONSE_CANCEL,
+                            NULL);
+
+    button = gtk_dialog_get_widget_for_response (GTK_DIALOG (chooser),
+                                                 GTK_RESPONSE_ACCEPT);
+    gtk_style_context_add_class (gtk_widget_get_style_context (button),
+                                 GTK_STYLE_CLASS_SUGGESTED_ACTION);
+
+    priv->stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->camera_embed));
+
+    clutter_actor_set_size (priv->stage, 640, 480);
+    clutter_stage_set_minimum_size (CLUTTER_STAGE (priv->stage), 320, 240);
+    clutter_actor_set_background_color (priv->stage, &stage_colour);
+
+    add_default_camera_device (chooser);
+}
+
+GtkWidget *
+gc_chooser_new (void)
+{
+    return g_object_new (GC_TYPE_CHOOSER, "use-header-bar", TRUE, NULL);
+}
diff --git a/service/gc-chooser.h b/service/gc-chooser.h
new file mode 100644
index 0000000..763f8fe
--- /dev/null
+++ b/service/gc-chooser.h
@@ -0,0 +1,46 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GC_CHOOSER_H_
+#define GC_CHOOSER_H_
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+    /*< private >*/
+    GtkDialog parent_instance;
+} GcChooser;
+
+typedef struct
+{
+    /*< private >*/
+    GtkDialogClass parent_class;
+} GcChooserClass;
+
+#define GC_TYPE_CHOOSER (gc_chooser_get_type ())
+#define GC_CHOOSER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GC_TYPE_CHOOSER, GcChooser))
+
+GType gc_chooser_get_type (void);
+GtkWidget * gc_chooser_new (void);
+
+G_END_DECLS
+
+#endif /* GC_CHOOSER_H_ */
diff --git a/service/gc-client-monitor.c b/service/gc-client-monitor.c
new file mode 100644
index 0000000..61ce70a
--- /dev/null
+++ b/service/gc-client-monitor.c
@@ -0,0 +1,311 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gc-client-monitor.h"
+
+typedef struct
+{
+    gchar *peer;
+    GDBusConnection *connection;
+    GDBusProxy *dbus_proxy;
+    guint watch_id;
+    guint32 user_id;
+} GcClientMonitorPrivate;
+
+static void gc_client_monitor_async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcClientMonitor,
+                         gc_client_monitor,
+                         G_TYPE_OBJECT,
+                         G_ADD_PRIVATE (GcClientMonitor)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, 
gc_client_monitor_async_initable_iface_init))
+
+enum
+{
+    PROP_0,
+    PROP_CONNECTION,
+    PROP_PEER,
+    N_PROPERTIES
+};
+
+enum
+{
+    PEER_VANISHED,
+    N_SIGNALS
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+static guint signals[N_SIGNALS];
+
+static void
+on_name_vanished (GDBusConnection *connection,
+                  const gchar *name,
+                  gpointer user_data)
+{
+    g_signal_emit (GC_CLIENT_MONITOR (user_data), signals[PEER_VANISHED], 0);
+}
+
+static void
+on_get_user_id_ready (GObject *source_object,
+                      GAsyncResult *result,
+                      gpointer user_data)
+{
+    GTask *task;
+    GcClientMonitor *self;
+    GcClientMonitorPrivate *priv;
+    GError *error = NULL;
+    GVariant *results = NULL;
+
+    task = G_TASK (user_data);
+    self = GC_CLIENT_MONITOR (g_task_get_source_object (task));
+    priv = gc_client_monitor_get_instance_private (self);
+
+    results = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
+                                        result,
+                                        &error);
+    if (results == NULL)
+    {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+
+        return;
+    }
+
+    g_assert (g_variant_n_children (results) > 0);
+    g_variant_get_child (results, 0, "u", &priv->user_id);
+    g_variant_unref (results);
+
+    priv->watch_id = g_bus_watch_name_on_connection (priv->connection,
+                                                     priv->peer,
+                                                     G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                                     NULL,
+                                                     on_name_vanished,
+                                                     self,
+                                                     NULL);
+
+    g_task_return_boolean (task, TRUE);
+
+    g_object_unref (task);
+}
+
+static void
+on_dbus_proxy_ready (GObject *source_object,
+                     GAsyncResult *result,
+                     gpointer user_data)
+{
+    GTask *task;
+    GcClientMonitor *self;
+    GcClientMonitorPrivate *priv;
+    GError *error = NULL;
+
+    task = G_TASK (user_data);
+    self = GC_CLIENT_MONITOR (g_task_get_source_object (task));
+    priv = gc_client_monitor_get_instance_private (self);
+
+    priv->dbus_proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+
+    if (priv->dbus_proxy == NULL)
+    {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    g_dbus_proxy_call (priv->dbus_proxy,
+                       "GetConnectionUnixUser",
+                       g_variant_new ("(s)", priv->peer),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       g_task_get_cancellable (task),
+                       on_get_user_id_ready,
+                       task);
+}
+
+static void
+gc_client_monitor_finalize (GObject *object)
+{
+    GcClientMonitorPrivate *priv;
+
+    priv = gc_client_monitor_get_instance_private (GC_CLIENT_MONITOR (object));
+
+    g_free (priv->peer);
+
+    G_OBJECT_CLASS (gc_client_monitor_parent_class)->finalize (object);
+}
+
+static void
+gc_client_monitor_get_property (GObject *object,
+                                guint prop_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+    GcClientMonitorPrivate *priv;
+
+    priv = gc_client_monitor_get_instance_private (GC_CLIENT_MONITOR (object));
+
+    switch (prop_id)
+    {
+        case PROP_CONNECTION:
+            g_value_set_object (value, priv->connection);
+            break;
+        case PROP_PEER:
+            g_value_set_string (value, priv->peer);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gc_client_monitor_set_property (GObject *object,
+                                guint prop_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+    GcClientMonitorPrivate *priv;
+
+    priv = gc_client_monitor_get_instance_private (GC_CLIENT_MONITOR (object));
+
+    switch (prop_id)
+    {
+        case PROP_CONNECTION:
+            priv->connection = g_value_dup_object (value);
+            break;
+        case PROP_PEER:
+            g_free (priv->peer);
+            priv->peer = g_value_dup_string (value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gc_client_monitor_class_init (GcClientMonitorClass *klass)
+{
+    GObjectClass *object_class;
+
+    object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = gc_client_monitor_finalize;
+    object_class->get_property = gc_client_monitor_get_property;
+    object_class->set_property = gc_client_monitor_set_property;
+
+    properties[PROP_CONNECTION] = g_param_spec_object ("connection",
+                                                       "Connection",
+                                                       "DBus Connection",
+                                                       G_TYPE_DBUS_CONNECTION,
+                                                       G_PARAM_READWRITE |
+                                                       G_PARAM_CONSTRUCT_ONLY |
+                                                       G_PARAM_STATIC_STRINGS);
+
+    properties[PROP_PEER] = g_param_spec_string ("peer",
+                                                 "Peer",
+                                                 "D-Bus object path of peer to monitor",
+                                                 NULL,
+                                                 G_PARAM_READWRITE |
+                                                 G_PARAM_CONSTRUCT_ONLY |
+                                                 G_PARAM_STATIC_STRINGS);
+
+    g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+}
+
+static void
+gc_client_monitor_init (GcClientMonitor *self)
+{
+}
+
+static void
+gc_client_monitor_async_initable_init_async (GAsyncInitable *initable,
+                                             gint io_priority,
+                                             GCancellable *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer user_data)
+{
+    GTask *task;
+
+    task = g_task_new (initable, cancellable, callback, user_data);
+
+    g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+                              G_DBUS_PROXY_FLAGS_NONE,
+                              NULL,
+                              "org.freedesktop.DBus",
+                              "/org/freedesktop/DBus",
+                              "org.freedesktop.DBus",
+                              cancellable,
+                              on_dbus_proxy_ready,
+                              task);
+}
+
+static gboolean
+gc_client_monitor_async_initable_init_finish (GAsyncInitable *initable,
+                                              GAsyncResult *result,
+                                              GError **error)
+{
+    return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+gc_client_monitor_async_initable_iface_init (GAsyncInitableIface *iface)
+{
+    iface->init_async = gc_client_monitor_async_initable_init_async;
+    iface->init_finish = gc_client_monitor_async_initable_init_finish;
+}
+
+void
+gc_client_monitor_new_async (const gchar *peer,
+                             GDBusConnection *connection,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+    g_async_initable_new_async (GC_TYPE_CLIENT_MONITOR,
+                                G_PRIORITY_DEFAULT,
+                                cancellable,
+                                callback,
+                                user_data,
+                                "connection",
+                                connection,
+                                "peer",
+                                peer,
+                                NULL);
+}
+
+GcClientMonitor *
+gc_client_monitor_new_finish (GAsyncResult *result, GError **error)
+{
+    GObject *object;
+    GObject *source_object;
+
+    source_object = g_async_result_get_source_object (result);
+    object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+                                          result,
+                                          error);
+    g_object_unref (source_object);
+
+    if (object != NULL)
+    {
+        return GC_CLIENT_MONITOR (object);
+    }
+    else
+    {
+        return NULL;
+    }
+}
diff --git a/service/gc-client-monitor.h b/service/gc-client-monitor.h
new file mode 100644
index 0000000..2da6f3b
--- /dev/null
+++ b/service/gc-client-monitor.h
@@ -0,0 +1,48 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GC_CLIENT_MONITOR_H_
+#define GC_CLIENT_MONITOR_H_
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+    /*< private >*/
+    GObject parent_instance;
+} GcClientMonitor;
+
+typedef struct
+{
+    GObjectClass parent_class;
+
+    void (* peer_vanished) (GcClientMonitor *self);
+} GcClientMonitorClass;
+
+#define GC_TYPE_CLIENT_MONITOR (gc_client_monitor_get_type ())
+#define GC_CLIENT_MONITOR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GC_TYPE_CLIENT_MONITOR, 
GcClientMonitor))
+
+GType gc_client_monitor_get_type (void);
+void gc_client_monitor_new_async (const gchar *bus_name, GDBusConnection *connection, GCancellable 
*cancellable, GAsyncReadyCallback callback, gpointer user_data);
+GcClientMonitor * gc_client_monitor_new_finish (GAsyncResult *result, GError **error);
+
+G_END_DECLS
+
+#endif /* GC_CLIENT_MONITOR_H_ */
diff --git a/service/gc-main.c b/service/gc-main.c
new file mode 100644
index 0000000..71a301d
--- /dev/null
+++ b/service/gc-main.c
@@ -0,0 +1,45 @@
+/*
+ *  GNOME Camera - Access camera devices on a system via D-Bus
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <cheese/cheese-gtk.h>
+#include <clutter-gtk/clutter-gtk.h>
+
+#include "gc-application.h"
+
+int
+main (int argc,
+      char *argv[])
+{
+    GApplication *application;
+    int status;
+
+    if (!cheese_gtk_init (&argc, &argv))
+    {
+        return 1;
+    }
+
+    g_set_prgname (PACKAGE_TARNAME);
+    application = G_APPLICATION (gc_application_new ());
+    status = g_application_run (application, argc, argv);
+    g_object_unref (application);
+
+    return status;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]