Move calendar handling out-of-process
Unfortunately the evolution-data-server client-side libraries seem to block the calling thread. This is a major problem as we must never ever block the main thread (doing so causes animations to flicker etc.). In the worst case, this problem causes login to hang (without falling back to fall-back mode) and in the best case it slows down login until a network connection is acquired. Additionally, in order to sanely use these evolution-data-server libraries, GConf has to be involved and GConf is not thread-safe. So it's not really feasible just moving the code to a separate thread. Therefore, move all calendar IO out of process and use a simple (and private) D-Bus interface for the shell to communicate with the out-of-process helper. For simplification, remove existing in-process code since internal interfaces have been slightly revised. This means that the shell is no longer using any native code for drawing the calendar dropdown. https://bugzilla.gnome.org/show_bug.cgi?id=641396 Signed-off-by: David Zeuthen <davidz@redhat.com>
This commit is contained in:
parent
ddc135839b
commit
e9e30138bd
18
configure.ac
18
configure.ac
@ -66,10 +66,10 @@ GJS_MIN_VERSION=0.7.11
|
||||
MUTTER_MIN_VERSION=2.91.90
|
||||
GTK_MIN_VERSION=3.0.0
|
||||
GIO_MIN_VERSION=2.25.9
|
||||
LIBECAL_REQUIRED=1.6.0
|
||||
LIBEDATASERVER_REQUIRED=1.2.0
|
||||
LIBEDATASERVERUI2_REQUIRED=1.2.0
|
||||
LIBEDATASERVERUI3_REQUIRED=2.91.6
|
||||
LIBECAL_MIN_VERSION=1.6.0
|
||||
LIBEDATASERVER_MIN_VERSION=1.2.0
|
||||
LIBEDATASERVERUI2_MIN_VERSION=1.2.0
|
||||
LIBEDATASERVERUI3_MIN_VERSION=2.91.6
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.13.12
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
|
||||
@ -125,12 +125,12 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 2.90.0],
|
||||
# Default to libedataserverui-3.0, but allow falling back to 1.2
|
||||
PKG_CHECK_EXISTS(libedataserverui-3.0,
|
||||
[EDS_API=3.0
|
||||
LIBEDATASERVERUI_REQUIRED=$LIBEDATASERVERUI3_REQUIRED],
|
||||
LIBEDATASERVERUI_MIN_VERSION=$LIBEDATASERVERUI3_MIN_VERSION],
|
||||
[EDS_API=1.2
|
||||
LIBEDATASERVERUI_REQUIRED=$LIBEDATASERVERUI2_REQUIRED])
|
||||
PKG_CHECK_MODULES(LIBECAL, libecal-1.2 >= $LIBECAL_REQUIRED libedataserver-1.2 >= $LIBEDATASERVER_REQUIRED libedataserverui-$EDS_API >= $LIBEDATASERVERUI_REQUIRED)
|
||||
AC_SUBST(LIBECAL_CFLAGS)
|
||||
AC_SUBST(LIBECAL_LIBS)
|
||||
LIBEDATASERVERUI_MIN_VERSION=$LIBEDATASERVERUI2_MIN_VERSION])
|
||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-$EDS_API >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
|
||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
||||
AC_SUBST(CALENDAR_SERVER_LIBS)
|
||||
|
||||
MUTTER_BIN_DIR=`$PKG_CONFIG --variable=exec_prefix mutter-plugins`/bin
|
||||
# FIXME: metacity-plugins.pc should point directly to its .gir file
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const DBus = imports.dbus;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
@ -158,13 +159,14 @@ function _getEventDayAbbreviation(dayNumber) {
|
||||
|
||||
// Abstraction for an appointment/event in a calendar
|
||||
|
||||
function CalendarEvent(date, summary, allDay) {
|
||||
this._init(date, summary, allDay);
|
||||
function CalendarEvent(date, end, summary, allDay) {
|
||||
this._init(date, end, summary, allDay);
|
||||
}
|
||||
|
||||
CalendarEvent.prototype = {
|
||||
_init: function(date, summary, allDay) {
|
||||
_init: function(date, end, summary, allDay) {
|
||||
this.date = date;
|
||||
this.end = end;
|
||||
this.summary = summary;
|
||||
this.allDay = allDay;
|
||||
}
|
||||
@ -196,139 +198,136 @@ EmptyEventSource.prototype = {
|
||||
};
|
||||
Signals.addSignalMethods(EmptyEventSource.prototype);
|
||||
|
||||
// Second, wrap native Evolution event source
|
||||
function EvolutionEventSource() {
|
||||
const CalendarServerIface = {
|
||||
name: 'org.gnome.Shell.CalendarServer',
|
||||
methods: [{ name: 'GetEvents',
|
||||
inSignature: 'xxb',
|
||||
outSignature: 'a(sssbxxa{sv})' }],
|
||||
signals: [{ name: 'Changed',
|
||||
inSignature: '' }]
|
||||
};
|
||||
|
||||
const CalendarServer = function () {
|
||||
this._init();
|
||||
}
|
||||
};
|
||||
|
||||
EvolutionEventSource.prototype = {
|
||||
CalendarServer.prototype = {
|
||||
_init: function() {
|
||||
this._native = new Shell.EvolutionEventSource();
|
||||
this._native.connect('changed', Lang.bind(this, function() {
|
||||
this.emit('changed');
|
||||
}));
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
this._native.request_range(begin.getTime(), end.getTime());
|
||||
},
|
||||
|
||||
getEvents: function(begin, end) {
|
||||
let result = [];
|
||||
let nativeEvents = this._native.get_events(begin.getTime(), end.getTime());
|
||||
for (let n = 0; n < nativeEvents.length; n++) {
|
||||
let nativeEvent = nativeEvents[n];
|
||||
result.push(new CalendarEvent(new Date(nativeEvent.msec_begin), nativeEvent.summary, nativeEvent.all_day));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
hasEvents: function(day) {
|
||||
let dayBegin = _getBeginningOfDay(day);
|
||||
let dayEnd = _getEndOfDay(day);
|
||||
|
||||
let events = this.getEvents(dayBegin, dayEnd);
|
||||
|
||||
if (events.length == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
DBus.session.proxifyObject(this, 'org.gnome.Shell.CalendarServer', '/org/gnome/Shell/CalendarServer');
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(EvolutionEventSource.prototype);
|
||||
|
||||
// Finally, an implementation with fake events
|
||||
function FakeEventSource() {
|
||||
this._init();
|
||||
DBus.proxifyPrototype(CalendarServer.prototype, CalendarServerIface);
|
||||
|
||||
// an implementation that reads data from a session bus service
|
||||
function DBusEventSource(owner) {
|
||||
this._init(owner);
|
||||
}
|
||||
|
||||
FakeEventSource.prototype = {
|
||||
_init: function() {
|
||||
function _datesEqual(a, b) {
|
||||
if (a < b)
|
||||
return false;
|
||||
else if (a > b)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
this._fakeEvents = [];
|
||||
function _dateIntervalsOverlap(a0, a1, b0, b1)
|
||||
{
|
||||
if (a1 <= b0)
|
||||
return false;
|
||||
else if (b1 <= a0)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generate fake events
|
||||
//
|
||||
let midnightToday = _getBeginningOfDay(new Date());
|
||||
let summary = '';
|
||||
|
||||
// '10-oclock pow-wow' is an event occuring IN THE PAST every four days at 10am
|
||||
for (let n = 0; n < 10; n++) {
|
||||
let t = new Date(midnightToday.getTime() - n * 4 * 86400 * 1000);
|
||||
t.setHours(10);
|
||||
summary = '10-oclock pow-wow (n=' + n + ')';
|
||||
this._fakeEvents.push(new CalendarEvent(t, summary, false));
|
||||
}
|
||||
DBusEventSource.prototype = {
|
||||
_init: function(owner) {
|
||||
this._resetCache();
|
||||
|
||||
// '11-oclock thing' is an event occuring every three days at 11am
|
||||
for (let n = 0; n < 10; n++) {
|
||||
let t = new Date(midnightToday.getTime() + n * 3 * 86400 * 1000);
|
||||
t.setHours(11);
|
||||
summary = '11-oclock thing (n=' + n + ')';
|
||||
this._fakeEvents.push(new CalendarEvent(t, summary, false));
|
||||
}
|
||||
this._dbusProxy = new CalendarServer(owner);
|
||||
this._dbusProxy.connect('Changed', Lang.bind(this, this._onChanged));
|
||||
|
||||
// 'Weekly Meeting' is an event occuring every seven days at 1:45pm (two days displaced)
|
||||
for (let n = 0; n < 5; n++) {
|
||||
let t = new Date(midnightToday.getTime() + (n * 7 + 2) * 86400 * 1000);
|
||||
t.setHours(13);
|
||||
t.setMinutes(45);
|
||||
summary = 'Weekly Meeting (n=' + n + ')';
|
||||
this._fakeEvents.push(new CalendarEvent(t, summary, false));
|
||||
}
|
||||
|
||||
// 'Fun All Day' is an all-day event occuring every fortnight (three days displayed)
|
||||
for (let n = 0; n < 10; n++) {
|
||||
let t = new Date(midnightToday.getTime() + (n * 14 + 3) * 86400 * 1000);
|
||||
summary = 'Fun All Day (n=' + n + ')';
|
||||
this._fakeEvents.push(new CalendarEvent(t, summary, true));
|
||||
}
|
||||
|
||||
// 'Get Married' is an event that actually reflects reality (Dec 4, 2010) :-)
|
||||
this._fakeEvents.push(new CalendarEvent(new Date(2010, 11, 4, 16, 0), 'Get Married', false));
|
||||
|
||||
// ditto for 'NE Patriots vs NY Jets'
|
||||
this._fakeEvents.push(new CalendarEvent(new Date(2010, 11, 6, 20, 30), 'NE Patriots vs NY Jets', false));
|
||||
|
||||
// An event for tomorrow @6:30pm that is added/removed every five
|
||||
// seconds (to check that the ::changed signal works)
|
||||
let transientEventDate = new Date(midnightToday.getTime() + 86400 * 1000);
|
||||
transientEventDate.setHours(18);
|
||||
transientEventDate.setMinutes(30);
|
||||
transientEventDate.setSeconds(0);
|
||||
Mainloop.timeout_add(5000, Lang.bind(this, this._updateTransientEvent));
|
||||
this._includeTransientEvent = false;
|
||||
this._transientEvent = new CalendarEvent(transientEventDate, 'A Transient Event', false);
|
||||
this._transientEventCounter = 1;
|
||||
DBus.session.watch_name('org.gnome.Shell.CalendarServer',
|
||||
false, // do not launch a name-owner if none exists
|
||||
Lang.bind(this, this._onNameAppeared),
|
||||
Lang.bind(this, this._onNameVanished));
|
||||
},
|
||||
|
||||
_updateTransientEvent: function() {
|
||||
this._includeTransientEvent = !this._includeTransientEvent;
|
||||
this._transientEventCounter = this._transientEventCounter + 1;
|
||||
this._transientEvent.summary = 'A Transient Event (' + this._transientEventCounter + ')';
|
||||
_resetCache: function() {
|
||||
this._events = [];
|
||||
this._lastRequestBegin = null;
|
||||
this._lastRequestEnd = null;
|
||||
},
|
||||
|
||||
_onNameAppeared: function(owner) {
|
||||
this._resetCache();
|
||||
this._loadEvents(true);
|
||||
},
|
||||
|
||||
_onNameVanished: function(oldOwner) {
|
||||
this._resetCache();
|
||||
this.emit('changed');
|
||||
Mainloop.timeout_add(5000, Lang.bind(this, this._updateTransientEvent));
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
_onChanged: function() {
|
||||
this._loadEvents(false);
|
||||
},
|
||||
|
||||
getEvents: function(begin, end) {
|
||||
let result = [];
|
||||
//log('begin:' + begin);
|
||||
//log('end: ' + end);
|
||||
for(let n = 0; n < this._fakeEvents.length; n++) {
|
||||
let event = this._fakeEvents[n];
|
||||
if (event.date >= begin && event.date <= end) {
|
||||
result.push(event);
|
||||
_onEventsReceived: function(appointments) {
|
||||
let newEvents = [];
|
||||
if (appointments != null) {
|
||||
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 summary = a[1];
|
||||
let allDay = a[3];
|
||||
let event = new CalendarEvent(date, end, summary, allDay);
|
||||
newEvents.push(event);
|
||||
}
|
||||
//log('when:' + event.date + ' summary:' + event.summary);
|
||||
}
|
||||
if (this._includeTransientEvent && this._transientEvent.date >= begin && this._transientEvent.date <= end)
|
||||
result.push(this._transientEvent);
|
||||
result.sort(function(event1, event2) {
|
||||
newEvents.sort(function(event1, event2) {
|
||||
return event1.date.getTime() - event2.date.getTime();
|
||||
});
|
||||
}
|
||||
|
||||
this._events = newEvents;
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
_loadEvents: function(forceReload) {
|
||||
if (this._curRequestBegin && this._curRequestEnd){
|
||||
let callFlags = 0;
|
||||
if (forceReload)
|
||||
callFlags |= DBus.CALL_FLAG_START;
|
||||
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
|
||||
this._curRequestEnd.getTime() / 1000,
|
||||
forceReload,
|
||||
Lang.bind(this, this._onEventsReceived),
|
||||
callFlags);
|
||||
}
|
||||
},
|
||||
|
||||
requestRange: function(begin, end, forceReload) {
|
||||
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
||||
this._lastRequestBegin = begin;
|
||||
this._lastRequestEnd = end;
|
||||
this._curRequestBegin = begin;
|
||||
this._curRequestEnd = end;
|
||||
this._loadEvents(forceReload);
|
||||
}
|
||||
},
|
||||
|
||||
getEvents: function(begin, end) {
|
||||
let result = [];
|
||||
for(let n = 0; n < this._events.length; n++) {
|
||||
let event = this._events[n];
|
||||
if (_dateIntervalsOverlap (event.date, event.end, begin, end)) {
|
||||
result.push(event);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
@ -343,9 +342,8 @@ FakeEventSource.prototype = {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
Signals.addSignalMethods(FakeEventSource.prototype);
|
||||
Signals.addSignalMethods(DBusEventSource.prototype);
|
||||
|
||||
// Calendar:
|
||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||
@ -358,7 +356,10 @@ Calendar.prototype = {
|
||||
_init: function(eventSource) {
|
||||
this._eventSource = eventSource;
|
||||
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
this._eventSource.connect('changed', Lang.bind(this,
|
||||
function() {
|
||||
this._update(false);
|
||||
}));
|
||||
|
||||
// FIXME: This is actually the fallback method for GTK+ for the week start;
|
||||
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
|
||||
@ -407,15 +408,17 @@ Calendar.prototype = {
|
||||
Lang.bind(this, this._onScroll));
|
||||
|
||||
this._buildHeader ();
|
||||
this._update();
|
||||
},
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
setDate: function(date) {
|
||||
setDate: function(date, forceReload) {
|
||||
if (!_sameDay(date, this._selectedDate)) {
|
||||
this._selectedDate = date;
|
||||
this._update();
|
||||
this._update(forceReload);
|
||||
this.emit('selected-date-changed', new Date(this._selectedDate));
|
||||
} else {
|
||||
if (forceReload)
|
||||
this._update(forceReload);
|
||||
}
|
||||
},
|
||||
|
||||
@ -510,7 +513,7 @@ Calendar.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
this.setDate(newDate);
|
||||
this.setDate(newDate, false);
|
||||
},
|
||||
|
||||
_onNextMonthButtonClicked: function() {
|
||||
@ -532,16 +535,16 @@ Calendar.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
this.setDate(newDate);
|
||||
this.setDate(newDate, false);
|
||||
},
|
||||
|
||||
_onSettingsChange: function() {
|
||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||
this._buildHeader();
|
||||
this._update();
|
||||
this._update(false);
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
_update: function(forceReload) {
|
||||
let now = new Date();
|
||||
|
||||
if (_sameYear(this._selectedDate, now))
|
||||
@ -570,7 +573,7 @@ Calendar.prototype = {
|
||||
let iterStr = iter.toUTCString();
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
let newlySelectedDate = new Date(iterStr);
|
||||
this.setDate(newlySelectedDate);
|
||||
this.setDate(newlySelectedDate, false);
|
||||
}));
|
||||
|
||||
let hasEvents = this._eventSource.hasEvents(iter);
|
||||
@ -620,7 +623,7 @@ Calendar.prototype = {
|
||||
}
|
||||
// Signal to the event source that we are interested in events
|
||||
// only from this date range
|
||||
this._eventSource.requestRange(beginDate, iter);
|
||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||
}
|
||||
};
|
||||
|
||||
@ -698,7 +701,7 @@ EventsList.prototype = {
|
||||
if (events.length == 0 && showNothingScheduled) {
|
||||
let now = new Date();
|
||||
/* Translators: Text to show if there are no events */
|
||||
let nothingEvent = new CalendarEvent(now, _("Nothing Scheduled"), true);
|
||||
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
|
||||
let timeString = _formatEventTime(nothingEvent, clockFormat);
|
||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
|
||||
}
|
||||
|
@ -52,9 +52,7 @@ DateMenuButton.prototype = {
|
||||
let hbox;
|
||||
let vbox;
|
||||
|
||||
//this._eventSource = new Calendar.EmptyEventSource();
|
||||
//this._eventSource = new Calendar.FakeEventSource();
|
||||
this._eventSource = new Calendar.EvolutionEventSource();
|
||||
this._eventSource = new Calendar.DBusEventSource();
|
||||
|
||||
let menuAlignment = 0.25;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
@ -117,7 +115,20 @@ DateMenuButton.prototype = {
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
if (isOpen) {
|
||||
let now = new Date();
|
||||
this._calendar.setDate(now);
|
||||
/* Passing true to setDate() forces events to be reloaded. We
|
||||
* want this behavior, because
|
||||
*
|
||||
* o It will cause activation of the calendar server which is
|
||||
* useful if it has crashed
|
||||
*
|
||||
* o It will cause the calendar server to reload events which
|
||||
* is useful if dynamic updates are not supported or not
|
||||
* properly working
|
||||
*
|
||||
* Since this only happens when the menu is opened, the cost
|
||||
* isn't very big.
|
||||
*/
|
||||
this._calendar.setDate(now, true);
|
||||
// No need to update this._eventList as ::selected-date-changed
|
||||
// signal will fire
|
||||
}
|
||||
|
@ -95,6 +95,10 @@ function start() {
|
||||
// back into sync ones.
|
||||
DBus.session.flush();
|
||||
|
||||
// Load the calendar server. Note that we are careful about
|
||||
// not loading any events until the user presses the clock
|
||||
global.launch_calendar_server();
|
||||
|
||||
Environment.init();
|
||||
|
||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||
|
@ -1,22 +0,0 @@
|
||||
|
||||
noinst_LTLIBRARIES += libcalendar-client.la
|
||||
|
||||
libcalendar_client_la_SOURCES = \
|
||||
calendar-client/calendar-client.h calendar-client/calendar-client.c \
|
||||
calendar-client/calendar-debug.h \
|
||||
calendar-client/calendar-sources.c calendar-client/calendar-sources.h \
|
||||
$(NULL)
|
||||
|
||||
libcalendar_client_la_CFLAGS = \
|
||||
-I$(top_srcdir)/src \
|
||||
-DPREFIX=\""$(prefix)"\" \
|
||||
-DLIBDIR=\""$(libdir)"\" \
|
||||
-DDATADIR=\""$(datadir)"\" \
|
||||
-DG_DISABLE_DEPRECATED \
|
||||
-DG_LOG_DOMAIN=\"CalendarClient\" \
|
||||
$(LIBECAL_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
libcalendar_client_la_LIBADD = $(LIBECAL_LIBS)
|
||||
|
||||
EXTRA_DIST += calendar-client/README
|
34
src/Makefile-calendar-server.am
Normal file
34
src/Makefile-calendar-server.am
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
servicedir = $(datadir)/dbus-1/services
|
||||
service_in_files = calendar-server/org.gnome.Shell.CalendarServer.service.in
|
||||
service_DATA = $(service_in_files:.service.in=.service)
|
||||
|
||||
$(service_DATA): $(service_in_files) Makefile
|
||||
@sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@.tmp && mv $@.tmp $@
|
||||
|
||||
libexec_PROGRAMS += gnome-shell-calendar-server
|
||||
|
||||
gnome_shell_calendar_server_SOURCES = \
|
||||
calendar-server/calendar-debug.h \
|
||||
calendar-server/calendar-sources.c calendar-server/calendar-sources.h \
|
||||
calendar-server/gnome-shell-calendar-server.c \
|
||||
$(NULL)
|
||||
|
||||
gnome_shell_calendar_server_CFLAGS = \
|
||||
-I$(top_srcdir)/src \
|
||||
-DPREFIX=\""$(prefix)"\" \
|
||||
-DLIBDIR=\""$(libdir)"\" \
|
||||
-DDATADIR=\""$(datadir)"\" \
|
||||
-DG_DISABLE_DEPRECATED \
|
||||
-DG_LOG_DOMAIN=\"ShellCalendarServer\" \
|
||||
$(CALENDAR_SERVER_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
gnome_shell_calendar_server_LDFLAGS = \
|
||||
$(CALENDAR_SERVER_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST += \
|
||||
calendar-server/README \
|
||||
calendar-server/org.gnome.Shell.CalendarServer.service.in \
|
||||
$(NULL)
|
@ -27,13 +27,14 @@ include Makefile-gdmuser.am
|
||||
include Makefile-st.am
|
||||
include Makefile-tray.am
|
||||
include Makefile-gvc.am
|
||||
include Makefile-calendar-client.am
|
||||
include Makefile-calendar-server.am
|
||||
|
||||
gnome_shell_cflags = \
|
||||
$(MUTTER_PLUGIN_CFLAGS) \
|
||||
$(LIBGNOMEUI_CFLAGS) \
|
||||
-I$(srcdir)/tray \
|
||||
-DLOCALEDIR=\"$(datadir)/locale\" \
|
||||
-DGNOME_SHELL_LIBEXECDIR=\"$(libexecdir)\" \
|
||||
-DGNOME_SHELL_DATADIR=\"$(pkgdatadir)\" \
|
||||
-DGNOME_SHELL_PKGLIBDIR=\"$(pkglibdir)\" \
|
||||
-DJSDIR=\"$(pkgdatadir)/js\"
|
||||
@ -88,8 +89,6 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-arrow.c \
|
||||
shell-doc-system.c \
|
||||
shell-embedded-window.c \
|
||||
shell-evolution-event-source.h \
|
||||
shell-evolution-event-source.c \
|
||||
shell-generic-container.c \
|
||||
shell-gtk-embed.c \
|
||||
shell-global.c \
|
||||
@ -216,10 +215,9 @@ libgnome_shell_la_LIBADD = \
|
||||
libgdmuser-1.0.la \
|
||||
libtray.la \
|
||||
libgvc.la \
|
||||
libcalendar-client.la \
|
||||
$(NULL)
|
||||
|
||||
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
|
||||
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags) $(LIBECAL_CFLAGS)
|
||||
|
||||
typelibdir = $(pkglibdir)
|
||||
typelib_DATA = Shell-0.1.typelib St-1.0.typelib Gdm-1.0.typelib Gvc-1.0.typelib
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Authors:
|
||||
* Mark McLoughlin <mark@skynet.ie>
|
||||
* William Jon McCann <mccann@jhu.edu>
|
||||
* Martin Grimme <martin@pycage.de>
|
||||
* Christian Kellner <gicmo@xatom.net>
|
||||
*/
|
||||
|
||||
#ifndef __CALENDAR_CLIENT_H__
|
||||
#define __CALENDAR_CLIENT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CALENDAR_EVENT_APPOINTMENT = 1 << 0,
|
||||
CALENDAR_EVENT_TASK = 1 << 1,
|
||||
CALENDAR_EVENT_ALL = (1 << 2) - 1
|
||||
} CalendarEventType;
|
||||
|
||||
#define CALENDAR_TYPE_CLIENT (calendar_client_get_type ())
|
||||
#define CALENDAR_CLIENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CALENDAR_TYPE_CLIENT, CalendarClient))
|
||||
#define CALENDAR_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), CALENDAR_TYPE_CLIENT, CalendarClientClass))
|
||||
#define CALENDAR_IS_CLIENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CALENDAR_TYPE_CLIENT))
|
||||
#define CALENDAR_IS_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CALENDAR_TYPE_CLIENT))
|
||||
#define CALENDAR_CLIENT_GET_CLASS(o)(G_TYPE_INSTANCE_GET_CLASS ((o), CALENDAR_TYPE_CLIENT, CalendarClientClass))
|
||||
|
||||
typedef struct _CalendarClient CalendarClient;
|
||||
typedef struct _CalendarClientClass CalendarClientClass;
|
||||
typedef struct _CalendarClientPrivate CalendarClientPrivate;
|
||||
|
||||
struct _CalendarClient
|
||||
{
|
||||
GObject parent;
|
||||
CalendarClientPrivate *priv;
|
||||
};
|
||||
|
||||
struct _CalendarClientClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* appointments_changed) (CalendarClient *client);
|
||||
void (* tasks_changed) (CalendarClient *client);
|
||||
};
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
time_t start_time;
|
||||
time_t end_time;
|
||||
} CalendarOccurrence;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *uid;
|
||||
char *rid;
|
||||
char *uri;
|
||||
char *summary;
|
||||
char *description;
|
||||
char *color_string;
|
||||
time_t start_time;
|
||||
time_t end_time;
|
||||
guint is_all_day : 1;
|
||||
|
||||
/* Only used internally */
|
||||
GSList *occurrences;
|
||||
} CalendarAppointment;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *uid;
|
||||
char *summary;
|
||||
char *description;
|
||||
char *color_string;
|
||||
char *url;
|
||||
time_t start_time;
|
||||
time_t due_time;
|
||||
guint percent_complete;
|
||||
time_t completed_time;
|
||||
int priority;
|
||||
} CalendarTask;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
CalendarAppointment appointment;
|
||||
CalendarTask task;
|
||||
} event;
|
||||
CalendarEventType type;
|
||||
} CalendarEvent;
|
||||
|
||||
#define CALENDAR_EVENT(e) ((CalendarEvent *)(e))
|
||||
#define CALENDAR_APPOINTMENT(e) ((CalendarAppointment *)(e))
|
||||
#define CALENDAR_TASK(e) ((CalendarTask *)(e))
|
||||
|
||||
typedef void (* CalendarDayIter) (CalendarClient *client,
|
||||
guint day,
|
||||
gpointer user_data);
|
||||
|
||||
|
||||
GType calendar_client_get_type (void) G_GNUC_CONST;
|
||||
CalendarClient *calendar_client_new (void);
|
||||
|
||||
void calendar_client_get_date (CalendarClient *client,
|
||||
guint *year,
|
||||
guint *month,
|
||||
guint *day);
|
||||
void calendar_client_select_month (CalendarClient *client,
|
||||
guint month,
|
||||
guint year);
|
||||
void calendar_client_select_day (CalendarClient *client,
|
||||
guint day);
|
||||
|
||||
GSList *calendar_client_get_events (CalendarClient *client,
|
||||
CalendarEventType event_mask);
|
||||
void calendar_client_foreach_appointment_day (CalendarClient *client,
|
||||
CalendarDayIter iter_func,
|
||||
gpointer user_data);
|
||||
|
||||
void calendar_client_set_task_completed (CalendarClient *client,
|
||||
char *task_uid,
|
||||
gboolean task_completed,
|
||||
guint percent_complete);
|
||||
|
||||
void calendar_event_free (CalendarEvent *event);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CALENDAR_CLIENT_H__ */
|
1109
src/calendar-server/gnome-shell-calendar-server.c
Normal file
1109
src/calendar-server/gnome-shell-calendar-server.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@
|
||||
[D-BUS Service]
|
||||
Name=org.gnome.Shell.CalendarServer
|
||||
Exec=@libexecdir@/gnome-shell-calendar-server
|
@ -197,7 +197,8 @@ def start_shell(perf_output=None):
|
||||
if running_from_source_tree:
|
||||
if os.environ.has_key('GI_TYPELIB_PATH'):
|
||||
typelib_dir = typelib_dir + ":" + os.environ.get('GI_TYPELIB_PATH')
|
||||
env.update({'GNOME_SHELL_DATADIR' : data_dir,
|
||||
env.update({'GNOME_SHELL_BINDIR' : bin_dir,
|
||||
'GNOME_SHELL_DATADIR' : data_dir,
|
||||
'GI_TYPELIB_PATH' : typelib_dir,
|
||||
'GSETTINGS_SCHEMA_DIR' : data_dir })
|
||||
else:
|
||||
|
@ -1,265 +0,0 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "calendar-client/calendar-client.h"
|
||||
#include "shell-evolution-event-source.h"
|
||||
|
||||
|
||||
struct _ShellEvolutionEventSourceClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
struct _ShellEvolutionEventSource {
|
||||
GObject parent;
|
||||
CalendarClient *client;
|
||||
/* The month that we are currently requesting events from */
|
||||
gint req_year;
|
||||
gint req_mon; /* starts at 1, not zero */
|
||||
};
|
||||
|
||||
/* Signals */
|
||||
enum
|
||||
{
|
||||
CHANGED_SIGNAL,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (ShellEvolutionEventSource, shell_evolution_event_source, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
on_tasks_changed (CalendarClient *client,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellEvolutionEventSource *source = SHELL_EVOLUTION_EVENT_SOURCE (user_data);
|
||||
/* g_print ("on tasks changed\n"); */
|
||||
g_signal_emit (source, signals[CHANGED_SIGNAL], 0);
|
||||
}
|
||||
|
||||
static void
|
||||
on_appointments_changed (CalendarClient *client,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellEvolutionEventSource *source = SHELL_EVOLUTION_EVENT_SOURCE (user_data);
|
||||
/* g_print ("on appointments changed\n"); */
|
||||
g_signal_emit (source, signals[CHANGED_SIGNAL], 0);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_evolution_event_source_init (ShellEvolutionEventSource *source)
|
||||
{
|
||||
source->client = calendar_client_new ();
|
||||
g_signal_connect (source->client,
|
||||
"tasks-changed",
|
||||
G_CALLBACK (on_tasks_changed),
|
||||
source);
|
||||
g_signal_connect (source->client,
|
||||
"appointments-changed",
|
||||
G_CALLBACK (on_appointments_changed),
|
||||
source);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_evolution_event_source_finalize (GObject *object)
|
||||
{
|
||||
ShellEvolutionEventSource *source = SHELL_EVOLUTION_EVENT_SOURCE (object);
|
||||
g_object_unref (source->client);
|
||||
G_OBJECT_CLASS (shell_evolution_event_source_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_evolution_event_source_class_init (ShellEvolutionEventSourceClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = shell_evolution_event_source_finalize;
|
||||
|
||||
signals[CHANGED_SIGNAL] =
|
||||
g_signal_new ("changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
}
|
||||
|
||||
ShellEvolutionEventSource *
|
||||
shell_evolution_event_source_new (void)
|
||||
{
|
||||
return SHELL_EVOLUTION_EVENT_SOURCE (g_object_new (SHELL_TYPE_EVOLUTION_EVENT_SOURCE, NULL));
|
||||
}
|
||||
|
||||
void
|
||||
shell_evolution_event_source_request_range (ShellEvolutionEventSource *source,
|
||||
gint64 msec_begin,
|
||||
gint64 msec_end)
|
||||
{
|
||||
GDateTime *middle_utc, *middle;
|
||||
|
||||
/* The CalendarClient type is a convenience wrapper on top of
|
||||
* Evolution Data Server. It is based on the assumption that only
|
||||
* a single month is shown at a time.
|
||||
*
|
||||
* To avoid reimplemting all the work already done in CalendarClient
|
||||
* we make the same assumption. This means that we only show events
|
||||
* in the month that is in the middle of @msec_begin and
|
||||
* @msec_end. Since the Shell displays a month at a time (plus the
|
||||
* days before and after) it works out just fine.
|
||||
*/
|
||||
|
||||
middle_utc = g_date_time_new_from_unix_utc ((msec_begin + msec_end) / 2 / 1000);
|
||||
/* CalendarClient uses localtime rather than UTC */
|
||||
middle = g_date_time_to_local (middle_utc);
|
||||
g_date_time_unref (middle_utc);
|
||||
g_date_time_get_ymd (middle, &source->req_year, &source->req_mon, NULL);
|
||||
g_date_time_unref (middle);
|
||||
calendar_client_select_month (source->client, source->req_mon - 1, source->req_year);
|
||||
}
|
||||
|
||||
static gint
|
||||
event_cmp (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
const ShellEvolutionEvent *ea;
|
||||
const ShellEvolutionEvent *eb;
|
||||
|
||||
ea = a;
|
||||
eb = b;
|
||||
if (ea->msec_begin < eb->msec_begin)
|
||||
return -1;
|
||||
else if (ea->msec_begin > eb->msec_begin)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_evolution_event_source_get_events:
|
||||
* @source: A #ShellEvolutionEventSource.
|
||||
* @msec_begin: Start date (milli-seconds since Epoch).
|
||||
* @msec_end: End date (milli-seconds since Epoch).
|
||||
*
|
||||
* Gets all events that occur between @msec_begin and @msec_end.
|
||||
*
|
||||
* Returns: (element-type ShellEvolutionEvent) (transfer full): List of events.
|
||||
*/
|
||||
GList *
|
||||
shell_evolution_event_source_get_events (ShellEvolutionEventSource *source,
|
||||
gint64 msec_begin,
|
||||
gint64 msec_end)
|
||||
{
|
||||
GList *result;
|
||||
GDateTime *cur_date;
|
||||
GDateTime *begin_date_utc, *begin_date;
|
||||
GDateTime *end_date_utc, *end_date;
|
||||
|
||||
g_return_val_if_fail (msec_begin <= msec_end, NULL);
|
||||
|
||||
result = NULL;
|
||||
|
||||
begin_date_utc = g_date_time_new_from_unix_utc (msec_begin / 1000);
|
||||
end_date_utc = g_date_time_new_from_unix_utc (msec_end / 1000);
|
||||
|
||||
/* CalendarClient uses localtime rather than UTC */
|
||||
begin_date = g_date_time_to_local (begin_date_utc);
|
||||
end_date = g_date_time_to_local (end_date_utc);
|
||||
g_date_time_unref (begin_date_utc);
|
||||
g_date_time_unref (end_date_utc);
|
||||
|
||||
cur_date = g_date_time_ref (begin_date);
|
||||
do
|
||||
{
|
||||
gint year, mon, day;
|
||||
GDateTime *next_date;
|
||||
|
||||
g_date_time_get_ymd (cur_date, &year, &mon, &day);
|
||||
/* g_print ("y=%04d m=%02d d=%02d\n", year, mon, day); */
|
||||
|
||||
/* Silently drop events not in range (see comment in
|
||||
* shell_evolution_event_source_request_range() above)
|
||||
*/
|
||||
if (!(year == source->req_year && mon == source->req_mon))
|
||||
{
|
||||
/* g_print ("skipping day\n"); */
|
||||
}
|
||||
else
|
||||
{
|
||||
GSList *events;
|
||||
GSList *l;
|
||||
calendar_client_select_day (source->client, day);
|
||||
events = calendar_client_get_events (source->client, CALENDAR_EVENT_APPOINTMENT);
|
||||
/* g_print ("num_events: %d\n", g_slist_length (events)); */
|
||||
for (l = events; l; l = l->next)
|
||||
{
|
||||
CalendarAppointment *appointment = l->data;
|
||||
ShellEvolutionEvent *event;
|
||||
gint64 start_time;
|
||||
|
||||
if (appointment->is_all_day)
|
||||
{
|
||||
start_time = g_date_time_to_unix (cur_date) * G_GINT64_CONSTANT (1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
start_time = appointment->start_time * G_GINT64_CONSTANT (1000);
|
||||
}
|
||||
event = shell_evolution_event_new (appointment->summary,
|
||||
appointment->is_all_day,
|
||||
start_time);
|
||||
result = g_list_prepend (result, event);
|
||||
}
|
||||
g_slist_foreach (events, (GFunc) calendar_event_free, NULL);
|
||||
g_slist_free (events);
|
||||
}
|
||||
|
||||
next_date = g_date_time_add_days (cur_date, 1);
|
||||
g_date_time_unref (cur_date);
|
||||
cur_date = next_date;
|
||||
}
|
||||
while (g_date_time_difference (end_date, cur_date) > 0);
|
||||
g_date_time_unref (begin_date);
|
||||
g_date_time_unref (end_date);
|
||||
|
||||
result = g_list_sort (result, event_cmp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
G_DEFINE_BOXED_TYPE (ShellEvolutionEvent,
|
||||
shell_evolution_event,
|
||||
shell_evolution_event_copy,
|
||||
shell_evolution_event_free);
|
||||
|
||||
void
|
||||
shell_evolution_event_free (ShellEvolutionEvent *event)
|
||||
{
|
||||
g_free (event->summary);
|
||||
g_free (event);
|
||||
}
|
||||
|
||||
ShellEvolutionEvent *
|
||||
shell_evolution_event_copy (ShellEvolutionEvent *event)
|
||||
{
|
||||
ShellEvolutionEvent *copy;
|
||||
copy = g_memdup (event, sizeof (ShellEvolutionEvent));
|
||||
copy->summary = g_strdup (event->summary);
|
||||
return copy;
|
||||
}
|
||||
|
||||
ShellEvolutionEvent *
|
||||
shell_evolution_event_new (const gchar *summary,
|
||||
gboolean all_day,
|
||||
gint64 msec_begin)
|
||||
{
|
||||
ShellEvolutionEvent *event;
|
||||
event = g_new0 (ShellEvolutionEvent, 1);
|
||||
event->summary = g_strdup (summary);
|
||||
event->all_day = all_day;
|
||||
event->msec_begin = msec_begin;
|
||||
return event;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __SHELL_EVOLUTION_EVENT_SOURCE_H__
|
||||
#define __SHELL_EVOLUTION_EVENT_SOURCE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _ShellEvolutionEvent ShellEvolutionEvent;
|
||||
|
||||
struct _ShellEvolutionEvent
|
||||
{
|
||||
gchar *summary;
|
||||
gboolean all_day;
|
||||
gint64 msec_begin;
|
||||
};
|
||||
|
||||
GType shell_evolution_event_get_type (void) G_GNUC_CONST;
|
||||
ShellEvolutionEvent *shell_evolution_event_new (const gchar *summary,
|
||||
gboolean all_day,
|
||||
gint64 msec_begin);
|
||||
ShellEvolutionEvent *shell_evolution_event_copy (ShellEvolutionEvent *event);
|
||||
void shell_evolution_event_free (ShellEvolutionEvent *event);
|
||||
|
||||
typedef struct _ShellEvolutionEventSource ShellEvolutionEventSource;
|
||||
typedef struct _ShellEvolutionEventSourceClass ShellEvolutionEventSourceClass;
|
||||
|
||||
#define SHELL_TYPE_EVOLUTION_EVENT_SOURCE (shell_evolution_event_source_get_type ())
|
||||
#define SHELL_EVOLUTION_EVENT_SOURCE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_EVOLUTION_EVENT_SOURCE, ShellEvolutionEventSource))
|
||||
#define SHELL_EVOLUTION_EVENT_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_EVOLUTION_EVENT_SOURCE, ShellEvolutionEventSourceClass))
|
||||
#define SHELL_IS_EVOLUTION_EVENT_SOURCE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_EVOLUTION_EVENT_SOURCE))
|
||||
#define SHELL_IS_EVOLUTION_EVENT_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_EVOLUTION_EVENT_SOURCE))
|
||||
#define SHELL_EVOLUTION_EVENT_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_EVOLUTION_EVENT_SOURCE, ShellEvolutionEventSourceClass))
|
||||
|
||||
GType shell_evolution_event_source_get_type (void) G_GNUC_CONST;
|
||||
ShellEvolutionEventSource *shell_evolution_event_source_new (void);
|
||||
void shell_evolution_event_source_request_range (ShellEvolutionEventSource *source,
|
||||
gint64 msec_begin,
|
||||
gint64 msec_end);
|
||||
GList *shell_evolution_event_source_get_events (ShellEvolutionEventSource *source,
|
||||
gint64 msec_begin,
|
||||
gint64 msec_end);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_EVOLUTION_EVENT_SOURCE_H__ */
|
@ -1984,3 +1984,57 @@ shell_get_tp_contacts (TpConnection *self,
|
||||
shell_global_get_tp_contacts_cb,
|
||||
callback, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_launch_calendar_server:
|
||||
* @global: The #ShellGlobal.
|
||||
*
|
||||
* Launch the gnome-shell-calendar-server helper.
|
||||
*/
|
||||
void
|
||||
shell_global_launch_calendar_server (ShellGlobal *global)
|
||||
{
|
||||
const gchar *bin_dir;
|
||||
gchar *calendar_server_exe;
|
||||
GError *error;
|
||||
gchar *argv[2];
|
||||
gint child_standard_input;
|
||||
|
||||
/* launch calendar-server */
|
||||
bin_dir = g_getenv ("GNOME_SHELL_BINDIR");
|
||||
if (bin_dir != NULL)
|
||||
calendar_server_exe = g_strdup_printf ("%s/gnome-shell-calendar-server", bin_dir);
|
||||
else
|
||||
calendar_server_exe = g_strdup_printf (GNOME_SHELL_LIBEXECDIR "/gnome-shell-calendar-server");
|
||||
|
||||
argv[0] = calendar_server_exe;
|
||||
argv[1] = NULL;
|
||||
error = NULL;
|
||||
if (!g_spawn_async_with_pipes (NULL, /* working_directory */
|
||||
argv,
|
||||
NULL, /* envp */
|
||||
0, /* GSpawnFlags */
|
||||
NULL, /* child_setup */
|
||||
NULL, /* user_data */
|
||||
NULL, /* GPid *child_pid */
|
||||
&child_standard_input,
|
||||
NULL, /* gint *stdout */
|
||||
NULL, /* gint *stderr */
|
||||
&error))
|
||||
{
|
||||
g_warning ("Error launching `%s': %s (%s %d)",
|
||||
calendar_server_exe,
|
||||
error->message,
|
||||
g_quark_to_string (error->domain),
|
||||
error->code);
|
||||
g_error_free (error);
|
||||
}
|
||||
/* Note that gnome-shell-calendar-server exits whenever its stdin
|
||||
* file descriptor is HUP'ed. This means that whenever the the shell
|
||||
* process exits or is being replaced, the calendar server is also
|
||||
* exits...and if the shell is being replaced, a new copy of the
|
||||
* calendar server is launched...
|
||||
*/
|
||||
|
||||
g_free (calendar_server_exe);
|
||||
}
|
||||
|
@ -156,6 +156,8 @@ void shell_get_tp_contacts (TpConnection *self,
|
||||
const TpContactFeature *features,
|
||||
ShellGetTpContactCb callback);
|
||||
|
||||
void shell_global_launch_calendar_server (ShellGlobal *global);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_GLOBAL_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user