calendar-server: Improve performance by properly using ECalClientView

The previous code always restarted whole ECalClientView when it received
any changes in it, which could sometimes lead to constant repeated restarts
of the view.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1875
This commit is contained in:
Milan Crha 2020-04-02 09:24:28 +02:00 committed by Florian Müllner
parent 2e8ade4da0
commit 9f87ffc054
6 changed files with 961 additions and 955 deletions

View File

@ -1,12 +1,19 @@
<node>
<interface name="org.gnome.Shell.CalendarServer">
<method name="GetEvents">
<arg type="x" direction="in" />
<arg type="x" direction="in" />
<arg type="b" direction="in" />
<arg type="a(sssbxxa{sv})" direction="out" />
<method name="SetTimeRange">
<arg type="x" name="since" direction="in"/>
<arg type="x" name="until" direction="in"/>
<arg type="b" name="force_reload" direction="in"/>
</method>
<signal name="EventsAddedOrUpdated">
<arg type="a(ssbxxa{sv})" name="events" direction="out"/>
</signal>
<signal name="EventsRemoved">
<arg type="as" name="ids" direction="out"/>
</signal>
<signal name="ClientDisappeared">
<arg type="s" name="source_uid" direction="out"/>
</signal>
<property name="HasCalendars" type="b" access="read" />
<signal name="Changed" />
</interface>
</node>

View File

@ -220,7 +220,12 @@ class DBusEventSource extends EventSourceBase {
}
}
this._dbusProxy.connectSignal('Changed', this._onChanged.bind(this));
this._dbusProxy.connectSignal('EventsAddedOrUpdated',
this._onEventsAddedOrUpdated.bind(this));
this._dbusProxy.connectSignal('EventsRemoved',
this._onEventsRemoved.bind(this));
this._dbusProxy.connectSignal('ClientDisappeared',
this._onClientDisappeared.bind(this));
this._dbusProxy.connect('notify::g-name-owner', () => {
if (this._dbusProxy.g_name_owner)
@ -257,7 +262,7 @@ class DBusEventSource extends EventSourceBase {
}
_resetCache() {
this._events = [];
this._events = new Map();
this._lastRequestBegin = null;
this._lastRequestEnd = null;
}
@ -273,28 +278,47 @@ class DBusEventSource extends EventSourceBase {
this.emit('changed');
}
_onChanged() {
this._loadEvents(false);
_onEventsAddedOrUpdated(dbusProxy, nameOwner, argArray) {
const [appointments = []] = argArray;
let changed = false;
for (let n = 0; n < appointments.length; n++) {
const [id, summary, allDay, startTime, endTime] = appointments[n];
const date = new Date(startTime * 1000);
const end = new Date(endTime * 1000);
let event = new CalendarEvent(id, date, end, summary, allDay);
this._events.set(event.id, event);
changed = true;
}
if (changed)
this.emit('changed');
}
_onEventsReceived(results, _error) {
let newEvents = [];
let appointments = results[0] || [];
for (let n = 0; n < appointments.length; n++) {
let a = appointments[n];
let date = new Date(a[4] * 1000);
let end = new Date(a[5] * 1000);
let id = a[0];
let summary = a[1];
let allDay = a[3];
let event = new CalendarEvent(id, date, end, summary, allDay);
newEvents.push(event);
}
newEvents.sort((ev1, ev2) => ev1.date.getTime() - ev2.date.getTime());
_onEventsRemoved(dbusProxy, nameOwner, argArray) {
const [ids = []] = argArray;
this._events = newEvents;
this._isLoading = false;
this.emit('changed');
let changed = false;
for (const id of ids)
changed |= this._events.delete(id);
if (changed)
this.emit('changed');
}
_onClientDisappeared(dbusProxy, nameOwner, argArray) {
let [sourceUid = ''] = argArray;
sourceUid += '\n';
let changed = false;
for (const id of this._events.keys()) {
if (id.startsWith(sourceUid))
changed |= this._events.delete(id);
}
if (changed)
this.emit('changed');
}
_loadEvents(forceReload) {
@ -303,27 +327,30 @@ class DBusEventSource extends EventSourceBase {
return;
if (this._curRequestBegin && this._curRequestEnd) {
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
this._curRequestEnd.getTime() / 1000,
forceReload,
this._onEventsReceived.bind(this),
Gio.DBusCallFlags.NONE);
if (forceReload) {
this._events.clear();
this.emit('changed');
}
this._dbusProxy.SetTimeRangeRemote(
this._curRequestBegin.getTime() / 1000,
this._curRequestEnd.getTime() / 1000,
forceReload,
Gio.DBusCallFlags.NONE);
}
}
requestRange(begin, end) {
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this._isLoading = true;
this._lastRequestBegin = begin;
this._lastRequestEnd = end;
this._curRequestBegin = begin;
this._curRequestEnd = end;
this._loadEvents(false);
this._loadEvents(true);
}
}
*_getFilteredEvents(begin, end) {
for (const event of this._events) {
for (const event of this._events.values()) {
if (_dateIntervalsOverlap(event.date, event.end, begin, end))
yield event;
}
@ -878,7 +905,7 @@ class EventsSection extends MessageList.MessageListSection {
}
_reloadEvents() {
if (this._eventSource.isLoading)
if (this._eventSource.isLoading || this._reloading)
return;
this._reloading = true;

View File

@ -19,7 +19,7 @@ cogl_pango_pc = 'mutter-cogl-pango-' + mutter_api_version
libmutter_pc = 'libmutter-' + mutter_api_version
ecal_req = '>= 3.33.1'
eds_req = '>= 3.17.2'
eds_req = '>= 3.33.1'
gcr_req = '>= 3.7.5'
gio_req = '>= 2.56.0'
gi_req = '>= 1.49.1'

View File

@ -45,137 +45,122 @@ struct _ClientData
gulong backend_died_id;
};
struct _CalendarSourceData
{
ECalClientSourceType source_type;
CalendarSources *sources;
guint changed_signal;
/* ESource -> EClient */
GHashTable *clients;
guint timeout_id;
guint loaded : 1;
};
typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate;
struct _CalendarSources
{
GObject parent;
ESourceRegistry *registry;
gulong source_added_id;
gulong source_changed_id;
gulong source_removed_id;
ESourceRegistryWatcher *registry_watcher;
gulong filter_id;
gulong appeared_id;
gulong disappeared_id;
CalendarSourceData appointment_sources;
CalendarSourceData task_sources;
GMutex clients_lock;
GHashTable *clients; /* ESource -> ClientData */
};
G_DEFINE_TYPE (CalendarSources, calendar_sources, G_TYPE_OBJECT)
static void calendar_sources_finalize (GObject *object);
static void backend_died_cb (EClient *client, CalendarSourceData *source_data);
static void calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources);
static void calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources);
enum
{
APPOINTMENT_SOURCES_CHANGED,
TASK_SOURCES_CHANGED,
CLIENT_APPEARED,
CLIENT_DISAPPEARED,
LAST_SIGNAL
};
static guint signals [LAST_SIGNAL] = { 0, };
static GObjectClass *parent_class = NULL;
static CalendarSources *calendar_sources_singleton = NULL;
static void
calendar_sources_client_connected_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
CalendarSources *sources = CALENDAR_SOURCES (source_object);
ESource *source = user_data;
EClient *client;
g_autoptr (GError) error = NULL;
/* The calendar_sources_connect_client_sync() already stored the 'client'
* into the sources->clients */
client = calendar_sources_connect_client_finish (sources, result, &error);
if (error)
{
g_warning ("Could not load source '%s': %s",
e_source_get_uid (source),
error->message);
}
else
{
g_signal_emit (sources, signals[CLIENT_APPEARED], 0, client, NULL);
}
g_clear_object (&client);
g_clear_object (&source);
}
static gboolean
registry_watcher_filter_cb (ESourceRegistryWatcher *watcher,
ESource *source,
CalendarSources *sources)
{
return e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR) &&
e_source_selectable_get_selected (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR));
}
static void
registry_watcher_source_appeared_cb (ESourceRegistryWatcher *watcher,
ESource *source,
CalendarSources *sources)
{
ECalClientSourceType source_type;
if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST))
source_type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
else
g_return_if_reached ();
calendar_sources_connect_client (sources, source, source_type, 30, NULL, calendar_sources_client_connected_cb, g_object_ref (source));
}
static void
registry_watcher_source_disappeared_cb (ESourceRegistryWatcher *watcher,
ESource *source,
CalendarSources *sources)
{
gboolean emit;
g_mutex_lock (&sources->clients_lock);
emit = g_hash_table_remove (sources->clients, source);
g_mutex_unlock (&sources->clients_lock);
if (emit)
g_signal_emit (sources, signals[CLIENT_DISAPPEARED], 0, e_source_get_uid (source), NULL);
}
static void
client_data_free (ClientData *data)
{
g_clear_signal_handler (&data->backend_died_id, data->client);
g_signal_handler_disconnect (data->client, data->backend_died_id);
g_object_unref (data->client);
g_slice_free (ClientData, data);
}
static void
calendar_sources_class_init (CalendarSourcesClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = calendar_sources_finalize;
signals [APPOINTMENT_SOURCES_CHANGED] =
g_signal_new ("appointment-sources-changed",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0);
signals [TASK_SOURCES_CHANGED] =
g_signal_new ("task-sources-changed",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0);
}
static void
calendar_sources_init (CalendarSources *sources)
calendar_sources_constructed (GObject *object)
{
CalendarSources *sources = CALENDAR_SOURCES (object);
ESourceRegistry *registry = NULL;
GError *error = NULL;
GDBusConnection *session_bus;
GVariant *result;
/* WORKAROUND: the hardcoded timeout for e_source_registry_new_sync()
(and other library calls that eventually call g_dbus_proxy_new[_sync]())
is 25 seconds. This has been shown to be too small for
evolution-source-registry in certain cases (slow disk, concurrent IO,
many configured sources), so we first ensure that the service
starts with a manual call and a higher timeout.
HACK: every time the DBus API is bumped in e-d-s we need
to update this!
*/
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (session_bus == NULL)
{
g_error ("Failed to connect to the session bus: %s", error->message);
}
result = g_dbus_connection_call_sync (session_bus, "org.freedesktop.DBus",
"/", "org.freedesktop.DBus",
"StartServiceByName",
g_variant_new ("(su)",
"org.gnome.evolution.dataserver.Sources5",
0),
NULL,
G_DBUS_CALL_FLAGS_NONE,
60 * 1000,
NULL, &error);
if (result != NULL)
{
g_variant_unref (result);
sources->registry = e_source_registry_new_sync (NULL, &error);
}
G_OBJECT_CLASS (calendar_sources_parent_class)->constructed (object);
registry = e_source_registry_new_sync (NULL, &error);
if (error != NULL)
{
/* Any error is fatal, but we don't want to crash gnome-shell-calendar-server
@ -185,53 +170,30 @@ calendar_sources_init (CalendarSources *sources)
exit (EXIT_FAILURE);
}
g_object_unref (session_bus);
g_return_if_fail (registry != NULL);
sources->source_added_id = g_signal_connect (sources->registry,
"source-added",
G_CALLBACK (calendar_sources_registry_source_changed_cb),
sources);
sources->source_changed_id = g_signal_connect (sources->registry,
"source-changed",
G_CALLBACK (calendar_sources_registry_source_changed_cb),
sources);
sources->source_removed_id = g_signal_connect (sources->registry,
"source-removed",
G_CALLBACK (calendar_sources_registry_source_removed_cb),
sources);
sources->registry_watcher = e_source_registry_watcher_new (registry, NULL);
sources->appointment_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
sources->appointment_sources.sources = sources;
sources->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED];
sources->appointment_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
(GEqualFunc) e_source_equal,
(GDestroyNotify) g_object_unref,
(GDestroyNotify) client_data_free);
sources->appointment_sources.timeout_id = 0;
g_clear_object (&registry);
sources->task_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
sources->task_sources.sources = sources;
sources->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED];
sources->task_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
(GEqualFunc) e_source_equal,
(GDestroyNotify) g_object_unref,
(GDestroyNotify) client_data_free);
sources->task_sources.timeout_id = 0;
}
sources->clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
(GEqualFunc) e_source_equal,
(GDestroyNotify) g_object_unref,
(GDestroyNotify) client_data_free);
sources->filter_id = g_signal_connect (sources->registry_watcher,
"filter",
G_CALLBACK (registry_watcher_filter_cb),
sources);
sources->appeared_id = g_signal_connect (sources->registry_watcher,
"appeared",
G_CALLBACK (registry_watcher_source_appeared_cb),
sources);
sources->disappeared_id = g_signal_connect (sources->registry_watcher,
"disappeared",
G_CALLBACK (registry_watcher_source_disappeared_cb),
sources);
static void
calendar_sources_finalize_source_data (CalendarSources *sources,
CalendarSourceData *source_data)
{
if (source_data->loaded)
{
g_hash_table_destroy (source_data->clients);
source_data->clients = NULL;
g_clear_handle_id (&source_data->timeout_id, g_source_remove);
source_data->loaded = FALSE;
}
e_source_registry_watcher_reclaim (sources->registry_watcher);
}
static void
@ -239,28 +201,67 @@ calendar_sources_finalize (GObject *object)
{
CalendarSources *sources = CALENDAR_SOURCES (object);
if (sources->registry)
g_clear_pointer (&sources->clients, g_hash_table_destroy);
if (sources->registry_watcher)
{
g_clear_signal_handler (&sources->source_added_id,
sources->registry);
g_clear_signal_handler (&sources->source_changed_id,
sources->registry);
g_clear_signal_handler (&sources->source_removed_id,
sources->registry);
g_object_unref (sources->registry);
g_signal_handler_disconnect (sources->registry_watcher,
sources->filter_id);
g_signal_handler_disconnect (sources->registry_watcher,
sources->appeared_id);
g_signal_handler_disconnect (sources->registry_watcher,
sources->disappeared_id);
g_clear_object (&sources->registry_watcher);
}
sources->registry = NULL;
calendar_sources_finalize_source_data (sources, &sources->appointment_sources);
calendar_sources_finalize_source_data (sources, &sources->task_sources);
g_mutex_clear (&sources->clients_lock);
if (G_OBJECT_CLASS (parent_class)->finalize)
G_OBJECT_CLASS (parent_class)->finalize (object);
G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object);
}
static void
calendar_sources_class_init (CalendarSourcesClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->constructed = calendar_sources_constructed;
gobject_class->finalize = calendar_sources_finalize;
signals [CLIENT_APPEARED] =
g_signal_new ("client-appeared",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
1,
E_TYPE_CAL_CLIENT);
signals [CLIENT_DISAPPEARED] =
g_signal_new ("client-disappeared",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
1,
G_TYPE_STRING); /* ESource::uid of the disappeared client */
}
static void
calendar_sources_init (CalendarSources *sources)
{
g_mutex_init (&sources->clients_lock);
}
CalendarSources *
calendar_sources_get (void)
{
static CalendarSources *calendar_sources_singleton = NULL;
gpointer singleton_location = &calendar_sources_singleton;
if (calendar_sources_singleton)
@ -273,80 +274,65 @@ calendar_sources_get (void)
return calendar_sources_singleton;
}
/* The clients are just created here but not loaded */
static void
create_client_for_source (ESource *source,
ECalClientSourceType source_type,
CalendarSourceData *source_data)
ESourceRegistry *
calendar_sources_get_registry (CalendarSources *sources)
{
ClientData *data;
EClient *client;
GError *error = NULL;
client = g_hash_table_lookup (source_data->clients, source);
g_return_if_fail (client == NULL);
client = e_cal_client_connect_sync (source, source_type, -1, NULL, &error);
if (!client)
{
g_warning ("Could not load source '%s': %s",
e_source_get_uid (source),
error->message);
g_clear_error (&error);
return;
}
data = g_slice_new0 (ClientData);
data->client = E_CAL_CLIENT (client); /* takes ownership */
data->backend_died_id = g_signal_connect (client,
"backend-died",
G_CALLBACK (backend_died_cb),
source_data);
g_hash_table_insert (source_data->clients, g_object_ref (source), data);
}
static inline void
debug_dump_ecal_list (GHashTable *clients)
{
#ifdef CALENDAR_ENABLE_DEBUG
GList *list, *link;
dprintf ("Loaded clients:\n");
list = g_hash_table_get_keys (clients);
for (link = list; link != NULL; link = g_list_next (link))
{
ESource *source = E_SOURCE (link->data);
dprintf (" %s %s\n",
e_source_get_uid (source),
e_source_get_display_name (source));
}
g_list_free (list);
#endif
return e_source_registry_watcher_get_registry (sources->registry_watcher);
}
static void
calendar_sources_load_esource_list (ESourceRegistry *registry,
CalendarSourceData *source_data);
static gboolean
backend_restart (gpointer data)
gather_event_clients_cb (gpointer key,
gpointer value,
gpointer user_data)
{
CalendarSourceData *source_data = data;
ESourceRegistry *registry;
GSList **plist = user_data;
ClientData *cd = value;
registry = source_data->sources->registry;
calendar_sources_load_esource_list (registry, source_data);
g_signal_emit (source_data->sources, source_data->changed_signal, 0);
if (cd)
*plist = g_slist_prepend (*plist, g_object_ref (cd->client));
}
source_data->timeout_id = 0;
return FALSE;
GSList *
calendar_sources_ref_clients (CalendarSources *sources)
{
GSList *list = NULL;
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
g_mutex_lock (&sources->clients_lock);
g_hash_table_foreach (sources->clients, gather_event_clients_cb, &list);
g_mutex_unlock (&sources->clients_lock);
return list;
}
gboolean
calendar_sources_has_clients (CalendarSources *sources)
{
GHashTableIter iter;
gpointer value;
gboolean has = FALSE;
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE);
g_mutex_lock (&sources->clients_lock);
g_hash_table_iter_init (&iter, sources->clients);
while (!has && g_hash_table_iter_next (&iter, NULL, &value))
{
ClientData *cd = value;
has = cd != NULL;
}
g_mutex_unlock (&sources->clients_lock);
return has;
}
static void
backend_died_cb (EClient *client, CalendarSourceData *source_data)
backend_died_cb (EClient *client,
CalendarSources *sources)
{
ESource *source;
const char *display_name;
@ -354,196 +340,167 @@ backend_died_cb (EClient *client, CalendarSourceData *source_data)
source = e_client_get_source (client);
display_name = e_source_get_display_name (source);
g_warning ("The calendar backend for '%s' has crashed.", display_name);
g_hash_table_remove (source_data->clients, source);
g_clear_handle_id (&source_data->timeout_id, g_source_remove);
source_data->timeout_id = g_timeout_add_seconds (2, backend_restart,
source_data);
g_source_set_name_by_id (source_data->timeout_id, "[gnome-shell] backend_restart");
g_mutex_lock (&sources->clients_lock);
g_hash_table_remove (sources->clients, source);
g_mutex_unlock (&sources->clients_lock);
}
static void
calendar_sources_load_esource_list (ESourceRegistry *registry,
CalendarSourceData *source_data)
static EClient *
calendar_sources_connect_client_sync (CalendarSources *sources,
ESource *source,
ECalClientSourceType source_type,
guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GError **error)
{
GList *list, *link;
const gchar *extension_name;
EClient *client = NULL;
ClientData *client_data;
switch (source_data->source_type)
g_mutex_lock (&sources->clients_lock);
client_data = g_hash_table_lookup (sources->clients, source);
if (client_data)
client = E_CLIENT (g_object_ref (client_data->client));
g_mutex_unlock (&sources->clients_lock);
if (client)
return client;
client = e_cal_client_connect_sync (source, source_type, wait_for_connected_seconds, cancellable, error);
if (!client)
return NULL;
g_mutex_lock (&sources->clients_lock);
client_data = g_hash_table_lookup (sources->clients, source);
if (client_data)
{
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
extension_name = E_SOURCE_EXTENSION_CALENDAR;
break;
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
extension_name = E_SOURCE_EXTENSION_TASK_LIST;
break;
default:
g_return_if_reached ();
g_clear_object (&client);
client = E_CLIENT (g_object_ref (client_data->client));
}
list = e_source_registry_list_sources (registry, extension_name);
for (link = list; link != NULL; link = g_list_next (link))
else
{
ESource *source = E_SOURCE (link->data);
ESourceSelectable *extension;
gboolean show_source;
client_data = g_slice_new0 (ClientData);
client_data->client = E_CAL_CLIENT (g_object_ref (client));
client_data->backend_died_id = g_signal_connect (client,
"backend-died",
G_CALLBACK (backend_died_cb),
sources);
extension = e_source_get_extension (source, extension_name);
show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
if (show_source)
create_client_for_source (source, source_data->source_type, source_data);
g_hash_table_insert (sources->clients, g_object_ref (source), client_data);
}
g_mutex_unlock (&sources->clients_lock);
debug_dump_ecal_list (source_data->clients);
g_list_free_full (list, g_object_unref);
return client;
}
typedef struct _AsyncContext {
ESource *source;
ECalClientSourceType source_type;
guint32 wait_for_connected_seconds;
} AsyncContext;
static void
calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources)
async_context_free (gpointer ptr)
{
if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
AsyncContext *ctx = ptr;
if (ctx)
{
CalendarSourceData *source_data;
ESourceSelectable *extension;
gboolean have_client;
gboolean show_source;
source_data = &sources->appointment_sources;
extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
if (!show_source && have_client)
{
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
if (show_source && !have_client)
{
create_client_for_source (source, source_data->source_type, source_data);
g_signal_emit (sources, source_data->changed_signal, 0);
}
}
if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
{
CalendarSourceData *source_data;
ESourceSelectable *extension;
gboolean have_client;
gboolean show_source;
source_data = &sources->task_sources;
extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
if (!show_source && have_client)
{
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
if (show_source && !have_client)
{
create_client_for_source (source, source_data->source_type, source_data);
g_signal_emit (sources, source_data->changed_signal, 0);
}
g_clear_object (&ctx->source);
g_slice_free (AsyncContext, ctx);
}
}
static void
calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources)
calendar_sources_connect_client_thread (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
CalendarSources *sources = source_object;
AsyncContext *ctx = task_data;
EClient *client;
GError *local_error = NULL;
client = calendar_sources_connect_client_sync (sources, ctx->source, ctx->source_type,
ctx->wait_for_connected_seconds, cancellable, &local_error);
if (!client)
{
CalendarSourceData *source_data;
source_data = &sources->appointment_sources;
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
{
CalendarSourceData *source_data;
source_data = &sources->task_sources;
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
if (local_error)
g_task_return_error (task, local_error);
else
g_task_return_pointer (task, NULL, NULL);
} else {
g_task_return_pointer (task, client, g_object_unref);
}
}
static void
ensure_appointment_sources (CalendarSources *sources)
void
calendar_sources_connect_client (CalendarSources *sources,
ESource *source,
ECalClientSourceType source_type,
guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
if (!sources->appointment_sources.loaded)
AsyncContext *ctx;
g_autoptr (GTask) task = NULL;
ctx = g_slice_new0 (AsyncContext);
ctx->source = g_object_ref (source);
ctx->source_type = source_type;
ctx->wait_for_connected_seconds = wait_for_connected_seconds;
task = g_task_new (sources, cancellable, callback, user_data);
g_task_set_source_tag (task, calendar_sources_connect_client);
g_task_set_task_data (task, ctx, async_context_free);
g_task_run_in_thread (task, calendar_sources_connect_client_thread);
}
EClient *
calendar_sources_connect_client_finish (CalendarSources *sources,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, sources), NULL);
g_return_val_if_fail (g_async_result_is_tagged (result, calendar_sources_connect_client), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
void
print_debug (const gchar *format,
...)
{
g_autofree char *s = NULL;
g_autofree char *timestamp = NULL;
va_list ap;
g_autoptr (GDateTime) now = NULL;
static volatile gsize once_init_value = 0;
static gboolean show_debug = FALSE;
static guint pid = 0;
if (g_once_init_enter (&once_init_value))
{
calendar_sources_load_esource_list (sources->registry,
&sources->appointment_sources);
sources->appointment_sources.loaded = TRUE;
show_debug = (g_getenv ("CALENDAR_SERVER_DEBUG") != NULL);
pid = getpid ();
g_once_init_leave (&once_init_value, 1);
}
}
GList *
calendar_sources_get_appointment_clients (CalendarSources *sources)
{
GList *list, *link;
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
ensure_appointment_sources (sources);
list = g_hash_table_get_values (sources->appointment_sources.clients);
for (link = list; link != NULL; link = g_list_next (link))
link->data = ((ClientData *) link->data)->client;
return list;
}
static void
ensure_task_sources (CalendarSources *sources)
{
if (!sources->task_sources.loaded)
{
calendar_sources_load_esource_list (sources->registry,
&sources->task_sources);
sources->task_sources.loaded = TRUE;
}
}
GList *
calendar_sources_get_task_clients (CalendarSources *sources)
{
GList *list, *link;
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
ensure_task_sources (sources);
list = g_hash_table_get_values (sources->task_sources.clients);
for (link = list; link != NULL; link = g_list_next (link))
link->data = ((ClientData *) link->data)->client;
return list;
}
gboolean
calendar_sources_has_sources (CalendarSources *sources)
{
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE);
ensure_appointment_sources (sources);
ensure_task_sources (sources);
return g_hash_table_size (sources->appointment_sources.clients) > 0 ||
g_hash_table_size (sources->task_sources.clients) > 0;
if (!show_debug)
goto out;
now = g_date_time_new_now_local ();
timestamp = g_date_time_format (now, "%H:%M:%S");
va_start (ap, format);
s = g_strdup_vprintf (format, ap);
va_end (ap);
g_print ("gnome-shell-calendar-server[%d]: %s.%03d: %s\n",
pid, timestamp, g_date_time_get_microsecond (now), s);
out:
;
}

View File

@ -26,17 +26,38 @@
#include <glib-object.h>
#define EDS_DISABLE_DEPRECATED
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
#include <libedataserver/libedataserver.h>
#include <libecal/libecal.h>
G_GNUC_END_IGNORE_DEPRECATIONS
G_BEGIN_DECLS
#define CALENDAR_TYPE_SOURCES (calendar_sources_get_type ())
G_DECLARE_FINAL_TYPE (CalendarSources, calendar_sources,
CALENDAR, SOURCES, GObject)
CalendarSources *calendar_sources_get (void);
GList *calendar_sources_get_appointment_clients (CalendarSources *sources);
GList *calendar_sources_get_task_clients (CalendarSources *sources);
CalendarSources *calendar_sources_get (void);
ESourceRegistry *calendar_sources_get_registry (CalendarSources *sources);
GSList *calendar_sources_ref_clients (CalendarSources *sources);
gboolean calendar_sources_has_clients (CalendarSources *sources);
gboolean calendar_sources_has_sources (CalendarSources *sources);
void calendar_sources_connect_client (CalendarSources *sources,
ESource *source,
ECalClientSourceType source_type,
guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
EClient *calendar_sources_connect_client_finish
(CalendarSources *sources,
GAsyncResult *result,
GError **error);
/* Set the environment variable CALENDAR_SERVER_DEBUG to show debug */
void print_debug (const gchar *str,
...) G_GNUC_PRINTF (1, 2);
G_END_DECLS

File diff suppressed because it is too large Load Diff