Commit 2bad02a5 authored by Jens Georg's avatar Jens Georg

faces: Add private DBus communication

parent 3f0f5180
......@@ -103,6 +103,11 @@ endif
if get_option('face-detection')
add_global_arguments(['--define=ENABLE_FACES'], language : 'vala')
if get_option('face_detection_helper_bus') == 'private'
add_global_arguments(['--define=FACEDETECT_BUS_PRIVATE'], language : 'vala')
endif
if get_option('face-detection-helper')
subproject('shotwell-facedetect')
endif
......
......@@ -8,4 +8,5 @@ option('udev', type: 'boolean', value : 'true', description: 'Enable or disable
option('install-apport-hook', type : 'boolean', value : 'true', description: 'Enable Ubuntu apport hook')
option('face-detection', type:'boolean', value:false, description: 'Enable face detection and recognition features')
option('face-detection-helper', type : 'boolean', value : 'true', description : 'If face-detection is enabled, build the external helper tool')
option('face_detection_helper_bus', type:'combo', choices: ['private', 'session'], value : 'session', description: 'Which DBus bus to use for external helper tool')
option('fatal_warnings', type:'boolean', value:false)
......@@ -263,7 +263,7 @@ class AppDirs {
File? install_dir = get_install_dir();
return (install_dir != null) ? install_dir.get_child("share").get_child("shotwell")
: get_exec_dir();
: get_lib_dir();
}
#endif
......@@ -332,7 +332,7 @@ class AppDirs {
#if ENABLE_FACES
public static File get_facedetect_bin() {
const string filename = "shotwell-facedetect";
File f = AppDirs.get_libexec_dir().get_parent().get_child("facedetect").get_child (filename);
File f = AppDirs.get_libexec_dir().get_parent().get_child("subprojects").get_child(filename).get_child (filename);
if (!f.query_exists()) {
f = AppDirs.get_libexec_dir().get_child("shotwell").get_child(filename);
}
......@@ -340,7 +340,8 @@ class AppDirs {
}
public static File get_haarcascade_file() {
File f = File.new_for_path(AppDirs.get_exec_dir().get_parent().get_parent().get_child("facedetect").get_child("facedetect-haarcascade.xml").get_path());
const string filename = "facedetect-haarcascade.xml";
var f = AppDirs.get_resources_dir().get_parent().get_child("subprojects").get_child("shotwell-facedetect").get_child (filename);
if (f.query_exists()) {//testing meson builddir
return f;
}
......
......@@ -33,7 +33,7 @@ public struct FaceRect {
}
[DBus (name = "org.gnome.Shotwell.Faces1")]
public interface FaceDetectInterface : Object {
public interface FaceDetectInterface : DBusProxy {
public abstract FaceRect[] detect_faces(string inputName, string cascadeName, double scale, bool infer)
throws IOError, DBusError;
public abstract bool load_net(string netFile)
......@@ -53,6 +53,11 @@ public class FaceDetect {
public static FaceDetectInterface interface;
#if FACEDETECT_BUS_PRIVATE
private static GLib.DBusServer dbus_server;
private static Subprocess process;
#endif
public static void create_interface(DBusConnection connection, string bus_name, string owner) {
if (bus_name == DBUS_NAME) {
message("Dbus name %s available", bus_name);
......@@ -64,8 +69,66 @@ public class FaceDetect {
connected = false;
}
private static bool on_new_connection(DBusServer server, DBusConnection connection) {
try {
interface = connection.get_proxy_sync(null, DBUS_PATH,
DBusProxyFlags.DO_NOT_LOAD_PROPERTIES
| DBusProxyFlags.DO_NOT_CONNECT_SIGNALS,
null);
Idle.add(() => {
try {
if (interface.load_net(net_file))
connected = true;
else {
AppWindow.error_message(ERROR_MESSAGE);
}
} catch (Error error) {
critical("Failed to call load_net: %s", error.message);
AppWindow.error_message(ERROR_MESSAGE);
}
return false;
});
return true;
} catch (Error error) {
critical("Failed to create interface for face detect: %s", error.message);
AppWindow.error_message(ERROR_MESSAGE);
return false;
}
}
public static void init(string net_file) {
FaceDetect.net_file = net_file;
#if FACEDETECT_BUS_PRIVATE
var address = "unix:tmpdir=%s".printf(Environment.get_tmp_dir());
var observer = new DBusAuthObserver();
observer.authorize_authenticated_peer.connect((stream, credentials) => {
debug("Observer trying to authorize for %s", credentials.to_string());
if (credentials == null)
return false;
try {
if (!credentials.is_same_user(new Credentials()))
return false;
return true;
} catch (Error error) {
return false;
}
});
try {
dbus_server = new GLib.DBusServer.sync(address, DBusServerFlags.NONE, DBus.generate_guid(), observer, null);
dbus_server.new_connection.connect(on_new_connection);
dbus_server.start();
process = new Subprocess(SubprocessFlags.NONE, AppDirs.get_facedetect_bin().get_path(),
"--address=" + dbus_server.get_client_address());
} catch (Error error) {
warning("Failed to create private DBus server: %s", error.message);
AppWindow.error_message(ERROR_MESSAGE);
}
#else
Bus.watch_name(BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE,
create_interface, interface_gone);
try {
......@@ -78,16 +141,7 @@ public class FaceDetect {
AppWindow.error_message(ERROR_MESSAGE);
}
connected = true;
#endif
}
public static double dot_product(double[] vec1, double[] vec2) {
if (vec1.length != vec2.length) {
return 0;
}
double ret = 0;
for (var i = 0; i < vec1.length; i++) {
ret += vec1[i] * vec2[i];
}
return ret;
}
}
......@@ -900,6 +900,18 @@ public class FacesTool : EditingTools.EditingTool {
}
}
private double dot_product(double[] vec1, double[] vec2) {
if (vec1.length != vec2.length) {
return 0;
}
double ret = 0;
for (var i = 0; i < vec1.length; i++) {
ret += vec1[i] * vec2[i];
}
return ret;
}
private Face? get_face_match(FaceShape face_shape, double threshold) {
Gee.List<FaceLocationRow?> face_vecs;
try {
......@@ -915,7 +927,7 @@ public class FacesTool : EditingTools.EditingTool {
string[] vec_str = row.vec.split(",");
double[] vec = {};
foreach (var d in vec_str) vec += double.parse(d);
double product = FaceDetect.dot_product(face_shape.get_face_vec(), vec[0:128]);
double product = dot_product(face_shape.get_face_vec(), vec[0:128]);
if (product > max_product) {
max_product = product;
guess_id = row.face_id;
......
project('shotwell-facedetect', ['c', 'cpp'])
project('shotwell-facedetect', ['c', 'cpp'], default_options : ['cpp_std=c++14'])
gnome = import('gnome')
facedetect_dep = dependency('opencv', version : ['>= 2.3.0'], required : true)
cpp = meson.get_compiler('cpp')
......
......@@ -11,6 +11,9 @@
#include "shotwell-facedetect.hpp"
#include "dbus-interface.h"
const char* FACEDETECT_INTERFACE_NAME = "org.gnome.Shotwell.Faces1";
const char* FACEDETECT_PATH = "/org/gnome/shotwell/faces";
// DBus binding functions
static gboolean on_handle_detect_faces(ShotwellFaces1 *object,
GDBusMethodInvocation *invocation,
......@@ -84,21 +87,25 @@ static gboolean on_handle_terminate(ShotwellFaces1 *object,
static void on_name_acquired(GDBusConnection *connection,
const gchar *name, gpointer user_data) {
ShotwellFaces1 *interface;
GError *error;
interface = shotwell_faces1_skeleton_new();
g_debug("Got name %s", name);
g_signal_connect(interface, "handle-detect-faces", G_CALLBACK (on_handle_detect_faces), NULL);
ShotwellFaces1 *interface = shotwell_faces1_skeleton_new();
g_signal_connect(interface, "handle-detect-faces", G_CALLBACK (on_handle_detect_faces), nullptr);
g_signal_connect(interface, "handle-terminate", G_CALLBACK (on_handle_terminate), user_data);
g_signal_connect(interface, "handle-load-net", G_CALLBACK (on_handle_load_net), NULL);
g_signal_connect(interface, "handle-face-to-vec", G_CALLBACK (on_handle_face_to_vec), NULL);
error = NULL;
g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(interface), connection, "/org/gnome/shotwell/faces", &error);
g_signal_connect(interface, "handle-load-net", G_CALLBACK (on_handle_load_net), nullptr);
g_signal_connect(interface, "handle-face-to-vec", G_CALLBACK (on_handle_face_to_vec), nullptr);
GError *error = nullptr;
g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(interface), connection, FACEDETECT_PATH, &error);
if (error != nullptr) {
g_print("Failed to export interface: %s", error->message);
g_clear_error(&error);
}
}
static void on_name_lost(GDBusConnection *connection,
const gchar *name, gpointer user_data) {
if (connection == NULL) {
if (connection == nullptr) {
g_debug("Unable to establish connection for name %s", name);
} else {
g_debug("Connection for name %s disconnected", name);
......@@ -106,11 +113,90 @@ static void on_name_lost(GDBusConnection *connection,
g_main_loop_quit((GMainLoop *)user_data);
}
static char* address = nullptr;
static GOptionEntry entries[] = {
{ "address", 'a', 0, G_OPTION_ARG_STRING, &address, "Use private DBus ADDRESS instead of session", "ADDRESS" },
{ nullptr }
};
static gboolean
on_authorize_authenticated_peer (GIOStream *iostream,
GCredentials *credentials,
gpointer user_data)
{
GCredentials *own_credentials = nullptr;
gboolean ret_val = FALSE;
g_debug("Authorizing peer with credentials %s\n", g_credentials_to_string (credentials));
if (credentials == nullptr)
goto out;
own_credentials = g_credentials_new ();
{
GError* error = nullptr;
if (!g_credentials_is_same_user (credentials, own_credentials, &error))
{
g_warning ("Unable to authorize peer: %s", error->message);
g_clear_error (&error);
goto out;
}
}
ret_val = TRUE;
out:
g_clear_object (&own_credentials);
return ret_val;
}
int main(int argc, char **argv) {
GMainLoop *loop;
loop = g_main_loop_new (NULL, FALSE);
g_bus_own_name(G_BUS_TYPE_SESSION, "org.gnome.Shotwell.Faces1", G_BUS_NAME_OWNER_FLAGS_NONE, NULL,
on_name_acquired, on_name_lost, loop, NULL);
GError *error = nullptr;
GOptionContext *context;
context = g_option_context_new ("- Shotwell face detection helper service");
g_option_context_add_main_entries (context, entries, "shotwell");
if (!g_option_context_parse (context, &argc, &argv, &error)) {
g_print ("Failed to parse options: %s\n", error->message);
exit(1);
}
loop = g_main_loop_new (nullptr, FALSE);
// We are running on the sesion bus
if (address == nullptr) {
g_debug("Starting %s on G_BUS_TYPE_SESSION", argv[0]);
g_bus_own_name(G_BUS_TYPE_SESSION, FACEDETECT_INTERFACE_NAME, G_BUS_NAME_OWNER_FLAGS_NONE,
nullptr, on_name_acquired, on_name_lost, loop, nullptr);
} else {
g_debug("Starting %s on %s", argv[0], address);
GDBusAuthObserver *observer = g_dbus_auth_observer_new ();
g_signal_connect (G_OBJECT (observer), "authorize-authenticated-peer",
G_CALLBACK (on_authorize_authenticated_peer), nullptr);
GDBusConnection *connection = g_dbus_connection_new_for_address_sync (address,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
observer,
nullptr,
&error);
if (connection != nullptr)
on_name_acquired(connection, FACEDETECT_INTERFACE_NAME, loop);
}
if (error != nullptr) {
g_error("Failed to get connection on %s bus: %s",
address == nullptr ? "session" : "private",
error->message);
}
g_main_loop_run (loop);
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment