Enable libnotify (desktop notifications) for Linux.

Nicky 2012-03-21 21:43:41 +01:00
parent 394ff74f11
commit 1c2674d552
6 changed files with 291 additions and 98 deletions

View File

@ -4,6 +4,9 @@
# LO - Mind the comment mess, I can only test on windows so im using the comments to make it do nothing on ohter systems
if (STANDALONE)
if( LINUX )
add_definitions( -DHAS_GROWL)
endif( LINUX )
#set(LIBNOTIFY_FIND_REQUIRED ON)
#include(FindLibnotify)
#set(GROWL_INCLUDE_DIRS ${LIBNOTIFY_INCLUDE_DIR})
@ -15,13 +18,12 @@ else (STANDALONE)
#set(GROWL_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/Growl)
#set(GROWL_LIBRARY growl)
elseif (WINDOWS)
include(Prebuilt)
use_prebuilt_binary(Growl)
set(GROWL_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/Growl)
set(GROWL_LIBRARY growl++)
elseif (LINUX)
# Everything glib-2.0 and GTK-specific is pulled in by UI.cmake.. Ugh.
#set(GROWL_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/libnotify)
#set(GROWL_LIBRARY notify)
include(Prebuilt)
use_prebuilt_binary(Growl)
set(GROWL_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/Growl)
set(GROWL_LIBRARY growl++)
add_definitions( -DHAS_GROWL)
elseif (LINUX)
add_definitions( -DHAS_GROWL)
endif (DARWIN)
endif (STANDALONE)

View File

@ -44,9 +44,7 @@ include(ViewerMiscLibs)
include(LLLogin)
include(GLOD)
include(CMakeCopyIfDifferent)
if (WINDOWS)
include(Growl)
endif (WINDOWS)
include_directories(
${DBUSGLIB_INCLUDE_DIRS}
@ -1355,8 +1353,9 @@ endif (DARWIN)
if (LINUX)
LIST(APPEND viewer_SOURCE_FILES llappviewerlinux.cpp)
LIST(APPEND viewer_SOURCE_FILES llappviewerlinux_api_dbus.cpp)
#LIST(APPEND viewer_HEADER_FILES desktopnotifierlinux.h)
#LIST(APPEND viewer_SOURCE_FILES desktopnotifierlinux.cpp)
LIST(APPEND viewer_HEADER_FILES desktopnotifierlinux.h growlmanager.h)
LIST(APPEND viewer_SOURCE_FILES desktopnotifierlinux.cpp growlmanager.cpp)
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
set(viewer_LIBRARIES

View File

@ -30,100 +30,284 @@
#include "llviewerprecompiledheaders.h"
#include "desktopnotifierlinux.h"
#include "notify.h"
// ND; LL's version of glib/gtk is so old, that finding a distrib old enough to to create a libnotify that links against prebuild glib seemed impossible.
// That's why libnotify is loaded dynamically if present.
#include <dlfcn.h>
#include <glib/gtypes.h>
#include <glib/gerror.h>
#include <glib/gmem.h>
typedef enum
{
NOTIFY_URGENCY_LOW,
NOTIFY_URGENCY_NORMAL,
NOTIFY_URGENCY_CRITICAL,
} NotifyUrgency;
struct NotifyNotification;
typedef gboolean (*pND_notify_init) ( const char *app_name );
typedef void (*pND_notify_uninit) ( void );
typedef gboolean (*pND_notify_is_initted) ( void );
typedef gboolean (*pND_notify_get_server_info) ( char **ret_name, char **ret_vendor, char **ret_version, char **ret_spec_version );
typedef gboolean (*pND_notify_notification_update) ( NotifyNotification *notification,const char *summary, const char *body, const char *icon );
typedef gboolean (*pND_notify_notification_show) ( NotifyNotification *notification, GError **error );
typedef void (*pND_notify_notification_set_timeout) ( NotifyNotification *notification, gint timeout );
typedef void (*pND_notify_notification_set_category) ( NotifyNotification *notification, const char *category );
typedef void (*pND_notify_notification_set_urgency) ( NotifyNotification *notification, NotifyUrgency urgency );
// ND; Never versions of notify_notification_new lack the forth parameters.
// As GCC uses cdecl for x86 and and AMD64 ABI for x64 it is safe to always pass the forth parameter. That's due to the fact that the caller cleans the stack and params are passed right to left. So
// the unused one is always pushed first and qualifies just as dead weight.
typedef NotifyNotification* (*pND_notify_notification_new) (const char *summary, const char *body, const char *icon, void*);
void* tryLoadLibnotify()
{
char const* aNames[] = {
"libnotify.so",
"libnotify.so.7",
"libnotify.so.6",
"libnotify.so.5",
"libnotify.so.4",
"libnotify.so.3",
"libnotify.so.2",
"libnotify.so.1",
0 };
void *pLibHandle(0);
for( int i = 0; !pLibHandle && aNames[i]; ++i )
{
pLibHandle = dlopen( aNames[i], RTLD_NOW );
if( !pLibHandle )
LL_INFOS( "DesktopNotifierLinux" ) << dlerror() << LL_ENDL;
else
LL_INFOS( "DesktopNotifierLinux" ) << "Loaded " << aNames[i] << LL_ENDL;
}
return pLibHandle;
};
template< typename tFunc > tFunc dlSym( void * aLibHandle, char const *aSym )
{
tFunc pRet = reinterpret_cast< tFunc >( dlsym( aLibHandle, aSym ) );
if( !pRet )
{
char const *pErr = dlerror();
if( !pErr )
pErr = "0";
LL_WARNS( "DesktopNotifierLinux" ) << "Error finding symbol '" << aSym << "' in libnotify.so, dlerror: '" << pErr << LL_ENDL;
}
else
{
LL_INFOS( "DesktopNotifierLinux" ) << "Resolved symbol '" << aSym << "' in libnotify.so to " << (void*)pRet << LL_ENDL;
}
return pRet;
}
#define DLSYM( handle, sym ) dlSym<pND_##sym>( handle, #sym )
struct NDLibnotifyWrapper
{
void *mLibHandle;
pND_notify_init mInit;
pND_notify_uninit mUnInit;
pND_notify_is_initted mIsInitted;
pND_notify_get_server_info mGetServerInfo;
pND_notify_notification_update mNotificationUpdate;
pND_notify_notification_show mNotificationShow;
pND_notify_notification_set_timeout mNotificationSetTimeout;
pND_notify_notification_set_category mNotificationSetCategory;
pND_notify_notification_set_urgency mNotificationSetUrgency;
pND_notify_notification_new mNotificationNew;
NDLibnotifyWrapper()
{
zeroMember();
}
void zeroMember()
{
mLibHandle = 0;
mInit = 0;
mUnInit = 0;
mIsInitted = 0;
mGetServerInfo = 0;
mNotificationUpdate = 0;
mNotificationShow = 0;
mNotificationSetTimeout = 0;
mNotificationSetCategory = 0;
mNotificationSetUrgency = 0;
mNotificationNew = 0;
}
~NDLibnotifyWrapper()
{
destroy();
}
bool isUseable()
{
return mInit &&
mUnInit &&
mIsInitted &&
mGetServerInfo &&
mNotificationUpdate &&
mNotificationShow &&
mNotificationSetTimeout &&
mNotificationSetCategory &&
mNotificationSetUrgency &&
mNotificationNew;
}
bool init()
{
if( isUseable() )
return true;
mLibHandle = tryLoadLibnotify();
if( !mLibHandle )
{
LL_WARNS( "DesktopNotifierLinux" ) << "Cannot load libnotify" << LL_ENDL;
return false;
}
else
{
LL_INFOS( "DesktopNotifierLinux" ) << "Loaded libnotify at address " << mLibHandle << LL_ENDL;
}
mInit = DLSYM( mLibHandle, notify_init );
mUnInit = DLSYM( mLibHandle, notify_uninit );
mIsInitted = DLSYM( mLibHandle, notify_is_initted );
mGetServerInfo = DLSYM( mLibHandle, notify_get_server_info );
mNotificationUpdate = DLSYM( mLibHandle, notify_notification_update );
mNotificationShow = DLSYM( mLibHandle, notify_notification_show );
mNotificationSetTimeout = DLSYM( mLibHandle, notify_notification_set_timeout );
mNotificationSetCategory = DLSYM( mLibHandle, notify_notification_set_category );
mNotificationSetUrgency = DLSYM( mLibHandle, notify_notification_set_urgency );
mNotificationNew = DLSYM( mLibHandle, notify_notification_new );
return isUseable();
}
void destroy()
{
if( !mLibHandle )
return;
dlclose( mLibHandle );
zeroMember();
}
};
const char* ICON_128 = "firestorm_icon128.png";
const char* ICON_512 = "firestorm_icon.png";
const gint NOTIFICATION_TIMEOUT_MS = 5000;
static const char* icon_wholename;
const char* Find_BMP_Resource(const char *basename)
std::string Find_BMP_Resource( bool a_bSmallIcon )
{
const int PATH_BUFFER_SIZE=1000;
char* path_buffer = new char[PATH_BUFFER_SIZE]; /* Flawfinder: ignore */
// Figure out where our BMP is living on the disk
snprintf(path_buffer, PATH_BUFFER_SIZE-1, "%s%sres-sdl%s%s",
gDirUtilp->getAppRODataDir().c_str(),
gDirUtilp->getDirDelimiter().c_str(),
gDirUtilp->getDirDelimiter().c_str(),
basename);
path_buffer[PATH_BUFFER_SIZE-1] = '\0';
return path_buffer;
const std::string ICON_128( "firestorm_icon128.png" );
const std::string ICON_512( "firestorm_icon.png" );
std::string strRet( gDirUtilp->getAppRODataDir() );
strRet += gDirUtilp->getDirDelimiter();
strRet += "res-sdl";
strRet += gDirUtilp->getDirDelimiter();
if( a_bSmallIcon )
strRet += ICON_128;
else
strRet += ICON_512;
return strRet;
}
DesktopNotifierLinux::DesktopNotifierLinux()
{
if (notify_init("Firestorm Viewer")) {
LL_INFOS("DesktopNotifierLinux") << "Linux desktop notifications initialized." << LL_ENDL;
// Find the name of our notification server. I kinda don't expect it to change after the start of the program.
gchar* name = NULL;
gchar* vendor = NULL;
gchar* version = NULL;
gchar* spec = NULL;
bool info_success = notify_get_server_info(&name, &vendor, &version, &spec);
if (info_success) {
LL_INFOS("DesktopNotifierLinux") << "Server name: " << name << LL_ENDL;
LL_INFOS("DesktopNotifierLinux") << "Server vendor: " << vendor << LL_ENDL;
LL_INFOS("DesktopNotifierLinux") << "Server version: " << version << LL_ENDL;
LL_INFOS("DesktopNotifierLinux") << "Server spec: " << spec << LL_ENDL;
}
if (!info_success || strncmp("notification-daemon", name, 19)) {
// We're likely talking to notification-daemon, and I don't feel like scaling. Use a premade 128x128 icon.
icon_wholename = Find_BMP_Resource(ICON_128);
} else {
// Talking to NotifyOSD or something else. Try the 512x512 icon and let it scale on its own.
icon_wholename = Find_BMP_Resource(ICON_512);
}
LL_INFOS("DesktopNotifierLinux") << "Linux desktop notification icon: " << icon_wholename << LL_ENDL;
} else {
LL_WARNS("DesktopNotifierLinux") << "Linux desktop notifications FAILED to initialize." << LL_ENDL;
m_pLibNotify = new NDLibnotifyWrapper();
m_pNotification = 0;
if ( m_pLibNotify->init() && m_pLibNotify->mInit( "Firestorm Viewer" ) )
{
LL_INFOS( "DesktopNotifierLinux" ) << "Linux desktop notifications initialized." << LL_ENDL;
// Find the name of our notification server. I kinda don't expect it to change after the start of the program.
char *name(0), *vendor(0), *version(0), *spec(0);
bool info_success = m_pLibNotify->mGetServerInfo( &name, &vendor, &version, &spec );
if ( info_success )
{
LL_INFOS( "DesktopNotifierLinux" ) << "Server name: " << name << LL_ENDL;
LL_INFOS( "DesktopNotifierLinux" ) << "Server vendor: " << vendor << LL_ENDL;
LL_INFOS( "DesktopNotifierLinux" ) << "Server version: " << version << LL_ENDL;
LL_INFOS( "DesktopNotifierLinux" ) << "Server spec: " << spec << LL_ENDL;
}
// When talking to notification-daemon use a 128x128 icon to avoid downscaling, all other daemons seem to be ok to scale?
bool bSmallIcon(false);
if ( !info_success || strncmp( "notification-daemon", name, 19 ) )
bSmallIcon = true;
g_free( name );
g_free( vendor );
g_free( version );
g_free( spec );
m_strIcon = Find_BMP_Resource( bSmallIcon );
LL_INFOS( "DesktopNotifierLinux" ) << "Linux desktop notification icon: " << m_strIcon << LL_ENDL;
m_pNotification = m_pLibNotify->mNotificationNew( "Firestorm", "Intializing", m_strIcon.c_str(), 0 );
}
else
{
LL_WARNS( "DesktopNotifierLinux" ) << "Linux desktop notifications FAILED to initialize." << LL_ENDL;
}
}
void DesktopNotifierLinux::showNotification(const std::string& notification_title, const std::string& notification_message, const std::string& notification_type)
DesktopNotifierLinux::~DesktopNotifierLinux()
{
LL_INFOS("DesktopNotifierLinux") << "New notification title: " << notification_title << LL_ENDL;
LL_INFOS("DesktopNotifierLinux") << "New notification message: " << notification_message << LL_ENDL;
LL_INFOS("DesktopNotifierLinux") << "New notification type: " << notification_type << LL_ENDL;
static NotifyNotification* notification = notify_notification_new(
"Firestorm Viewer",//(gchar*)notification_title.c_str(),
NULL,//(gchar*)notification_message.c_str(),
icon_wholename,
NULL
);
notify_notification_update(
notification,
(gchar*)notification_title.c_str(),
(gchar*)notification_message.c_str(),
icon_wholename
);
notify_notification_set_urgency(notification, NOTIFY_URGENCY_LOW);
notify_notification_set_category(notification, (gchar*)notification_type.c_str());
notify_notification_set_timeout(notification, NOTIFICATION_TIMEOUT_MS); // NotifyOSD ignores this, sadly.
GError* error = NULL;
if (notify_notification_show(notification, &error)) {
LL_INFOS("DesktopNotifierLinux") << "Linux desktop notification type " << notification_type << "sent." << LL_ENDL;
} else {
LL_WARNS("DesktopNotifierLinux") << "Linux desktop notification FAILED to send. " << error->message << LL_ENDL;
}
delete m_pLibNotify;
}
void DesktopNotifierLinux::showNotification( const std::string& notification_title, const std::string& notification_message, const std::string& notification_type )
{
LL_INFOS( "DesktopNotifierLinux" ) << "New notification title: " << notification_title << LL_ENDL;
LL_INFOS( "DesktopNotifierLinux" ) << "New notification message: " << notification_message << LL_ENDL;
LL_INFOS( "DesktopNotifierLinux" ) << "New notification type: " << notification_type << LL_ENDL;
m_pLibNotify->mNotificationUpdate( m_pNotification,(gchar*)notification_title.c_str(), (gchar*)notification_message.c_str(), m_strIcon.c_str() );
m_pLibNotify->mNotificationSetUrgency( m_pNotification, NOTIFY_URGENCY_LOW );
m_pLibNotify->mNotificationSetCategory( m_pNotification, ( gchar* )notification_type.c_str() );
m_pLibNotify->mNotificationSetTimeout( m_pNotification, NOTIFICATION_TIMEOUT_MS ); // NotifyOSD ignores this, sadly.
GError* error(0);
if ( m_pLibNotify->mNotificationShow( m_pNotification, &error ) )
{
LL_INFOS( "DesktopNotifierLinux" ) << "Linux desktop notification type " << notification_type << "sent." << LL_ENDL;
}
else
{
LL_WARNS( "DesktopNotifierLinux" ) << "Linux desktop notification FAILED to send. " << error->message << LL_ENDL;
}
}
bool DesktopNotifierLinux::isUsable()
{
return notify_is_initted();
return m_pLibNotify->isUseable() && m_pLibNotify->mIsInitted() && m_pNotification;
}
/*void DesktopNotifierLinux::registerApplication(const std::string& application, const std::set<std::string>& notificationTypes)
void DesktopNotifierLinux::registerApplication(const std::string& application, const std::set<std::string>& notificationTypes)
{
// Do nothing for now.
}*/
}
bool DesktopNotifierLinux::needsThrottle()
{

View File

@ -35,14 +35,22 @@
class DesktopNotifierLinux : public GrowlNotifier
{
LOG_CLASS(DesktopNotifierLinux);
public:
DesktopNotifierLinux();
~DesktopNotifierLinux(){}
LOG_CLASS( DesktopNotifierLinux );
void showNotification(const std::string& notification_title, const std::string& notification_message, const std::string& notificationTypes);
struct NDLibnotifyWrapper *m_pLibNotify;
struct NotifyNotification *m_pNotification;
std::string m_strIcon;
DesktopNotifierLinux( DesktopNotifierLinux const & );
DesktopNotifierLinux& operator=(DesktopNotifierLinux const& );
public:
DesktopNotifierLinux();
~DesktopNotifierLinux();
void showNotification( const std::string& notification_title, const std::string& notification_message, const std::string& notificationTypes );
bool isUsable();
//void registerApplication(const std::string& application, const std::set<std::string>& notificationTypes);
void registerApplication(const std::string& application, const std::set<std::string>& notificationTypes);
bool needsThrottle();
};

View File

@ -53,7 +53,7 @@
#elif LL_WINDOWS
#include "growlnotifierwin.h"
#elif LL_LINUX
//#include "desktopnotifierlinux.h"
#include "desktopnotifierlinux.h"
#endif
@ -69,15 +69,15 @@ GrowlManager::GrowlManager() : LLEventTimer(GROWL_THROTTLE_CLEANUP_PERIOD)
this->mNotifier = new GrowlNotifierWin();
LL_INFOS("GrowlManagerInit") << "Created GrowlNotifierWin." << LL_ENDL;
#elif LL_LINUX
//this->mNotifier = new DesktopNotifierLinux();
//LL_INFOS("GrowlManagerInit") << "Created DesktopNotifierLinux." << LL_ENDL;
this->mNotifier = new DesktopNotifierLinux();
LL_INFOS("GrowlManagerInit") << "Created DesktopNotifierLinux." << LL_ENDL;
#else
this->mNotifier = new GrowlNotifier();
LL_INFOS("GrowlManagerInit") << "Created generic GrowlNotifier." << LL_ENDL;
#endif
// Don't do anything more if Growl isn't usable.
if(!mNotifier->isUsable())
if( !mNotifier || !mNotifier->isUsable())
{
LL_WARNS("GrowlManagerInit") << "Growl is unusable; bailing out." << LL_ENDL;
return;

View File

@ -205,7 +205,7 @@
#include "llnotificationmanager.h"
#if LL_WINDOWS
#if HAS_GROWL
#include "growlmanager.h"
#endif
@ -438,7 +438,7 @@ bool idle_startup()
std::string lastGPU = gSavedSettings.getString("LastGPUString");
std::string thisGPU = LLFeatureManager::getInstance()->getGPUString();
#if LL_WINDOWS
#if HAS_GROWL
GrowlManager::InitiateManager();
#endif