diff options
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/daemon/Makefile.am | 48 | ||||
-rw-r--r-- | src/daemon/Makefile.in | 643 | ||||
-rw-r--r-- | src/daemon/daemon.c | 1141 | ||||
-rw-r--r-- | src/daemon/daemon.h | 106 | ||||
-rw-r--r-- | src/daemon/engines.c | 308 | ||||
-rw-r--r-- | src/daemon/engines.h | 47 | ||||
-rw-r--r-- | src/daemon/notificationdaemon.xml | 44 | ||||
-rw-r--r-- | src/daemon/stack.c | 278 | ||||
-rw-r--r-- | src/daemon/stack.h | 52 |
10 files changed, 2671 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..69939d3 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,4 @@ +# $Id$ + +SUBDIRS = \ + daemon diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am new file mode 100644 index 0000000..c7c8310 --- /dev/null +++ b/src/daemon/Makefile.am @@ -0,0 +1,48 @@ +# $Id: Makefile.am 2337 2007-01-11 19:17:30Z nick $ + +INCLUDES = \ + -I$(top_srcdir) \ + -DDBUS_API_SUBJECT_TO_CHANGE \ + -DG_LOG_DOMAIN=\"notification-daemon-xfce\" \ + -DENGINES_DIR=\"$(libdir)/notification-daemon-xfce-1.0/engines\" + +libexec_PROGRAMS = \ + notification-daemon-xfce + +notification_daemon_xfce_SOURCES = \ + daemon.c \ + daemon.h \ + engines.c \ + engines.h \ + stack.c \ + stack.h + +notification_daemon_xfce_CFLAGS = \ + $(GTK_CFLAGS) \ + $(LIBXFCEGUI4_CFLAGS) \ + $(LIBXFCE4UTIL_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(DBUS_GLIB_CFLAGS) + + +notification_daemon_xfce_LDADD = \ + $(GTK_LIBS) \ + $(LIBXFCEGUI4_LIBS) \ + $(LIBXFCE4UTIL_LIBS) \ + $(DBUS_LIBS) \ + $(DBUS_GLIB_LIBS) + +BUILT_SOURCES = \ + notificationdaemon-dbus-glue.h + +notificationdaemon-dbus-glue.h: notificationdaemon.xml + dbus-binding-tool \ + --mode=glib-server \ + --prefix=notification_daemon_xfce \ + $(srcdir)/notificationdaemon.xml > notificationdaemon-dbus-glue.h + +EXTRA_DIST = \ + notificationdaemon.xml + +DISTCLEANFILES = \ + notificationdaemon-dbus-glue.h diff --git a/src/daemon/Makefile.in b/src/daemon/Makefile.in new file mode 100644 index 0000000..cd5b21f --- /dev/null +++ b/src/daemon/Makefile.in @@ -0,0 +1,643 @@ +# Makefile.in generated by automake 1.10 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# $Id: Makefile.am 2337 2007-01-11 19:17:30Z nick $ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +libexec_PROGRAMS = notification-daemon-xfce$(EXEEXT) +subdir = src/daemon +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(libexecdir)" +libexecPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(libexec_PROGRAMS) +am_notification_daemon_xfce_OBJECTS = \ + notification_daemon_xfce-daemon.$(OBJEXT) \ + notification_daemon_xfce-engines.$(OBJEXT) \ + notification_daemon_xfce-stack.$(OBJEXT) +notification_daemon_xfce_OBJECTS = \ + $(am_notification_daemon_xfce_OBJECTS) +am__DEPENDENCIES_1 = +notification_daemon_xfce_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +notification_daemon_xfce_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(notification_daemon_xfce_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I. -I$(top_builddir)@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(notification_daemon_xfce_SOURCES) +DIST_SOURCES = $(notification_daemon_xfce_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_GLIB_CFLAGS = @DBUS_GLIB_CFLAGS@ +DBUS_GLIB_LIBS = @DBUS_GLIB_LIBS@ +DBUS_GLIB_REQUIRED_VERSION = @DBUS_GLIB_REQUIRED_VERSION@ +DBUS_GLIB_VERSION = @DBUS_GLIB_VERSION@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_REQUIRED_VERSION = @DBUS_REQUIRED_VERSION@ +DBUS_VERSION = @DBUS_VERSION@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +GTHREAD_CFLAGS = @GTHREAD_CFLAGS@ +GTHREAD_LIBS = @GTHREAD_LIBS@ +GTHREAD_REQUIRED_VERSION = @GTHREAD_REQUIRED_VERSION@ +GTHREAD_VERSION = @GTHREAD_VERSION@ +GTK_CFLAGS = @GTK_CFLAGS@ +GTK_LIBS = @GTK_LIBS@ +GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@ +GTK_VERSION = @GTK_VERSION@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_CAVES_RULE = @INTLTOOL_CAVES_RULE@ +INTLTOOL_DESKTOP_RULE = @INTLTOOL_DESKTOP_RULE@ +INTLTOOL_DIRECTORY_RULE = @INTLTOOL_DIRECTORY_RULE@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_ICONV = @INTLTOOL_ICONV@ +INTLTOOL_KBD_RULE = @INTLTOOL_KBD_RULE@ +INTLTOOL_KEYS_RULE = @INTLTOOL_KEYS_RULE@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_MSGFMT = @INTLTOOL_MSGFMT@ +INTLTOOL_MSGMERGE = @INTLTOOL_MSGMERGE@ +INTLTOOL_OAF_RULE = @INTLTOOL_OAF_RULE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_PONG_RULE = @INTLTOOL_PONG_RULE@ +INTLTOOL_PROP_RULE = @INTLTOOL_PROP_RULE@ +INTLTOOL_SCHEMAS_RULE = @INTLTOOL_SCHEMAS_RULE@ +INTLTOOL_SERVER_RULE = @INTLTOOL_SERVER_RULE@ +INTLTOOL_SERVICE_RULE = @INTLTOOL_SERVICE_RULE@ +INTLTOOL_SHEET_RULE = @INTLTOOL_SHEET_RULE@ +INTLTOOL_SOUNDLIST_RULE = @INTLTOOL_SOUNDLIST_RULE@ +INTLTOOL_THEME_RULE = @INTLTOOL_THEME_RULE@ +INTLTOOL_UI_RULE = @INTLTOOL_UI_RULE@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +INTLTOOL_XAM_RULE = @INTLTOOL_XAM_RULE@ +INTLTOOL_XGETTEXT = @INTLTOOL_XGETTEXT@ +INTLTOOL_XML_NOMERGE_RULE = @INTLTOOL_XML_NOMERGE_RULE@ +INTLTOOL_XML_RULE = @INTLTOOL_XML_RULE@ +LDFLAGS = @LDFLAGS@ +LIBNOTIFY_CFLAGS = @LIBNOTIFY_CFLAGS@ +LIBNOTIFY_LIBS = @LIBNOTIFY_LIBS@ +LIBNOTIFY_REQUIRED_VERSION = @LIBNOTIFY_REQUIRED_VERSION@ +LIBNOTIFY_VERSION = @LIBNOTIFY_VERSION@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSEXY_CFLAGS = @LIBSEXY_CFLAGS@ +LIBSEXY_LIBS = @LIBSEXY_LIBS@ +LIBSEXY_REQUIRED_VERSION = @LIBSEXY_REQUIRED_VERSION@ +LIBSEXY_VERSION = @LIBSEXY_VERSION@ +LIBTOOL = @LIBTOOL@ +LIBXFCE4UTIL_CFLAGS = @LIBXFCE4UTIL_CFLAGS@ +LIBXFCE4UTIL_LIBS = @LIBXFCE4UTIL_LIBS@ +LIBXFCE4UTIL_REQUIRED_VERSION = @LIBXFCE4UTIL_REQUIRED_VERSION@ +LIBXFCE4UTIL_VERSION = @LIBXFCE4UTIL_VERSION@ +LIBXFCEGUI4_CFLAGS = @LIBXFCEGUI4_CFLAGS@ +LIBXFCEGUI4_LIBS = @LIBXFCEGUI4_LIBS@ +LIBXFCEGUI4_REQUIRED_VERSION = @LIBXFCEGUI4_REQUIRED_VERSION@ +LIBXFCEGUI4_VERSION = @LIBXFCEGUI4_VERSION@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +XFCE_MCS_MANAGER_CFLAGS = @XFCE_MCS_MANAGER_CFLAGS@ +XFCE_MCS_MANAGER_LIBS = @XFCE_MCS_MANAGER_LIBS@ +XFCE_MCS_MANAGER_REQUIRED_VERSION = @XFCE_MCS_MANAGER_REQUIRED_VERSION@ +XFCE_MCS_MANAGER_VERSION = @XFCE_MCS_MANAGER_VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_ARGS = @XGETTEXT_ARGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I$(top_srcdir) \ + -DDBUS_API_SUBJECT_TO_CHANGE \ + -DG_LOG_DOMAIN=\"notification-daemon-xfce\" \ + -DENGINES_DIR=\"$(libdir)/notification-daemon-xfce-1.0/engines\" + +notification_daemon_xfce_SOURCES = \ + daemon.c \ + daemon.h \ + engines.c \ + engines.h \ + stack.c \ + stack.h + +notification_daemon_xfce_CFLAGS = \ + $(GTK_CFLAGS) \ + $(LIBXFCEGUI4_CFLAGS) \ + $(LIBXFCE4UTIL_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(DBUS_GLIB_CFLAGS) + +notification_daemon_xfce_LDADD = \ + $(GTK_LIBS) \ + $(LIBXFCEGUI4_LIBS) \ + $(LIBXFCE4UTIL_LIBS) \ + $(DBUS_LIBS) \ + $(DBUS_GLIB_LIBS) + +BUILT_SOURCES = \ + notificationdaemon-dbus-glue.h + +EXTRA_DIST = \ + notificationdaemon.xml + +DISTCLEANFILES = \ + notificationdaemon-dbus-glue.h + +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/daemon/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/daemon/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libexecPROGRAMS: $(libexec_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(libexecdir)" || $(MKDIR_P) "$(DESTDIR)$(libexecdir)" + @list='$(libexec_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(libexecPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(libexecdir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(libexecPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(libexecdir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-libexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(libexec_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(libexecdir)/$$f'"; \ + rm -f "$(DESTDIR)$(libexecdir)/$$f"; \ + done + +clean-libexecPROGRAMS: + @list='$(libexec_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +notification-daemon-xfce$(EXEEXT): $(notification_daemon_xfce_OBJECTS) $(notification_daemon_xfce_DEPENDENCIES) + @rm -f notification-daemon-xfce$(EXEEXT) + $(notification_daemon_xfce_LINK) $(notification_daemon_xfce_OBJECTS) $(notification_daemon_xfce_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notification_daemon_xfce-daemon.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notification_daemon_xfce-engines.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notification_daemon_xfce-stack.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +notification_daemon_xfce-daemon.o: daemon.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -MT notification_daemon_xfce-daemon.o -MD -MP -MF $(DEPDIR)/notification_daemon_xfce-daemon.Tpo -c -o notification_daemon_xfce-daemon.o `test -f 'daemon.c' || echo '$(srcdir)/'`daemon.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/notification_daemon_xfce-daemon.Tpo $(DEPDIR)/notification_daemon_xfce-daemon.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='daemon.c' object='notification_daemon_xfce-daemon.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -c -o notification_daemon_xfce-daemon.o `test -f 'daemon.c' || echo '$(srcdir)/'`daemon.c + +notification_daemon_xfce-daemon.obj: daemon.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -MT notification_daemon_xfce-daemon.obj -MD -MP -MF $(DEPDIR)/notification_daemon_xfce-daemon.Tpo -c -o notification_daemon_xfce-daemon.obj `if test -f 'daemon.c'; then $(CYGPATH_W) 'daemon.c'; else $(CYGPATH_W) '$(srcdir)/daemon.c'; fi` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/notification_daemon_xfce-daemon.Tpo $(DEPDIR)/notification_daemon_xfce-daemon.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='daemon.c' object='notification_daemon_xfce-daemon.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -c -o notification_daemon_xfce-daemon.obj `if test -f 'daemon.c'; then $(CYGPATH_W) 'daemon.c'; else $(CYGPATH_W) '$(srcdir)/daemon.c'; fi` + +notification_daemon_xfce-engines.o: engines.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -MT notification_daemon_xfce-engines.o -MD -MP -MF $(DEPDIR)/notification_daemon_xfce-engines.Tpo -c -o notification_daemon_xfce-engines.o `test -f 'engines.c' || echo '$(srcdir)/'`engines.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/notification_daemon_xfce-engines.Tpo $(DEPDIR)/notification_daemon_xfce-engines.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='engines.c' object='notification_daemon_xfce-engines.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -c -o notification_daemon_xfce-engines.o `test -f 'engines.c' || echo '$(srcdir)/'`engines.c + +notification_daemon_xfce-engines.obj: engines.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -MT notification_daemon_xfce-engines.obj -MD -MP -MF $(DEPDIR)/notification_daemon_xfce-engines.Tpo -c -o notification_daemon_xfce-engines.obj `if test -f 'engines.c'; then $(CYGPATH_W) 'engines.c'; else $(CYGPATH_W) '$(srcdir)/engines.c'; fi` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/notification_daemon_xfce-engines.Tpo $(DEPDIR)/notification_daemon_xfce-engines.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='engines.c' object='notification_daemon_xfce-engines.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -c -o notification_daemon_xfce-engines.obj `if test -f 'engines.c'; then $(CYGPATH_W) 'engines.c'; else $(CYGPATH_W) '$(srcdir)/engines.c'; fi` + +notification_daemon_xfce-stack.o: stack.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -MT notification_daemon_xfce-stack.o -MD -MP -MF $(DEPDIR)/notification_daemon_xfce-stack.Tpo -c -o notification_daemon_xfce-stack.o `test -f 'stack.c' || echo '$(srcdir)/'`stack.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/notification_daemon_xfce-stack.Tpo $(DEPDIR)/notification_daemon_xfce-stack.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='stack.c' object='notification_daemon_xfce-stack.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -c -o notification_daemon_xfce-stack.o `test -f 'stack.c' || echo '$(srcdir)/'`stack.c + +notification_daemon_xfce-stack.obj: stack.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -MT notification_daemon_xfce-stack.obj -MD -MP -MF $(DEPDIR)/notification_daemon_xfce-stack.Tpo -c -o notification_daemon_xfce-stack.obj `if test -f 'stack.c'; then $(CYGPATH_W) 'stack.c'; else $(CYGPATH_W) '$(srcdir)/stack.c'; fi` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/notification_daemon_xfce-stack.Tpo $(DEPDIR)/notification_daemon_xfce-stack.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='stack.c' object='notification_daemon_xfce-stack.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(notification_daemon_xfce_CFLAGS) $(CFLAGS) -c -o notification_daemon_xfce-stack.obj `if test -f 'stack.c'; then $(CYGPATH_W) 'stack.c'; else $(CYGPATH_W) '$(srcdir)/stack.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(libexecdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: install-libexecPROGRAMS + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libexecPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libexecPROGRAMS clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libexecPROGRAMS install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-libexecPROGRAMS + + +notificationdaemon-dbus-glue.h: notificationdaemon.xml + dbus-binding-tool \ + --mode=glib-server \ + --prefix=notification_daemon_xfce \ + $(srcdir)/notificationdaemon.xml > notificationdaemon-dbus-glue.h +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c new file mode 100644 index 0000000..4552764 --- /dev/null +++ b/src/daemon/daemon.c @@ -0,0 +1,1141 @@ +/* $Id: daemon.c 2337 2007-01-11 19:17:30Z nick $ + * + * Copyright (C) 2006 Christian Hammond <[email protected]> + * Copyright (C) 2005 John (J5) Palmieri <[email protected]> + * Copyright (C) 2006 Nick Schermer <[email protected]> + * + * 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, 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. + */ + +#include "config.h" + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +#include <X11/Xproto.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <gdk/gdkx.h> + +#include <libxfcegui4/libxfcegui4.h> +#include <libxfce4util/libxfce4util.h> + +#include "daemon.h" +#include "engines.h" +#include "stack.h" +#include "notificationdaemon-dbus-glue.h" + +#define IMAGE_SIZE 48 + +#define NW_GET_NOTIFY_ID(nw) \ + (GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(nw), "_notify_id"))) +#define NW_GET_NOTIFY_SENDER(nw) \ + (g_object_get_data(G_OBJECT(nw), "_notify_sender")) +#define NW_GET_DAEMON(nw) \ + (g_object_get_data(G_OBJECT(nw), "_notify_daemon")) + +typedef struct +{ + NotifyStackLocation type; + const gchar *identifier; + +} PopupNotifyStackLocation; + +const PopupNotifyStackLocation popup_stack_locations[] = +{ + {NOTIFY_STACK_LOCATION_TOP_LEFT, "top_left" }, + {NOTIFY_STACK_LOCATION_TOP_RIGHT, "top_right" }, + {NOTIFY_STACK_LOCATION_BOTTOM_LEFT, "bottom_left" }, + {NOTIFY_STACK_LOCATION_BOTTOM_RIGHT, "bottom_right" }, + {NOTIFY_STACK_LOCATION_UNKNOWN, NULL } +}; + +#define POPUP_STACK_DEFAULT_INDEX 3 /* XXX Hack! */ + +typedef struct +{ + GTimeVal expiration; + GTimeVal paused_diff; + gboolean has_timeout; + gboolean paused; + guint id; + GtkWindow *nw; + +} NotifyTimeout; + +struct _NotifyDaemonPrivate +{ + guint next_id; + guint timeout_source; + GHashTable *notification_hash; + gboolean url_clicked_lock; + NotifyStack **stacks; + gint stacks_size; +}; + +static DBusConnection *dbus_conn = NULL; + +#define CHECK_DBUS_VERSION(major, minor) \ + (DBUS_MAJOR_VER > (major) || \ + (DBUS_MAJOR_VER == (major) && DBUS_MINOR_VER >= (minor))) + +#if !CHECK_DBUS_VERSION(0, 60) +/* This is a hack that will go away in time. For now, it's fairly safe. */ +struct _DBusGMethodInvocation +{ + DBusGConnection *connection; + DBusGMessage *message; + const DBusGObjectInfo *object; + const DBusGMethodInfo *method; +}; +#endif /* D-BUS < 0.60 */ + +static void notify_daemon_finalize(GObject *object); +static void _close_notification(NotifyDaemon *daemon, guint id, + gboolean hide_notification); +static void _emit_closed_signal(GtkWindow *nw); +static void _action_invoked_cb(GtkWindow *nw, const char *key); +static NotifyStackLocation get_stack_location_from_string(const char *slocation); + +G_DEFINE_TYPE(NotifyDaemon, notify_daemon, G_TYPE_OBJECT); + +static void +notify_daemon_class_init(NotifyDaemonClass *daemon_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(daemon_class); + + object_class->finalize = notify_daemon_finalize; + + g_type_class_add_private(daemon_class, sizeof(NotifyDaemonPrivate)); +} + +static void +_notify_timeout_destroy(NotifyTimeout *nt) +{ + gtk_widget_destroy(GTK_WIDGET(nt->nw)); + g_free(nt); +} + +gchar * +xfce_load_setting (const gchar *key, + const gchar *fallback) +{ + gchar *file; + const gchar *tmp; + gchar *value = NULL; + XfceRc *rc; + + file = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, NOTIFY_RC_FILE, TRUE); + if (G_LIKELY (file)) + { + rc = xfce_rc_simple_open (file, FALSE); + g_free (file); + + if (G_LIKELY (rc)) + { + tmp = xfce_rc_read_entry (rc, key, NULL); + + if (G_LIKELY (tmp)) + value = g_strdup (tmp); + else + xfce_rc_write_entry (rc, key, fallback); + + xfce_rc_close (rc); + } + } + + return value; +} + +static void +notify_daemon_init(NotifyDaemon *daemon) +{ + NotifyStackLocation location; + GdkDisplay *display; + GdkScreen *screen; + gchar *slocation = NULL; + gint i; + + daemon->priv = G_TYPE_INSTANCE_GET_PRIVATE(daemon, NOTIFY_TYPE_DAEMON, + NotifyDaemonPrivate); + + daemon->priv->next_id = 1; + daemon->priv->timeout_source = 0; + + slocation = xfce_load_setting ("popup_location", "bottom_right"); + + location = get_stack_location_from_string(slocation); + g_free(slocation); + + display = gdk_display_get_default(); + screen = gdk_display_get_default_screen(display); + daemon->priv->stacks_size = gdk_screen_get_n_monitors(screen); + daemon->priv->stacks = g_new0(NotifyStack *, daemon->priv->stacks_size); + + for (i = 0; i < daemon->priv->stacks_size; i++) + { + daemon->priv->stacks[i] = notify_stack_new(daemon, screen, + i, location); + } + + daemon->priv->notification_hash = + g_hash_table_new_full(g_int_hash, g_int_equal, g_free, + (GDestroyNotify)_notify_timeout_destroy); +} + +static void +notify_daemon_finalize(GObject *object) +{ + NotifyDaemon *daemon = NOTIFY_DAEMON(object); + GObjectClass *parent_class = G_OBJECT_CLASS(notify_daemon_parent_class); + + g_hash_table_destroy(daemon->priv->notification_hash); + g_free(daemon->priv); + + if (parent_class->finalize != NULL) + parent_class->finalize(object); +} + +static NotifyStackLocation +get_stack_location_from_string(const char *slocation) +{ + NotifyStackLocation stack_location = NOTIFY_STACK_LOCATION_DEFAULT; + + if (slocation == NULL || *slocation == '\0') + return NOTIFY_STACK_LOCATION_DEFAULT; + else + { + const PopupNotifyStackLocation *l; + + for (l = popup_stack_locations; + l->type != NOTIFY_STACK_LOCATION_UNKNOWN; + l++) + { + if (!strcmp(slocation, l->identifier)) + stack_location = l->type; + } + } + + return stack_location; +} + +static DBusMessage * +create_signal(GtkWindow *nw, const char *signal_name) +{ + guint id = NW_GET_NOTIFY_ID(nw); + gchar *dest = NW_GET_NOTIFY_SENDER(nw); + DBusMessage *message; + + g_assert(dest != NULL); + + message = dbus_message_new_signal("/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + signal_name); + + dbus_message_set_destination(message, dest); + dbus_message_append_args(message, + DBUS_TYPE_UINT32, &id, + DBUS_TYPE_INVALID); + + return message; +} + +static void +_action_invoked_cb(GtkWindow *nw, const char *key) +{ + NotifyDaemon *daemon = NW_GET_DAEMON(nw); + guint id = NW_GET_NOTIFY_ID(nw); + DBusMessage *message; + + message = create_signal(nw, "ActionInvoked"); + dbus_message_append_args(message, + DBUS_TYPE_STRING, &key, + DBUS_TYPE_INVALID); + + dbus_connection_send(dbus_conn, message, NULL); + dbus_message_unref(message); + + _close_notification(daemon, id, TRUE); +} + +static void +_emit_closed_signal(GtkWindow *nw) +{ + DBusMessage *message = create_signal(nw, "NotificationClosed"); + dbus_connection_send(dbus_conn, message, NULL); + dbus_message_unref(message); +} + +static void +_close_notification(NotifyDaemon *daemon, guint id, gboolean hide_notification) +{ + NotifyDaemonPrivate *priv = daemon->priv; + NotifyTimeout *nt; + + nt = (NotifyTimeout *)g_hash_table_lookup(priv->notification_hash, &id); + + if (nt != NULL) + { + _emit_closed_signal(nt->nw); + + if (hide_notification) + theme_hide_notification(nt->nw); + + g_hash_table_remove(priv->notification_hash, &id); + } +} + +static void +_notification_destroyed_cb(GtkWindow *nw, NotifyDaemon *daemon) +{ + _close_notification(daemon, NW_GET_NOTIFY_ID(nw), FALSE); +} + +static void +_mouse_entered_cb(GtkWindow *nw, GdkEventCrossing *event, NotifyDaemon *daemon) +{ + NotifyTimeout *nt; + guint id; + GTimeVal now; + + if (event->detail == GDK_NOTIFY_INFERIOR) + return; + + id = NW_GET_NOTIFY_ID(nw); + nt = (NotifyTimeout *)g_hash_table_lookup(daemon->priv->notification_hash, + &id); + + nt->paused = TRUE; + g_get_current_time(&now); + + nt->paused_diff.tv_usec = nt->expiration.tv_usec - now.tv_usec; + nt->paused_diff.tv_sec = nt->expiration.tv_sec - now.tv_sec; + + if (nt->paused_diff.tv_usec < 0) + { + nt->paused_diff.tv_usec += G_USEC_PER_SEC; + nt->paused_diff.tv_sec--; + } +} + +static void +_mouse_exitted_cb(GtkWindow *nw, GdkEventCrossing *event, + NotifyDaemon *daemon) +{ + NotifyTimeout *nt; + guint id; + + if (event->detail == GDK_NOTIFY_INFERIOR) + return; + + id = NW_GET_NOTIFY_ID(nw); + nt = (NotifyTimeout *)g_hash_table_lookup(daemon->priv->notification_hash, + &id); + + nt->paused = FALSE; +} + +static gboolean +_is_expired(gpointer key, gpointer value, gpointer data) +{ + NotifyTimeout *nt = (NotifyTimeout *)value; + gboolean *phas_more_timeouts = (gboolean *)data; + time_t now_time; + time_t expiration_time; + GTimeVal now; + + if (!nt->has_timeout) + return FALSE; + + g_get_current_time(&now); + + expiration_time = (nt->expiration.tv_sec * 1000) + + (nt->expiration.tv_usec / 1000); + now_time = (now.tv_sec * 1000) + (now.tv_usec / 1000); + + if (now_time > expiration_time) + { + theme_notification_tick(nt->nw, 0); + _emit_closed_signal(nt->nw); + return TRUE; + } + else if (nt->paused) + { + nt->expiration.tv_usec = nt->paused_diff.tv_usec + now.tv_usec; + nt->expiration.tv_sec = nt->paused_diff.tv_sec + now.tv_sec; + + if (nt->expiration.tv_usec >= G_USEC_PER_SEC) + { + nt->expiration.tv_usec -= G_USEC_PER_SEC; + nt->expiration.tv_sec++; + } + } + else + { + theme_notification_tick(nt->nw, expiration_time - now_time); + } + + *phas_more_timeouts = TRUE; + + return FALSE; +} + +static gboolean +_check_expiration(gpointer data) +{ + NotifyDaemon *daemon = (NotifyDaemon *)data; + gboolean has_more_timeouts = FALSE; + + g_hash_table_foreach_remove(daemon->priv->notification_hash, + _is_expired, (gpointer)&has_more_timeouts); + + if (!has_more_timeouts) + daemon->priv->timeout_source = 0; + + return has_more_timeouts; +} + +static void +_calculate_timeout(NotifyDaemon *daemon, NotifyTimeout *nt, int timeout) +{ + if (timeout == 0) + nt->has_timeout = FALSE; + else + { + glong usec; + + nt->has_timeout = TRUE; + + if (timeout == -1) + timeout = NOTIFY_DAEMON_DEFAULT_TIMEOUT; + + theme_set_notification_timeout(nt->nw, timeout); + + usec = timeout * 1000; /* convert from msec to usec */ + + /* + * If it's less than 0, wrap around back to MAXLONG. + * g_time_val_add() requires a glong, and anything larger than + * MAXLONG will be treated as a negative value. + */ + if (usec < 0) + usec = G_MAXLONG; + + g_get_current_time(&nt->expiration); + g_time_val_add(&nt->expiration, usec); + + if (daemon->priv->timeout_source == 0) + { + daemon->priv->timeout_source = + g_timeout_add(100, _check_expiration, daemon); + } + } +} + +static guint +_store_notification(NotifyDaemon *daemon, GtkWindow *nw, int timeout) +{ + NotifyDaemonPrivate *priv = daemon->priv; + NotifyTimeout *nt; + guint id = 0; + + do + { + id = priv->next_id; + + if (id != UINT_MAX) + priv->next_id++; + else + priv->next_id = 1; + + if (g_hash_table_lookup(priv->notification_hash, &id) != NULL) + id = 0; + + } while (id == 0); + + nt = g_new0(NotifyTimeout, 1); + nt->id = id; + nt->nw = nw; + + _calculate_timeout(daemon, nt, timeout); + + g_hash_table_insert(priv->notification_hash, + g_memdup(&id, sizeof(guint)), nt); + + return id; +} + +static gboolean +_notify_daemon_process_icon_data(NotifyDaemon *daemon, GtkWindow *nw, + GValue *icon_data) +{ + const guchar *data = NULL; + gboolean has_alpha; + int bits_per_sample; + int width; + int height; + int rowstride; + int n_channels; + gsize expected_len; + GdkPixbuf *pixbuf; + GValueArray *image_struct; + GValue *value; + GArray *tmp_array; +#if CHECK_DBUS_VERSION(0, 61) + GType struct_type; + + struct_type = dbus_g_type_get_struct( + "GValueArray", + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_BOOLEAN, + G_TYPE_INT, + G_TYPE_INT, + dbus_g_type_get_collection("GArray", G_TYPE_UCHAR), + G_TYPE_INVALID); + + if (!G_VALUE_HOLDS(icon_data, struct_type)) + { + g_warning("_notify_daemon_process_icon_data expected a " + "GValue of type GValueArray"); + return FALSE; + } +#endif /* D-BUS >= 0.61 */ + + image_struct = (GValueArray *)g_value_get_boxed(icon_data); + value = g_value_array_get_nth(image_struct, 0); + + if (value == NULL) + { + g_warning("_notify_daemon_process_icon_data expected position " + "0 of the GValueArray to exist"); + return FALSE; + } + + if (!G_VALUE_HOLDS(value, G_TYPE_INT)) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 0 of the GValueArray to be of type int"); + return FALSE; + } + + width = g_value_get_int(value); + value = g_value_array_get_nth(image_struct, 1); + + if (value == NULL) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 1 of the GValueArray to exist"); + return FALSE; + } + + if (!G_VALUE_HOLDS(value, G_TYPE_INT)) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 1 of the GValueArray to be of type int"); + return FALSE; + } + + height = g_value_get_int(value); + value = g_value_array_get_nth(image_struct, 2); + + if (value == NULL) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 2 of the GValueArray to exist"); + return FALSE; + } + + if (!G_VALUE_HOLDS(value, G_TYPE_INT)) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 2 of the GValueArray to be of type int"); + return FALSE; + } + + rowstride = g_value_get_int(value); + value = g_value_array_get_nth(image_struct, 3); + + if (value == NULL) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 3 of the GValueArray to exist"); + return FALSE; + } + + if (!G_VALUE_HOLDS(value, G_TYPE_BOOLEAN)) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 3 of the GValueArray to be of type gboolean"); + return FALSE; + } + + has_alpha = g_value_get_boolean(value); + value = g_value_array_get_nth(image_struct, 4); + + if (value == NULL) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 4 of the GValueArray to exist"); + return FALSE; + } + + if (!G_VALUE_HOLDS(value, G_TYPE_INT)) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 4 of the GValueArray to be of type int"); + return FALSE; + } + + bits_per_sample = g_value_get_int(value); + value = g_value_array_get_nth(image_struct, 5); + + if (value == NULL) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 5 of the GValueArray to exist"); + return FALSE; + } + + if (!G_VALUE_HOLDS(value, G_TYPE_INT)) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 5 of the GValueArray to be of type int"); + return FALSE; + } + + n_channels = g_value_get_int(value); + value = g_value_array_get_nth(image_struct, 6); + + if (value == NULL) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 6 of the GValueArray to exist"); + return FALSE; + } + + if (!G_VALUE_HOLDS(value, + dbus_g_type_get_collection("GArray", G_TYPE_UCHAR))) + { + g_warning("_notify_daemon_process_icon_data expected " + "position 6 of the GValueArray to be of type GArray"); + return FALSE; + } + + tmp_array = (GArray *)g_value_get_boxed(value); + expected_len = (height - 1) * rowstride + width * + ((n_channels * bits_per_sample + 7) / 8); + + if (expected_len != tmp_array->len) + { + g_warning("_notify_daemon_process_icon_data expected image " + "data to be of length %i but got a length of %i", + expected_len, tmp_array->len); + return FALSE; + } + + data = (guchar *)g_memdup(tmp_array->data, tmp_array->len); + pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, has_alpha, + bits_per_sample, width, height, + rowstride, + (GdkPixbufDestroyNotify)g_free, + NULL); + theme_set_notification_icon(nw, pixbuf); + g_object_unref(G_OBJECT(pixbuf)); + + return TRUE; +} + +static void +window_clicked_cb(GtkWindow *nw, GdkEventButton *button, NotifyDaemon *daemon) +{ + if (daemon->priv->url_clicked_lock) + { + daemon->priv->url_clicked_lock = FALSE; + return; + } + + _action_invoked_cb(nw, "default"); + _close_notification(daemon, NW_GET_NOTIFY_ID(nw), TRUE); +} + +static void +popup_location_changed_cb(NotifyDaemon *daemon) +{ + NotifyStackLocation stack_location; + gchar *slocation; + gint i; + + if (daemon == NULL) + return; + + slocation = xfce_load_setting ("popup_location", "bottom_right"); + + g_message ("location: %s", slocation); + + stack_location = get_stack_location_from_string(slocation); + g_free (slocation); + + for (i = 0; i < daemon->priv->stacks_size; i++) + notify_stack_set_location(daemon->priv->stacks[i], stack_location); +} + +static void +url_clicked_cb(GtkWindow *nw, const char *url) +{ + NotifyDaemon *daemon = NW_GET_DAEMON(nw); + char *escaped_url; + char *cmd = NULL; + + /* Somewhat of a hack.. */ + daemon->priv->url_clicked_lock = TRUE; + + escaped_url = g_shell_quote(url); + + /* + * We can't actually check for GNOME_DESKTOP_SESSION_ID, because it's + * not in the environment for this program :( + */ + if (/*g_getenv("GNOME_DESKTOP_SESSION_ID") != NULL &&*/ + g_find_program_in_path("gnome-open") != NULL) + { + cmd = g_strdup_printf("gnome-open %s", escaped_url); + } + else if (g_find_program_in_path("mozilla-firefox") != NULL) + { + cmd = g_strdup_printf("mozilla-firefox %s", escaped_url); + } + else if (g_find_program_in_path("firefox") != NULL) + { + cmd = g_strdup_printf("firefox %s", escaped_url); + } + else if (g_find_program_in_path("mozilla") != NULL) + { + cmd = g_strdup_printf("mozilla %s", escaped_url); + } + else + { + g_warning("Unable to find a browser."); + } + + g_free(escaped_url); + + if (cmd != NULL) + { + g_spawn_command_line_async(cmd, NULL); + g_free(cmd); + } +} + +static gboolean +screensaver_active(GtkWidget *nw) +{ + GdkDisplay *display = gdk_drawable_get_display(GDK_DRAWABLE(nw->window)); + Atom type; + int format; + unsigned long nitems, bytes_after; + unsigned char *temp_data; + gboolean active = FALSE; + Atom XA_BLANK = gdk_x11_get_xatom_by_name_for_display(display, "BLANK"); + Atom XA_LOCK = gdk_x11_get_xatom_by_name_for_display(display, "LOCK"); + + /* Check for a screensaver first. */ + if (XGetWindowProperty( + GDK_DISPLAY_XDISPLAY(display), + GDK_ROOT_WINDOW(), + gdk_x11_get_xatom_by_name_for_display(display, "_SCREENSAVER_STATUS"), + 0, G_MAXLONG, False, XA_INTEGER, &type, &format, &nitems, + &bytes_after, &temp_data) == Success && + type && temp_data != NULL) + { + CARD32 *data = (CARD32 *)temp_data; + + active = (type == XA_INTEGER && nitems >= 3 && + (time_t)data[1] > (time_t)666000000L && + (data[0] == XA_BLANK || data[0] == XA_LOCK)); + } + + if (temp_data != NULL) + free(temp_data); + + return active; +} + +static gboolean +fullscreen_window_exists(GtkWidget *nw) +{ + NetkScreen *netk_screen; + GList *l; + + netk_screen = netk_screen_get(GDK_SCREEN_XNUMBER( + gdk_drawable_get_screen(GDK_DRAWABLE(GTK_WIDGET(nw)->window)))); + netk_screen_force_update(netk_screen); + + for (l = netk_screen_get_windows_stacked(netk_screen); + l != NULL; + l = l->next) + { + NetkWindow *netk_win = (NetkWindow *)l->data; + + if (netk_window_is_fullscreen(netk_win)) + { + /* + * Sanity check if the window is _really_ fullscreen to + * work around a bug in netk that doesn't get all + * unfullscreen events. + */ + int sw = netk_screen_get_width(netk_screen); + int sh = netk_screen_get_height(netk_screen); + int x, y, w, h; + + netk_window_get_geometry(netk_win, &x, &y, &w, &h); + + if (sw == w && sh == h) + return TRUE; + } + } + + return FALSE; +} + +gboolean +notify_daemon_notify_handler(NotifyDaemon *daemon, + const gchar *app_name, + guint id, + const gchar *icon, + const gchar *summary, + const gchar *body, + gchar **actions, + GHashTable *hints, + int timeout, DBusGMethodInvocation *context) +{ + NotifyDaemonPrivate *priv = daemon->priv; + NotifyTimeout *nt = NULL; + GtkWindow *nw = NULL; + GValue *data; + gboolean use_pos_data = FALSE; + gboolean new_notification = FALSE; + gint x = 0; + gint y = 0; + guint return_id; + gchar *sender; + gint i; + + if (id > 0) + { + nt = (NotifyTimeout *)g_hash_table_lookup(priv->notification_hash, + &id); + + if (nt != NULL) + nw = nt->nw; + else + id = 0; + } + + if (nw == NULL) + { + nw = theme_create_notification(url_clicked_cb); + g_object_set_data(G_OBJECT(nw), "_notify_daemon", daemon); + gtk_widget_realize(GTK_WIDGET(nw)); + new_notification = TRUE; + + g_signal_connect(G_OBJECT(nw), "button-release-event", + G_CALLBACK(window_clicked_cb), daemon); + g_signal_connect(G_OBJECT(nw), "destroy", + G_CALLBACK(_notification_destroyed_cb), daemon); + g_signal_connect(G_OBJECT(nw), "enter-notify-event", + G_CALLBACK(_mouse_entered_cb), daemon); + g_signal_connect(G_OBJECT(nw), "leave-notify-event", + G_CALLBACK(_mouse_exitted_cb), daemon); + } + else + { + theme_clear_notification_actions(nw); + } + + theme_set_notification_text(nw, summary, body); + theme_set_notification_hints(nw, hints); + + /* + *XXX This needs to handle file URIs and all that. + */ + + /* deal with x, and y hints */ + if ((data = (GValue *)g_hash_table_lookup(hints, "x")) != NULL) + { + x = g_value_get_int(data); + + if ((data = (GValue *)g_hash_table_lookup(hints, "y")) != NULL) + { + y = g_value_get_int(data); + use_pos_data = TRUE; + } + } + + /* set up action buttons */ + for (i = 0; actions[i] != NULL; i += 2) + { + gchar *l = actions[i + 1]; + + if (l == NULL) + { + g_warning("Label not found for action %s. " + "The protocol specifies that a label must " + "follow an action in the actions array", actions[i]); + + break; + } + + if (strcasecmp(actions[i], "default")) + { + theme_add_notification_action(nw, l, actions[i], + G_CALLBACK(_action_invoked_cb)); + } + } + + /* check for icon_data if icon == "" */ + if (*icon == '\0') + { + data = (GValue *)g_hash_table_lookup(hints, "icon_data"); + + if (data) + _notify_daemon_process_icon_data(daemon, nw, data); + } + else + { + GdkPixbuf *pixbuf = NULL; + + if (!strncmp(icon, "file://", 7) || *icon == '/') + { + if (!strncmp(icon, "file://", 7)) + icon += 7; + + /* Load file */ + pixbuf = gdk_pixbuf_new_from_file(icon, NULL); + } + else + { + /* Load icon theme icon */ + GtkIconTheme *theme = gtk_icon_theme_get_default(); + GtkIconInfo *icon_info = + gtk_icon_theme_lookup_icon(theme, icon, IMAGE_SIZE, + GTK_ICON_LOOKUP_USE_BUILTIN); + + if (icon_info != NULL) + { + gint icon_size = MIN(IMAGE_SIZE, + gtk_icon_info_get_base_size(icon_info)); + + if (icon_size == 0) + icon_size = IMAGE_SIZE; + + pixbuf = gtk_icon_theme_load_icon(theme, icon, icon_size, + GTK_ICON_LOOKUP_USE_BUILTIN, + NULL); + + gtk_icon_info_free(icon_info); + } + + if (pixbuf == NULL) + { + /* Well... maybe this is a file afterall. */ + pixbuf = gdk_pixbuf_new_from_file(icon, NULL); + } + } + + if (pixbuf != NULL) + { + theme_set_notification_icon(nw, pixbuf); + g_object_unref(G_OBJECT(pixbuf)); + } + } + + if (use_pos_data) + { + /* + * Typically, the theme engine will set its own position based on + * the arrow X, Y hints. However, in case, move the notification to + * that position. + */ + theme_set_notification_arrow(nw, TRUE, x, y); + theme_move_notification(nw, x, y); + } + else + { + gint monitor; + GdkScreen *screen; + gint x, y; + + theme_set_notification_arrow(nw, FALSE, 0, 0); + + gdk_display_get_pointer(gdk_display_get_default(), + &screen, &x, &y, NULL); + monitor = gdk_screen_get_monitor_at_point(screen, x, y); + g_assert(monitor >= 0 && monitor < priv->stacks_size); + + notify_stack_add_window(priv->stacks[monitor], nw, new_notification); + } + + if (!screensaver_active(GTK_WIDGET(nw)) && + !fullscreen_window_exists(GTK_WIDGET(nw))) + { + theme_show_notification(nw); + } + + return_id = (id == 0 ? _store_notification(daemon, nw, timeout) : id); + +#if CHECK_DBUS_VERSION(0, 60) + sender = dbus_g_method_get_sender(context); +#else + sender = g_strdup(dbus_message_get_sender( + dbus_g_message_get_message(context->message))); +#endif + + g_object_set_data(G_OBJECT(nw), "_notify_id", + GUINT_TO_POINTER(return_id)); + g_object_set_data_full(G_OBJECT(nw), "_notify_sender", sender, + (GDestroyNotify)g_free); + + if (nt) + _calculate_timeout(daemon, nt, timeout); + + dbus_g_method_return(context, return_id); + + return TRUE; +} + +gboolean +notify_daemon_close_notification_handler(NotifyDaemon *daemon, + guint id, GError **error) +{ + _close_notification(daemon, id, TRUE); + + return TRUE; +} + +gboolean +notify_daemon_get_capabilities(NotifyDaemon *daemon, char ***caps) +{ + *caps = g_new0(char *, 6); + + (*caps)[0] = g_strdup("actions"); + (*caps)[1] = g_strdup("body"); + (*caps)[2] = g_strdup("body-hyperlinks"); + (*caps)[3] = g_strdup("body-markup"); + (*caps)[4] = g_strdup("icon-static"); + (*caps)[5] = NULL; + + return TRUE; +} + +gboolean +notify_daemon_reload_settings (NotifyDaemon *daemon) +{ + /* reset the theme engine (not needed with only 1 theme) */ + /*theme_changed_cb ();*/ + + /* new location */ + popup_location_changed_cb (daemon); + + return TRUE; +} + +gboolean +notify_daemon_get_server_information(NotifyDaemon *daemon, + char **out_name, + char **out_vendor, + char **out_version, + char **out_spec_ver) +{ + *out_name = g_strdup("Notification Daemon"); + *out_vendor = g_strdup("Galago Project"); + *out_version = g_strdup(VERSION); + *out_spec_ver = g_strdup("0.9"); + + return TRUE; +} + +int +main(int argc, char **argv) +{ + NotifyDaemon *daemon; + DBusGConnection *connection; + DBusGProxy *bus_proxy; + GError *error; + guint request_name_result; + + g_set_application_name ("notification-daemon-xfce"); + +#ifdef G_ENABLE_DEBUG + g_log_set_always_fatal(G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); +#endif + + gtk_init(&argc, &argv); + + error = NULL; + + connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + + if (connection == NULL) + { + g_printerr("Failed to open connection to bus: %s\n", + error->message); + g_error_free(error); + exit(1); + } + + dbus_conn = dbus_g_connection_get_connection(connection); + + dbus_g_object_type_install_info(NOTIFY_TYPE_DAEMON, + &dbus_glib_notification_daemon_xfce_object_info); + + bus_proxy = dbus_g_proxy_new_for_name(connection, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus"); + + if (!dbus_g_proxy_call(bus_proxy, "RequestName", &error, + G_TYPE_STRING, "org.freedesktop.Notifications", + G_TYPE_UINT, 0, + G_TYPE_INVALID, + G_TYPE_UINT, &request_name_result, + G_TYPE_INVALID)) + { + g_error("Could not aquire name: %s", error->message); + } + + daemon = g_object_new(NOTIFY_TYPE_DAEMON, NULL); + + dbus_g_connection_register_g_object(connection, + "/org/freedesktop/Notifications", + G_OBJECT(daemon)); + + gtk_main(); + + return 0; +} diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h new file mode 100644 index 0000000..e671037 --- /dev/null +++ b/src/daemon/daemon.h @@ -0,0 +1,106 @@ +/* $Id: daemon.h 2114 2006-10-22 14:44:42Z nick $ + * + * Copyright (C) 2006 Christian Hammond <[email protected]> + * Copyright (C) 2005 John (J5) Palmieri <[email protected]> + * + * 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, 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. + */ + +#ifndef NOTIFY_DAEMON_H +#define NOTIFY_DAEMON_H + +#include <glib.h> +#include <glib-object.h> + +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#define NOTIFY_RC_FILE "xfce4/notication-daemon-xfce/settingsrc" + +#define NOTIFY_TYPE_DAEMON (notify_daemon_get_type()) +#define NOTIFY_DAEMON(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), NOTIFY_TYPE_DAEMON, NotifyDaemon)) +#define NOTIFY_DAEMON_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), NOTIFY_TYPE_DAEMON, NotifyDaemonClass)) +#define NOTIFY_IS_DAEMON(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NOTIFY_TYPE_DAEMON)) +#define NOTIFY_IS_DAEMON_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), NOTIFY_TYPE_DAEMON)) +#define NOTIFY_DAEMON_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NOTIFY_TYPE_DAEMON, NotifyDaemonClass)) + +#define NOTIFY_DAEMON_DEFAULT_TIMEOUT 7000 + +typedef struct _NotifyDaemon NotifyDaemon; +typedef struct _NotifyDaemonClass NotifyDaemonClass; +typedef struct _NotifyDaemonPrivate NotifyDaemonPrivate; + +struct _NotifyDaemon +{ + GObject parent; + + /*< private > */ + NotifyDaemonPrivate *priv; +}; + +struct _NotifyDaemonClass +{ + GObjectClass parent_class; +}; + +enum _NotifyDaemonError +{ + NOTIFY_DAEMON_ERROR_GENERIC = 0, +}; + +G_BEGIN_DECLS + +GType notify_daemon_get_type(void); + +NotifyDaemon *notify_daemon_new(void) + G_GNUC_MALLOC; + +gboolean notify_daemon_notify_handler(NotifyDaemon *daemon, + const gchar *app_name, + guint id, + const gchar *icon, + const gchar *summary, + const gchar *body, + gchar **actions, + GHashTable *hints, + int timeout, + DBusGMethodInvocation *context); + +gboolean notify_daemon_close_notification_handler(NotifyDaemon *daemon, + guint id, GError **error); + +gboolean notify_daemon_get_capabilities(NotifyDaemon *daemon, + char ***out_caps); + +gboolean notify_daemon_reload_settings (NotifyDaemon *daemon); + +gboolean notify_daemon_get_server_information(NotifyDaemon *daemon, + char **out_name, + char **out_vendor, + char **out_version, + char **out_spec_ver); + +gchar *xfce_load_setting (const gchar *key, + const gchar *fallback); + +G_END_DECLS + +#endif /* NOTIFY_DAEMON_H */ diff --git a/src/daemon/engines.c b/src/daemon/engines.c new file mode 100644 index 0000000..69efc39 --- /dev/null +++ b/src/daemon/engines.c @@ -0,0 +1,308 @@ +/* $Id: engines.c 2114 2006-10-22 14:44:42Z nick $ + * + * Copyright (C) 2006 Christian Hammond <[email protected]> + * Copyright (C) 2005 John (J5) Palmieri <[email protected]> + * Copyright (C) 2006 Nick Schermer <[email protected]> + * + * 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, 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. + */ + +#include "config.h" + +#include <libxfce4util/libxfce4util.h> +#include "daemon.h" +#include "engines.h" + +typedef struct +{ + GModule *module; + guint ref_count; + + gboolean (*theme_check_init)(unsigned int major_ver, + unsigned int minor_ver, + unsigned int micro_ver); + void (*get_theme_info)(char **theme_name, char **theme_ver, + char **author, char **homepage); + GtkWindow *(*create_notification)(UrlClickedCb url_clicked_cb); + void (*destroy_notification)(GtkWindow *nw); + void (*show_notification)(GtkWindow *nw); + void (*hide_notification)(GtkWindow *nw); + void (*set_notification_hints)(GtkWindow *nw, GHashTable *hints); + void (*set_notification_text)(GtkWindow *nw, const char *summary, + const char *body); + void (*set_notification_icon)(GtkWindow *nw, GdkPixbuf *pixbuf); + void (*set_notification_arrow)(GtkWindow *nw, gboolean visible, + int x, int y); + void (*add_notification_action)(GtkWindow *nw, const char *label, + const char *key, GCallback cb); + void (*clear_notification_actions)(GtkWindow *nw); + void (*move_notification)(GtkWindow *nw, int x, int y); + void (*set_notification_timeout)(GtkWindow *nw, glong timeout); + void (*notification_tick)(GtkWindow *nw, glong timeout); + +} ThemeEngine; + +static ThemeEngine *active_engine = NULL; + +static ThemeEngine * +load_theme_engine(const char *name) +{ + ThemeEngine *engine; + char *filename; + char *path; + + filename = g_strdup_printf("lib%s.so", name); + path = g_build_filename(ENGINES_DIR, filename, NULL); + g_free(filename); + + engine = g_new0(ThemeEngine, 1); + engine->ref_count = 1; + engine->module = g_module_open(path, G_MODULE_BIND_LAZY); + + g_free(path); + + if (engine->module == NULL) + goto error; + +#define BIND_REQUIRED_FUNC(name) \ + if (!g_module_symbol(engine->module, #name, (gpointer *)&engine->name)) \ + { \ + /* Too harsh! Fall back to default. */ \ + g_warning("Theme doesn't provide the required function '%s'", #name); \ + goto error; \ + } + +#define BIND_OPTIONAL_FUNC(name) \ + g_module_symbol(engine->module, #name, (gpointer *)&engine->name); + + BIND_REQUIRED_FUNC(theme_check_init); + BIND_REQUIRED_FUNC(get_theme_info); + BIND_REQUIRED_FUNC(create_notification); + BIND_REQUIRED_FUNC(set_notification_text); + BIND_REQUIRED_FUNC(set_notification_icon); + BIND_REQUIRED_FUNC(set_notification_arrow); + BIND_REQUIRED_FUNC(add_notification_action); + BIND_REQUIRED_FUNC(clear_notification_actions); + BIND_REQUIRED_FUNC(move_notification); + + BIND_OPTIONAL_FUNC(destroy_notification); + BIND_OPTIONAL_FUNC(show_notification); + BIND_OPTIONAL_FUNC(hide_notification); + BIND_OPTIONAL_FUNC(set_notification_timeout); + BIND_OPTIONAL_FUNC(set_notification_hints); + BIND_OPTIONAL_FUNC(notification_tick); + + if (!engine->theme_check_init(NOTIFICATION_DAEMON_MAJOR_VERSION, + NOTIFICATION_DAEMON_MINOR_VERSION, + NOTIFICATION_DAEMON_MICRO_VERSION)) + { + g_error("Theme doesn't work with this version of notification-daemon"); + goto error; + } + + return engine; + +error: + if (engine->module != NULL && !g_module_close(engine->module)) + g_warning("%s: %s", filename, g_module_error()); + + g_free(engine); + return NULL; +} + +static void +destroy_engine(ThemeEngine *engine) +{ + g_assert(engine->ref_count == 0); + + if (active_engine == engine) + active_engine = NULL; + + g_module_close(engine->module); + g_free(engine); +} + +void +theme_changed_cb(void) +{ + if (active_engine == NULL) + return; + + active_engine->ref_count--; + + if (active_engine->ref_count == 0) + destroy_engine(active_engine); + + /* This is no longer the true active engine, so reset this. */ + active_engine = NULL; +} + +static ThemeEngine * +get_theme_engine(void) +{ + if (active_engine == NULL) + { + gchar *enginename; + + enginename = xfce_load_setting ("engine_name", "standard"); + + if (enginename == NULL) + { + active_engine = load_theme_engine("standard"); + g_assert(active_engine != NULL); + } + else + { + active_engine = load_theme_engine(enginename); + + if (active_engine == NULL) + { + g_warning("Unable to load theme engine '%s'", enginename); + active_engine = load_theme_engine("standard"); + } + + g_free(enginename); + + g_assert(active_engine != NULL); + } + } + + return active_engine; +} + +static void +theme_engine_unref(ThemeEngine *engine) +{ + engine->ref_count--; + + if (engine->ref_count == 0) + destroy_engine(engine); +} + +GtkWindow * +theme_create_notification(UrlClickedCb url_clicked_cb) +{ + ThemeEngine *engine = get_theme_engine(); + GtkWindow *nw = engine->create_notification(url_clicked_cb); + g_object_set_data_full(G_OBJECT(nw), "_theme_engine", engine, + (GDestroyNotify)theme_engine_unref); + engine->ref_count++; + return nw; +} + +void +theme_destroy_notification(GtkWindow *nw) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + + if (engine->destroy_notification != NULL) + engine->destroy_notification(nw); + else + gtk_widget_destroy(GTK_WIDGET(nw)); +} + +void +theme_show_notification(GtkWindow *nw) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + + if (engine->show_notification != NULL) + engine->show_notification(nw); + else + gtk_widget_show(GTK_WIDGET(nw)); +} + +void +theme_hide_notification(GtkWindow *nw) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + + if (engine->hide_notification != NULL) + engine->hide_notification(nw); + else + gtk_widget_hide(GTK_WIDGET(nw)); +} + +void +theme_set_notification_hints(GtkWindow *nw, GHashTable *hints) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + + if (engine->set_notification_hints != NULL) + engine->set_notification_hints(nw, hints); +} + +void +theme_set_notification_timeout(GtkWindow *nw, glong timeout) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + + if (engine->set_notification_timeout != NULL) + engine->set_notification_timeout(nw, timeout); +} + +void +theme_notification_tick(GtkWindow *nw, glong remaining) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + + if (engine->notification_tick != NULL) + engine->notification_tick(nw, remaining); +} + +void +theme_set_notification_text(GtkWindow *nw, const char *summary, + const char *body) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + engine->set_notification_text(nw, summary, body); +} + +void +theme_set_notification_icon(GtkWindow *nw, GdkPixbuf *pixbuf) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + engine->set_notification_icon(nw, pixbuf); +} + +void +theme_set_notification_arrow(GtkWindow *nw, gboolean visible, int x, int y) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + engine->set_notification_arrow(nw, visible, x, y); +} + +void +theme_add_notification_action(GtkWindow *nw, const char *label, + const char *key, GCallback cb) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + engine->add_notification_action(nw, label, key, cb); +} + +void +theme_clear_notification_actions(GtkWindow *nw) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + engine->clear_notification_actions(nw); +} + +void +theme_move_notification(GtkWindow *nw, int x, int y) +{ + ThemeEngine *engine = g_object_get_data(G_OBJECT(nw), "_theme_engine"); + engine->move_notification(nw, x, y); +} diff --git a/src/daemon/engines.h b/src/daemon/engines.h new file mode 100644 index 0000000..fc2ccf7 --- /dev/null +++ b/src/daemon/engines.h @@ -0,0 +1,47 @@ +/* $Id: engines.h 2114 2006-10-22 14:44:42Z nick $ + * + * Copyright (C) 2006 Christian Hammond <[email protected]> + * Copyright (C) 2005 John (J5) Palmieri <[email protected]> + * + * 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, 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. + */ + +#ifndef _ENGINES_H_ +#define _ENGINES_H_ + +#include <gtk/gtk.h> + +typedef void (*UrlClickedCb)(GtkWindow *nw, const char *url); + +GtkWindow *theme_create_notification(UrlClickedCb url_clicked_cb); +void theme_destroy_notification(GtkWindow *nw); +void theme_show_notification(GtkWindow *nw); +void theme_hide_notification(GtkWindow *nw); +void theme_set_notification_hints(GtkWindow *nw, GHashTable *hints); +void theme_set_notification_timeout(GtkWindow *nw, glong timeout); +void theme_notification_tick(GtkWindow *nw, glong remaining); +void theme_set_notification_text(GtkWindow *nw, const char *summary, + const char *body); +void theme_set_notification_icon(GtkWindow *nw, GdkPixbuf *pixbuf); +void theme_set_notification_arrow(GtkWindow *nw, gboolean visible, + int x, int y); +void theme_add_notification_action(GtkWindow *nw, const char *label, + const char *key, GCallback cb); +void theme_clear_notification_actions(GtkWindow *nw); +void theme_move_notification(GtkWindow *nw, int x, int y); +void theme_changed_cb(void); + +#endif /* _ENGINES_H_ */ diff --git a/src/daemon/notificationdaemon.xml b/src/daemon/notificationdaemon.xml new file mode 100644 index 0000000..81c1674 --- /dev/null +++ b/src/daemon/notificationdaemon.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/org/freedesktop/Notifications"> + + <interface name="org.freedesktop.Notifications"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="NotifyDaemon"/> + <method name="Notify"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="notify_daemon_notify_handler"/> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="s" name="app_name" direction="in" /> + <arg type="u" name="id" direction="in" /> + <arg type="s" name="icon" direction="in" /> + <arg type="s" name="summary" direction="in" /> + <arg type="s" name="body" direction="in" /> + <arg type="as" name="actions" direction="in" /> + <arg type="a{sv}" name="hints" direction="in" /> + <arg type="i" name="timeout" direction="in" /> + <arg type="u" name="return_id" direction="out" /> + </method> + + <method name="CloseNotification"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="notify_daemon_close_notification_handler"/> + <arg type="u" name="id" direction="in" /> + </method> + + <method name="GetCapabilities"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="notify_daemon_get_capabilities"/> + <arg type="as" name="return_caps" direction="out"/> + </method> + + <method name="ReloadSettings"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="notify_daemon_reload_settings"/> + </method> + + <method name="GetServerInformation"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="notify_daemon_get_server_information"/> + <arg type="s" name="return_name" direction="out"/> + <arg type="s" name="return_vendor" direction="out"/> + <arg type="s" name="return_version" direction="out"/> + <arg type="s" name="return_spec_version" direction="out"/> + </method> + + </interface> +</node> diff --git a/src/daemon/stack.c b/src/daemon/stack.c new file mode 100644 index 0000000..8a07c82 --- /dev/null +++ b/src/daemon/stack.c @@ -0,0 +1,278 @@ +/* + * stack.c - Notification stack groups. + * + * Copyright (C) 2006 Christian Hammond <[email protected]> + * + * 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, 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. + */ +#include "config.h" +#include "engines.h" +#include "stack.h" + +#include <X11/Xproto.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <gdk/gdkx.h> + +struct _NotifyStack +{ + NotifyDaemon *daemon; + GdkScreen *screen; + guint monitor; + NotifyStackLocation location; + GSList *windows; +}; + +static gboolean +get_work_area(GtkWidget *nw, GdkRectangle *rect) +{ + Atom workarea = XInternAtom(GDK_DISPLAY(), "_NET_WORKAREA", True); + Atom type; + Window win; + int format; + gulong num, leftovers; + gulong max_len = 4 * 32; + guchar *ret_workarea; + long *workareas; + int result; + GdkScreen *screen; + int disp_screen; + + gtk_widget_realize(nw); + screen = gdk_drawable_get_screen(GDK_DRAWABLE(nw->window)); + disp_screen = GDK_SCREEN_XNUMBER(screen); + + /* Defaults in case of error */ + rect->x = 0; + rect->y = 0; + rect->width = gdk_screen_get_width(screen); + rect->height = gdk_screen_get_height(screen); + + if (workarea == None) + return FALSE; + + win = XRootWindow(GDK_DISPLAY(), disp_screen); + result = XGetWindowProperty(GDK_DISPLAY(), win, workarea, 0, + max_len, False, AnyPropertyType, + &type, &format, &num, &leftovers, + &ret_workarea); + + if (result != Success || type == None || format == 0 || leftovers || + num % 4) + { + return FALSE; + } + + workareas = (long *)ret_workarea; + rect->x = workareas[disp_screen * 4]; + rect->y = workareas[disp_screen * 4 + 1]; + rect->width = workareas[disp_screen * 4 + 2]; + rect->height = workareas[disp_screen * 4 + 3]; + + XFree(ret_workarea); + + return TRUE; +} + +static void +get_origin_coordinates(NotifyStackLocation stack_location, + GdkRectangle *workarea, + gint *x, gint *y, gint *shiftx, gint *shifty, + gint width, gint height) +{ + switch (stack_location) + { + case NOTIFY_STACK_LOCATION_TOP_LEFT: + *x = workarea->x; + *y = workarea->y; + *shifty = height; + break; + + case NOTIFY_STACK_LOCATION_TOP_RIGHT: + *x = workarea->x + workarea->width - width; + *y = workarea->y; + *shifty = height; + break; + + case NOTIFY_STACK_LOCATION_BOTTOM_LEFT: + *x = workarea->x; + *y = workarea->y + workarea->height - height; + break; + + case NOTIFY_STACK_LOCATION_BOTTOM_RIGHT: + *x = workarea->x + workarea->width - width; + *y = workarea->y + workarea->height - height; + break; + + default: + g_assert_not_reached(); + } +} + +static void +translate_coordinates(NotifyStackLocation stack_location, + GdkRectangle *workarea, + gint *x, gint *y, gint *shiftx, gint *shifty, + gint width, gint height, gint index) +{ + switch (stack_location) + { + case NOTIFY_STACK_LOCATION_TOP_LEFT: + *x = workarea->x; + *y += *shifty; + *shifty = height; + break; + + case NOTIFY_STACK_LOCATION_TOP_RIGHT: + *x = workarea->x + workarea->width - width; + *y += *shifty; + *shifty = height; + break; + + case NOTIFY_STACK_LOCATION_BOTTOM_LEFT: + *x = workarea->x; + *y -= height; + break; + + case NOTIFY_STACK_LOCATION_BOTTOM_RIGHT: + *x = workarea->x + workarea->width - width; + *y -= height; + break; + + default: + g_assert_not_reached(); + } +} + +NotifyStack * +notify_stack_new(NotifyDaemon *daemon, + GdkScreen *screen, + guint monitor, + NotifyStackLocation location) +{ + NotifyStack *stack; + + g_assert(daemon != NULL); + g_assert(screen != NULL && GDK_IS_SCREEN(screen)); + g_assert(monitor < gdk_screen_get_n_monitors(screen)); + g_assert(location != NOTIFY_STACK_LOCATION_UNKNOWN); + + stack = g_new0(NotifyStack, 1); + stack->daemon = daemon; + stack->screen = screen; + stack->monitor = monitor; + stack->location = location; + + return stack; +} + +void +notify_stack_destroy(NotifyStack *stack) +{ + g_assert(stack != NULL); + + g_slist_free(stack->windows); + g_free(stack); +} + +void +notify_stack_set_location(NotifyStack *stack, + NotifyStackLocation location) +{ + stack->location = location; +} + +static void +notify_stack_shift_notifications(NotifyStack *stack, + GtkWindow *nw, + GSList **nw_l, + gint init_width, + gint init_height, + gint *nw_x, + gint *nw_y) +{ + GdkRectangle workarea; + GSList *l; + gint x, y, shiftx = 0, shifty = 0, index = 1; + + get_work_area(GTK_WIDGET(nw), &workarea); + get_origin_coordinates(stack->location, &workarea, &x, &y, + &shiftx, &shifty, init_width, init_height); + + if (nw_x != NULL) + *nw_x = x; + + if (nw_y != NULL) + *nw_y = y; + + for (l = stack->windows; l != NULL; l = l->next) + { + GtkWindow *nw2 = GTK_WINDOW(l->data); + GtkRequisition req; + + if (nw2 != nw) + { + gtk_widget_size_request(GTK_WIDGET(nw2), &req); + + translate_coordinates(stack->location, &workarea, &x, &y, + &shiftx, &shifty, req.width, req.height, + index++); + theme_move_notification(nw2, x, y); + } + else if (nw_l != NULL) + { + *nw_l = l; + } + } +} + +void +notify_stack_add_window(NotifyStack *stack, + GtkWindow *nw, + gboolean new_notification) +{ + GtkRequisition req; + gint x, y; + + gtk_widget_size_request(GTK_WIDGET(nw), &req); + notify_stack_shift_notifications(stack, nw, NULL, + req.width, req.height, &x, &y); + theme_move_notification(nw, x, y); + + if (new_notification) + { + g_signal_connect_swapped(G_OBJECT(nw), "destroy", + G_CALLBACK(notify_stack_remove_window), + stack); + stack->windows = g_slist_prepend(stack->windows, nw); + } +} + +void +notify_stack_remove_window(NotifyStack *stack, + GtkWindow *nw) +{ + GSList *remove_l = NULL; + + notify_stack_shift_notifications(stack, nw, &remove_l, 0, 0, NULL, NULL); + + if (remove_l != NULL) + stack->windows = g_slist_delete_link(stack->windows, remove_l); + + if (GTK_WIDGET_REALIZED(GTK_WIDGET(nw))) + gtk_widget_unrealize(GTK_WIDGET(nw)); +} diff --git a/src/daemon/stack.h b/src/daemon/stack.h new file mode 100644 index 0000000..6a6acfa --- /dev/null +++ b/src/daemon/stack.h @@ -0,0 +1,52 @@ +/* $Id: stack.h 2114 2006-10-22 14:44:42Z nick $ + * + * Copyright (C) 2006 Christian Hammond <[email protected]> + * + * 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, 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. + */ + +#ifndef _NOTIFY_STACK_H_ +#define _NOTIFY_STACK_H_ + +#include <gtk/gtk.h> +#include "daemon.h" + +typedef enum +{ + NOTIFY_STACK_LOCATION_UNKNOWN = -1, + NOTIFY_STACK_LOCATION_TOP_LEFT, + NOTIFY_STACK_LOCATION_TOP_RIGHT, + NOTIFY_STACK_LOCATION_BOTTOM_LEFT, + NOTIFY_STACK_LOCATION_BOTTOM_RIGHT, + NOTIFY_STACK_LOCATION_DEFAULT = NOTIFY_STACK_LOCATION_BOTTOM_RIGHT + +} NotifyStackLocation; + +typedef struct _NotifyStack NotifyStack; + +NotifyStack *notify_stack_new(NotifyDaemon *daemon, + GdkScreen *screen, + guint monitor, + NotifyStackLocation stack_location); +void notify_stack_destroy(NotifyStack *stack); + +void notify_stack_set_location(NotifyStack *stack, + NotifyStackLocation location); +void notify_stack_add_window(NotifyStack *stack, GtkWindow *nw, + gboolean new_notification); +void notify_stack_remove_window(NotifyStack *stack, GtkWindow *nw); + +#endif /* _NOTIFY_STACK_H_ */ |