/*
* Copyright © 2017 Red Hat, Inc
*
* This program 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, see .
*
* Authors:
* Christian J. Kellner
*/
#include "bolt-proxy.h"
#include "bolt-enums.h"
#include "bolt-error.h"
#include "bolt-names.h"
#include "bolt-str.h"
static void bolt_proxy_handle_props_changed (GDBusProxy *proxy,
GVariant *changed_properties,
GStrv invalidated_properties,
gpointer user_data);
static void bolt_proxy_handle_dbus_signal (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *params,
gpointer user_data);
G_DEFINE_TYPE (BoltProxy, bolt_proxy, G_TYPE_DBUS_PROXY);
static void
bolt_proxy_constructed (GObject *object)
{
G_OBJECT_CLASS (bolt_proxy_parent_class)->constructed (object);
g_signal_connect (object, "g-properties-changed",
G_CALLBACK (bolt_proxy_handle_props_changed), object);
g_signal_connect (object, "g-signal",
G_CALLBACK (bolt_proxy_handle_dbus_signal), object);
}
static const BoltProxySignal *
bolt_proxy_get_dbus_signals (guint *n)
{
*n = 0;
return NULL;
}
static void
bolt_proxy_class_init (BoltProxyClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructed = bolt_proxy_constructed;
klass->get_dbus_signals = bolt_proxy_get_dbus_signals;
}
static void
bolt_proxy_init (BoltProxy *object)
{
}
static void
bolt_proxy_handle_props_changed (GDBusProxy *proxy,
GVariant *changed_properties,
GStrv invalidated_properties,
gpointer user_data)
{
g_autoptr(GVariantIter) iter = NULL;
gboolean handled;
GParamSpec **pp;
const char *key;
guint n;
pp = g_object_class_list_properties (G_OBJECT_GET_CLASS (proxy), &n);
g_variant_get (changed_properties, "a{sv}", &iter);
while (g_variant_iter_next (iter, "{&sv}", &key, NULL))
{
handled = FALSE;
for (guint i = 0; !handled && i < n; i++)
{
GParamSpec *pspec = pp[i];
const char *nick;
const char *name;
nick = g_param_spec_get_nick (pspec);
name = g_param_spec_get_name (pspec);
handled = bolt_streq (nick, key);
if (handled)
g_object_notify (G_OBJECT (user_data), name);
}
}
g_free (pp);
}
static void
bolt_proxy_handle_dbus_signal (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *params,
gpointer user_data)
{
const BoltProxySignal *ps;
guint n;
if (signal_name == NULL)
return;
ps = BOLT_PROXY_GET_CLASS (proxy)->get_dbus_signals (&n);
for (guint i = 0; i < n; i++)
{
const BoltProxySignal *sig = &ps[i];
if (g_str_equal (sig->theirs, signal_name))
{
sig->handle (G_OBJECT (proxy), proxy, params);
break;
}
}
}
/* public methods */
gboolean
bolt_proxy_get_dbus_property (GObject *proxy,
GParamSpec *spec,
GValue *value)
{
g_autoptr(GVariant) val = NULL;
const GVariantType *vt;
gboolean handled = FALSE;
const char *nick;
nick = g_param_spec_get_nick (spec);
val = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), nick);
if (val == NULL)
return FALSE;
vt = g_variant_get_type (val);
if (g_variant_type_equal (vt, G_VARIANT_TYPE_STRING) &&
G_IS_PARAM_SPEC_ENUM (spec))
{
GParamSpecEnum *enum_spec = G_PARAM_SPEC_ENUM (spec);
GEnumValue *ev;
const char *str;
str = g_variant_get_string (val, NULL);
ev = g_enum_get_value_by_nick (enum_spec->enum_class, str);
handled = ev != NULL;
if (handled)
g_value_set_enum (value, ev->value);
else
g_value_set_enum (value, enum_spec->default_value);
}
else if (g_variant_type_equal (vt, G_VARIANT_TYPE_STRING) &&
G_IS_PARAM_SPEC_FLAGS (spec))
{
GParamSpecFlags *flags_spec = G_PARAM_SPEC_FLAGS (spec);
GFlagsClass *flags_class = flags_spec->flags_class;
const char *str;
guint v;
str = g_variant_get_string (val, NULL);
handled = bolt_flags_class_from_string (flags_class, str, &v, NULL);
if (handled)
g_value_set_flags (value, v);
else
g_value_set_flags (value, flags_spec->default_value);
}
else
{
g_dbus_gvariant_to_gvalue (val, value);
}
return handled;
}
gboolean
bolt_proxy_has_name_owner (BoltProxy *proxy)
{
const char *name_owner;
g_return_val_if_fail (proxy != NULL, FALSE);
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (proxy));
return name_owner != NULL;
}
static GParamSpec *
find_property (BoltProxy *proxy,
const char *name,
GError **error)
{
GParamSpec *res = NULL;
GParamSpec **pp;
guint n;
pp = g_object_class_list_properties (G_OBJECT_GET_CLASS (proxy), &n);
for (guint i = 0; i < n; i++)
{
GParamSpec *pspec = pp[i];
if (bolt_streq (pspec->name, name))
{
res = pspec;
break;
}
}
if (pp == NULL)
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY,
"could not find property '%s'", name);
g_free (pp);
return res;
}
static GVariant *
bolt_proxy_get_cached_property (BoltProxy *proxy,
const char *name)
{
const char *bus_name = NULL;
GParamSpec *pspec;
GVariant *var;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), NULL);
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (proxy), name);
if (pspec == NULL)
return NULL;
bus_name = g_param_spec_get_nick (pspec);
var = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), bus_name);
return var;
}
gboolean
bolt_proxy_get_property_bool (BoltProxy *proxy,
const char *name,
gboolean *value)
{
g_autoptr(GVariant) var = NULL;
var = bolt_proxy_get_cached_property (proxy, name);
if (var == NULL)
return FALSE;
else if (value)
*value = g_variant_get_boolean (var);
return TRUE;
}
gboolean
bolt_proxy_get_property_enum (BoltProxy *proxy,
const char *name,
gint *value)
{
g_autoptr(GVariant) var = NULL;
const char *str = NULL;
const char *bus_name = NULL;
GParamSpec *pspec;
GEnumValue *ev;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (proxy), name);
if (pspec == NULL)
return FALSE;
bus_name = g_param_spec_get_nick (pspec);
var = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), bus_name);
if (var == NULL)
return FALSE;
str = g_variant_get_string (var, NULL);
if (str == NULL)
return FALSE;
ev = g_enum_get_value_by_nick (G_PARAM_SPEC_ENUM (pspec)->enum_class, str);
if (ev == NULL)
return FALSE;
if (value)
*value = ev->value;
return TRUE;
}
gboolean
bolt_proxy_get_property_flags (BoltProxy *proxy,
const char *name,
guint *value)
{
g_autoptr(GVariant) var = NULL;
const char *str = NULL;
const char *bus_name = NULL;
GFlagsClass *flags_class;
GParamSpec *pspec;
guint v;
gboolean ok;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (proxy), name);
if (pspec == NULL || !G_IS_PARAM_SPEC_FLAGS (pspec))
return FALSE;
bus_name = g_param_spec_get_nick (pspec);
var = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), bus_name);
if (var == NULL)
return FALSE;
str = g_variant_get_string (var, NULL);
if (str == NULL)
return FALSE;
flags_class = G_PARAM_SPEC_FLAGS (pspec)->flags_class;
ok = bolt_flags_class_from_string (flags_class, str, &v, NULL);
if (ok && value)
*value = v;
return ok;
}
gboolean
bolt_proxy_get_property_uint32 (BoltProxy *proxy,
const char *name,
guint *value)
{
g_autoptr(GVariant) var = NULL;
var = bolt_proxy_get_cached_property (proxy, name);
if (var == NULL)
return FALSE;
else if (value)
*value = g_variant_get_uint32 (var);
return TRUE;
}
gboolean
bolt_proxy_get_property_int64 (BoltProxy *proxy,
const char *name,
gint64 *value)
{
g_autoptr(GVariant) var = NULL;
var = bolt_proxy_get_cached_property (proxy, name);
if (var == NULL)
return FALSE;
else if (value)
*value = g_variant_get_int64 (var);
return TRUE;
}
gboolean
bolt_proxy_get_property_uint64 (BoltProxy *proxy,
const char *name,
guint64 *value)
{
g_autoptr(GVariant) var = NULL;
var = bolt_proxy_get_cached_property (proxy, name);
if (var == NULL)
return FALSE;
else if (value)
*value = g_variant_get_uint64 (var);
return TRUE;
}
const char *
bolt_proxy_get_property_string (BoltProxy *proxy,
const char *name)
{
g_autoptr(GVariant) var = NULL;
const char *val = NULL;
var = bolt_proxy_get_cached_property (proxy, name);
if (var != NULL)
val = g_variant_get_string (var, NULL);
if (val && *val == '\0')
val = NULL;
return val;
}
gboolean
bolt_proxy_set_property (BoltProxy *proxy,
const char *name,
GVariant *value,
GCancellable *cancellable,
GError **error)
{
GParamSpec *pp;
const char *iface;
gboolean ok = FALSE;
GVariant *res;
pp = find_property (proxy, name, NULL);
if (pp != NULL)
name = g_param_spec_get_nick (pp);
iface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (proxy));
res = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
"org.freedesktop.DBus.Properties.Set",
g_variant_new ("(ssv)",
iface,
name,
value),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (res)
{
g_variant_unref (res);
ok = TRUE;
}
return ok;
}
void
bolt_proxy_set_property_async (BoltProxy *proxy,
const char *name,
GVariant *value,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GParamSpec *pp;
const char *iface;
pp = find_property (proxy, name, NULL);
if (pp != NULL)
name = g_param_spec_get_nick (pp);
iface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (proxy));
g_dbus_proxy_call (G_DBUS_PROXY (proxy),
"org.freedesktop.DBus.Properties.Set",
g_variant_new ("(ssv)",
iface,
name,
value),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
callback,
user_data);
}
gboolean
bolt_proxy_set_property_finish (GAsyncResult *res,
GError **error)
{
BoltProxy *proxy;
GVariant *val = NULL;
proxy = (BoltProxy *) g_async_result_get_source_object (res);
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error);
if (val == NULL)
return FALSE;
g_variant_unref (val);
return TRUE;
}