Removed dependency on gtkmm/libnotify for fdo-style notifications.

First step toward unifying code for Gnome and KDE notifications.
This commit is contained in:
Guillaume Melquiond 2009-08-09 23:21:08 +00:00
parent 13bc5406f2
commit 96a88e73a0
2 changed files with 146 additions and 80 deletions

View file

@ -284,11 +284,11 @@ AC_ARG_ENABLE([notifications],
[notifications=$enableval],
[notifications=yes])
AC_ARG_ENABLE([gtkmm],
AS_HELP_STRING([--disable-gtkmm],
[disable gtkmm support for notifications]),
[gtkmm=$enableval],
[gtkmm=yes])
AC_ARG_ENABLE([dbus],
AS_HELP_STRING([--disable-dbus],
[disable dbus support for notifications]),
[dbus=$enableval],
[dbus=yes])
#if test "x$game" = "xno"
#then
@ -873,14 +873,12 @@ LIBS="$LIBS $LUA_LIBS"
#######################################################################
found_notifications=no
if test "x$notifications" = "xyes"; then
if test "x$gtkmm" = "xyes"; then
PKG_CHECK_MODULES([GTKMM], [gtkmm-2.4 >= 2.8.0], [
PKG_CHECK_MODULES([LIBNOTIFY], [libnotifymm-1.0], [
CPPFLAGS="$CPPFLAGS $GTKMM_CFLAGS $LIBNOTIFY_CFLAGS"
LIBS="$LIBS $GTKMM_LIBS $LIBNOTIFY_LIBS"
found_notifications=yes
AC_DEFINE([HAVE_LIBNOTIFY],,[Define if you have Libnotify for GTK.])
], [AC_MSG_RESULT([no])])
if test "x$dbus" = "xyes"; then
PKG_CHECK_MODULES([LIBDBUS], [dbus-1], [
CPPFLAGS="$CPPFLAGS $LIBDBUS_CFLAGS"
LIBS="$LIBS $LIBDBUS_LIBS"
found_notifications=yes
AC_DEFINE([HAVE_LIBDBUS],,[Define if you have libdbus.])
], [AC_MSG_RESULT([no])])
fi
# tests for other notification systems

View file

@ -21,8 +21,8 @@
#include "game_display.hpp"
#ifdef HAVE_LIBNOTIFY
#include <libnotifymm-1.0/libnotifymm.h>
#ifdef HAVE_LIBDBUS
#include <dbus/dbus.h>
#endif
#ifdef HAVE_GROWL
@ -1038,73 +1038,128 @@ std::string game_display::current_team_name() const
return std::string();
}
#ifdef HAVE_LIBNOTIFY
/**
* Class for libnotify-based auto-updating desktop notifications.
*/
struct wnotify: public Notify::Notification
#ifdef HAVE_LIBDBUS
static int kde_style = 0;
struct wnotify
{
typedef std::list<wnotify *> tset;
static tset set;
bool closed;
wnotify(std::string const &owner, std::string const &icon)
: Notify::Notification(owner, "", icon), closed(false)
{}
virtual void on_closed()
{ closed = true; }
void append(std::string const &body)
{ property_body() = property_body() + body; }
/**
* Finds the notification for a given owner.
* Removes obsolete notifications along the way.
*/
static wnotify *locate(std::string const &owner)
{
// Ensure "closed" signals are propagated.
while (Glib::MainContext::get_default()->iteration(false)) {}
tset::iterator i = set.begin(), i_end = set.end(), res = i_end;
while (i != i_end)
{
tset::iterator j = i++;
if ((*j)->property_summary() == owner) {
res = j;
break;
}
if (!(*j)->closed) continue;
delete *j;
set.erase(j);
}
wnotify *n;
if (res != i_end) {
n = *res;
n->property_body() = n->closed ?
"" : n->property_body() + '\n';
return n;
}
static Glib::ustring wesnoth_icon =
game_config::path + "/images/wesnoth-icon-small.png";
n = new wnotify(owner, wesnoth_icon);
set.push_back(n);
return n;
}
uint32_t id;
std::string owner;
std::string message;
};
wnotify::tset wnotify::set;
static std::list<wnotify> notifications;
static DBusHandlerResult filter_dbus_signal(DBusConnection *, DBusMessage *buf, void *)
{
if (!dbus_message_is_signal(buf, "org.freedesktop.Notifications", "NotificationClosed")) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
uint32_t id;
dbus_message_get_args(buf, NULL,
DBUS_TYPE_UINT32, &id,
DBUS_TYPE_INVALID);
std::list<wnotify>::iterator i = notifications.begin(),
i_end = notifications.end();
while (i != i_end) {
std::list<wnotify>::iterator j = i++;
if (j->id == id) continue;
notifications.erase(j);
break;
}
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusConnection *get_dbus_connection()
{
static bool initted = false;
static DBusConnection *connection = NULL;
if (!initted)
{
initted = true;
DBusError err;
dbus_error_init(&err);
connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
if (!connection) {
ERR_DP << "Failed to open DBus session: " << err.message << '\n';
dbus_error_free(&err);
return NULL;
}
dbus_connection_add_filter(connection, filter_dbus_signal, NULL, NULL);
}
if (connection) {
dbus_connection_read_write(connection, 0);
while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) {}
}
return connection;
}
static uint32_t send_dbus_notification(DBusConnection *connection, uint32_t replaces_id,
const std::string &owner, const std::string &message)
{
DBusMessage *buf = dbus_message_new_method_call(
kde_style ? "org.kde.VisualNotifications" : "org.freedesktop.Notifications",
kde_style ? "/org/kde/VisualNotifications" : "/org/freedesktop/Notifications",
kde_style ? "org.kde.VisualNotifications" : "org.freedesktop.Notifications",
"Notify");
const char *app_name = "Battle for Wesnoth";
dbus_message_append_args(buf,
DBUS_TYPE_STRING, &app_name,
DBUS_TYPE_UINT32, &replaces_id,
DBUS_TYPE_INVALID);
if (kde_style) {
const char *event_id = "";
dbus_message_append_args(buf,
DBUS_TYPE_STRING, &event_id,
DBUS_TYPE_INVALID);
}
std::string app_icon_ = game_config::path + "/images/wesnoth-icon.png";
const char *app_icon = app_icon_.c_str();
const char *summary = owner.c_str();
const char *body = message.c_str();
const char **actions = NULL;
dbus_message_append_args(buf,
DBUS_TYPE_STRING, &app_icon,
DBUS_TYPE_STRING, &summary,
DBUS_TYPE_STRING, &body,
DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &actions, 0,
DBUS_TYPE_INVALID);
DBusMessageIter iter, hints;
dbus_message_iter_init_append(buf, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &hints);
dbus_message_iter_close_container(&iter, &hints);
int expire_timeout = -1;
dbus_message_append_args(buf,
DBUS_TYPE_INT32, &expire_timeout,
DBUS_TYPE_INVALID);
DBusError err;
dbus_error_init(&err);
DBusMessage *ret = dbus_connection_send_with_reply_and_block(connection, buf, 1000, &err);
dbus_message_unref(buf);
if (!ret) {
ERR_DP << "Failed to send visual notification: " << err.message << '\n';
dbus_error_free(&err);
return 0;
}
uint32_t id;
dbus_message_get_args(ret, NULL,
DBUS_TYPE_UINT32, &id,
DBUS_TYPE_INVALID);
dbus_message_unref(ret);
// TODO: remove once closing signals for KDE are handled in filter_dbus_signal.
if (kde_style) return 0;
return id;
}
#endif
#if defined(HAVE_LIBNOTIFY) || defined(HAVE_QTDBUS) || defined(HAVE_GROWL)
#if defined(HAVE_LIBDBUS) || defined(HAVE_QTDBUS) || defined(HAVE_GROWL)
void game_display::send_notification(const std::string& owner, const std::string& message)
#else
void game_display::send_notification(const std::string& /*owner*/, const std::string& /*message*/)
#endif
{
#if defined(HAVE_LIBNOTIFY) || defined(HAVE_QTDBUS) || defined(HAVE_GROWL)
#if defined(HAVE_LIBDBUS) || defined(HAVE_QTDBUS) || defined(HAVE_GROWL)
Uint8 app_state = SDL_GetAppState();
// Do not show notifications when the window is visible...
@ -1117,15 +1172,28 @@ void game_display::send_notification(const std::string& /*owner*/, const std::st
}
#endif
#ifdef HAVE_LIBNOTIFY
try {
if (!Notify::is_initted()) Notify::init("Wesnoth");
wnotify *n = wnotify::locate(owner);
n->append(message);
n->show();
} catch(const Glib::Error& error) {
ERR_DP << "Failed to send libnotify notification: " << error.what() << "\n";
#ifdef HAVE_LIBDBUS
DBusConnection *connection = get_dbus_connection();
if (!connection) return;
std::list<wnotify>::iterator i = notifications.begin(),
i_end = notifications.end();
while (i != i_end && i->owner != owner) ++i;
if (i != i_end) {
i->message += "\n";
i->message += message;
send_dbus_notification(connection, i->id, owner, i->message);
return;
}
uint32_t id = send_dbus_notification(connection, 0, owner, message);
if (!id) return;
wnotify visual;
visual.id = id;
visual.owner = owner;
visual.message = message;
notifications.push_back(visual);
#endif
#ifdef HAVE_QTDBUS