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 Georges Basile Stavracas Neto
parent 30d902f898
commit c00d79bae2
6 changed files with 961 additions and 955 deletions

View File

@ -1,12 +1,19 @@
<node> <node>
<interface name="org.gnome.Shell.CalendarServer"> <interface name="org.gnome.Shell.CalendarServer">
<method name="GetEvents"> <method name="SetTimeRange">
<arg type="x" direction="in" /> <arg type="x" name="since" direction="in"/>
<arg type="x" direction="in" /> <arg type="x" name="until" direction="in"/>
<arg type="b" direction="in" /> <arg type="b" name="force_reload" direction="in"/>
<arg type="a(sssbxxa{sv})" direction="out" />
</method> </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" /> <property name="HasCalendars" type="b" access="read" />
<signal name="Changed" />
</interface> </interface>
</node> </node>

View File

@ -222,7 +222,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', () => { this._dbusProxy.connect('notify::g-name-owner', () => {
if (this._dbusProxy.g_name_owner) if (this._dbusProxy.g_name_owner)
@ -258,7 +263,7 @@ class DBusEventSource extends EventSourceBase {
} }
_resetCache() { _resetCache() {
this._events = []; this._events = new Map();
this._lastRequestBegin = null; this._lastRequestBegin = null;
this._lastRequestEnd = null; this._lastRequestEnd = null;
} }
@ -274,28 +279,47 @@ class DBusEventSource extends EventSourceBase {
this.emit('changed'); this.emit('changed');
} }
_onChanged() { _onEventsAddedOrUpdated(dbusProxy, nameOwner, argArray) {
this._loadEvents(false); 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) { _onEventsRemoved(dbusProxy, nameOwner, argArray) {
let newEvents = []; const [ids = []] = argArray;
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());
this._events = newEvents; let changed = false;
this._isLoading = false; for (const id of ids)
this.emit('changed'); 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) { _loadEvents(forceReload) {
@ -304,27 +328,30 @@ class DBusEventSource extends EventSourceBase {
return; return;
if (this._curRequestBegin && this._curRequestEnd) { if (this._curRequestBegin && this._curRequestEnd) {
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000, if (forceReload) {
this._curRequestEnd.getTime() / 1000, this._events.clear();
forceReload, this.emit('changed');
this._onEventsReceived.bind(this), }
Gio.DBusCallFlags.NONE); this._dbusProxy.SetTimeRangeRemote(
this._curRequestBegin.getTime() / 1000,
this._curRequestEnd.getTime() / 1000,
forceReload,
Gio.DBusCallFlags.NONE);
} }
} }
requestRange(begin, end) { requestRange(begin, end) {
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) { if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this._isLoading = true;
this._lastRequestBegin = begin; this._lastRequestBegin = begin;
this._lastRequestEnd = end; this._lastRequestEnd = end;
this._curRequestBegin = begin; this._curRequestBegin = begin;
this._curRequestEnd = end; this._curRequestEnd = end;
this._loadEvents(false); this._loadEvents(true);
} }
} }
*_getFilteredEvents(begin, end) { *_getFilteredEvents(begin, end) {
for (const event of this._events) { for (const event of this._events.values()) {
if (_dateIntervalsOverlap(event.date, event.end, begin, end)) if (_dateIntervalsOverlap(event.date, event.end, begin, end))
yield event; yield event;
} }
@ -879,7 +906,7 @@ class EventsSection extends MessageList.MessageListSection {
} }
_reloadEvents() { _reloadEvents() {
if (this._eventSource.isLoading) if (this._eventSource.isLoading || this._reloading)
return; return;
this._reloading = true; this._reloading = true;

View File

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

View File

@ -45,137 +45,122 @@ struct _ClientData
gulong backend_died_id; 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; typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate;
struct _CalendarSources struct _CalendarSources
{ {
GObject parent; GObject parent;
ESourceRegistry *registry; ESourceRegistryWatcher *registry_watcher;
gulong source_added_id; gulong filter_id;
gulong source_changed_id; gulong appeared_id;
gulong source_removed_id; gulong disappeared_id;
CalendarSourceData appointment_sources; GMutex clients_lock;
CalendarSourceData task_sources; GHashTable *clients; /* ESource -> ClientData */
}; };
G_DEFINE_TYPE (CalendarSources, calendar_sources, G_TYPE_OBJECT) 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 enum
{ {
APPOINTMENT_SOURCES_CHANGED, CLIENT_APPEARED,
TASK_SOURCES_CHANGED, CLIENT_DISAPPEARED,
LAST_SIGNAL LAST_SIGNAL
}; };
static guint signals [LAST_SIGNAL] = { 0, }; static guint signals [LAST_SIGNAL] = { 0, };
static GObjectClass *parent_class = NULL; static void
static CalendarSources *calendar_sources_singleton = NULL; 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 static void
client_data_free (ClientData *data) 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_object_unref (data->client);
g_slice_free (ClientData, data); g_slice_free (ClientData, data);
} }
static void static void
calendar_sources_class_init (CalendarSourcesClass *klass) calendar_sources_constructed (GObject *object)
{
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)
{ {
CalendarSources *sources = CALENDAR_SOURCES (object);
ESourceRegistry *registry = NULL;
GError *error = NULL; GError *error = NULL;
GDBusConnection *session_bus;
GVariant *result;
/* WORKAROUND: the hardcoded timeout for e_source_registry_new_sync() G_OBJECT_CLASS (calendar_sources_parent_class)->constructed (object);
(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);
}
registry = e_source_registry_new_sync (NULL, &error);
if (error != NULL) if (error != NULL)
{ {
/* Any error is fatal, but we don't want to crash gnome-shell-calendar-server /* 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); exit (EXIT_FAILURE);
} }
g_object_unref (session_bus); g_return_if_fail (registry != NULL);
sources->source_added_id = g_signal_connect (sources->registry, sources->registry_watcher = e_source_registry_watcher_new (registry, NULL);
"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->appointment_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; g_clear_object (&registry);
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;
sources->task_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; sources->clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
sources->task_sources.sources = sources; (GEqualFunc) e_source_equal,
sources->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED]; (GDestroyNotify) g_object_unref,
sources->task_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash, (GDestroyNotify) client_data_free);
(GEqualFunc) e_source_equal, sources->filter_id = g_signal_connect (sources->registry_watcher,
(GDestroyNotify) g_object_unref, "filter",
(GDestroyNotify) client_data_free); G_CALLBACK (registry_watcher_filter_cb),
sources->task_sources.timeout_id = 0; 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 e_source_registry_watcher_reclaim (sources->registry_watcher);
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;
}
} }
static void static void
@ -239,28 +201,67 @@ calendar_sources_finalize (GObject *object)
{ {
CalendarSources *sources = CALENDAR_SOURCES (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, g_signal_handler_disconnect (sources->registry_watcher,
sources->registry); sources->filter_id);
g_clear_signal_handler (&sources->source_changed_id, g_signal_handler_disconnect (sources->registry_watcher,
sources->registry); sources->appeared_id);
g_clear_signal_handler (&sources->source_removed_id, g_signal_handler_disconnect (sources->registry_watcher,
sources->registry); sources->disappeared_id);
g_object_unref (sources->registry); g_clear_object (&sources->registry_watcher);
} }
sources->registry = NULL;
calendar_sources_finalize_source_data (sources, &sources->appointment_sources); g_mutex_clear (&sources->clients_lock);
calendar_sources_finalize_source_data (sources, &sources->task_sources);
if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object);
G_OBJECT_CLASS (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 * CalendarSources *
calendar_sources_get (void) calendar_sources_get (void)
{ {
static CalendarSources *calendar_sources_singleton = NULL;
gpointer singleton_location = &calendar_sources_singleton; gpointer singleton_location = &calendar_sources_singleton;
if (calendar_sources_singleton) if (calendar_sources_singleton)
@ -273,80 +274,65 @@ calendar_sources_get (void)
return calendar_sources_singleton; return calendar_sources_singleton;
} }
/* The clients are just created here but not loaded */ ESourceRegistry *
static void calendar_sources_get_registry (CalendarSources *sources)
create_client_for_source (ESource *source,
ECalClientSourceType source_type,
CalendarSourceData *source_data)
{ {
ClientData *data; return e_source_registry_watcher_get_registry (sources->registry_watcher);
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
} }
static void static void
calendar_sources_load_esource_list (ESourceRegistry *registry, gather_event_clients_cb (gpointer key,
CalendarSourceData *source_data); gpointer value,
gpointer user_data)
static gboolean
backend_restart (gpointer data)
{ {
CalendarSourceData *source_data = data; GSList **plist = user_data;
ESourceRegistry *registry; ClientData *cd = value;
registry = source_data->sources->registry; if (cd)
calendar_sources_load_esource_list (registry, source_data); *plist = g_slist_prepend (*plist, g_object_ref (cd->client));
g_signal_emit (source_data->sources, source_data->changed_signal, 0); }
source_data->timeout_id = 0; GSList *
calendar_sources_ref_clients (CalendarSources *sources)
{
GSList *list = NULL;
return FALSE; 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 static void
backend_died_cb (EClient *client, CalendarSourceData *source_data) backend_died_cb (EClient *client,
CalendarSources *sources)
{ {
ESource *source; ESource *source;
const char *display_name; const char *display_name;
@ -354,196 +340,167 @@ backend_died_cb (EClient *client, CalendarSourceData *source_data)
source = e_client_get_source (client); source = e_client_get_source (client);
display_name = e_source_get_display_name (source); display_name = e_source_get_display_name (source);
g_warning ("The calendar backend for '%s' has crashed.", display_name); g_warning ("The calendar backend for '%s' has crashed.", display_name);
g_hash_table_remove (source_data->clients, source); g_mutex_lock (&sources->clients_lock);
g_hash_table_remove (sources->clients, source);
g_clear_handle_id (&source_data->timeout_id, g_source_remove); g_mutex_unlock (&sources->clients_lock);
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");
} }
static void static EClient *
calendar_sources_load_esource_list (ESourceRegistry *registry, calendar_sources_connect_client_sync (CalendarSources *sources,
CalendarSourceData *source_data) ESource *source,
ECalClientSourceType source_type,
guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GError **error)
{ {
GList *list, *link; EClient *client = NULL;
const gchar *extension_name; 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: g_clear_object (&client);
extension_name = E_SOURCE_EXTENSION_CALENDAR; client = E_CLIENT (g_object_ref (client_data->client));
break;
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
extension_name = E_SOURCE_EXTENSION_TASK_LIST;
break;
default:
g_return_if_reached ();
} }
else
list = e_source_registry_list_sources (registry, extension_name);
for (link = list; link != NULL; link = g_list_next (link))
{ {
ESource *source = E_SOURCE (link->data); client_data = g_slice_new0 (ClientData);
ESourceSelectable *extension; client_data->client = E_CAL_CLIENT (g_object_ref (client));
gboolean show_source; 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); g_hash_table_insert (sources->clients, g_object_ref (source), client_data);
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_mutex_unlock (&sources->clients_lock);
debug_dump_ecal_list (source_data->clients); return client;
g_list_free_full (list, g_object_unref);
} }
typedef struct _AsyncContext {
ESource *source;
ECalClientSourceType source_type;
guint32 wait_for_connected_seconds;
} AsyncContext;
static void static void
calendar_sources_registry_source_changed_cb (ESourceRegistry *registry, async_context_free (gpointer ptr)
ESource *source,
CalendarSources *sources)
{ {
if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) AsyncContext *ctx = ptr;
if (ctx)
{ {
CalendarSourceData *source_data; g_clear_object (&ctx->source);
ESourceSelectable *extension; g_slice_free (AsyncContext, ctx);
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);
}
} }
} }
static void static void
calendar_sources_registry_source_removed_cb (ESourceRegistry *registry, calendar_sources_connect_client_thread (GTask *task,
ESource *source, gpointer source_object,
CalendarSources *sources) 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; if (local_error)
g_task_return_error (task, local_error);
source_data = &sources->appointment_sources; else
g_hash_table_remove (source_data->clients, source); g_task_return_pointer (task, NULL, NULL);
g_signal_emit (sources, source_data->changed_signal, 0); } else {
} g_task_return_pointer (task, client, g_object_unref);
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);
} }
} }
static void void
ensure_appointment_sources (CalendarSources *sources) 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, show_debug = (g_getenv ("CALENDAR_SERVER_DEBUG") != NULL);
&sources->appointment_sources); pid = getpid ();
sources->appointment_sources.loaded = TRUE; g_once_init_leave (&once_init_value, 1);
} }
}
if (!show_debug)
GList * goto out;
calendar_sources_get_appointment_clients (CalendarSources *sources)
{ now = g_date_time_new_now_local ();
GList *list, *link; timestamp = g_date_time_format (now, "%H:%M:%S");
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); va_start (ap, format);
s = g_strdup_vprintf (format, ap);
ensure_appointment_sources (sources); va_end (ap);
list = g_hash_table_get_values (sources->appointment_sources.clients); g_print ("gnome-shell-calendar-server[%d]: %s.%03d: %s\n",
pid, timestamp, g_date_time_get_microsecond (now), s);
for (link = list; link != NULL; link = g_list_next (link)) out:
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;
} }

View File

@ -26,17 +26,38 @@
#include <glib-object.h> #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 G_BEGIN_DECLS
#define CALENDAR_TYPE_SOURCES (calendar_sources_get_type ()) #define CALENDAR_TYPE_SOURCES (calendar_sources_get_type ())
G_DECLARE_FINAL_TYPE (CalendarSources, calendar_sources, G_DECLARE_FINAL_TYPE (CalendarSources, calendar_sources,
CALENDAR, SOURCES, GObject) CALENDAR, SOURCES, GObject)
CalendarSources *calendar_sources_get (void); CalendarSources *calendar_sources_get (void);
GList *calendar_sources_get_appointment_clients (CalendarSources *sources); ESourceRegistry *calendar_sources_get_registry (CalendarSources *sources);
GList *calendar_sources_get_task_clients (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 G_END_DECLS

File diff suppressed because it is too large Load Diff