diff options
author | Milan Crha <mcrha@redhat.com> | 2014-06-05 01:46:25 +0800 |
---|---|---|
committer | Milan Crha <mcrha@redhat.com> | 2014-06-05 01:46:25 +0800 |
commit | 2f3fbdd6c6ff42a6c71ebe1d1d78108affe59d0f (patch) | |
tree | fd60a103158c3cfdbbce197feca07d7fea008a3d /e-util/e-misc-utils.c | |
parent | 668745ff49d8f312c7d419a691cdab3e128ded06 (diff) | |
download | gsoc2013-evolution-2f3fbdd6c6ff42a6c71ebe1d1d78108affe59d0f.tar gsoc2013-evolution-2f3fbdd6c6ff42a6c71ebe1d1d78108affe59d0f.tar.gz gsoc2013-evolution-2f3fbdd6c6ff42a6c71ebe1d1d78108affe59d0f.tar.bz2 gsoc2013-evolution-2f3fbdd6c6ff42a6c71ebe1d1d78108affe59d0f.tar.lz gsoc2013-evolution-2f3fbdd6c6ff42a6c71ebe1d1d78108affe59d0f.tar.xz gsoc2013-evolution-2f3fbdd6c6ff42a6c71ebe1d1d78108affe59d0f.tar.zst gsoc2013-evolution-2f3fbdd6c6ff42a6c71ebe1d1d78108affe59d0f.zip |
Ignore false GObject property change notifications
This is related to bug 698275, which did not cover all cases.
The problem here is that the dconf can in certain situation claim
that everything changed (path "/" changed), which GSettingsBinding
propagates to a GObject property unconditionally and GObject's
property setter (g_object_set_property()) also notifies about
the property change unconditionally, despite the real descendant
property setter properly checks for the value change. After all
these false notifications a callback on "notify" signal is called
and possibly an expensive operation is run.
Checking whether the value really changed helps in performance, for
which were added new e-util functions:
e_signal_connect_notify()
e_signal_connect_notify_after()
e_signal_connect_notify_swapped()
e_signal_connect_notify_object()
which have the same prototype as their GLib counterparts, but they allow
only "notify::..." signals and they test whether the value really changed
before they call the registered callback.
Diffstat (limited to 'e-util/e-misc-utils.c')
-rw-r--r-- | e-util/e-misc-utils.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/e-util/e-misc-utils.c b/e-util/e-misc-utils.c index 3a52292af4..f7be7af5ac 100644 --- a/e-util/e-misc-utils.c +++ b/e-util/e-misc-utils.c @@ -2256,3 +2256,239 @@ e_binding_bind_object_text_property (gpointer source, e_binding_transform_text_non_null, NULL, NULL); } + +typedef struct _EConnectNotifyData { + GConnectFlags flags; + GValue *old_value; + + GCallback c_handler; + gpointer user_data; +} EConnectNotifyData; + +static EConnectNotifyData * +e_connect_notify_data_new (GCallback c_handler, + gpointer user_data, + guint32 connect_flags) +{ + EConnectNotifyData *connect_data; + + connect_data = g_new0 (EConnectNotifyData, 1); + connect_data->flags = connect_flags; + connect_data->c_handler = c_handler; + connect_data->user_data = user_data; + + return connect_data; +} + +static void +e_connect_notify_data_free (EConnectNotifyData *data) +{ + if (!data) + return; + + if (data->old_value) { + g_value_unset (data->old_value); + g_free (data->old_value); + } + g_free (data); +} + +static gboolean +e_value_equal (GValue *value1, + GValue *value2) +{ + if (value1 == value2) + return TRUE; + + if (!value1 || !value2) + return FALSE; + + #define testType(_uc,_lc) G_STMT_START { \ + if (G_VALUE_HOLDS_ ## _uc (value1)) \ + return g_value_get_ ## _lc (value1) == g_value_get_ ## _lc (value2); \ + } G_STMT_END + + testType (BOOLEAN, boolean); + testType (BOXED, boxed); + testType (CHAR, schar); + testType (DOUBLE, double); + testType (ENUM, enum); + testType (FLAGS, flags); + testType (FLOAT, float); + testType (GTYPE, gtype); + testType (INT, int); + testType (INT64, int64); + testType (LONG, long); + testType (OBJECT, object); + testType (POINTER, pointer); + testType (UCHAR, uchar); + testType (UINT, uint); + testType (UINT64, uint64); + testType (ULONG, ulong); + + #undef testType + + if (G_VALUE_HOLDS_PARAM (value1)) { + GParamSpec *param1, *param2; + + param1 = g_value_get_param (value1); + param2 = g_value_get_param (value2); + + return param1 && param2 && + g_strcmp0 (param1->name, param2->name) == 0 && + param1->flags == param2->flags && + param1->value_type == param2->value_type && + param1->owner_type == param2->owner_type; + } else if (G_VALUE_HOLDS_STRING (value1)) { + const gchar *string1, *string2; + + string1 = g_value_get_string (value1); + string2 = g_value_get_string (value2); + + return g_strcmp0 (string1, string2) == 0; + } else if (G_VALUE_HOLDS_VARIANT (value1)) { + GVariant *variant1, *variant2; + + variant1 = g_value_get_variant (value1); + variant2 = g_value_get_variant (value2); + + return variant1 == variant2 || + (variant1 && variant2 && g_variant_equal (variant1, variant2)); + } + + return FALSE; +} + +static void +e_signal_connect_notify_cb (gpointer instance, + GParamSpec *param, + gpointer user_data) +{ + EConnectNotifyData *connect_data = user_data; + GValue *value; + + g_return_if_fail (connect_data != NULL); + + value = g_new0 (GValue, 1); + g_value_init (value, param->value_type); + g_object_get_property (instance, param->name, value); + + if (!e_value_equal (connect_data->old_value, value)) { + typedef void (* NotifyCBType) (gpointer instance, GParamSpec *param, gpointer user_data); + NotifyCBType c_handler = (NotifyCBType) connect_data->c_handler; + + if (connect_data->old_value) { + g_value_unset (connect_data->old_value); + g_free (connect_data->old_value); + } + connect_data->old_value = value; + + if (connect_data->flags == G_CONNECT_SWAPPED) { + c_handler (connect_data->user_data, param, instance); + } else { + c_handler (instance, param, connect_data->user_data); + } + } else { + g_value_unset (value); + g_free (value); + } +} + +gulong +e_signal_connect_notify (gpointer instance, + const gchar *notify_name, + GCallback c_handler, + gpointer user_data) +{ + EConnectNotifyData *connect_data; + + g_return_val_if_fail (g_str_has_prefix (notify_name, "notify::"), 0); + + connect_data = e_connect_notify_data_new (c_handler, user_data, 0); + + return g_signal_connect_data (instance, + notify_name, + G_CALLBACK (e_signal_connect_notify_cb), + connect_data, + (GClosureNotify) e_connect_notify_data_free, + 0); +} + +gulong +e_signal_connect_notify_after (gpointer instance, + const gchar *notify_name, + GCallback c_handler, + gpointer user_data) +{ + EConnectNotifyData *connect_data; + + g_return_val_if_fail (g_str_has_prefix (notify_name, "notify::"), 0); + + connect_data = e_connect_notify_data_new (c_handler, user_data, G_CONNECT_AFTER); + + return g_signal_connect_data (instance, + notify_name, + G_CALLBACK (e_signal_connect_notify_cb), + connect_data, + (GClosureNotify) e_connect_notify_data_free, + G_CONNECT_AFTER); +} + +gulong +e_signal_connect_notify_swapped (gpointer instance, + const gchar *notify_name, + GCallback c_handler, + gpointer user_data) +{ + EConnectNotifyData *connect_data; + + g_return_val_if_fail (g_str_has_prefix (notify_name, "notify::"), 0); + + connect_data = e_connect_notify_data_new (c_handler, user_data, G_CONNECT_SWAPPED); + + return g_signal_connect_data (instance, + notify_name, + G_CALLBACK (e_signal_connect_notify_cb), + connect_data, + (GClosureNotify) e_connect_notify_data_free, + 0); +} + +gulong +e_signal_connect_notify_object (gpointer instance, + const gchar *notify_name, + GCallback c_handler, + gpointer gobject, + GConnectFlags connect_flags) +{ + EConnectNotifyData *connect_data; + GClosure *closure; + + g_return_val_if_fail (g_str_has_prefix (notify_name, "notify::"), 0); + + if (!gobject) { + if ((connect_flags & G_CONNECT_SWAPPED) != 0) + return e_signal_connect_notify_swapped (instance, notify_name, c_handler, gobject); + else if ((connect_flags & G_CONNECT_AFTER) != 0) + e_signal_connect_notify_after (instance, notify_name, c_handler, gobject); + else + g_warn_if_fail (connect_flags == 0); + + return e_signal_connect_notify (instance, notify_name, c_handler, gobject); + } + + g_return_val_if_fail (G_IS_OBJECT (gobject), 0); + + connect_data = e_connect_notify_data_new (c_handler, gobject, connect_flags & G_CONNECT_SWAPPED); + closure = g_cclosure_new ( + G_CALLBACK (e_signal_connect_notify_cb), + connect_data, + (GClosureNotify) e_connect_notify_data_free); + + g_object_watch_closure (G_OBJECT (gobject), closure); + + return g_signal_connect_closure (instance, + notify_name, + closure, + connect_flags & G_CONNECT_AFTER); +} |