diff options
Diffstat (limited to 'mate-session')
53 files changed, 20093 insertions, 0 deletions
diff --git a/mate-session/Makefile.am b/mate-session/Makefile.am new file mode 100644 index 0000000..ee98a30 --- /dev/null +++ b/mate-session/Makefile.am @@ -0,0 +1,140 @@ +bin_PROGRAMS = mate-session +noinst_LTLIBRARIES = libgsmutil.la +noinst_PROGRAMS = \ + test-client-dbus \ + test-inhibit + +AM_CPPFLAGS = \ + $(MATE_SESSION_CFLAGS) \ + $(DISABLE_DEPRECATED_CFLAGS) + +AM_CFLAGS = $(WARN_CFLAGS) + +mate_session_SOURCES = \ + gsm-app.h \ + gsm-app.c \ + gsm-autostart-app.h \ + gsm-autostart-app.c \ + gsm-client.c \ + gsm-client.h \ + gsm-xsmp-client.h \ + gsm-xsmp-client.c \ + gsm-dbus-client.h \ + gsm-dbus-client.c \ + gsm-marshal.h \ + gsm-marshal.c \ + gsm-consolekit.c \ + gsm-consolekit.h \ + gsm-logout-dialog.h \ + gsm-logout-dialog.c \ + gsm-inhibit-dialog.h \ + gsm-inhibit-dialog.c \ + gs-idle-monitor.h \ + gs-idle-monitor.c \ + gsm-presence.h \ + gsm-presence.c \ + gsm-mateconf.c \ + gsm-mateconf.h \ + mdm.h \ + mdm.c \ + mdm-signal-handler.h \ + mdm-signal-handler.c \ + mdm-log.h \ + mdm-log.c \ + main.c \ + gsm-store.h \ + gsm-store.c \ + gsm-inhibitor.h \ + gsm-inhibitor.c \ + gsm-manager.c \ + gsm-manager.h \ + gsm-session-save.c \ + gsm-session-save.h \ + gsm-xsmp-server.c \ + gsm-xsmp-server.h + +mate_session_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(SM_CFLAGS) \ + $(ICE_CFLAGS) \ + $(XEXT_CFLAGS) \ + $(MATECONF_CFLAGS) \ + -I$(top_srcdir)/egg \ + -DLOCALE_DIR=\""$(datadir)/locale"\" \ + -DDATA_DIR=\""$(datadir)/mate-session"\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DGTKBUILDER_DIR=\""$(pkgdatadir)"\" \ + -DMATECONF_SANITY_CHECK=\""$(MATECONF_SANITY_CHECK)"\" \ + -DMATECONFTOOL_CMD=\"$(MATECONFTOOL)\" \ + -DI_KNOW_THE_DEVICEKIT_POWER_API_IS_SUBJECT_TO_CHANGE + +mate_session_LDADD = \ + libgsmutil.la \ + $(top_builddir)/egg/libeggdesktopfile.la \ + $(SM_LIBS) \ + $(ICE_LIBS) \ + $(XRENDER_LIBS) \ + $(XTEST_LIBS) \ + $(XEXT_LIBS) \ + $(MATE_SESSION_LIBS) \ + $(MATECONF_LIBS) \ + $(EXECINFO_LIBS) + +libgsmutil_la_SOURCES = \ + gsm-util.c \ + gsm-util.h + +libgsmutil_la_LIBADD = \ + $(MATE_SESSION_LIBS) + +test_inhibit_SOURCES = test-inhibit.c +test_inhibit_LDADD = $(MATE_SESSION_LIBS) + +test_client_dbus_SOURCES = test-client-dbus.c +test_client_dbus_LDADD = $(DBUS_GLIB_LIBS) + +gsm-marshal.c: gsm-marshal.list + $(AM_V_GEN)echo "#include \"gsm-marshal.h\"" > $@ && \ + $(GLIB_GENMARSHAL) $< --prefix=gsm_marshal --body >> $@ + +gsm-marshal.h: gsm-marshal.list + $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --prefix=gsm_marshal --header > $@ + +gsm-manager-glue.h: org.mate.SessionManager.xml Makefile.am + $(AM_V_GEN)dbus-binding-tool --prefix=gsm_manager --mode=glib-server --output=gsm-manager-glue.h $(srcdir)/org.mate.SessionManager.xml + +gsm-client-glue.h: org.mate.SessionManager.Client.xml Makefile.am + $(AM_V_GEN)dbus-binding-tool --prefix=gsm_client --mode=glib-server --output=gsm-client-glue.h $(srcdir)/org.mate.SessionManager.Client.xml + +gsm-app-glue.h: org.mate.SessionManager.App.xml Makefile.am + $(AM_V_GEN)dbus-binding-tool --prefix=gsm_app --mode=glib-server --output=gsm-app-glue.h $(srcdir)/org.mate.SessionManager.App.xml + +gsm-inhibitor-glue.h: org.mate.SessionManager.Inhibitor.xml Makefile.am + $(AM_V_GEN)dbus-binding-tool --prefix=gsm_inhibitor --mode=glib-server --output=gsm-inhibitor-glue.h $(srcdir)/org.mate.SessionManager.Inhibitor.xml + +gsm-presence-glue.h: org.mate.SessionManager.Presence.xml Makefile.am + $(AM_V_GEN)dbus-binding-tool --prefix=gsm_presence --mode=glib-server --output=gsm-presence-glue.h $(srcdir)/org.mate.SessionManager.Presence.xml + +BUILT_SOURCES = \ + gsm-marshal.c \ + gsm-marshal.h \ + gsm-manager-glue.h \ + gsm-presence-glue.h \ + gsm-inhibitor-glue.h \ + gsm-client-glue.h \ + gsm-app-glue.h + +EXTRA_DIST = \ + README \ + gsm-marshal.list \ + org.mate.SessionManager.xml \ + org.mate.SessionManager.App.xml \ + org.mate.SessionManager.Client.xml \ + org.mate.SessionManager.ClientPrivate.xml \ + org.mate.SessionManager.Inhibitor.xml \ + org.mate.SessionManager.Presence.xml + +CLEANFILES = \ + $(BUILT_SOURCES) + +-include $(top_srcdir)/git.mk diff --git a/mate-session/Makefile.in b/mate-session/Makefile.in new file mode 100644 index 0000000..c2a5b48 --- /dev/null +++ b/mate-session/Makefile.in @@ -0,0 +1,1158 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@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@ +bin_PROGRAMS = mate-session$(EXEEXT) +noinst_PROGRAMS = test-client-dbus$(EXEEXT) test-inhibit$(EXEEXT) +subdir = mate-session +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +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 = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgsmutil_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_libgsmutil_la_OBJECTS = gsm-util.lo +libgsmutil_la_OBJECTS = $(am_libgsmutil_la_OBJECTS) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) +am_mate_session_OBJECTS = mate_session-gsm-app.$(OBJEXT) \ + mate_session-gsm-autostart-app.$(OBJEXT) \ + mate_session-gsm-client.$(OBJEXT) \ + mate_session-gsm-xsmp-client.$(OBJEXT) \ + mate_session-gsm-dbus-client.$(OBJEXT) \ + mate_session-gsm-marshal.$(OBJEXT) \ + mate_session-gsm-consolekit.$(OBJEXT) \ + mate_session-gsm-logout-dialog.$(OBJEXT) \ + mate_session-gsm-inhibit-dialog.$(OBJEXT) \ + mate_session-gs-idle-monitor.$(OBJEXT) \ + mate_session-gsm-presence.$(OBJEXT) \ + mate_session-gsm-mateconf.$(OBJEXT) mate_session-mdm.$(OBJEXT) \ + mate_session-mdm-signal-handler.$(OBJEXT) \ + mate_session-mdm-log.$(OBJEXT) mate_session-main.$(OBJEXT) \ + mate_session-gsm-store.$(OBJEXT) \ + mate_session-gsm-inhibitor.$(OBJEXT) \ + mate_session-gsm-manager.$(OBJEXT) \ + mate_session-gsm-session-save.$(OBJEXT) \ + mate_session-gsm-xsmp-server.$(OBJEXT) +mate_session_OBJECTS = $(am_mate_session_OBJECTS) +mate_session_DEPENDENCIES = libgsmutil.la \ + $(top_builddir)/egg/libeggdesktopfile.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_test_client_dbus_OBJECTS = test-client-dbus.$(OBJEXT) +test_client_dbus_OBJECTS = $(am_test_client_dbus_OBJECTS) +test_client_dbus_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_test_inhibit_OBJECTS = test-inhibit.$(OBJEXT) +test_inhibit_OBJECTS = $(am_test_inhibit_OBJECTS) +test_inhibit_DEPENDENCIES = $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(libgsmutil_la_SOURCES) $(mate_session_SOURCES) \ + $(test_client_dbus_SOURCES) $(test_inhibit_SOURCES) +DIST_SOURCES = $(libgsmutil_la_SOURCES) $(mate_session_SOURCES) \ + $(test_client_dbus_SOURCES) $(test_inhibit_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_GLIB_CFLAGS = @DBUS_GLIB_CFLAGS@ +DBUS_GLIB_LIBS = @DBUS_GLIB_LIBS@ +DEFAULT_WM = @DEFAULT_WM@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DISABLE_DEPRECATED_CFLAGS = @DISABLE_DEPRECATED_CFLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGG_SMCLIENT_CFLAGS = @EGG_SMCLIENT_CFLAGS@ +EGG_SMCLIENT_LIBS = @EGG_SMCLIENT_LIBS@ +EGREP = @EGREP@ +EXECINFO_LIBS = @EXECINFO_LIBS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +HAVE_XRENDER = @HAVE_XRENDER@ +HAVE_XTEST = @HAVE_XTEST@ +ICE_CFLAGS = @ICE_CFLAGS@ +ICE_LIBS = @ICE_LIBS@ +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_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MATECONFTOOL = @MATECONFTOOL@ +MATECONF_CFLAGS = @MATECONF_CFLAGS@ +MATECONF_LIBS = @MATECONF_LIBS@ +MATECONF_SANITY_CHECK = @MATECONF_SANITY_CHECK@ +MATECONF_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@ +MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@ +MATE_SESSION_CFLAGS = @MATE_SESSION_CFLAGS@ +MATE_SESSION_LIBS = @MATE_SESSION_LIBS@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +RANLIB = @RANLIB@ +REBUILD = @REBUILD@ +SED = @SED@ +SESSION_PROPERTIES_CFLAGS = @SESSION_PROPERTIES_CFLAGS@ +SESSION_PROPERTIES_LIBS = @SESSION_PROPERTIES_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SM_CFLAGS = @SM_CFLAGS@ +SM_LIBS = @SM_LIBS@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +XEXT_CFLAGS = @XEXT_CFLAGS@ +XEXT_LIBS = @XEXT_LIBS@ +XGETTEXT = @XGETTEXT@ +XMKMF = @XMKMF@ +XMLTO = @XMLTO@ +XRENDER_CFLAGS = @XRENDER_CFLAGS@ +XRENDER_LIBS = @XRENDER_LIBS@ +XSLTPROC = @XSLTPROC@ +XTEST_CFLAGS = @XTEST_CFLAGS@ +XTEST_LIBS = @XTEST_LIBS@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +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_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LTLIBRARIES = libgsmutil.la +AM_CPPFLAGS = \ + $(MATE_SESSION_CFLAGS) \ + $(DISABLE_DEPRECATED_CFLAGS) + +AM_CFLAGS = $(WARN_CFLAGS) +mate_session_SOURCES = \ + gsm-app.h \ + gsm-app.c \ + gsm-autostart-app.h \ + gsm-autostart-app.c \ + gsm-client.c \ + gsm-client.h \ + gsm-xsmp-client.h \ + gsm-xsmp-client.c \ + gsm-dbus-client.h \ + gsm-dbus-client.c \ + gsm-marshal.h \ + gsm-marshal.c \ + gsm-consolekit.c \ + gsm-consolekit.h \ + gsm-logout-dialog.h \ + gsm-logout-dialog.c \ + gsm-inhibit-dialog.h \ + gsm-inhibit-dialog.c \ + gs-idle-monitor.h \ + gs-idle-monitor.c \ + gsm-presence.h \ + gsm-presence.c \ + gsm-mateconf.c \ + gsm-mateconf.h \ + mdm.h \ + mdm.c \ + mdm-signal-handler.h \ + mdm-signal-handler.c \ + mdm-log.h \ + mdm-log.c \ + main.c \ + gsm-store.h \ + gsm-store.c \ + gsm-inhibitor.h \ + gsm-inhibitor.c \ + gsm-manager.c \ + gsm-manager.h \ + gsm-session-save.c \ + gsm-session-save.h \ + gsm-xsmp-server.c \ + gsm-xsmp-server.h + +mate_session_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(SM_CFLAGS) \ + $(ICE_CFLAGS) \ + $(XEXT_CFLAGS) \ + $(MATECONF_CFLAGS) \ + -I$(top_srcdir)/egg \ + -DLOCALE_DIR=\""$(datadir)/locale"\" \ + -DDATA_DIR=\""$(datadir)/mate-session"\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DGTKBUILDER_DIR=\""$(pkgdatadir)"\" \ + -DMATECONF_SANITY_CHECK=\""$(MATECONF_SANITY_CHECK)"\" \ + -DMATECONFTOOL_CMD=\"$(MATECONFTOOL)\" \ + -DI_KNOW_THE_DEVICEKIT_POWER_API_IS_SUBJECT_TO_CHANGE + +mate_session_LDADD = \ + libgsmutil.la \ + $(top_builddir)/egg/libeggdesktopfile.la \ + $(SM_LIBS) \ + $(ICE_LIBS) \ + $(XRENDER_LIBS) \ + $(XTEST_LIBS) \ + $(XEXT_LIBS) \ + $(MATE_SESSION_LIBS) \ + $(MATECONF_LIBS) \ + $(EXECINFO_LIBS) + +libgsmutil_la_SOURCES = \ + gsm-util.c \ + gsm-util.h + +libgsmutil_la_LIBADD = \ + $(MATE_SESSION_LIBS) + +test_inhibit_SOURCES = test-inhibit.c +test_inhibit_LDADD = $(MATE_SESSION_LIBS) +test_client_dbus_SOURCES = test-client-dbus.c +test_client_dbus_LDADD = $(DBUS_GLIB_LIBS) +BUILT_SOURCES = \ + gsm-marshal.c \ + gsm-marshal.h \ + gsm-manager-glue.h \ + gsm-presence-glue.h \ + gsm-inhibitor-glue.h \ + gsm-client-glue.h \ + gsm-app-glue.h + +EXTRA_DIST = \ + README \ + gsm-marshal.list \ + org.mate.SessionManager.xml \ + org.mate.SessionManager.App.xml \ + org.mate.SessionManager.Client.xml \ + org.mate.SessionManager.ClientPrivate.xml \ + org.mate.SessionManager.Inhibitor.xml \ + org.mate.SessionManager.Presence.xml + +CLEANFILES = \ + $(BUILT_SOURCES) + +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 ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu mate-session/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu mate-session/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 +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libgsmutil.la: $(libgsmutil_la_OBJECTS) $(libgsmutil_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libgsmutil_la_OBJECTS) $(libgsmutil_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +mate-session$(EXEEXT): $(mate_session_OBJECTS) $(mate_session_DEPENDENCIES) + @rm -f mate-session$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(mate_session_OBJECTS) $(mate_session_LDADD) $(LIBS) +test-client-dbus$(EXEEXT): $(test_client_dbus_OBJECTS) $(test_client_dbus_DEPENDENCIES) + @rm -f test-client-dbus$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_client_dbus_OBJECTS) $(test_client_dbus_LDADD) $(LIBS) +test-inhibit$(EXEEXT): $(test_inhibit_OBJECTS) $(test_inhibit_DEPENDENCIES) + @rm -f test-inhibit$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_inhibit_OBJECTS) $(test_inhibit_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsm-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gs-idle-monitor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-app.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-autostart-app.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-client.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-consolekit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-dbus-client.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-inhibit-dialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-inhibitor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-logout-dialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-marshal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-mateconf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-presence.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-session-save.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-store.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-xsmp-client.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-gsm-xsmp-server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-mdm-log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-mdm-signal-handler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_session-mdm.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-client-dbus.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-inhibit.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@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@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@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@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@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 $@ $< + +mate_session-gsm-app.o: gsm-app.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-app.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-app.Tpo -c -o mate_session-gsm-app.o `test -f 'gsm-app.c' || echo '$(srcdir)/'`gsm-app.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-app.Tpo $(DEPDIR)/mate_session-gsm-app.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-app.c' object='mate_session-gsm-app.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-app.o `test -f 'gsm-app.c' || echo '$(srcdir)/'`gsm-app.c + +mate_session-gsm-app.obj: gsm-app.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-app.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-app.Tpo -c -o mate_session-gsm-app.obj `if test -f 'gsm-app.c'; then $(CYGPATH_W) 'gsm-app.c'; else $(CYGPATH_W) '$(srcdir)/gsm-app.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-app.Tpo $(DEPDIR)/mate_session-gsm-app.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-app.c' object='mate_session-gsm-app.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-app.obj `if test -f 'gsm-app.c'; then $(CYGPATH_W) 'gsm-app.c'; else $(CYGPATH_W) '$(srcdir)/gsm-app.c'; fi` + +mate_session-gsm-autostart-app.o: gsm-autostart-app.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-autostart-app.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-autostart-app.Tpo -c -o mate_session-gsm-autostart-app.o `test -f 'gsm-autostart-app.c' || echo '$(srcdir)/'`gsm-autostart-app.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-autostart-app.Tpo $(DEPDIR)/mate_session-gsm-autostart-app.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-autostart-app.c' object='mate_session-gsm-autostart-app.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-autostart-app.o `test -f 'gsm-autostart-app.c' || echo '$(srcdir)/'`gsm-autostart-app.c + +mate_session-gsm-autostart-app.obj: gsm-autostart-app.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-autostart-app.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-autostart-app.Tpo -c -o mate_session-gsm-autostart-app.obj `if test -f 'gsm-autostart-app.c'; then $(CYGPATH_W) 'gsm-autostart-app.c'; else $(CYGPATH_W) '$(srcdir)/gsm-autostart-app.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-autostart-app.Tpo $(DEPDIR)/mate_session-gsm-autostart-app.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-autostart-app.c' object='mate_session-gsm-autostart-app.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-autostart-app.obj `if test -f 'gsm-autostart-app.c'; then $(CYGPATH_W) 'gsm-autostart-app.c'; else $(CYGPATH_W) '$(srcdir)/gsm-autostart-app.c'; fi` + +mate_session-gsm-client.o: gsm-client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-client.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-client.Tpo -c -o mate_session-gsm-client.o `test -f 'gsm-client.c' || echo '$(srcdir)/'`gsm-client.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-client.Tpo $(DEPDIR)/mate_session-gsm-client.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-client.c' object='mate_session-gsm-client.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-client.o `test -f 'gsm-client.c' || echo '$(srcdir)/'`gsm-client.c + +mate_session-gsm-client.obj: gsm-client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-client.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-client.Tpo -c -o mate_session-gsm-client.obj `if test -f 'gsm-client.c'; then $(CYGPATH_W) 'gsm-client.c'; else $(CYGPATH_W) '$(srcdir)/gsm-client.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-client.Tpo $(DEPDIR)/mate_session-gsm-client.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-client.c' object='mate_session-gsm-client.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-client.obj `if test -f 'gsm-client.c'; then $(CYGPATH_W) 'gsm-client.c'; else $(CYGPATH_W) '$(srcdir)/gsm-client.c'; fi` + +mate_session-gsm-xsmp-client.o: gsm-xsmp-client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-xsmp-client.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-xsmp-client.Tpo -c -o mate_session-gsm-xsmp-client.o `test -f 'gsm-xsmp-client.c' || echo '$(srcdir)/'`gsm-xsmp-client.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-xsmp-client.Tpo $(DEPDIR)/mate_session-gsm-xsmp-client.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-xsmp-client.c' object='mate_session-gsm-xsmp-client.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-xsmp-client.o `test -f 'gsm-xsmp-client.c' || echo '$(srcdir)/'`gsm-xsmp-client.c + +mate_session-gsm-xsmp-client.obj: gsm-xsmp-client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-xsmp-client.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-xsmp-client.Tpo -c -o mate_session-gsm-xsmp-client.obj `if test -f 'gsm-xsmp-client.c'; then $(CYGPATH_W) 'gsm-xsmp-client.c'; else $(CYGPATH_W) '$(srcdir)/gsm-xsmp-client.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-xsmp-client.Tpo $(DEPDIR)/mate_session-gsm-xsmp-client.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-xsmp-client.c' object='mate_session-gsm-xsmp-client.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-xsmp-client.obj `if test -f 'gsm-xsmp-client.c'; then $(CYGPATH_W) 'gsm-xsmp-client.c'; else $(CYGPATH_W) '$(srcdir)/gsm-xsmp-client.c'; fi` + +mate_session-gsm-dbus-client.o: gsm-dbus-client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-dbus-client.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-dbus-client.Tpo -c -o mate_session-gsm-dbus-client.o `test -f 'gsm-dbus-client.c' || echo '$(srcdir)/'`gsm-dbus-client.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-dbus-client.Tpo $(DEPDIR)/mate_session-gsm-dbus-client.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-dbus-client.c' object='mate_session-gsm-dbus-client.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-dbus-client.o `test -f 'gsm-dbus-client.c' || echo '$(srcdir)/'`gsm-dbus-client.c + +mate_session-gsm-dbus-client.obj: gsm-dbus-client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-dbus-client.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-dbus-client.Tpo -c -o mate_session-gsm-dbus-client.obj `if test -f 'gsm-dbus-client.c'; then $(CYGPATH_W) 'gsm-dbus-client.c'; else $(CYGPATH_W) '$(srcdir)/gsm-dbus-client.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-dbus-client.Tpo $(DEPDIR)/mate_session-gsm-dbus-client.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-dbus-client.c' object='mate_session-gsm-dbus-client.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-dbus-client.obj `if test -f 'gsm-dbus-client.c'; then $(CYGPATH_W) 'gsm-dbus-client.c'; else $(CYGPATH_W) '$(srcdir)/gsm-dbus-client.c'; fi` + +mate_session-gsm-marshal.o: gsm-marshal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-marshal.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-marshal.Tpo -c -o mate_session-gsm-marshal.o `test -f 'gsm-marshal.c' || echo '$(srcdir)/'`gsm-marshal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-marshal.Tpo $(DEPDIR)/mate_session-gsm-marshal.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-marshal.c' object='mate_session-gsm-marshal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-marshal.o `test -f 'gsm-marshal.c' || echo '$(srcdir)/'`gsm-marshal.c + +mate_session-gsm-marshal.obj: gsm-marshal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-marshal.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-marshal.Tpo -c -o mate_session-gsm-marshal.obj `if test -f 'gsm-marshal.c'; then $(CYGPATH_W) 'gsm-marshal.c'; else $(CYGPATH_W) '$(srcdir)/gsm-marshal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-marshal.Tpo $(DEPDIR)/mate_session-gsm-marshal.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-marshal.c' object='mate_session-gsm-marshal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-marshal.obj `if test -f 'gsm-marshal.c'; then $(CYGPATH_W) 'gsm-marshal.c'; else $(CYGPATH_W) '$(srcdir)/gsm-marshal.c'; fi` + +mate_session-gsm-consolekit.o: gsm-consolekit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-consolekit.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-consolekit.Tpo -c -o mate_session-gsm-consolekit.o `test -f 'gsm-consolekit.c' || echo '$(srcdir)/'`gsm-consolekit.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-consolekit.Tpo $(DEPDIR)/mate_session-gsm-consolekit.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-consolekit.c' object='mate_session-gsm-consolekit.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-consolekit.o `test -f 'gsm-consolekit.c' || echo '$(srcdir)/'`gsm-consolekit.c + +mate_session-gsm-consolekit.obj: gsm-consolekit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-consolekit.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-consolekit.Tpo -c -o mate_session-gsm-consolekit.obj `if test -f 'gsm-consolekit.c'; then $(CYGPATH_W) 'gsm-consolekit.c'; else $(CYGPATH_W) '$(srcdir)/gsm-consolekit.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-consolekit.Tpo $(DEPDIR)/mate_session-gsm-consolekit.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-consolekit.c' object='mate_session-gsm-consolekit.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-consolekit.obj `if test -f 'gsm-consolekit.c'; then $(CYGPATH_W) 'gsm-consolekit.c'; else $(CYGPATH_W) '$(srcdir)/gsm-consolekit.c'; fi` + +mate_session-gsm-logout-dialog.o: gsm-logout-dialog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-logout-dialog.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-logout-dialog.Tpo -c -o mate_session-gsm-logout-dialog.o `test -f 'gsm-logout-dialog.c' || echo '$(srcdir)/'`gsm-logout-dialog.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-logout-dialog.Tpo $(DEPDIR)/mate_session-gsm-logout-dialog.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-logout-dialog.c' object='mate_session-gsm-logout-dialog.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-logout-dialog.o `test -f 'gsm-logout-dialog.c' || echo '$(srcdir)/'`gsm-logout-dialog.c + +mate_session-gsm-logout-dialog.obj: gsm-logout-dialog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-logout-dialog.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-logout-dialog.Tpo -c -o mate_session-gsm-logout-dialog.obj `if test -f 'gsm-logout-dialog.c'; then $(CYGPATH_W) 'gsm-logout-dialog.c'; else $(CYGPATH_W) '$(srcdir)/gsm-logout-dialog.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-logout-dialog.Tpo $(DEPDIR)/mate_session-gsm-logout-dialog.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-logout-dialog.c' object='mate_session-gsm-logout-dialog.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-logout-dialog.obj `if test -f 'gsm-logout-dialog.c'; then $(CYGPATH_W) 'gsm-logout-dialog.c'; else $(CYGPATH_W) '$(srcdir)/gsm-logout-dialog.c'; fi` + +mate_session-gsm-inhibit-dialog.o: gsm-inhibit-dialog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-inhibit-dialog.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-inhibit-dialog.Tpo -c -o mate_session-gsm-inhibit-dialog.o `test -f 'gsm-inhibit-dialog.c' || echo '$(srcdir)/'`gsm-inhibit-dialog.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-inhibit-dialog.Tpo $(DEPDIR)/mate_session-gsm-inhibit-dialog.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-inhibit-dialog.c' object='mate_session-gsm-inhibit-dialog.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-inhibit-dialog.o `test -f 'gsm-inhibit-dialog.c' || echo '$(srcdir)/'`gsm-inhibit-dialog.c + +mate_session-gsm-inhibit-dialog.obj: gsm-inhibit-dialog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-inhibit-dialog.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-inhibit-dialog.Tpo -c -o mate_session-gsm-inhibit-dialog.obj `if test -f 'gsm-inhibit-dialog.c'; then $(CYGPATH_W) 'gsm-inhibit-dialog.c'; else $(CYGPATH_W) '$(srcdir)/gsm-inhibit-dialog.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-inhibit-dialog.Tpo $(DEPDIR)/mate_session-gsm-inhibit-dialog.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-inhibit-dialog.c' object='mate_session-gsm-inhibit-dialog.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-inhibit-dialog.obj `if test -f 'gsm-inhibit-dialog.c'; then $(CYGPATH_W) 'gsm-inhibit-dialog.c'; else $(CYGPATH_W) '$(srcdir)/gsm-inhibit-dialog.c'; fi` + +mate_session-gs-idle-monitor.o: gs-idle-monitor.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gs-idle-monitor.o -MD -MP -MF $(DEPDIR)/mate_session-gs-idle-monitor.Tpo -c -o mate_session-gs-idle-monitor.o `test -f 'gs-idle-monitor.c' || echo '$(srcdir)/'`gs-idle-monitor.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gs-idle-monitor.Tpo $(DEPDIR)/mate_session-gs-idle-monitor.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gs-idle-monitor.c' object='mate_session-gs-idle-monitor.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gs-idle-monitor.o `test -f 'gs-idle-monitor.c' || echo '$(srcdir)/'`gs-idle-monitor.c + +mate_session-gs-idle-monitor.obj: gs-idle-monitor.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gs-idle-monitor.obj -MD -MP -MF $(DEPDIR)/mate_session-gs-idle-monitor.Tpo -c -o mate_session-gs-idle-monitor.obj `if test -f 'gs-idle-monitor.c'; then $(CYGPATH_W) 'gs-idle-monitor.c'; else $(CYGPATH_W) '$(srcdir)/gs-idle-monitor.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gs-idle-monitor.Tpo $(DEPDIR)/mate_session-gs-idle-monitor.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gs-idle-monitor.c' object='mate_session-gs-idle-monitor.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gs-idle-monitor.obj `if test -f 'gs-idle-monitor.c'; then $(CYGPATH_W) 'gs-idle-monitor.c'; else $(CYGPATH_W) '$(srcdir)/gs-idle-monitor.c'; fi` + +mate_session-gsm-presence.o: gsm-presence.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-presence.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-presence.Tpo -c -o mate_session-gsm-presence.o `test -f 'gsm-presence.c' || echo '$(srcdir)/'`gsm-presence.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-presence.Tpo $(DEPDIR)/mate_session-gsm-presence.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-presence.c' object='mate_session-gsm-presence.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-presence.o `test -f 'gsm-presence.c' || echo '$(srcdir)/'`gsm-presence.c + +mate_session-gsm-presence.obj: gsm-presence.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-presence.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-presence.Tpo -c -o mate_session-gsm-presence.obj `if test -f 'gsm-presence.c'; then $(CYGPATH_W) 'gsm-presence.c'; else $(CYGPATH_W) '$(srcdir)/gsm-presence.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-presence.Tpo $(DEPDIR)/mate_session-gsm-presence.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-presence.c' object='mate_session-gsm-presence.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-presence.obj `if test -f 'gsm-presence.c'; then $(CYGPATH_W) 'gsm-presence.c'; else $(CYGPATH_W) '$(srcdir)/gsm-presence.c'; fi` + +mate_session-gsm-mateconf.o: gsm-mateconf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-mateconf.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-mateconf.Tpo -c -o mate_session-gsm-mateconf.o `test -f 'gsm-mateconf.c' || echo '$(srcdir)/'`gsm-mateconf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-mateconf.Tpo $(DEPDIR)/mate_session-gsm-mateconf.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-mateconf.c' object='mate_session-gsm-mateconf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-mateconf.o `test -f 'gsm-mateconf.c' || echo '$(srcdir)/'`gsm-mateconf.c + +mate_session-gsm-mateconf.obj: gsm-mateconf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-mateconf.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-mateconf.Tpo -c -o mate_session-gsm-mateconf.obj `if test -f 'gsm-mateconf.c'; then $(CYGPATH_W) 'gsm-mateconf.c'; else $(CYGPATH_W) '$(srcdir)/gsm-mateconf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-mateconf.Tpo $(DEPDIR)/mate_session-gsm-mateconf.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-mateconf.c' object='mate_session-gsm-mateconf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-mateconf.obj `if test -f 'gsm-mateconf.c'; then $(CYGPATH_W) 'gsm-mateconf.c'; else $(CYGPATH_W) '$(srcdir)/gsm-mateconf.c'; fi` + +mate_session-mdm.o: mdm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-mdm.o -MD -MP -MF $(DEPDIR)/mate_session-mdm.Tpo -c -o mate_session-mdm.o `test -f 'mdm.c' || echo '$(srcdir)/'`mdm.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-mdm.Tpo $(DEPDIR)/mate_session-mdm.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mdm.c' object='mate_session-mdm.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-mdm.o `test -f 'mdm.c' || echo '$(srcdir)/'`mdm.c + +mate_session-mdm.obj: mdm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-mdm.obj -MD -MP -MF $(DEPDIR)/mate_session-mdm.Tpo -c -o mate_session-mdm.obj `if test -f 'mdm.c'; then $(CYGPATH_W) 'mdm.c'; else $(CYGPATH_W) '$(srcdir)/mdm.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-mdm.Tpo $(DEPDIR)/mate_session-mdm.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mdm.c' object='mate_session-mdm.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-mdm.obj `if test -f 'mdm.c'; then $(CYGPATH_W) 'mdm.c'; else $(CYGPATH_W) '$(srcdir)/mdm.c'; fi` + +mate_session-mdm-signal-handler.o: mdm-signal-handler.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-mdm-signal-handler.o -MD -MP -MF $(DEPDIR)/mate_session-mdm-signal-handler.Tpo -c -o mate_session-mdm-signal-handler.o `test -f 'mdm-signal-handler.c' || echo '$(srcdir)/'`mdm-signal-handler.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-mdm-signal-handler.Tpo $(DEPDIR)/mate_session-mdm-signal-handler.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mdm-signal-handler.c' object='mate_session-mdm-signal-handler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-mdm-signal-handler.o `test -f 'mdm-signal-handler.c' || echo '$(srcdir)/'`mdm-signal-handler.c + +mate_session-mdm-signal-handler.obj: mdm-signal-handler.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-mdm-signal-handler.obj -MD -MP -MF $(DEPDIR)/mate_session-mdm-signal-handler.Tpo -c -o mate_session-mdm-signal-handler.obj `if test -f 'mdm-signal-handler.c'; then $(CYGPATH_W) 'mdm-signal-handler.c'; else $(CYGPATH_W) '$(srcdir)/mdm-signal-handler.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-mdm-signal-handler.Tpo $(DEPDIR)/mate_session-mdm-signal-handler.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mdm-signal-handler.c' object='mate_session-mdm-signal-handler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-mdm-signal-handler.obj `if test -f 'mdm-signal-handler.c'; then $(CYGPATH_W) 'mdm-signal-handler.c'; else $(CYGPATH_W) '$(srcdir)/mdm-signal-handler.c'; fi` + +mate_session-mdm-log.o: mdm-log.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-mdm-log.o -MD -MP -MF $(DEPDIR)/mate_session-mdm-log.Tpo -c -o mate_session-mdm-log.o `test -f 'mdm-log.c' || echo '$(srcdir)/'`mdm-log.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-mdm-log.Tpo $(DEPDIR)/mate_session-mdm-log.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mdm-log.c' object='mate_session-mdm-log.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-mdm-log.o `test -f 'mdm-log.c' || echo '$(srcdir)/'`mdm-log.c + +mate_session-mdm-log.obj: mdm-log.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-mdm-log.obj -MD -MP -MF $(DEPDIR)/mate_session-mdm-log.Tpo -c -o mate_session-mdm-log.obj `if test -f 'mdm-log.c'; then $(CYGPATH_W) 'mdm-log.c'; else $(CYGPATH_W) '$(srcdir)/mdm-log.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-mdm-log.Tpo $(DEPDIR)/mate_session-mdm-log.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mdm-log.c' object='mate_session-mdm-log.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-mdm-log.obj `if test -f 'mdm-log.c'; then $(CYGPATH_W) 'mdm-log.c'; else $(CYGPATH_W) '$(srcdir)/mdm-log.c'; fi` + +mate_session-main.o: main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-main.o -MD -MP -MF $(DEPDIR)/mate_session-main.Tpo -c -o mate_session-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-main.Tpo $(DEPDIR)/mate_session-main.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='main.c' object='mate_session-main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c + +mate_session-main.obj: main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-main.obj -MD -MP -MF $(DEPDIR)/mate_session-main.Tpo -c -o mate_session-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-main.Tpo $(DEPDIR)/mate_session-main.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='main.c' object='mate_session-main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` + +mate_session-gsm-store.o: gsm-store.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-store.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-store.Tpo -c -o mate_session-gsm-store.o `test -f 'gsm-store.c' || echo '$(srcdir)/'`gsm-store.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-store.Tpo $(DEPDIR)/mate_session-gsm-store.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-store.c' object='mate_session-gsm-store.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-store.o `test -f 'gsm-store.c' || echo '$(srcdir)/'`gsm-store.c + +mate_session-gsm-store.obj: gsm-store.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-store.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-store.Tpo -c -o mate_session-gsm-store.obj `if test -f 'gsm-store.c'; then $(CYGPATH_W) 'gsm-store.c'; else $(CYGPATH_W) '$(srcdir)/gsm-store.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-store.Tpo $(DEPDIR)/mate_session-gsm-store.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-store.c' object='mate_session-gsm-store.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-store.obj `if test -f 'gsm-store.c'; then $(CYGPATH_W) 'gsm-store.c'; else $(CYGPATH_W) '$(srcdir)/gsm-store.c'; fi` + +mate_session-gsm-inhibitor.o: gsm-inhibitor.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-inhibitor.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-inhibitor.Tpo -c -o mate_session-gsm-inhibitor.o `test -f 'gsm-inhibitor.c' || echo '$(srcdir)/'`gsm-inhibitor.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-inhibitor.Tpo $(DEPDIR)/mate_session-gsm-inhibitor.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-inhibitor.c' object='mate_session-gsm-inhibitor.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-inhibitor.o `test -f 'gsm-inhibitor.c' || echo '$(srcdir)/'`gsm-inhibitor.c + +mate_session-gsm-inhibitor.obj: gsm-inhibitor.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-inhibitor.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-inhibitor.Tpo -c -o mate_session-gsm-inhibitor.obj `if test -f 'gsm-inhibitor.c'; then $(CYGPATH_W) 'gsm-inhibitor.c'; else $(CYGPATH_W) '$(srcdir)/gsm-inhibitor.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-inhibitor.Tpo $(DEPDIR)/mate_session-gsm-inhibitor.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-inhibitor.c' object='mate_session-gsm-inhibitor.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-inhibitor.obj `if test -f 'gsm-inhibitor.c'; then $(CYGPATH_W) 'gsm-inhibitor.c'; else $(CYGPATH_W) '$(srcdir)/gsm-inhibitor.c'; fi` + +mate_session-gsm-manager.o: gsm-manager.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-manager.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-manager.Tpo -c -o mate_session-gsm-manager.o `test -f 'gsm-manager.c' || echo '$(srcdir)/'`gsm-manager.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-manager.Tpo $(DEPDIR)/mate_session-gsm-manager.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-manager.c' object='mate_session-gsm-manager.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-manager.o `test -f 'gsm-manager.c' || echo '$(srcdir)/'`gsm-manager.c + +mate_session-gsm-manager.obj: gsm-manager.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-manager.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-manager.Tpo -c -o mate_session-gsm-manager.obj `if test -f 'gsm-manager.c'; then $(CYGPATH_W) 'gsm-manager.c'; else $(CYGPATH_W) '$(srcdir)/gsm-manager.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-manager.Tpo $(DEPDIR)/mate_session-gsm-manager.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-manager.c' object='mate_session-gsm-manager.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-manager.obj `if test -f 'gsm-manager.c'; then $(CYGPATH_W) 'gsm-manager.c'; else $(CYGPATH_W) '$(srcdir)/gsm-manager.c'; fi` + +mate_session-gsm-session-save.o: gsm-session-save.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-session-save.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-session-save.Tpo -c -o mate_session-gsm-session-save.o `test -f 'gsm-session-save.c' || echo '$(srcdir)/'`gsm-session-save.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-session-save.Tpo $(DEPDIR)/mate_session-gsm-session-save.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-session-save.c' object='mate_session-gsm-session-save.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-session-save.o `test -f 'gsm-session-save.c' || echo '$(srcdir)/'`gsm-session-save.c + +mate_session-gsm-session-save.obj: gsm-session-save.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-session-save.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-session-save.Tpo -c -o mate_session-gsm-session-save.obj `if test -f 'gsm-session-save.c'; then $(CYGPATH_W) 'gsm-session-save.c'; else $(CYGPATH_W) '$(srcdir)/gsm-session-save.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-session-save.Tpo $(DEPDIR)/mate_session-gsm-session-save.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-session-save.c' object='mate_session-gsm-session-save.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-session-save.obj `if test -f 'gsm-session-save.c'; then $(CYGPATH_W) 'gsm-session-save.c'; else $(CYGPATH_W) '$(srcdir)/gsm-session-save.c'; fi` + +mate_session-gsm-xsmp-server.o: gsm-xsmp-server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-xsmp-server.o -MD -MP -MF $(DEPDIR)/mate_session-gsm-xsmp-server.Tpo -c -o mate_session-gsm-xsmp-server.o `test -f 'gsm-xsmp-server.c' || echo '$(srcdir)/'`gsm-xsmp-server.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-xsmp-server.Tpo $(DEPDIR)/mate_session-gsm-xsmp-server.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-xsmp-server.c' object='mate_session-gsm-xsmp-server.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-xsmp-server.o `test -f 'gsm-xsmp-server.c' || echo '$(srcdir)/'`gsm-xsmp-server.c + +mate_session-gsm-xsmp-server.obj: gsm-xsmp-server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mate_session-gsm-xsmp-server.obj -MD -MP -MF $(DEPDIR)/mate_session-gsm-xsmp-server.Tpo -c -o mate_session-gsm-xsmp-server.obj `if test -f 'gsm-xsmp-server.c'; then $(CYGPATH_W) 'gsm-xsmp-server.c'; else $(CYGPATH_W) '$(srcdir)/gsm-xsmp-server.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_session-gsm-xsmp-server.Tpo $(DEPDIR)/mate_session-gsm-xsmp-server.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsm-xsmp-server.c' object='mate_session-gsm-xsmp-server.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_session_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mate_session-gsm-xsmp-server.obj `if test -f 'gsm-xsmp-server.c'; then $(CYGPATH_W) 'gsm-xsmp-server.c'; else $(CYGPATH_W) '$(srcdir)/gsm-xsmp-server.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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + 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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + 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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__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 "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$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 $(LTLIBRARIES) $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; 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: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +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-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES clean-noinstPROGRAMS 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 + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +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-binPROGRAMS + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-noinstPROGRAMS ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS 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-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-binPROGRAMS + + +gsm-marshal.c: gsm-marshal.list + $(AM_V_GEN)echo "#include \"gsm-marshal.h\"" > $@ && \ + $(GLIB_GENMARSHAL) $< --prefix=gsm_marshal --body >> $@ + +gsm-marshal.h: gsm-marshal.list + $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --prefix=gsm_marshal --header > $@ + +gsm-manager-glue.h: org.mate.SessionManager.xml Makefile.am + $(AM_V_GEN)dbus-binding-tool --prefix=gsm_manager --mode=glib-server --output=gsm-manager-glue.h $(srcdir)/org.mate.SessionManager.xml + +gsm-client-glue.h: org.mate.SessionManager.Client.xml Makefile.am + $(AM_V_GEN)dbus-binding-tool --prefix=gsm_client --mode=glib-server --output=gsm-client-glue.h $(srcdir)/org.mate.SessionManager.Client.xml + +gsm-app-glue.h: org.mate.SessionManager.App.xml Makefile.am + $(AM_V_GEN)dbus-binding-tool --prefix=gsm_app --mode=glib-server --output=gsm-app-glue.h $(srcdir)/org.mate.SessionManager.App.xml + +gsm-inhibitor-glue.h: org.mate.SessionManager.Inhibitor.xml Makefile.am + $(AM_V_GEN)dbus-binding-tool --prefix=gsm_inhibitor --mode=glib-server --output=gsm-inhibitor-glue.h $(srcdir)/org.mate.SessionManager.Inhibitor.xml + +gsm-presence-glue.h: org.mate.SessionManager.Presence.xml Makefile.am + $(AM_V_GEN)dbus-binding-tool --prefix=gsm_presence --mode=glib-server --output=gsm-presence-glue.h $(srcdir)/org.mate.SessionManager.Presence.xml + +-include $(top_srcdir)/git.mk + +# 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/mate-session/README b/mate-session/README new file mode 100644 index 0000000..05bfe9d --- /dev/null +++ b/mate-session/README @@ -0,0 +1,65 @@ +See also http://live.gnome.org/SessionManagement/NewGnomeSession + +Startup +------- + +main() creates the GsmSession object representing the session (either +failsafe or normal). gsm_session_new() reads the appropriate autostart +and session files to create a list of GsmApps to be started. +(GsmAppAutostart represents an autostarted app, and GsmAppResumed +represents an app resumed from the previous saved session.) + +Startup is divided into 6 phases (GsmSessionPhase): + + * GSM_SESSION_PHASE_STARTUP covers mate-session's internal + startup, which also includes starting mateconfd and dbus-daemon (if + it's not already running). Mate-session starts up those + explicitly because it needs them for its own purposes. + + * GSM_SESSION_PHASE_INITIALIZATION is the first phase of "normal" + startup (ie, startup controlled by .desktop files rather than + hardcoding). It covers low-level stuff like + mate-settings-daemon and at-spi-registryd, that need to be + running very early (before any windows are displayed). + + Apps in this phase can make use of a D-Bus interface + (org.mate.SessionManager.Setenv) to set environment variables + in mate-session's environment. This can be used for things like + $GTK_MODULES, $MATE_KEYRING_SOCKET, etc + + * GSM_SESSION_PHASE_WINDOW_MANAGER includes window managers and + compositing managers, and anything else that has to be running + before any windows are mapped + + * GSM_SESSION_PHASE_PANEL includes anything that permanently takes + up screen real estate (via EWMH struts). This is the first phase + where things actually appear on the screen. + + * GSM_SESSION_PHASE_DESKTOP includes anything that draws directly + on the desktop (eg, caja). + + * GSM_SESSION_PHASE_APPLICATION is everything else (normal apps, + tray icons, etc) + +For each startup phase, GsmSession launches the appropriate GsmApps. +When apps connect to the XSMP or D-Bus servers, GsmClients are created +and added to the session. The session tries to map these clients to +GsmApps. GsmApps signal when they register (via XSMP or SN) or exit, +and GsmSession uses this to decide when the phase is complete. + +FIXME: after starting the session, we need to run the DiscardCommands +of resumed apps. + + +Running/Shutdown +---------------- + +GSM_SESSION_PHASE_RUNNING is pretty similar to the old mate-session; +mostly it just tracks XSMP clients, and watches for +SmRestartImmediately clients exiting (NOTE: THIS DOESN'T HAPPEN YET). + +GsmClient is in theory not XSMP-specific, but it's very very +XSMP-like, and the shutdown procedure is also very XSMP-like. This is +just because there's no way to do XSMP shutdown correctly otherwise. +However, GsmClientDBus will still be able to present a more sane +protocol to its clients than GsmClient presents to it. diff --git a/mate-session/gs-idle-monitor.c b/mate-session/gs-idle-monitor.c new file mode 100644 index 0000000..05dbb02 --- /dev/null +++ b/mate-session/gs-idle-monitor.c @@ -0,0 +1,507 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Authors: William Jon McCann <[email protected]> + * + */ + +#include "config.h" + +#include <time.h> +#include <string.h> + +#include <X11/Xlib.h> +#include <X11/extensions/sync.h> + +#ifdef HAVE_XTEST +#include <X11/keysym.h> +#include <X11/extensions/XTest.h> +#endif /* HAVE_XTEST */ + +#include <glib.h> +#include <gdk/gdkx.h> +#include <gdk/gdk.h> + +#include "gs-idle-monitor.h" + +static void gs_idle_monitor_class_init (GSIdleMonitorClass *klass); +static void gs_idle_monitor_init (GSIdleMonitor *idle_monitor); +static void gs_idle_monitor_finalize (GObject *object); + +#define GS_IDLE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_IDLE_MONITOR, GSIdleMonitorPrivate)) + +struct GSIdleMonitorPrivate +{ + GHashTable *watches; + int sync_event_base; + XSyncCounter counter; + + /* For use with XTest */ + int *keycode; + int keycode1; + int keycode2; + gboolean have_xtest; +}; + +typedef struct +{ + guint id; + XSyncValue interval; + GSIdleMonitorWatchFunc callback; + gpointer user_data; + XSyncAlarm xalarm_positive; + XSyncAlarm xalarm_negative; +} GSIdleMonitorWatch; + +static guint32 watch_serial = 1; + +G_DEFINE_TYPE (GSIdleMonitor, gs_idle_monitor, G_TYPE_OBJECT) + +static gint64 +_xsyncvalue_to_int64 (XSyncValue value) +{ + return ((guint64) XSyncValueHigh32 (value)) << 32 + | (guint64) XSyncValueLow32 (value); +} + +static XSyncValue +_int64_to_xsyncvalue (gint64 value) +{ + XSyncValue ret; + + XSyncIntsToValue (&ret, value, ((guint64)value) >> 32); + + return ret; +} + +static void +gs_idle_monitor_dispose (GObject *object) +{ + GSIdleMonitor *monitor; + + g_return_if_fail (GS_IS_IDLE_MONITOR (object)); + + monitor = GS_IDLE_MONITOR (object); + + if (monitor->priv->watches != NULL) { + g_hash_table_destroy (monitor->priv->watches); + monitor->priv->watches = NULL; + } + + G_OBJECT_CLASS (gs_idle_monitor_parent_class)->dispose (object); +} + +static gboolean +_find_alarm (gpointer key, + GSIdleMonitorWatch *watch, + XSyncAlarm *alarm) +{ + g_debug ("Searching for %d in %d,%d", (int)*alarm, (int)watch->xalarm_positive, (int)watch->xalarm_negative); + if (watch->xalarm_positive == *alarm + || watch->xalarm_negative == *alarm) { + return TRUE; + } + return FALSE; +} + +static GSIdleMonitorWatch * +find_watch_for_alarm (GSIdleMonitor *monitor, + XSyncAlarm alarm) +{ + GSIdleMonitorWatch *watch; + + watch = g_hash_table_find (monitor->priv->watches, + (GHRFunc)_find_alarm, + &alarm); + return watch; +} + +#ifdef HAVE_XTEST +static gboolean +send_fake_event (GSIdleMonitor *monitor) +{ + if (! monitor->priv->have_xtest) { + return FALSE; + } + + g_debug ("GSIdleMonitor: sending fake key"); + + XLockDisplay (GDK_DISPLAY()); + XTestFakeKeyEvent (GDK_DISPLAY(), + *monitor->priv->keycode, + True, + CurrentTime); + XTestFakeKeyEvent (GDK_DISPLAY(), + *monitor->priv->keycode, + False, + CurrentTime); + XUnlockDisplay (GDK_DISPLAY()); + + /* Swap the keycode */ + if (monitor->priv->keycode == &monitor->priv->keycode1) { + monitor->priv->keycode = &monitor->priv->keycode2; + } else { + monitor->priv->keycode = &monitor->priv->keycode1; + } + + return TRUE; +} +#endif /* HAVE_XTEST */ + +void +gs_idle_monitor_reset (GSIdleMonitor *monitor) +{ + g_return_if_fail (GS_IS_IDLE_MONITOR (monitor)); + +#ifdef HAVE_XTEST + /* FIXME: is there a better way to reset the IDLETIME? */ + send_fake_event (monitor); +#endif +} + +static void +handle_alarm_notify_event (GSIdleMonitor *monitor, + XSyncAlarmNotifyEvent *alarm_event) +{ + GSIdleMonitorWatch *watch; + gboolean res; + gboolean condition; + + if (alarm_event->state == XSyncAlarmDestroyed) { + return; + } + + watch = find_watch_for_alarm (monitor, alarm_event->alarm); + + if (watch == NULL) { + g_debug ("Unable to find watch for alarm %d", (int)alarm_event->alarm); + return; + } + + g_debug ("Watch %d fired, idle time = %lld", + watch->id, + _xsyncvalue_to_int64 (alarm_event->counter_value)); + + if (alarm_event->alarm == watch->xalarm_positive) { + condition = TRUE; + } else { + condition = FALSE; + } + + res = TRUE; + if (watch->callback != NULL) { + res = watch->callback (monitor, + watch->id, + condition, + watch->user_data); + } + + if (! res) { + /* reset all timers */ + g_debug ("GSIdleMonitor: callback returned FALSE; resetting idle time"); + gs_idle_monitor_reset (monitor); + } +} + +static GdkFilterReturn +xevent_filter (GdkXEvent *xevent, + GdkEvent *event, + GSIdleMonitor *monitor) +{ + XEvent *ev; + XSyncAlarmNotifyEvent *alarm_event; + + ev = xevent; + if (ev->xany.type != monitor->priv->sync_event_base + XSyncAlarmNotify) { + return GDK_FILTER_CONTINUE; + } + + alarm_event = xevent; + + handle_alarm_notify_event (monitor, alarm_event); + + return GDK_FILTER_CONTINUE; +} + +static gboolean +init_xsync (GSIdleMonitor *monitor) +{ + int sync_error_base; + int res; + int major; + int minor; + int i; + int ncounters; + XSyncSystemCounter *counters; + + res = XSyncQueryExtension (GDK_DISPLAY (), + &monitor->priv->sync_event_base, + &sync_error_base); + if (! res) { + g_warning ("GSIdleMonitor: Sync extension not present"); + return FALSE; + } + + res = XSyncInitialize (GDK_DISPLAY (), &major, &minor); + if (! res) { + g_warning ("GSIdleMonitor: Unable to initialize Sync extension"); + return FALSE; + } + + counters = XSyncListSystemCounters (GDK_DISPLAY (), &ncounters); + for (i = 0; i < ncounters; i++) { + if (counters[i].name != NULL + && strcmp (counters[i].name, "IDLETIME") == 0) { + monitor->priv->counter = counters[i].counter; + break; + } + } + XSyncFreeSystemCounterList (counters); + + if (monitor->priv->counter == None) { + g_warning ("GSIdleMonitor: IDLETIME counter not found"); + return FALSE; + } + + gdk_window_add_filter (NULL, (GdkFilterFunc)xevent_filter, monitor); + + return TRUE; +} + +static void +_init_xtest (GSIdleMonitor *monitor) +{ +#ifdef HAVE_XTEST + int a, b, c, d; + + XLockDisplay (GDK_DISPLAY()); + monitor->priv->have_xtest = (XTestQueryExtension (GDK_DISPLAY(), &a, &b, &c, &d) == True); + if (monitor->priv->have_xtest) { + monitor->priv->keycode1 = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_L); + if (monitor->priv->keycode1 == 0) { + g_warning ("keycode1 not existant"); + } + monitor->priv->keycode2 = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_R); + if (monitor->priv->keycode2 == 0) { + monitor->priv->keycode2 = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_L); + if (monitor->priv->keycode2 == 0) { + g_warning ("keycode2 not existant"); + } + } + monitor->priv->keycode = &monitor->priv->keycode1; + } + XUnlockDisplay (GDK_DISPLAY()); +#endif /* HAVE_XTEST */ +} + +static GObject * +gs_idle_monitor_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GSIdleMonitor *monitor; + + monitor = GS_IDLE_MONITOR (G_OBJECT_CLASS (gs_idle_monitor_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + _init_xtest (monitor); + + if (! init_xsync (monitor)) { + g_object_unref (monitor); + return NULL; + } + + return G_OBJECT (monitor); +} + +static void +gs_idle_monitor_class_init (GSIdleMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gs_idle_monitor_finalize; + object_class->dispose = gs_idle_monitor_dispose; + object_class->constructor = gs_idle_monitor_constructor; + + g_type_class_add_private (klass, sizeof (GSIdleMonitorPrivate)); +} + +static guint32 +get_next_watch_serial (void) +{ + guint32 serial; + + serial = watch_serial++; + + if ((gint32)watch_serial < 0) { + watch_serial = 1; + } + + /* FIXME: make sure it isn't in the hash */ + + return serial; +} + +static GSIdleMonitorWatch * +idle_monitor_watch_new (guint interval) +{ + GSIdleMonitorWatch *watch; + + watch = g_slice_new0 (GSIdleMonitorWatch); + watch->interval = _int64_to_xsyncvalue ((gint64)interval); + watch->id = get_next_watch_serial (); + watch->xalarm_positive = None; + watch->xalarm_negative = None; + + return watch; +} + +static void +idle_monitor_watch_free (GSIdleMonitorWatch *watch) +{ + if (watch == NULL) { + return; + } + if (watch->xalarm_positive != None) { + XSyncDestroyAlarm (GDK_DISPLAY (), watch->xalarm_positive); + } + if (watch->xalarm_negative != None) { + XSyncDestroyAlarm (GDK_DISPLAY (), watch->xalarm_negative); + } + g_slice_free (GSIdleMonitorWatch, watch); +} + +static void +gs_idle_monitor_init (GSIdleMonitor *monitor) +{ + monitor->priv = GS_IDLE_MONITOR_GET_PRIVATE (monitor); + + monitor->priv->watches = g_hash_table_new_full (NULL, + NULL, + NULL, + (GDestroyNotify)idle_monitor_watch_free); + + monitor->priv->counter = None; +} + +static void +gs_idle_monitor_finalize (GObject *object) +{ + GSIdleMonitor *idle_monitor; + + g_return_if_fail (object != NULL); + g_return_if_fail (GS_IS_IDLE_MONITOR (object)); + + idle_monitor = GS_IDLE_MONITOR (object); + + g_return_if_fail (idle_monitor->priv != NULL); + + G_OBJECT_CLASS (gs_idle_monitor_parent_class)->finalize (object); +} + +GSIdleMonitor * +gs_idle_monitor_new (void) +{ + GObject *idle_monitor; + + idle_monitor = g_object_new (GS_TYPE_IDLE_MONITOR, + NULL); + + return GS_IDLE_MONITOR (idle_monitor); +} + +static gboolean +_xsync_alarm_set (GSIdleMonitor *monitor, + GSIdleMonitorWatch *watch) +{ + XSyncAlarmAttributes attr; + XSyncValue delta; + guint flags; + + flags = XSyncCACounter + | XSyncCAValueType + | XSyncCATestType + | XSyncCAValue + | XSyncCADelta + | XSyncCAEvents; + + XSyncIntToValue (&delta, 0); + attr.trigger.counter = monitor->priv->counter; + attr.trigger.value_type = XSyncAbsolute; + attr.trigger.wait_value = watch->interval; + attr.delta = delta; + attr.events = TRUE; + + attr.trigger.test_type = XSyncPositiveTransition; + if (watch->xalarm_positive != None) { + g_debug ("GSIdleMonitor: updating alarm for positive transition wait=%lld", + _xsyncvalue_to_int64 (attr.trigger.wait_value)); + XSyncChangeAlarm (GDK_DISPLAY (), watch->xalarm_positive, flags, &attr); + } else { + g_debug ("GSIdleMonitor: creating new alarm for positive transition wait=%lld", + _xsyncvalue_to_int64 (attr.trigger.wait_value)); + watch->xalarm_positive = XSyncCreateAlarm (GDK_DISPLAY (), flags, &attr); + } + + attr.trigger.test_type = XSyncNegativeTransition; + if (watch->xalarm_negative != None) { + g_debug ("GSIdleMonitor: updating alarm for negative transition wait=%lld", + _xsyncvalue_to_int64 (attr.trigger.wait_value)); + XSyncChangeAlarm (GDK_DISPLAY (), watch->xalarm_negative, flags, &attr); + } else { + g_debug ("GSIdleMonitor: creating new alarm for negative transition wait=%lld", + _xsyncvalue_to_int64 (attr.trigger.wait_value)); + watch->xalarm_negative = XSyncCreateAlarm (GDK_DISPLAY (), flags, &attr); + } + + return TRUE; +} + +guint +gs_idle_monitor_add_watch (GSIdleMonitor *monitor, + guint interval, + GSIdleMonitorWatchFunc callback, + gpointer user_data) +{ + GSIdleMonitorWatch *watch; + + g_return_val_if_fail (GS_IS_IDLE_MONITOR (monitor), 0); + g_return_val_if_fail (callback != NULL, 0); + + watch = idle_monitor_watch_new (interval); + watch->callback = callback; + watch->user_data = user_data; + + _xsync_alarm_set (monitor, watch); + + g_hash_table_insert (monitor->priv->watches, + GUINT_TO_POINTER (watch->id), + watch); + return watch->id; +} + +void +gs_idle_monitor_remove_watch (GSIdleMonitor *monitor, + guint id) +{ + g_return_if_fail (GS_IS_IDLE_MONITOR (monitor)); + + g_hash_table_remove (monitor->priv->watches, + GUINT_TO_POINTER (id)); +} diff --git a/mate-session/gs-idle-monitor.h b/mate-session/gs-idle-monitor.h new file mode 100644 index 0000000..631f8eb --- /dev/null +++ b/mate-session/gs-idle-monitor.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Authors: William Jon McCann <[email protected]> + * + */ + +#ifndef __GS_IDLE_MONITOR_H +#define __GS_IDLE_MONITOR_H + +#include <glib-object.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GS_TYPE_IDLE_MONITOR (gs_idle_monitor_get_type ()) +#define GS_IDLE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_IDLE_MONITOR, GSIdleMonitor)) +#define GS_IDLE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_IDLE_MONITOR, GSIdleMonitorClass)) +#define GS_IS_IDLE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_IDLE_MONITOR)) +#define GS_IS_IDLE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_IDLE_MONITOR)) +#define GS_IDLE_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_IDLE_MONITOR, GSIdleMonitorClass)) + +typedef struct GSIdleMonitorPrivate GSIdleMonitorPrivate; + +typedef struct +{ + GObject parent; + GSIdleMonitorPrivate *priv; +} GSIdleMonitor; + +typedef struct +{ + GObjectClass parent_class; +} GSIdleMonitorClass; + +typedef gboolean (*GSIdleMonitorWatchFunc) (GSIdleMonitor *monitor, + guint id, + gboolean condition, + gpointer user_data); + +GType gs_idle_monitor_get_type (void); + +GSIdleMonitor * gs_idle_monitor_new (void); + +guint gs_idle_monitor_add_watch (GSIdleMonitor *monitor, + guint interval, + GSIdleMonitorWatchFunc callback, + gpointer user_data); + +void gs_idle_monitor_remove_watch (GSIdleMonitor *monitor, + guint id); +void gs_idle_monitor_reset (GSIdleMonitor *monitor); + + +#ifdef __cplusplus +} +#endif + +#endif /* __GS_IDLE_MONITOR_H */ diff --git a/mate-session/gsm-app.c b/mate-session/gsm-app.c new file mode 100644 index 0000000..8131177 --- /dev/null +++ b/mate-session/gsm-app.c @@ -0,0 +1,489 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <glib.h> +#include <string.h> + +#include "gsm-app.h" +#include "gsm-app-glue.h" + +#define GSM_APP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_APP, GsmAppPrivate)) + +struct _GsmAppPrivate +{ + char *id; + char *app_id; + int phase; + char *startup_id; + DBusGConnection *connection; +}; + + +enum { + EXITED, + DIED, + REGISTERED, + LAST_SIGNAL +}; + +static guint32 app_serial = 1; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_ID, + PROP_STARTUP_ID, + PROP_PHASE, + LAST_PROP +}; + +G_DEFINE_TYPE (GsmApp, gsm_app, G_TYPE_OBJECT) + +GQuark +gsm_app_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gsm_app_error"); + } + + return ret; + +} + +static guint32 +get_next_app_serial (void) +{ + guint32 serial; + + serial = app_serial++; + + if ((gint32)app_serial < 0) { + app_serial = 1; + } + + return serial; +} + +static gboolean +register_app (GsmApp *app) +{ + GError *error; + + error = NULL; + app->priv->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (app->priv->connection == NULL) { + if (error != NULL) { + g_critical ("error getting session bus: %s", error->message); + g_error_free (error); + } + return FALSE; + } + + dbus_g_connection_register_g_object (app->priv->connection, app->priv->id, G_OBJECT (app)); + + return TRUE; +} + +static GObject * +gsm_app_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmApp *app; + gboolean res; + + app = GSM_APP (G_OBJECT_CLASS (gsm_app_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + g_free (app->priv->id); + app->priv->id = g_strdup_printf ("/org/mate/SessionManager/App%u", get_next_app_serial ()); + + res = register_app (app); + if (! res) { + g_warning ("Unable to register app with session bus"); + } + + return G_OBJECT (app); +} + +static void +gsm_app_init (GsmApp *app) +{ + app->priv = GSM_APP_GET_PRIVATE (app); +} + +static void +gsm_app_set_phase (GsmApp *app, + int phase) +{ + g_return_if_fail (GSM_IS_APP (app)); + + app->priv->phase = phase; +} + +static void +gsm_app_set_id (GsmApp *app, + const char *id) +{ + g_return_if_fail (GSM_IS_APP (app)); + + g_free (app->priv->id); + + app->priv->id = g_strdup (id); + g_object_notify (G_OBJECT (app), "id"); + +} +static void +gsm_app_set_startup_id (GsmApp *app, + const char *startup_id) +{ + g_return_if_fail (GSM_IS_APP (app)); + + g_free (app->priv->startup_id); + + app->priv->startup_id = g_strdup (startup_id); + g_object_notify (G_OBJECT (app), "startup-id"); + +} + +static void +gsm_app_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmApp *app = GSM_APP (object); + + switch (prop_id) { + case PROP_STARTUP_ID: + gsm_app_set_startup_id (app, g_value_get_string (value)); + break; + case PROP_ID: + gsm_app_set_id (app, g_value_get_string (value)); + break; + case PROP_PHASE: + gsm_app_set_phase (app, g_value_get_int (value)); + break; + default: + break; + } +} + +static void +gsm_app_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmApp *app = GSM_APP (object); + + switch (prop_id) { + case PROP_STARTUP_ID: + g_value_set_string (value, app->priv->startup_id); + break; + case PROP_ID: + g_value_set_string (value, app->priv->id); + break; + case PROP_PHASE: + g_value_set_int (value, app->priv->phase); + break; + default: + break; + } +} + +static void +gsm_app_dispose (GObject *object) +{ + GsmApp *app = GSM_APP (object); + + g_free (app->priv->startup_id); + app->priv->startup_id = NULL; + + g_free (app->priv->id); + app->priv->id = NULL; + + G_OBJECT_CLASS (gsm_app_parent_class)->dispose (object); +} + +static void +gsm_app_class_init (GsmAppClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gsm_app_set_property; + object_class->get_property = gsm_app_get_property; + object_class->dispose = gsm_app_dispose; + object_class->constructor = gsm_app_constructor; + + klass->impl_start = NULL; + klass->impl_get_app_id = NULL; + klass->impl_get_autorestart = NULL; + klass->impl_provides = NULL; + klass->impl_is_running = NULL; + + g_object_class_install_property (object_class, + PROP_PHASE, + g_param_spec_int ("phase", + "Phase", + "Phase", + -1, + G_MAXINT, + -1, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_ID, + g_param_spec_string ("id", + "ID", + "ID", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_STARTUP_ID, + g_param_spec_string ("startup-id", + "startup ID", + "Session management startup ID", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + signals[EXITED] = + g_signal_new ("exited", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmAppClass, exited), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals[DIED] = + g_signal_new ("died", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmAppClass, died), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + signals[REGISTERED] = + g_signal_new ("registered", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmAppClass, registered), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_type_class_add_private (klass, sizeof (GsmAppPrivate)); + dbus_g_object_type_install_info (GSM_TYPE_APP, &dbus_glib_gsm_app_object_info); +} + +const char * +gsm_app_peek_id (GsmApp *app) +{ + return app->priv->id; +} + +const char * +gsm_app_peek_app_id (GsmApp *app) +{ + return GSM_APP_GET_CLASS (app)->impl_get_app_id (app); +} + +const char * +gsm_app_peek_startup_id (GsmApp *app) +{ + return app->priv->startup_id; +} + +/** + * gsm_app_peek_phase: + * @app: a %GsmApp + * + * Returns @app's startup phase. + * + * Return value: @app's startup phase + **/ +GsmManagerPhase +gsm_app_peek_phase (GsmApp *app) +{ + g_return_val_if_fail (GSM_IS_APP (app), GSM_MANAGER_PHASE_APPLICATION); + + return app->priv->phase; +} + +gboolean +gsm_app_peek_is_disabled (GsmApp *app) +{ + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + + if (GSM_APP_GET_CLASS (app)->impl_is_disabled) { + return GSM_APP_GET_CLASS (app)->impl_is_disabled (app); + } else { + return FALSE; + } +} + +gboolean +gsm_app_peek_is_conditionally_disabled (GsmApp *app) +{ + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + + if (GSM_APP_GET_CLASS (app)->impl_is_conditionally_disabled) { + return GSM_APP_GET_CLASS (app)->impl_is_conditionally_disabled (app); + } else { + return FALSE; + } +} + +gboolean +gsm_app_is_running (GsmApp *app) +{ + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + + if (GSM_APP_GET_CLASS (app)->impl_is_running) { + return GSM_APP_GET_CLASS (app)->impl_is_running (app); + } else { + return FALSE; + } +} + +gboolean +gsm_app_peek_autorestart (GsmApp *app) +{ + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + + if (GSM_APP_GET_CLASS (app)->impl_get_autorestart) { + return GSM_APP_GET_CLASS (app)->impl_get_autorestart (app); + } else { + return FALSE; + } +} + +gboolean +gsm_app_provides (GsmApp *app, const char *service) +{ + + if (GSM_APP_GET_CLASS (app)->impl_provides) { + return GSM_APP_GET_CLASS (app)->impl_provides (app, service); + } else { + return FALSE; + } +} + +gboolean +gsm_app_has_autostart_condition (GsmApp *app, + const char *condition) +{ + + if (GSM_APP_GET_CLASS (app)->impl_has_autostart_condition) { + return GSM_APP_GET_CLASS (app)->impl_has_autostart_condition (app, condition); + } else { + return FALSE; + } +} + +gboolean +gsm_app_start (GsmApp *app, + GError **error) +{ + g_debug ("Starting app: %s", app->priv->id); + + return GSM_APP_GET_CLASS (app)->impl_start (app, error); +} + +gboolean +gsm_app_restart (GsmApp *app, + GError **error) +{ + g_debug ("Re-starting app: %s", app->priv->id); + + return GSM_APP_GET_CLASS (app)->impl_restart (app, error); +} + +gboolean +gsm_app_stop (GsmApp *app, + GError **error) +{ + return GSM_APP_GET_CLASS (app)->impl_stop (app, error); +} + +void +gsm_app_registered (GsmApp *app) +{ + g_return_if_fail (GSM_IS_APP (app)); + + g_signal_emit (app, signals[REGISTERED], 0); +} + +void +gsm_app_exited (GsmApp *app) +{ + g_return_if_fail (GSM_IS_APP (app)); + + g_signal_emit (app, signals[EXITED], 0); +} + +void +gsm_app_died (GsmApp *app) +{ + g_return_if_fail (GSM_IS_APP (app)); + + g_signal_emit (app, signals[DIED], 0); +} + +gboolean +gsm_app_get_app_id (GsmApp *app, + char **id, + GError **error) +{ + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + *id = g_strdup (GSM_APP_GET_CLASS (app)->impl_get_app_id (app)); + return TRUE; +} + +gboolean +gsm_app_get_startup_id (GsmApp *app, + char **id, + GError **error) +{ + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + *id = g_strdup (app->priv->startup_id); + return TRUE; +} + +gboolean +gsm_app_get_phase (GsmApp *app, + guint *phase, + GError **error) +{ + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + *phase = app->priv->phase; + return TRUE; +} diff --git a/mate-session/gsm-app.h b/mate-session/gsm-app.h new file mode 100644 index 0000000..eaef6c5 --- /dev/null +++ b/mate-session/gsm-app.h @@ -0,0 +1,136 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 __GSM_APP_H__ +#define __GSM_APP_H__ + +#include <glib-object.h> +#include <sys/types.h> + +#include "eggdesktopfile.h" + +#include "gsm-manager.h" +#include "gsm-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_APP (gsm_app_get_type ()) +#define GSM_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_APP, GsmApp)) +#define GSM_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_APP, GsmAppClass)) +#define GSM_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_APP)) +#define GSM_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_APP)) +#define GSM_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_APP, GsmAppClass)) + +typedef struct _GsmApp GsmApp; +typedef struct _GsmAppClass GsmAppClass; +typedef struct _GsmAppPrivate GsmAppPrivate; + +struct _GsmApp +{ + GObject parent; + GsmAppPrivate *priv; +}; + +struct _GsmAppClass +{ + GObjectClass parent_class; + + /* signals */ + void (*exited) (GsmApp *app); + void (*died) (GsmApp *app); + void (*registered) (GsmApp *app); + + /* virtual methods */ + gboolean (*impl_start) (GsmApp *app, + GError **error); + gboolean (*impl_restart) (GsmApp *app, + GError **error); + gboolean (*impl_stop) (GsmApp *app, + GError **error); + gboolean (*impl_provides) (GsmApp *app, + const char *service); + gboolean (*impl_has_autostart_condition) (GsmApp *app, + const char *service); + gboolean (*impl_is_running) (GsmApp *app); + + gboolean (*impl_get_autorestart) (GsmApp *app); + const char *(*impl_get_app_id) (GsmApp *app); + gboolean (*impl_is_disabled) (GsmApp *app); + gboolean (*impl_is_conditionally_disabled) (GsmApp *app); +}; + +typedef enum +{ + GSM_APP_ERROR_GENERAL = 0, + GSM_APP_ERROR_START, + GSM_APP_ERROR_STOP, + GSM_APP_NUM_ERRORS +} GsmAppError; + +#define GSM_APP_ERROR gsm_app_error_quark () + +GQuark gsm_app_error_quark (void); +GType gsm_app_get_type (void) G_GNUC_CONST; + +gboolean gsm_app_peek_autorestart (GsmApp *app); + +const char *gsm_app_peek_id (GsmApp *app); +const char *gsm_app_peek_app_id (GsmApp *app); +const char *gsm_app_peek_startup_id (GsmApp *app); +GsmManagerPhase gsm_app_peek_phase (GsmApp *app); +gboolean gsm_app_peek_is_disabled (GsmApp *app); +gboolean gsm_app_peek_is_conditionally_disabled (GsmApp *app); + +gboolean gsm_app_start (GsmApp *app, + GError **error); +gboolean gsm_app_restart (GsmApp *app, + GError **error); +gboolean gsm_app_stop (GsmApp *app, + GError **error); +gboolean gsm_app_is_running (GsmApp *app); + +void gsm_app_exited (GsmApp *app); +void gsm_app_died (GsmApp *app); + +gboolean gsm_app_provides (GsmApp *app, + const char *service); +gboolean gsm_app_has_autostart_condition (GsmApp *app, + const char *condition); +void gsm_app_registered (GsmApp *app); + +/* exported to bus */ +gboolean gsm_app_get_app_id (GsmApp *app, + char **id, + GError **error); +gboolean gsm_app_get_startup_id (GsmApp *app, + char **id, + GError **error); +gboolean gsm_app_get_phase (GsmApp *app, + guint *phase, + GError **error); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_APP_H__ */ diff --git a/mate-session/gsm-autostart-app.c b/mate-session/gsm-autostart-app.c new file mode 100644 index 0000000..a696a96 --- /dev/null +++ b/mate-session/gsm-autostart-app.c @@ -0,0 +1,1144 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 <ctype.h> +#include <string.h> +#include <sys/wait.h> +#include <errno.h> + +#include <glib.h> +#include <gio/gio.h> + +#include <mateconf/mateconf-client.h> + +#include "gsm-autostart-app.h" +#include "gsm-util.h" + +enum { + AUTOSTART_LAUNCH_SPAWN = 0, + AUTOSTART_LAUNCH_ACTIVATE +}; + +enum { + GSM_CONDITION_NONE = 0, + GSM_CONDITION_IF_EXISTS = 1, + GSM_CONDITION_UNLESS_EXISTS = 2, + GSM_CONDITION_MATE = 3, + GSM_CONDITION_UNKNOWN = 4 +}; + +#define GSM_SESSION_CLIENT_DBUS_INTERFACE "org.mate.SessionClient" + +struct _GsmAutostartAppPrivate { + char *desktop_filename; + char *desktop_id; + char *startup_id; + + EggDesktopFile *desktop_file; + + /* desktop file state */ + char *condition_string; + gboolean condition; + gboolean autorestart; + + GFileMonitor *condition_monitor; + guint condition_notify_id; + + int launch_type; + GPid pid; + guint child_watch_id; + + DBusGProxy *proxy; + DBusGProxyCall *proxy_call; +}; + +enum { + CONDITION_CHANGED, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_DESKTOP_FILENAME +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +#define GSM_AUTOSTART_APP_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), GSM_TYPE_AUTOSTART_APP, GsmAutostartAppPrivate)) + +G_DEFINE_TYPE (GsmAutostartApp, gsm_autostart_app, GSM_TYPE_APP) + +static void +gsm_autostart_app_init (GsmAutostartApp *app) +{ + app->priv = GSM_AUTOSTART_APP_GET_PRIVATE (app); + + app->priv->pid = -1; + app->priv->condition_monitor = NULL; + app->priv->condition = FALSE; +} + +static gboolean +is_disabled (GsmApp *app) +{ + GsmAutostartAppPrivate *priv; + + priv = GSM_AUTOSTART_APP (app)->priv; + + /* GSM_AUTOSTART_APP_ENABLED_KEY key, used by old mate-session */ + if (egg_desktop_file_has_key (priv->desktop_file, + GSM_AUTOSTART_APP_ENABLED_KEY, NULL) && + !egg_desktop_file_get_boolean (priv->desktop_file, + GSM_AUTOSTART_APP_ENABLED_KEY, NULL)) { + g_debug ("app %s is disabled by " GSM_AUTOSTART_APP_ENABLED_KEY, + gsm_app_peek_id (app)); + return TRUE; + } + + /* Hidden key, used by autostart spec */ + if (egg_desktop_file_get_boolean (priv->desktop_file, + EGG_DESKTOP_FILE_KEY_HIDDEN, NULL)) { + g_debug ("app %s is disabled by Hidden", + gsm_app_peek_id (app)); + return TRUE; + } + + /* Check OnlyShowIn/NotShowIn/TryExec */ + if (!egg_desktop_file_can_launch (priv->desktop_file, "MATE")) { + g_debug ("app %s not installed or not for MATE", + gsm_app_peek_id (app)); + return TRUE; + } + + /* Do not check AutostartCondition - this method is only to determine + if the app is unconditionally disabled */ + + return FALSE; +} + +static gboolean +parse_condition_string (const char *condition_string, + guint *condition_kindp, + char **keyp) +{ + const char *space; + const char *key; + int len; + guint kind; + + space = condition_string + strcspn (condition_string, " "); + len = space - condition_string; + key = space; + while (isspace ((unsigned char)*key)) { + key++; + } + + if (!g_ascii_strncasecmp (condition_string, "if-exists", len) && key) { + kind = GSM_CONDITION_IF_EXISTS; + } else if (!g_ascii_strncasecmp (condition_string, "unless-exists", len) && key) { + kind = GSM_CONDITION_UNLESS_EXISTS; + } else if (!g_ascii_strncasecmp (condition_string, "MATE", len)) { + kind = GSM_CONDITION_MATE; + } else { + key = NULL; + kind = GSM_CONDITION_UNKNOWN; + } + + if (keyp != NULL) { + *keyp = g_strdup (key); + } + + if (condition_kindp != NULL) { + *condition_kindp = kind; + } + + return (kind != GSM_CONDITION_UNKNOWN); +} + +static void +if_exists_condition_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + GsmApp *app) +{ + GsmAutostartAppPrivate *priv; + gboolean condition = FALSE; + + priv = GSM_AUTOSTART_APP (app)->priv; + + switch (event) { + case G_FILE_MONITOR_EVENT_CREATED: + condition = TRUE; + break; + case G_FILE_MONITOR_EVENT_DELETED: + condition = FALSE; + break; + default: + /* Ignore any other monitor event */ + return; + } + + /* Emit only if the condition actually changed */ + if (condition != priv->condition) { + priv->condition = condition; + g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition); + } +} + +static void +unless_exists_condition_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + GsmApp *app) +{ + GsmAutostartAppPrivate *priv; + gboolean condition = FALSE; + + priv = GSM_AUTOSTART_APP (app)->priv; + + switch (event) { + case G_FILE_MONITOR_EVENT_CREATED: + condition = FALSE; + break; + case G_FILE_MONITOR_EVENT_DELETED: + condition = TRUE; + break; + default: + /* Ignore any other monitor event */ + return; + } + + /* Emit only if the condition actually changed */ + if (condition != priv->condition) { + priv->condition = condition; + g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition); + } +} + +static void +mateconf_condition_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + GsmApp *app; + GsmAutostartAppPrivate *priv; + gboolean condition; + + g_return_if_fail (GSM_IS_APP (user_data)); + + app = GSM_APP (user_data); + + priv = GSM_AUTOSTART_APP (app)->priv; + + condition = FALSE; + if (entry->value != NULL && entry->value->type == MATECONF_VALUE_BOOL) { + condition = mateconf_value_get_bool (entry->value); + } + + g_debug ("GsmAutostartApp: app:%s condition changed condition:%d", + gsm_app_peek_id (app), + condition); + + /* Emit only if the condition actually changed */ + if (condition != priv->condition) { + priv->condition = condition; + g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition); + } +} + +static void +setup_condition_monitor (GsmAutostartApp *app) +{ + guint kind; + char *key; + gboolean res; + gboolean disabled; + + if (app->priv->condition_monitor != NULL) { + g_file_monitor_cancel (app->priv->condition_monitor); + } + + if (app->priv->condition_notify_id > 0) { + MateConfClient *client; + client = mateconf_client_get_default (); + mateconf_client_notify_remove (client, + app->priv->condition_notify_id); + app->priv->condition_notify_id = 0; + } + + if (app->priv->condition_string == NULL) { + return; + } + + /* if it is disabled outright there is no point in monitoring */ + if (is_disabled (GSM_APP (app))) { + return; + } + + key = NULL; + res = parse_condition_string (app->priv->condition_string, &kind, &key); + if (! res) { + g_free (key); + return; + } + + if (key == NULL) { + return; + } + + if (kind == GSM_CONDITION_IF_EXISTS) { + char *file_path; + GFile *file; + + file_path = g_build_filename (g_get_user_config_dir (), key, NULL); + + disabled = !g_file_test (file_path, G_FILE_TEST_EXISTS); + + file = g_file_new_for_path (file_path); + app->priv->condition_monitor = g_file_monitor_file (file, 0, NULL, NULL); + + g_signal_connect (app->priv->condition_monitor, "changed", + G_CALLBACK (if_exists_condition_cb), + app); + + g_object_unref (file); + g_free (file_path); + } else if (kind == GSM_CONDITION_UNLESS_EXISTS) { + char *file_path; + GFile *file; + + file_path = g_build_filename (g_get_user_config_dir (), key, NULL); + + disabled = g_file_test (file_path, G_FILE_TEST_EXISTS); + + file = g_file_new_for_path (file_path); + app->priv->condition_monitor = g_file_monitor_file (file, 0, NULL, NULL); + + g_signal_connect (app->priv->condition_monitor, "changed", + G_CALLBACK (unless_exists_condition_cb), + app); + + g_object_unref (file); + g_free (file_path); + } else if (kind == GSM_CONDITION_MATE) { + MateConfClient *client; + char *dir; + + client = mateconf_client_get_default (); + g_assert (MATECONF_IS_CLIENT (client)); + + disabled = !mateconf_client_get_bool (client, key, NULL); + + dir = g_path_get_dirname (key); + + mateconf_client_add_dir (client, + dir, + MATECONF_CLIENT_PRELOAD_NONE, NULL); + g_free (dir); + + app->priv->condition_notify_id = mateconf_client_notify_add (client, + key, + mateconf_condition_cb, + app, NULL, NULL); + g_object_unref (client); + } else { + disabled = TRUE; + } + + g_free (key); + + /* FIXME: cache the disabled value? */ +} + +static gboolean +load_desktop_file (GsmAutostartApp *app) +{ + char *dbus_name; + char *startup_id; + char *phase_str; + int phase; + gboolean res; + + if (app->priv->desktop_file == NULL) { + return FALSE; + } + + phase_str = egg_desktop_file_get_string (app->priv->desktop_file, + GSM_AUTOSTART_APP_PHASE_KEY, + NULL); + if (phase_str != NULL) { + if (strcmp (phase_str, "Initialization") == 0) { + phase = GSM_MANAGER_PHASE_INITIALIZATION; + } else if (strcmp (phase_str, "WindowManager") == 0) { + phase = GSM_MANAGER_PHASE_WINDOW_MANAGER; + } else if (strcmp (phase_str, "Panel") == 0) { + phase = GSM_MANAGER_PHASE_PANEL; + } else if (strcmp (phase_str, "Desktop") == 0) { + phase = GSM_MANAGER_PHASE_DESKTOP; + } else { + phase = GSM_MANAGER_PHASE_APPLICATION; + } + + g_free (phase_str); + } else { + phase = GSM_MANAGER_PHASE_APPLICATION; + } + + dbus_name = egg_desktop_file_get_string (app->priv->desktop_file, + GSM_AUTOSTART_APP_DBUS_NAME_KEY, + NULL); + if (dbus_name != NULL) { + app->priv->launch_type = AUTOSTART_LAUNCH_ACTIVATE; + } else { + app->priv->launch_type = AUTOSTART_LAUNCH_SPAWN; + } + + /* this must only be done on first load */ + switch (app->priv->launch_type) { + case AUTOSTART_LAUNCH_SPAWN: + startup_id = + egg_desktop_file_get_string (app->priv->desktop_file, + GSM_AUTOSTART_APP_STARTUP_ID_KEY, + NULL); + + if (startup_id == NULL) { + startup_id = gsm_util_generate_startup_id (); + } + break; + case AUTOSTART_LAUNCH_ACTIVATE: + startup_id = g_strdup (dbus_name); + break; + default: + g_assert_not_reached (); + } + + res = egg_desktop_file_has_key (app->priv->desktop_file, + GSM_AUTOSTART_APP_AUTORESTART_KEY, + NULL); + if (res) { + app->priv->autorestart = egg_desktop_file_get_boolean (app->priv->desktop_file, + GSM_AUTOSTART_APP_AUTORESTART_KEY, + NULL); + } else { + app->priv->autorestart = FALSE; + } + + g_free (app->priv->condition_string); + app->priv->condition_string = egg_desktop_file_get_string (app->priv->desktop_file, + "AutostartCondition", + NULL); + setup_condition_monitor (app); + + g_object_set (app, + "phase", phase, + "startup-id", startup_id, + NULL); + + g_free (startup_id); + g_free (dbus_name); + + return TRUE; +} + +static void +gsm_autostart_app_set_desktop_filename (GsmAutostartApp *app, + const char *desktop_filename) +{ + GError *error; + + if (app->priv->desktop_file != NULL) { + egg_desktop_file_free (app->priv->desktop_file); + app->priv->desktop_file = NULL; + g_free (app->priv->desktop_id); + } + + if (desktop_filename == NULL) { + return; + } + + app->priv->desktop_id = g_path_get_basename (desktop_filename); + + error = NULL; + app->priv->desktop_file = egg_desktop_file_new (desktop_filename, &error); + if (app->priv->desktop_file == NULL) { + g_warning ("Could not parse desktop file %s: %s", + desktop_filename, + error->message); + g_error_free (error); + return; + } +} + +static void +gsm_autostart_app_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmAutostartApp *self; + + self = GSM_AUTOSTART_APP (object); + + switch (prop_id) { + case PROP_DESKTOP_FILENAME: + gsm_autostart_app_set_desktop_filename (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_autostart_app_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmAutostartApp *self; + + self = GSM_AUTOSTART_APP (object); + + switch (prop_id) { + case PROP_DESKTOP_FILENAME: + if (self->priv->desktop_file != NULL) { + g_value_set_string (value, egg_desktop_file_get_source (self->priv->desktop_file)); + } else { + g_value_set_string (value, NULL); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_autostart_app_dispose (GObject *object) +{ + GsmAutostartAppPrivate *priv; + + priv = GSM_AUTOSTART_APP (object)->priv; + + if (priv->startup_id) { + g_free (priv->startup_id); + priv->startup_id = NULL; + } + + if (priv->condition_string) { + g_free (priv->condition_string); + priv->condition_string = NULL; + } + + if (priv->desktop_file) { + egg_desktop_file_free (priv->desktop_file); + priv->desktop_file = NULL; + } + + if (priv->desktop_id) { + g_free (priv->desktop_id); + priv->desktop_id = NULL; + } + + if (priv->child_watch_id > 0) { + g_source_remove (priv->child_watch_id); + priv->child_watch_id = 0; + } + + if (priv->proxy_call != NULL) { + dbus_g_proxy_cancel_call (priv->proxy, priv->proxy_call); + priv->proxy_call = NULL; + } + + if (priv->proxy != NULL) { + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + if (priv->condition_monitor) { + g_file_monitor_cancel (priv->condition_monitor); + } + + G_OBJECT_CLASS (gsm_autostart_app_parent_class)->dispose (object); +} + +static gboolean +is_running (GsmApp *app) +{ + GsmAutostartAppPrivate *priv; + gboolean is; + + priv = GSM_AUTOSTART_APP (app)->priv; + + /* is running if pid is still valid or + * or a client is connected + */ + /* FIXME: check client */ + is = (priv->pid != -1); + + return is; +} + +static gboolean +is_conditionally_disabled (GsmApp *app) +{ + GsmAutostartAppPrivate *priv; + gboolean res; + gboolean disabled; + char *key; + guint kind; + + priv = GSM_AUTOSTART_APP (app)->priv; + + /* Check AutostartCondition */ + if (priv->condition_string == NULL) { + return FALSE; + } + + key = NULL; + res = parse_condition_string (priv->condition_string, &kind, &key); + if (! res) { + g_free (key); + return TRUE; + } + + if (key == NULL) { + return TRUE; + } + + if (kind == GSM_CONDITION_IF_EXISTS) { + char *file_path; + + file_path = g_build_filename (g_get_user_config_dir (), key, NULL); + disabled = !g_file_test (file_path, G_FILE_TEST_EXISTS); + g_free (file_path); + } else if (kind == GSM_CONDITION_UNLESS_EXISTS) { + char *file_path; + + file_path = g_build_filename (g_get_user_config_dir (), key, NULL); + disabled = g_file_test (file_path, G_FILE_TEST_EXISTS); + g_free (file_path); + } else if (kind == GSM_CONDITION_MATE) { + MateConfClient *client; + client = mateconf_client_get_default (); + g_assert (MATECONF_IS_CLIENT (client)); + disabled = !mateconf_client_get_bool (client, key, NULL); + g_object_unref (client); + } else { + disabled = TRUE; + } + + /* Set initial condition */ + priv->condition = !disabled; + + g_free (key); + + return disabled; +} + +static void +app_exited (GPid pid, + int status, + GsmAutostartApp *app) +{ + g_debug ("GsmAutostartApp: (pid:%d) done (%s:%d)", + (int) pid, + WIFEXITED (status) ? "status" + : WIFSIGNALED (status) ? "signal" + : "unknown", + WIFEXITED (status) ? WEXITSTATUS (status) + : WIFSIGNALED (status) ? WTERMSIG (status) + : -1); + + g_spawn_close_pid (app->priv->pid); + app->priv->pid = -1; + app->priv->child_watch_id = 0; + + if (WIFEXITED (status)) { + gsm_app_exited (GSM_APP (app)); + } else if (WIFSIGNALED (status)) { + gsm_app_died (GSM_APP (app)); + } +} + +static int +_signal_pid (int pid, + int signal) +{ + int status = -1; + + /* perhaps block sigchld */ + g_debug ("GsmAutostartApp: sending signal %d to process %d", signal, pid); + errno = 0; + status = kill (pid, signal); + + if (status < 0) { + if (errno == ESRCH) { + g_warning ("Child process %d was already dead.", + (int)pid); + } else { + g_warning ("Couldn't kill child process %d: %s", + pid, + g_strerror (errno)); + } + } + + /* perhaps unblock sigchld */ + + return status; +} + +static gboolean +autostart_app_stop_spawn (GsmAutostartApp *app, + GError **error) +{ + int res; + + if (app->priv->pid < 1) { + g_set_error (error, + GSM_APP_ERROR, + GSM_APP_ERROR_STOP, + "Not running"); + return FALSE; + } + + res = _signal_pid (app->priv->pid, SIGTERM); + if (res != 0) { + g_set_error (error, + GSM_APP_ERROR, + GSM_APP_ERROR_STOP, + "Unable to stop: %s", + g_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +static gboolean +autostart_app_stop_activate (GsmAutostartApp *app, + GError **error) +{ + return TRUE; +} + +static gboolean +gsm_autostart_app_stop (GsmApp *app, + GError **error) +{ + GsmAutostartApp *aapp; + gboolean ret; + + aapp = GSM_AUTOSTART_APP (app); + + g_return_val_if_fail (aapp->priv->desktop_file != NULL, FALSE); + + switch (aapp->priv->launch_type) { + case AUTOSTART_LAUNCH_SPAWN: + ret = autostart_app_stop_spawn (aapp, error); + break; + case AUTOSTART_LAUNCH_ACTIVATE: + ret = autostart_app_stop_activate (aapp, error); + break; + default: + g_assert_not_reached (); + break; + } + + return ret; +} + +static gboolean +autostart_app_start_spawn (GsmAutostartApp *app, + GError **error) +{ + char *env[2] = { NULL, NULL }; + gboolean success; + GError *local_error; + const char *startup_id; + char *command; + + startup_id = gsm_app_peek_startup_id (GSM_APP (app)); + g_assert (startup_id != NULL); + + env[0] = g_strdup_printf ("DESKTOP_AUTOSTART_ID=%s", startup_id); + + local_error = NULL; + command = egg_desktop_file_parse_exec (app->priv->desktop_file, + NULL, + &local_error); + if (command == NULL) { + g_warning ("Unable to parse command from '%s': %s", + egg_desktop_file_get_source (app->priv->desktop_file), + local_error->message); + g_error_free (local_error); + } + + g_debug ("GsmAutostartApp: starting %s: command=%s startup-id=%s", app->priv->desktop_id, command, startup_id); + g_free (command); + + g_free (app->priv->startup_id); + local_error = NULL; + success = egg_desktop_file_launch (app->priv->desktop_file, + NULL, + &local_error, + EGG_DESKTOP_FILE_LAUNCH_PUTENV, env, + EGG_DESKTOP_FILE_LAUNCH_FLAGS, G_SPAWN_DO_NOT_REAP_CHILD, + EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, &app->priv->pid, + EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID, &app->priv->startup_id, + NULL); + g_free (env[0]); + + if (success) { + g_debug ("GsmAutostartApp: started pid:%d", app->priv->pid); + app->priv->child_watch_id = g_child_watch_add (app->priv->pid, + (GChildWatchFunc)app_exited, + app); + } else { + g_set_error (error, + GSM_APP_ERROR, + GSM_APP_ERROR_START, + "Unable to start application: %s", local_error->message); + g_error_free (local_error); + } + + return success; +} + +static void +start_notify (DBusGProxy *proxy, + DBusGProxyCall *call, + GsmAutostartApp *app) +{ + gboolean res; + GError *error; + + error = NULL; + res = dbus_g_proxy_end_call (proxy, + call, + &error, + G_TYPE_INVALID); + app->priv->proxy_call = NULL; + + if (! res) { + g_warning ("GsmAutostartApp: Error starting application: %s", error->message); + g_error_free (error); + } else { + g_debug ("GsmAutostartApp: Started application %s", app->priv->desktop_id); + } +} + +static gboolean +autostart_app_start_activate (GsmAutostartApp *app, + GError **error) +{ + const char *name; + char *path; + char *arguments; + DBusGConnection *bus; + GError *local_error; + + local_error = NULL; + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &local_error); + if (bus == NULL) { + if (local_error != NULL) { + g_warning ("error getting session bus: %s", local_error->message); + } + g_propagate_error (error, local_error); + return FALSE; + } + + name = gsm_app_peek_startup_id (GSM_APP (app)); + g_assert (name != NULL); + + path = egg_desktop_file_get_string (app->priv->desktop_file, + GSM_AUTOSTART_APP_DBUS_PATH_KEY, + NULL); + if (path == NULL) { + /* just pick one? */ + path = g_strdup ("/"); + } + + arguments = egg_desktop_file_get_string (app->priv->desktop_file, + GSM_AUTOSTART_APP_DBUS_ARGS_KEY, + NULL); + + app->priv->proxy = dbus_g_proxy_new_for_name (bus, + name, + path, + GSM_SESSION_CLIENT_DBUS_INTERFACE); + if (app->priv->proxy == NULL) { + g_set_error (error, + GSM_APP_ERROR, + GSM_APP_ERROR_START, + "Unable to start application: unable to create proxy for client"); + return FALSE; + } + + app->priv->proxy_call = dbus_g_proxy_begin_call (app->priv->proxy, + "Start", + (DBusGProxyCallNotify)start_notify, + app, + NULL, + G_TYPE_STRING, arguments, + G_TYPE_INVALID); + if (app->priv->proxy_call == NULL) { + g_object_unref (app->priv->proxy); + app->priv->proxy = NULL; + g_set_error (error, + GSM_APP_ERROR, + GSM_APP_ERROR_START, + "Unable to start application: unable to call Start on client"); + return FALSE; + } + + return TRUE; +} + +static gboolean +gsm_autostart_app_start (GsmApp *app, + GError **error) +{ + GsmAutostartApp *aapp; + gboolean ret; + + aapp = GSM_AUTOSTART_APP (app); + + g_return_val_if_fail (aapp->priv->desktop_file != NULL, FALSE); + + switch (aapp->priv->launch_type) { + case AUTOSTART_LAUNCH_SPAWN: + ret = autostart_app_start_spawn (aapp, error); + break; + case AUTOSTART_LAUNCH_ACTIVATE: + ret = autostart_app_start_activate (aapp, error); + break; + default: + g_assert_not_reached (); + break; + } + + return ret; +} + +static gboolean +gsm_autostart_app_restart (GsmApp *app, + GError **error) +{ + GError *local_error; + gboolean res; + + /* ignore stop errors - it is fine if it is already stopped */ + local_error = NULL; + res = gsm_app_stop (app, &local_error); + if (! res) { + g_debug ("GsmAutostartApp: Couldn't stop app: %s", local_error->message); + g_error_free (local_error); + } + + res = gsm_app_start (app, &local_error); + if (! res) { + g_propagate_error (error, local_error); + return FALSE; + } + + return TRUE; +} + +static gboolean +gsm_autostart_app_provides (GsmApp *app, + const char *service) +{ + char **provides; + gsize len; + gsize i; + GsmAutostartApp *aapp; + + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + + aapp = GSM_AUTOSTART_APP (app); + + if (aapp->priv->desktop_file == NULL) { + return FALSE; + } + + provides = egg_desktop_file_get_string_list (aapp->priv->desktop_file, + GSM_AUTOSTART_APP_PROVIDES_KEY, + &len, NULL); + if (!provides) { + return FALSE; + } + + for (i = 0; i < len; i++) { + if (!strcmp (provides[i], service)) { + g_strfreev (provides); + return TRUE; + } + } + + g_strfreev (provides); + return FALSE; +} + +static gboolean +gsm_autostart_app_has_autostart_condition (GsmApp *app, + const char *condition) +{ + GsmAutostartApp *aapp; + + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + g_return_val_if_fail (condition != NULL, FALSE); + + aapp = GSM_AUTOSTART_APP (app); + + if (aapp->priv->condition_string == NULL) { + return FALSE; + } + + if (strcmp (aapp->priv->condition_string, condition) == 0) { + return TRUE; + } + + return FALSE; +} + +static gboolean +gsm_autostart_app_get_autorestart (GsmApp *app) +{ + gboolean res; + gboolean autorestart; + + if (GSM_AUTOSTART_APP (app)->priv->desktop_file == NULL) { + return FALSE; + } + + autorestart = FALSE; + + res = egg_desktop_file_has_key (GSM_AUTOSTART_APP (app)->priv->desktop_file, + GSM_AUTOSTART_APP_AUTORESTART_KEY, + NULL); + if (res) { + autorestart = egg_desktop_file_get_boolean (GSM_AUTOSTART_APP (app)->priv->desktop_file, + GSM_AUTOSTART_APP_AUTORESTART_KEY, + NULL); + } + + return autorestart; +} + +static const char * +gsm_autostart_app_get_app_id (GsmApp *app) +{ + const char *location; + const char *slash; + + if (GSM_AUTOSTART_APP (app)->priv->desktop_file == NULL) { + return NULL; + } + + location = egg_desktop_file_get_source (GSM_AUTOSTART_APP (app)->priv->desktop_file); + + slash = strrchr (location, '/'); + if (slash != NULL) { + return slash + 1; + } else { + return location; + } +} + +static GObject * +gsm_autostart_app_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmAutostartApp *app; + + app = GSM_AUTOSTART_APP (G_OBJECT_CLASS (gsm_autostart_app_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + if (! load_desktop_file (app)) { + g_object_unref (app); + app = NULL; + } + + return G_OBJECT (app); +} + +static void +gsm_autostart_app_class_init (GsmAutostartAppClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GsmAppClass *app_class = GSM_APP_CLASS (klass); + + object_class->set_property = gsm_autostart_app_set_property; + object_class->get_property = gsm_autostart_app_get_property; + object_class->dispose = gsm_autostart_app_dispose; + object_class->constructor = gsm_autostart_app_constructor; + + app_class->impl_is_disabled = is_disabled; + app_class->impl_is_conditionally_disabled = is_conditionally_disabled; + app_class->impl_is_running = is_running; + app_class->impl_start = gsm_autostart_app_start; + app_class->impl_restart = gsm_autostart_app_restart; + app_class->impl_stop = gsm_autostart_app_stop; + app_class->impl_provides = gsm_autostart_app_provides; + app_class->impl_has_autostart_condition = gsm_autostart_app_has_autostart_condition; + app_class->impl_get_app_id = gsm_autostart_app_get_app_id; + app_class->impl_get_autorestart = gsm_autostart_app_get_autorestart; + + g_object_class_install_property (object_class, + PROP_DESKTOP_FILENAME, + g_param_spec_string ("desktop-filename", + "Desktop filename", + "Freedesktop .desktop file", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + signals[CONDITION_CHANGED] = + g_signal_new ("condition-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmAutostartAppClass, condition_changed), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); + + g_type_class_add_private (object_class, sizeof (GsmAutostartAppPrivate)); +} + +GsmApp * +gsm_autostart_app_new (const char *desktop_file) +{ + GsmAutostartApp *app; + + app = g_object_new (GSM_TYPE_AUTOSTART_APP, + "desktop-filename", desktop_file, + NULL); + + return GSM_APP (app); +} diff --git a/mate-session/gsm-autostart-app.h b/mate-session/gsm-autostart-app.h new file mode 100644 index 0000000..866522d --- /dev/null +++ b/mate-session/gsm-autostart-app.h @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 __GSM_AUTOSTART_APP_H__ +#define __GSM_AUTOSTART_APP_H__ + +#include "gsm-app.h" + +#include <X11/SM/SMlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_AUTOSTART_APP (gsm_autostart_app_get_type ()) +#define GSM_AUTOSTART_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_AUTOSTART_APP, GsmAutostartApp)) +#define GSM_AUTOSTART_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_AUTOSTART_APP, GsmAutostartAppClass)) +#define GSM_IS_AUTOSTART_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_AUTOSTART_APP)) +#define GSM_IS_AUTOSTART_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_AUTOSTART_APP)) +#define GSM_AUTOSTART_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_AUTOSTART_APP, GsmAutostartAppClass)) + +typedef struct _GsmAutostartApp GsmAutostartApp; +typedef struct _GsmAutostartAppClass GsmAutostartAppClass; +typedef struct _GsmAutostartAppPrivate GsmAutostartAppPrivate; + +struct _GsmAutostartApp +{ + GsmApp parent; + + GsmAutostartAppPrivate *priv; +}; + +struct _GsmAutostartAppClass +{ + GsmAppClass parent_class; + + /* signals */ + void (*condition_changed) (GsmApp *app, + gboolean condition); +}; + +GType gsm_autostart_app_get_type (void) G_GNUC_CONST; + +GsmApp *gsm_autostart_app_new (const char *desktop_file); + +#define GSM_AUTOSTART_APP_ENABLED_KEY "X-MATE-Autostart-enabled" +#define GSM_AUTOSTART_APP_PHASE_KEY "X-MATE-Autostart-Phase" +#define GSM_AUTOSTART_APP_PROVIDES_KEY "X-MATE-Provides" +#define GSM_AUTOSTART_APP_STARTUP_ID_KEY "X-MATE-Autostart-startup-id" +#define GSM_AUTOSTART_APP_AUTORESTART_KEY "X-MATE-AutoRestart" +#define GSM_AUTOSTART_APP_DBUS_NAME_KEY "X-MATE-DBus-Name" +#define GSM_AUTOSTART_APP_DBUS_PATH_KEY "X-MATE-DBus-Path" +#define GSM_AUTOSTART_APP_DBUS_ARGS_KEY "X-MATE-DBus-Start-Arguments" +#define GSM_AUTOSTART_APP_DISCARD_KEY "X-MATE-Autostart-discard-exec" + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_AUTOSTART_APP_H__ */ diff --git a/mate-session/gsm-client.c b/mate-session/gsm-client.c new file mode 100644 index 0000000..9cb1d81 --- /dev/null +++ b/mate-session/gsm-client.c @@ -0,0 +1,531 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 <dbus/dbus-glib.h> + +#include "eggdesktopfile.h" + +#include "gsm-marshal.h" +#include "gsm-client.h" +#include "gsm-client-glue.h" + +static guint32 client_serial = 1; + +#define GSM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_CLIENT, GsmClientPrivate)) + +struct GsmClientPrivate +{ + char *id; + char *startup_id; + char *app_id; + guint status; + DBusGConnection *connection; +}; + +enum { + PROP_0, + PROP_ID, + PROP_STARTUP_ID, + PROP_APP_ID, + PROP_STATUS +}; + +enum { + DISCONNECTED, + END_SESSION_RESPONSE, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_ABSTRACT_TYPE (GsmClient, gsm_client, G_TYPE_OBJECT) + +GQuark +gsm_client_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gsm_client_error"); + } + + return ret; +} + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +gsm_client_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (GSM_CLIENT_ERROR_GENERAL, "GeneralError"), + ENUM_ENTRY (GSM_CLIENT_ERROR_NOT_REGISTERED, "NotRegistered"), + { 0, 0, 0 } + }; + + g_assert (GSM_CLIENT_NUM_ERRORS == G_N_ELEMENTS (values) - 1); + + etype = g_enum_register_static ("GsmClientError", values); + } + + return etype; +} + +static guint32 +get_next_client_serial (void) +{ + guint32 serial; + + serial = client_serial++; + + if ((gint32)client_serial < 0) { + client_serial = 1; + } + + return serial; +} + +static gboolean +register_client (GsmClient *client) +{ + GError *error; + + error = NULL; + client->priv->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (client->priv->connection == NULL) { + if (error != NULL) { + g_critical ("error getting session bus: %s", error->message); + g_error_free (error); + } + return FALSE; + } + + dbus_g_connection_register_g_object (client->priv->connection, client->priv->id, G_OBJECT (client)); + + return TRUE; +} + +static GObject * +gsm_client_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmClient *client; + gboolean res; + + client = GSM_CLIENT (G_OBJECT_CLASS (gsm_client_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + g_free (client->priv->id); + client->priv->id = g_strdup_printf ("/org/mate/SessionManager/Client%u", get_next_client_serial ()); + + res = register_client (client); + if (! res) { + g_warning ("Unable to register client with session bus"); + } + + return G_OBJECT (client); +} + +static void +gsm_client_init (GsmClient *client) +{ + client->priv = GSM_CLIENT_GET_PRIVATE (client); +} + +static void +gsm_client_finalize (GObject *object) +{ + GsmClient *client; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_CLIENT (object)); + + client = GSM_CLIENT (object); + + g_return_if_fail (client->priv != NULL); + + g_free (client->priv->id); + g_free (client->priv->startup_id); + g_free (client->priv->app_id); + + G_OBJECT_CLASS (gsm_client_parent_class)->finalize (object); +} + +void +gsm_client_set_status (GsmClient *client, + guint status) +{ + g_return_if_fail (GSM_IS_CLIENT (client)); + if (client->priv->status != status) { + client->priv->status = status; + g_object_notify (G_OBJECT (client), "status"); + } +} + +static void +gsm_client_set_startup_id (GsmClient *client, + const char *startup_id) +{ + g_return_if_fail (GSM_IS_CLIENT (client)); + + g_free (client->priv->startup_id); + + if (startup_id != NULL) { + client->priv->startup_id = g_strdup (startup_id); + } else { + client->priv->startup_id = g_strdup (""); + } + g_object_notify (G_OBJECT (client), "startup-id"); +} + +void +gsm_client_set_app_id (GsmClient *client, + const char *app_id) +{ + g_return_if_fail (GSM_IS_CLIENT (client)); + + g_free (client->priv->app_id); + + if (app_id != NULL) { + client->priv->app_id = g_strdup (app_id); + } else { + client->priv->app_id = g_strdup (""); + } + g_object_notify (G_OBJECT (client), "app-id"); +} + +static void +gsm_client_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmClient *self; + + self = GSM_CLIENT (object); + + switch (prop_id) { + case PROP_STARTUP_ID: + gsm_client_set_startup_id (self, g_value_get_string (value)); + break; + case PROP_APP_ID: + gsm_client_set_app_id (self, g_value_get_string (value)); + break; + case PROP_STATUS: + gsm_client_set_status (self, g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_client_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmClient *self; + + self = GSM_CLIENT (object); + + switch (prop_id) { + case PROP_STARTUP_ID: + g_value_set_string (value, self->priv->startup_id); + break; + case PROP_APP_ID: + g_value_set_string (value, self->priv->app_id); + break; + case PROP_STATUS: + g_value_set_uint (value, self->priv->status); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +default_stop (GsmClient *client, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + g_warning ("Stop not implemented"); + + return TRUE; +} + +static void +gsm_client_dispose (GObject *object) +{ + GsmClient *client; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_CLIENT (object)); + + client = GSM_CLIENT (object); + + g_debug ("GsmClient: disposing %s", client->priv->id); + + G_OBJECT_CLASS (gsm_client_parent_class)->dispose (object); +} + +static void +gsm_client_class_init (GsmClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gsm_client_get_property; + object_class->set_property = gsm_client_set_property; + object_class->constructor = gsm_client_constructor; + object_class->finalize = gsm_client_finalize; + object_class->dispose = gsm_client_dispose; + + klass->impl_stop = default_stop; + + signals[DISCONNECTED] = + g_signal_new ("disconnected", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmClientClass, disconnected), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals[END_SESSION_RESPONSE] = + g_signal_new ("end-session-response", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmClientClass, end_session_response), + NULL, NULL, + gsm_marshal_VOID__BOOLEAN_BOOLEAN_BOOLEAN_STRING, + G_TYPE_NONE, + 4, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING); + + g_object_class_install_property (object_class, + PROP_STARTUP_ID, + g_param_spec_string ("startup-id", + "startup-id", + "startup-id", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_APP_ID, + g_param_spec_string ("app-id", + "app-id", + "app-id", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_STATUS, + g_param_spec_uint ("status", + "status", + "status", + 0, + G_MAXINT, + GSM_CLIENT_UNREGISTERED, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GsmClientPrivate)); + + dbus_g_object_type_install_info (GSM_TYPE_CLIENT, &dbus_glib_gsm_client_object_info); +} + +const char * +gsm_client_peek_id (GsmClient *client) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), NULL); + + return client->priv->id; +} + +const char * +gsm_client_peek_app_id (GsmClient *client) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), NULL); + + return client->priv->app_id; +} + +const char * +gsm_client_peek_startup_id (GsmClient *client) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), NULL); + + return client->priv->startup_id; +} + +guint +gsm_client_peek_status (GsmClient *client) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), GSM_CLIENT_UNREGISTERED); + + return client->priv->status; +} + +guint +gsm_client_peek_restart_style_hint (GsmClient *client) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), GSM_CLIENT_RESTART_NEVER); + + return GSM_CLIENT_GET_CLASS (client)->impl_get_restart_style_hint (client); +} + +gboolean +gsm_client_get_startup_id (GsmClient *client, + char **id, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + *id = g_strdup (client->priv->startup_id); + + return TRUE; +} + +gboolean +gsm_client_get_app_id (GsmClient *client, + char **id, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + *id = g_strdup (client->priv->app_id); + + return TRUE; +} + +gboolean +gsm_client_get_restart_style_hint (GsmClient *client, + guint *hint, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + *hint = GSM_CLIENT_GET_CLASS (client)->impl_get_restart_style_hint (client); + + return TRUE; +} + +gboolean +gsm_client_get_status (GsmClient *client, + guint *status, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + *status = client->priv->status; + + return TRUE; +} + +gboolean +gsm_client_get_unix_process_id (GsmClient *client, + guint *pid, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + *pid = GSM_CLIENT_GET_CLASS (client)->impl_get_unix_process_id (client); + + return TRUE; +} + +char * +gsm_client_get_app_name (GsmClient *client) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), NULL); + + return GSM_CLIENT_GET_CLASS (client)->impl_get_app_name (client); +} + +gboolean +gsm_client_cancel_end_session (GsmClient *client, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_cancel_end_session (client, error); +} + + +gboolean +gsm_client_query_end_session (GsmClient *client, + guint flags, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_query_end_session (client, flags, error); +} + +gboolean +gsm_client_end_session (GsmClient *client, + guint flags, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_end_session (client, flags, error); +} + +gboolean +gsm_client_stop (GsmClient *client, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_stop (client, error); +} + +void +gsm_client_disconnected (GsmClient *client) +{ + g_signal_emit (client, signals[DISCONNECTED], 0); +} + +GKeyFile * +gsm_client_save (GsmClient *client, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_save (client, error); +} + +void +gsm_client_end_session_response (GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason) +{ + g_signal_emit (client, signals[END_SESSION_RESPONSE], 0, + is_ok, do_last, cancel, reason); +} diff --git a/mate-session/gsm-client.h b/mate-session/gsm-client.h new file mode 100644 index 0000000..8c096a6 --- /dev/null +++ b/mate-session/gsm-client.h @@ -0,0 +1,175 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 __GSM_CLIENT_H__ +#define __GSM_CLIENT_H__ + +#include <glib.h> +#include <glib-object.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_CLIENT (gsm_client_get_type ()) +#define GSM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_CLIENT, GsmClient)) +#define GSM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_CLIENT, GsmClientClass)) +#define GSM_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_CLIENT)) +#define GSM_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_CLIENT)) +#define GSM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_CLIENT, GsmClientClass)) + +typedef struct _GsmClient GsmClient; +typedef struct _GsmClientClass GsmClientClass; + +typedef struct GsmClientPrivate GsmClientPrivate; + +typedef enum { + GSM_CLIENT_UNREGISTERED = 0, + GSM_CLIENT_REGISTERED, + GSM_CLIENT_FINISHED, + GSM_CLIENT_FAILED +} GsmClientStatus; + +typedef enum { + GSM_CLIENT_RESTART_NEVER = 0, + GSM_CLIENT_RESTART_IF_RUNNING, + GSM_CLIENT_RESTART_ANYWAY, + GSM_CLIENT_RESTART_IMMEDIATELY +} GsmClientRestartStyle; + +typedef enum { + GSM_CLIENT_END_SESSION_FLAG_FORCEFUL = 1 << 0, + GSM_CLIENT_END_SESSION_FLAG_SAVE = 1 << 1, + GSM_CLIENT_END_SESSION_FLAG_LAST = 1 << 2 +} GsmClientEndSessionFlag; + +struct _GsmClient +{ + GObject parent; + GsmClientPrivate *priv; +}; + +struct _GsmClientClass +{ + GObjectClass parent_class; + + /* signals */ + void (*disconnected) (GsmClient *client); + void (*end_session_response) (GsmClient *client, + gboolean ok, + gboolean do_last, + gboolean cancel, + const char *reason); + + /* virtual methods */ + char * (*impl_get_app_name) (GsmClient *client); + GsmClientRestartStyle (*impl_get_restart_style_hint) (GsmClient *client); + guint (*impl_get_unix_process_id) (GsmClient *client); + gboolean (*impl_query_end_session) (GsmClient *client, + guint flags, + GError **error); + gboolean (*impl_end_session) (GsmClient *client, + guint flags, + GError **error); + gboolean (*impl_cancel_end_session) (GsmClient *client, + GError **error); + gboolean (*impl_stop) (GsmClient *client, + GError **error); + GKeyFile * (*impl_save) (GsmClient *client, + GError **error); +}; + +typedef enum +{ + GSM_CLIENT_ERROR_GENERAL = 0, + GSM_CLIENT_ERROR_NOT_REGISTERED, + GSM_CLIENT_NUM_ERRORS +} GsmClientError; + +#define GSM_CLIENT_ERROR gsm_client_error_quark () +#define GSM_CLIENT_TYPE_ERROR (gsm_client_error_get_type ()) + +GType gsm_client_error_get_type (void); +GQuark gsm_client_error_quark (void); + +GType gsm_client_get_type (void) G_GNUC_CONST; + +const char *gsm_client_peek_id (GsmClient *client); + + +const char * gsm_client_peek_startup_id (GsmClient *client); +const char * gsm_client_peek_app_id (GsmClient *client); +guint gsm_client_peek_restart_style_hint (GsmClient *client); +guint gsm_client_peek_status (GsmClient *client); + + +char *gsm_client_get_app_name (GsmClient *client); +void gsm_client_set_app_id (GsmClient *client, + const char *app_id); +void gsm_client_set_status (GsmClient *client, + guint status); + +gboolean gsm_client_end_session (GsmClient *client, + guint flags, + GError **error); +gboolean gsm_client_query_end_session (GsmClient *client, + guint flags, + GError **error); +gboolean gsm_client_cancel_end_session (GsmClient *client, + GError **error); + +void gsm_client_disconnected (GsmClient *client); + +GKeyFile *gsm_client_save (GsmClient *client, + GError **error); +/* exported to bus */ +gboolean gsm_client_stop (GsmClient *client, + GError **error); +gboolean gsm_client_get_startup_id (GsmClient *client, + char **startup_id, + GError **error); +gboolean gsm_client_get_app_id (GsmClient *client, + char **app_id, + GError **error); +gboolean gsm_client_get_restart_style_hint (GsmClient *client, + guint *hint, + GError **error); +gboolean gsm_client_get_status (GsmClient *client, + guint *status, + GError **error); +gboolean gsm_client_get_unix_process_id (GsmClient *client, + guint *pid, + GError **error); + +/* private */ + +void gsm_client_end_session_response (GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_CLIENT_H__ */ diff --git a/mate-session/gsm-consolekit.c b/mate-session/gsm-consolekit.c new file mode 100644 index 0000000..e8dd726 --- /dev/null +++ b/mate-session/gsm-consolekit.c @@ -0,0 +1,903 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Jon McCann <[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 <errno.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> +#include <glib-object.h> +#include <glib/gi18n.h> + +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include "gsm-marshal.h" +#include "gsm-consolekit.h" + +#define CK_NAME "org.freedesktop.ConsoleKit" +#define CK_PATH "/org/freedesktop/ConsoleKit" +#define CK_INTERFACE "org.freedesktop.ConsoleKit" + +#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager" +#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager" +#define CK_SEAT_INTERFACE "org.freedesktop.ConsoleKit.Seat" +#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session" + +#define GSM_CONSOLEKIT_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_CONSOLEKIT, GsmConsolekitPrivate)) + +struct _GsmConsolekitPrivate +{ + DBusGConnection *dbus_connection; + DBusGProxy *bus_proxy; + DBusGProxy *ck_proxy; + guint32 is_connected : 1; +}; + +enum { + PROP_0, + PROP_IS_CONNECTED +}; + +enum { + REQUEST_COMPLETED = 0, + PRIVILEGES_COMPLETED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void gsm_consolekit_class_init (GsmConsolekitClass *klass); +static void gsm_consolekit_init (GsmConsolekit *ck); +static void gsm_consolekit_finalize (GObject *object); + +static void gsm_consolekit_free_dbus (GsmConsolekit *manager); + +static DBusHandlerResult gsm_consolekit_dbus_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data); + +static void gsm_consolekit_on_name_owner_changed (DBusGProxy *bus_proxy, + const char *name, + const char *prev_owner, + const char *new_owner, + GsmConsolekit *manager); + +G_DEFINE_TYPE (GsmConsolekit, gsm_consolekit, G_TYPE_OBJECT); + +static void +gsm_consolekit_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmConsolekit *manager = GSM_CONSOLEKIT (object); + + switch (prop_id) { + case PROP_IS_CONNECTED: + g_value_set_boolean (value, + manager->priv->is_connected); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, + prop_id, + pspec); + } +} + +static void +gsm_consolekit_class_init (GsmConsolekitClass *manager_class) +{ + GObjectClass *object_class; + GParamSpec *param_spec; + + object_class = G_OBJECT_CLASS (manager_class); + + object_class->finalize = gsm_consolekit_finalize; + object_class->get_property = gsm_consolekit_get_property; + + param_spec = g_param_spec_boolean ("is-connected", + "Is connected", + "Whether the session is connected to ConsoleKit", + FALSE, + G_PARAM_READABLE); + + g_object_class_install_property (object_class, PROP_IS_CONNECTED, + param_spec); + + signals [REQUEST_COMPLETED] = + g_signal_new ("request-completed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmConsolekitClass, request_completed), + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, G_TYPE_POINTER); + + signals [PRIVILEGES_COMPLETED] = + g_signal_new ("privileges-completed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmConsolekitClass, privileges_completed), + NULL, + NULL, + gsm_marshal_VOID__BOOLEAN_BOOLEAN_POINTER, + G_TYPE_NONE, + 3, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_POINTER); + + g_type_class_add_private (manager_class, sizeof (GsmConsolekitPrivate)); +} + +static DBusHandlerResult +gsm_consolekit_dbus_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + GsmConsolekit *manager; + + manager = GSM_CONSOLEKIT (user_data); + + if (dbus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, "Disconnected") && + strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) { + gsm_consolekit_free_dbus (manager); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static gboolean +gsm_consolekit_ensure_ck_connection (GsmConsolekit *manager, + GError **error) +{ + GError *connection_error; + gboolean is_connected; + + connection_error = NULL; + + if (manager->priv->dbus_connection == NULL) { + DBusConnection *connection; + + manager->priv->dbus_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, + &connection_error); + + if (manager->priv->dbus_connection == NULL) { + g_propagate_error (error, connection_error); + is_connected = FALSE; + goto out; + } + + connection = dbus_g_connection_get_connection (manager->priv->dbus_connection); + dbus_connection_set_exit_on_disconnect (connection, FALSE); + dbus_connection_add_filter (connection, + gsm_consolekit_dbus_filter, + manager, NULL); + } + + if (manager->priv->bus_proxy == NULL) { + manager->priv->bus_proxy = + dbus_g_proxy_new_for_name_owner (manager->priv->dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + &connection_error); + + if (manager->priv->bus_proxy == NULL) { + g_propagate_error (error, connection_error); + is_connected = FALSE; + goto out; + } + + dbus_g_proxy_add_signal (manager->priv->bus_proxy, + "NameOwnerChanged", + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (manager->priv->bus_proxy, + "NameOwnerChanged", + G_CALLBACK (gsm_consolekit_on_name_owner_changed), + manager, NULL); + } + + if (manager->priv->ck_proxy == NULL) { + manager->priv->ck_proxy = + dbus_g_proxy_new_for_name_owner (manager->priv->dbus_connection, + "org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager", + &connection_error); + + if (manager->priv->ck_proxy == NULL) { + g_propagate_error (error, connection_error); + is_connected = FALSE; + goto out; + } + } + + is_connected = TRUE; + + out: + if (manager->priv->is_connected != is_connected) { + manager->priv->is_connected = is_connected; + g_object_notify (G_OBJECT (manager), "is-connected"); + } + + if (!is_connected) { + if (manager->priv->dbus_connection == NULL) { + if (manager->priv->bus_proxy != NULL) { + g_object_unref (manager->priv->bus_proxy); + manager->priv->bus_proxy = NULL; + } + + if (manager->priv->ck_proxy != NULL) { + g_object_unref (manager->priv->ck_proxy); + manager->priv->ck_proxy = NULL; + } + } else if (manager->priv->bus_proxy == NULL) { + if (manager->priv->ck_proxy != NULL) { + g_object_unref (manager->priv->ck_proxy); + manager->priv->ck_proxy = NULL; + } + } + } + + return is_connected; +} + +static void +gsm_consolekit_on_name_owner_changed (DBusGProxy *bus_proxy, + const char *name, + const char *prev_owner, + const char *new_owner, + GsmConsolekit *manager) +{ + if (name != NULL && strcmp (name, "org.freedesktop.ConsoleKit") != 0) { + return; + } + + if (manager->priv->ck_proxy != NULL) { + g_object_unref (manager->priv->ck_proxy); + manager->priv->ck_proxy = NULL; + } + + gsm_consolekit_ensure_ck_connection (manager, NULL); +} + +static void +gsm_consolekit_init (GsmConsolekit *manager) +{ + GError *error; + + manager->priv = GSM_CONSOLEKIT_GET_PRIVATE (manager); + + error = NULL; + + if (!gsm_consolekit_ensure_ck_connection (manager, &error)) { + g_warning ("Could not connect to ConsoleKit: %s", + error->message); + g_error_free (error); + } +} + +static void +gsm_consolekit_free_dbus (GsmConsolekit *manager) +{ + if (manager->priv->bus_proxy != NULL) { + g_object_unref (manager->priv->bus_proxy); + manager->priv->bus_proxy = NULL; + } + + if (manager->priv->ck_proxy != NULL) { + g_object_unref (manager->priv->ck_proxy); + manager->priv->ck_proxy = NULL; + } + + if (manager->priv->dbus_connection != NULL) { + DBusConnection *connection; + connection = dbus_g_connection_get_connection (manager->priv->dbus_connection); + dbus_connection_remove_filter (connection, + gsm_consolekit_dbus_filter, + manager); + + dbus_g_connection_unref (manager->priv->dbus_connection); + manager->priv->dbus_connection = NULL; + } +} + +static void +gsm_consolekit_finalize (GObject *object) +{ + GsmConsolekit *manager; + GObjectClass *parent_class; + + manager = GSM_CONSOLEKIT (object); + + parent_class = G_OBJECT_CLASS (gsm_consolekit_parent_class); + + gsm_consolekit_free_dbus (manager); + + if (parent_class->finalize != NULL) { + parent_class->finalize (object); + } +} + +GQuark +gsm_consolekit_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) { + error_quark = g_quark_from_static_string ("gsm-consolekit-error"); + } + + return error_quark; +} + +GsmConsolekit * +gsm_consolekit_new (void) +{ + GsmConsolekit *manager; + + manager = g_object_new (GSM_TYPE_CONSOLEKIT, NULL); + + return manager; +} + +static void +emit_restart_complete (GsmConsolekit *manager, + GError *error) +{ + GError *call_error; + + call_error = NULL; + + if (error != NULL) { + call_error = g_error_new_literal (GSM_CONSOLEKIT_ERROR, + GSM_CONSOLEKIT_ERROR_RESTARTING, + error->message); + } + + g_signal_emit (G_OBJECT (manager), + signals [REQUEST_COMPLETED], + 0, call_error); + + if (call_error != NULL) { + g_error_free (call_error); + } +} + +static void +emit_stop_complete (GsmConsolekit *manager, + GError *error) +{ + GError *call_error; + + call_error = NULL; + + if (error != NULL) { + call_error = g_error_new_literal (GSM_CONSOLEKIT_ERROR, + GSM_CONSOLEKIT_ERROR_STOPPING, + error->message); + } + + g_signal_emit (G_OBJECT (manager), + signals [REQUEST_COMPLETED], + 0, call_error); + + if (call_error != NULL) { + g_error_free (call_error); + } +} + +void +gsm_consolekit_attempt_restart (GsmConsolekit *manager) +{ + gboolean res; + GError *error; + + error = NULL; + + if (!gsm_consolekit_ensure_ck_connection (manager, &error)) { + g_warning ("Could not connect to ConsoleKit: %s", + error->message); + emit_restart_complete (manager, error); + g_error_free (error); + return; + } + + res = dbus_g_proxy_call_with_timeout (manager->priv->ck_proxy, + "Restart", + INT_MAX, + &error, + G_TYPE_INVALID, + G_TYPE_INVALID); + + if (!res) { + g_warning ("Unable to restart system: %s", error->message); + emit_restart_complete (manager, error); + g_error_free (error); + } else { + emit_restart_complete (manager, NULL); + } +} + +void +gsm_consolekit_attempt_stop (GsmConsolekit *manager) +{ + gboolean res; + GError *error; + + error = NULL; + + if (!gsm_consolekit_ensure_ck_connection (manager, &error)) { + g_warning ("Could not connect to ConsoleKit: %s", + error->message); + emit_stop_complete (manager, error); + g_error_free (error); + return; + } + + res = dbus_g_proxy_call_with_timeout (manager->priv->ck_proxy, + "Stop", + INT_MAX, + &error, + G_TYPE_INVALID, + G_TYPE_INVALID); + + if (!res) { + g_warning ("Unable to stop system: %s", error->message); + emit_stop_complete (manager, error); + g_error_free (error); + } else { + emit_stop_complete (manager, NULL); + } +} + +static gboolean +get_current_session_id (DBusConnection *connection, + char **session_id) +{ + DBusError local_error; + DBusMessage *message; + DBusMessage *reply; + gboolean ret; + DBusMessageIter iter; + const char *value; + + ret = FALSE; + reply = NULL; + + dbus_error_init (&local_error); + message = dbus_message_new_method_call (CK_NAME, + CK_MANAGER_PATH, + CK_MANAGER_INTERFACE, + "GetCurrentSession"); + if (message == NULL) { + goto out; + } + + dbus_error_init (&local_error); + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, + &local_error); + if (reply == NULL) { + if (dbus_error_is_set (&local_error)) { + g_warning ("Unable to determine session: %s", local_error.message); + dbus_error_free (&local_error); + goto out; + } + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_get_basic (&iter, &value); + if (session_id != NULL) { + *session_id = g_strdup (value); + } + + ret = TRUE; + out: + if (message != NULL) { + dbus_message_unref (message); + } + if (reply != NULL) { + dbus_message_unref (reply); + } + + return ret; +} + +static gboolean +get_seat_id_for_session (DBusConnection *connection, + const char *session_id, + char **seat_id) +{ + DBusError local_error; + DBusMessage *message; + DBusMessage *reply; + gboolean ret; + DBusMessageIter iter; + const char *value; + + ret = FALSE; + reply = NULL; + + dbus_error_init (&local_error); + message = dbus_message_new_method_call (CK_NAME, + session_id, + CK_SESSION_INTERFACE, + "GetSeatId"); + if (message == NULL) { + goto out; + } + + dbus_error_init (&local_error); + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, + &local_error); + if (reply == NULL) { + if (dbus_error_is_set (&local_error)) { + g_warning ("Unable to determine seat: %s", local_error.message); + dbus_error_free (&local_error); + goto out; + } + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_get_basic (&iter, &value); + if (seat_id != NULL) { + *seat_id = g_strdup (value); + } + + ret = TRUE; + out: + if (message != NULL) { + dbus_message_unref (message); + } + if (reply != NULL) { + dbus_message_unref (reply); + } + + return ret; +} + +static char * +get_current_seat_id (DBusConnection *connection) +{ + gboolean res; + char *session_id; + char *seat_id; + + session_id = NULL; + seat_id = NULL; + + res = get_current_session_id (connection, &session_id); + if (res) { + res = get_seat_id_for_session (connection, session_id, &seat_id); + } + g_free (session_id); + + return seat_id; +} + +void +gsm_consolekit_set_session_idle (GsmConsolekit *manager, + gboolean is_idle) +{ + gboolean res; + GError *error; + char *session_id; + DBusMessage *message; + DBusMessage *reply; + DBusError dbus_error; + DBusMessageIter iter; + + error = NULL; + + if (!gsm_consolekit_ensure_ck_connection (manager, &error)) { + g_warning ("Could not connect to ConsoleKit: %s", + error->message); + g_error_free (error); + return; + } + + session_id = NULL; + res = get_current_session_id (dbus_g_connection_get_connection (manager->priv->dbus_connection), + &session_id); + if (!res) { + goto out; + } + + + g_debug ("Updating ConsoleKit idle status: %d", is_idle); + message = dbus_message_new_method_call (CK_NAME, + session_id, + CK_SESSION_INTERFACE, + "SetIdleHint"); + if (message == NULL) { + g_debug ("Couldn't allocate the D-Bus message"); + return; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &is_idle); + + /* FIXME: use async? */ + dbus_error_init (&dbus_error); + reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (manager->priv->dbus_connection), + message, + -1, + &dbus_error); + dbus_message_unref (message); + + if (reply != NULL) { + dbus_message_unref (reply); + } + + if (dbus_error_is_set (&dbus_error)) { + g_debug ("%s raised:\n %s\n\n", dbus_error.name, dbus_error.message); + dbus_error_free (&dbus_error); + } + +out: + g_free (session_id); +} + +static gboolean +seat_can_activate_sessions (DBusConnection *connection, + const char *seat_id) +{ + DBusError local_error; + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + gboolean can_activate; + + can_activate = FALSE; + reply = NULL; + + dbus_error_init (&local_error); + message = dbus_message_new_method_call (CK_NAME, + seat_id, + CK_SEAT_INTERFACE, + "CanActivateSessions"); + if (message == NULL) { + goto out; + } + + dbus_error_init (&local_error); + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, + &local_error); + if (reply == NULL) { + if (dbus_error_is_set (&local_error)) { + g_warning ("Unable to activate session: %s", local_error.message); + dbus_error_free (&local_error); + goto out; + } + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_get_basic (&iter, &can_activate); + + out: + if (message != NULL) { + dbus_message_unref (message); + } + if (reply != NULL) { + dbus_message_unref (reply); + } + + return can_activate; +} + +gboolean +gsm_consolekit_can_switch_user (GsmConsolekit *manager) +{ + GError *error; + char *seat_id; + gboolean ret; + + error = NULL; + + if (!gsm_consolekit_ensure_ck_connection (manager, &error)) { + g_warning ("Could not connect to ConsoleKit: %s", + error->message); + g_error_free (error); + return FALSE; + } + + seat_id = get_current_seat_id (dbus_g_connection_get_connection (manager->priv->dbus_connection)); + if (seat_id == NULL || seat_id[0] == '\0') { + g_debug ("seat id is not set; can't switch sessions"); + return FALSE; + } + + ret = seat_can_activate_sessions (dbus_g_connection_get_connection (manager->priv->dbus_connection), + seat_id); + g_free (seat_id); + + return ret; +} + +gboolean +gsm_consolekit_get_restart_privileges (GsmConsolekit *manager) +{ + g_signal_emit (G_OBJECT (manager), + signals [PRIVILEGES_COMPLETED], + 0, TRUE, TRUE, NULL); + + return TRUE; +} + +gboolean +gsm_consolekit_get_stop_privileges (GsmConsolekit *manager) +{ + g_signal_emit (G_OBJECT (manager), + signals [PRIVILEGES_COMPLETED], + 0, TRUE, TRUE, NULL); + + return TRUE; +} + +gboolean +gsm_consolekit_can_restart (GsmConsolekit *manager) +{ + gboolean res; + gboolean can_restart; + GError *error; + + error = NULL; + + if (!gsm_consolekit_ensure_ck_connection (manager, &error)) { + g_warning ("Could not connect to ConsoleKit: %s", + error->message); + g_error_free (error); + return FALSE; + } + + res = dbus_g_proxy_call_with_timeout (manager->priv->ck_proxy, + "CanRestart", + INT_MAX, + &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &can_restart, + G_TYPE_INVALID); + + return can_restart; +} + +gboolean +gsm_consolekit_can_stop (GsmConsolekit *manager) +{ + gboolean res; + gboolean can_stop; + GError *error; + + error = NULL; + + if (!gsm_consolekit_ensure_ck_connection (manager, &error)) { + g_warning ("Could not connect to ConsoleKit: %s", + error->message); + g_error_free (error); + return FALSE; + } + + res = dbus_g_proxy_call_with_timeout (manager->priv->ck_proxy, + "CanStop", + INT_MAX, + &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &can_stop, + G_TYPE_INVALID); + + return can_stop; +} + +gchar * +gsm_consolekit_get_current_session_type (GsmConsolekit *manager) +{ + GError *gerror; + DBusConnection *connection; + DBusError error; + DBusMessage *message = NULL; + DBusMessage *reply = NULL; + gchar *session_id; + gchar *ret; + DBusMessageIter iter; + const char *value; + + session_id = NULL; + ret = NULL; + gerror = NULL; + + if (!gsm_consolekit_ensure_ck_connection (manager, &gerror)) { + g_warning ("Could not connect to ConsoleKit: %s", + gerror->message); + g_error_free (gerror); + goto out; + } + + connection = dbus_g_connection_get_connection (manager->priv->dbus_connection); + if (!get_current_session_id (connection, &session_id)) { + goto out; + } + + dbus_error_init (&error); + message = dbus_message_new_method_call (CK_NAME, + session_id, + CK_SESSION_INTERFACE, + "GetSessionType"); + if (message == NULL) { + goto out; + } + + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, + &error); + + if (reply == NULL) { + if (dbus_error_is_set (&error)) { + g_warning ("Unable to determine session type: %s", error.message); + dbus_error_free (&error); + } + goto out; + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_get_basic (&iter, &value); + ret = g_strdup (value); + +out: + if (message != NULL) { + dbus_message_unref (message); + } + if (reply != NULL) { + dbus_message_unref (reply); + } + g_free (session_id); + + return ret; +} + + +GsmConsolekit * +gsm_get_consolekit (void) +{ + static GsmConsolekit *manager = NULL; + + if (manager == NULL) { + manager = gsm_consolekit_new (); + } + + return g_object_ref (manager); +} diff --git a/mate-session/gsm-consolekit.h b/mate-session/gsm-consolekit.h new file mode 100644 index 0000000..203ec40 --- /dev/null +++ b/mate-session/gsm-consolekit.h @@ -0,0 +1,105 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Jon McCann <[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 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: + * Jon McCann <[email protected]> + */ + +#ifndef __GSM_CONSOLEKIT_H__ +#define __GSM_CONSOLEKIT_H__ + +#include <glib.h> +#include <glib-object.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_CONSOLEKIT (gsm_consolekit_get_type ()) +#define GSM_CONSOLEKIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_CONSOLEKIT, GsmConsolekit)) +#define GSM_CONSOLEKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_CONSOLEKIT, GsmConsolekitClass)) +#define GSM_IS_CONSOLEKIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_CONSOLEKIT)) +#define GSM_IS_CONSOLEKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_CONSOLEKIT)) +#define GSM_CONSOLEKIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GSM_TYPE_CONSOLEKIT, GsmConsolekitClass)) +#define GSM_CONSOLEKIT_ERROR (gsm_consolekit_error_quark ()) + +typedef struct _GsmConsolekit GsmConsolekit; +typedef struct _GsmConsolekitClass GsmConsolekitClass; +typedef struct _GsmConsolekitPrivate GsmConsolekitPrivate; +typedef enum _GsmConsolekitError GsmConsolekitError; + +struct _GsmConsolekit +{ + GObject parent; + + GsmConsolekitPrivate *priv; +}; + +struct _GsmConsolekitClass +{ + GObjectClass parent_class; + + void (* request_completed) (GsmConsolekit *manager, + GError *error); + + void (* privileges_completed) (GsmConsolekit *manager, + gboolean success, + gboolean ask_later, + GError *error); +}; + +enum _GsmConsolekitError { + GSM_CONSOLEKIT_ERROR_RESTARTING = 0, + GSM_CONSOLEKIT_ERROR_STOPPING +}; + +#define GSM_CONSOLEKIT_SESSION_TYPE_LOGIN_WINDOW "LoginWindow" + +GType gsm_consolekit_get_type (void); + +GQuark gsm_consolekit_error_quark (void); + +GsmConsolekit *gsm_consolekit_new (void) G_GNUC_MALLOC; + +gboolean gsm_consolekit_can_switch_user (GsmConsolekit *manager); + +gboolean gsm_consolekit_get_restart_privileges (GsmConsolekit *manager); + +gboolean gsm_consolekit_get_stop_privileges (GsmConsolekit *manager); + +gboolean gsm_consolekit_can_stop (GsmConsolekit *manager); + +gboolean gsm_consolekit_can_restart (GsmConsolekit *manager); + +void gsm_consolekit_attempt_stop (GsmConsolekit *manager); + +void gsm_consolekit_attempt_restart (GsmConsolekit *manager); + +void gsm_consolekit_set_session_idle (GsmConsolekit *manager, + gboolean is_idle); + +gchar *gsm_consolekit_get_current_session_type (GsmConsolekit *manager); + +GsmConsolekit *gsm_get_consolekit (void); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_CONSOLEKIT_H__ */ diff --git a/mate-session/gsm-dbus-client.c b/mate-session/gsm-dbus-client.c new file mode 100644 index 0000000..65b393c --- /dev/null +++ b/mate-session/gsm-dbus-client.c @@ -0,0 +1,700 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include "gsm-dbus-client.h" +#include "gsm-marshal.h" + +#include "gsm-manager.h" + +#define GSM_DBUS_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_DBUS_CLIENT, GsmDBusClientPrivate)) + +#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0') + + +#define SM_DBUS_NAME "org.mate.SessionManager" +#define SM_DBUS_CLIENT_PRIVATE_INTERFACE "org.mate.SessionManager.ClientPrivate" + +struct GsmDBusClientPrivate +{ + char *bus_name; + GPid caller_pid; + GsmClientRestartStyle restart_style_hint; + DBusConnection *connection; +}; + +enum { + PROP_0, + PROP_BUS_NAME +}; + +G_DEFINE_TYPE (GsmDBusClient, gsm_dbus_client, GSM_TYPE_CLIENT) + +GQuark +gsm_dbus_client_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gsm_dbus_client_error"); + } + + return ret; +} + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +gsm_dbus_client_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (GSM_DBUS_CLIENT_ERROR_GENERAL, "GeneralError"), + ENUM_ENTRY (GSM_DBUS_CLIENT_ERROR_NOT_CLIENT, "NotClient"), + { 0, 0, 0 } + }; + + g_assert (GSM_DBUS_CLIENT_NUM_ERRORS == G_N_ELEMENTS (values) - 1); + + etype = g_enum_register_static ("GsmDbusClientError", values); + } + + return etype; +} + +static gboolean +setup_connection (GsmDBusClient *client) +{ + DBusError error; + + dbus_error_init (&error); + + if (client->priv->connection == NULL) { + client->priv->connection = dbus_bus_get (DBUS_BUS_SESSION, &error); + if (client->priv->connection == NULL) { + if (dbus_error_is_set (&error)) { + g_debug ("GsmDbusClient: Couldn't connect to session bus: %s", + error.message); + dbus_error_free (&error); + } + return FALSE; + } + + dbus_connection_setup_with_g_main (client->priv->connection, NULL); + dbus_connection_set_exit_on_disconnect (client->priv->connection, FALSE); + } + + return TRUE; +} + +static void +raise_error (DBusConnection *connection, + DBusMessage *in_reply_to, + const char *error_name, + char *format, ...) +{ + char buf[512]; + DBusMessage *reply; + + va_list args; + va_start (args, format); + vsnprintf (buf, sizeof (buf), format, args); + va_end (args); + + reply = dbus_message_new_error (in_reply_to, error_name, buf); + if (reply == NULL) { + g_error ("No memory"); + } + if (! dbus_connection_send (connection, reply, NULL)) { + g_error ("No memory"); + } + + dbus_message_unref (reply); +} + +static void +handle_end_session_response (GsmDBusClient *client, + DBusMessage *message) +{ + const char *sender; + DBusMessage *reply; + DBusError error; + dbus_bool_t is_ok; + const char *reason; + + dbus_error_init (&error); + if (! dbus_message_get_args (message, &error, + DBUS_TYPE_BOOLEAN, &is_ok, + DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID)) { + if (dbus_error_is_set (&error)) { + g_warning ("Invalid method call: %s", error.message); + dbus_error_free (&error); + } + raise_error (client->priv->connection, + message, + DBUS_ERROR_FAILED, + "There is a syntax error in the invocation of the method EndSessionResponse"); + return; + } + + g_debug ("GsmDBusClient: got EndSessionResponse is-ok:%d reason=%s", is_ok, reason); + + /* make sure it is from our client */ + sender = dbus_message_get_sender (message); + if (sender == NULL + || IS_STRING_EMPTY (client->priv->bus_name) + || strcmp (sender, client->priv->bus_name) != 0) { + + raise_error (client->priv->connection, + message, + DBUS_ERROR_FAILED, + "Caller not recognized as the client"); + return; + } + + reply = dbus_message_new_method_return (message); + if (reply == NULL) { + g_error ("No memory"); + } + + gsm_client_end_session_response (GSM_CLIENT (client), + is_ok, FALSE, FALSE, reason); + + + if (! dbus_connection_send (client->priv->connection, reply, NULL)) { + g_error ("No memory"); + } + + dbus_message_unref (reply); +} + +static DBusHandlerResult +client_dbus_filter_function (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + GsmDBusClient *client = GSM_DBUS_CLIENT (user_data); + const char *path; + + g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + + path = dbus_message_get_path (message); + + g_debug ("GsmDBusClient: obj_path=%s interface=%s method=%s", + dbus_message_get_path (message), + dbus_message_get_interface (message), + dbus_message_get_member (message)); + + if (dbus_message_is_method_call (message, SM_DBUS_CLIENT_PRIVATE_INTERFACE, "EndSessionResponse")) { + g_assert (gsm_client_peek_id (GSM_CLIENT (client)) != NULL); + + if (path != NULL && strcmp (path, gsm_client_peek_id (GSM_CLIENT (client))) != 0) { + /* Different object path */ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + handle_end_session_response (client, message); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static GObject * +gsm_dbus_client_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmDBusClient *client; + + client = GSM_DBUS_CLIENT (G_OBJECT_CLASS (gsm_dbus_client_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + if (! setup_connection (client)) { + g_object_unref (client); + return NULL; + } + + /* Object path is already registered by base class */ + dbus_connection_add_filter (client->priv->connection, client_dbus_filter_function, client, NULL); + + return G_OBJECT (client); +} + +static void +gsm_dbus_client_init (GsmDBusClient *client) +{ + client->priv = GSM_DBUS_CLIENT_GET_PRIVATE (client); +} + +/* adapted from PolicyKit */ +static gboolean +get_caller_info (GsmDBusClient *client, + const char *sender, + uid_t *calling_uid, + pid_t *calling_pid) +{ + gboolean res; + GError *error; + DBusGConnection *connection; + DBusGProxy *bus_proxy; + + res = FALSE; + bus_proxy = NULL; + + if (sender == NULL) { + goto out; + } + + error = NULL; + connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (connection == NULL) { + if (error != NULL) { + g_warning ("error getting session bus: %s", error->message); + g_error_free (error); + } + goto out; + } + + bus_proxy = dbus_g_proxy_new_for_name (connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + + error = NULL; + if (! dbus_g_proxy_call (bus_proxy, "GetConnectionUnixUser", &error, + G_TYPE_STRING, sender, + G_TYPE_INVALID, + G_TYPE_UINT, calling_uid, + G_TYPE_INVALID)) { + g_debug ("GetConnectionUnixUser() failed: %s", error->message); + g_error_free (error); + goto out; + } + + error = NULL; + if (! dbus_g_proxy_call (bus_proxy, "GetConnectionUnixProcessID", &error, + G_TYPE_STRING, sender, + G_TYPE_INVALID, + G_TYPE_UINT, calling_pid, + G_TYPE_INVALID)) { + g_debug ("GetConnectionUnixProcessID() failed: %s", error->message); + g_error_free (error); + goto out; + } + + res = TRUE; + + g_debug ("uid = %d", *calling_uid); + g_debug ("pid = %d", *calling_pid); + +out: + if (bus_proxy != NULL) { + g_object_unref (bus_proxy); + } + return res; +} + +static void +gsm_dbus_client_set_bus_name (GsmDBusClient *client, + const char *bus_name) +{ + uid_t uid; + pid_t pid; + + g_return_if_fail (GSM_IS_DBUS_CLIENT (client)); + + g_free (client->priv->bus_name); + + client->priv->bus_name = g_strdup (bus_name); + g_object_notify (G_OBJECT (client), "bus-name"); + + if (client->priv->bus_name != NULL) { + gboolean res; + + res = get_caller_info (client, bus_name, &uid, &pid); + if (! res) { + pid = 0; + } + } else { + pid = 0; + } + client->priv->caller_pid = pid; +} + +const char * +gsm_dbus_client_get_bus_name (GsmDBusClient *client) +{ + g_return_val_if_fail (GSM_IS_DBUS_CLIENT (client), NULL); + + return client->priv->bus_name; +} + +static void +gsm_dbus_client_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmDBusClient *self; + + self = GSM_DBUS_CLIENT (object); + + switch (prop_id) { + case PROP_BUS_NAME: + gsm_dbus_client_set_bus_name (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_dbus_client_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmDBusClient *self; + + self = GSM_DBUS_CLIENT (object); + + switch (prop_id) { + case PROP_BUS_NAME: + g_value_set_string (value, self->priv->bus_name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_dbus_client_finalize (GObject *object) +{ + GsmDBusClient *client = (GsmDBusClient *) object; + + g_free (client->priv->bus_name); + + G_OBJECT_CLASS (gsm_dbus_client_parent_class)->finalize (object); +} + +static GKeyFile * +dbus_client_save (GsmClient *client, + GError **error) +{ + g_debug ("GsmDBusClient: saving client with id %s", + gsm_client_peek_id (client)); + + /* FIXME: We still don't support client saving for D-Bus + * session clients */ + + return NULL; +} + +static gboolean +dbus_client_stop (GsmClient *client, + GError **error) +{ + GsmDBusClient *dbus_client = (GsmDBusClient *) client; + DBusMessage *message; + gboolean ret; + + ret = FALSE; + + /* unicast the signal to only the registered bus name */ + message = dbus_message_new_signal (gsm_client_peek_id (client), + SM_DBUS_CLIENT_PRIVATE_INTERFACE, + "Stop"); + if (message == NULL) { + goto out; + } + if (!dbus_message_set_destination (message, dbus_client->priv->bus_name)) { + goto out; + } + + if (!dbus_connection_send (dbus_client->priv->connection, message, NULL)) { + goto out; + } + + ret = TRUE; + + out: + if (message != NULL) { + dbus_message_unref (message); + } + + return ret; +} + +static char * +dbus_client_get_app_name (GsmClient *client) +{ + /* Always use app-id instead */ + return NULL; +} + +static GsmClientRestartStyle +dbus_client_get_restart_style_hint (GsmClient *client) +{ + return (GSM_DBUS_CLIENT (client)->priv->restart_style_hint); +} + +static guint +dbus_client_get_unix_process_id (GsmClient *client) +{ + return (GSM_DBUS_CLIENT (client)->priv->caller_pid); +} + +static gboolean +dbus_client_query_end_session (GsmClient *client, + guint flags, + GError **error) +{ + GsmDBusClient *dbus_client = (GsmDBusClient *) client; + DBusMessage *message; + DBusMessageIter iter; + gboolean ret; + + ret = FALSE; + + if (dbus_client->priv->bus_name == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Client is not registered"); + return FALSE; + } + + g_debug ("GsmDBusClient: sending QueryEndSession signal to %s", dbus_client->priv->bus_name); + + /* unicast the signal to only the registered bus name */ + message = dbus_message_new_signal (gsm_client_peek_id (client), + SM_DBUS_CLIENT_PRIVATE_INTERFACE, + "QueryEndSession"); + if (message == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Unable to send QueryEndSession message"); + goto out; + } + if (!dbus_message_set_destination (message, dbus_client->priv->bus_name)) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Unable to send QueryEndSession message"); + goto out; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &flags); + + if (!dbus_connection_send (dbus_client->priv->connection, message, NULL)) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Unable to send QueryEndSession message"); + goto out; + } + + ret = TRUE; + + out: + if (message != NULL) { + dbus_message_unref (message); + } + + return ret; +} + +static gboolean +dbus_client_end_session (GsmClient *client, + guint flags, + GError **error) +{ + GsmDBusClient *dbus_client = (GsmDBusClient *) client; + DBusMessage *message; + DBusMessageIter iter; + gboolean ret; + + ret = FALSE; + + /* unicast the signal to only the registered bus name */ + message = dbus_message_new_signal (gsm_client_peek_id (client), + SM_DBUS_CLIENT_PRIVATE_INTERFACE, + "EndSession"); + if (message == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Unable to send EndSession message"); + goto out; + } + if (!dbus_message_set_destination (message, dbus_client->priv->bus_name)) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Unable to send EndSession message"); + goto out; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &flags); + + if (!dbus_connection_send (dbus_client->priv->connection, message, NULL)) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Unable to send EndSession message"); + goto out; + } + + ret = TRUE; + + out: + if (message != NULL) { + dbus_message_unref (message); + } + return ret; +} + +static gboolean +dbus_client_cancel_end_session (GsmClient *client, + GError **error) +{ + GsmDBusClient *dbus_client = (GsmDBusClient *) client; + DBusMessage *message; + gboolean ret; + + /* unicast the signal to only the registered bus name */ + message = dbus_message_new_signal (gsm_client_peek_id (client), + SM_DBUS_CLIENT_PRIVATE_INTERFACE, + "CancelEndSession"); + if (message == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Unable to send CancelEndSession message"); + goto out; + } + if (!dbus_message_set_destination (message, dbus_client->priv->bus_name)) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Unable to send CancelEndSession message"); + goto out; + } + + if (!dbus_connection_send (dbus_client->priv->connection, message, NULL)) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Unable to send CancelEndSession message"); + goto out; + } + + ret = TRUE; + + out: + if (message != NULL) { + dbus_message_unref (message); + } + + return ret; +} + +static void +gsm_dbus_client_dispose (GObject *object) +{ + GsmDBusClient *client; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_DBUS_CLIENT (object)); + + client = GSM_DBUS_CLIENT (object); + + dbus_connection_remove_filter (client->priv->connection, client_dbus_filter_function, client); + + G_OBJECT_CLASS (gsm_dbus_client_parent_class)->dispose (object); +} + +static void +gsm_dbus_client_class_init (GsmDBusClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GsmClientClass *client_class = GSM_CLIENT_CLASS (klass); + + object_class->finalize = gsm_dbus_client_finalize; + object_class->constructor = gsm_dbus_client_constructor; + object_class->get_property = gsm_dbus_client_get_property; + object_class->set_property = gsm_dbus_client_set_property; + object_class->dispose = gsm_dbus_client_dispose; + + client_class->impl_save = dbus_client_save; + client_class->impl_stop = dbus_client_stop; + client_class->impl_query_end_session = dbus_client_query_end_session; + client_class->impl_end_session = dbus_client_end_session; + client_class->impl_cancel_end_session = dbus_client_cancel_end_session; + client_class->impl_get_app_name = dbus_client_get_app_name; + client_class->impl_get_restart_style_hint = dbus_client_get_restart_style_hint; + client_class->impl_get_unix_process_id = dbus_client_get_unix_process_id; + + g_object_class_install_property (object_class, + PROP_BUS_NAME, + g_param_spec_string ("bus-name", + "bus-name", + "bus-name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GsmDBusClientPrivate)); +} + +GsmClient * +gsm_dbus_client_new (const char *startup_id, + const char *bus_name) +{ + GsmDBusClient *client; + + client = g_object_new (GSM_TYPE_DBUS_CLIENT, + "startup-id", startup_id, + "bus-name", bus_name, + NULL); + + return GSM_CLIENT (client); +} diff --git a/mate-session/gsm-dbus-client.h b/mate-session/gsm-dbus-client.h new file mode 100644 index 0000000..4b1895d --- /dev/null +++ b/mate-session/gsm-dbus-client.h @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 __GSM_DBUS_CLIENT_H__ +#define __GSM_DBUS_CLIENT_H__ + +#include "gsm-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_DBUS_CLIENT (gsm_dbus_client_get_type ()) +#define GSM_DBUS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_DBUS_CLIENT, GsmDBusClient)) +#define GSM_DBUS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_DBUS_CLIENT, GsmDBusClientClass)) +#define GSM_IS_DBUS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_DBUS_CLIENT)) +#define GSM_IS_DBUS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_DBUS_CLIENT)) +#define GSM_DBUS_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_DBUS_CLIENT, GsmDBusClientClass)) + +typedef struct _GsmDBusClient GsmDBusClient; +typedef struct _GsmDBusClientClass GsmDBusClientClass; + +typedef struct GsmDBusClientPrivate GsmDBusClientPrivate; + +struct _GsmDBusClient +{ + GsmClient parent; + GsmDBusClientPrivate *priv; +}; + +struct _GsmDBusClientClass +{ + GsmClientClass parent_class; +}; + +typedef enum +{ + GSM_DBUS_CLIENT_ERROR_GENERAL = 0, + GSM_DBUS_CLIENT_ERROR_NOT_CLIENT, + GSM_DBUS_CLIENT_NUM_ERRORS +} GsmDBusClientError; + +#define GSM_DBUS_CLIENT_ERROR gsm_dbus_client_error_quark () + +GType gsm_dbus_client_error_get_type (void); +#define GSM_DBUS_CLIENT_TYPE_ERROR (gsm_dbus_client_error_get_type ()) + +GQuark gsm_dbus_client_error_quark (void); + +GType gsm_dbus_client_get_type (void) G_GNUC_CONST; + +GsmClient * gsm_dbus_client_new (const char *startup_id, + const char *bus_name); +const char * gsm_dbus_client_get_bus_name (GsmDBusClient *client); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_DBUS_CLIENT_H__ */ diff --git a/mate-session/gsm-inhibit-dialog.c b/mate-session/gsm-inhibit-dialog.c new file mode 100644 index 0000000..d5b4d57 --- /dev/null +++ b/mate-session/gsm-inhibit-dialog.c @@ -0,0 +1,1164 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#include <gtk/gtk.h> +#include <gdk/gdkx.h> + +#include <mateconf/mateconf-client.h> + +#include "gsm-inhibit-dialog.h" +#include "gsm-store.h" +#include "gsm-client.h" +#include "gsm-inhibitor.h" +#include "eggdesktopfile.h" +#include "gsm-util.h" + +#ifdef HAVE_XRENDER +#include <X11/extensions/Xrender.h> +#endif + +#define GSM_INHIBIT_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_INHIBIT_DIALOG, GsmInhibitDialogPrivate)) + +#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0') + +#define GTKBUILDER_FILE "gsm-inhibit-dialog.ui" + +#ifndef DEFAULT_ICON_SIZE +#define DEFAULT_ICON_SIZE 32 +#endif + +#ifndef DEFAULT_SNAPSHOT_SIZE +#define DEFAULT_SNAPSHOT_SIZE 128 +#endif + +#define DIALOG_RESPONSE_LOCK_SCREEN 1 + +struct GsmInhibitDialogPrivate +{ + GtkBuilder *xml; + int action; + gboolean is_done; + GsmStore *inhibitors; + GsmStore *clients; + GtkListStore *list_store; + gboolean have_xrender; + int xrender_event_base; + int xrender_error_base; +}; + +enum { + PROP_0, + PROP_ACTION, + PROP_INHIBITOR_STORE, + PROP_CLIENT_STORE +}; + +enum { + INHIBIT_IMAGE_COLUMN = 0, + INHIBIT_NAME_COLUMN, + INHIBIT_REASON_COLUMN, + INHIBIT_ID_COLUMN, + NUMBER_OF_COLUMNS +}; + +static void gsm_inhibit_dialog_class_init (GsmInhibitDialogClass *klass); +static void gsm_inhibit_dialog_init (GsmInhibitDialog *inhibit_dialog); +static void gsm_inhibit_dialog_finalize (GObject *object); + +G_DEFINE_TYPE (GsmInhibitDialog, gsm_inhibit_dialog, GTK_TYPE_DIALOG) + +static void +lock_screen (GsmInhibitDialog *dialog) +{ + GError *error; + error = NULL; + g_spawn_command_line_async ("mate-screensaver-command --lock", &error); + if (error != NULL) { + g_warning ("Couldn't lock screen: %s", error->message); + g_error_free (error); + } +} + +static void +on_response (GsmInhibitDialog *dialog, + gint response_id) + +{ + if (dialog->priv->is_done) { + g_signal_stop_emission_by_name (dialog, "response"); + return; + } + + switch (response_id) { + case DIALOG_RESPONSE_LOCK_SCREEN: + g_signal_stop_emission_by_name (dialog, "response"); + lock_screen (dialog); + break; + default: + dialog->priv->is_done = TRUE; + break; + } +} + +static void +gsm_inhibit_dialog_set_action (GsmInhibitDialog *dialog, + int action) +{ + dialog->priv->action = action; +} + +static gboolean +find_inhibitor (GsmInhibitDialog *dialog, + const char *id, + GtkTreeIter *iter) +{ + GtkTreeModel *model; + gboolean found_item; + + g_assert (GSM_IS_INHIBIT_DIALOG (dialog)); + + found_item = FALSE; + model = GTK_TREE_MODEL (dialog->priv->list_store); + + if (!gtk_tree_model_get_iter_first (model, iter)) { + return FALSE; + } + + do { + char *item_id; + + gtk_tree_model_get (model, + iter, + INHIBIT_ID_COLUMN, &item_id, + -1); + if (item_id != NULL + && id != NULL + && strcmp (item_id, id) == 0) { + found_item = TRUE; + } + g_free (item_id); + } while (!found_item && gtk_tree_model_iter_next (model, iter)); + + return found_item; +} + +/* copied from mate-panel panel-util.c */ +static char * +_util_icon_remove_extension (const char *icon) +{ + char *icon_no_extension; + char *p; + + icon_no_extension = g_strdup (icon); + p = strrchr (icon_no_extension, '.'); + if (p && + (strcmp (p, ".png") == 0 || + strcmp (p, ".xpm") == 0 || + strcmp (p, ".svg") == 0)) { + *p = 0; + } + + return icon_no_extension; +} + +/* copied from mate-panel panel-util.c */ +static char * +_find_icon (GtkIconTheme *icon_theme, + const char *icon_name, + gint size) +{ + GtkIconInfo *info; + char *retval; + char *icon_no_extension; + + if (icon_name == NULL || strcmp (icon_name, "") == 0) + return NULL; + + if (g_path_is_absolute (icon_name)) { + if (g_file_test (icon_name, G_FILE_TEST_EXISTS)) { + return g_strdup (icon_name); + } else { + char *basename; + + basename = g_path_get_basename (icon_name); + retval = _find_icon (icon_theme, basename, + size); + g_free (basename); + + return retval; + } + } + + /* This is needed because some .desktop files have an icon name *and* + * an extension as icon */ + icon_no_extension = _util_icon_remove_extension (icon_name); + + info = gtk_icon_theme_lookup_icon (icon_theme, icon_no_extension, + size, 0); + + g_free (icon_no_extension); + + if (info) { + retval = g_strdup (gtk_icon_info_get_filename (info)); + gtk_icon_info_free (info); + } else + retval = NULL; + + return retval; +} + +/* copied from mate-panel panel-util.c */ +static GdkPixbuf * +_load_icon (GtkIconTheme *icon_theme, + const char *icon_name, + int size, + int desired_width, + int desired_height, + char **error_msg) +{ + GdkPixbuf *retval; + char *file; + GError *error; + + g_return_val_if_fail (error_msg == NULL || *error_msg == NULL, NULL); + + file = _find_icon (icon_theme, icon_name, size); + if (!file) { + if (error_msg) + *error_msg = g_strdup_printf (_("Icon '%s' not found"), + icon_name); + + return NULL; + } + + error = NULL; + retval = gdk_pixbuf_new_from_file_at_size (file, + desired_width, + desired_height, + &error); + if (error) { + if (error_msg) + *error_msg = g_strdup (error->message); + g_error_free (error); + } + + g_free (file); + + return retval; +} + + +static GdkPixbuf * +scale_pixbuf (GdkPixbuf *pixbuf, + int max_width, + int max_height, + gboolean no_stretch_hint) +{ + int pw; + int ph; + float scale_factor_x = 1.0; + float scale_factor_y = 1.0; + float scale_factor = 1.0; + + pw = gdk_pixbuf_get_width (pixbuf); + ph = gdk_pixbuf_get_height (pixbuf); + + /* Determine which dimension requires the smallest scale. */ + scale_factor_x = (float) max_width / (float) pw; + scale_factor_y = (float) max_height / (float) ph; + + if (scale_factor_x > scale_factor_y) { + scale_factor = scale_factor_y; + } else { + scale_factor = scale_factor_x; + } + + /* always scale down, allow to disable scaling up */ + if (scale_factor < 1.0 || !no_stretch_hint) { + int scale_x = (int) (pw * scale_factor); + int scale_y = (int) (ph * scale_factor); + g_debug ("Scaling to %dx%d", scale_x, scale_y); + return gdk_pixbuf_scale_simple (pixbuf, + scale_x, + scale_y, + GDK_INTERP_BILINEAR); + } else { + return g_object_ref (pixbuf); + } +} + +#ifdef HAVE_XRENDER + +/* adapted from marco */ +static GdkColormap* +get_cmap (GdkPixmap *pixmap) +{ + GdkColormap *cmap; + + cmap = gdk_drawable_get_colormap (pixmap); + if (cmap) { + g_object_ref (G_OBJECT (cmap)); + } + + if (cmap == NULL) { + if (gdk_drawable_get_depth (pixmap) == 1) { + g_debug ("Using NULL colormap for snapshotting bitmap\n"); + cmap = NULL; + } else { + g_debug ("Using system cmap to snapshot pixmap\n"); + cmap = gdk_screen_get_system_colormap (gdk_drawable_get_screen (pixmap)); + + g_object_ref (G_OBJECT (cmap)); + } + } + + /* Be sure we aren't going to blow up due to visual mismatch */ + if (cmap && + (gdk_visual_get_depth (gdk_colormap_get_visual (cmap)) != + gdk_drawable_get_depth (pixmap))) { + cmap = NULL; + g_debug ("Switching back to NULL cmap because of depth mismatch\n"); + } + + return cmap; +} + +static GdkPixbuf * +pixbuf_get_from_pixmap (Pixmap xpixmap) +{ + GdkDrawable *drawable; + GdkPixbuf *retval; + GdkColormap *cmap; + int width; + int height; + + retval = NULL; + cmap = NULL; + + g_debug ("GsmInhibitDialog: getting foreign pixmap for %u", (guint)xpixmap); + drawable = gdk_pixmap_foreign_new (xpixmap); + if (GDK_IS_PIXMAP (drawable)) { + cmap = get_cmap (drawable); + + #if GTK_CHECK_VERSION(3, 0, 0) + width = gdk_window_get_width(drawable); + height = gdk_window_get_height(drawable); + #else + gdk_drawable_get_size(drawable, &width, &height); + #endif + + g_debug ("GsmInhibitDialog: getting pixbuf w=%d h=%d", width, height); + + retval = gdk_pixbuf_get_from_drawable (NULL, + drawable, + cmap, + 0, 0, + 0, 0, + width, height); + } + if (cmap) { + g_object_unref (G_OBJECT (cmap)); + } + if (drawable) { + g_object_unref (G_OBJECT (drawable)); + } + + return retval; +} + +static Pixmap +get_pixmap_for_window (Window window) +{ + XWindowAttributes attr; + XRenderPictureAttributes pa; + Pixmap pixmap; + XRenderPictFormat *format; + Picture src_picture; + Picture dst_picture; + gboolean has_alpha; + int x; + int y; + int width; + int height; + + XGetWindowAttributes (GDK_DISPLAY (), window, &attr); + + format = XRenderFindVisualFormat (GDK_DISPLAY (), attr.visual); + has_alpha = (format->type == PictTypeDirect && format->direct.alphaMask); + x = attr.x; + y = attr.y; + width = attr.width; + height = attr.height; + + pa.subwindow_mode = IncludeInferiors; /* Don't clip child widgets */ + + src_picture = XRenderCreatePicture (GDK_DISPLAY (), window, format, CPSubwindowMode, &pa); + + pixmap = XCreatePixmap (GDK_DISPLAY (), + window, + width, height, + attr.depth); + + dst_picture = XRenderCreatePicture (GDK_DISPLAY (), pixmap, format, 0, 0); + XRenderComposite (GDK_DISPLAY (), + has_alpha ? PictOpOver : PictOpSrc, + src_picture, + None, + dst_picture, + 0, 0, 0, 0, + 0, 0, + width, height); + + + return pixmap; +} + +#endif /* HAVE_COMPOSITE */ + +static GdkPixbuf * +get_pixbuf_for_window (guint xid, + int width, + int height) +{ + GdkPixbuf *pixbuf = NULL; +#ifdef HAVE_XRENDER + Window xwindow; + Pixmap xpixmap; + + xwindow = (Window) xid; + xpixmap = get_pixmap_for_window (xwindow); + if (xpixmap == None) { + g_debug ("GsmInhibitDialog: Unable to get window snapshot for %u", xid); + return NULL; + } else { + g_debug ("GsmInhibitDialog: Got xpixmap %u", (guint)xpixmap); + } + + pixbuf = pixbuf_get_from_pixmap (xpixmap); + + if (xpixmap != None) { + gdk_error_trap_push (); + XFreePixmap (GDK_DISPLAY (), xpixmap); + gdk_display_sync (gdk_display_get_default ()); + gdk_error_trap_pop (); + } + + if (pixbuf != NULL) { + GdkPixbuf *scaled; + g_debug ("GsmInhibitDialog: scaling pixbuf to w=%d h=%d", width, height); + scaled = scale_pixbuf (pixbuf, width, height, TRUE); + g_object_unref (pixbuf); + pixbuf = scaled; + } +#else + g_debug ("GsmInhibitDialog: no support for getting window snapshot"); +#endif + return pixbuf; +} + +static void +add_inhibitor (GsmInhibitDialog *dialog, + GsmInhibitor *inhibitor) +{ + const char *name; + const char *icon_name; + const char *app_id; + char *desktop_filename; + GdkPixbuf *pixbuf; + EggDesktopFile *desktop_file; + GError *error; + char **search_dirs; + guint xid; + char *freeme; + + /* FIXME: get info from xid */ + + desktop_file = NULL; + name = NULL; + pixbuf = NULL; + freeme = NULL; + + app_id = gsm_inhibitor_peek_app_id (inhibitor); + + if (IS_STRING_EMPTY (app_id)) { + desktop_filename = NULL; + } else if (! g_str_has_suffix (app_id, ".desktop")) { + desktop_filename = g_strdup_printf ("%s.desktop", app_id); + } else { + desktop_filename = g_strdup (app_id); + } + + xid = gsm_inhibitor_peek_toplevel_xid (inhibitor); + g_debug ("GsmInhibitDialog: inhibitor has XID %u", xid); + if (xid > 0 && dialog->priv->have_xrender) { + pixbuf = get_pixbuf_for_window (xid, DEFAULT_SNAPSHOT_SIZE, DEFAULT_SNAPSHOT_SIZE); + if (pixbuf == NULL) { + g_debug ("GsmInhibitDialog: unable to read pixbuf from %u", xid); + } + } + + if (desktop_filename != NULL) { + search_dirs = gsm_util_get_desktop_dirs (); + + if (g_path_is_absolute (desktop_filename)) { + char *basename; + + error = NULL; + desktop_file = egg_desktop_file_new (desktop_filename, + &error); + if (desktop_file == NULL) { + if (error) { + g_warning ("Unable to load desktop file '%s': %s", + desktop_filename, error->message); + g_error_free (error); + } else { + g_warning ("Unable to load desktop file '%s'", + desktop_filename); + } + + basename = g_path_get_basename (desktop_filename); + g_free (desktop_filename); + desktop_filename = basename; + } + } + + if (desktop_file == NULL) { + error = NULL; + desktop_file = egg_desktop_file_new_from_dirs (desktop_filename, + (const char **)search_dirs, + &error); + } + + /* look for a file with a vendor prefix */ + if (desktop_file == NULL) { + if (error) { + g_warning ("Unable to find desktop file '%s': %s", + desktop_filename, error->message); + g_error_free (error); + } else { + g_warning ("Unable to find desktop file '%s'", + desktop_filename); + } + g_free (desktop_filename); + desktop_filename = g_strdup_printf ("mate-%s.desktop", app_id); + error = NULL; + desktop_file = egg_desktop_file_new_from_dirs (desktop_filename, + (const char **)search_dirs, + &error); + } + g_strfreev (search_dirs); + + if (desktop_file == NULL) { + if (error) { + g_warning ("Unable to find desktop file '%s': %s", + desktop_filename, error->message); + g_error_free (error); + } else { + g_warning ("Unable to find desktop file '%s'", + desktop_filename); + } + } else { + name = egg_desktop_file_get_name (desktop_file); + icon_name = egg_desktop_file_get_icon (desktop_file); + + if (pixbuf == NULL) { + pixbuf = _load_icon (gtk_icon_theme_get_default (), + icon_name, + DEFAULT_ICON_SIZE, + DEFAULT_ICON_SIZE, + DEFAULT_ICON_SIZE, + NULL); + } + } + } + + /* try client info */ + if (name == NULL) { + const char *client_id; + client_id = gsm_inhibitor_peek_client_id (inhibitor); + if (! IS_STRING_EMPTY (client_id)) { + GsmClient *client; + client = GSM_CLIENT (gsm_store_lookup (dialog->priv->clients, client_id)); + if (client != NULL) { + freeme = gsm_client_get_app_name (client); + name = freeme; + } + } + } + + if (name == NULL) { + if (! IS_STRING_EMPTY (app_id)) { + name = app_id; + } else { + name = _("Unknown"); + } + } + + if (pixbuf == NULL) { + pixbuf = _load_icon (gtk_icon_theme_get_default (), + "mate-windows", + DEFAULT_ICON_SIZE, + DEFAULT_ICON_SIZE, + DEFAULT_ICON_SIZE, + NULL); + } + + gtk_list_store_insert_with_values (dialog->priv->list_store, + NULL, 0, + INHIBIT_IMAGE_COLUMN, pixbuf, + INHIBIT_NAME_COLUMN, name, + INHIBIT_REASON_COLUMN, gsm_inhibitor_peek_reason (inhibitor), + INHIBIT_ID_COLUMN, gsm_inhibitor_peek_id (inhibitor), + -1); + + g_free (desktop_filename); + g_free (freeme); + if (pixbuf != NULL) { + g_object_unref (pixbuf); + } + if (desktop_file != NULL) { + egg_desktop_file_free (desktop_file); + } +} + +static gboolean +model_has_one_entry (GtkTreeModel *model) +{ + guint n_rows; + + n_rows = gtk_tree_model_iter_n_children (model, NULL); + g_debug ("Model has %d rows", n_rows); + + return (n_rows > 0 && n_rows < 2); +} + +static void +update_dialog_text (GsmInhibitDialog *dialog) +{ + const char *description_text; + const char *header_text; + GtkWidget *widget; + + if (model_has_one_entry (GTK_TREE_MODEL (dialog->priv->list_store))) { + g_debug ("Found one entry in model"); + header_text = _("A program is still running:"); + description_text = _("Waiting for the program to finish. Interrupting the program may cause you to lose work."); + } else { + g_debug ("Found multiple entries (or none) in model"); + header_text = _("Some programs are still running:"); + description_text = _("Waiting for programs to finish. Interrupting these programs may cause you to lose work."); + } + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + "header-label")); + if (widget != NULL) { + char *markup; + markup = g_strdup_printf ("<b>%s</b>", header_text); + gtk_label_set_markup (GTK_LABEL (widget), markup); + g_free (markup); + } + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + "description-label")); + if (widget != NULL) { + gtk_label_set_text (GTK_LABEL (widget), description_text); + } +} + +static void +on_store_inhibitor_added (GsmStore *store, + const char *id, + GsmInhibitDialog *dialog) +{ + GsmInhibitor *inhibitor; + GtkTreeIter iter; + + g_debug ("GsmInhibitDialog: inhibitor added: %s", id); + + if (dialog->priv->is_done) { + return; + } + + inhibitor = (GsmInhibitor *)gsm_store_lookup (store, id); + + /* Add to model */ + if (! find_inhibitor (dialog, id, &iter)) { + add_inhibitor (dialog, inhibitor); + update_dialog_text (dialog); + } + +} + +static void +on_store_inhibitor_removed (GsmStore *store, + const char *id, + GsmInhibitDialog *dialog) +{ + GtkTreeIter iter; + + g_debug ("GsmInhibitDialog: inhibitor removed: %s", id); + + if (dialog->priv->is_done) { + return; + } + + /* Remove from model */ + if (find_inhibitor (dialog, id, &iter)) { + gtk_list_store_remove (dialog->priv->list_store, &iter); + update_dialog_text (dialog); + } + + /* if there are no inhibitors left then trigger response */ + if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dialog->priv->list_store), &iter)) { + gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); + } +} + +static void +gsm_inhibit_dialog_set_inhibitor_store (GsmInhibitDialog *dialog, + GsmStore *store) +{ + g_return_if_fail (GSM_IS_INHIBIT_DIALOG (dialog)); + + if (store != NULL) { + g_object_ref (store); + } + + if (dialog->priv->inhibitors != NULL) { + g_signal_handlers_disconnect_by_func (dialog->priv->inhibitors, + on_store_inhibitor_added, + dialog); + g_signal_handlers_disconnect_by_func (dialog->priv->inhibitors, + on_store_inhibitor_removed, + dialog); + + g_object_unref (dialog->priv->inhibitors); + } + + + g_debug ("GsmInhibitDialog: setting store %p", store); + + dialog->priv->inhibitors = store; + + if (dialog->priv->inhibitors != NULL) { + g_signal_connect (dialog->priv->inhibitors, + "added", + G_CALLBACK (on_store_inhibitor_added), + dialog); + g_signal_connect (dialog->priv->inhibitors, + "removed", + G_CALLBACK (on_store_inhibitor_removed), + dialog); + } +} + +static void +gsm_inhibit_dialog_set_client_store (GsmInhibitDialog *dialog, + GsmStore *store) +{ + g_return_if_fail (GSM_IS_INHIBIT_DIALOG (dialog)); + + if (store != NULL) { + g_object_ref (store); + } + + if (dialog->priv->clients != NULL) { + g_object_unref (dialog->priv->clients); + } + + dialog->priv->clients = store; +} + +static void +gsm_inhibit_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmInhibitDialog *dialog = GSM_INHIBIT_DIALOG (object); + + switch (prop_id) { + case PROP_ACTION: + gsm_inhibit_dialog_set_action (dialog, g_value_get_int (value)); + break; + case PROP_INHIBITOR_STORE: + gsm_inhibit_dialog_set_inhibitor_store (dialog, g_value_get_object (value)); + break; + case PROP_CLIENT_STORE: + gsm_inhibit_dialog_set_client_store (dialog, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_inhibit_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmInhibitDialog *dialog = GSM_INHIBIT_DIALOG (object); + + switch (prop_id) { + case PROP_ACTION: + g_value_set_int (value, dialog->priv->action); + break; + case PROP_INHIBITOR_STORE: + g_value_set_object (value, dialog->priv->inhibitors); + break; + case PROP_CLIENT_STORE: + g_value_set_object (value, dialog->priv->clients); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +name_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + GsmInhibitDialog *dialog) +{ + char *name; + char *reason; + char *markup; + + name = NULL; + reason = NULL; + gtk_tree_model_get (model, + iter, + INHIBIT_NAME_COLUMN, &name, + INHIBIT_REASON_COLUMN, &reason, + -1); + + markup = g_strdup_printf ("<b>%s</b>\n" + "<span size=\"small\">%s</span>", + name ? name : "(null)", + reason ? reason : "(null)"); + + g_free (name); + g_free (reason); + + g_object_set (cell, "markup", markup, NULL); + g_free (markup); +} + +static gboolean +add_to_model (const char *id, + GsmInhibitor *inhibitor, + GsmInhibitDialog *dialog) +{ + add_inhibitor (dialog, inhibitor); + return FALSE; +} + +static void +populate_model (GsmInhibitDialog *dialog) +{ + gsm_store_foreach_remove (dialog->priv->inhibitors, + (GsmStoreFunc)add_to_model, + dialog); + update_dialog_text (dialog); +} + +static void +setup_dialog (GsmInhibitDialog *dialog) +{ + const char *button_text; + GtkWidget *treeview; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + switch (dialog->priv->action) { + case GSM_LOGOUT_ACTION_SWITCH_USER: + button_text = _("Switch User Anyway"); + break; + case GSM_LOGOUT_ACTION_LOGOUT: + button_text = _("Log Out Anyway"); + break; + case GSM_LOGOUT_ACTION_SLEEP: + button_text = _("Suspend Anyway"); + break; + case GSM_LOGOUT_ACTION_HIBERNATE: + button_text = _("Hibernate Anyway"); + break; + case GSM_LOGOUT_ACTION_SHUTDOWN: + button_text = _("Shut Down Anyway"); + break; + case GSM_LOGOUT_ACTION_REBOOT: + button_text = _("Reboot Anyway"); + break; + default: + g_assert_not_reached (); + break; + } + + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("Lock Screen"), + DIALOG_RESPONSE_LOCK_SCREEN); + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("Cancel"), + GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (GTK_DIALOG (dialog), + button_text, + GTK_RESPONSE_ACCEPT); + g_signal_connect (dialog, + "response", + G_CALLBACK (on_response), + dialog); + + dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + + treeview = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + "inhibitors-treeview")); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); + gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), + GTK_TREE_MODEL (dialog->priv->list_store)); + + /* IMAGE COLUMN */ + renderer = gtk_cell_renderer_pixbuf_new (); + column = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + gtk_tree_view_column_set_attributes (column, + renderer, + "pixbuf", INHIBIT_IMAGE_COLUMN, + NULL); + + g_object_set (renderer, "xalign", 1.0, NULL); + + /* NAME COLUMN */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + gtk_tree_view_column_set_cell_data_func (column, + renderer, + (GtkTreeCellDataFunc) name_cell_data_func, + dialog, + NULL); + + gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), + INHIBIT_REASON_COLUMN); + + populate_model (dialog); +} + +static GObject * +gsm_inhibit_dialog_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmInhibitDialog *dialog; + + dialog = GSM_INHIBIT_DIALOG (G_OBJECT_CLASS (gsm_inhibit_dialog_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + +#ifdef HAVE_XRENDER + gdk_error_trap_push (); + if (XRenderQueryExtension (GDK_DISPLAY (), &dialog->priv->xrender_event_base, &dialog->priv->xrender_error_base)) { + g_debug ("GsmInhibitDialog: Initialized XRender extension"); + dialog->priv->have_xrender = TRUE; + } else { + g_debug ("GsmInhibitDialog: Unable to initialize XRender extension"); + dialog->priv->have_xrender = FALSE; + } + gdk_display_sync (gdk_display_get_default ()); + gdk_error_trap_pop (); +#endif /* HAVE_XRENDER */ + + /* FIXME: turn this on when it is ready */ + dialog->priv->have_xrender = FALSE; + + setup_dialog (dialog); + + gtk_widget_show_all (GTK_WIDGET (dialog)); + + return G_OBJECT (dialog); +} + +static void +gsm_inhibit_dialog_dispose (GObject *object) +{ + GsmInhibitDialog *dialog; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_INHIBIT_DIALOG (object)); + + dialog = GSM_INHIBIT_DIALOG (object); + + g_debug ("GsmInhibitDialog: dispose called"); + + if (dialog->priv->list_store != NULL) { + g_object_unref (dialog->priv->list_store); + dialog->priv->list_store = NULL; + } + + if (dialog->priv->inhibitors != NULL) { + g_signal_handlers_disconnect_by_func (dialog->priv->inhibitors, + on_store_inhibitor_added, + dialog); + g_signal_handlers_disconnect_by_func (dialog->priv->inhibitors, + on_store_inhibitor_removed, + dialog); + + g_object_unref (dialog->priv->inhibitors); + dialog->priv->inhibitors = NULL; + } + + if (dialog->priv->xml != NULL) { + g_object_unref (dialog->priv->xml); + dialog->priv->xml = NULL; + } + + G_OBJECT_CLASS (gsm_inhibit_dialog_parent_class)->dispose (object); +} + +static void +gsm_inhibit_dialog_class_init (GsmInhibitDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gsm_inhibit_dialog_get_property; + object_class->set_property = gsm_inhibit_dialog_set_property; + object_class->constructor = gsm_inhibit_dialog_constructor; + object_class->dispose = gsm_inhibit_dialog_dispose; + object_class->finalize = gsm_inhibit_dialog_finalize; + + g_object_class_install_property (object_class, + PROP_ACTION, + g_param_spec_int ("action", + "action", + "action", + -1, + G_MAXINT, + -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_INHIBITOR_STORE, + g_param_spec_object ("inhibitor-store", + NULL, + NULL, + GSM_TYPE_STORE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_CLIENT_STORE, + g_param_spec_object ("client-store", + NULL, + NULL, + GSM_TYPE_STORE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GsmInhibitDialogPrivate)); +} + +static void +gsm_inhibit_dialog_init (GsmInhibitDialog *dialog) +{ + GtkWidget *content_area; + GtkWidget *widget; + GError *error; + + dialog->priv = GSM_INHIBIT_DIALOG_GET_PRIVATE (dialog); + + dialog->priv->xml = gtk_builder_new (); + gtk_builder_set_translation_domain (dialog->priv->xml, GETTEXT_PACKAGE); + + error = NULL; + if (!gtk_builder_add_from_file (dialog->priv->xml, + GTKBUILDER_DIR "/" GTKBUILDER_FILE, + &error)) { + if (error) { + g_warning ("Could not load inhibitor UI file: %s", + error->message); + g_error_free (error); + } else { + g_warning ("Could not load inhibitor UI file."); + } + } + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + "main-box")); + gtk_container_add (GTK_CONTAINER (content_area), widget); + + gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_window_set_icon_name (GTK_WINDOW (dialog), "system-log-out"); + gtk_window_set_title (GTK_WINDOW (dialog), ""); + g_object_set (dialog, + "allow-shrink", FALSE, + "allow-grow", FALSE, + NULL); +} + +static void +gsm_inhibit_dialog_finalize (GObject *object) +{ + GsmInhibitDialog *dialog; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_INHIBIT_DIALOG (object)); + + dialog = GSM_INHIBIT_DIALOG (object); + + g_return_if_fail (dialog->priv != NULL); + + g_debug ("GsmInhibitDialog: finalizing"); + + G_OBJECT_CLASS (gsm_inhibit_dialog_parent_class)->finalize (object); +} + +GtkWidget * +gsm_inhibit_dialog_new (GsmStore *inhibitors, + GsmStore *clients, + int action) +{ + GObject *object; + + object = g_object_new (GSM_TYPE_INHIBIT_DIALOG, + "action", action, + "inhibitor-store", inhibitors, + "client-store", clients, + NULL); + + return GTK_WIDGET (object); +} diff --git a/mate-session/gsm-inhibit-dialog.h b/mate-session/gsm-inhibit-dialog.h new file mode 100644 index 0000000..035f424 --- /dev/null +++ b/mate-session/gsm-inhibit-dialog.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef __GSM_INHIBIT_DIALOG_H +#define __GSM_INHIBIT_DIALOG_H + +#include <glib-object.h> +#include <gtk/gtk.h> + +#include "gsm-store.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_INHIBIT_DIALOG (gsm_inhibit_dialog_get_type ()) +#define GSM_INHIBIT_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_INHIBIT_DIALOG, GsmInhibitDialog)) +#define GSM_INHIBIT_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_INHIBIT_DIALOG, GsmInhibitDialogClass)) +#define GSM_IS_INHIBIT_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_INHIBIT_DIALOG)) +#define GSM_IS_INHIBIT_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_INHIBIT_DIALOG)) +#define GSM_INHIBIT_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_INHIBIT_DIALOG, GsmInhibitDialogClass)) + +typedef struct GsmInhibitDialogPrivate GsmInhibitDialogPrivate; + +typedef enum +{ + GSM_LOGOUT_ACTION_LOGOUT, + GSM_LOGOUT_ACTION_SWITCH_USER, + GSM_LOGOUT_ACTION_SHUTDOWN, + GSM_LOGOUT_ACTION_REBOOT, + GSM_LOGOUT_ACTION_HIBERNATE, + GSM_LOGOUT_ACTION_SLEEP +} GsmLogoutAction; + +typedef struct +{ + GtkDialog parent; + GsmInhibitDialogPrivate *priv; +} GsmInhibitDialog; + +typedef struct +{ + GtkDialogClass parent_class; +} GsmInhibitDialogClass; + +GType gsm_inhibit_dialog_get_type (void); + +GtkWidget * gsm_inhibit_dialog_new (GsmStore *inhibitors, + GsmStore *clients, + int action); +GtkTreeModel * gsm_inhibit_dialog_get_model (GsmInhibitDialog *dialog); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_INHIBIT_DIALOG_H */ diff --git a/mate-session/gsm-inhibitor.c b/mate-session/gsm-inhibitor.c new file mode 100644 index 0000000..13124a4 --- /dev/null +++ b/mate-session/gsm-inhibitor.c @@ -0,0 +1,605 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <dbus/dbus-glib.h> + +#include "gsm-inhibitor.h" +#include "gsm-inhibitor-glue.h" + +static guint32 inhibitor_serial = 1; + +#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0') + +#define GSM_INHIBITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_INHIBITOR, GsmInhibitorPrivate)) + +struct GsmInhibitorPrivate +{ + char *id; + char *bus_name; + char *app_id; + char *client_id; + char *reason; + guint flags; + guint toplevel_xid; + guint cookie; + DBusGConnection *connection; +}; + +enum { + PROP_0, + PROP_BUS_NAME, + PROP_REASON, + PROP_APP_ID, + PROP_CLIENT_ID, + PROP_FLAGS, + PROP_TOPLEVEL_XID, + PROP_COOKIE +}; + +G_DEFINE_TYPE (GsmInhibitor, gsm_inhibitor, G_TYPE_OBJECT) + +GQuark +gsm_inhibitor_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gsm_inhibitor_error"); + } + + return ret; +} + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +gsm_inhibitor_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (GSM_INHIBITOR_ERROR_GENERAL, "GeneralError"), + ENUM_ENTRY (GSM_INHIBITOR_ERROR_NOT_SET, "NotSet"), + { 0, 0, 0 } + }; + + g_assert (GSM_INHIBITOR_NUM_ERRORS == G_N_ELEMENTS (values) - 1); + + etype = g_enum_register_static ("GsmInhibitorError", values); + } + + return etype; +} + +static guint32 +get_next_inhibitor_serial (void) +{ + guint32 serial; + + serial = inhibitor_serial++; + + if ((gint32)inhibitor_serial < 0) { + inhibitor_serial = 1; + } + + return serial; +} + +static gboolean +register_inhibitor (GsmInhibitor *inhibitor) +{ + GError *error; + + error = NULL; + inhibitor->priv->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (inhibitor->priv->connection == NULL) { + if (error != NULL) { + g_critical ("error getting session bus: %s", error->message); + g_error_free (error); + } + return FALSE; + } + + dbus_g_connection_register_g_object (inhibitor->priv->connection, inhibitor->priv->id, G_OBJECT (inhibitor)); + + return TRUE; +} + +static GObject * +gsm_inhibitor_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmInhibitor *inhibitor; + gboolean res; + + inhibitor = GSM_INHIBITOR (G_OBJECT_CLASS (gsm_inhibitor_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + g_free (inhibitor->priv->id); + inhibitor->priv->id = g_strdup_printf ("/org/mate/SessionManager/Inhibitor%u", get_next_inhibitor_serial ()); + res = register_inhibitor (inhibitor); + if (! res) { + g_warning ("Unable to register inhibitor with session bus"); + } + + return G_OBJECT (inhibitor); +} + +static void +gsm_inhibitor_init (GsmInhibitor *inhibitor) +{ + inhibitor->priv = GSM_INHIBITOR_GET_PRIVATE (inhibitor); +} + +static void +gsm_inhibitor_set_bus_name (GsmInhibitor *inhibitor, + const char *bus_name) +{ + g_return_if_fail (GSM_IS_INHIBITOR (inhibitor)); + + g_free (inhibitor->priv->bus_name); + + if (bus_name != NULL) { + inhibitor->priv->bus_name = g_strdup (bus_name); + } else { + inhibitor->priv->bus_name = g_strdup (""); + } + g_object_notify (G_OBJECT (inhibitor), "bus-name"); +} + +static void +gsm_inhibitor_set_app_id (GsmInhibitor *inhibitor, + const char *app_id) +{ + g_return_if_fail (GSM_IS_INHIBITOR (inhibitor)); + + g_free (inhibitor->priv->app_id); + + inhibitor->priv->app_id = g_strdup (app_id); + g_object_notify (G_OBJECT (inhibitor), "app-id"); +} + +static void +gsm_inhibitor_set_client_id (GsmInhibitor *inhibitor, + const char *client_id) +{ + g_return_if_fail (GSM_IS_INHIBITOR (inhibitor)); + + g_free (inhibitor->priv->client_id); + + g_debug ("GsmInhibitor: setting client-id = %s", client_id); + + if (client_id != NULL) { + inhibitor->priv->client_id = g_strdup (client_id); + } else { + inhibitor->priv->client_id = g_strdup (""); + } + g_object_notify (G_OBJECT (inhibitor), "client-id"); +} + +static void +gsm_inhibitor_set_reason (GsmInhibitor *inhibitor, + const char *reason) +{ + g_return_if_fail (GSM_IS_INHIBITOR (inhibitor)); + + g_free (inhibitor->priv->reason); + + if (reason != NULL) { + inhibitor->priv->reason = g_strdup (reason); + } else { + inhibitor->priv->reason = g_strdup (""); + } + g_object_notify (G_OBJECT (inhibitor), "reason"); +} + +static void +gsm_inhibitor_set_cookie (GsmInhibitor *inhibitor, + guint cookie) +{ + g_return_if_fail (GSM_IS_INHIBITOR (inhibitor)); + + if (inhibitor->priv->cookie != cookie) { + inhibitor->priv->cookie = cookie; + g_object_notify (G_OBJECT (inhibitor), "cookie"); + } +} + +static void +gsm_inhibitor_set_flags (GsmInhibitor *inhibitor, + guint flags) +{ + g_return_if_fail (GSM_IS_INHIBITOR (inhibitor)); + + if (inhibitor->priv->flags != flags) { + inhibitor->priv->flags = flags; + g_object_notify (G_OBJECT (inhibitor), "flags"); + } +} + +static void +gsm_inhibitor_set_toplevel_xid (GsmInhibitor *inhibitor, + guint xid) +{ + g_return_if_fail (GSM_IS_INHIBITOR (inhibitor)); + + if (inhibitor->priv->toplevel_xid != xid) { + inhibitor->priv->toplevel_xid = xid; + g_object_notify (G_OBJECT (inhibitor), "toplevel-xid"); + } +} + +const char * +gsm_inhibitor_peek_bus_name (GsmInhibitor *inhibitor) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), NULL); + + return inhibitor->priv->bus_name; +} + +gboolean +gsm_inhibitor_get_app_id (GsmInhibitor *inhibitor, + char **id, + GError **error) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), FALSE); + + if (inhibitor->priv->app_id != NULL) { + *id = g_strdup (inhibitor->priv->app_id); + } else { + *id = g_strdup (""); + } + + return TRUE; +} + +gboolean +gsm_inhibitor_get_client_id (GsmInhibitor *inhibitor, + char **id, + GError **error) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), FALSE); + + /* object paths are not allowed to be NULL or blank */ + if (IS_STRING_EMPTY (inhibitor->priv->client_id)) { + g_set_error (error, + GSM_INHIBITOR_ERROR, + GSM_INHIBITOR_ERROR_NOT_SET, + "Value is not set"); + return FALSE; + } + + *id = g_strdup (inhibitor->priv->client_id); + + g_debug ("GsmInhibitor: getting client-id = '%s'", *id); + + return TRUE; +} + +gboolean +gsm_inhibitor_get_reason (GsmInhibitor *inhibitor, + char **reason, + GError **error) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), FALSE); + + if (inhibitor->priv->reason != NULL) { + *reason = g_strdup (inhibitor->priv->reason); + } else { + *reason = g_strdup (""); + } + + return TRUE; +} + +gboolean +gsm_inhibitor_get_flags (GsmInhibitor *inhibitor, + guint *flags, + GError **error) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), FALSE); + + *flags = inhibitor->priv->flags; + + return TRUE; +} + +gboolean +gsm_inhibitor_get_toplevel_xid (GsmInhibitor *inhibitor, + guint *xid, + GError **error) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), FALSE); + + *xid = inhibitor->priv->toplevel_xid; + + return TRUE; +} + +const char * +gsm_inhibitor_peek_id (GsmInhibitor *inhibitor) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), NULL); + + return inhibitor->priv->id; +} + +const char * +gsm_inhibitor_peek_app_id (GsmInhibitor *inhibitor) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), NULL); + + return inhibitor->priv->app_id; +} + +const char * +gsm_inhibitor_peek_client_id (GsmInhibitor *inhibitor) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), NULL); + + return inhibitor->priv->client_id; +} + +const char * +gsm_inhibitor_peek_reason (GsmInhibitor *inhibitor) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), NULL); + + return inhibitor->priv->reason; +} + +guint +gsm_inhibitor_peek_flags (GsmInhibitor *inhibitor) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), 0); + + return inhibitor->priv->flags; +} + +guint +gsm_inhibitor_peek_toplevel_xid (GsmInhibitor *inhibitor) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), 0); + + return inhibitor->priv->toplevel_xid; +} + +guint +gsm_inhibitor_peek_cookie (GsmInhibitor *inhibitor) +{ + g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), 0); + + return inhibitor->priv->cookie; +} + +static void +gsm_inhibitor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmInhibitor *self; + + self = GSM_INHIBITOR (object); + + switch (prop_id) { + case PROP_BUS_NAME: + gsm_inhibitor_set_bus_name (self, g_value_get_string (value)); + break; + case PROP_APP_ID: + gsm_inhibitor_set_app_id (self, g_value_get_string (value)); + break; + case PROP_CLIENT_ID: + gsm_inhibitor_set_client_id (self, g_value_get_string (value)); + break; + case PROP_REASON: + gsm_inhibitor_set_reason (self, g_value_get_string (value)); + break; + case PROP_FLAGS: + gsm_inhibitor_set_flags (self, g_value_get_uint (value)); + break; + case PROP_COOKIE: + gsm_inhibitor_set_cookie (self, g_value_get_uint (value)); + break; + case PROP_TOPLEVEL_XID: + gsm_inhibitor_set_toplevel_xid (self, g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_inhibitor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmInhibitor *self; + + self = GSM_INHIBITOR (object); + + switch (prop_id) { + case PROP_BUS_NAME: + g_value_set_string (value, self->priv->bus_name); + break; + case PROP_APP_ID: + g_value_set_string (value, self->priv->app_id); + break; + case PROP_CLIENT_ID: + g_value_set_string (value, self->priv->client_id); + break; + case PROP_REASON: + g_value_set_string (value, self->priv->reason); + break; + case PROP_FLAGS: + g_value_set_uint (value, self->priv->flags); + break; + case PROP_COOKIE: + g_value_set_uint (value, self->priv->cookie); + break; + case PROP_TOPLEVEL_XID: + g_value_set_uint (value, self->priv->toplevel_xid); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_inhibitor_finalize (GObject *object) +{ + GsmInhibitor *inhibitor = (GsmInhibitor *) object; + + g_free (inhibitor->priv->id); + g_free (inhibitor->priv->bus_name); + g_free (inhibitor->priv->app_id); + g_free (inhibitor->priv->client_id); + g_free (inhibitor->priv->reason); + + G_OBJECT_CLASS (gsm_inhibitor_parent_class)->finalize (object); +} + +static void +gsm_inhibitor_class_init (GsmInhibitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gsm_inhibitor_finalize; + object_class->constructor = gsm_inhibitor_constructor; + object_class->get_property = gsm_inhibitor_get_property; + object_class->set_property = gsm_inhibitor_set_property; + + g_object_class_install_property (object_class, + PROP_BUS_NAME, + g_param_spec_string ("bus-name", + "bus-name", + "bus-name", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_APP_ID, + g_param_spec_string ("app-id", + "app-id", + "app-id", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_CLIENT_ID, + g_param_spec_string ("client-id", + "client-id", + "client-id", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_REASON, + g_param_spec_string ("reason", + "reason", + "reason", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_FLAGS, + g_param_spec_uint ("flags", + "flags", + "flags", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_TOPLEVEL_XID, + g_param_spec_uint ("toplevel-xid", + "toplevel-xid", + "toplevel-xid", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_COOKIE, + g_param_spec_uint ("cookie", + "cookie", + "cookie", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + dbus_g_object_type_install_info (GSM_TYPE_INHIBITOR, &dbus_glib_gsm_inhibitor_object_info); + dbus_g_error_domain_register (GSM_INHIBITOR_ERROR, NULL, GSM_INHIBITOR_TYPE_ERROR); + g_type_class_add_private (klass, sizeof (GsmInhibitorPrivate)); +} + +GsmInhibitor * +gsm_inhibitor_new (const char *app_id, + guint toplevel_xid, + guint flags, + const char *reason, + const char *bus_name, + guint cookie) +{ + GsmInhibitor *inhibitor; + + inhibitor = g_object_new (GSM_TYPE_INHIBITOR, + "app-id", app_id, + "reason", reason, + "bus-name", bus_name, + "flags", flags, + "toplevel-xid", toplevel_xid, + "cookie", cookie, + NULL); + + return inhibitor; +} + +GsmInhibitor * +gsm_inhibitor_new_for_client (const char *client_id, + const char *app_id, + guint flags, + const char *reason, + const char *bus_name, + guint cookie) +{ + GsmInhibitor *inhibitor; + + inhibitor = g_object_new (GSM_TYPE_INHIBITOR, + "client-id", client_id, + "app-id", app_id, + "reason", reason, + "bus-name", bus_name, + "flags", flags, + "cookie", cookie, + NULL); + + return inhibitor; +} diff --git a/mate-session/gsm-inhibitor.h b/mate-session/gsm-inhibitor.h new file mode 100644 index 0000000..c4fc5b3 --- /dev/null +++ b/mate-session/gsm-inhibitor.h @@ -0,0 +1,120 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 __GSM_INHIBITOR_H__ +#define __GSM_INHIBITOR_H__ + +#include <glib-object.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_INHIBITOR (gsm_inhibitor_get_type ()) +#define GSM_INHIBITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_INHIBITOR, GsmInhibitor)) +#define GSM_INHIBITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_INHIBITOR, GsmInhibitorClass)) +#define GSM_IS_INHIBITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_INHIBITOR)) +#define GSM_IS_INHIBITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_INHIBITOR)) +#define GSM_INHIBITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_INHIBITOR, GsmInhibitorClass)) + +typedef struct _GsmInhibitor GsmInhibitor; +typedef struct _GsmInhibitorClass GsmInhibitorClass; + +typedef struct GsmInhibitorPrivate GsmInhibitorPrivate; + +struct _GsmInhibitor +{ + GObject parent; + GsmInhibitorPrivate *priv; +}; + +struct _GsmInhibitorClass +{ + GObjectClass parent_class; +}; + +typedef enum { + GSM_INHIBITOR_FLAG_LOGOUT = 1 << 0, + GSM_INHIBITOR_FLAG_SWITCH_USER = 1 << 1, + GSM_INHIBITOR_FLAG_SUSPEND = 1 << 2, + GSM_INHIBITOR_FLAG_IDLE = 1 << 3 +} GsmInhibitorFlag; + +typedef enum +{ + GSM_INHIBITOR_ERROR_GENERAL = 0, + GSM_INHIBITOR_ERROR_NOT_SET, + GSM_INHIBITOR_NUM_ERRORS +} GsmInhibitorError; + +#define GSM_INHIBITOR_ERROR gsm_inhibitor_error_quark () +GType gsm_inhibitor_error_get_type (void); +#define GSM_INHIBITOR_TYPE_ERROR (gsm_inhibitor_error_get_type ()) + +GQuark gsm_inhibitor_error_quark (void); + +GType gsm_inhibitor_get_type (void) G_GNUC_CONST; + +GsmInhibitor * gsm_inhibitor_new (const char *app_id, + guint toplevel_xid, + guint flags, + const char *reason, + const char *bus_name, + guint cookie); +GsmInhibitor * gsm_inhibitor_new_for_client (const char *client_id, + const char *app_id, + guint flags, + const char *reason, + const char *bus_name, + guint cookie); + +const char * gsm_inhibitor_peek_id (GsmInhibitor *inhibitor); +const char * gsm_inhibitor_peek_app_id (GsmInhibitor *inhibitor); +const char * gsm_inhibitor_peek_client_id (GsmInhibitor *inhibitor); +const char * gsm_inhibitor_peek_reason (GsmInhibitor *inhibitor); +const char * gsm_inhibitor_peek_bus_name (GsmInhibitor *inhibitor); +guint gsm_inhibitor_peek_cookie (GsmInhibitor *inhibitor); +guint gsm_inhibitor_peek_flags (GsmInhibitor *inhibitor); +guint gsm_inhibitor_peek_toplevel_xid (GsmInhibitor *inhibitor); + +/* exported to bus */ +gboolean gsm_inhibitor_get_app_id (GsmInhibitor *inhibitor, + char **id, + GError **error); +gboolean gsm_inhibitor_get_client_id (GsmInhibitor *inhibitor, + char **id, + GError **error); +gboolean gsm_inhibitor_get_reason (GsmInhibitor *inhibitor, + char **reason, + GError **error); +gboolean gsm_inhibitor_get_flags (GsmInhibitor *inhibitor, + guint *flags, + GError **error); +gboolean gsm_inhibitor_get_toplevel_xid (GsmInhibitor *inhibitor, + guint *xid, + GError **error); + + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_INHIBITOR_H__ */ diff --git a/mate-session/gsm-logout-dialog.c b/mate-session/gsm-logout-dialog.c new file mode 100644 index 0000000..de606a4 --- /dev/null +++ b/mate-session/gsm-logout-dialog.c @@ -0,0 +1,461 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2006 Vincent Untz + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: + * Vincent Untz <[email protected]> + */ + +#include <config.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include <upower.h> + +#include "gsm-logout-dialog.h" +#include "gsm-consolekit.h" +#include "mdm.h" + +#define GSM_LOGOUT_DIALOG_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_LOGOUT_DIALOG, GsmLogoutDialogPrivate)) + +#define AUTOMATIC_ACTION_TIMEOUT 60 + +#define GSM_ICON_LOGOUT "system-log-out" +#define GSM_ICON_SHUTDOWN "system-shutdown" + +typedef enum { + GSM_DIALOG_LOGOUT_TYPE_LOGOUT, + GSM_DIALOG_LOGOUT_TYPE_SHUTDOWN +} GsmDialogLogoutType; + +struct _GsmLogoutDialogPrivate +{ + GsmDialogLogoutType type; + + UpClient *up_client; + GsmConsolekit *consolekit; + + int timeout; + unsigned int timeout_id; + + unsigned int default_response; +}; + +static GsmLogoutDialog *current_dialog = NULL; + +static void gsm_logout_dialog_set_timeout (GsmLogoutDialog *logout_dialog); + +static void gsm_logout_dialog_destroy (GsmLogoutDialog *logout_dialog, + gpointer data); + +static void gsm_logout_dialog_show (GsmLogoutDialog *logout_dialog, + gpointer data); + +enum { + PROP_0, + PROP_MESSAGE_TYPE +}; + +G_DEFINE_TYPE (GsmLogoutDialog, gsm_logout_dialog, GTK_TYPE_MESSAGE_DIALOG); + +static void +gsm_logout_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_MESSAGE_TYPE: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_logout_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_MESSAGE_TYPE: + g_value_set_enum (value, GTK_MESSAGE_WARNING); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_logout_dialog_class_init (GsmLogoutDialogClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + + /* This is a workaround to avoid a stupid crash: libmateui + * listens for the "show" signal on all GtkMessageDialog and + * gets the "message-type" of the dialogs. We will crash when + * it accesses this property if we don't override it since we + * didn't define it. */ + gobject_class->set_property = gsm_logout_dialog_set_property; + gobject_class->get_property = gsm_logout_dialog_get_property; + + g_object_class_override_property (gobject_class, + PROP_MESSAGE_TYPE, + "message-type"); + + g_type_class_add_private (klass, sizeof (GsmLogoutDialogPrivate)); +} + +static void +gsm_logout_dialog_init (GsmLogoutDialog *logout_dialog) +{ + logout_dialog->priv = GSM_LOGOUT_DIALOG_GET_PRIVATE (logout_dialog); + + logout_dialog->priv->timeout_id = 0; + logout_dialog->priv->timeout = 0; + logout_dialog->priv->default_response = GTK_RESPONSE_CANCEL; + + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (logout_dialog), TRUE); + gtk_window_set_keep_above (GTK_WINDOW (logout_dialog), TRUE); + gtk_window_stick (GTK_WINDOW (logout_dialog)); + + logout_dialog->priv->up_client = up_client_new (); + + logout_dialog->priv->consolekit = gsm_get_consolekit (); + + g_signal_connect (logout_dialog, + "destroy", + G_CALLBACK (gsm_logout_dialog_destroy), + NULL); + + g_signal_connect (logout_dialog, + "show", + G_CALLBACK (gsm_logout_dialog_show), + NULL); +} + +static void +gsm_logout_dialog_destroy (GsmLogoutDialog *logout_dialog, + gpointer data) +{ + if (logout_dialog->priv->timeout_id != 0) { + g_source_remove (logout_dialog->priv->timeout_id); + logout_dialog->priv->timeout_id = 0; + } + + if (logout_dialog->priv->up_client) { + g_object_unref (logout_dialog->priv->up_client); + logout_dialog->priv->up_client = NULL; + } + + if (logout_dialog->priv->consolekit) { + g_object_unref (logout_dialog->priv->consolekit); + logout_dialog->priv->consolekit = NULL; + } + + current_dialog = NULL; +} + +static gboolean +gsm_logout_supports_system_suspend (GsmLogoutDialog *logout_dialog) +{ + return up_client_get_can_suspend (logout_dialog->priv->up_client); +} + +static gboolean +gsm_logout_supports_system_hibernate (GsmLogoutDialog *logout_dialog) +{ + return up_client_get_can_hibernate (logout_dialog->priv->up_client); +} + +static gboolean +gsm_logout_supports_switch_user (GsmLogoutDialog *logout_dialog) +{ + gboolean ret; + + ret = gsm_consolekit_can_switch_user (logout_dialog->priv->consolekit); + + return ret; +} + +static gboolean +gsm_logout_supports_reboot (GsmLogoutDialog *logout_dialog) +{ + gboolean ret; + + ret = gsm_consolekit_can_restart (logout_dialog->priv->consolekit); + if (!ret) { + ret = mdm_supports_logout_action (MDM_LOGOUT_ACTION_REBOOT); + } + + return ret; +} + +static gboolean +gsm_logout_supports_shutdown (GsmLogoutDialog *logout_dialog) +{ + gboolean ret; + + ret = gsm_consolekit_can_stop (logout_dialog->priv->consolekit); + + if (!ret) { + ret = mdm_supports_logout_action (MDM_LOGOUT_ACTION_SHUTDOWN); + } + + return ret; +} + +static void +gsm_logout_dialog_show (GsmLogoutDialog *logout_dialog, gpointer user_data) +{ + gsm_logout_dialog_set_timeout (logout_dialog); +} + +static gboolean +gsm_logout_dialog_timeout (gpointer data) +{ + GsmLogoutDialog *logout_dialog; + char *seconds_warning; + char *secondary_text; + int seconds_to_show; + static char *session_type = NULL; + + logout_dialog = (GsmLogoutDialog *) data; + + if (!logout_dialog->priv->timeout) { + gtk_dialog_response (GTK_DIALOG (logout_dialog), + logout_dialog->priv->default_response); + + return FALSE; + } + + if (logout_dialog->priv->timeout <= 30) { + seconds_to_show = logout_dialog->priv->timeout; + } else { + seconds_to_show = (logout_dialog->priv->timeout/10) * 10; + + if (logout_dialog->priv->timeout % 10) + seconds_to_show += 10; + } + + switch (logout_dialog->priv->type) { + case GSM_DIALOG_LOGOUT_TYPE_LOGOUT: + seconds_warning = ngettext ("You will be automatically logged " + "out in %d second.", + "You will be automatically logged " + "out in %d seconds.", + seconds_to_show); + break; + + case GSM_DIALOG_LOGOUT_TYPE_SHUTDOWN: + seconds_warning = ngettext ("This system will be automatically " + "shut down in %d second.", + "This system will be automatically " + "shut down in %d seconds.", + seconds_to_show); + break; + + default: + g_assert_not_reached (); + } + + if (session_type == NULL) { + GsmConsolekit *consolekit; + + consolekit = gsm_get_consolekit (); + session_type = gsm_consolekit_get_current_session_type (consolekit); + g_object_unref (consolekit); + } + + if (g_strcmp0 (session_type, GSM_CONSOLEKIT_SESSION_TYPE_LOGIN_WINDOW) != 0) { + char *name, *tmp; + + name = g_locale_to_utf8 (g_get_real_name (), -1, NULL, NULL, NULL); + + if (!name || name[0] == '\0' || strcmp (name, "Unknown") == 0) { + name = g_locale_to_utf8 (g_get_user_name (), -1 , NULL, NULL, NULL); + } + + if (!name) { + name = g_strdup (g_get_user_name ()); + } + + tmp = g_strdup_printf (_("You are currently logged in as \"%s\"."), name); + secondary_text = g_strconcat (tmp, "\n", seconds_warning, NULL); + g_free (tmp); + + g_free (name); + } else { + secondary_text = g_strdup (seconds_warning); + } + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (logout_dialog), + secondary_text, + seconds_to_show, + NULL); + + logout_dialog->priv->timeout--; + + g_free (secondary_text); + + return TRUE; +} + +static void +gsm_logout_dialog_set_timeout (GsmLogoutDialog *logout_dialog) +{ + logout_dialog->priv->timeout = AUTOMATIC_ACTION_TIMEOUT; + + /* Sets the secondary text */ + gsm_logout_dialog_timeout (logout_dialog); + + if (logout_dialog->priv->timeout_id != 0) { + g_source_remove (logout_dialog->priv->timeout_id); + } + + logout_dialog->priv->timeout_id = g_timeout_add (1000, + gsm_logout_dialog_timeout, + logout_dialog); +} + +static GtkWidget * +gsm_get_dialog (GsmDialogLogoutType type, + GdkScreen *screen, + guint32 activate_time) +{ + GsmLogoutDialog *logout_dialog; + GtkWidget *dialog_image; + const char *primary_text; + const char *icon_name; + + if (current_dialog != NULL) { + gtk_widget_destroy (GTK_WIDGET (current_dialog)); + } + + logout_dialog = g_object_new (GSM_TYPE_LOGOUT_DIALOG, NULL); + + current_dialog = logout_dialog; + + gtk_window_set_title (GTK_WINDOW (logout_dialog), ""); + + logout_dialog->priv->type = type; + + icon_name = NULL; + primary_text = NULL; + + switch (type) { + case GSM_DIALOG_LOGOUT_TYPE_LOGOUT: + icon_name = GSM_ICON_LOGOUT; + primary_text = _("Log out of this system now?"); + + logout_dialog->priv->default_response = GSM_LOGOUT_RESPONSE_LOGOUT; + + if (gsm_logout_supports_switch_user (logout_dialog)) { + gtk_dialog_add_button (GTK_DIALOG (logout_dialog), + _("_Switch User"), + GSM_LOGOUT_RESPONSE_SWITCH_USER); + } + + gtk_dialog_add_button (GTK_DIALOG (logout_dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + + gtk_dialog_add_button (GTK_DIALOG (logout_dialog), + _("_Log Out"), + GSM_LOGOUT_RESPONSE_LOGOUT); + + break; + case GSM_DIALOG_LOGOUT_TYPE_SHUTDOWN: + icon_name = GSM_ICON_SHUTDOWN; + primary_text = _("Shut down this system now?"); + + logout_dialog->priv->default_response = GSM_LOGOUT_RESPONSE_SHUTDOWN; + + if (gsm_logout_supports_system_suspend (logout_dialog)) { + gtk_dialog_add_button (GTK_DIALOG (logout_dialog), + _("S_uspend"), + GSM_LOGOUT_RESPONSE_SLEEP); + } + + if (gsm_logout_supports_system_hibernate (logout_dialog)) { + gtk_dialog_add_button (GTK_DIALOG (logout_dialog), + _("_Hibernate"), + GSM_LOGOUT_RESPONSE_HIBERNATE); + } + + if (gsm_logout_supports_reboot (logout_dialog)) { + gtk_dialog_add_button (GTK_DIALOG (logout_dialog), + _("_Restart"), + GSM_LOGOUT_RESPONSE_REBOOT); + } + + gtk_dialog_add_button (GTK_DIALOG (logout_dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + + if (gsm_logout_supports_shutdown (logout_dialog)) { + gtk_dialog_add_button (GTK_DIALOG (logout_dialog), + _("_Shut Down"), + GSM_LOGOUT_RESPONSE_SHUTDOWN); + } + break; + default: + g_assert_not_reached (); + } + + dialog_image = gtk_message_dialog_get_image (GTK_MESSAGE_DIALOG (logout_dialog)); + + gtk_image_set_from_icon_name (GTK_IMAGE (dialog_image), + icon_name, GTK_ICON_SIZE_DIALOG); + gtk_window_set_icon_name (GTK_WINDOW (logout_dialog), icon_name); + gtk_window_set_position (GTK_WINDOW (logout_dialog), GTK_WIN_POS_CENTER_ALWAYS); + gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (logout_dialog), primary_text); + + gtk_dialog_set_default_response (GTK_DIALOG (logout_dialog), + logout_dialog->priv->default_response); + + gtk_window_set_screen (GTK_WINDOW (logout_dialog), screen); + + return GTK_WIDGET (logout_dialog); +} + +GtkWidget * +gsm_get_shutdown_dialog (GdkScreen *screen, + guint32 activate_time) +{ + return gsm_get_dialog (GSM_DIALOG_LOGOUT_TYPE_SHUTDOWN, + screen, + activate_time); +} + +GtkWidget * +gsm_get_logout_dialog (GdkScreen *screen, + guint32 activate_time) +{ + return gsm_get_dialog (GSM_DIALOG_LOGOUT_TYPE_LOGOUT, + screen, + activate_time); +} diff --git a/mate-session/gsm-logout-dialog.h b/mate-session/gsm-logout-dialog.h new file mode 100644 index 0000000..c3aad69 --- /dev/null +++ b/mate-session/gsm-logout-dialog.h @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2006 Vincent Untz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: + * Vincent Untz <[email protected]> + */ + +#ifndef __GSM_LOGOUT_DIALOG_H__ +#define __GSM_LOGOUT_DIALOG_H__ + +#include <gtk/gtk.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + GSM_LOGOUT_RESPONSE_LOGOUT, + GSM_LOGOUT_RESPONSE_SWITCH_USER, + GSM_LOGOUT_RESPONSE_SHUTDOWN, + GSM_LOGOUT_RESPONSE_REBOOT, + GSM_LOGOUT_RESPONSE_HIBERNATE, + GSM_LOGOUT_RESPONSE_SLEEP +}; + +#define GSM_TYPE_LOGOUT_DIALOG (gsm_logout_dialog_get_type ()) +#define GSM_LOGOUT_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_LOGOUT_DIALOG, GsmLogoutDialog)) +#define GSM_LOGOUT_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_LOGOUT_DIALOG, GsmLogoutDialogClass)) +#define GSM_IS_LOGOUT_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_LOGOUT_DIALOG)) +#define GSM_IS_LOGOUT_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_LOGOUT_DIALOG)) +#define GSM_LOGOUT_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_LOGOUT_DIALOG, GsmLogoutDialogClass)) + +typedef struct _GsmLogoutDialog GsmLogoutDialog; +typedef struct _GsmLogoutDialogClass GsmLogoutDialogClass; +typedef struct _GsmLogoutDialogPrivate GsmLogoutDialogPrivate; + +struct _GsmLogoutDialog +{ + GtkMessageDialog parent; + + GsmLogoutDialogPrivate *priv; +}; + +struct _GsmLogoutDialogClass +{ + GtkMessageDialogClass parent_class; +}; + +GType gsm_logout_dialog_get_type (void) G_GNUC_CONST; + +GtkWidget *gsm_get_logout_dialog (GdkScreen *screen, + guint32 activate_time); +GtkWidget *gsm_get_shutdown_dialog (GdkScreen *screen, + guint32 activate_time); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_LOGOUT_DIALOG_H__ */ diff --git a/mate-session/gsm-manager.c b/mate-session/gsm-manager.c new file mode 100644 index 0000000..cbddf21 --- /dev/null +++ b/mate-session/gsm-manager.c @@ -0,0 +1,3481 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2008 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include <upower.h> + +#include <gtk/gtk.h> /* for logout dialog */ +#include <mateconf/mateconf-client.h> + +#include "gsm-manager.h" +#include "gsm-manager-glue.h" + +#include "gsm-store.h" +#include "gsm-inhibitor.h" +#include "gsm-presence.h" + +#include "gsm-xsmp-client.h" +#include "gsm-dbus-client.h" + +#include "gsm-autostart-app.h" + +#include "gsm-util.h" +#include "mdm.h" +#include "gsm-logout-dialog.h" +#include "gsm-inhibit-dialog.h" +#include "gsm-consolekit.h" +#include "gsm-session-save.h" + +#define GSM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_MANAGER, GsmManagerPrivate)) + +#define GSM_MANAGER_DBUS_PATH "/org/mate/SessionManager" +#define GSM_MANAGER_DBUS_NAME "org.mate.SessionManager" + +#define GSM_MANAGER_PHASE_TIMEOUT 10 /* seconds */ + +#define MDM_FLEXISERVER_COMMAND "mdmflexiserver" +#define MDM_FLEXISERVER_ARGS "--startnew Standard" + + +#define KEY_LOCKDOWN_DIR "/desktop/mate/lockdown" +#define KEY_LOCK_DISABLE KEY_LOCKDOWN_DIR "/disable_lock_screen" +#define KEY_USER_SWITCH_DISABLE KEY_LOCKDOWN_DIR "/disable_user_switching" + +#define KEY_DESKTOP_DIR "/desktop/mate/session" +#define KEY_IDLE_DELAY KEY_DESKTOP_DIR "/idle_delay" + +#define KEY_MATE_SESSION_DIR "/apps/mate-session/options" +#define KEY_AUTOSAVE KEY_MATE_SESSION_DIR "/auto_save_session" + +#define KEY_SLEEP_LOCK "/apps/mate-screensaver/lock_enabled" + +#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0') + +typedef enum +{ + GSM_MANAGER_LOGOUT_NONE, + GSM_MANAGER_LOGOUT_LOGOUT, + GSM_MANAGER_LOGOUT_REBOOT, + GSM_MANAGER_LOGOUT_REBOOT_INTERACT, + GSM_MANAGER_LOGOUT_REBOOT_MDM, + GSM_MANAGER_LOGOUT_SHUTDOWN, + GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT, + GSM_MANAGER_LOGOUT_SHUTDOWN_MDM +} GsmManagerLogoutType; + +struct GsmManagerPrivate +{ + gboolean failsafe; + GsmStore *clients; + GsmStore *inhibitors; + GsmStore *apps; + GsmPresence *presence; + + /* Current status */ + GsmManagerPhase phase; + guint phase_timeout_id; + GSList *pending_apps; + gboolean forceful_logout; + GSList *query_clients; + guint query_timeout_id; + /* This is used for GSM_MANAGER_PHASE_END_SESSION only at the moment, + * since it uses a sublist of all running client that replied in a + * specific way */ + GSList *next_query_clients; + /* This is the action that will be done just before we exit */ + GsmManagerLogoutType logout_type; + + GtkWidget *inhibit_dialog; + + /* List of clients which were disconnected due to disabled condition + * and shouldn't be automatically restarted */ + GSList *condition_clients; + + MateConfClient *mateconf_client; + guint desktop_notify_id; + guint lockdown_notify_id; + + DBusGProxy *bus_proxy; + DBusGConnection *connection; + + /* Interface with other parts of the system */ + UpClient *up_client; +}; + +enum { + PROP_0, + PROP_CLIENT_STORE, + PROP_FAILSAFE +}; + +enum { + PHASE_CHANGED, + CLIENT_ADDED, + CLIENT_REMOVED, + INHIBITOR_ADDED, + INHIBITOR_REMOVED, + SESSION_RUNNING, + SESSION_OVER, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0 }; + +static void gsm_manager_class_init (GsmManagerClass *klass); +static void gsm_manager_init (GsmManager *manager); +static void gsm_manager_finalize (GObject *object); + +static gboolean auto_save_is_enabled (GsmManager *manager); +static void maybe_save_session (GsmManager *manager); + +static gpointer manager_object = NULL; + +G_DEFINE_TYPE (GsmManager, gsm_manager, G_TYPE_OBJECT) + +GQuark +gsm_manager_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gsm_manager_error"); + } + + return ret; +} + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +gsm_manager_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (GSM_MANAGER_ERROR_GENERAL, "GeneralError"), + ENUM_ENTRY (GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION, "NotInInitialization"), + ENUM_ENTRY (GSM_MANAGER_ERROR_NOT_IN_RUNNING, "NotInRunning"), + ENUM_ENTRY (GSM_MANAGER_ERROR_ALREADY_REGISTERED, "AlreadyRegistered"), + ENUM_ENTRY (GSM_MANAGER_ERROR_NOT_REGISTERED, "NotRegistered"), + ENUM_ENTRY (GSM_MANAGER_ERROR_INVALID_OPTION, "InvalidOption"), + { 0, 0, 0 } + }; + + g_assert (GSM_MANAGER_NUM_ERRORS == G_N_ELEMENTS (values) - 1); + + etype = g_enum_register_static ("GsmManagerError", values); + } + + return etype; +} + +static gboolean +_debug_client (const char *id, + GsmClient *client, + GsmManager *manager) +{ + g_debug ("GsmManager: Client %s", gsm_client_peek_id (client)); + return FALSE; +} + +static void +debug_clients (GsmManager *manager) +{ + gsm_store_foreach (manager->priv->clients, + (GsmStoreFunc)_debug_client, + manager); +} + +static gboolean +_debug_inhibitor (const char *id, + GsmInhibitor *inhibitor, + GsmManager *manager) +{ + g_debug ("GsmManager: Inhibitor app:%s client:%s bus-name:%s reason:%s", + gsm_inhibitor_peek_app_id (inhibitor), + gsm_inhibitor_peek_client_id (inhibitor), + gsm_inhibitor_peek_bus_name (inhibitor), + gsm_inhibitor_peek_reason (inhibitor)); + return FALSE; +} + +static void +debug_inhibitors (GsmManager *manager) +{ + gsm_store_foreach (manager->priv->inhibitors, + (GsmStoreFunc)_debug_inhibitor, + manager); +} + +static gboolean +_find_by_cookie (const char *id, + GsmInhibitor *inhibitor, + guint *cookie_ap) +{ + guint cookie_b; + + cookie_b = gsm_inhibitor_peek_cookie (inhibitor); + + return (*cookie_ap == cookie_b); +} + +static gboolean +_find_by_startup_id (const char *id, + GsmClient *client, + const char *startup_id_a) +{ + const char *startup_id_b; + + startup_id_b = gsm_client_peek_startup_id (client); + if (IS_STRING_EMPTY (startup_id_b)) { + return FALSE; + } + + return (strcmp (startup_id_a, startup_id_b) == 0); +} + +static void +app_condition_changed (GsmApp *app, + gboolean condition, + GsmManager *manager) +{ + GsmClient *client; + + g_debug ("GsmManager: app:%s condition changed condition:%d", + gsm_app_peek_id (app), + condition); + + client = (GsmClient *)gsm_store_find (manager->priv->clients, + (GsmStoreFunc)_find_by_startup_id, + (char *)gsm_app_peek_startup_id (app)); + + if (condition) { + if (!gsm_app_is_running (app) && client == NULL) { + GError *error; + gboolean res; + + g_debug ("GsmManager: starting app '%s'", gsm_app_peek_id (app)); + + error = NULL; + res = gsm_app_start (app, &error); + if (error != NULL) { + g_warning ("Not able to start app from its condition: %s", + error->message); + g_error_free (error); + } + } else { + g_debug ("GsmManager: not starting - app still running '%s'", gsm_app_peek_id (app)); + } + } else { + GError *error; + gboolean res; + + if (client != NULL) { + /* Kill client in case condition if false and make sure it won't + * be automatically restarted by adding the client to + * condition_clients */ + manager->priv->condition_clients = + g_slist_prepend (manager->priv->condition_clients, client); + + g_debug ("GsmManager: stopping client %s for app", gsm_client_peek_id (client)); + + error = NULL; + res = gsm_client_stop (client, &error); + if (error != NULL) { + g_warning ("Not able to stop app client from its condition: %s", + error->message); + g_error_free (error); + } + } else { + g_debug ("GsmManager: stopping app %s", gsm_app_peek_id (app)); + + /* If we don't have a client then we should try to kill the app */ + error = NULL; + res = gsm_app_stop (app, &error); + if (error != NULL) { + g_warning ("Not able to stop app from its condition: %s", + error->message); + g_error_free (error); + } + } + } +} + +static const char * +phase_num_to_name (guint phase) +{ + const char *name; + + switch (phase) { + case GSM_MANAGER_PHASE_STARTUP: + name = "STARTUP"; + break; + case GSM_MANAGER_PHASE_INITIALIZATION: + name = "INITIALIZATION"; + break; + case GSM_MANAGER_PHASE_WINDOW_MANAGER: + name = "WINDOW_MANAGER"; + break; + case GSM_MANAGER_PHASE_PANEL: + name = "PANEL"; + break; + case GSM_MANAGER_PHASE_DESKTOP: + name = "DESKTOP"; + break; + case GSM_MANAGER_PHASE_APPLICATION: + name = "APPLICATION"; + break; + case GSM_MANAGER_PHASE_RUNNING: + name = "RUNNING"; + break; + case GSM_MANAGER_PHASE_QUERY_END_SESSION: + name = "QUERY_END_SESSION"; + break; + case GSM_MANAGER_PHASE_END_SESSION: + name = "END_SESSION"; + break; + case GSM_MANAGER_PHASE_EXIT: + name = "EXIT"; + break; + default: + g_assert_not_reached (); + break; + } + + return name; +} + +static void start_phase (GsmManager *manager); + +static void +quit_request_completed (GsmConsolekit *consolekit, + GError *error, + gpointer user_data) +{ + MdmLogoutAction fallback_action = GPOINTER_TO_INT (user_data); + + if (error != NULL) { + mdm_set_logout_action (fallback_action); + } + + g_object_unref (consolekit); + + gtk_main_quit (); +} + +static void +gsm_manager_quit (GsmManager *manager) +{ + GsmConsolekit *consolekit; + + /* See the comment in request_reboot() for some more details about how + * this works. */ + + switch (manager->priv->logout_type) { + case GSM_MANAGER_LOGOUT_LOGOUT: + gtk_main_quit (); + break; + case GSM_MANAGER_LOGOUT_REBOOT: + case GSM_MANAGER_LOGOUT_REBOOT_INTERACT: + mdm_set_logout_action (MDM_LOGOUT_ACTION_NONE); + + consolekit = gsm_get_consolekit (); + g_signal_connect (consolekit, + "request-completed", + G_CALLBACK (quit_request_completed), + GINT_TO_POINTER (MDM_LOGOUT_ACTION_REBOOT)); + gsm_consolekit_attempt_restart (consolekit); + break; + case GSM_MANAGER_LOGOUT_REBOOT_MDM: + mdm_set_logout_action (MDM_LOGOUT_ACTION_REBOOT); + gtk_main_quit (); + break; + case GSM_MANAGER_LOGOUT_SHUTDOWN: + case GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT: + mdm_set_logout_action (MDM_LOGOUT_ACTION_NONE); + + consolekit = gsm_get_consolekit (); + g_signal_connect (consolekit, + "request-completed", + G_CALLBACK (quit_request_completed), + GINT_TO_POINTER (MDM_LOGOUT_ACTION_SHUTDOWN)); + gsm_consolekit_attempt_stop (consolekit); + break; + case GSM_MANAGER_LOGOUT_SHUTDOWN_MDM: + mdm_set_logout_action (MDM_LOGOUT_ACTION_SHUTDOWN); + gtk_main_quit (); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +end_phase (GsmManager *manager) +{ + g_debug ("GsmManager: ending phase %s\n", + phase_num_to_name (manager->priv->phase)); + + g_slist_free (manager->priv->pending_apps); + manager->priv->pending_apps = NULL; + + g_slist_free (manager->priv->query_clients); + manager->priv->query_clients = NULL; + + g_slist_free (manager->priv->next_query_clients); + manager->priv->next_query_clients = NULL; + + if (manager->priv->phase_timeout_id > 0) { + g_source_remove (manager->priv->phase_timeout_id); + manager->priv->phase_timeout_id = 0; + } + + switch (manager->priv->phase) { + case GSM_MANAGER_PHASE_STARTUP: + case GSM_MANAGER_PHASE_INITIALIZATION: + case GSM_MANAGER_PHASE_WINDOW_MANAGER: + case GSM_MANAGER_PHASE_PANEL: + case GSM_MANAGER_PHASE_DESKTOP: + case GSM_MANAGER_PHASE_APPLICATION: + case GSM_MANAGER_PHASE_RUNNING: + case GSM_MANAGER_PHASE_QUERY_END_SESSION: + manager->priv->phase++; + start_phase (manager); + break; + case GSM_MANAGER_PHASE_END_SESSION: + if (auto_save_is_enabled (manager)) { + maybe_save_session (manager); + } + manager->priv->phase++; + start_phase (manager); + break; + case GSM_MANAGER_PHASE_EXIT: + gsm_manager_quit (manager); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +app_registered (GsmApp *app, + GsmManager *manager) +{ + manager->priv->pending_apps = g_slist_remove (manager->priv->pending_apps, app); + g_signal_handlers_disconnect_by_func (app, app_registered, manager); + + if (manager->priv->pending_apps == NULL) { + if (manager->priv->phase_timeout_id > 0) { + g_source_remove (manager->priv->phase_timeout_id); + manager->priv->phase_timeout_id = 0; + } + + end_phase (manager); + } +} + +static gboolean +_client_failed_to_stop (const char *id, + GsmClient *client, + gpointer user_data) +{ + g_debug ("GsmManager: client failed to stop: %s, %s", gsm_client_peek_id (client), gsm_client_peek_app_id (client)); + return FALSE; +} + +static gboolean +on_phase_timeout (GsmManager *manager) +{ + GSList *a; + + manager->priv->phase_timeout_id = 0; + + switch (manager->priv->phase) { + case GSM_MANAGER_PHASE_STARTUP: + case GSM_MANAGER_PHASE_INITIALIZATION: + case GSM_MANAGER_PHASE_WINDOW_MANAGER: + case GSM_MANAGER_PHASE_PANEL: + case GSM_MANAGER_PHASE_DESKTOP: + case GSM_MANAGER_PHASE_APPLICATION: + for (a = manager->priv->pending_apps; a; a = a->next) { + g_warning ("Application '%s' failed to register before timeout", + gsm_app_peek_app_id (a->data)); + g_signal_handlers_disconnect_by_func (a->data, app_registered, manager); + /* FIXME: what if the app was filling in a required slot? */ + } + break; + case GSM_MANAGER_PHASE_RUNNING: + break; + case GSM_MANAGER_PHASE_QUERY_END_SESSION: + case GSM_MANAGER_PHASE_END_SESSION: + break; + case GSM_MANAGER_PHASE_EXIT: + gsm_store_foreach (manager->priv->clients, + (GsmStoreFunc)_client_failed_to_stop, + NULL); + break; + default: + g_assert_not_reached (); + break; + } + + end_phase (manager); + + return FALSE; +} + +static gboolean +_start_app (const char *id, + GsmApp *app, + GsmManager *manager) +{ + GError *error; + gboolean res; + + if (gsm_app_peek_phase (app) != manager->priv->phase) { + goto out; + } + + /* Keep track of app autostart condition in order to react + * accordingly in the future. */ + g_signal_connect (app, + "condition-changed", + G_CALLBACK (app_condition_changed), + manager); + + if (gsm_app_peek_is_disabled (app) + || gsm_app_peek_is_conditionally_disabled (app)) { + g_debug ("GsmManager: Skipping disabled app: %s", id); + goto out; + } + + error = NULL; + res = gsm_app_start (app, &error); + if (!res) { + if (error != NULL) { + g_warning ("Could not launch application '%s': %s", + gsm_app_peek_app_id (app), + error->message); + g_error_free (error); + error = NULL; + } + goto out; + } + + if (manager->priv->phase < GSM_MANAGER_PHASE_APPLICATION) { + g_signal_connect (app, + "exited", + G_CALLBACK (app_registered), + manager); + g_signal_connect (app, + "registered", + G_CALLBACK (app_registered), + manager); + manager->priv->pending_apps = g_slist_prepend (manager->priv->pending_apps, app); + } + out: + return FALSE; +} + +static void +do_phase_startup (GsmManager *manager) +{ + gsm_store_foreach (manager->priv->apps, + (GsmStoreFunc)_start_app, + manager); + + if (manager->priv->pending_apps != NULL) { + if (manager->priv->phase < GSM_MANAGER_PHASE_APPLICATION) { + manager->priv->phase_timeout_id = g_timeout_add_seconds (GSM_MANAGER_PHASE_TIMEOUT, + (GSourceFunc)on_phase_timeout, + manager); + } + } else { + end_phase (manager); + } +} + +typedef struct { + GsmManager *manager; + guint flags; +} ClientEndSessionData; + + +static gboolean +_client_end_session (GsmClient *client, + ClientEndSessionData *data) +{ + gboolean ret; + GError *error; + + error = NULL; + ret = gsm_client_end_session (client, data->flags, &error); + if (! ret) { + g_warning ("Unable to query client: %s", error->message); + g_error_free (error); + /* FIXME: what should we do if we can't communicate with client? */ + } else { + g_debug ("GsmManager: adding client to end-session clients: %s", gsm_client_peek_id (client)); + data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients, + client); + } + + return FALSE; +} + +static gboolean +_client_end_session_helper (const char *id, + GsmClient *client, + ClientEndSessionData *data) +{ + return _client_end_session (client, data); +} + +static void +do_phase_end_session (GsmManager *manager) +{ + ClientEndSessionData data; + + data.manager = manager; + data.flags = 0; + + if (manager->priv->forceful_logout) { + data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL; + } + if (auto_save_is_enabled (manager)) { + data.flags |= GSM_CLIENT_END_SESSION_FLAG_SAVE; + } + + if (manager->priv->phase_timeout_id > 0) { + g_source_remove (manager->priv->phase_timeout_id); + manager->priv->phase_timeout_id = 0; + } + + if (gsm_store_size (manager->priv->clients) > 0) { + manager->priv->phase_timeout_id = g_timeout_add_seconds (GSM_MANAGER_PHASE_TIMEOUT, + (GSourceFunc)on_phase_timeout, + manager); + + gsm_store_foreach (manager->priv->clients, + (GsmStoreFunc)_client_end_session_helper, + &data); + } else { + end_phase (manager); + } +} + +static void +do_phase_end_session_part_2 (GsmManager *manager) +{ + ClientEndSessionData data; + + data.manager = manager; + data.flags = 0; + + if (manager->priv->forceful_logout) { + data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL; + } + if (auto_save_is_enabled (manager)) { + data.flags |= GSM_CLIENT_END_SESSION_FLAG_SAVE; + } + data.flags |= GSM_CLIENT_END_SESSION_FLAG_LAST; + + /* keep the timeout that was started at the beginning of the + * GSM_MANAGER_PHASE_END_SESSION phase */ + + if (g_slist_length (manager->priv->next_query_clients) > 0) { + g_slist_foreach (manager->priv->next_query_clients, + (GFunc)_client_end_session, + &data); + + g_slist_free (manager->priv->next_query_clients); + manager->priv->next_query_clients = NULL; + } else { + end_phase (manager); + } +} + +static gboolean +_client_stop (const char *id, + GsmClient *client, + gpointer user_data) +{ + gboolean ret; + GError *error; + + error = NULL; + ret = gsm_client_stop (client, &error); + if (! ret) { + g_warning ("Unable to stop client: %s", error->message); + g_error_free (error); + /* FIXME: what should we do if we can't communicate with client? */ + } else { + g_debug ("GsmManager: stopped client: %s", gsm_client_peek_id (client)); + } + + return FALSE; +} + +static void +do_phase_exit (GsmManager *manager) +{ + if (gsm_store_size (manager->priv->clients) > 0) { + manager->priv->phase_timeout_id = g_timeout_add_seconds (GSM_MANAGER_PHASE_TIMEOUT, + (GSourceFunc)on_phase_timeout, + manager); + + gsm_store_foreach (manager->priv->clients, + (GsmStoreFunc)_client_stop, + NULL); + } else { + end_phase (manager); + } +} + +static gboolean +_client_query_end_session (const char *id, + GsmClient *client, + ClientEndSessionData *data) +{ + gboolean ret; + GError *error; + + error = NULL; + ret = gsm_client_query_end_session (client, data->flags, &error); + if (! ret) { + g_warning ("Unable to query client: %s", error->message); + g_error_free (error); + /* FIXME: what should we do if we can't communicate with client? */ + } else { + g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client)); + data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients, + client); + } + + return FALSE; +} + +static gboolean +inhibitor_has_flag (gpointer key, + GsmInhibitor *inhibitor, + gpointer data) +{ + guint flag; + guint flags; + + flag = GPOINTER_TO_UINT (data); + + flags = gsm_inhibitor_peek_flags (inhibitor); + + return (flags & flag); +} + +static gboolean +gsm_manager_is_logout_inhibited (GsmManager *manager) +{ + GsmInhibitor *inhibitor; + + if (manager->priv->inhibitors == NULL) { + return FALSE; + } + + inhibitor = (GsmInhibitor *)gsm_store_find (manager->priv->inhibitors, + (GsmStoreFunc)inhibitor_has_flag, + GUINT_TO_POINTER (GSM_INHIBITOR_FLAG_LOGOUT)); + if (inhibitor == NULL) { + return FALSE; + } + return TRUE; +} + +static gboolean +gsm_manager_is_idle_inhibited (GsmManager *manager) +{ + GsmInhibitor *inhibitor; + + if (manager->priv->inhibitors == NULL) { + return FALSE; + } + + inhibitor = (GsmInhibitor *)gsm_store_find (manager->priv->inhibitors, + (GsmStoreFunc)inhibitor_has_flag, + GUINT_TO_POINTER (GSM_INHIBITOR_FLAG_IDLE)); + if (inhibitor == NULL) { + return FALSE; + } + return TRUE; +} + +static gboolean +_client_cancel_end_session (const char *id, + GsmClient *client, + GsmManager *manager) +{ + gboolean res; + GError *error; + + error = NULL; + res = gsm_client_cancel_end_session (client, &error); + if (! res) { + g_warning ("Unable to cancel end session: %s", error->message); + g_error_free (error); + } + + return FALSE; +} + +static gboolean +inhibitor_is_jit (gpointer key, + GsmInhibitor *inhibitor, + GsmManager *manager) +{ + gboolean matches; + const char *id; + + id = gsm_inhibitor_peek_client_id (inhibitor); + + matches = (id != NULL && id[0] != '\0'); + + return matches; +} + +static void +cancel_end_session (GsmManager *manager) +{ + /* just ignore if received outside of shutdown */ + if (manager->priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) { + return; + } + + /* switch back to running phase */ + g_debug ("GsmManager: Cancelling the end of session"); + + /* remove the dialog before we remove the inhibitors, else the dialog + * will activate itself automatically when the last inhibitor will be + * removed */ + if (manager->priv->inhibit_dialog) + gtk_widget_destroy (GTK_WIDGET (manager->priv->inhibit_dialog)); + manager->priv->inhibit_dialog = NULL; + + /* clear all JIT inhibitors */ + gsm_store_foreach_remove (manager->priv->inhibitors, + (GsmStoreFunc)inhibitor_is_jit, + (gpointer)manager); + + gsm_store_foreach (manager->priv->clients, + (GsmStoreFunc)_client_cancel_end_session, + NULL); + + gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_RUNNING); + manager->priv->forceful_logout = FALSE; + + manager->priv->logout_type = GSM_MANAGER_LOGOUT_NONE; + mdm_set_logout_action (MDM_LOGOUT_ACTION_NONE); + + start_phase (manager); +} + + +static void +manager_switch_user (GsmManager *manager) +{ + GError *error; + gboolean res; + char *command; + + command = g_strdup_printf ("%s %s", + MDM_FLEXISERVER_COMMAND, + MDM_FLEXISERVER_ARGS); + + error = NULL; + res = gdk_spawn_command_line_on_screen (gdk_screen_get_default (), + command, + &error); + + g_free (command); + + if (! res) { + g_debug ("GsmManager: Unable to start MDM greeter: %s", error->message); + g_error_free (error); + } +} + +static gboolean +sleep_lock_is_enabled (GsmManager *manager) +{ + GError *error; + gboolean enable_lock; + + error = NULL; + enable_lock = mateconf_client_get_bool (manager->priv->mateconf_client, + KEY_SLEEP_LOCK, &error); + + if (error) { + g_warning ("Error retrieving configuration key '%s': %s", + KEY_SLEEP_LOCK, error->message); + g_error_free (error); + + /* If we fail to query mateconf key, just enable locking */ + enable_lock = TRUE; + } + + return enable_lock; +} + +static void +manager_perhaps_lock (GsmManager *manager) +{ + GError *error; + gboolean ret; + + /* only lock if mate-screensaver is set to lock */ + if (!sleep_lock_is_enabled (manager)) { + return; + } + + /* do this sync to ensure it's on the screen when we start suspending */ + error = NULL; + ret = g_spawn_command_line_sync ("mate-screensaver-command --lock", NULL, NULL, NULL, &error); + if (!ret) { + g_warning ("Couldn't lock screen: %s", error->message); + g_error_free (error); + } +} + +static void +manager_attempt_hibernate (GsmManager *manager) +{ + gboolean can_hibernate; + GError *error; + gboolean ret; + + can_hibernate = up_client_get_can_hibernate (manager->priv->up_client); + if (can_hibernate) { + + /* lock the screen before we suspend */ + manager_perhaps_lock (manager); + + error = NULL; + ret = up_client_hibernate_sync (manager->priv->up_client, NULL, &error); + if (!ret) { + g_warning ("Unexpected hibernate failure: %s", + error->message); + g_error_free (error); + } + } +} + +static void +manager_attempt_suspend (GsmManager *manager) +{ + gboolean can_suspend; + GError *error; + gboolean ret; + + can_suspend = up_client_get_can_suspend (manager->priv->up_client); + if (can_suspend) { + + /* lock the screen before we suspend */ + manager_perhaps_lock (manager); + + error = NULL; + ret = up_client_suspend_sync (manager->priv->up_client, NULL, &error); + if (!ret) { + g_warning ("Unexpected suspend failure: %s", + error->message); + g_error_free (error); + } + } +} + +static void +do_inhibit_dialog_action (GsmManager *manager, + int action) +{ + switch (action) { + case GSM_LOGOUT_ACTION_SWITCH_USER: + manager_switch_user (manager); + break; + case GSM_LOGOUT_ACTION_HIBERNATE: + manager_attempt_hibernate (manager); + break; + case GSM_LOGOUT_ACTION_SLEEP: + manager_attempt_suspend (manager); + break; + case GSM_LOGOUT_ACTION_SHUTDOWN: + case GSM_LOGOUT_ACTION_REBOOT: + case GSM_LOGOUT_ACTION_LOGOUT: + manager->priv->forceful_logout = TRUE; + end_phase (manager); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +inhibit_dialog_response (GsmInhibitDialog *dialog, + guint response_id, + GsmManager *manager) +{ + int action; + + g_debug ("GsmManager: Inhibit dialog response: %d", response_id); + + /* must destroy dialog before cancelling since we'll + remove JIT inhibitors and we don't want to trigger + action. */ + g_object_get (dialog, "action", &action, NULL); + gtk_widget_destroy (GTK_WIDGET (dialog)); + manager->priv->inhibit_dialog = NULL; + + /* In case of dialog cancel, switch user, hibernate and + * suspend, we just perform the respective action and return, + * without shutting down the session. */ + switch (response_id) { + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_NONE: + case GTK_RESPONSE_DELETE_EVENT: + if (action == GSM_LOGOUT_ACTION_LOGOUT + || action == GSM_LOGOUT_ACTION_SHUTDOWN + || action == GSM_LOGOUT_ACTION_REBOOT) { + cancel_end_session (manager); + } + break; + case GTK_RESPONSE_ACCEPT: + g_debug ("GsmManager: doing action %d", action); + do_inhibit_dialog_action (manager, action); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +query_end_session_complete (GsmManager *manager) +{ + GsmLogoutAction action; + + g_debug ("GsmManager: query end session complete"); + + /* Remove the timeout since this can be called from outside the timer + * and we don't want to have it called twice */ + if (manager->priv->query_timeout_id > 0) { + g_source_remove (manager->priv->query_timeout_id); + manager->priv->query_timeout_id = 0; + } + + if (! gsm_manager_is_logout_inhibited (manager)) { + end_phase (manager); + return; + } + + if (manager->priv->inhibit_dialog != NULL) { + g_debug ("GsmManager: inhibit dialog already up"); + gtk_window_present (GTK_WINDOW (manager->priv->inhibit_dialog)); + return; + } + + switch (manager->priv->logout_type) { + case GSM_MANAGER_LOGOUT_LOGOUT: + action = GSM_LOGOUT_ACTION_LOGOUT; + break; + case GSM_MANAGER_LOGOUT_REBOOT: + case GSM_MANAGER_LOGOUT_REBOOT_INTERACT: + case GSM_MANAGER_LOGOUT_REBOOT_MDM: + action = GSM_LOGOUT_ACTION_REBOOT; + break; + case GSM_MANAGER_LOGOUT_SHUTDOWN: + case GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT: + case GSM_MANAGER_LOGOUT_SHUTDOWN_MDM: + action = GSM_LOGOUT_ACTION_SHUTDOWN; + break; + default: + g_warning ("Unexpected logout type %d when creating inhibit dialog", + manager->priv->logout_type); + action = GSM_LOGOUT_ACTION_LOGOUT; + break; + } + + /* Note: GSM_LOGOUT_ACTION_SHUTDOWN and GSM_LOGOUT_ACTION_REBOOT are + * actually handled the same way as GSM_LOGOUT_ACTION_LOGOUT in the + * inhibit dialog; the action, if the button is clicked, will be to + * simply go to the next phase. */ + manager->priv->inhibit_dialog = gsm_inhibit_dialog_new (manager->priv->inhibitors, + manager->priv->clients, + action); + + g_signal_connect (manager->priv->inhibit_dialog, + "response", + G_CALLBACK (inhibit_dialog_response), + manager); + gtk_widget_show (manager->priv->inhibit_dialog); + +} + +static guint32 +generate_cookie (void) +{ + guint32 cookie; + + cookie = (guint32)g_random_int_range (1, G_MAXINT32); + + return cookie; +} + +static guint32 +_generate_unique_cookie (GsmManager *manager) +{ + guint32 cookie; + + do { + cookie = generate_cookie (); + } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL); + + return cookie; +} + +static gboolean +_on_query_end_session_timeout (GsmManager *manager) +{ + GSList *l; + + manager->priv->query_timeout_id = 0; + + g_debug ("GsmManager: query end session timed out"); + + for (l = manager->priv->query_clients; l != NULL; l = l->next) { + guint cookie; + GsmInhibitor *inhibitor; + const char *bus_name; + char *app_id; + + g_warning ("Client '%s' failed to reply before timeout", + gsm_client_peek_id (l->data)); + + /* Add JIT inhibit for unresponsive client */ + if (GSM_IS_DBUS_CLIENT (l->data)) { + bus_name = gsm_dbus_client_get_bus_name (l->data); + } else { + bus_name = NULL; + } + + app_id = g_strdup (gsm_client_peek_app_id (l->data)); + if (IS_STRING_EMPTY (app_id)) { + /* XSMP clients don't give us an app id unless we start them */ + g_free (app_id); + app_id = gsm_client_get_app_name (l->data); + } + + cookie = _generate_unique_cookie (manager); + inhibitor = gsm_inhibitor_new_for_client (gsm_client_peek_id (l->data), + app_id, + GSM_INHIBITOR_FLAG_LOGOUT, + _("Not responding"), + bus_name, + cookie); + g_free (app_id); + gsm_store_add (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor)); + g_object_unref (inhibitor); + } + + g_slist_free (manager->priv->query_clients); + manager->priv->query_clients = NULL; + + query_end_session_complete (manager); + + return FALSE; +} + +static void +do_phase_query_end_session (GsmManager *manager) +{ + ClientEndSessionData data; + + data.manager = manager; + data.flags = 0; + + if (manager->priv->forceful_logout) { + data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL; + } + /* We only query if an app is ready to log out, so we don't use + * GSM_CLIENT_END_SESSION_FLAG_SAVE here. + */ + + debug_clients (manager); + g_debug ("GsmManager: sending query-end-session to clients forceful:%d", manager->priv->forceful_logout); + gsm_store_foreach (manager->priv->clients, + (GsmStoreFunc)_client_query_end_session, + &data); + + /* This phase doesn't time out. This separate timer is only used to + * show UI. */ + manager->priv->query_timeout_id = g_timeout_add_seconds (1, (GSourceFunc)_on_query_end_session_timeout, manager); +} + +static void +update_idle (GsmManager *manager) +{ + if (gsm_manager_is_idle_inhibited (manager)) { + gsm_presence_set_idle_enabled (manager->priv->presence, FALSE); + } else { + gsm_presence_set_idle_enabled (manager->priv->presence, TRUE); + } +} + +static void +start_phase (GsmManager *manager) +{ + + g_debug ("GsmManager: starting phase %s\n", + phase_num_to_name (manager->priv->phase)); + + /* reset state */ + g_slist_free (manager->priv->pending_apps); + manager->priv->pending_apps = NULL; + g_slist_free (manager->priv->query_clients); + manager->priv->query_clients = NULL; + g_slist_free (manager->priv->next_query_clients); + manager->priv->next_query_clients = NULL; + + if (manager->priv->query_timeout_id > 0) { + g_source_remove (manager->priv->query_timeout_id); + manager->priv->query_timeout_id = 0; + } + if (manager->priv->phase_timeout_id > 0) { + g_source_remove (manager->priv->phase_timeout_id); + manager->priv->phase_timeout_id = 0; + } + + switch (manager->priv->phase) { + case GSM_MANAGER_PHASE_STARTUP: + case GSM_MANAGER_PHASE_INITIALIZATION: + case GSM_MANAGER_PHASE_WINDOW_MANAGER: + case GSM_MANAGER_PHASE_PANEL: + case GSM_MANAGER_PHASE_DESKTOP: + case GSM_MANAGER_PHASE_APPLICATION: + do_phase_startup (manager); + break; + case GSM_MANAGER_PHASE_RUNNING: + g_signal_emit (manager, signals[SESSION_RUNNING], 0); + update_idle (manager); + break; + case GSM_MANAGER_PHASE_QUERY_END_SESSION: + do_phase_query_end_session (manager); + break; + case GSM_MANAGER_PHASE_END_SESSION: + do_phase_end_session (manager); + break; + case GSM_MANAGER_PHASE_EXIT: + do_phase_exit (manager); + break; + default: + g_assert_not_reached (); + break; + } +} + +static gboolean +_debug_app_for_phase (const char *id, + GsmApp *app, + gpointer data) +{ + guint phase; + + phase = GPOINTER_TO_UINT (data); + + if (gsm_app_peek_phase (app) != phase) { + return FALSE; + } + + g_debug ("GsmManager:\tID: %s\tapp-id:%s\tis-disabled:%d\tis-conditionally-disabled:%d", + gsm_app_peek_id (app), + gsm_app_peek_app_id (app), + gsm_app_peek_is_disabled (app), + gsm_app_peek_is_conditionally_disabled (app)); + + return FALSE; +} + +static void +debug_app_summary (GsmManager *manager) +{ + guint phase; + + g_debug ("GsmManager: App startup summary"); + for (phase = GSM_MANAGER_PHASE_INITIALIZATION; phase < GSM_MANAGER_PHASE_RUNNING; phase++) { + g_debug ("GsmManager: Phase %s", phase_num_to_name (phase)); + gsm_store_foreach (manager->priv->apps, + (GsmStoreFunc)_debug_app_for_phase, + GUINT_TO_POINTER (phase)); + } +} + +void +gsm_manager_start (GsmManager *manager) +{ + g_debug ("GsmManager: GSM starting to manage"); + + g_return_if_fail (GSM_IS_MANAGER (manager)); + + gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_INITIALIZATION); + debug_app_summary (manager); + start_phase (manager); +} + +static gboolean +_app_has_app_id (const char *id, + GsmApp *app, + const char *app_id_a) +{ + const char *app_id_b; + + app_id_b = gsm_app_peek_app_id (app); + return (app_id_b != NULL && strcmp (app_id_a, app_id_b) == 0); +} + +static GsmApp * +find_app_for_app_id (GsmManager *manager, + const char *app_id) +{ + GsmApp *app; + app = (GsmApp *)gsm_store_find (manager->priv->apps, + (GsmStoreFunc)_app_has_app_id, + (char *)app_id); + return app; +} + +static gboolean +inhibitor_has_client_id (gpointer key, + GsmInhibitor *inhibitor, + const char *client_id_a) +{ + gboolean matches; + const char *client_id_b; + + client_id_b = gsm_inhibitor_peek_client_id (inhibitor); + + matches = FALSE; + if (! IS_STRING_EMPTY (client_id_a) && ! IS_STRING_EMPTY (client_id_b)) { + matches = (strcmp (client_id_a, client_id_b) == 0); + if (matches) { + g_debug ("GsmManager: removing JIT inhibitor for %s for reason '%s'", + gsm_inhibitor_peek_client_id (inhibitor), + gsm_inhibitor_peek_reason (inhibitor)); + } + } + + return matches; +} + +static gboolean +_app_has_startup_id (const char *id, + GsmApp *app, + const char *startup_id_a) +{ + const char *startup_id_b; + + startup_id_b = gsm_app_peek_startup_id (app); + + if (IS_STRING_EMPTY (startup_id_b)) { + return FALSE; + } + + return (strcmp (startup_id_a, startup_id_b) == 0); +} + +static GsmApp * +find_app_for_startup_id (GsmManager *manager, + const char *startup_id) +{ + GsmApp *found_app; + GSList *a; + + found_app = NULL; + + /* If we're starting up the session, try to match the new client + * with one pending apps for the current phase. If not, try to match + * with any of the autostarted apps. */ + if (manager->priv->phase < GSM_MANAGER_PHASE_APPLICATION) { + for (a = manager->priv->pending_apps; a != NULL; a = a->next) { + GsmApp *app = GSM_APP (a->data); + + if (strcmp (startup_id, gsm_app_peek_startup_id (app)) == 0) { + found_app = app; + goto out; + } + } + } else { + GsmApp *app; + + app = (GsmApp *)gsm_store_find (manager->priv->apps, + (GsmStoreFunc)_app_has_startup_id, + (char *)startup_id); + if (app != NULL) { + found_app = app; + goto out; + } + } + out: + return found_app; +} + +static void +_disconnect_client (GsmManager *manager, + GsmClient *client) +{ + gboolean is_condition_client; + GsmApp *app; + GError *error; + gboolean res; + const char *app_id; + const char *startup_id; + gboolean app_restart; + GsmClientRestartStyle client_restart_hint; + + g_debug ("GsmManager: disconnect client: %s", gsm_client_peek_id (client)); + + /* take a ref so it doesn't get finalized */ + g_object_ref (client); + + gsm_client_set_status (client, GSM_CLIENT_FINISHED); + + is_condition_client = FALSE; + if (g_slist_find (manager->priv->condition_clients, client)) { + manager->priv->condition_clients = g_slist_remove (manager->priv->condition_clients, client); + + is_condition_client = TRUE; + } + + /* remove any inhibitors for this client */ + gsm_store_foreach_remove (manager->priv->inhibitors, + (GsmStoreFunc)inhibitor_has_client_id, + (gpointer)gsm_client_peek_id (client)); + + app = NULL; + + /* first try to match on startup ID */ + startup_id = gsm_client_peek_startup_id (client); + if (! IS_STRING_EMPTY (startup_id)) { + app = find_app_for_startup_id (manager, startup_id); + + } + + /* then try to find matching app-id */ + if (app == NULL) { + app_id = gsm_client_peek_app_id (client); + if (! IS_STRING_EMPTY (app_id)) { + g_debug ("GsmManager: disconnect for app '%s'", app_id); + app = find_app_for_app_id (manager, app_id); + } + } + + if (app == NULL) { + g_debug ("GsmManager: unable to find application for client - not restarting"); + goto out; + } + + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) { + g_debug ("GsmManager: in shutdown, not restarting application"); + goto out; + } + + app_restart = gsm_app_peek_autorestart (app); + client_restart_hint = gsm_client_peek_restart_style_hint (client); + + /* allow legacy clients to override the app info */ + if (! app_restart + && client_restart_hint != GSM_CLIENT_RESTART_IMMEDIATELY) { + g_debug ("GsmManager: autorestart not set, not restarting application"); + goto out; + } + + if (is_condition_client) { + g_debug ("GsmManager: app conditionally disabled, not restarting application"); + goto out; + } + + g_debug ("GsmManager: restarting app"); + + error = NULL; + res = gsm_app_restart (app, &error); + if (error != NULL) { + g_warning ("Error on restarting session managed app: %s", error->message); + g_error_free (error); + } + + out: + g_object_unref (client); +} + +typedef struct { + const char *service_name; + GsmManager *manager; +} RemoveClientData; + +static gboolean +_disconnect_dbus_client (const char *id, + GsmClient *client, + RemoveClientData *data) +{ + const char *name; + + if (! GSM_IS_DBUS_CLIENT (client)) { + return FALSE; + } + + name = gsm_dbus_client_get_bus_name (GSM_DBUS_CLIENT (client)); + if (IS_STRING_EMPTY (name)) { + return FALSE; + } + + if (strcmp (data->service_name, name) == 0) { + _disconnect_client (data->manager, client); + return TRUE; + } + + return FALSE; +} + +static void +remove_clients_for_connection (GsmManager *manager, + const char *service_name) +{ + RemoveClientData data; + + data.service_name = service_name; + data.manager = manager; + + /* disconnect dbus clients for name */ + gsm_store_foreach_remove (manager->priv->clients, + (GsmStoreFunc)_disconnect_dbus_client, + &data); + + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION + && gsm_store_size (manager->priv->clients) == 0) { + g_debug ("GsmManager: last client disconnected - exiting"); + end_phase (manager); + } +} + +static gboolean +inhibitor_has_bus_name (gpointer key, + GsmInhibitor *inhibitor, + RemoveClientData *data) +{ + gboolean matches; + const char *bus_name_b; + + bus_name_b = gsm_inhibitor_peek_bus_name (inhibitor); + + matches = FALSE; + if (! IS_STRING_EMPTY (data->service_name) && ! IS_STRING_EMPTY (bus_name_b)) { + matches = (strcmp (data->service_name, bus_name_b) == 0); + if (matches) { + g_debug ("GsmManager: removing inhibitor from %s for reason '%s' on connection %s", + gsm_inhibitor_peek_app_id (inhibitor), + gsm_inhibitor_peek_reason (inhibitor), + gsm_inhibitor_peek_bus_name (inhibitor)); + } + } + + return matches; +} + +static void +remove_inhibitors_for_connection (GsmManager *manager, + const char *service_name) +{ + guint n_removed; + RemoveClientData data; + + data.service_name = service_name; + data.manager = manager; + + debug_inhibitors (manager); + + n_removed = gsm_store_foreach_remove (manager->priv->inhibitors, + (GsmStoreFunc)inhibitor_has_bus_name, + &data); +} + +static void +bus_name_owner_changed (DBusGProxy *bus_proxy, + const char *service_name, + const char *old_service_name, + const char *new_service_name, + GsmManager *manager) +{ + if (strlen (new_service_name) == 0 + && strlen (old_service_name) > 0) { + /* service removed */ + remove_inhibitors_for_connection (manager, old_service_name); + remove_clients_for_connection (manager, old_service_name); + } else if (strlen (old_service_name) == 0 + && strlen (new_service_name) > 0) { + /* service added */ + + /* use this if we support automatically registering + * well known bus names */ + } +} + +static gboolean +register_manager (GsmManager *manager) +{ + GError *error = NULL; + + error = NULL; + manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (manager->priv->connection == NULL) { + if (error != NULL) { + g_critical ("error getting session bus: %s", error->message); + g_error_free (error); + } + exit (1); + } + + manager->priv->bus_proxy = dbus_g_proxy_new_for_name (manager->priv->connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + dbus_g_proxy_add_signal (manager->priv->bus_proxy, + "NameOwnerChanged", + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (manager->priv->bus_proxy, + "NameOwnerChanged", + G_CALLBACK (bus_name_owner_changed), + manager, + NULL); + + dbus_g_connection_register_g_object (manager->priv->connection, GSM_MANAGER_DBUS_PATH, G_OBJECT (manager)); + + return TRUE; +} + +static void +gsm_manager_set_failsafe (GsmManager *manager, + gboolean enabled) +{ + g_return_if_fail (GSM_IS_MANAGER (manager)); + + manager->priv->failsafe = enabled; +} + +static gboolean +_client_has_startup_id (const char *id, + GsmClient *client, + const char *startup_id_a) +{ + const char *startup_id_b; + + startup_id_b = gsm_client_peek_startup_id (client); + + if (IS_STRING_EMPTY (startup_id_b)) { + return FALSE; + } + + return (strcmp (startup_id_a, startup_id_b) == 0); +} + +static void +on_client_disconnected (GsmClient *client, + GsmManager *manager) +{ + g_debug ("GsmManager: disconnect client"); + _disconnect_client (manager, client); + gsm_store_remove (manager->priv->clients, gsm_client_peek_id (client)); + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION + && gsm_store_size (manager->priv->clients) == 0) { + g_debug ("GsmManager: last client disconnected - exiting"); + end_phase (manager); + } +} + +static gboolean +on_xsmp_client_register_request (GsmXSMPClient *client, + char **id, + GsmManager *manager) +{ + gboolean handled; + char *new_id; + GsmApp *app; + + handled = TRUE; + new_id = NULL; + + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) { + goto out; + } + + if (IS_STRING_EMPTY (*id)) { + new_id = gsm_util_generate_startup_id (); + } else { + GsmClient *client; + + client = (GsmClient *)gsm_store_find (manager->priv->clients, + (GsmStoreFunc)_client_has_startup_id, + *id); + /* We can't have two clients with the same id. */ + if (client != NULL) { + goto out; + } + + new_id = g_strdup (*id); + } + + g_debug ("GsmManager: Adding new client %s to session", new_id); + + g_signal_connect (client, + "disconnected", + G_CALLBACK (on_client_disconnected), + manager); + + /* If it's a brand new client id, we just accept the client*/ + if (IS_STRING_EMPTY (*id)) { + goto out; + } + + app = find_app_for_startup_id (manager, new_id); + if (app != NULL) { + gsm_client_set_app_id (GSM_CLIENT (client), gsm_app_peek_app_id (app)); + gsm_app_registered (app); + goto out; + } + + /* app not found */ + g_free (new_id); + new_id = NULL; + + out: + g_free (*id); + *id = new_id; + + return handled; +} + +static gboolean +auto_save_is_enabled (GsmManager *manager) +{ + GError *error; + gboolean auto_save; + + error = NULL; + auto_save = mateconf_client_get_bool (manager->priv->mateconf_client, + KEY_AUTOSAVE, + &error); + + if (error) { + g_warning ("Error retrieving configuration key '%s': %s", + KEY_AUTOSAVE, + error->message); + g_error_free (error); + + /* If we fail to query mateconf key, disable auto save */ + auto_save = FALSE; + } + + return auto_save; +} + +static void +maybe_save_session (GsmManager *manager) +{ + GsmConsolekit *consolekit; + char *session_type; + GError *error; + + consolekit = gsm_get_consolekit (); + session_type = gsm_consolekit_get_current_session_type (consolekit); + + if (g_strcmp0 (session_type, GSM_CONSOLEKIT_SESSION_TYPE_LOGIN_WINDOW) == 0) { + goto out; + } + + /* We only allow session saving when session is running or when + * logging out */ + if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING && + manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) { + goto out; + } + + error = NULL; + gsm_session_save (manager->priv->clients, &error); + + if (error) { + g_warning ("Error saving session: %s", error->message); + g_error_free (error); + } + +out: + g_object_unref (consolekit); + g_free (session_type); +} + +static void +on_client_end_session_response (GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason, + GsmManager *manager) +{ + /* just ignore if received outside of shutdown */ + if (manager->priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) { + return; + } + + g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :""); + + if (cancel) { + cancel_end_session (manager); + return; + } + + manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client); + + if (! is_ok) { + guint cookie; + GsmInhibitor *inhibitor; + char *app_id; + const char *bus_name; + + /* FIXME: do we support updating the reason? */ + + /* Create JIT inhibit */ + if (GSM_IS_DBUS_CLIENT (client)) { + bus_name = gsm_dbus_client_get_bus_name (GSM_DBUS_CLIENT (client)); + } else { + bus_name = NULL; + } + + app_id = g_strdup (gsm_client_peek_app_id (client)); + if (IS_STRING_EMPTY (app_id)) { + /* XSMP clients don't give us an app id unless we start them */ + g_free (app_id); + app_id = gsm_client_get_app_name (client); + } + + cookie = _generate_unique_cookie (manager); + inhibitor = gsm_inhibitor_new_for_client (gsm_client_peek_id (client), + app_id, + GSM_INHIBITOR_FLAG_LOGOUT, + reason != NULL ? reason : _("Not responding"), + bus_name, + cookie); + g_free (app_id); + gsm_store_add (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor)); + g_object_unref (inhibitor); + } else { + gsm_store_foreach_remove (manager->priv->inhibitors, + (GsmStoreFunc)inhibitor_has_client_id, + (gpointer)gsm_client_peek_id (client)); + } + + if (manager->priv->phase == GSM_MANAGER_PHASE_QUERY_END_SESSION) { + if (manager->priv->query_clients == NULL) { + query_end_session_complete (manager); + } + } else if (manager->priv->phase == GSM_MANAGER_PHASE_END_SESSION) { + if (do_last) { + /* This only makes sense if we're in part 1 of + * GSM_MANAGER_PHASE_END_SESSION. Doing this in part 2 + * can only happen because of a buggy client that loops + * wanting to be last again and again. The phase + * timeout will take care of this issue. */ + manager->priv->next_query_clients = g_slist_prepend (manager->priv->next_query_clients, + client); + } + + /* we can continue to the next step if all clients have replied + * and if there's no inhibitor */ + if (manager->priv->query_clients != NULL + || gsm_manager_is_logout_inhibited (manager)) { + return; + } + + if (manager->priv->next_query_clients != NULL) { + do_phase_end_session_part_2 (manager); + } else { + end_phase (manager); + } + } +} + +static void +on_xsmp_client_logout_request (GsmXSMPClient *client, + gboolean show_dialog, + GsmManager *manager) +{ + GError *error; + int logout_mode; + + if (show_dialog) { + logout_mode = GSM_MANAGER_LOGOUT_MODE_NORMAL; + } else { + logout_mode = GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION; + } + + error = NULL; + gsm_manager_logout (manager, logout_mode, &error); + if (error != NULL) { + g_warning ("Unable to logout: %s", error->message); + g_error_free (error); + } +} + +static void +on_store_client_added (GsmStore *store, + const char *id, + GsmManager *manager) +{ + GsmClient *client; + + g_debug ("GsmManager: Client added: %s", id); + + client = (GsmClient *)gsm_store_lookup (store, id); + + /* a bit hacky */ + if (GSM_IS_XSMP_CLIENT (client)) { + g_signal_connect (client, + "register-request", + G_CALLBACK (on_xsmp_client_register_request), + manager); + g_signal_connect (client, + "logout-request", + G_CALLBACK (on_xsmp_client_logout_request), + manager); + } + + g_signal_connect (client, + "end-session-response", + G_CALLBACK (on_client_end_session_response), + manager); + + g_signal_emit (manager, signals [CLIENT_ADDED], 0, id); + /* FIXME: disconnect signal handler */ +} + +static void +on_store_client_removed (GsmStore *store, + const char *id, + GsmManager *manager) +{ + g_debug ("GsmManager: Client removed: %s", id); + + g_signal_emit (manager, signals [CLIENT_REMOVED], 0, id); +} + +static void +gsm_manager_set_client_store (GsmManager *manager, + GsmStore *store) +{ + g_return_if_fail (GSM_IS_MANAGER (manager)); + + if (store != NULL) { + g_object_ref (store); + } + + if (manager->priv->clients != NULL) { + g_signal_handlers_disconnect_by_func (manager->priv->clients, + on_store_client_added, + manager); + g_signal_handlers_disconnect_by_func (manager->priv->clients, + on_store_client_removed, + manager); + + g_object_unref (manager->priv->clients); + } + + + g_debug ("GsmManager: setting client store %p", store); + + manager->priv->clients = store; + + if (manager->priv->clients != NULL) { + g_signal_connect (manager->priv->clients, + "added", + G_CALLBACK (on_store_client_added), + manager); + g_signal_connect (manager->priv->clients, + "removed", + G_CALLBACK (on_store_client_removed), + manager); + } +} + +static void +gsm_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmManager *self; + + self = GSM_MANAGER (object); + + switch (prop_id) { + case PROP_FAILSAFE: + gsm_manager_set_failsafe (self, g_value_get_boolean (value)); + break; + case PROP_CLIENT_STORE: + gsm_manager_set_client_store (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmManager *self; + + self = GSM_MANAGER (object); + + switch (prop_id) { + case PROP_FAILSAFE: + g_value_set_boolean (value, self->priv->failsafe); + break; + case PROP_CLIENT_STORE: + g_value_set_object (value, self->priv->clients); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +_find_app_provides (const char *id, + GsmApp *app, + const char *service) +{ + return gsm_app_provides (app, service); +} + +static GObject * +gsm_manager_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmManager *manager; + + manager = GSM_MANAGER (G_OBJECT_CLASS (gsm_manager_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + return G_OBJECT (manager); +} + +static void +on_store_inhibitor_added (GsmStore *store, + const char *id, + GsmManager *manager) +{ + g_debug ("GsmManager: Inhibitor added: %s", id); + g_signal_emit (manager, signals [INHIBITOR_ADDED], 0, id); + update_idle (manager); +} + +static void +on_store_inhibitor_removed (GsmStore *store, + const char *id, + GsmManager *manager) +{ + g_debug ("GsmManager: Inhibitor removed: %s", id); + g_signal_emit (manager, signals [INHIBITOR_REMOVED], 0, id); + update_idle (manager); +} + +static void +gsm_manager_dispose (GObject *object) +{ + GsmManager *manager = GSM_MANAGER (object); + + g_debug ("GsmManager: disposing manager"); + + if (manager->priv->clients != NULL) { + g_signal_handlers_disconnect_by_func (manager->priv->clients, + on_store_client_added, + manager); + g_signal_handlers_disconnect_by_func (manager->priv->clients, + on_store_client_removed, + manager); + g_object_unref (manager->priv->clients); + manager->priv->clients = NULL; + } + + if (manager->priv->apps != NULL) { + g_object_unref (manager->priv->apps); + manager->priv->apps = NULL; + } + + if (manager->priv->inhibitors != NULL) { + g_signal_handlers_disconnect_by_func (manager->priv->inhibitors, + on_store_inhibitor_added, + manager); + g_signal_handlers_disconnect_by_func (manager->priv->inhibitors, + on_store_inhibitor_removed, + manager); + + g_object_unref (manager->priv->inhibitors); + manager->priv->inhibitors = NULL; + } + + if (manager->priv->presence != NULL) { + g_object_unref (manager->priv->presence); + manager->priv->presence = NULL; + } + + if (manager->priv->mateconf_client) { + if (manager->priv->desktop_notify_id != 0) { + mateconf_client_remove_dir (manager->priv->mateconf_client, + KEY_DESKTOP_DIR, + NULL); + mateconf_client_notify_remove (manager->priv->mateconf_client, + manager->priv->desktop_notify_id); + manager->priv->desktop_notify_id = 0; + } + if (manager->priv->lockdown_notify_id != 0) { + mateconf_client_remove_dir (manager->priv->mateconf_client, + KEY_LOCKDOWN_DIR, + NULL); + mateconf_client_notify_remove (manager->priv->mateconf_client, + manager->priv->lockdown_notify_id); + manager->priv->lockdown_notify_id = 0; + } + + g_object_unref (manager->priv->mateconf_client); + manager->priv->mateconf_client = NULL; + } + + if (manager->priv->up_client != NULL) { + g_object_unref (manager->priv->up_client); + manager->priv->up_client = NULL; + } + + G_OBJECT_CLASS (gsm_manager_parent_class)->dispose (object); +} + +static void +gsm_manager_class_init (GsmManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gsm_manager_get_property; + object_class->set_property = gsm_manager_set_property; + object_class->constructor = gsm_manager_constructor; + object_class->finalize = gsm_manager_finalize; + object_class->dispose = gsm_manager_dispose; + + signals [PHASE_CHANGED] = + g_signal_new ("phase-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmManagerClass, phase_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, G_TYPE_STRING); + + signals [SESSION_RUNNING] = + g_signal_new ("session-running", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmManagerClass, session_running), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + signals [SESSION_OVER] = + g_signal_new ("session-over", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmManagerClass, session_over), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals [CLIENT_ADDED] = + g_signal_new ("client-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmManagerClass, client_added), + NULL, + NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, + 1, DBUS_TYPE_G_OBJECT_PATH); + signals [CLIENT_REMOVED] = + g_signal_new ("client-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmManagerClass, client_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, + 1, DBUS_TYPE_G_OBJECT_PATH); + signals [INHIBITOR_ADDED] = + g_signal_new ("inhibitor-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmManagerClass, inhibitor_added), + NULL, + NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, + 1, DBUS_TYPE_G_OBJECT_PATH); + signals [INHIBITOR_REMOVED] = + g_signal_new ("inhibitor-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmManagerClass, inhibitor_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, + 1, DBUS_TYPE_G_OBJECT_PATH); + + g_object_class_install_property (object_class, + PROP_FAILSAFE, + g_param_spec_boolean ("failsafe", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_CLIENT_STORE, + g_param_spec_object ("client-store", + NULL, + NULL, + GSM_TYPE_STORE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GsmManagerPrivate)); + + dbus_g_object_type_install_info (GSM_TYPE_MANAGER, &dbus_glib_gsm_manager_object_info); + dbus_g_error_domain_register (GSM_MANAGER_ERROR, NULL, GSM_MANAGER_TYPE_ERROR); +} + +static void +invalid_type_warning (const char *type) +{ + g_warning ("Error retrieving configuration key '%s': Invalid type", + type); +} + +static void +load_idle_delay_from_mateconf (GsmManager *manager) +{ + GError *error; + glong value; + + error = NULL; + value = mateconf_client_get_int (manager->priv->mateconf_client, + KEY_IDLE_DELAY, + &error); + if (error == NULL) { + gsm_presence_set_idle_timeout (manager->priv->presence, value * 60000); + } else { + g_warning ("Error retrieving configuration key '%s': %s", + KEY_IDLE_DELAY, + error->message); + g_error_free (error); + } + +} + +static void +on_mateconf_key_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + GsmManager *manager) +{ + const char *key; + MateConfValue *value; + + key = mateconf_entry_get_key (entry); + + if (! g_str_has_prefix (key, KEY_DESKTOP_DIR) + && ! g_str_has_prefix (key, KEY_LOCKDOWN_DIR)) { + return; + } + + value = mateconf_entry_get_value (entry); + + if (strcmp (key, KEY_IDLE_DELAY) == 0) { + if (value->type == MATECONF_VALUE_INT) { + int delay; + + delay = mateconf_value_get_int (value); + + gsm_presence_set_idle_timeout (manager->priv->presence, delay * 60000); + } else { + invalid_type_warning (key); + } + } else if (strcmp (key, KEY_LOCK_DISABLE) == 0) { + if (value->type == MATECONF_VALUE_BOOL) { + gboolean disabled; + + disabled = mateconf_value_get_bool (value); + + /* FIXME: handle this */ + } else { + invalid_type_warning (key); + } + } else if (strcmp (key, KEY_USER_SWITCH_DISABLE) == 0) { + + if (value->type == MATECONF_VALUE_BOOL) { + gboolean disabled; + + disabled = mateconf_value_get_bool (value); + + /* FIXME: handle this */ + } else { + invalid_type_warning (key); + } + + } else { + g_debug ("Config key not handled: %s", key); + } +} + +static void +on_presence_status_changed (GsmPresence *presence, + guint status, + GsmManager *manager) +{ + GsmConsolekit *consolekit; + + consolekit = gsm_get_consolekit (); + gsm_consolekit_set_session_idle (consolekit, + (status == GSM_PRESENCE_STATUS_IDLE)); +} + +static void +gsm_manager_init (GsmManager *manager) +{ + + manager->priv = GSM_MANAGER_GET_PRIVATE (manager); + + manager->priv->mateconf_client = mateconf_client_get_default (); + + manager->priv->inhibitors = gsm_store_new (); + g_signal_connect (manager->priv->inhibitors, + "added", + G_CALLBACK (on_store_inhibitor_added), + manager); + g_signal_connect (manager->priv->inhibitors, + "removed", + G_CALLBACK (on_store_inhibitor_removed), + manager); + + manager->priv->apps = gsm_store_new (); + + manager->priv->presence = gsm_presence_new (); + g_signal_connect (manager->priv->presence, + "status-changed", + G_CALLBACK (on_presence_status_changed), + manager); + + manager->priv->up_client = up_client_new (); + + /* MateConf setup */ + mateconf_client_add_dir (manager->priv->mateconf_client, + KEY_DESKTOP_DIR, + MATECONF_CLIENT_PRELOAD_RECURSIVE, NULL); + mateconf_client_add_dir (manager->priv->mateconf_client, + KEY_LOCKDOWN_DIR, + MATECONF_CLIENT_PRELOAD_NONE, NULL); + + manager->priv->desktop_notify_id = mateconf_client_notify_add (manager->priv->mateconf_client, + KEY_DESKTOP_DIR, + (MateConfClientNotifyFunc)on_mateconf_key_changed, + manager, + NULL, NULL); + manager->priv->lockdown_notify_id = mateconf_client_notify_add (manager->priv->mateconf_client, + KEY_LOCKDOWN_DIR, + (MateConfClientNotifyFunc)on_mateconf_key_changed, + manager, + NULL, NULL); + + load_idle_delay_from_mateconf (manager); +} + +static void +gsm_manager_finalize (GObject *object) +{ + GsmManager *manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_MANAGER (object)); + + manager = GSM_MANAGER (object); + + g_return_if_fail (manager->priv != NULL); + + G_OBJECT_CLASS (gsm_manager_parent_class)->finalize (object); +} + +GsmManager * +gsm_manager_new (GsmStore *client_store, + gboolean failsafe) +{ + if (manager_object != NULL) { + g_object_ref (manager_object); + } else { + gboolean res; + + manager_object = g_object_new (GSM_TYPE_MANAGER, + "client-store", client_store, + "failsafe", failsafe, + NULL); + + g_object_add_weak_pointer (manager_object, + (gpointer *) &manager_object); + res = register_manager (manager_object); + if (! res) { + g_object_unref (manager_object); + return NULL; + } + } + + return GSM_MANAGER (manager_object); +} + +gboolean +gsm_manager_setenv (GsmManager *manager, + const char *variable, + const char *value, + GError **error) +{ + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + if (manager->priv->phase > GSM_MANAGER_PHASE_INITIALIZATION) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION, + "Setenv interface is only available during the Initialization phase"); + return FALSE; + } + + gsm_util_setenv (variable, value); + + return TRUE; +} + +gboolean +gsm_manager_initialization_error (GsmManager *manager, + const char *message, + gboolean fatal, + GError **error) +{ + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + if (manager->priv->phase > GSM_MANAGER_PHASE_INITIALIZATION) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION, + "InitializationError interface is only available during the Initialization phase"); + return FALSE; + } + + gsm_util_init_error (fatal, "%s", message); + + return TRUE; +} + +static gboolean +gsm_manager_is_switch_user_inhibited (GsmManager *manager) +{ + GsmInhibitor *inhibitor; + + if (manager->priv->inhibitors == NULL) { + return FALSE; + } + + inhibitor = (GsmInhibitor *)gsm_store_find (manager->priv->inhibitors, + (GsmStoreFunc)inhibitor_has_flag, + GUINT_TO_POINTER (GSM_INHIBITOR_FLAG_SWITCH_USER)); + if (inhibitor == NULL) { + return FALSE; + } + return TRUE; +} + +static gboolean +gsm_manager_is_suspend_inhibited (GsmManager *manager) +{ + GsmInhibitor *inhibitor; + + if (manager->priv->inhibitors == NULL) { + return FALSE; + } + + inhibitor = (GsmInhibitor *)gsm_store_find (manager->priv->inhibitors, + (GsmStoreFunc)inhibitor_has_flag, + GUINT_TO_POINTER (GSM_INHIBITOR_FLAG_SUSPEND)); + if (inhibitor == NULL) { + return FALSE; + } + return TRUE; +} + +static void +request_reboot_privileges_completed (GsmConsolekit *consolekit, + gboolean success, + gboolean ask_later, + GError *error, + GsmManager *manager) +{ + /* make sure we disconnect the signal handler so that it's not called + * again next time the event is fired -- this can happen if the reboot + * is cancelled. */ + g_signal_handlers_disconnect_by_func (consolekit, + request_reboot_privileges_completed, + manager); + + g_object_unref (consolekit); + + if (success) { + if (ask_later) { + manager->priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT_INTERACT; + } else { + manager->priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT; + } + + end_phase (manager); + } +} + +static void +request_reboot (GsmManager *manager) +{ + GsmConsolekit *consolekit; + gboolean success; + + g_debug ("GsmManager: requesting reboot"); + + /* We request the privileges before doing anything. There are a few + * different cases here: + * + * - no ConsoleKit: we fallback on MDM + * - no password required: everything is fine + * - password asked once: we ask for it now. If the user enters it + * fine, then all is great. If the user doesn't enter it fine, we + * don't do anything (so no logout). + * - password asked each time: we don't ask it for now since we don't + * want to ask for it twice. Instead we'll ask for it at the very + * end. If the user will enter it fine, then all is great again. If + * the user doesn't enter it fine, then we'll just fallback to MDM. + * + * The last case (password asked each time) is a bit broken, but + * there's really nothing we can do about it. Generally speaking, + * though, the password will only be asked once (unless the system is + * configured in paranoid mode), and most probably only if there are + * more than one user connected. So the general case is that it will + * just work fine. + */ + + consolekit = gsm_get_consolekit (); + g_signal_connect (consolekit, + "privileges-completed", + G_CALLBACK (request_reboot_privileges_completed), + manager); + success = gsm_consolekit_get_restart_privileges (consolekit); + + if (!success) { + g_signal_handlers_disconnect_by_func (consolekit, + request_reboot_privileges_completed, + manager); + g_object_unref (consolekit); + + manager->priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT_MDM; + end_phase (manager); + } +} + +static void +request_shutdown_privileges_completed (GsmConsolekit *consolekit, + gboolean success, + gboolean ask_later, + GError *error, + GsmManager *manager) +{ + /* make sure we disconnect the signal handler so that it's not called + * again next time the event is fired -- this can happen if the reboot + * is cancelled. */ + g_signal_handlers_disconnect_by_func (consolekit, + request_shutdown_privileges_completed, + manager); + + g_object_unref (consolekit); + + if (success) { + if (ask_later) { + manager->priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT; + } else { + manager->priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN; + } + + end_phase (manager); + } +} + +static void +request_shutdown (GsmManager *manager) +{ + GsmConsolekit *consolekit; + gboolean success; + + g_debug ("GsmManager: requesting shutdown"); + + /* See the comment in request_reboot() for some more details about how + * this works. */ + + consolekit = gsm_get_consolekit (); + g_signal_connect (consolekit, + "privileges-completed", + G_CALLBACK (request_shutdown_privileges_completed), + manager); + success = gsm_consolekit_get_stop_privileges (consolekit); + + if (!success) { + g_signal_handlers_disconnect_by_func (consolekit, + request_shutdown_privileges_completed, + manager); + g_object_unref (consolekit); + + manager->priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN_MDM; + end_phase (manager); + } +} + +static void +request_suspend (GsmManager *manager) +{ + g_debug ("GsmManager: requesting suspend"); + + if (! gsm_manager_is_suspend_inhibited (manager)) { + manager_attempt_suspend (manager); + return; + } + + if (manager->priv->inhibit_dialog != NULL) { + g_debug ("GsmManager: inhibit dialog already up"); + gtk_window_present (GTK_WINDOW (manager->priv->inhibit_dialog)); + return; + } + + manager->priv->inhibit_dialog = gsm_inhibit_dialog_new (manager->priv->inhibitors, + manager->priv->clients, + GSM_LOGOUT_ACTION_SLEEP); + + g_signal_connect (manager->priv->inhibit_dialog, + "response", + G_CALLBACK (inhibit_dialog_response), + manager); + gtk_widget_show (manager->priv->inhibit_dialog); +} + +static void +request_hibernate (GsmManager *manager) +{ + g_debug ("GsmManager: requesting hibernate"); + + /* hibernate uses suspend inhibit */ + if (! gsm_manager_is_suspend_inhibited (manager)) { + manager_attempt_hibernate (manager); + return; + } + + if (manager->priv->inhibit_dialog != NULL) { + g_debug ("GsmManager: inhibit dialog already up"); + gtk_window_present (GTK_WINDOW (manager->priv->inhibit_dialog)); + return; + } + + manager->priv->inhibit_dialog = gsm_inhibit_dialog_new (manager->priv->inhibitors, + manager->priv->clients, + GSM_LOGOUT_ACTION_HIBERNATE); + + g_signal_connect (manager->priv->inhibit_dialog, + "response", + G_CALLBACK (inhibit_dialog_response), + manager); + gtk_widget_show (manager->priv->inhibit_dialog); +} + + +static void +request_logout (GsmManager *manager, + gboolean forceful_logout) +{ + g_debug ("GsmManager: requesting logout"); + + manager->priv->forceful_logout = forceful_logout; + manager->priv->logout_type = GSM_MANAGER_LOGOUT_LOGOUT; + + end_phase (manager); +} + +static void +request_switch_user (GsmManager *manager) +{ + g_debug ("GsmManager: requesting user switch"); + + if (! gsm_manager_is_switch_user_inhibited (manager)) { + manager_switch_user (manager); + return; + } + + if (manager->priv->inhibit_dialog != NULL) { + g_debug ("GsmManager: inhibit dialog already up"); + gtk_window_present (GTK_WINDOW (manager->priv->inhibit_dialog)); + return; + } + + manager->priv->inhibit_dialog = gsm_inhibit_dialog_new (manager->priv->inhibitors, + manager->priv->clients, + GSM_LOGOUT_ACTION_SWITCH_USER); + + g_signal_connect (manager->priv->inhibit_dialog, + "response", + G_CALLBACK (inhibit_dialog_response), + manager); + gtk_widget_show (manager->priv->inhibit_dialog); +} + +static void +logout_dialog_response (GsmLogoutDialog *logout_dialog, + guint response_id, + GsmManager *manager) +{ + g_debug ("GsmManager: Logout dialog response: %d", response_id); + + gtk_widget_destroy (GTK_WIDGET (logout_dialog)); + + /* In case of dialog cancel, switch user, hibernate and + * suspend, we just perform the respective action and return, + * without shutting down the session. */ + switch (response_id) { + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_NONE: + case GTK_RESPONSE_DELETE_EVENT: + break; + case GSM_LOGOUT_RESPONSE_SWITCH_USER: + request_switch_user (manager); + break; + case GSM_LOGOUT_RESPONSE_HIBERNATE: + request_hibernate (manager); + break; + case GSM_LOGOUT_RESPONSE_SLEEP: + request_suspend (manager); + break; + case GSM_LOGOUT_RESPONSE_SHUTDOWN: + request_shutdown (manager); + break; + case GSM_LOGOUT_RESPONSE_REBOOT: + request_reboot (manager); + break; + case GSM_LOGOUT_RESPONSE_LOGOUT: + request_logout (manager, FALSE); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +show_shutdown_dialog (GsmManager *manager) +{ + GtkWidget *dialog; + + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) { + /* Already shutting down, nothing more to do */ + return; + } + + dialog = gsm_get_shutdown_dialog (gdk_screen_get_default (), + gtk_get_current_event_time ()); + + g_signal_connect (dialog, + "response", + G_CALLBACK (logout_dialog_response), + manager); + gtk_widget_show (dialog); +} + +static void +show_logout_dialog (GsmManager *manager) +{ + GtkWidget *dialog; + + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) { + /* Already shutting down, nothing more to do */ + return; + } + + dialog = gsm_get_logout_dialog (gdk_screen_get_default (), + gtk_get_current_event_time ()); + + g_signal_connect (dialog, + "response", + G_CALLBACK (logout_dialog_response), + manager); + gtk_widget_show (dialog); +} + +static void +user_logout (GsmManager *manager, + gboolean show_confirmation, + gboolean forceful_logout) +{ + gboolean logout_prompt; + + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) { + /* Already shutting down, nothing more to do */ + return; + } + + logout_prompt = + mateconf_client_get_bool (manager->priv->mateconf_client, + "/apps/mate-session/options/logout_prompt", + NULL); + + /* Global settings overides input parameter in order to disable confirmation + * dialog accordingly. If we're shutting down, we always show the confirmation + * dialog */ + logout_prompt = (logout_prompt && show_confirmation); + + if (logout_prompt) { + show_logout_dialog (manager); + } else { + request_logout (manager, forceful_logout); + } +} + +/* + dbus-send --session --type=method_call --print-reply + --dest=org.mate.SessionManager + /org/mate/SessionManager + org.freedesktop.DBus.Introspectable.Introspect +*/ + +gboolean +gsm_manager_set_phase (GsmManager *manager, + GsmManagerPhase phase) +{ + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + manager->priv->phase = phase; + return (TRUE); +} + +gboolean +gsm_manager_shutdown (GsmManager *manager, + GError **error) +{ + g_debug ("GsmManager: Shutdown called"); + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_RUNNING, + "Shutdown interface is only available during the Running phase"); + return FALSE; + } + + show_shutdown_dialog (manager); + + return TRUE; +} + +gboolean +gsm_manager_can_shutdown (GsmManager *manager, + gboolean *shutdown_available, + GError **error) +{ + GsmConsolekit *consolekit; + gboolean can_suspend; + gboolean can_hibernate; + + g_object_get (manager->priv->up_client, + "can-suspend", &can_suspend, + "can-hibernate", &can_hibernate, + NULL); + + g_debug ("GsmManager: CanShutdown called"); + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + consolekit = gsm_get_consolekit (); + *shutdown_available = gsm_consolekit_can_stop (consolekit) + || gsm_consolekit_can_restart (consolekit) + || can_suspend + || can_hibernate; + g_object_unref (consolekit); + + return TRUE; +} + +gboolean +gsm_manager_logout (GsmManager *manager, + guint logout_mode, + GError **error) +{ + g_debug ("GsmManager: Logout called"); + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_RUNNING, + "Shutdown interface is only available during the Running phase"); + return FALSE; + } + + switch (logout_mode) { + case GSM_MANAGER_LOGOUT_MODE_NORMAL: + user_logout (manager, TRUE, FALSE); + break; + + case GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION: + user_logout (manager, FALSE, FALSE); + break; + + case GSM_MANAGER_LOGOUT_MODE_FORCE: + user_logout (manager, FALSE, TRUE); + break; + + default: + g_debug ("Unknown logout mode option"); + + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_INVALID_OPTION, + "Unknown logout mode flag"); + return FALSE; + } + + return TRUE; +} + +gboolean +gsm_manager_register_client (GsmManager *manager, + const char *app_id, + const char *startup_id, + DBusGMethodInvocation *context) +{ + char *new_startup_id; + char *sender; + GsmClient *client; + GsmApp *app; + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + app = NULL; + client = NULL; + + g_debug ("GsmManager: RegisterClient %s", startup_id); + + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) { + GError *new_error; + + g_debug ("Unable to register client: shutting down"); + + new_error = g_error_new (GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_RUNNING, + "Unable to register client"); + dbus_g_method_return_error (context, new_error); + g_error_free (new_error); + return FALSE; + } + + if (IS_STRING_EMPTY (startup_id)) { + new_startup_id = gsm_util_generate_startup_id (); + } else { + + client = (GsmClient *)gsm_store_find (manager->priv->clients, + (GsmStoreFunc)_client_has_startup_id, + (char *)startup_id); + /* We can't have two clients with the same startup id. */ + if (client != NULL) { + GError *new_error; + + g_debug ("Unable to register client: already registered"); + + new_error = g_error_new (GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_ALREADY_REGISTERED, + "Unable to register client"); + dbus_g_method_return_error (context, new_error); + g_error_free (new_error); + return FALSE; + } + + new_startup_id = g_strdup (startup_id); + } + + g_debug ("GsmManager: Adding new client %s to session", new_startup_id); + + if (app == NULL && !IS_STRING_EMPTY (startup_id)) { + app = find_app_for_startup_id (manager, startup_id); + } + if (app == NULL && !IS_STRING_EMPTY (app_id)) { + /* try to associate this app id with a known app */ + app = find_app_for_app_id (manager, app_id); + } + + sender = dbus_g_method_get_sender (context); + client = gsm_dbus_client_new (new_startup_id, sender); + g_free (sender); + if (client == NULL) { + GError *new_error; + + g_debug ("Unable to create client"); + + new_error = g_error_new (GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_GENERAL, + "Unable to register client"); + dbus_g_method_return_error (context, new_error); + g_error_free (new_error); + return FALSE; + } + + gsm_store_add (manager->priv->clients, gsm_client_peek_id (client), G_OBJECT (client)); + /* the store will own the ref */ + g_object_unref (client); + + if (app != NULL) { + gsm_client_set_app_id (client, gsm_app_peek_app_id (app)); + gsm_app_registered (app); + } else { + /* if an app id is specified store it in the client + so we can save it later */ + gsm_client_set_app_id (client, app_id); + } + + gsm_client_set_status (client, GSM_CLIENT_REGISTERED); + + g_assert (new_startup_id != NULL); + g_free (new_startup_id); + + dbus_g_method_return (context, gsm_client_peek_id (client)); + + return TRUE; +} + +gboolean +gsm_manager_unregister_client (GsmManager *manager, + const char *client_id, + DBusGMethodInvocation *context) +{ + GsmClient *client; + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + g_debug ("GsmManager: UnregisterClient %s", client_id); + + client = (GsmClient *)gsm_store_lookup (manager->priv->clients, client_id); + if (client == NULL) { + GError *new_error; + + g_debug ("Unable to unregister client: not registered"); + + new_error = g_error_new (GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_REGISTERED, + "Unable to unregister client"); + dbus_g_method_return_error (context, new_error); + g_error_free (new_error); + return FALSE; + } + + /* don't disconnect client here, only change the status. + Wait until it leaves the bus before disconnecting it */ + gsm_client_set_status (client, GSM_CLIENT_UNREGISTERED); + + dbus_g_method_return (context); + + return TRUE; +} + +gboolean +gsm_manager_inhibit (GsmManager *manager, + const char *app_id, + guint toplevel_xid, + const char *reason, + guint flags, + DBusGMethodInvocation *context) +{ + GsmInhibitor *inhibitor; + guint cookie; + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + g_debug ("GsmManager: Inhibit xid=%u app_id=%s reason=%s flags=%u", + toplevel_xid, + app_id, + reason, + flags); + + if (IS_STRING_EMPTY (app_id)) { + GError *new_error; + + new_error = g_error_new (GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_GENERAL, + "Application ID not specified"); + g_debug ("GsmManager: Unable to inhibit: %s", new_error->message); + dbus_g_method_return_error (context, new_error); + g_error_free (new_error); + return FALSE; + } + + if (IS_STRING_EMPTY (reason)) { + GError *new_error; + + new_error = g_error_new (GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_GENERAL, + "Reason not specified"); + g_debug ("GsmManager: Unable to inhibit: %s", new_error->message); + dbus_g_method_return_error (context, new_error); + g_error_free (new_error); + return FALSE; + } + + if (flags == 0) { + GError *new_error; + + new_error = g_error_new (GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_GENERAL, + "Invalid inhibit flags"); + g_debug ("GsmManager: Unable to inhibit: %s", new_error->message); + dbus_g_method_return_error (context, new_error); + g_error_free (new_error); + return FALSE; + } + + cookie = _generate_unique_cookie (manager); + inhibitor = gsm_inhibitor_new (app_id, + toplevel_xid, + flags, + reason, + dbus_g_method_get_sender (context), + cookie); + gsm_store_add (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor)); + g_object_unref (inhibitor); + + dbus_g_method_return (context, cookie); + + return TRUE; +} + +gboolean +gsm_manager_uninhibit (GsmManager *manager, + guint cookie, + DBusGMethodInvocation *context) +{ + GsmInhibitor *inhibitor; + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + g_debug ("GsmManager: Uninhibit %u", cookie); + + inhibitor = (GsmInhibitor *)gsm_store_find (manager->priv->inhibitors, + (GsmStoreFunc)_find_by_cookie, + &cookie); + if (inhibitor == NULL) { + GError *new_error; + + new_error = g_error_new (GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_GENERAL, + "Unable to uninhibit: Invalid cookie"); + dbus_g_method_return_error (context, new_error); + g_debug ("Unable to uninhibit: %s", new_error->message); + g_error_free (new_error); + return FALSE; + } + + g_debug ("GsmManager: removing inhibitor %s %u reason '%s' %u connection %s", + gsm_inhibitor_peek_app_id (inhibitor), + gsm_inhibitor_peek_toplevel_xid (inhibitor), + gsm_inhibitor_peek_reason (inhibitor), + gsm_inhibitor_peek_flags (inhibitor), + gsm_inhibitor_peek_bus_name (inhibitor)); + + gsm_store_remove (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor)); + + dbus_g_method_return (context); + + return TRUE; +} + +gboolean +gsm_manager_is_inhibited (GsmManager *manager, + guint flags, + gboolean *is_inhibited, + GError *error) +{ + GsmInhibitor *inhibitor; + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + if (manager->priv->inhibitors == NULL + || gsm_store_size (manager->priv->inhibitors) == 0) { + *is_inhibited = FALSE; + return TRUE; + } + + inhibitor = (GsmInhibitor *) gsm_store_find (manager->priv->inhibitors, + (GsmStoreFunc)inhibitor_has_flag, + GUINT_TO_POINTER (flags)); + if (inhibitor == NULL) { + *is_inhibited = FALSE; + } else { + *is_inhibited = TRUE; + } + + return TRUE; + +} + +static gboolean +listify_store_ids (char *id, + GObject *object, + GPtrArray **array) +{ + g_ptr_array_add (*array, g_strdup (id)); + return FALSE; +} + +gboolean +gsm_manager_get_clients (GsmManager *manager, + GPtrArray **clients, + GError **error) +{ + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + if (clients == NULL) { + return FALSE; + } + + *clients = g_ptr_array_new (); + gsm_store_foreach (manager->priv->clients, (GsmStoreFunc)listify_store_ids, clients); + + return TRUE; +} + +gboolean +gsm_manager_get_inhibitors (GsmManager *manager, + GPtrArray **inhibitors, + GError **error) +{ + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + if (inhibitors == NULL) { + return FALSE; + } + + *inhibitors = g_ptr_array_new (); + gsm_store_foreach (manager->priv->inhibitors, + (GsmStoreFunc) listify_store_ids, + inhibitors); + + return TRUE; +} + + +static gboolean +_app_has_autostart_condition (const char *id, + GsmApp *app, + const char *condition) +{ + gboolean has; + gboolean disabled; + + has = gsm_app_has_autostart_condition (app, condition); + disabled = gsm_app_peek_is_disabled (app); + + return has && !disabled; +} + +gboolean +gsm_manager_is_autostart_condition_handled (GsmManager *manager, + const char *condition, + gboolean *handled, + GError **error) +{ + GsmApp *app; + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + app = (GsmApp *) gsm_store_find (manager->priv->apps,( + GsmStoreFunc) _app_has_autostart_condition, + (char *)condition); + + if (app != NULL) { + *handled = TRUE; + } else { + *handled = FALSE; + } + + return TRUE; +} + +static void +append_app (GsmManager *manager, + GsmApp *app) +{ + const char *id; + const char *app_id; + GsmApp *dup; + + id = gsm_app_peek_id (app); + if (IS_STRING_EMPTY (id)) { + g_debug ("GsmManager: not adding app: no id"); + return; + } + + dup = (GsmApp *)gsm_store_lookup (manager->priv->apps, id); + if (dup != NULL) { + g_debug ("GsmManager: not adding app: already added"); + return; + } + + app_id = gsm_app_peek_app_id (app); + if (IS_STRING_EMPTY (app_id)) { + g_debug ("GsmManager: not adding app: no app-id"); + return; + } + + dup = find_app_for_app_id (manager, app_id); + if (dup != NULL) { + g_debug ("GsmManager: not adding app: app-id already exists"); + return; + } + + gsm_store_add (manager->priv->apps, id, G_OBJECT (app)); +} + +gboolean +gsm_manager_add_autostart_app (GsmManager *manager, + const char *path, + const char *provides) +{ + GsmApp *app; + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + /* first check to see if service is already provided */ + if (provides != NULL) { + GsmApp *dup; + + dup = (GsmApp *)gsm_store_find (manager->priv->apps, + (GsmStoreFunc)_find_app_provides, + (char *)provides); + if (dup != NULL) { + g_debug ("GsmManager: service '%s' is already provided", provides); + return FALSE; + } + } + + app = gsm_autostart_app_new (path); + if (app == NULL) { + g_warning ("could not read %s", path); + return FALSE; + } + + g_debug ("GsmManager: read %s", path); + append_app (manager, app); + g_object_unref (app); + + return TRUE; +} + +gboolean +gsm_manager_add_autostart_apps_from_dir (GsmManager *manager, + const char *path) +{ + GDir *dir; + const char *name; + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + g_debug ("GsmManager: *** Adding autostart apps for %s", path); + + dir = g_dir_open (path, 0, NULL); + if (dir == NULL) { + return FALSE; + } + + while ((name = g_dir_read_name (dir))) { + char *desktop_file; + + if (!g_str_has_suffix (name, ".desktop")) { + continue; + } + + desktop_file = g_build_filename (path, name, NULL); + gsm_manager_add_autostart_app (manager, desktop_file, NULL); + g_free (desktop_file); + } + + g_dir_close (dir); + + return TRUE; +} diff --git a/mate-session/gsm-manager.h b/mate-session/gsm-manager.h new file mode 100644 index 0000000..635590c --- /dev/null +++ b/mate-session/gsm-manager.h @@ -0,0 +1,190 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GSM_MANAGER_H +#define __GSM_MANAGER_H + +#include <glib-object.h> +#include <dbus/dbus-glib.h> + +#include "gsm-store.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_MANAGER (gsm_manager_get_type ()) +#define GSM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_MANAGER, GsmManager)) +#define GSM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_MANAGER, GsmManagerClass)) +#define GSM_IS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_MANAGER)) +#define GSM_IS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_MANAGER)) +#define GSM_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_MANAGER, GsmManagerClass)) + +typedef struct GsmManagerPrivate GsmManagerPrivate; + +typedef struct +{ + GObject parent; + GsmManagerPrivate *priv; +} GsmManager; + +typedef struct +{ + GObjectClass parent_class; + + void (* session_running) (GsmManager *manager); + void (* session_over) (GsmManager *manager); + void (* session_over_notice) (GsmManager *manager); + + void (* phase_changed) (GsmManager *manager, + const char *phase); + + void (* client_added) (GsmManager *manager, + const char *id); + void (* client_removed) (GsmManager *manager, + const char *id); + void (* inhibitor_added) (GsmManager *manager, + const char *id); + void (* inhibitor_removed) (GsmManager *manager, + const char *id); +} GsmManagerClass; + +typedef enum { + /* gsm's own startup/initialization phase */ + GSM_MANAGER_PHASE_STARTUP = 0, + /* xrandr setup, mate-settings-daemon, etc */ + GSM_MANAGER_PHASE_INITIALIZATION, + /* window/compositing managers */ + GSM_MANAGER_PHASE_WINDOW_MANAGER, + /* apps that will create _NET_WM_WINDOW_TYPE_PANEL windows */ + GSM_MANAGER_PHASE_PANEL, + /* apps that will create _NET_WM_WINDOW_TYPE_DESKTOP windows */ + GSM_MANAGER_PHASE_DESKTOP, + /* everything else */ + GSM_MANAGER_PHASE_APPLICATION, + /* done launching */ + GSM_MANAGER_PHASE_RUNNING, + /* shutting down */ + GSM_MANAGER_PHASE_QUERY_END_SESSION, + GSM_MANAGER_PHASE_END_SESSION, + GSM_MANAGER_PHASE_EXIT +} GsmManagerPhase; + +typedef enum +{ + GSM_MANAGER_ERROR_GENERAL = 0, + GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION, + GSM_MANAGER_ERROR_NOT_IN_RUNNING, + GSM_MANAGER_ERROR_ALREADY_REGISTERED, + GSM_MANAGER_ERROR_NOT_REGISTERED, + GSM_MANAGER_ERROR_INVALID_OPTION, + GSM_MANAGER_NUM_ERRORS +} GsmManagerError; + +#define GSM_MANAGER_ERROR gsm_manager_error_quark () + +typedef enum { + GSM_MANAGER_LOGOUT_MODE_NORMAL = 0, + GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION, + GSM_MANAGER_LOGOUT_MODE_FORCE +} GsmManagerLogoutMode; + +GType gsm_manager_error_get_type (void); +#define GSM_MANAGER_TYPE_ERROR (gsm_manager_error_get_type ()) + +GQuark gsm_manager_error_quark (void); +GType gsm_manager_get_type (void); + +GsmManager * gsm_manager_new (GsmStore *client_store, + gboolean failsafe); + +gboolean gsm_manager_add_autostart_app (GsmManager *manager, + const char *path, + const char *provides); +gboolean gsm_manager_add_autostart_apps_from_dir (GsmManager *manager, + const char *path); +gboolean gsm_manager_add_legacy_session_apps (GsmManager *manager, + const char *path); + +void gsm_manager_start (GsmManager *manager); + + +/* exported methods */ + +gboolean gsm_manager_register_client (GsmManager *manager, + const char *app_id, + const char *client_startup_id, + DBusGMethodInvocation *context); +gboolean gsm_manager_unregister_client (GsmManager *manager, + const char *session_client_id, + DBusGMethodInvocation *context); + +gboolean gsm_manager_inhibit (GsmManager *manager, + const char *app_id, + guint toplevel_xid, + const char *reason, + guint flags, + DBusGMethodInvocation *context); +gboolean gsm_manager_uninhibit (GsmManager *manager, + guint inhibit_cookie, + DBusGMethodInvocation *context); +gboolean gsm_manager_is_inhibited (GsmManager *manager, + guint flags, + gboolean *is_inhibited, + GError *error); + +gboolean gsm_manager_shutdown (GsmManager *manager, + GError **error); + +gboolean gsm_manager_can_shutdown (GsmManager *manager, + gboolean *shutdown_available, + GError **error); +gboolean gsm_manager_logout (GsmManager *manager, + guint logout_mode, + GError **error); + +gboolean gsm_manager_setenv (GsmManager *manager, + const char *variable, + const char *value, + GError **error); +gboolean gsm_manager_initialization_error (GsmManager *manager, + const char *message, + gboolean fatal, + GError **error); + +gboolean gsm_manager_get_clients (GsmManager *manager, + GPtrArray **clients, + GError **error); +gboolean gsm_manager_get_inhibitors (GsmManager *manager, + GPtrArray **inhibitors, + GError **error); +gboolean gsm_manager_is_autostart_condition_handled (GsmManager *manager, + const char *condition, + gboolean *handled, + GError **error); +gboolean gsm_manager_set_phase (GsmManager *manager, + GsmManagerPhase phase); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_MANAGER_H */ diff --git a/mate-session/gsm-marshal.list b/mate-session/gsm-marshal.list new file mode 100644 index 0000000..f31a8b1 --- /dev/null +++ b/mate-session/gsm-marshal.list @@ -0,0 +1,3 @@ +BOOLEAN:POINTER +VOID:BOOLEAN,BOOLEAN,BOOLEAN,STRING +VOID:BOOLEAN,BOOLEAN,POINTER diff --git a/mate-session/gsm-mateconf.c b/mate-session/gsm-mateconf.c new file mode 100644 index 0000000..cd5adc4 --- /dev/null +++ b/mate-session/gsm-mateconf.c @@ -0,0 +1,143 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * gsm-mateconf.c + * Copyright (C) 2007 Novell, Inc. + * + * FIXME: (C) on mateconf-sanity-check call, gsm_get_conf_client, + * gsm_shutdown_mateconfd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 <glib/gi18n.h> + +#include <sys/wait.h> +#include <sys/types.h> + +#include "gsm-mateconf.h" +#include "gsm-util.h" + +static pid_t gsc_pid; + +static void unset_display_setup (gpointer user_data); + +/** + * gsm_mateconf_init: + * + * Starts mateconfd asynchronously if it is not already running. This + * must be called very soon after startup. It requires no + * initialization beyond g_type_init(). + **/ +void +gsm_mateconf_init (void) +{ + GError *error = NULL; + char *argv[2]; + + /* Run mateconf-sanity-check. As a side effect, this will cause mateconfd + * to be started. (We do this asynchronously so that other GSM + * initialization can happen in parallel.) + */ + + argv[0] = MATECONF_SANITY_CHECK; + argv[1] = NULL; + + g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + unset_display_setup, NULL, &gsc_pid, &error); + if (error != NULL) { + g_warning ("Failed to run mateconf-sanity-check-2: %s\n", + error->message); + g_error_free (error); + + /* This probably means mateconf-sanity-check wasn't found, which + * really shouldn't happen, but we'll just ignore it for now as + * long as mateconf seems to be working later on... + */ + + gsc_pid = 0; + } +} + +static void +unset_display_setup (gpointer user_data) +{ + /* Unset DISPLAY to make sure mateconf-sanity-check spews errors to + * stderr instead of trying to show a dialog (since it doesn't + * compensate for the fact that a window manager isn't running yet.) + */ + g_unsetenv ("DISPLAY"); +} + +/** + * gsm_mateconf_check: + * + * Verifies that gsm_mateconf_init() succeeded. (Exits with an error + * dialog on failure.) + **/ +void +gsm_mateconf_check (void) +{ + if (gsc_pid) { + int status; + + /* Wait for mateconf-sanity-check to finish */ + while (waitpid (gsc_pid, &status, 0) != gsc_pid) { + ; + } + gsc_pid = 0; + + if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) { + /* FIXME: capture mateconf-sanity-check's stderr */ + gsm_util_init_error (TRUE, + _("There is a problem with the configuration server.\n" + "(%s exited with status %d)"), + MATECONF_SANITY_CHECK, status); + } + } +} + +/** + * gsm_mateconf_shutdown: + * + * Shuts down mateconfd before exiting. + * + * FIXME: does this need to be called even if mateconf-sanity-check fails? + **/ +void +gsm_mateconf_shutdown (void) +{ + GError *error; + char *command; + int status; + + command = g_strjoin (" ", MATECONFTOOL_CMD, "--shutdown", NULL); + + status = 0; + error = NULL; + if (!g_spawn_command_line_sync (command, NULL, NULL, &status, &error)) { + g_warning ("Failed to execute '%s' on logout: %s\n", + command, error->message); + g_error_free (error); + } + + if (status) { + g_warning ("Running '%s' at logout returned an exit status of '%d'", + command, status); + } + + g_free (command); +} diff --git a/mate-session/gsm-mateconf.h b/mate-session/gsm-mateconf.h new file mode 100644 index 0000000..d30d548 --- /dev/null +++ b/mate-session/gsm-mateconf.h @@ -0,0 +1,31 @@ +/* gsm-mateconf.h + * Copyright (C) 2007 Novell, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 __GSM_MATECONF_H__ +#define __GSM_MATECONF_H__ + +#include <mateconf/mateconf-client.h> + +void gsm_mateconf_init (void); +void gsm_mateconf_check (void); +void gsm_mateconf_shutdown (void); + +MateConfClient *gsm_mateconf_get_client (void); + +#endif /* __GSM_MATECONF_H__ */ diff --git a/mate-session/gsm-presence.c b/mate-session/gsm-presence.c new file mode 100644 index 0000000..dc179c0 --- /dev/null +++ b/mate-session/gsm-presence.c @@ -0,0 +1,550 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <dbus/dbus-glib.h> + +#include "gs-idle-monitor.h" + +#include "gsm-presence.h" +#include "gsm-presence-glue.h" + +#define GSM_PRESENCE_DBUS_PATH "/org/mate/SessionManager/Presence" + +#define GS_NAME "org.mate.ScreenSaver" +#define GS_PATH "/org/mate/ScreenSaver" +#define GS_INTERFACE "org.mate.ScreenSaver" + +#define MAX_STATUS_TEXT 140 + +#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0') + +#define GSM_PRESENCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PRESENCE, GsmPresencePrivate)) + +struct GsmPresencePrivate +{ + guint status; + guint saved_status; + char *status_text; + gboolean idle_enabled; + GSIdleMonitor *idle_monitor; + guint idle_watch_id; + guint idle_timeout; + gboolean screensaver_active; + DBusGConnection *bus_connection; + DBusGProxy *bus_proxy; + DBusGProxy *screensaver_proxy; +}; + +enum { + PROP_0, + PROP_STATUS, + PROP_STATUS_TEXT, + PROP_IDLE_ENABLED, + PROP_IDLE_TIMEOUT, +}; + + +enum { + STATUS_CHANGED, + STATUS_TEXT_CHANGED, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (GsmPresence, gsm_presence, G_TYPE_OBJECT) + +GQuark +gsm_presence_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gsm_presence_error"); + } + + return ret; +} + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +gsm_presence_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (GSM_PRESENCE_ERROR_GENERAL, "GeneralError"), + { 0, 0, 0 } + }; + + g_assert (GSM_PRESENCE_NUM_ERRORS == G_N_ELEMENTS (values) - 1); + + etype = g_enum_register_static ("GsmPresenceError", values); + } + + return etype; +} + +static void +set_session_idle (GsmPresence *presence, + gboolean is_idle) +{ + g_debug ("GsmPresence: setting idle: %d", is_idle); + + if (is_idle) { + if (presence->priv->status == GSM_PRESENCE_STATUS_IDLE) { + g_debug ("GsmPresence: already idle, ignoring"); + return; + } + + /* save current status */ + presence->priv->saved_status = presence->priv->status; + gsm_presence_set_status (presence, GSM_PRESENCE_STATUS_IDLE, NULL); + } else { + if (presence->priv->status != GSM_PRESENCE_STATUS_IDLE) { + g_debug ("GsmPresence: already not idle, ignoring"); + return; + } + + /* restore saved status */ + gsm_presence_set_status (presence, presence->priv->saved_status, NULL); + presence->priv->saved_status = GSM_PRESENCE_STATUS_AVAILABLE; + } +} + +static gboolean +on_idle_timeout (GSIdleMonitor *monitor, + guint id, + gboolean condition, + GsmPresence *presence) +{ + gboolean handled; + + handled = TRUE; + set_session_idle (presence, condition); + + return handled; +} + +static void +reset_idle_watch (GsmPresence *presence) +{ + if (presence->priv->idle_monitor == NULL) { + return; + } + + if (presence->priv->idle_watch_id > 0) { + g_debug ("GsmPresence: removing idle watch"); + gs_idle_monitor_remove_watch (presence->priv->idle_monitor, + presence->priv->idle_watch_id); + presence->priv->idle_watch_id = 0; + } + + if (! presence->priv->screensaver_active + && presence->priv->idle_enabled) { + g_debug ("GsmPresence: adding idle watch"); + + presence->priv->idle_watch_id = gs_idle_monitor_add_watch (presence->priv->idle_monitor, + presence->priv->idle_timeout, + (GSIdleMonitorWatchFunc)on_idle_timeout, + presence); + } +} + +static void +on_screensaver_active_changed (DBusGProxy *proxy, + gboolean is_active, + GsmPresence *presence) +{ + g_debug ("screensaver status changed: %d", is_active); + if (presence->priv->screensaver_active != is_active) { + presence->priv->screensaver_active = is_active; + reset_idle_watch (presence); + set_session_idle (presence, is_active); + } +} + +static void +on_screensaver_proxy_destroy (GObject *proxy, + GsmPresence *presence) +{ + g_warning ("Detected that screensaver has left the bus"); + + presence->priv->screensaver_proxy = NULL; + presence->priv->screensaver_active = FALSE; + set_session_idle (presence, FALSE); + reset_idle_watch (presence); +} + +static void +on_bus_name_owner_changed (DBusGProxy *bus_proxy, + const char *service_name, + const char *old_service_name, + const char *new_service_name, + GsmPresence *presence) +{ + GError *error; + + if (service_name == NULL + || strcmp (service_name, GS_NAME) != 0) { + /* ignore */ + return; + } + + if (strlen (new_service_name) == 0 + && strlen (old_service_name) > 0) { + /* service removed */ + /* let destroy signal handle this? */ + } else if (strlen (old_service_name) == 0 + && strlen (new_service_name) > 0) { + /* service added */ + error = NULL; + presence->priv->screensaver_proxy = dbus_g_proxy_new_for_name_owner (presence->priv->bus_connection, + GS_NAME, + GS_PATH, + GS_INTERFACE, + &error); + if (presence->priv->screensaver_proxy != NULL) { + g_signal_connect (presence->priv->screensaver_proxy, + "destroy", + G_CALLBACK (on_screensaver_proxy_destroy), + presence); + dbus_g_proxy_add_signal (presence->priv->screensaver_proxy, + "ActiveChanged", + G_TYPE_BOOLEAN, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (presence->priv->screensaver_proxy, + "ActiveChanged", + G_CALLBACK (on_screensaver_active_changed), + presence, + NULL); + } else { + g_warning ("Unable to get screensaver proxy: %s", error->message); + g_error_free (error); + } + } +} + +static gboolean +register_presence (GsmPresence *presence) +{ + GError *error; + + error = NULL; + presence->priv->bus_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (presence->priv->bus_connection == NULL) { + if (error != NULL) { + g_critical ("error getting session bus: %s", error->message); + g_error_free (error); + } + return FALSE; + } + + dbus_g_connection_register_g_object (presence->priv->bus_connection, GSM_PRESENCE_DBUS_PATH, G_OBJECT (presence)); + + return TRUE; +} + +static GObject * +gsm_presence_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmPresence *presence; + gboolean res; + + presence = GSM_PRESENCE (G_OBJECT_CLASS (gsm_presence_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + res = register_presence (presence); + if (! res) { + g_warning ("Unable to register presence with session bus"); + } + + presence->priv->bus_proxy = dbus_g_proxy_new_for_name (presence->priv->bus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + if (presence->priv->bus_proxy != NULL) { + dbus_g_proxy_add_signal (presence->priv->bus_proxy, + "NameOwnerChanged", + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (presence->priv->bus_proxy, + "NameOwnerChanged", + G_CALLBACK (on_bus_name_owner_changed), + presence, + NULL); + } + + return G_OBJECT (presence); +} + +static void +gsm_presence_init (GsmPresence *presence) +{ + presence->priv = GSM_PRESENCE_GET_PRIVATE (presence); + + presence->priv->idle_monitor = gs_idle_monitor_new (); +} + +void +gsm_presence_set_idle_enabled (GsmPresence *presence, + gboolean enabled) +{ + g_return_if_fail (GSM_IS_PRESENCE (presence)); + + if (presence->priv->idle_enabled != enabled) { + presence->priv->idle_enabled = enabled; + reset_idle_watch (presence); + g_object_notify (G_OBJECT (presence), "idle-enabled"); + + } +} + +gboolean +gsm_presence_set_status_text (GsmPresence *presence, + const char *status_text, + GError **error) +{ + g_return_val_if_fail (GSM_IS_PRESENCE (presence), FALSE); + + g_free (presence->priv->status_text); + + /* check length */ + if (status_text != NULL && strlen (status_text) > MAX_STATUS_TEXT) { + g_set_error (error, + GSM_PRESENCE_ERROR, + GSM_PRESENCE_ERROR_GENERAL, + "Status text too long"); + return FALSE; + } + + if (status_text != NULL) { + presence->priv->status_text = g_strdup (status_text); + } else { + presence->priv->status_text = g_strdup (""); + } + g_object_notify (G_OBJECT (presence), "status-text"); + g_signal_emit (presence, signals[STATUS_TEXT_CHANGED], 0, presence->priv->status_text); + return TRUE; +} + +gboolean +gsm_presence_set_status (GsmPresence *presence, + guint status, + GError **error) +{ + g_return_val_if_fail (GSM_IS_PRESENCE (presence), FALSE); + + if (status != presence->priv->status) { + presence->priv->status = status; + g_object_notify (G_OBJECT (presence), "status"); + g_signal_emit (presence, signals[STATUS_CHANGED], 0, presence->priv->status); + } + return TRUE; +} + +void +gsm_presence_set_idle_timeout (GsmPresence *presence, + guint timeout) +{ + g_return_if_fail (GSM_IS_PRESENCE (presence)); + + if (timeout != presence->priv->idle_timeout) { + presence->priv->idle_timeout = timeout; + reset_idle_watch (presence); + g_object_notify (G_OBJECT (presence), "idle-timeout"); + } +} + +static void +gsm_presence_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmPresence *self; + + self = GSM_PRESENCE (object); + + switch (prop_id) { + case PROP_STATUS: + gsm_presence_set_status (self, g_value_get_uint (value), NULL); + break; + case PROP_STATUS_TEXT: + gsm_presence_set_status_text (self, g_value_get_string (value), NULL); + break; + case PROP_IDLE_ENABLED: + gsm_presence_set_idle_enabled (self, g_value_get_boolean (value)); + break; + case PROP_IDLE_TIMEOUT: + gsm_presence_set_idle_timeout (self, g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_presence_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmPresence *self; + + self = GSM_PRESENCE (object); + + switch (prop_id) { + case PROP_STATUS: + g_value_set_uint (value, self->priv->status); + break; + case PROP_STATUS_TEXT: + g_value_set_string (value, self->priv->status_text); + break; + case PROP_IDLE_ENABLED: + g_value_set_boolean (value, self->priv->idle_enabled); + break; + case PROP_IDLE_TIMEOUT: + g_value_set_uint (value, self->priv->idle_timeout); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_presence_finalize (GObject *object) +{ + GsmPresence *presence = (GsmPresence *) object; + + if (presence->priv->idle_watch_id > 0) { + gs_idle_monitor_remove_watch (presence->priv->idle_monitor, + presence->priv->idle_watch_id); + presence->priv->idle_watch_id = 0; + } + + if (presence->priv->status_text != NULL) { + g_free (presence->priv->status_text); + presence->priv->status_text = NULL; + } + + if (presence->priv->idle_monitor != NULL) { + g_object_unref (presence->priv->idle_monitor); + presence->priv->idle_monitor = NULL; + } + + G_OBJECT_CLASS (gsm_presence_parent_class)->finalize (object); +} + +static void +gsm_presence_class_init (GsmPresenceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gsm_presence_finalize; + object_class->constructor = gsm_presence_constructor; + object_class->get_property = gsm_presence_get_property; + object_class->set_property = gsm_presence_set_property; + + signals [STATUS_CHANGED] = + g_signal_new ("status-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmPresenceClass, status_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, G_TYPE_UINT); + signals [STATUS_TEXT_CHANGED] = + g_signal_new ("status-text-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmPresenceClass, status_text_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, G_TYPE_STRING); + + g_object_class_install_property (object_class, + PROP_STATUS, + g_param_spec_uint ("status", + "status", + "status", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_STATUS_TEXT, + g_param_spec_string ("status-text", + "status text", + "status text", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_IDLE_ENABLED, + g_param_spec_boolean ("idle-enabled", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_IDLE_TIMEOUT, + g_param_spec_uint ("idle-timeout", + "idle timeout", + "idle timeout", + 0, + G_MAXINT, + 300000, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + dbus_g_object_type_install_info (GSM_TYPE_PRESENCE, &dbus_glib_gsm_presence_object_info); + dbus_g_error_domain_register (GSM_PRESENCE_ERROR, NULL, GSM_PRESENCE_TYPE_ERROR); + g_type_class_add_private (klass, sizeof (GsmPresencePrivate)); +} + +GsmPresence * +gsm_presence_new (void) +{ + GsmPresence *presence; + + presence = g_object_new (GSM_TYPE_PRESENCE, + NULL); + + return presence; +} diff --git a/mate-session/gsm-presence.h b/mate-session/gsm-presence.h new file mode 100644 index 0000000..8762569 --- /dev/null +++ b/mate-session/gsm-presence.h @@ -0,0 +1,100 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 __GSM_PRESENCE_H__ +#define __GSM_PRESENCE_H__ + +#include <glib-object.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_PRESENCE (gsm_presence_get_type ()) +#define GSM_PRESENCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_PRESENCE, GsmPresence)) +#define GSM_PRESENCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_PRESENCE, GsmPresenceClass)) +#define GSM_IS_PRESENCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_PRESENCE)) +#define GSM_IS_PRESENCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_PRESENCE)) +#define GSM_PRESENCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_PRESENCE, GsmPresenceClass)) + +typedef struct _GsmPresence GsmPresence; +typedef struct _GsmPresenceClass GsmPresenceClass; + +typedef struct GsmPresencePrivate GsmPresencePrivate; + +struct _GsmPresence +{ + GObject parent; + GsmPresencePrivate *priv; +}; + +struct _GsmPresenceClass +{ + GObjectClass parent_class; + + void (* status_changed) (GsmPresence *presence, + guint status); + void (* status_text_changed) (GsmPresence *presence, + const char *status_text); + +}; + +typedef enum { + GSM_PRESENCE_STATUS_AVAILABLE = 0, + GSM_PRESENCE_STATUS_INVISIBLE, + GSM_PRESENCE_STATUS_BUSY, + GSM_PRESENCE_STATUS_IDLE, +} GsmPresenceStatus; + +typedef enum +{ + GSM_PRESENCE_ERROR_GENERAL = 0, + GSM_PRESENCE_NUM_ERRORS +} GsmPresenceError; + +#define GSM_PRESENCE_ERROR gsm_presence_error_quark () +GType gsm_presence_error_get_type (void); +#define GSM_PRESENCE_TYPE_ERROR (gsm_presence_error_get_type ()) + +GQuark gsm_presence_error_quark (void); + +GType gsm_presence_get_type (void) G_GNUC_CONST; + +GsmPresence * gsm_presence_new (void); + +void gsm_presence_set_idle_enabled (GsmPresence *presence, + gboolean enabled); +void gsm_presence_set_idle_timeout (GsmPresence *presence, + guint n_seconds); + +/* exported to bus */ +gboolean gsm_presence_set_status (GsmPresence *presence, + guint status, + GError **error); +gboolean gsm_presence_set_status_text (GsmPresence *presence, + const char *status_text, + GError **error); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_PRESENCE_H__ */ diff --git a/mate-session/gsm-session-save.c b/mate-session/gsm-session-save.c new file mode 100644 index 0000000..b31d9fa --- /dev/null +++ b/mate-session/gsm-session-save.c @@ -0,0 +1,254 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * gsm-session-save.c + * Copyright (C) 2008 Lucas Rocha. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 <glib.h> +#include <glib/gstdio.h> + +#include "gsm-util.h" +#include "gsm-autostart-app.h" +#include "gsm-client.h" + +#include "gsm-session-save.h" + +static gboolean gsm_session_clear_saved_session (const char *directory, + GHashTable *discard_hash); + +typedef struct { + char *dir; + GHashTable *discard_hash; + GError **error; +} SessionSaveData; + +static gboolean +save_one_client (char *id, + GObject *object, + SessionSaveData *data) +{ + GsmClient *client; + GKeyFile *keyfile; + char *path = NULL; + char *filename = NULL; + char *contents = NULL; + gsize length = 0; + char *discard_exec; + GError *local_error; + + client = GSM_CLIENT (object); + + local_error = NULL; + + keyfile = gsm_client_save (client, &local_error); + + if (keyfile == NULL || local_error) { + goto out; + } + + contents = g_key_file_to_data (keyfile, &length, &local_error); + + if (local_error) { + goto out; + } + + filename = g_strdup_printf ("%s.desktop", + gsm_client_peek_startup_id (client)); + + path = g_build_filename (data->dir, filename, NULL); + + g_file_set_contents (path, + contents, + length, + &local_error); + + if (local_error) { + goto out; + } + + discard_exec = g_key_file_get_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + GSM_AUTOSTART_APP_DISCARD_KEY, + NULL); + if (discard_exec) { + g_hash_table_insert (data->discard_hash, + discard_exec, discard_exec); + } + + g_debug ("GsmSessionSave: saved client %s to %s", id, filename); + +out: + if (keyfile != NULL) { + g_key_file_free (keyfile); + } + + g_free (contents); + g_free (filename); + g_free (path); + + /* in case of any error, stop saving session */ + if (local_error) { + g_propagate_error (data->error, local_error); + g_error_free (local_error); + + return TRUE; + } + + return FALSE; +} + +void +gsm_session_save (GsmStore *client_store, + GError **error) +{ + const char *save_dir; + char *tmp_dir; + SessionSaveData data; + + g_debug ("GsmSessionSave: Saving session"); + + save_dir = gsm_util_get_saved_session_dir (); + if (save_dir == NULL) { + g_warning ("GsmSessionSave: cannot create saved session directory"); + return; + } + + tmp_dir = gsm_util_get_empty_tmp_session_dir (); + if (tmp_dir == NULL) { + g_warning ("GsmSessionSave: cannot create new saved session directory"); + return; + } + + /* save the session in a temp directory, and remember the discard + * commands */ + data.dir = tmp_dir; + data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + data.error = error; + + gsm_store_foreach (client_store, + (GsmStoreFunc) save_one_client, + &data); + + if (!*error) { + /* remove the old saved session */ + gsm_session_clear_saved_session (save_dir, data.discard_hash); + + /* rename the temp session dir */ + if (g_file_test (save_dir, G_FILE_TEST_IS_DIR)) + g_rmdir (save_dir); + g_rename (tmp_dir, save_dir); + } else { + g_warning ("GsmSessionSave: error saving session: %s", (*error)->message); + /* FIXME: we should create a hash table filled with the discard + * commands that are in desktop files from save_dir. */ + gsm_session_clear_saved_session (tmp_dir, NULL); + g_rmdir (tmp_dir); + } + + g_hash_table_destroy (data.discard_hash); + g_free (tmp_dir); +} + +static gboolean +gsm_session_clear_one_client (const char *filename, + GHashTable *discard_hash) +{ + gboolean result = TRUE; + GKeyFile *key_file = NULL; + char *discard_exec = NULL; + + g_debug ("GsmSessionSave: removing '%s' from saved session", filename); + + key_file = g_key_file_new (); + if (g_key_file_load_from_file (key_file, filename, + G_KEY_FILE_NONE, NULL)) { + char **argv; + int argc; + + discard_exec = g_key_file_get_string (key_file, + G_KEY_FILE_DESKTOP_GROUP, + GSM_AUTOSTART_APP_DISCARD_KEY, + NULL); + if (!discard_exec) + goto out; + + if (g_hash_table_lookup (discard_hash, discard_exec)) + goto out; + + if (!g_shell_parse_argv (discard_exec, &argc, &argv, NULL)) + goto out; + + result = g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, NULL) && result; + + g_strfreev (argv); + } else { + result = FALSE; + } + +out: + if (key_file) + g_key_file_free (key_file); + if (discard_exec) + g_free (discard_exec); + + result = (g_unlink (filename) == 0) && result; + + return result; +} + +static gboolean +gsm_session_clear_saved_session (const char *directory, + GHashTable *discard_hash) +{ + GDir *dir; + const char *filename; + gboolean result = TRUE; + GError *error; + + g_debug ("GsmSessionSave: clearing currently saved session at %s", + directory); + + if (directory == NULL) { + return FALSE; + } + + error = NULL; + dir = g_dir_open (directory, 0, &error); + if (error) { + g_warning ("GsmSessionSave: error loading saved session directory: %s", error->message); + g_error_free (error); + return FALSE; + } + + while ((filename = g_dir_read_name (dir))) { + char *path = g_build_filename (directory, + filename, NULL); + + result = gsm_session_clear_one_client (path, discard_hash) + && result; + + g_free (path); + } + + g_dir_close (dir); + + return result; +} diff --git a/mate-session/gsm-session-save.h b/mate-session/gsm-session-save.h new file mode 100644 index 0000000..2939bd9 --- /dev/null +++ b/mate-session/gsm-session-save.h @@ -0,0 +1,38 @@ +/* gsm-session-save.h + * Copyright (C) 2008 Lucas Rocha. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 __GSM_SESSION_SAVE_H__ +#define __GSM_SESSION_SAVE_H__ + +#include <glib.h> + +#include "gsm-store.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void gsm_session_save (GsmStore *client_store, + GError **error); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_SESSION_SAVE_H__ */ diff --git a/mate-session/gsm-store.c b/mate-session/gsm-store.c new file mode 100644 index 0000000..e250d8d --- /dev/null +++ b/mate-session/gsm-store.c @@ -0,0 +1,413 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007-2008 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#include "gsm-store.h" + +#define GSM_STORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_STORE, GsmStorePrivate)) + +struct GsmStorePrivate +{ + GHashTable *objects; + gboolean locked; +}; + +enum { + ADDED, + REMOVED, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_LOCKED +}; + +static guint signals [LAST_SIGNAL] = { 0 }; + +static void gsm_store_class_init (GsmStoreClass *klass); +static void gsm_store_init (GsmStore *store); +static void gsm_store_finalize (GObject *object); + +G_DEFINE_TYPE (GsmStore, gsm_store, G_TYPE_OBJECT) + +GQuark +gsm_store_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gsm_store_error"); + } + + return ret; +} + +guint +gsm_store_size (GsmStore *store) +{ + return g_hash_table_size (store->priv->objects); +} + +gboolean +gsm_store_remove (GsmStore *store, + const char *id) +{ + GObject *found; + gboolean removed; + char *id_copy; + + g_return_val_if_fail (store != NULL, FALSE); + + found = g_hash_table_lookup (store->priv->objects, id); + if (found == NULL) { + return FALSE; + } + + id_copy = g_strdup (id); + + g_object_ref (found); + + removed = g_hash_table_remove (store->priv->objects, id_copy); + g_assert (removed); + + g_signal_emit (store, signals [REMOVED], 0, id_copy); + + g_object_unref (found); + g_free (id_copy); + + return TRUE; +} + +void +gsm_store_foreach (GsmStore *store, + GsmStoreFunc func, + gpointer user_data) +{ + g_return_if_fail (store != NULL); + g_return_if_fail (func != NULL); + + g_hash_table_find (store->priv->objects, + (GHRFunc)func, + user_data); +} + +GObject * +gsm_store_find (GsmStore *store, + GsmStoreFunc predicate, + gpointer user_data) +{ + GObject *object; + + g_return_val_if_fail (store != NULL, NULL); + g_return_val_if_fail (predicate != NULL, NULL); + + object = g_hash_table_find (store->priv->objects, + (GHRFunc)predicate, + user_data); + return object; +} + +GObject * +gsm_store_lookup (GsmStore *store, + const char *id) +{ + GObject *object; + + g_return_val_if_fail (store != NULL, NULL); + g_return_val_if_fail (id != NULL, NULL); + + object = g_hash_table_lookup (store->priv->objects, id); + + return object; +} + + +typedef struct +{ + GsmStoreFunc func; + gpointer user_data; + GsmStore *store; + GList *removed; +} WrapperData; + +static gboolean +foreach_remove_wrapper (const char *id, + GObject *object, + WrapperData *data) +{ + gboolean res; + + res = (data->func) (id, object, data->user_data); + if (res) { + data->removed = g_list_prepend (data->removed, g_strdup (id)); + } + + return res; +} + +guint +gsm_store_foreach_remove (GsmStore *store, + GsmStoreFunc func, + gpointer user_data) +{ + guint ret; + WrapperData data; + + g_return_val_if_fail (store != NULL, 0); + g_return_val_if_fail (func != NULL, 0); + + data.store = store; + data.user_data = user_data; + data.func = func; + data.removed = NULL; + + ret = g_hash_table_foreach_remove (store->priv->objects, + (GHRFunc)foreach_remove_wrapper, + &data); + + while (data.removed != NULL) { + char *id; + id = data.removed->data; + g_debug ("GsmStore: emitting removed for %s", id); + g_signal_emit (store, signals [REMOVED], 0, id); + g_free (data.removed->data); + data.removed->data = NULL; + data.removed = g_list_delete_link (data.removed, data.removed); + } + + return ret; +} + +static gboolean +_remove_all (const char *id, + GObject *object, + gpointer data) +{ + return TRUE; +} + +void +gsm_store_clear (GsmStore *store) +{ + g_return_if_fail (store != NULL); + + g_debug ("GsmStore: Clearing object store"); + + gsm_store_foreach_remove (store, + _remove_all, + NULL); +} + +gboolean +gsm_store_add (GsmStore *store, + const char *id, + GObject *object) +{ + g_return_val_if_fail (store != NULL, FALSE); + g_return_val_if_fail (id != NULL, FALSE); + g_return_val_if_fail (object != NULL, FALSE); + + /* If we're locked, we don't accept any new session + objects. */ + if (store->priv->locked) { + return FALSE; + } + + g_debug ("GsmStore: Adding object id %s to store", id); + + g_hash_table_insert (store->priv->objects, + g_strdup (id), + g_object_ref (object)); + + g_signal_emit (store, signals [ADDED], 0, id); + + return TRUE; +} + +void +gsm_store_set_locked (GsmStore *store, + gboolean locked) +{ + g_return_if_fail (GSM_IS_STORE (store)); + + store->priv->locked = locked; +} + +gboolean +gsm_store_get_locked (GsmStore *store) +{ + g_return_val_if_fail (GSM_IS_STORE (store), FALSE); + + return store->priv->locked; +} + +static void +gsm_store_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmStore *self; + + self = GSM_STORE (object); + + switch (prop_id) { + case PROP_LOCKED: + gsm_store_set_locked (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_store_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmStore *self; + + self = GSM_STORE (object); + + switch (prop_id) { + case PROP_LOCKED: + g_value_set_boolean (value, self->priv->locked); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_store_dispose (GObject *object) +{ + GsmStore *store; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_STORE (object)); + + store = GSM_STORE (object); + + gsm_store_clear (store); + + G_OBJECT_CLASS (gsm_store_parent_class)->dispose (object); +} + +static void +gsm_store_class_init (GsmStoreClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gsm_store_get_property; + object_class->set_property = gsm_store_set_property; + object_class->finalize = gsm_store_finalize; + object_class->dispose = gsm_store_dispose; + + signals [ADDED] = + g_signal_new ("added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmStoreClass, added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, G_TYPE_STRING); + signals [REMOVED] = + g_signal_new ("removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmStoreClass, removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, G_TYPE_STRING); + g_object_class_install_property (object_class, + PROP_LOCKED, + g_param_spec_boolean ("locked", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GsmStorePrivate)); +} + +static void +_destroy_object (GObject *object) +{ + g_debug ("GsmStore: Unreffing object: %p", object); + g_object_unref (object); +} + +static void +gsm_store_init (GsmStore *store) +{ + + store->priv = GSM_STORE_GET_PRIVATE (store); + + store->priv->objects = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) _destroy_object); +} + +static void +gsm_store_finalize (GObject *object) +{ + GsmStore *store; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_STORE (object)); + + store = GSM_STORE (object); + + g_return_if_fail (store->priv != NULL); + + g_hash_table_destroy (store->priv->objects); + + G_OBJECT_CLASS (gsm_store_parent_class)->finalize (object); +} + +GsmStore * +gsm_store_new (void) +{ + GObject *object; + + object = g_object_new (GSM_TYPE_STORE, + NULL); + + return GSM_STORE (object); +} diff --git a/mate-session/gsm-store.h b/mate-session/gsm-store.h new file mode 100644 index 0000000..ee7be10 --- /dev/null +++ b/mate-session/gsm-store.h @@ -0,0 +1,101 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007-2008 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GSM_STORE_H +#define __GSM_STORE_H + +#include <glib-object.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_STORE (gsm_store_get_type ()) +#define GSM_STORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_STORE, GsmStore)) +#define GSM_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_STORE, GsmStoreClass)) +#define GSM_IS_STORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_STORE)) +#define GSM_IS_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_STORE)) +#define GSM_STORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_STORE, GsmStoreClass)) + +typedef struct GsmStorePrivate GsmStorePrivate; + +typedef struct +{ + GObject parent; + GsmStorePrivate *priv; +} GsmStore; + +typedef struct +{ + GObjectClass parent_class; + + void (* added) (GsmStore *store, + const char *id); + void (* removed) (GsmStore *store, + const char *id); +} GsmStoreClass; + +typedef enum +{ + GSM_STORE_ERROR_GENERAL +} GsmStoreError; + +#define GSM_STORE_ERROR gsm_store_error_quark () + +typedef gboolean (*GsmStoreFunc) (const char *id, + GObject *object, + gpointer user_data); + +GQuark gsm_store_error_quark (void); +GType gsm_store_get_type (void); + +GsmStore * gsm_store_new (void); + +gboolean gsm_store_get_locked (GsmStore *store); +void gsm_store_set_locked (GsmStore *store, + gboolean locked); + +guint gsm_store_size (GsmStore *store); +gboolean gsm_store_add (GsmStore *store, + const char *id, + GObject *object); +void gsm_store_clear (GsmStore *store); +gboolean gsm_store_remove (GsmStore *store, + const char *id); + +void gsm_store_foreach (GsmStore *store, + GsmStoreFunc func, + gpointer user_data); +guint gsm_store_foreach_remove (GsmStore *store, + GsmStoreFunc func, + gpointer user_data); +GObject * gsm_store_find (GsmStore *store, + GsmStoreFunc predicate, + gpointer user_data); +GObject * gsm_store_lookup (GsmStore *store, + const char *id); + + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_STORE_H */ diff --git a/mate-session/gsm-util.c b/mate-session/gsm-util.c new file mode 100644 index 0000000..5d2983e --- /dev/null +++ b/mate-session/gsm-util.c @@ -0,0 +1,505 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * gsm-util.c + * Copyright (C) 2008 Lucas Rocha. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 <ctype.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/time.h> +#include <errno.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> + +#include <dbus/dbus-glib.h> + +#include "gsm-util.h" + +static gchar *_saved_session_dir = NULL; + +char * +gsm_util_find_desktop_file_for_app_name (const char *name, + char **autostart_dirs) +{ + char *app_path; + char **app_dirs; + GKeyFile *key_file; + char *desktop_file; + int i; + + app_path = NULL; + + app_dirs = gsm_util_get_app_dirs (); + + key_file = g_key_file_new (); + + desktop_file = g_strdup_printf ("%s.desktop", name); + + g_debug ("GsmUtil: Looking for file '%s'", desktop_file); + + for (i = 0; app_dirs[i] != NULL; i++) { + g_debug ("GsmUtil: Looking in '%s'", app_dirs[i]); + } + + g_key_file_load_from_dirs (key_file, + desktop_file, + (const char **) app_dirs, + &app_path, + G_KEY_FILE_NONE, + NULL); + + if (app_path != NULL) { + g_debug ("GsmUtil: found in XDG app dirs: '%s'", app_path); + } + + if (app_path == NULL && autostart_dirs != NULL) { + g_key_file_load_from_dirs (key_file, + desktop_file, + (const char **) autostart_dirs, + &app_path, + G_KEY_FILE_NONE, + NULL); + if (app_path != NULL) { + g_debug ("GsmUtil: found in autostart dirs: '%s'", app_path); + } + + } + + /* look for mate vender prefix */ + if (app_path == NULL) { + g_free (desktop_file); + desktop_file = g_strdup_printf ("mate-%s.desktop", name); + + g_key_file_load_from_dirs (key_file, + desktop_file, + (const char **) app_dirs, + &app_path, + G_KEY_FILE_NONE, + NULL); + if (app_path != NULL) { + g_debug ("GsmUtil: found in XDG app dirs: '%s'", app_path); + } + } + + if (app_path == NULL && autostart_dirs != NULL) { + g_key_file_load_from_dirs (key_file, + desktop_file, + (const char **) autostart_dirs, + &app_path, + G_KEY_FILE_NONE, + NULL); + if (app_path != NULL) { + g_debug ("GsmUtil: found in autostart dirs: '%s'", app_path); + } + } + + g_free (desktop_file); + g_key_file_free (key_file); + + g_strfreev (app_dirs); + + return app_path; +} + +static gboolean +ensure_dir_exists (const char *dir) +{ + if (g_file_test (dir, G_FILE_TEST_IS_DIR)) + return TRUE; + + if (g_mkdir_with_parents (dir, 0755) == 0) + return TRUE; + + if (errno == EEXIST) + return g_file_test (dir, G_FILE_TEST_IS_DIR); + + g_warning ("GsmSessionSave: Failed to create directory %s: %s", dir, strerror (errno)); + + return FALSE; +} + +gchar * +gsm_util_get_empty_tmp_session_dir (void) +{ + char *tmp; + gboolean exists; + + tmp = g_build_filename (g_get_user_config_dir (), + "mate-session", + "saved-session.new", + NULL); + + exists = ensure_dir_exists (tmp); + + if (G_UNLIKELY (!exists)) { + g_warning ("GsmSessionSave: could not create directory for saved session: %s", tmp); + g_free (tmp); + return NULL; + } else { + /* make sure it's empty */ + GDir *dir; + const char *filename; + + dir = g_dir_open (tmp, 0, NULL); + if (dir) { + while ((filename = g_dir_read_name (dir))) { + char *path = g_build_filename (tmp, filename, + NULL); + g_unlink (path); + } + g_dir_close (dir); + } + } + + return tmp; +} + +const gchar * +gsm_util_get_saved_session_dir (void) +{ + if (_saved_session_dir == NULL) { + gboolean exists; + + _saved_session_dir = + g_build_filename (g_get_user_config_dir (), + "mate-session", + "saved-session", + NULL); + + exists = ensure_dir_exists (_saved_session_dir); + + if (G_UNLIKELY (!exists)) { + static gboolean printed_warning = FALSE; + + if (!printed_warning) { + g_warning ("GsmSessionSave: could not create directory for saved session: %s", _saved_session_dir); + printed_warning = TRUE; + } + + _saved_session_dir = NULL; + + return NULL; + } + } + + return _saved_session_dir; +} + + +char ** +gsm_util_get_autostart_dirs () +{ + GPtrArray *dirs; + const char * const *system_config_dirs; + const char * const *system_data_dirs; + int i; + + dirs = g_ptr_array_new (); + + g_ptr_array_add (dirs, + g_build_filename (g_get_user_config_dir (), + "autostart", NULL)); + + system_data_dirs = g_get_system_data_dirs (); + for (i = 0; system_data_dirs[i]; i++) { + g_ptr_array_add (dirs, + g_build_filename (system_data_dirs[i], + "mate", "autostart", NULL)); + } + + system_config_dirs = g_get_system_config_dirs (); + for (i = 0; system_config_dirs[i]; i++) { + g_ptr_array_add (dirs, + g_build_filename (system_config_dirs[i], + "autostart", NULL)); + } + + g_ptr_array_add (dirs, NULL); + + return (char **) g_ptr_array_free (dirs, FALSE); +} + +char ** +gsm_util_get_app_dirs () +{ + GPtrArray *dirs; + const char * const *system_data_dirs; + int i; + + dirs = g_ptr_array_new (); + + g_ptr_array_add (dirs, + g_build_filename (g_get_user_data_dir (), + "applications", + NULL)); + + system_data_dirs = g_get_system_data_dirs (); + for (i = 0; system_data_dirs[i]; i++) { + g_ptr_array_add (dirs, + g_build_filename (system_data_dirs[i], + "applications", + NULL)); + } + + g_ptr_array_add (dirs, NULL); + + return (char **) g_ptr_array_free (dirs, FALSE); +} + +char ** +gsm_util_get_desktop_dirs () +{ + char **apps; + char **autostart; + char **result; + int size; + int i; + + apps = gsm_util_get_app_dirs (); + autostart = gsm_util_get_autostart_dirs (); + + size = 0; + for (i = 0; apps[i] != NULL; i++) { size++; } + for (i = 0; autostart[i] != NULL; i++) { size++; } + size += 2; /* saved session + last NULL */ + + result = g_new (char *, size + 1); + + for (i = 0; apps[i] != NULL; i++) { + result[i] = apps[i]; + } + g_free (apps); + size = i; + + for (i = 0; autostart[i] != NULL; i++) { + result[size + i] = autostart[i]; + } + g_free (autostart); + size = size + i; + + result[size] = g_strdup (gsm_util_get_saved_session_dir ()); + result[size + 1] = NULL; + + return result; +} + +gboolean +gsm_util_text_is_blank (const char *str) +{ + if (str == NULL) { + return TRUE; + } + + while (*str) { + if (!isspace(*str)) { + return FALSE; + } + + str++; + } + + return TRUE; +} + +/** + * gsm_util_init_error: + * @fatal: whether or not the error is fatal to the login session + * @format: printf-style error message format + * @...: error message args + * + * Displays the error message to the user. If @fatal is %TRUE, gsm + * will exit after displaying the message. + * + * This should be called for major errors that occur before the + * session is up and running. (Notably, it positions the dialog box + * itself, since no window manager will be running yet.) + **/ +void +gsm_util_init_error (gboolean fatal, + const char *format, ...) +{ + GtkWidget *dialog; + char *msg; + va_list args; + + va_start (args, format); + msg = g_strdup_vprintf (format, args); + va_end (args); + + /* If option parsing failed, Gtk won't have been initialized... */ + if (!gdk_display_get_default ()) { + if (!gtk_init_check (NULL, NULL)) { + /* Oh well, no X for you! */ + g_printerr (_("Unable to start login session (and unable to connect to the X server)")); + g_printerr ("%s", msg); + exit (1); + } + } + + dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, "%s", msg); + + g_free (msg); + + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); + gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (dialog); + + if (fatal) { + gtk_main_quit (); + } +} + +/** + * gsm_util_generate_startup_id: + * + * Generates a new SM client ID. + * + * Return value: an SM client ID. + **/ +char * +gsm_util_generate_startup_id (void) +{ + static int sequence = -1; + static guint rand1 = 0; + static guint rand2 = 0; + static pid_t pid = 0; + struct timeval tv; + + /* The XSMP spec defines the ID as: + * + * Version: "1" + * Address type and address: + * "1" + an IPv4 address as 8 hex digits + * "2" + a DECNET address as 12 hex digits + * "6" + an IPv6 address as 32 hex digits + * Time stamp: milliseconds since UNIX epoch as 13 decimal digits + * Process-ID type and process-ID: + * "1" + POSIX PID as 10 decimal digits + * Sequence number as 4 decimal digits + * + * XSMP client IDs are supposed to be globally unique: if + * SmsGenerateClientID() is unable to determine a network + * address for the machine, it gives up and returns %NULL. + * MATE and KDE have traditionally used a fourth address + * format in this case: + * "0" + 16 random hex digits + * + * We don't even bother trying SmsGenerateClientID(), since the + * user's IP address is probably "192.168.1.*" anyway, so a random + * number is actually more likely to be globally unique. + */ + + if (!rand1) { + rand1 = g_random_int (); + rand2 = g_random_int (); + pid = getpid (); + } + + sequence = (sequence + 1) % 10000; + gettimeofday (&tv, NULL); + return g_strdup_printf ("10%.04x%.04x%.10lu%.3u%.10lu%.4d", + rand1, + rand2, + (unsigned long) tv.tv_sec, + (unsigned) tv.tv_usec, + (unsigned long) pid, + sequence); +} + +static gboolean +gsm_util_update_activation_environment (const char *variable, + const char *value, + GError **error) +{ + DBusGConnection *dbus_connection; + DBusGProxy *bus_proxy; + GHashTable *environment; + gboolean environment_updated; + + environment_updated = FALSE; + bus_proxy = NULL; + environment = NULL; + + dbus_connection = dbus_g_bus_get (DBUS_BUS_SESSION, error); + + if (dbus_connection == NULL) { + return FALSE; + } + + bus_proxy = dbus_g_proxy_new_for_name_owner (dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + error); + + if (bus_proxy == NULL) { + goto out; + } + + environment = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (environment, (void *) variable, (void *) value); + + if (!dbus_g_proxy_call (bus_proxy, + "UpdateActivationEnvironment", error, + DBUS_TYPE_G_STRING_STRING_HASHTABLE, + environment, G_TYPE_INVALID, + G_TYPE_INVALID)) + goto out; + + environment_updated = TRUE; + + out: + + if (bus_proxy != NULL) { + g_object_unref (bus_proxy); + } + + if (environment != NULL) { + g_hash_table_destroy (environment); + } + + return environment_updated; +} + +void +gsm_util_setenv (const char *variable, + const char *value) +{ + GError *bus_error; + + g_setenv (variable, value, TRUE); + + bus_error = NULL; + + /* If this fails it isn't fatal, it means some things like session + * management and keyring won't work in activated clients. + */ + if (!gsm_util_update_activation_environment (variable, value, &bus_error)) { + g_warning ("Could not make bus activated clients aware of %s=%s environment variable: %s", variable, value, bus_error->message); + g_error_free (bus_error); + } +} diff --git a/mate-session/gsm-util.h b/mate-session/gsm-util.h new file mode 100644 index 0000000..2521f9d --- /dev/null +++ b/mate-session/gsm-util.h @@ -0,0 +1,56 @@ +/* gsm-util.h + * Copyright (C) 2008 Lucas Rocha. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 __GSM_UTIL_H__ +#define __GSM_UTIL_H__ + +#include <glib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +char * gsm_util_find_desktop_file_for_app_name (const char *app_name, + char **dirs); + +gchar *gsm_util_get_empty_tmp_session_dir (void); + +const char *gsm_util_get_saved_session_dir (void); + +gchar** gsm_util_get_app_dirs (void); + +gchar** gsm_util_get_autostart_dirs (void); + +gchar ** gsm_util_get_desktop_dirs (void); + +gboolean gsm_util_text_is_blank (const char *str); + +void gsm_util_init_error (gboolean fatal, + const char *format, ...); + +char * gsm_util_generate_startup_id (void); + +void gsm_util_setenv (const char *variable, + const char *value); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_UTIL_H__ */ diff --git a/mate-session/gsm-xsmp-client.c b/mate-session/gsm-xsmp-client.c new file mode 100644 index 0000000..ce0c92f --- /dev/null +++ b/mate-session/gsm-xsmp-client.c @@ -0,0 +1,1332 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <errno.h> + +#include <gio/gio.h> +#include <glib/gi18n.h> + +#include "gsm-xsmp-client.h" +#include "gsm-marshal.h" + +#include "gsm-util.h" +#include "gsm-autostart-app.h" +#include "gsm-manager.h" + +#define GsmDesktopFile "_GSM_DesktopFile" + +#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0') + +#define GSM_XSMP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientPrivate)) + +struct GsmXSMPClientPrivate +{ + + SmsConn conn; + IceConn ice_connection; + + guint watch_id; + + char *description; + GPtrArray *props; + + /* SaveYourself state */ + int current_save_yourself; + int next_save_yourself; + guint next_save_yourself_allow_interact : 1; +}; + +enum { + PROP_0, + PROP_ICE_CONNECTION +}; + +enum { + REGISTER_REQUEST, + LOGOUT_REQUEST, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (GsmXSMPClient, gsm_xsmp_client, GSM_TYPE_CLIENT) + +static gboolean +client_iochannel_watch (GIOChannel *channel, + GIOCondition condition, + GsmXSMPClient *client) +{ + gboolean keep_going; + + g_object_ref (client); + switch (IceProcessMessages (client->priv->ice_connection, NULL, NULL)) { + case IceProcessMessagesSuccess: + keep_going = TRUE; + break; + + case IceProcessMessagesIOError: + g_debug ("GsmXSMPClient: IceProcessMessagesIOError on '%s'", client->priv->description); + gsm_client_set_status (GSM_CLIENT (client), GSM_CLIENT_FAILED); + /* Emitting "disconnected" will eventually cause + * IceCloseConnection() to be called. + */ + gsm_client_disconnected (GSM_CLIENT (client)); + keep_going = FALSE; + break; + + case IceProcessMessagesConnectionClosed: + g_debug ("GsmXSMPClient: IceProcessMessagesConnectionClosed on '%s'", + client->priv->description); + client->priv->ice_connection = NULL; + keep_going = FALSE; + break; + + default: + g_assert_not_reached (); + } + g_object_unref (client); + + return keep_going; +} + +static SmProp * +find_property (GsmXSMPClient *client, + const char *name, + int *index) +{ + SmProp *prop; + int i; + + for (i = 0; i < client->priv->props->len; i++) { + prop = client->priv->props->pdata[i]; + + if (!strcmp (prop->name, name)) { + if (index) { + *index = i; + } + return prop; + } + } + + return NULL; +} + +static void +set_description (GsmXSMPClient *client) +{ + SmProp *prop; + const char *id; + + prop = find_property (client, SmProgram, NULL); + id = gsm_client_peek_startup_id (GSM_CLIENT (client)); + + g_free (client->priv->description); + if (prop) { + client->priv->description = g_strdup_printf ("%p [%.*s %s]", + client, + prop->vals[0].length, + (char *)prop->vals[0].value, + id); + } else if (id != NULL) { + client->priv->description = g_strdup_printf ("%p [%s]", client, id); + } else { + client->priv->description = g_strdup_printf ("%p", client); + } +} + +static void +setup_connection (GsmXSMPClient *client) +{ + GIOChannel *channel; + int fd; + + g_debug ("GsmXSMPClient: Setting up new connection"); + + fd = IceConnectionNumber (client->priv->ice_connection); + fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC); + channel = g_io_channel_unix_new (fd); + client->priv->watch_id = g_io_add_watch (channel, + G_IO_IN | G_IO_ERR, + (GIOFunc)client_iochannel_watch, + client); + g_io_channel_unref (channel); + + set_description (client); + + g_debug ("GsmXSMPClient: New client '%s'", client->priv->description); +} + +static GObject * +gsm_xsmp_client_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmXSMPClient *client; + + client = GSM_XSMP_CLIENT (G_OBJECT_CLASS (gsm_xsmp_client_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + setup_connection (client); + + return G_OBJECT (client); +} + +static void +gsm_xsmp_client_init (GsmXSMPClient *client) +{ + client->priv = GSM_XSMP_CLIENT_GET_PRIVATE (client); + + client->priv->props = g_ptr_array_new (); + client->priv->current_save_yourself = -1; + client->priv->next_save_yourself = -1; + client->priv->next_save_yourself_allow_interact = FALSE; +} + + +static void +delete_property (GsmXSMPClient *client, + const char *name) +{ + int index; + SmProp *prop; + + prop = find_property (client, name, &index); + if (!prop) { + return; + } + +#if 0 + /* This is wrong anyway; we can't unconditionally run the current + * discard command; if this client corresponds to a GsmAppResumed, + * and the current discard command is identical to the app's + * discard_command, then we don't run the discard command now, + * because that would delete a saved state we may want to resume + * again later. + */ + if (!strcmp (name, SmDiscardCommand)) { + gsm_client_run_discard (GSM_CLIENT (client)); + } +#endif + + g_ptr_array_remove_index_fast (client->priv->props, index); + SmFreeProperty (prop); +} + + +static void +debug_print_property (SmProp *prop) +{ + GString *tmp; + int i; + + switch (prop->type[0]) { + case 'C': /* CARD8 */ + g_debug ("GsmXSMPClient: %s = %d", prop->name, *(unsigned char *)prop->vals[0].value); + break; + + case 'A': /* ARRAY8 */ + g_debug ("GsmXSMPClient: %s = '%s'", prop->name, (char *)prop->vals[0].value); + break; + + case 'L': /* LISTofARRAY8 */ + tmp = g_string_new (NULL); + for (i = 0; i < prop->num_vals; i++) { + g_string_append_printf (tmp, "'%.*s' ", prop->vals[i].length, + (char *)prop->vals[i].value); + } + g_debug ("GsmXSMPClient: %s = %s", prop->name, tmp->str); + g_string_free (tmp, TRUE); + break; + + default: + g_debug ("GsmXSMPClient: %s = ??? (%s)", prop->name, prop->type); + break; + } +} + + +static void +set_properties_callback (SmsConn conn, + SmPointer manager_data, + int num_props, + SmProp **props) +{ + GsmXSMPClient *client = manager_data; + int i; + + g_debug ("GsmXSMPClient: Set properties from client '%s'", client->priv->description); + + for (i = 0; i < num_props; i++) { + delete_property (client, props[i]->name); + g_ptr_array_add (client->priv->props, props[i]); + + debug_print_property (props[i]); + + if (!strcmp (props[i]->name, SmProgram)) + set_description (client); + } + + free (props); + +} + +static void +delete_properties_callback (SmsConn conn, + SmPointer manager_data, + int num_props, + char **prop_names) +{ + GsmXSMPClient *client = manager_data; + int i; + + g_debug ("GsmXSMPClient: Delete properties from '%s'", client->priv->description); + + for (i = 0; i < num_props; i++) { + delete_property (client, prop_names[i]); + + g_debug (" %s", prop_names[i]); + } + + free (prop_names); +} + +static void +get_properties_callback (SmsConn conn, + SmPointer manager_data) +{ + GsmXSMPClient *client = manager_data; + + g_debug ("GsmXSMPClient: Get properties request from '%s'", client->priv->description); + + SmsReturnProperties (conn, + client->priv->props->len, + (SmProp **)client->priv->props->pdata); +} + +static char * +prop_to_command (SmProp *prop) +{ + GString *str; + int i, j; + gboolean need_quotes; + + str = g_string_new (NULL); + for (i = 0; i < prop->num_vals; i++) { + char *val = prop->vals[i].value; + + need_quotes = FALSE; + for (j = 0; j < prop->vals[i].length; j++) { + if (!g_ascii_isalnum (val[j]) && !strchr ("-_=:./", val[j])) { + need_quotes = TRUE; + break; + } + } + + if (i > 0) { + g_string_append_c (str, ' '); + } + + if (!need_quotes) { + g_string_append_printf (str, + "%.*s", + prop->vals[i].length, + (char *)prop->vals[i].value); + } else { + g_string_append_c (str, '\''); + while (val < (char *)prop->vals[i].value + prop->vals[i].length) { + if (*val == '\'') { + g_string_append (str, "'\''"); + } else { + g_string_append_c (str, *val); + } + val++; + } + g_string_append_c (str, '\''); + } + } + + return g_string_free (str, FALSE); +} + +static char * +xsmp_get_restart_command (GsmClient *client) +{ + SmProp *prop; + + prop = find_property (GSM_XSMP_CLIENT (client), SmRestartCommand, NULL); + + if (!prop || strcmp (prop->type, SmLISTofARRAY8) != 0) { + return NULL; + } + + return prop_to_command (prop); +} + +static char * +xsmp_get_discard_command (GsmClient *client) +{ + SmProp *prop; + + prop = find_property (GSM_XSMP_CLIENT (client), SmDiscardCommand, NULL); + + if (!prop || strcmp (prop->type, SmLISTofARRAY8) != 0) { + return NULL; + } + + return prop_to_command (prop); +} + +static void +do_save_yourself (GsmXSMPClient *client, + int save_type, + gboolean allow_interact) +{ + g_assert (client->priv->conn != NULL); + + if (client->priv->next_save_yourself != -1) { + /* Either we're currently doing a shutdown and there's a checkpoint + * queued after it, or vice versa. Either way, the new SaveYourself + * is redundant. + */ + g_debug ("GsmXSMPClient: skipping redundant SaveYourself for '%s'", + client->priv->description); + } else if (client->priv->current_save_yourself != -1) { + g_debug ("GsmXSMPClient: queuing new SaveYourself for '%s'", + client->priv->description); + client->priv->next_save_yourself = save_type; + client->priv->next_save_yourself_allow_interact = allow_interact; + } else { + client->priv->current_save_yourself = save_type; + /* make sure we don't have anything queued */ + client->priv->next_save_yourself = -1; + client->priv->next_save_yourself_allow_interact = FALSE; + + switch (save_type) { + case SmSaveLocal: + /* Save state */ + SmsSaveYourself (client->priv->conn, + SmSaveLocal, + FALSE, + SmInteractStyleNone, + FALSE); + break; + + default: + /* Logout */ + if (!allow_interact) { + SmsSaveYourself (client->priv->conn, + save_type, /* save type */ + TRUE, /* shutdown */ + SmInteractStyleNone, /* interact style */ + TRUE); /* fast */ + } else { + SmsSaveYourself (client->priv->conn, + save_type, /* save type */ + TRUE, /* shutdown */ + SmInteractStyleAny, /* interact style */ + FALSE /* fast */); + } + break; + } + } +} + +static void +xsmp_save_yourself_phase2 (GsmClient *client) +{ + GsmXSMPClient *xsmp = (GsmXSMPClient *) client; + + g_debug ("GsmXSMPClient: xsmp_save_yourself_phase2 ('%s')", xsmp->priv->description); + + SmsSaveYourselfPhase2 (xsmp->priv->conn); +} + +static void +xsmp_interact (GsmClient *client) +{ + GsmXSMPClient *xsmp = (GsmXSMPClient *) client; + + g_debug ("GsmXSMPClient: xsmp_interact ('%s')", xsmp->priv->description); + + SmsInteract (xsmp->priv->conn); +} + +static gboolean +xsmp_cancel_end_session (GsmClient *client, + GError **error) +{ + GsmXSMPClient *xsmp = (GsmXSMPClient *) client; + + g_debug ("GsmXSMPClient: xsmp_cancel_end_session ('%s')", xsmp->priv->description); + + if (xsmp->priv->conn == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Client is not registered"); + return FALSE; + } + + SmsShutdownCancelled (xsmp->priv->conn); + + /* reset the state */ + xsmp->priv->current_save_yourself = -1; + xsmp->priv->next_save_yourself = -1; + xsmp->priv->next_save_yourself_allow_interact = FALSE; + + return TRUE; +} + +static char * +get_desktop_file_path (GsmXSMPClient *client) +{ + SmProp *prop; + char *desktop_file_path = NULL; + char **dirs; + const char *program_name; + + /* XSMP clients using eggsmclient defines a special property + * pointing to their respective desktop entry file */ + prop = find_property (client, GsmDesktopFile, NULL); + + if (prop) { + GFile *file = g_file_new_for_uri (prop->vals[0].value); + desktop_file_path = g_file_get_path (file); + g_object_unref (file); + goto out; + } + + /* If we can't get desktop file from GsmDesktopFile then we + * try to find the desktop file from its program name */ + prop = find_property (client, SmProgram, NULL); + program_name = prop->vals[0].value; + + dirs = gsm_util_get_autostart_dirs (); + + desktop_file_path = + gsm_util_find_desktop_file_for_app_name (program_name, + dirs); + + g_strfreev (dirs); + +out: + g_debug ("GsmXSMPClient: desktop file for client %s is %s", + gsm_client_peek_id (GSM_CLIENT (client)), + desktop_file_path ? desktop_file_path : "(null)"); + + return desktop_file_path; +} + +static void +set_desktop_file_keys_from_client (GsmClient *client, + GKeyFile *keyfile) +{ + SmProp *prop; + char *name; + char *comment; + + prop = find_property (GSM_XSMP_CLIENT (client), SmProgram, NULL); + name = g_strdup (prop->vals[0].value); + + comment = g_strdup_printf ("Client %s which was automatically saved", + gsm_client_peek_startup_id (client)); + + g_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + G_KEY_FILE_DESKTOP_KEY_NAME, + name); + + g_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + G_KEY_FILE_DESKTOP_KEY_COMMENT, + comment); + + g_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + G_KEY_FILE_DESKTOP_KEY_ICON, + "system-run"); + + g_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + G_KEY_FILE_DESKTOP_KEY_TYPE, + "Application"); + + g_key_file_set_boolean (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, + TRUE); + + g_free (name); + g_free (comment); +} + +static GKeyFile * +create_client_key_file (GsmClient *client, + const char *desktop_file_path, + GError **error) { + GKeyFile *keyfile; + + keyfile = g_key_file_new (); + + if (desktop_file_path != NULL) { + g_key_file_load_from_file (keyfile, + desktop_file_path, + G_KEY_FILE_KEEP_COMMENTS | + G_KEY_FILE_KEEP_TRANSLATIONS, + error); + } else { + set_desktop_file_keys_from_client (client, keyfile); + } + + return keyfile; +} + +static GsmClientRestartStyle +xsmp_get_restart_style_hint (GsmClient *client); + +static GKeyFile * +xsmp_save (GsmClient *client, + GError **error) +{ + GsmClientRestartStyle restart_style; + + GKeyFile *keyfile = NULL; + char *desktop_file_path = NULL; + char *exec_program = NULL; + char *exec_discard = NULL; + char *startup_id = NULL; + GError *local_error; + + g_debug ("GsmXSMPClient: saving client with id %s", + gsm_client_peek_id (client)); + + local_error = NULL; + + restart_style = xsmp_get_restart_style_hint (client); + if (restart_style == GSM_CLIENT_RESTART_NEVER) { + goto out; + } + + exec_program = xsmp_get_restart_command (client); + if (!exec_program) { + goto out; + } + + desktop_file_path = get_desktop_file_path (GSM_XSMP_CLIENT (client)); + + keyfile = create_client_key_file (client, + desktop_file_path, + &local_error); + + if (local_error) { + goto out; + } + + g_object_get (client, + "startup-id", &startup_id, + NULL); + + g_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + GSM_AUTOSTART_APP_STARTUP_ID_KEY, + startup_id); + + g_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + G_KEY_FILE_DESKTOP_KEY_EXEC, + exec_program); + + exec_discard = xsmp_get_discard_command (client); + if (exec_discard) + g_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + GSM_AUTOSTART_APP_DISCARD_KEY, + exec_discard); + +out: + g_free (desktop_file_path); + g_free (exec_program); + g_free (exec_discard); + g_free (startup_id); + + if (local_error != NULL) { + g_propagate_error (error, local_error); + g_key_file_free (keyfile); + + return NULL; + } + + return keyfile; +} + +static gboolean +xsmp_stop (GsmClient *client, + GError **error) +{ + GsmXSMPClient *xsmp = (GsmXSMPClient *) client; + + g_debug ("GsmXSMPClient: xsmp_stop ('%s')", xsmp->priv->description); + + if (xsmp->priv->conn == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Client is not registered"); + return FALSE; + } + + SmsDie (xsmp->priv->conn); + + return TRUE; +} + +static gboolean +xsmp_query_end_session (GsmClient *client, + guint flags, + GError **error) +{ + gboolean allow_interact; + int save_type; + + if (GSM_XSMP_CLIENT (client)->priv->conn == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Client is not registered"); + return FALSE; + } + + allow_interact = !(flags & GSM_CLIENT_END_SESSION_FLAG_FORCEFUL); + + /* we don't want to save the session state, but we just want to know if + * there's user data the client has to save and we want to give the + * client a chance to tell the user about it. This is consistent with + * the manager not setting GSM_CLIENT_END_SESSION_FLAG_SAVE for this + * phase. */ + save_type = SmSaveGlobal; + + do_save_yourself (GSM_XSMP_CLIENT (client), save_type, allow_interact); + return TRUE; +} + +static gboolean +xsmp_end_session (GsmClient *client, + guint flags, + GError **error) +{ + gboolean phase2; + + if (GSM_XSMP_CLIENT (client)->priv->conn == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Client is not registered"); + return FALSE; + } + + phase2 = (flags & GSM_CLIENT_END_SESSION_FLAG_LAST); + + if (phase2) { + xsmp_save_yourself_phase2 (client); + } else { + gboolean allow_interact; + int save_type; + + /* we gave a chance to interact to the app during + * xsmp_query_end_session(), now it's too late to interact */ + allow_interact = FALSE; + + if (flags & GSM_CLIENT_END_SESSION_FLAG_SAVE) { + save_type = SmSaveBoth; + } else { + save_type = SmSaveGlobal; + } + + do_save_yourself (GSM_XSMP_CLIENT (client), + save_type, allow_interact); + } + + return TRUE; +} + +static char * +xsmp_get_app_name (GsmClient *client) +{ + SmProp *prop; + char *name; + + prop = find_property (GSM_XSMP_CLIENT (client), SmProgram, NULL); + name = prop_to_command (prop); + + return name; +} + +static void +gsm_client_set_ice_connection (GsmXSMPClient *client, + gpointer conn) +{ + client->priv->ice_connection = conn; +} + +static void +gsm_xsmp_client_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmXSMPClient *self; + + self = GSM_XSMP_CLIENT (object); + + switch (prop_id) { + case PROP_ICE_CONNECTION: + gsm_client_set_ice_connection (self, g_value_get_pointer (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_xsmp_client_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmXSMPClient *self; + + self = GSM_XSMP_CLIENT (object); + + switch (prop_id) { + case PROP_ICE_CONNECTION: + g_value_set_pointer (value, self->priv->ice_connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_xsmp_client_disconnect (GsmXSMPClient *client) +{ + if (client->priv->watch_id > 0) { + g_source_remove (client->priv->watch_id); + } + + if (client->priv->conn != NULL) { + SmsCleanUp (client->priv->conn); + } + + if (client->priv->ice_connection != NULL) { + IceSetShutdownNegotiation (client->priv->ice_connection, FALSE); + IceCloseConnection (client->priv->ice_connection); + } +} + +static void +gsm_xsmp_client_finalize (GObject *object) +{ + GsmXSMPClient *client = (GsmXSMPClient *) object; + + g_debug ("GsmXSMPClient: xsmp_finalize (%s)", client->priv->description); + gsm_xsmp_client_disconnect (client); + + g_free (client->priv->description); + g_ptr_array_foreach (client->priv->props, (GFunc)SmFreeProperty, NULL); + g_ptr_array_free (client->priv->props, TRUE); + + G_OBJECT_CLASS (gsm_xsmp_client_parent_class)->finalize (object); +} + +static gboolean +_boolean_handled_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy) +{ + gboolean continue_emission; + gboolean signal_handled; + + signal_handled = g_value_get_boolean (handler_return); + g_value_set_boolean (return_accu, signal_handled); + continue_emission = !signal_handled; + + return continue_emission; +} + +static GsmClientRestartStyle +xsmp_get_restart_style_hint (GsmClient *client) +{ + SmProp *prop; + GsmClientRestartStyle hint; + + g_debug ("GsmXSMPClient: getting restart style"); + hint = GSM_CLIENT_RESTART_IF_RUNNING; + + prop = find_property (GSM_XSMP_CLIENT (client), SmRestartStyleHint, NULL); + + if (!prop || strcmp (prop->type, SmCARD8) != 0) { + return GSM_CLIENT_RESTART_IF_RUNNING; + } + + switch (((unsigned char *)prop->vals[0].value)[0]) { + case SmRestartIfRunning: + hint = GSM_CLIENT_RESTART_IF_RUNNING; + break; + case SmRestartAnyway: + hint = GSM_CLIENT_RESTART_ANYWAY; + break; + case SmRestartImmediately: + hint = GSM_CLIENT_RESTART_IMMEDIATELY; + break; + case SmRestartNever: + hint = GSM_CLIENT_RESTART_NEVER; + break; + default: + break; + } + + return hint; +} + +static gboolean +_parse_value_as_uint (const char *value, + guint *uintval) +{ + char *end_of_valid_uint; + gulong ulong_value; + guint uint_value; + + errno = 0; + ulong_value = strtoul (value, &end_of_valid_uint, 10); + + if (*value == '\0' || *end_of_valid_uint != '\0') { + return FALSE; + } + + uint_value = ulong_value; + if (uint_value != ulong_value || errno == ERANGE) { + return FALSE; + } + + *uintval = uint_value; + + return TRUE; +} + +static guint +xsmp_get_unix_process_id (GsmClient *client) +{ + SmProp *prop; + guint pid; + gboolean res; + + g_debug ("GsmXSMPClient: getting pid"); + + prop = find_property (GSM_XSMP_CLIENT (client), SmProcessID, NULL); + + if (!prop || strcmp (prop->type, SmARRAY8) != 0) { + return 0; + } + + pid = 0; + res = _parse_value_as_uint ((char *)prop->vals[0].value, &pid); + if (! res) { + pid = 0; + } + + return pid; +} + +static void +gsm_xsmp_client_class_init (GsmXSMPClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GsmClientClass *client_class = GSM_CLIENT_CLASS (klass); + + object_class->finalize = gsm_xsmp_client_finalize; + object_class->constructor = gsm_xsmp_client_constructor; + object_class->get_property = gsm_xsmp_client_get_property; + object_class->set_property = gsm_xsmp_client_set_property; + + client_class->impl_save = xsmp_save; + client_class->impl_stop = xsmp_stop; + client_class->impl_query_end_session = xsmp_query_end_session; + client_class->impl_end_session = xsmp_end_session; + client_class->impl_cancel_end_session = xsmp_cancel_end_session; + client_class->impl_get_app_name = xsmp_get_app_name; + client_class->impl_get_restart_style_hint = xsmp_get_restart_style_hint; + client_class->impl_get_unix_process_id = xsmp_get_unix_process_id; + + signals[REGISTER_REQUEST] = + g_signal_new ("register-request", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmXSMPClientClass, register_request), + _boolean_handled_accumulator, + NULL, + gsm_marshal_BOOLEAN__POINTER, + G_TYPE_BOOLEAN, + 1, G_TYPE_POINTER); + signals[LOGOUT_REQUEST] = + g_signal_new ("logout-request", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmXSMPClientClass, logout_request), + NULL, + NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, G_TYPE_BOOLEAN); + + g_object_class_install_property (object_class, + PROP_ICE_CONNECTION, + g_param_spec_pointer ("ice-connection", + "ice-connection", + "ice-connection", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (klass, sizeof (GsmXSMPClientPrivate)); +} + +GsmClient * +gsm_xsmp_client_new (IceConn ice_conn) +{ + GsmXSMPClient *xsmp; + + xsmp = g_object_new (GSM_TYPE_XSMP_CLIENT, + "ice-connection", ice_conn, + NULL); + + return GSM_CLIENT (xsmp); +} + +static Status +register_client_callback (SmsConn conn, + SmPointer manager_data, + char *previous_id) +{ + GsmXSMPClient *client = manager_data; + gboolean handled; + char *id; + + g_debug ("GsmXSMPClient: Client '%s' received RegisterClient(%s)", + client->priv->description, + previous_id ? previous_id : "NULL"); + + + /* There are three cases: + * 1. id is NULL - we'll use a new one + * 2. id is known - we'll use known one + * 3. id is unknown - this is an error + */ + id = g_strdup (previous_id); + + handled = FALSE; + g_signal_emit (client, signals[REGISTER_REQUEST], 0, &id, &handled); + if (! handled) { + g_debug ("GsmXSMPClient: RegisterClient not handled!"); + g_free (id); + free (previous_id); + g_assert_not_reached (); + return FALSE; + } + + if (IS_STRING_EMPTY (id)) { + g_debug ("GsmXSMPClient: rejected: invalid previous_id"); + free (previous_id); + return FALSE; + } + + g_object_set (client, "startup-id", id, NULL); + + set_description (client); + + g_debug ("GsmXSMPClient: Sending RegisterClientReply to '%s'", client->priv->description); + + SmsRegisterClientReply (conn, id); + + if (IS_STRING_EMPTY (previous_id)) { + /* Send the initial SaveYourself. */ + g_debug ("GsmXSMPClient: Sending initial SaveYourself"); + SmsSaveYourself (conn, SmSaveLocal, False, SmInteractStyleNone, False); + client->priv->current_save_yourself = SmSaveLocal; + } + + gsm_client_set_status (GSM_CLIENT (client), GSM_CLIENT_REGISTERED); + + g_free (id); + free (previous_id); + + return TRUE; +} + + +static void +save_yourself_request_callback (SmsConn conn, + SmPointer manager_data, + int save_type, + Bool shutdown, + int interact_style, + Bool fast, + Bool global) +{ + GsmXSMPClient *client = manager_data; + + g_debug ("GsmXSMPClient: Client '%s' received SaveYourselfRequest(%s, %s, %s, %s, %s)", + client->priv->description, + save_type == SmSaveLocal ? "SmSaveLocal" : + save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth", + shutdown ? "Shutdown" : "!Shutdown", + interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : + interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" : + "SmInteractStyleNone", fast ? "Fast" : "!Fast", + global ? "Global" : "!Global"); + + /* Examining the g_debug above, you can see that there are a total + * of 72 different combinations of options that this could have been + * called with. However, most of them are stupid. + * + * If @shutdown and @global are both TRUE, that means the caller is + * requesting that a logout message be sent to all clients, so we do + * that. We use @fast to decide whether or not to show a + * confirmation dialog. (This isn't really what @fast is for, but + * the old mate-session and ksmserver both interpret it that way, + * so we do too.) We ignore @save_type because we pick the correct + * save_type ourselves later based on user prefs, dialog choices, + * etc, and we ignore @interact_style, because clients have not used + * it correctly consistently enough to make it worth honoring. + * + * If @shutdown is TRUE and @global is FALSE, the caller is + * confused, so we ignore the request. + * + * If @shutdown is FALSE and @save_type is SmSaveGlobal or + * SmSaveBoth, then the client wants us to ask some or all open + * applications to save open files to disk, but NOT quit. This is + * silly and so we ignore the request. + * + * If @shutdown is FALSE and @save_type is SmSaveLocal, then the + * client wants us to ask some or all open applications to update + * their current saved state, but not log out. At the moment, the + * code only supports this for the !global case (ie, a client + * requesting that it be allowed to update *its own* saved state, + * but not having everyone else update their saved state). + */ + + if (shutdown && global) { + g_debug ("GsmXSMPClient: initiating shutdown"); + g_signal_emit (client, signals[LOGOUT_REQUEST], 0, !fast); + } else if (!shutdown && !global) { + g_debug ("GsmXSMPClient: initiating checkpoint"); + do_save_yourself (client, SmSaveLocal, TRUE); + } else { + g_debug ("GsmXSMPClient: ignoring"); + } +} + +static void +save_yourself_phase2_request_callback (SmsConn conn, + SmPointer manager_data) +{ + GsmXSMPClient *client = manager_data; + + g_debug ("GsmXSMPClient: Client '%s' received SaveYourselfPhase2Request", + client->priv->description); + + client->priv->current_save_yourself = -1; + + /* this is a valid response to SaveYourself and therefore + may be a response to a QES or ES */ + gsm_client_end_session_response (GSM_CLIENT (client), + TRUE, TRUE, FALSE, + NULL); +} + +static void +interact_request_callback (SmsConn conn, + SmPointer manager_data, + int dialog_type) +{ + GsmXSMPClient *client = manager_data; +#if 0 + gboolean res; + GError *error; +#endif + + g_debug ("GsmXSMPClient: Client '%s' received InteractRequest(%s)", + client->priv->description, + dialog_type == SmDialogNormal ? "Dialog" : "Errors"); + + gsm_client_end_session_response (GSM_CLIENT (client), + FALSE, FALSE, FALSE, + _("This program is blocking logout.")); + +#if 0 + /* Can't just call back with Interact because session client + grabs the keyboard! So, we try to get it to release + grabs by telling it we've cancelled the shutdown. + This grabbing is clearly bullshit and is not supported by + the client spec or protocol spec. + */ + res = xsmp_cancel_end_session (GSM_CLIENT (client), &error); + if (! res) { + g_warning ("Unable to cancel end session: %s", error->message); + g_error_free (error); + } +#endif + xsmp_interact (GSM_CLIENT (client)); +} + +static void +interact_done_callback (SmsConn conn, + SmPointer manager_data, + Bool cancel_shutdown) +{ + GsmXSMPClient *client = manager_data; + + g_debug ("GsmXSMPClient: Client '%s' received InteractDone(cancel_shutdown = %s)", + client->priv->description, + cancel_shutdown ? "True" : "False"); + + gsm_client_end_session_response (GSM_CLIENT (client), + TRUE, FALSE, cancel_shutdown, + NULL); +} + +static void +save_yourself_done_callback (SmsConn conn, + SmPointer manager_data, + Bool success) +{ + GsmXSMPClient *client = manager_data; + + g_debug ("GsmXSMPClient: Client '%s' received SaveYourselfDone(success = %s)", + client->priv->description, + success ? "True" : "False"); + + if (client->priv->current_save_yourself != -1) { + SmsSaveComplete (client->priv->conn); + client->priv->current_save_yourself = -1; + } + + /* If success is false then the application couldn't save data. Nothing + * the session manager can do about, though. FIXME: we could display a + * dialog about this, I guess. */ + gsm_client_end_session_response (GSM_CLIENT (client), + TRUE, FALSE, FALSE, + NULL); + + if (client->priv->next_save_yourself) { + int save_type = client->priv->next_save_yourself; + gboolean allow_interact = client->priv->next_save_yourself_allow_interact; + + client->priv->next_save_yourself = -1; + client->priv->next_save_yourself_allow_interact = -1; + do_save_yourself (client, save_type, allow_interact); + } +} + +static void +close_connection_callback (SmsConn conn, + SmPointer manager_data, + int count, + char **reason_msgs) +{ + GsmXSMPClient *client = manager_data; + int i; + + g_debug ("GsmXSMPClient: Client '%s' received CloseConnection", client->priv->description); + for (i = 0; i < count; i++) { + g_debug ("GsmXSMPClient: close reason: '%s'", reason_msgs[i]); + } + SmFreeReasons (count, reason_msgs); + + gsm_client_set_status (GSM_CLIENT (client), GSM_CLIENT_FINISHED); + gsm_client_disconnected (GSM_CLIENT (client)); +} + +void +gsm_xsmp_client_connect (GsmXSMPClient *client, + SmsConn conn, + unsigned long *mask_ret, + SmsCallbacks *callbacks_ret) +{ + client->priv->conn = conn; + + g_debug ("GsmXSMPClient: Initializing client %s", client->priv->description); + + *mask_ret = 0; + + *mask_ret |= SmsRegisterClientProcMask; + callbacks_ret->register_client.callback = register_client_callback; + callbacks_ret->register_client.manager_data = client; + + *mask_ret |= SmsInteractRequestProcMask; + callbacks_ret->interact_request.callback = interact_request_callback; + callbacks_ret->interact_request.manager_data = client; + + *mask_ret |= SmsInteractDoneProcMask; + callbacks_ret->interact_done.callback = interact_done_callback; + callbacks_ret->interact_done.manager_data = client; + + *mask_ret |= SmsSaveYourselfRequestProcMask; + callbacks_ret->save_yourself_request.callback = save_yourself_request_callback; + callbacks_ret->save_yourself_request.manager_data = client; + + *mask_ret |= SmsSaveYourselfP2RequestProcMask; + callbacks_ret->save_yourself_phase2_request.callback = save_yourself_phase2_request_callback; + callbacks_ret->save_yourself_phase2_request.manager_data = client; + + *mask_ret |= SmsSaveYourselfDoneProcMask; + callbacks_ret->save_yourself_done.callback = save_yourself_done_callback; + callbacks_ret->save_yourself_done.manager_data = client; + + *mask_ret |= SmsCloseConnectionProcMask; + callbacks_ret->close_connection.callback = close_connection_callback; + callbacks_ret->close_connection.manager_data = client; + + *mask_ret |= SmsSetPropertiesProcMask; + callbacks_ret->set_properties.callback = set_properties_callback; + callbacks_ret->set_properties.manager_data = client; + + *mask_ret |= SmsDeletePropertiesProcMask; + callbacks_ret->delete_properties.callback = delete_properties_callback; + callbacks_ret->delete_properties.manager_data = client; + + *mask_ret |= SmsGetPropertiesProcMask; + callbacks_ret->get_properties.callback = get_properties_callback; + callbacks_ret->get_properties.manager_data = client; +} + +void +gsm_xsmp_client_save_state (GsmXSMPClient *client) +{ + g_return_if_fail (GSM_IS_XSMP_CLIENT (client)); +} diff --git a/mate-session/gsm-xsmp-client.h b/mate-session/gsm-xsmp-client.h new file mode 100644 index 0000000..20ef3df --- /dev/null +++ b/mate-session/gsm-xsmp-client.h @@ -0,0 +1,93 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 __GSM_XSMP_CLIENT_H__ +#define __GSM_XSMP_CLIENT_H__ + +#include "gsm-client.h" + +#include <X11/SM/SMlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_XSMP_CLIENT (gsm_xsmp_client_get_type ()) +#define GSM_XSMP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClient)) +#define GSM_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass)) +#define GSM_IS_XSMP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_XSMP_CLIENT)) +#define GSM_IS_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_XSMP_CLIENT)) +#define GSM_XSMP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass)) + +typedef struct _GsmXSMPClient GsmXSMPClient; +typedef struct _GsmXSMPClientClass GsmXSMPClientClass; + +typedef struct GsmXSMPClientPrivate GsmXSMPClientPrivate; + +struct _GsmXSMPClient +{ + GsmClient parent; + GsmXSMPClientPrivate *priv; +}; + +struct _GsmXSMPClientClass +{ + GsmClientClass parent_class; + + /* signals */ + gboolean (*register_request) (GsmXSMPClient *client, + char **client_id); + gboolean (*logout_request) (GsmXSMPClient *client, + gboolean prompt); + + + void (*saved_state) (GsmXSMPClient *client); + + void (*request_phase2) (GsmXSMPClient *client); + + void (*request_interaction) (GsmXSMPClient *client); + void (*interaction_done) (GsmXSMPClient *client, + gboolean cancel_shutdown); + + void (*save_yourself_done) (GsmXSMPClient *client); + +}; + +GType gsm_xsmp_client_get_type (void) G_GNUC_CONST; + +GsmClient *gsm_xsmp_client_new (IceConn ice_conn); + +void gsm_xsmp_client_connect (GsmXSMPClient *client, + SmsConn conn, + unsigned long *mask_ret, + SmsCallbacks *callbacks_ret); + +void gsm_xsmp_client_save_state (GsmXSMPClient *client); +void gsm_xsmp_client_save_yourself (GsmXSMPClient *client, + gboolean save_state); +void gsm_xsmp_client_save_yourself_phase2 (GsmXSMPClient *client); +void gsm_xsmp_client_interact (GsmXSMPClient *client); +void gsm_xsmp_client_shutdown_cancelled (GsmXSMPClient *client); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_XSMP_CLIENT_H__ */ diff --git a/mate-session/gsm-xsmp-server.c b/mate-session/gsm-xsmp-server.c new file mode 100644 index 0000000..1f0e045 --- /dev/null +++ b/mate-session/gsm-xsmp-server.c @@ -0,0 +1,732 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2008 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#include <X11/ICE/ICElib.h> +#include <X11/ICE/ICEutil.h> +#include <X11/ICE/ICEconn.h> +#include <X11/SM/SMlib.h> + +#ifdef HAVE_X11_XTRANS_XTRANS_H +/* Get the proto for _IceTransNoListen */ +#define ICE_t +#define TRANS_SERVER +#include <X11/Xtrans/Xtrans.h> +#undef ICE_t +#undef TRANS_SERVER +#endif /* HAVE_X11_XTRANS_XTRANS_H */ + +#include "gsm-xsmp-server.h" +#include "gsm-xsmp-client.h" +#include "gsm-util.h" + +/* ICEauthority stuff */ +/* Various magic numbers stolen from iceauth.c */ +#define GSM_ICE_AUTH_RETRIES 10 +#define GSM_ICE_AUTH_INTERVAL 2 /* 2 seconds */ +#define GSM_ICE_AUTH_LOCK_TIMEOUT 600 /* 10 minutes */ + +#define GSM_ICE_MAGIC_COOKIE_AUTH_NAME "MIT-MAGIC-COOKIE-1" +#define GSM_ICE_MAGIC_COOKIE_LEN 16 + +#define GSM_XSMP_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_XSMP_SERVER, GsmXsmpServerPrivate)) + +struct GsmXsmpServerPrivate +{ + GsmStore *client_store; + + IceListenObj *xsmp_sockets; + int num_xsmp_sockets; + int num_local_xsmp_sockets; + +}; + +enum { + PROP_0, + PROP_CLIENT_STORE +}; + +static void gsm_xsmp_server_class_init (GsmXsmpServerClass *klass); +static void gsm_xsmp_server_init (GsmXsmpServer *xsmp_server); +static void gsm_xsmp_server_finalize (GObject *object); + +static gpointer xsmp_server_object = NULL; + +G_DEFINE_TYPE (GsmXsmpServer, gsm_xsmp_server, G_TYPE_OBJECT) + +typedef struct { + GsmXsmpServer *server; + IceListenObj listener; +} GsmIceConnectionData; + +typedef struct { + guint watch_id; + guint protocol_timeout; +} GsmIceConnectionWatch; + +static void +disconnect_ice_connection (IceConn ice_conn) +{ + IceSetShutdownNegotiation (ice_conn, FALSE); + IceCloseConnection (ice_conn); +} + +static void +free_ice_connection_watch (GsmIceConnectionWatch *data) +{ + if (data->watch_id) { + g_source_remove (data->watch_id); + data->watch_id = 0; + } + + if (data->protocol_timeout) { + g_source_remove (data->protocol_timeout); + data->protocol_timeout = 0; + } + + g_free (data); +} + +static gboolean +ice_protocol_timeout (IceConn ice_conn) +{ + GsmIceConnectionWatch *data; + + g_debug ("GsmXsmpServer: ice_protocol_timeout for IceConn %p with status %d", + ice_conn, IceConnectionStatus (ice_conn)); + + data = ice_conn->context; + + free_ice_connection_watch (data); + disconnect_ice_connection (ice_conn); + + return FALSE; +} + +static gboolean +auth_iochannel_watch (GIOChannel *source, + GIOCondition condition, + IceConn ice_conn) +{ + + GsmIceConnectionWatch *data; + gboolean keep_going; + + data = ice_conn->context; + + switch (IceProcessMessages (ice_conn, NULL, NULL)) { + case IceProcessMessagesSuccess: + keep_going = TRUE; + break; + case IceProcessMessagesIOError: + g_debug ("GsmXsmpServer: IceProcessMessages returned IceProcessMessagesIOError"); + free_ice_connection_watch (data); + disconnect_ice_connection (ice_conn); + keep_going = FALSE; + break; + case IceProcessMessagesConnectionClosed: + g_debug ("GsmXsmpServer: IceProcessMessages returned IceProcessMessagesConnectionClosed"); + free_ice_connection_watch (data); + keep_going = FALSE; + break; + default: + g_assert_not_reached (); + } + + return keep_going; +} + +/* IceAcceptConnection returns a new ICE connection that is in a "pending" state, + * this is because authentification may be necessary. + * So we've to authenticate it, before accept_xsmp_connection() is called. + * Then each GsmXSMPClient will have its own IceConn watcher + */ +static void +auth_ice_connection (IceConn ice_conn) +{ + GIOChannel *channel; + GsmIceConnectionWatch *data; + int fd; + + g_debug ("GsmXsmpServer: auth_ice_connection()"); + + fd = IceConnectionNumber (ice_conn); + fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC); + channel = g_io_channel_unix_new (fd); + + data = g_new0 (GsmIceConnectionWatch, 1); + ice_conn->context = data; + + data->protocol_timeout = g_timeout_add_seconds (5, + (GSourceFunc)ice_protocol_timeout, + ice_conn); + data->watch_id = g_io_add_watch (channel, + G_IO_IN | G_IO_ERR, + (GIOFunc)auth_iochannel_watch, + ice_conn); + g_io_channel_unref (channel); +} + +/* This is called (by glib via xsmp->ice_connection_watch) when a + * connection is first received on the ICE listening socket. + */ +static gboolean +accept_ice_connection (GIOChannel *source, + GIOCondition condition, + GsmIceConnectionData *data) +{ + IceConn ice_conn; + IceAcceptStatus status; + + g_debug ("GsmXsmpServer: accept_ice_connection()"); + + ice_conn = IceAcceptConnection (data->listener, &status); + if (status != IceAcceptSuccess) { + g_debug ("GsmXsmpServer: IceAcceptConnection returned %d", status); + return TRUE; + } + + auth_ice_connection (ice_conn); + + return TRUE; +} + +void +gsm_xsmp_server_start (GsmXsmpServer *server) +{ + GIOChannel *channel; + int i; + + for (i = 0; i < server->priv->num_local_xsmp_sockets; i++) { + GsmIceConnectionData *data; + + data = g_new0 (GsmIceConnectionData, 1); + data->server = server; + data->listener = server->priv->xsmp_sockets[i]; + + channel = g_io_channel_unix_new (IceGetListenConnectionNumber (server->priv->xsmp_sockets[i])); + g_io_add_watch_full (channel, + G_PRIORITY_DEFAULT, + G_IO_IN | G_IO_HUP | G_IO_ERR, + (GIOFunc)accept_ice_connection, + data, + (GDestroyNotify)g_free); + g_io_channel_unref (channel); + } +} + +static void +gsm_xsmp_server_set_client_store (GsmXsmpServer *xsmp_server, + GsmStore *store) +{ + g_return_if_fail (GSM_IS_XSMP_SERVER (xsmp_server)); + + if (store != NULL) { + g_object_ref (store); + } + + if (xsmp_server->priv->client_store != NULL) { + g_object_unref (xsmp_server->priv->client_store); + } + + xsmp_server->priv->client_store = store; +} + +static void +gsm_xsmp_server_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmXsmpServer *self; + + self = GSM_XSMP_SERVER (object); + + switch (prop_id) { + case PROP_CLIENT_STORE: + gsm_xsmp_server_set_client_store (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_xsmp_server_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmXsmpServer *self; + + self = GSM_XSMP_SERVER (object); + + switch (prop_id) { + case PROP_CLIENT_STORE: + g_value_set_object (value, self->priv->client_store); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* This is called (by libSM) when XSMP is initiated on an ICE + * connection that was already accepted by accept_ice_connection. + */ +static Status +accept_xsmp_connection (SmsConn sms_conn, + GsmXsmpServer *server, + unsigned long *mask_ret, + SmsCallbacks *callbacks_ret, + char **failure_reason_ret) +{ + IceConn ice_conn; + GsmClient *client; + GsmIceConnectionWatch *data; + + /* FIXME: what about during shutdown but before gsm_xsmp_shutdown? */ + if (server->priv->xsmp_sockets == NULL) { + g_debug ("GsmXsmpServer: In shutdown, rejecting new client"); + + *failure_reason_ret = strdup (_("Refusing new client connection because the session is currently being shut down\n")); + return FALSE; + } + + ice_conn = SmsGetIceConnection (sms_conn); + data = ice_conn->context; + + /* Each GsmXSMPClient has its own IceConn watcher */ + free_ice_connection_watch (data); + + client = gsm_xsmp_client_new (ice_conn); + + gsm_store_add (server->priv->client_store, gsm_client_peek_id (client), G_OBJECT (client)); + /* the store will own the ref */ + g_object_unref (client); + + gsm_xsmp_client_connect (GSM_XSMP_CLIENT (client), sms_conn, mask_ret, callbacks_ret); + + return TRUE; +} + +static void +ice_error_handler (IceConn conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + IcePointer values) +{ + g_debug ("GsmXsmpServer: ice_error_handler (%p, %s, %d, %lx, %d, %d)", + conn, swap ? "TRUE" : "FALSE", offending_minor_opcode, + offending_sequence, error_class, severity); + + if (severity == IceCanContinue) { + return; + } + + /* FIXME: the ICElib docs are completely vague about what we're + * supposed to do in this case. Need to verify that calling + * IceCloseConnection() here is guaranteed to cause neither + * free-memory-reads nor leaks. + */ + IceCloseConnection (conn); +} + +static void +ice_io_error_handler (IceConn conn) +{ + g_debug ("GsmXsmpServer: ice_io_error_handler (%p)", conn); + + /* We don't need to do anything here; the next call to + * IceProcessMessages() for this connection will receive + * IceProcessMessagesIOError and we can handle the error there. + */ +} + +static void +sms_error_handler (SmsConn conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence_num, + int error_class, + int severity, + IcePointer values) +{ + g_debug ("GsmXsmpServer: sms_error_handler (%p, %s, %d, %lx, %d, %d)", + conn, swap ? "TRUE" : "FALSE", offending_minor_opcode, + offending_sequence_num, error_class, severity); + + /* We don't need to do anything here; if the connection needs to be + * closed, libSM will do that itself. + */ +} + +static IceAuthFileEntry * +auth_entry_new (const char *protocol, + const char *network_id) +{ + IceAuthFileEntry *file_entry; + IceAuthDataEntry data_entry; + + file_entry = malloc (sizeof (IceAuthFileEntry)); + + file_entry->protocol_name = strdup (protocol); + file_entry->protocol_data = NULL; + file_entry->protocol_data_length = 0; + file_entry->network_id = strdup (network_id); + file_entry->auth_name = strdup (GSM_ICE_MAGIC_COOKIE_AUTH_NAME); + file_entry->auth_data = IceGenerateMagicCookie (GSM_ICE_MAGIC_COOKIE_LEN); + file_entry->auth_data_length = GSM_ICE_MAGIC_COOKIE_LEN; + + /* Also create an in-memory copy, which is what the server will + * actually use for checking client auth. + */ + data_entry.protocol_name = file_entry->protocol_name; + data_entry.network_id = file_entry->network_id; + data_entry.auth_name = file_entry->auth_name; + data_entry.auth_data = file_entry->auth_data; + data_entry.auth_data_length = file_entry->auth_data_length; + IceSetPaAuthData (1, &data_entry); + + return file_entry; +} + +static gboolean +update_iceauthority (GsmXsmpServer *server, + gboolean adding) +{ + char *filename; + char **our_network_ids; + FILE *fp; + IceAuthFileEntry *auth_entry; + GSList *entries; + GSList *e; + int i; + gboolean ok = FALSE; + + filename = IceAuthFileName (); + if (IceLockAuthFile (filename, + GSM_ICE_AUTH_RETRIES, + GSM_ICE_AUTH_INTERVAL, + GSM_ICE_AUTH_LOCK_TIMEOUT) != IceAuthLockSuccess) { + return FALSE; + } + + our_network_ids = g_malloc (server->priv->num_local_xsmp_sockets * sizeof (char *)); + for (i = 0; i < server->priv->num_local_xsmp_sockets; i++) { + our_network_ids[i] = IceGetListenConnectionString (server->priv->xsmp_sockets[i]); + } + + entries = NULL; + + fp = fopen (filename, "r+"); + if (fp != NULL) { + while ((auth_entry = IceReadAuthFileEntry (fp)) != NULL) { + /* Skip/delete entries with no network ID (invalid), or with + * our network ID; if we're starting up, an entry with our + * ID must be a stale entry left behind by an old process, + * and if we're shutting down, it won't be valid in the + * future, so either way we want to remove it from the list. + */ + if (!auth_entry->network_id) { + IceFreeAuthFileEntry (auth_entry); + continue; + } + + for (i = 0; i < server->priv->num_local_xsmp_sockets; i++) { + if (!strcmp (auth_entry->network_id, our_network_ids[i])) { + IceFreeAuthFileEntry (auth_entry); + break; + } + } + if (i != server->priv->num_local_xsmp_sockets) { + continue; + } + + entries = g_slist_prepend (entries, auth_entry); + } + + rewind (fp); + } else { + int fd; + + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { + g_warning ("Unable to read ICE authority file: %s", filename); + goto cleanup; + } + + fd = open (filename, O_CREAT | O_WRONLY, 0600); + fp = fdopen (fd, "w"); + if (!fp) { + g_warning ("Unable to write to ICE authority file: %s", filename); + if (fd != -1) { + close (fd); + } + goto cleanup; + } + } + + if (adding) { + for (i = 0; i < server->priv->num_local_xsmp_sockets; i++) { + entries = g_slist_append (entries, + auth_entry_new ("ICE", our_network_ids[i])); + entries = g_slist_prepend (entries, + auth_entry_new ("XSMP", our_network_ids[i])); + } + } + + for (e = entries; e; e = e->next) { + IceAuthFileEntry *auth_entry = e->data; + IceWriteAuthFileEntry (fp, auth_entry); + IceFreeAuthFileEntry (auth_entry); + } + g_slist_free (entries); + + fclose (fp); + ok = TRUE; + + cleanup: + IceUnlockAuthFile (filename); + for (i = 0; i < server->priv->num_local_xsmp_sockets; i++) { + free (our_network_ids[i]); + } + g_free (our_network_ids); + + return ok; +} + + +static void +setup_listener (GsmXsmpServer *server) +{ + char error[256]; + mode_t saved_umask; + char *network_id_list; + int i; + int res; + + /* Set up sane error handlers */ + IceSetErrorHandler (ice_error_handler); + IceSetIOErrorHandler (ice_io_error_handler); + SmsSetErrorHandler (sms_error_handler); + + /* Initialize libSM; we pass NULL for hostBasedAuthProc to disable + * host-based authentication. + */ + res = SmsInitialize (PACKAGE, + VERSION, + (SmsNewClientProc)accept_xsmp_connection, + server, + NULL, + sizeof (error), + error); + if (! res) { + gsm_util_init_error (TRUE, "Could not initialize libSM: %s", error); + } + +#ifdef HAVE_X11_XTRANS_XTRANS_H + /* By default, IceListenForConnections will open one socket for each + * transport type known to X. We don't want connections from remote + * hosts, so for security reasons it would be best if ICE didn't + * even open any non-local sockets. So we use an internal ICElib + * method to disable them here. Unfortunately, there is no way to + * ask X what transport types it knows about, so we're forced to + * guess. + */ + _IceTransNoListen ("tcp"); +#endif + + /* Create the XSMP socket. Older versions of IceListenForConnections + * have a bug which causes the umask to be set to 0 on certain types + * of failures. Probably not an issue on any modern systems, but + * we'll play it safe. + */ + saved_umask = umask (0); + umask (saved_umask); + res = IceListenForConnections (&server->priv->num_xsmp_sockets, + &server->priv->xsmp_sockets, + sizeof (error), + error); + if (! res) { + gsm_util_init_error (TRUE, _("Could not create ICE listening socket: %s"), error); + } + + umask (saved_umask); + + /* Find the local sockets in the returned socket list and move them + * to the start of the list. + */ + for (i = server->priv->num_local_xsmp_sockets = 0; i < server->priv->num_xsmp_sockets; i++) { + char *id = IceGetListenConnectionString (server->priv->xsmp_sockets[i]); + + if (!strncmp (id, "local/", sizeof ("local/") - 1) || + !strncmp (id, "unix/", sizeof ("unix/") - 1)) { + if (i > server->priv->num_local_xsmp_sockets) { + IceListenObj tmp; + tmp = server->priv->xsmp_sockets[i]; + server->priv->xsmp_sockets[i] = server->priv->xsmp_sockets[server->priv->num_local_xsmp_sockets]; + server->priv->xsmp_sockets[server->priv->num_local_xsmp_sockets] = tmp; + } + server->priv->num_local_xsmp_sockets++; + } + free (id); + } + + if (server->priv->num_local_xsmp_sockets == 0) { + gsm_util_init_error (TRUE, "IceListenForConnections did not return a local listener!"); + } + +#ifdef HAVE_X11_XTRANS_XTRANS_H + if (server->priv->num_local_xsmp_sockets != server->priv->num_xsmp_sockets) { + /* Xtrans was apparently compiled with support for some + * non-local transport besides TCP (which we disabled above); we + * won't create IO watches on those extra sockets, so + * connections to them will never be noticed, but they're still + * there, which is inelegant. + * + * If the g_warning below is triggering for you and you want to + * stop it, the fix is to add additional _IceTransNoListen() + * calls above. + */ + network_id_list = IceComposeNetworkIdList (server->priv->num_xsmp_sockets - server->priv->num_local_xsmp_sockets, + server->priv->xsmp_sockets + server->priv->num_local_xsmp_sockets); + g_warning ("IceListenForConnections returned %d non-local listeners: %s", + server->priv->num_xsmp_sockets - server->priv->num_local_xsmp_sockets, + network_id_list); + free (network_id_list); + } +#endif + + /* Update .ICEauthority with new auth entries for our socket */ + if (!update_iceauthority (server, TRUE)) { + /* FIXME: is this really fatal? Hm... */ + gsm_util_init_error (TRUE, + "Could not update ICEauthority file %s", + IceAuthFileName ()); + } + + network_id_list = IceComposeNetworkIdList (server->priv->num_local_xsmp_sockets, + server->priv->xsmp_sockets); + + gsm_util_setenv ("SESSION_MANAGER", network_id_list); + g_debug ("GsmXsmpServer: SESSION_MANAGER=%s\n", network_id_list); + free (network_id_list); +} + +static GObject * +gsm_xsmp_server_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmXsmpServer *xsmp_server; + + xsmp_server = GSM_XSMP_SERVER (G_OBJECT_CLASS (gsm_xsmp_server_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + setup_listener (xsmp_server); + + return G_OBJECT (xsmp_server); +} + +static void +gsm_xsmp_server_class_init (GsmXsmpServerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gsm_xsmp_server_get_property; + object_class->set_property = gsm_xsmp_server_set_property; + object_class->constructor = gsm_xsmp_server_constructor; + object_class->finalize = gsm_xsmp_server_finalize; + + g_object_class_install_property (object_class, + PROP_CLIENT_STORE, + g_param_spec_object ("client-store", + NULL, + NULL, + GSM_TYPE_STORE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GsmXsmpServerPrivate)); +} + +static void +gsm_xsmp_server_init (GsmXsmpServer *xsmp_server) +{ + xsmp_server->priv = GSM_XSMP_SERVER_GET_PRIVATE (xsmp_server); + +} + +static void +gsm_xsmp_server_finalize (GObject *object) +{ + GsmXsmpServer *xsmp_server; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_XSMP_SERVER (object)); + + xsmp_server = GSM_XSMP_SERVER (object); + + g_return_if_fail (xsmp_server->priv != NULL); + + IceFreeListenObjs (xsmp_server->priv->num_xsmp_sockets, + xsmp_server->priv->xsmp_sockets); + + if (xsmp_server->priv->client_store != NULL) { + g_object_unref (xsmp_server->priv->client_store); + } + + G_OBJECT_CLASS (gsm_xsmp_server_parent_class)->finalize (object); +} + +GsmXsmpServer * +gsm_xsmp_server_new (GsmStore *client_store) +{ + if (xsmp_server_object != NULL) { + g_object_ref (xsmp_server_object); + } else { + xsmp_server_object = g_object_new (GSM_TYPE_XSMP_SERVER, + "client-store", client_store, + NULL); + + g_object_add_weak_pointer (xsmp_server_object, + (gpointer *) &xsmp_server_object); + } + + return GSM_XSMP_SERVER (xsmp_server_object); +} diff --git a/mate-session/gsm-xsmp-server.h b/mate-session/gsm-xsmp-server.h new file mode 100644 index 0000000..afefa4e --- /dev/null +++ b/mate-session/gsm-xsmp-server.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GSM_XSMP_SERVER_H +#define __GSM_XSMP_SERVER_H + +#include <glib-object.h> + +#include "gsm-store.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_XSMP_SERVER (gsm_xsmp_server_get_type ()) +#define GSM_XSMP_SERVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_XSMP_SERVER, GsmXsmpServer)) +#define GSM_XSMP_SERVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_XSMP_SERVER, GsmXsmpServerClass)) +#define GSM_IS_XSMP_SERVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_XSMP_SERVER)) +#define GSM_IS_XSMP_SERVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_XSMP_SERVER)) +#define GSM_XSMP_SERVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_XSMP_SERVER, GsmXsmpServerClass)) + +typedef struct GsmXsmpServerPrivate GsmXsmpServerPrivate; + +typedef struct +{ + GObject parent; + GsmXsmpServerPrivate *priv; +} GsmXsmpServer; + +typedef struct +{ + GObjectClass parent_class; +} GsmXsmpServerClass; + +GType gsm_xsmp_server_get_type (void); + +GsmXsmpServer * gsm_xsmp_server_new (GsmStore *client_store); +void gsm_xsmp_server_start (GsmXsmpServer *server); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_XSMP_SERVER_H */ diff --git a/mate-session/main.c b/mate-session/main.c new file mode 100644 index 0000000..2d7dd64 --- /dev/null +++ b/mate-session/main.c @@ -0,0 +1,543 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2006 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 <libintl.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include <glib/gi18n.h> +#include <glib.h> +#include <gtk/gtk.h> + +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-bindings.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include "mdm-signal-handler.h" +#include "mdm-log.h" + +#include "gsm-consolekit.h" +#include "gsm-mateconf.h" +#include "gsm-util.h" +#include "gsm-manager.h" +#include "gsm-xsmp-server.h" +#include "gsm-store.h" + +#define GSM_MATECONF_DEFAULT_SESSION_KEY "/desktop/mate/session/default_session" +#define GSM_MATECONF_REQUIRED_COMPONENTS_DIRECTORY "/desktop/mate/session/required_components" +#define GSM_MATECONF_REQUIRED_COMPONENTS_LIST_KEY "/desktop/mate/session/required_components_list" + +#define GSM_DBUS_NAME "org.mate.SessionManager" + +#define IS_STRING_EMPTY(x) \ + ((x) == NULL || (x)[0] == '\0') + +static gboolean failsafe = FALSE; +static gboolean show_version = FALSE; +static gboolean debug = FALSE; + +static void on_bus_name_lost(DBusGProxy* bus_proxy, const char* name, gpointer data) +{ + g_warning("Lost name on bus: %s, exiting", name); + exit(1); +} + +static gboolean acquire_name_on_proxy(DBusGProxy* bus_proxy, const char* name) +{ + GError* error; + guint result; + gboolean res; + gboolean ret; + + ret = FALSE; + + if (bus_proxy == NULL) + { + goto out; + } + + error = NULL; + res = dbus_g_proxy_call(bus_proxy, "RequestName", &error, G_TYPE_STRING, name, G_TYPE_UINT, 0, G_TYPE_INVALID, G_TYPE_UINT, &result, G_TYPE_INVALID); + + if (! res) + { + if (error != NULL) + { + g_warning("Failed to acquire %s: %s", name, error->message); + g_error_free(error); + } + else + { + g_warning ("Failed to acquire %s", name); + } + + goto out; + } + + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + if (error != NULL) + { + g_warning("Failed to acquire %s: %s", name, error->message); + g_error_free(error); + } + else + { + g_warning("Failed to acquire %s", name); + } + + goto out; + } + + /* register for name lost */ + dbus_g_proxy_add_signal(bus_proxy, "NameLost", G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(bus_proxy, "NameLost", G_CALLBACK(on_bus_name_lost), NULL, NULL); + + ret = TRUE; + + out: + + return ret; +} + +static gboolean acquire_name(void) +{ + DBusGProxy* bus_proxy; + GError* error; + DBusGConnection* connection; + + error = NULL; + connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + + if (connection == NULL) + { + gsm_util_init_error(TRUE, "Could not connect to session bus: %s", error->message); + /* not reached */ + } + + bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); + + if (!acquire_name_on_proxy(bus_proxy, GSM_DBUS_NAME)) + { + gsm_util_init_error(TRUE, "%s", "Could not acquire name on session bus"); + /* not reached */ + } + + g_object_unref(bus_proxy); + + return TRUE; +} + +/* This doesn't contain the required components, so we need to always + * call append_required_apps() after a call to append_default_apps(). */ +static void append_default_apps(GsmManager* manager, const char* default_session_key, char** autostart_dirs) +{ + GSList* default_apps; + GSList* a; + MateConfClient* client; + + g_debug("main: *** Adding default apps"); + + g_assert(default_session_key != NULL); + g_assert(autostart_dirs != NULL); + + client = mateconf_client_get_default(); + default_apps = mateconf_client_get_list(client, default_session_key, MATECONF_VALUE_STRING, NULL); + g_object_unref(client); + + for (a = default_apps; a; a = a->next) + { + char* app_path; + + if (IS_STRING_EMPTY((char*) a->data)) + { + continue; + } + + app_path = gsm_util_find_desktop_file_for_app_name(a->data, autostart_dirs); + + if (app_path != NULL) + { + gsm_manager_add_autostart_app(manager, app_path, NULL); + g_free(app_path); + } + } + + g_slist_foreach(default_apps, (GFunc) g_free, NULL); + g_slist_free(default_apps); +} + +static void append_required_apps(GsmManager* manager) +{ + GSList* required_components; + GSList* r; + MateConfClient* client; + + g_debug("main: *** Adding required apps"); + + client = mateconf_client_get_default(); + required_components = mateconf_client_get_list(client, GSM_MATECONF_REQUIRED_COMPONENTS_LIST_KEY, MATECONF_VALUE_STRING, NULL); + + if (required_components == NULL) + { + g_warning("No required applications specified"); + } + + for (r = required_components; r != NULL; r = r->next) + { + char* path; + char* default_provider; + const char* component; + + if (IS_STRING_EMPTY((char*) r->data)) + { + continue; + } + + component = r->data; + + path = g_strdup_printf("%s/%s", GSM_MATECONF_REQUIRED_COMPONENTS_DIRECTORY, component); + + default_provider = mateconf_client_get_string(client, path, NULL); + + g_debug ("main: %s looking for component: '%s'", path, default_provider); + + if (default_provider != NULL) + { + char* app_path; + + app_path = gsm_util_find_desktop_file_for_app_name(default_provider, NULL); + + if (app_path != NULL) + { + gsm_manager_add_autostart_app(manager, app_path, component); + } + else + { + g_warning("Unable to find provider '%s' of required component '%s'", default_provider, component); + } + + g_free(app_path); + } + + g_free(default_provider); + g_free(path); + } + + g_debug("main: *** Done adding required apps"); + + g_slist_foreach(required_components, (GFunc) g_free, NULL); + g_slist_free(required_components); + + g_object_unref(client); +} + +static void maybe_load_saved_session_apps(GsmManager* manager) +{ + GsmConsolekit* consolekit; + char* session_type; + + consolekit = gsm_get_consolekit(); + session_type = gsm_consolekit_get_current_session_type(consolekit); + + if (g_strcmp0 (session_type, GSM_CONSOLEKIT_SESSION_TYPE_LOGIN_WINDOW) != 0) + { + gsm_manager_add_autostart_apps_from_dir(manager, gsm_util_get_saved_session_dir()); + } + + g_object_unref(consolekit); + g_free(session_type); +} + +static void load_standard_apps (GsmManager* manager, const char* default_session_key) +{ + char** autostart_dirs; + int i; + + autostart_dirs = gsm_util_get_autostart_dirs(); + + if (!failsafe) + { + maybe_load_saved_session_apps(manager); + + for (i = 0; autostart_dirs[i]; i++) + { + gsm_manager_add_autostart_apps_from_dir(manager, autostart_dirs[i]); + } + } + + /* We do this at the end in case a saved session contains an + * application that already provides one of the components. */ + append_default_apps(manager, default_session_key, autostart_dirs); + append_required_apps(manager); + + g_strfreev(autostart_dirs); +} + +static void load_override_apps(GsmManager* manager, char** override_autostart_dirs) +{ + int i; + + for (i = 0; override_autostart_dirs[i]; i++) + { + gsm_manager_add_autostart_apps_from_dir(manager, override_autostart_dirs[i]); + } +} + +static gboolean signal_cb(int signo, gpointer data) +{ + int ret; + GsmManager* manager; + + g_debug("Got callback for signal %d", signo); + + ret = TRUE; + + switch (signo) + { + case SIGFPE: + case SIGPIPE: + /* let the fatal signals interrupt us */ + g_debug ("Caught signal %d, shutting down abnormally.", signo); + ret = FALSE; + break; + case SIGINT: + case SIGTERM: + manager = (GsmManager*) data; + gsm_manager_logout(manager, GSM_MANAGER_LOGOUT_MODE_FORCE, NULL); + + /* let the fatal signals interrupt us */ + g_debug("Caught signal %d, shutting down normally.", signo); + ret = TRUE; + break; + case SIGHUP: + g_debug("Got HUP signal"); + ret = TRUE; + break; + case SIGUSR1: + g_debug("Got USR1 signal"); + ret = TRUE; + mdm_log_toggle_debug(); + break; + default: + g_debug("Caught unhandled signal %d", signo); + ret = TRUE; + + break; + } + + return ret; +} + +static void shutdown_cb(gpointer data) +{ + GsmManager* manager = (GsmManager*) data; + g_debug("Calling shutdown callback function"); + + /* + * When the signal handler gets a shutdown signal, it calls + * this function to inform GsmManager to not restart + * applications in the off chance a handler is already queued + * to dispatch following the below call to gtk_main_quit. + */ + gsm_manager_set_phase(manager, GSM_MANAGER_PHASE_EXIT); + + gtk_main_quit(); +} + +static gboolean require_dbus_session(int argc, char** argv, GError** error) +{ + char** new_argv; + int i; + + if (g_getenv("DBUS_SESSION_BUS_ADDRESS")) + { + return TRUE; + } + + /* Just a sanity check to prevent infinite recursion if + * dbus-launch fails to set DBUS_SESSION_BUS_ADDRESS + */ + g_return_val_if_fail(!g_str_has_prefix(argv[0], "dbus-launch"), TRUE); + + /* +2 for our new arguments, +1 for NULL */ + new_argv = g_malloc(argc + 3 * sizeof (*argv)); + + new_argv[0] = "dbus-launch"; + new_argv[1] = "--exit-with-session"; + + for (i = 0; i < argc; i++) + { + new_argv[i + 2] = argv[i]; + } + + new_argv[i + 2] = NULL; + + if (!execvp("dbus-launch", new_argv)) + { + g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "No session bus and could not exec dbus-launch: %s", g_strerror(errno)); + return FALSE; + } + + /* Should not be reached */ + return TRUE; +} + +int main(int argc, char** argv) +{ + struct sigaction sa; + GError* error; + char* display_str; + GsmManager* manager; + GsmStore* client_store; + GsmXsmpServer* xsmp_server; + MdmSignalHandler* signal_handler; + static char** override_autostart_dirs = NULL; + static char* default_session_key = NULL; + + static GOptionEntry entries[] = { + {"autostart", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &override_autostart_dirs, N_("Override standard autostart directories"), NULL}, + {"default-session-key", 0, 0, G_OPTION_ARG_STRING, &default_session_key, N_("MateConf key used to look up default session"), NULL}, + {"debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable debugging code"), NULL}, + {"failsafe", 'f', 0, G_OPTION_ARG_NONE, &failsafe, N_("Do not load user-specified applications"), NULL}, + {"version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL}, + {NULL, 0, 0, 0, NULL, NULL, NULL } + }; + + /* Make sure that we have a session bus */ + if (!require_dbus_session(argc, argv, &error)) + { + gsm_util_init_error(TRUE, "%s", error->message); + } + + bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGPIPE, &sa, 0); + + error = NULL; + gtk_init_with_args(&argc, &argv, (char*) _(" - the MATE session manager"), entries, GETTEXT_PACKAGE, &error); + + if (error != NULL) + { + g_warning("%s", error->message); + exit(1); + } + + if (show_version) + { + g_print("%s %s\n", argv [0], VERSION); + exit(1); + } + + mdm_log_init(); + mdm_log_set_debug(debug); + + /* Set DISPLAY explicitly for all our children, in case --display + * was specified on the command line. + */ + display_str = gdk_get_display(); + gsm_util_setenv("DISPLAY", display_str); + g_free(display_str); + + /* Some third-party programs rely on MATE_DESKTOP_SESSION_ID to + * detect if MATE is running. We keep this for compatibility reasons. + */ + gsm_util_setenv("MATE_DESKTOP_SESSION_ID", "this-is-deprecated"); + + client_store = gsm_store_new(); + + + /* Start up mateconfd if not already running. */ + gsm_mateconf_init(); + + xsmp_server = gsm_xsmp_server_new(client_store); + + /* Now make sure they succeeded. (They'll call + * gsm_util_init_error() if they failed.) + */ + gsm_mateconf_check(); + acquire_name(); + + manager = gsm_manager_new(client_store, failsafe); + + signal_handler = mdm_signal_handler_new(); + mdm_signal_handler_add_fatal(signal_handler); + mdm_signal_handler_add(signal_handler, SIGFPE, signal_cb, NULL); + mdm_signal_handler_add(signal_handler, SIGHUP, signal_cb, NULL); + mdm_signal_handler_add(signal_handler, SIGUSR1, signal_cb, NULL); + mdm_signal_handler_add(signal_handler, SIGTERM, signal_cb, manager); + mdm_signal_handler_add(signal_handler, SIGINT, signal_cb, manager); + mdm_signal_handler_set_fatal_func(signal_handler, shutdown_cb, manager); + + if (override_autostart_dirs != NULL) + { + load_override_apps(manager, override_autostart_dirs); + } + else + { + if (!IS_STRING_EMPTY(default_session_key)) + { + load_standard_apps(manager, default_session_key); + } + else + { + load_standard_apps(manager, GSM_MATECONF_DEFAULT_SESSION_KEY); + } + } + + gsm_xsmp_server_start(xsmp_server); + gsm_manager_start(manager); + + gtk_main(); + + if (xsmp_server != NULL) + { + g_object_unref(xsmp_server); + } + + if (manager != NULL) + { + g_debug("Unreffing manager"); + g_object_unref(manager); + } + + if (client_store != NULL) + { + g_object_unref(client_store); + } + + gsm_mateconf_shutdown(); + + mdm_log_shutdown(); + + return 0; +} diff --git a/mate-session/mdm-log.c b/mate-session/mdm-log.c new file mode 100644 index 0000000..ba384bb --- /dev/null +++ b/mate-session/mdm-log.c @@ -0,0 +1,206 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Authors: William Jon McCann <[email protected]> + * + */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <signal.h> +#include <time.h> +#include <unistd.h> + +#include <syslog.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "mdm-log.h" + +static gboolean initialized = FALSE; +static int syslog_levels = (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); + +static void +log_level_to_priority_and_prefix (GLogLevelFlags log_level, + int *priorityp, + const char **prefixp) +{ + int priority; + const char *prefix; + + /* Process the message prefix and priority */ + switch (log_level & G_LOG_LEVEL_MASK) { + case G_LOG_FLAG_FATAL: + priority = LOG_EMERG; + prefix = "FATAL"; + break; + case G_LOG_LEVEL_ERROR: + priority = LOG_ERR; + prefix = "ERROR"; + break; + case G_LOG_LEVEL_CRITICAL: + priority = LOG_CRIT; + prefix = "CRITICAL"; + break; + case G_LOG_LEVEL_WARNING: + priority = LOG_WARNING; + prefix = "WARNING"; + break; + case G_LOG_LEVEL_MESSAGE: + priority = LOG_NOTICE; + prefix = "MESSAGE"; + break; + case G_LOG_LEVEL_INFO: + priority = LOG_INFO; + prefix = "INFO"; + break; + case G_LOG_LEVEL_DEBUG: + /* if debug was requested then bump this up to ERROR + * to ensure it is seen in a log */ + if (syslog_levels & G_LOG_LEVEL_DEBUG) { + priority = LOG_WARNING; + prefix = "DEBUG(+)"; + } else { + priority = LOG_DEBUG; + prefix = "DEBUG"; + } + break; + default: + priority = LOG_DEBUG; + prefix = "UNKNOWN"; + break; + } + + if (priorityp != NULL) { + *priorityp = priority; + } + if (prefixp != NULL) { + *prefixp = prefix; + } +} + +void +mdm_log_default_handler (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer unused_data) +{ + GString *gstring; + int priority; + const char *level_prefix; + char *string; + gboolean do_log; + gboolean is_fatal; + + is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0; + + do_log = (log_level & syslog_levels); + if (! do_log) { + return; + } + + if (! initialized) { + mdm_log_init (); + } + + log_level_to_priority_and_prefix (log_level, + &priority, + &level_prefix); + + gstring = g_string_new (NULL); + + if (log_domain != NULL) { + g_string_append (gstring, log_domain); + g_string_append_c (gstring, '-'); + } + g_string_append (gstring, level_prefix); + + g_string_append (gstring, ": "); + if (message == NULL) { + g_string_append (gstring, "(NULL) message"); + } else { + g_string_append (gstring, message); + } + if (is_fatal) { + g_string_append (gstring, "\naborting...\n"); + } else { + g_string_append (gstring, "\n"); + } + + string = g_string_free (gstring, FALSE); + + syslog (priority, "%s", string); + + g_free (string); +} + +void +mdm_log_toggle_debug (void) +{ + if (syslog_levels & G_LOG_LEVEL_DEBUG) { + g_debug ("Debugging disabled"); + syslog_levels &= ~G_LOG_LEVEL_DEBUG; + } else { + syslog_levels |= G_LOG_LEVEL_DEBUG; + g_debug ("Debugging enabled"); + } +} + +void +mdm_log_set_debug (gboolean debug) +{ + if (debug) { + syslog_levels |= G_LOG_LEVEL_DEBUG; + g_debug ("Enabling debugging"); + } else { + g_debug ("Disabling debugging"); + syslog_levels &= ~G_LOG_LEVEL_DEBUG; + } +} + +void +mdm_log_init (void) +{ + const char *prg_name; + int options; + + g_log_set_default_handler (mdm_log_default_handler, NULL); + + prg_name = g_get_prgname (); + + options = LOG_PID; +#ifdef LOG_PERROR + options |= LOG_PERROR; +#endif + + openlog (prg_name, options, LOG_DAEMON); + + initialized = TRUE; +} + +void +mdm_log_shutdown (void) +{ + closelog (); + initialized = FALSE; +} + diff --git a/mate-session/mdm-log.h b/mate-session/mdm-log.h new file mode 100644 index 0000000..589f058 --- /dev/null +++ b/mate-session/mdm-log.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Authors: William Jon McCann <[email protected]> + * + */ + +#ifndef __MDM_LOG_H +#define __MDM_LOG_H + +#include <stdarg.h> +#include <glib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void mdm_log_default_handler (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer unused_data); +void mdm_log_set_debug (gboolean debug); +void mdm_log_toggle_debug (void); +void mdm_log_init (void); +void mdm_log_shutdown (void); + +/* compatibility */ +#define mdm_fail g_critical +#define mdm_error g_warning +#define mdm_info g_message +#define mdm_debug g_debug + +#define mdm_assert g_assert +#define mdm_assert_not_reached g_assert_not_reached + +#ifdef __cplusplus +} +#endif + +#endif /* __MDM_LOG_H */ diff --git a/mate-session/mdm-signal-handler.c b/mate-session/mdm-signal-handler.c new file mode 100644 index 0000000..46a835b --- /dev/null +++ b/mate-session/mdm-signal-handler.c @@ -0,0 +1,553 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2006 Red Hat, Inc. + * Copyright (C) 2007 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#if HAVE_EXECINFO_H + #include <execinfo.h> +#endif +#include <syslog.h> +#include <sys/wait.h> +#include <sys/stat.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> + +#include "mdm-signal-handler.h" + +#define MDM_SIGNAL_HANDLER_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), MDM_TYPE_SIGNAL_HANDLER, MdmSignalHandlerPrivate)) + +typedef struct { + int signal_number; + MdmSignalHandlerFunc func; + gpointer data; + guint id; +} CallbackData; + +struct MdmSignalHandlerPrivate { + GHashTable* lookup; + GHashTable* id_lookup; + GHashTable* action_lookup; + guint next_id; + GDestroyNotify fatal_func; + gpointer fatal_data; +}; + +static void mdm_signal_handler_class_init(MdmSignalHandlerClass* klass); +static void mdm_signal_handler_init(MdmSignalHandler* signal_handler); +static void mdm_signal_handler_finalize(GObject* object); + +static gpointer signal_handler_object = NULL; +static int signal_pipes[2]; +static int signals_blocked = 0; +static sigset_t signals_block_mask; +static sigset_t signals_oldmask; + +G_DEFINE_TYPE(MdmSignalHandler, mdm_signal_handler, G_TYPE_OBJECT) + +static void block_signals_push(void) +{ + signals_blocked++; + + if (signals_blocked == 1) + { + /* Set signal mask */ + sigemptyset(&signals_block_mask); + sigfillset(&signals_block_mask); + sigprocmask(SIG_BLOCK, &signals_block_mask, &signals_oldmask); + } +} + +static void block_signals_pop(void) +{ + signals_blocked--; + + if (signals_blocked == 0) + { + /* Set signal mask */ + sigprocmask(SIG_SETMASK, &signals_oldmask, NULL); + } +} + +static gboolean signal_io_watch(GIOChannel* ioc, GIOCondition condition, MdmSignalHandler* handler) +{ + char buf[256]; + gboolean is_fatal; + gsize bytes_read; + int i; + + block_signals_push(); + + g_io_channel_read_chars(ioc, buf, sizeof(buf), &bytes_read, NULL); + + is_fatal = FALSE; + + for (i = 0; i < bytes_read; i++) + { + int signum; + GSList* handlers; + GSList* l; + + signum = (gint32) buf[i]; + + g_debug("MdmSignalHandler: handling signal %d", signum); + handlers = g_hash_table_lookup(handler->priv->lookup, GINT_TO_POINTER(signum)); + + g_debug("MdmSignalHandler: Found %u callbacks", g_slist_length(handlers)); + + for (l = handlers; l != NULL; l = l->next) + { + gboolean res; + CallbackData* data; + + data = g_hash_table_lookup(handler->priv->id_lookup, l->data); + + if (data != NULL) + { + if (data->func != NULL) + { + g_debug("MdmSignalHandler: running %d handler: %p", signum, data->func); + + res = data->func(signum, data->data); + + if (!res) + { + is_fatal = TRUE; + } + } + } + } + } + + block_signals_pop(); + + if (is_fatal) + { + if (handler->priv->fatal_func != NULL) + { + g_debug("MdmSignalHandler: Caught termination signal - calling fatal func"); + handler->priv->fatal_func(handler->priv->fatal_data); + } + else + { + g_debug("MdmSignalHandler: Caught termination signal - exiting"); + exit (1); + } + + return FALSE; + } + + g_debug("MdmSignalHandler: Done handling signals"); + + return TRUE; +} + +static void fallback_get_backtrace(void) +{ + #if HAVE_EXECINFO_H + void* frames[64]; + size_t size; + char** strings; + size_t i; + + size = backtrace(frames, G_N_ELEMENTS(frames)); + + if ((strings = backtrace_symbols(frames, size))) + { + syslog(LOG_CRIT, "******************* START ********************************"); + + for (i = 0; i < size; i++) + { + syslog(LOG_CRIT, "Frame %zd: %s", i, strings[i]); + } + + free(strings); + syslog(LOG_CRIT, "******************* END **********************************"); + } + else + { + #endif + g_warning ("MDM crashed, but symbols couldn't be retrieved."); + #if HAVE_EXECINFO_H + } + #endif +} + +static gboolean crashlogger_get_backtrace(void) +{ + gboolean success = FALSE; + int pid; + + pid = fork(); + + if (pid > 0) + { + /* Wait for the child to finish */ + int estatus; + + if (waitpid(pid, &estatus, 0) != -1) + { + /* Only succeed if the crashlogger succeeded */ + if (WIFEXITED(estatus) && (WEXITSTATUS(estatus) == 0)) + { + success = TRUE; + } + } + } + else if (pid == 0) + { + /* Child process */ + execl(LIBEXECDIR "/mdm-crash-logger", LIBEXECDIR "/mdm-crash-logger", NULL); + } + + return success; +} + + +static void mdm_signal_handler_backtrace(void) +{ + struct stat s; + gboolean fallback = TRUE; + + /* Try to use gdb via mdm-crash-logger if it exists, since + * we get much better information out of it. Otherwise + * fall back to execinfo. + */ + if (g_stat(LIBEXECDIR "/mdm-crash-logger", &s) == 0) + { + fallback = crashlogger_get_backtrace() ? FALSE : TRUE; + } + + if (fallback) + { + fallback_get_backtrace(); + } +} + +static void signal_handler(int signo) +{ + static int in_fatal = 0; + int ignore; + guchar signo_byte = signo; + + /* avoid loops */ + if (in_fatal > 0) + { + return; + } + + ++in_fatal; + + switch (signo) + { + case SIGSEGV: + case SIGBUS: + case SIGILL: + case SIGABRT: + case SIGTRAP: + mdm_signal_handler_backtrace(); + exit(1); + break; + case SIGFPE: + case SIGPIPE: + /* let the fatal signals interrupt us */ + --in_fatal; + mdm_signal_handler_backtrace(); + ignore = write(signal_pipes [1], &signo_byte, 1); + break; + default: + --in_fatal; + ignore = write(signal_pipes [1], &signo_byte, 1); + break; + } +} + +static void catch_signal(MdmSignalHandler *handler, int signal_number) +{ + struct sigaction action; + struct sigaction* old_action; + + g_debug("MdmSignalHandler: Registering for %d signals", signal_number); + + action.sa_handler = signal_handler; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + + old_action = g_new0(struct sigaction, 1); + + sigaction(signal_number, &action, old_action); + + g_hash_table_insert(handler->priv->action_lookup, GINT_TO_POINTER(signal_number), old_action); +} + +static void uncatch_signal(MdmSignalHandler* handler, int signal_number) +{ + struct sigaction* old_action; + + g_debug("MdmSignalHandler: Unregistering for %d signals", signal_number); + + old_action = g_hash_table_lookup(handler->priv->action_lookup, GINT_TO_POINTER(signal_number)); + g_hash_table_remove(handler->priv->action_lookup, GINT_TO_POINTER(signal_number)); + + sigaction(signal_number, old_action, NULL); + + g_free(old_action); +} + +guint mdm_signal_handler_add(MdmSignalHandler* handler, int signal_number, MdmSignalHandlerFunc callback, gpointer data) +{ + CallbackData* cdata; + GSList* list; + + g_return_val_if_fail(MDM_IS_SIGNAL_HANDLER(handler), 0); + + cdata = g_new0(CallbackData, 1); + cdata->signal_number = signal_number; + cdata->func = callback; + cdata->data = data; + cdata->id = handler->priv->next_id++; + + g_debug("MdmSignalHandler: Adding handler %u: signum=%d %p", cdata->id, cdata->signal_number, cdata->func); + + if (g_hash_table_lookup(handler->priv->action_lookup, GINT_TO_POINTER(signal_number)) == NULL) + { + catch_signal(handler, signal_number); + } + + /* ID lookup owns the CallbackData */ + g_hash_table_insert(handler->priv->id_lookup, GUINT_TO_POINTER(cdata->id), cdata); + + list = g_hash_table_lookup(handler->priv->lookup, GINT_TO_POINTER(signal_number)); + list = g_slist_prepend(list, GUINT_TO_POINTER (cdata->id)); + + g_hash_table_insert(handler->priv->lookup, GINT_TO_POINTER(signal_number), list); + + return cdata->id; +} + +void mdm_signal_handler_add_fatal(MdmSignalHandler* handler) +{ + g_return_if_fail(MDM_IS_SIGNAL_HANDLER(handler)); + + mdm_signal_handler_add(handler, SIGILL, NULL, NULL); + mdm_signal_handler_add(handler, SIGBUS, NULL, NULL); + mdm_signal_handler_add(handler, SIGSEGV, NULL, NULL); + mdm_signal_handler_add(handler, SIGABRT, NULL, NULL); + mdm_signal_handler_add(handler, SIGTRAP, NULL, NULL); +} + +static void callback_data_free(CallbackData* d) +{ + g_free(d); +} + +static void mdm_signal_handler_remove_and_free_data(MdmSignalHandler* handler, CallbackData* cdata) +{ + GSList* list; + + g_return_if_fail(MDM_IS_SIGNAL_HANDLER(handler)); + + list = g_hash_table_lookup(handler->priv->lookup, GINT_TO_POINTER(cdata->signal_number)); + list = g_slist_remove_all(list, GUINT_TO_POINTER(cdata->id)); + + if (list == NULL) + { + uncatch_signal(handler, cdata->signal_number); + } + + g_debug("MdmSignalHandler: Removing handler %u: signum=%d %p", cdata->signal_number, cdata->id, cdata->func); + /* put changed list back in */ + g_hash_table_insert(handler->priv->lookup, GINT_TO_POINTER(cdata->signal_number), list); + + g_hash_table_remove(handler->priv->id_lookup, GUINT_TO_POINTER(cdata->id)); +} + +void mdm_signal_handler_remove(MdmSignalHandler* handler, guint id) +{ + CallbackData* found; + + g_return_if_fail(MDM_IS_SIGNAL_HANDLER(handler)); + + found = g_hash_table_lookup(handler->priv->id_lookup, GUINT_TO_POINTER(id)); + + if (found != NULL) + { + mdm_signal_handler_remove_and_free_data(handler, found); + found = NULL; + } +} + +static CallbackData* find_callback_data_by_func(MdmSignalHandler* handler, guint signal_number, MdmSignalHandlerFunc callback, gpointer data) +{ + GSList* list; + GSList* l; + CallbackData* found; + + found = NULL; + + list = g_hash_table_lookup(handler->priv->lookup, GINT_TO_POINTER(signal_number)); + + for (l = list; l != NULL; l = l->next) + { + guint id; + CallbackData* d; + + id = GPOINTER_TO_UINT(l->data); + + d = g_hash_table_lookup(handler->priv->id_lookup, GUINT_TO_POINTER (id)); + + if (d != NULL && d->func == callback && d->data == data) + { + found = d; + break; + } + } + + return found; +} + +void mdm_signal_handler_remove_func(MdmSignalHandler* handler, guint signal_number, MdmSignalHandlerFunc callback, gpointer data) +{ + CallbackData* found; + + g_return_if_fail(MDM_IS_SIGNAL_HANDLER(handler)); + + found = find_callback_data_by_func(handler, signal_number, callback, data); + + if (found != NULL) + { + mdm_signal_handler_remove_and_free_data(handler, found); + found = NULL; + } + + /* FIXME: once all handlers are removed deregister signum handler */ +} + +static void mdm_signal_handler_class_init(MdmSignalHandlerClass* klass) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = mdm_signal_handler_finalize; + + g_type_class_add_private(klass, sizeof(MdmSignalHandlerPrivate)); +} + +static void signal_list_free(GSList *list) +{ + g_slist_free(list); +} + +void mdm_signal_handler_set_fatal_func(MdmSignalHandler* handler, MdmShutdownHandlerFunc func, gpointer user_data) +{ + g_return_if_fail(MDM_IS_SIGNAL_HANDLER(handler)); + + handler->priv->fatal_func = func; + handler->priv->fatal_data = user_data; +} + +static void mdm_signal_handler_init(MdmSignalHandler* handler) +{ + GIOChannel* ioc; + + handler->priv = MDM_SIGNAL_HANDLER_GET_PRIVATE(handler); + + handler->priv->next_id = 1; + + handler->priv->lookup = g_hash_table_new(NULL, NULL); + handler->priv->id_lookup = g_hash_table_new(NULL, NULL); + handler->priv->action_lookup = g_hash_table_new(NULL, NULL); + + if (pipe(signal_pipes) == -1) + { + g_error ("Could not create pipe() for signal handling"); + } + + ioc = g_io_channel_unix_new(signal_pipes[0]); + g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL); + g_io_add_watch_full(ioc, G_PRIORITY_HIGH, G_IO_IN, (GIOFunc) signal_io_watch, handler, NULL); + g_io_channel_set_close_on_unref(ioc, TRUE); + g_io_channel_unref(ioc); +} + +static void mdm_signal_handler_finalize(GObject* object) +{ + MdmSignalHandler* handler; + GList* l; + + g_return_if_fail(object != NULL); + g_return_if_fail(MDM_IS_SIGNAL_HANDLER(object)); + + handler = MDM_SIGNAL_HANDLER(object); + + g_debug("MdmSignalHandler: Finalizing signal handler"); + + g_return_if_fail(handler->priv != NULL); + + for (l = g_hash_table_get_values(handler->priv->lookup); l != NULL; l = l->next) + { + signal_list_free((GSList*) l->data); + } + + g_hash_table_destroy(handler->priv->lookup); + + for (l = g_hash_table_get_values(handler->priv->id_lookup); l != NULL; l = l->next) + { + callback_data_free((CallbackData*) l->data); + } + + g_hash_table_destroy(handler->priv->id_lookup); + + for (l = g_hash_table_get_values(handler->priv->action_lookup); l != NULL; l = l->next) + { + g_free(l->data); + } + + g_hash_table_destroy(handler->priv->action_lookup); + + close(signal_pipes[0]); + close(signal_pipes[1]); + + G_OBJECT_CLASS(mdm_signal_handler_parent_class)->finalize(object); +} + +MdmSignalHandler* mdm_signal_handler_new(void) +{ + if (signal_handler_object != NULL) + { + g_object_ref(signal_handler_object); + } + else + { + signal_handler_object = g_object_new(MDM_TYPE_SIGNAL_HANDLER, NULL); + g_object_add_weak_pointer(signal_handler_object, (gpointer*) &signal_handler_object); + } + + return MDM_SIGNAL_HANDLER(signal_handler_object); +} diff --git a/mate-session/mdm-signal-handler.h b/mate-session/mdm-signal-handler.h new file mode 100644 index 0000000..8ca6e74 --- /dev/null +++ b/mate-session/mdm-signal-handler.h @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <[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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef __MDM_SIGNAL_HANDLER_H +#define __MDM_SIGNAL_HANDLER_H + +#include <glib-object.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define MDM_TYPE_SIGNAL_HANDLER \ + (mdm_signal_handler_get_type()) + +#define MDM_SIGNAL_HANDLER(o) \ + (G_TYPE_CHECK_INSTANCE_CAST((o), MDM_TYPE_SIGNAL_HANDLER, MdmSignalHandler)) + +#define MDM_SIGNAL_HANDLER_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST((k), MDM_TYPE_SIGNAL_HANDLER, MdmSignalHandlerClass)) + +#define MDM_IS_SIGNAL_HANDLER(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE((o), MDM_TYPE_SIGNAL_HANDLER)) + +#define MDM_IS_SIGNAL_HANDLER_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE((k), MDM_TYPE_SIGNAL_HANDLER)) + +#define MDM_SIGNAL_HANDLER_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS((o), MDM_TYPE_SIGNAL_HANDLER, MdmSignalHandlerClass)) + + +typedef gboolean (*MdmSignalHandlerFunc)(int signal, gpointer data); + +typedef void (*MdmShutdownHandlerFunc)(gpointer data); + +typedef struct MdmSignalHandlerPrivate MdmSignalHandlerPrivate; + +typedef struct { + GObject parent; + MdmSignalHandlerPrivate* priv; +} MdmSignalHandler; + +typedef struct { + GObjectClass parent_class; +} MdmSignalHandlerClass; + +GType mdm_signal_handler_get_type(void); + +MdmSignalHandler* mdm_signal_handler_new(void); +void mdm_signal_handler_set_fatal_func(MdmSignalHandler* handler, MdmShutdownHandlerFunc func, gpointer user_data); + +void mdm_signal_handler_add_fatal(MdmSignalHandler* handler); +guint mdm_signal_handler_add(MdmSignalHandler* handler, int signal_number, MdmSignalHandlerFunc callback, gpointer data); +void mdm_signal_handler_remove(MdmSignalHandler* handler, guint id); +void mdm_signal_handler_remove_func(MdmSignalHandler* handler, guint signal_number, MdmSignalHandlerFunc callback, gpointer data); + +#ifdef __cplusplus +} +#endif + +#endif /* __MDM_SIGNAL_HANDLER_H */ diff --git a/mate-session/mdm.c b/mate-session/mdm.c new file mode 100644 index 0000000..e6e2eac --- /dev/null +++ b/mate-session/mdm.c @@ -0,0 +1,496 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 Raffaele Sandrini + * Copyright (C) 2005 Red Hat, Inc. + * Copyright (C) 2002, 2003 George Lebl + * Copyright (C) 2001 Queen of England, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: + * Raffaele Sandrini <[email protected]> + * George Lebl <[email protected]> + * Mark McLoughlin <[email protected]> + */ + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <X11/Xauth.h> +#include <gdk/gdk.h> + +#include "mdm.h" + +#define MDM_PROTOCOL_UPDATE_INTERVAL 1 /* seconds */ + +#define MDM_PROTOCOL_SOCKET_PATH "/var/run/mdm_socket" + +#define MDM_PROTOCOL_MSG_CLOSE "CLOSE" +#define MDM_PROTOCOL_MSG_VERSION "VERSION" +#define MDM_PROTOCOL_MSG_AUTHENTICATE "AUTH_LOCAL" +#define MDM_PROTOCOL_MSG_QUERY_ACTION "QUERY_LOGOUT_ACTION" +#define MDM_PROTOCOL_MSG_SET_ACTION "SET_SAFE_LOGOUT_ACTION" +#define MDM_PROTOCOL_MSG_FLEXI_XSERVER "FLEXI_XSERVER" + +#define MDM_ACTION_STR_NONE "NONE" +#define MDM_ACTION_STR_SHUTDOWN "HALT" +#define MDM_ACTION_STR_REBOOT "REBOOT" +#define MDM_ACTION_STR_SUSPEND "SUSPEND" + +typedef struct { + int fd; + char* auth_cookie; + + MdmLogoutAction available_actions; + MdmLogoutAction current_actions; + + time_t last_update; +} MdmProtocolData; + +static MdmProtocolData mdm_protocol_data = { + 0, + NULL, + MDM_LOGOUT_ACTION_NONE, + MDM_LOGOUT_ACTION_NONE, + 0 +}; + +static char* mdm_send_protocol_msg(MdmProtocolData* data, const char* msg) +{ + GString* retval; + char buf[256]; + char* p; + int len; + + p = g_strconcat(msg, "\n", NULL); + + if (write(data->fd, p, strlen(p)) < 0) + { + g_free(p); + + g_warning("Failed to send message to MDM: %s", g_strerror(errno)); + + return NULL; + } + + g_free(p); + + p = NULL; + retval = NULL; + + while ((len = read(data->fd, buf, sizeof(buf) - 1)) > 0) + { + buf[len] = '\0'; + + if (!retval) + { + retval = g_string_new(buf); + } + else + { + retval = g_string_append(retval, buf); + } + + if ((p = strchr(retval->str, '\n'))) + { + break; + } + } + + if (p) + { + *p = '\0'; + } + + return retval ? g_string_free(retval, FALSE) : NULL; +} + +static char* get_display_number(void) +{ + const char* display_name; + char* retval; + char* p; + + display_name = gdk_display_get_name(gdk_display_get_default()); + + p = strchr(display_name, ':'); + + if (!p) + { + return g_strdup("0"); + } + + while (*p == ':') + { + p++; + } + + retval = g_strdup(p); + + p = strchr(retval, '.'); + + if (p != NULL) + { + *p = '\0'; + } + + return retval; +} + +static gboolean mdm_authenticate_connection(MdmProtocolData* data) +{ + #define MDM_MIT_MAGIC_COOKIE_LEN 16 + + const char* xau_path; + FILE* f; + Xauth* xau; + char* display_number; + gboolean retval; + + if (data->auth_cookie) + { + char* msg; + char* response; + + msg = g_strdup_printf(MDM_PROTOCOL_MSG_AUTHENTICATE " %s", data->auth_cookie); + response = mdm_send_protocol_msg(data, msg); + g_free(msg); + + if (response && !strcmp(response, "OK")) + { + g_free(response); + return TRUE; + } + else + { + g_free(response); + g_free(data->auth_cookie); + data->auth_cookie = NULL; + } + } + + if (!(xau_path = XauFileName())) + { + return FALSE; + } + + if (!(f = fopen(xau_path, "r"))) + { + return FALSE; + } + + retval = FALSE; + display_number = get_display_number(); + + while ((xau = XauReadAuth(f))) + { + char buffer[40]; /* 2*16 == 32, so 40 is enough */ + char* msg; + char* response; + int i; + + if (xau->family != FamilyLocal || strncmp(xau->number, display_number, xau->number_length) || strncmp(xau->name, "MIT-MAGIC-COOKIE-1", xau->name_length) || xau->data_length != MDM_MIT_MAGIC_COOKIE_LEN) + { + XauDisposeAuth(xau); + continue; + } + + for (i = 0; i < MDM_MIT_MAGIC_COOKIE_LEN; i++) + { + g_snprintf(buffer + 2 * i, 3, "%02x", (guint)(guchar) xau->data[i]); + } + + XauDisposeAuth(xau); + + msg = g_strdup_printf(MDM_PROTOCOL_MSG_AUTHENTICATE " %s", buffer); + response = mdm_send_protocol_msg(data, msg); + g_free(msg); + + if (response && !strcmp(response, "OK")) + { + data->auth_cookie = g_strdup(buffer); + g_free(response); + retval = TRUE; + break; + } + + g_free(response); + } + + g_free(display_number); + + fclose(f); + + return retval; + + #undef MDM_MIT_MAGIC_COOKIE_LEN +} + +static void mdm_shutdown_protocol_connection(MdmProtocolData *data) +{ + if (data->fd) + { + close(data->fd); + } + + data->fd = 0; +} + +static gboolean mdm_init_protocol_connection(MdmProtocolData* data) +{ + struct sockaddr_un addr; + char* response; + + g_assert(data->fd <= 0); + + if (g_file_test(MDM_PROTOCOL_SOCKET_PATH, G_FILE_TEST_EXISTS)) + { + strcpy(addr.sun_path, MDM_PROTOCOL_SOCKET_PATH); + } + else if (g_file_test("/tmp/.mdm_socket", G_FILE_TEST_EXISTS)) + { + strcpy(addr.sun_path, "/tmp/.mdm_socket"); + } + else + { + return FALSE; + } + + data->fd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (data->fd < 0) + { + g_warning("Failed to create MDM socket: %s", g_strerror(errno)); + + mdm_shutdown_protocol_connection(data); + + return FALSE; + } + + addr.sun_family = AF_UNIX; + + if (connect(data->fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) + { + g_warning("Failed to establish a connection with MDM: %s", g_strerror(errno)); + + mdm_shutdown_protocol_connection(data); + + return FALSE; + } + + response = mdm_send_protocol_msg(data, MDM_PROTOCOL_MSG_VERSION); + + if (!response || strncmp(response, "MDM ", strlen("MDM ") != 0)) + { + g_free(response); + + g_warning("Failed to get protocol version from MDM"); + mdm_shutdown_protocol_connection(data); + + return FALSE; + } + + g_free(response); + + if (!mdm_authenticate_connection(data)) + { + g_warning("Failed to authenticate with MDM"); + mdm_shutdown_protocol_connection(data); + return FALSE; + } + + return TRUE; +} + +static void mdm_parse_query_response(MdmProtocolData* data, const char* response) +{ + char** actions; + int i; + + data->available_actions = MDM_LOGOUT_ACTION_NONE; + data->current_actions = MDM_LOGOUT_ACTION_NONE; + + if (strncmp(response, "OK ", 3) != 0) + { + return; + } + + response += 3; + + actions = g_strsplit(response, ";", -1); + + for (i = 0; actions[i]; i++) + { + MdmLogoutAction action = MDM_LOGOUT_ACTION_NONE; + gboolean selected = FALSE; + char* str = actions [i]; + int len; + + len = strlen(str); + + if (!len) + { + continue; + } + + if (str[len - 1] == '!') + { + selected = TRUE; + str[len - 1] = '\0'; + } + + if (!strcmp(str, MDM_ACTION_STR_SHUTDOWN)) + { + action = MDM_LOGOUT_ACTION_SHUTDOWN; + } + else if (!strcmp(str, MDM_ACTION_STR_REBOOT)) + { + action = MDM_LOGOUT_ACTION_REBOOT; + } + else if (!strcmp(str, MDM_ACTION_STR_SUSPEND)) + { + action = MDM_LOGOUT_ACTION_SUSPEND; + } + + data->available_actions |= action; + + if (selected) + { + data->current_actions |= action; + } + } + + g_strfreev(actions); +} + +static void mdm_update_logout_actions(MdmProtocolData* data) +{ + time_t current_time; + char* response; + + current_time = time(NULL); + + if (current_time <= (data->last_update + MDM_PROTOCOL_UPDATE_INTERVAL)) + { + return; + } + + data->last_update = current_time; + + if (!mdm_init_protocol_connection(data)) + { + return; + } + + if ((response = mdm_send_protocol_msg(data, MDM_PROTOCOL_MSG_QUERY_ACTION))) + { + mdm_parse_query_response(data, response); + g_free(response); + } + + mdm_shutdown_protocol_connection(data); +} + +gboolean mdm_is_available(void) +{ + if (!mdm_init_protocol_connection(&mdm_protocol_data)) + { + return FALSE; + } + + mdm_shutdown_protocol_connection(&mdm_protocol_data); + + return TRUE; +} + +gboolean mdm_supports_logout_action(MdmLogoutAction action) +{ + mdm_update_logout_actions(&mdm_protocol_data); + + return (mdm_protocol_data.available_actions & action) != 0; +} + +MdmLogoutAction mdm_get_logout_action(void) +{ + mdm_update_logout_actions(&mdm_protocol_data); + + return mdm_protocol_data.current_actions; +} + +void mdm_set_logout_action(MdmLogoutAction action) +{ + char* action_str = NULL; + char* msg; + char* response; + + if (!mdm_init_protocol_connection(&mdm_protocol_data)) + { + return; + } + + switch (action) + { + case MDM_LOGOUT_ACTION_NONE: + action_str = MDM_ACTION_STR_NONE; + break; + case MDM_LOGOUT_ACTION_SHUTDOWN: + action_str = MDM_ACTION_STR_SHUTDOWN; + break; + case MDM_LOGOUT_ACTION_REBOOT: + action_str = MDM_ACTION_STR_REBOOT; + break; + case MDM_LOGOUT_ACTION_SUSPEND: + action_str = MDM_ACTION_STR_SUSPEND; + break; + } + + msg = g_strdup_printf(MDM_PROTOCOL_MSG_SET_ACTION " %s", action_str); + + response = mdm_send_protocol_msg(&mdm_protocol_data, msg); + + g_free(msg); + g_free(response); + + mdm_protocol_data.last_update = 0; + + mdm_shutdown_protocol_connection(&mdm_protocol_data); +} + +void mdm_new_login(void) +{ + char* response; + + if (!mdm_init_protocol_connection(&mdm_protocol_data)) + { + return; + } + + response = mdm_send_protocol_msg(&mdm_protocol_data, MDM_PROTOCOL_MSG_FLEXI_XSERVER); + + g_free(response); + + mdm_protocol_data.last_update = 0; + + mdm_shutdown_protocol_connection(&mdm_protocol_data); +} diff --git a/mate-session/mdm.h b/mate-session/mdm.h new file mode 100644 index 0000000..124598b --- /dev/null +++ b/mate-session/mdm.h @@ -0,0 +1,58 @@ +/* mdm.h + * Copyright (C) 2005 Raffaele Sandrini + * Copyright (C) 2005 Red Hat, Inc. + * Copyright (C) 2002, 2003 George Lebl + * Copyright (C) 2001 Queen of England, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: + * Raffaele Sandrini <[email protected]> + * George Lebl <[email protected]> + * Mark McLoughlin <[email protected]> + */ + +#ifndef __MDM_LOGOUT_ACTION_H__ +#define __MDM_LOGOUT_ACTION_H__ + +#include <glib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MDM_LOGOUT_ACTION_NONE = 0, + MDM_LOGOUT_ACTION_SHUTDOWN = 1 << 0, + MDM_LOGOUT_ACTION_REBOOT = 1 << 1, + MDM_LOGOUT_ACTION_SUSPEND = 1 << 2 +} MdmLogoutAction; + +gboolean mdm_is_available(void); + +void mdm_new_login(void); + +void mdm_set_logout_action(MdmLogoutAction action); + +MdmLogoutAction mdm_get_logout_action(void); + +gboolean mdm_supports_logout_action(MdmLogoutAction action); + +#ifdef __cplusplus +} +#endif + +#endif /* __MDM_LOGOUT_ACTION_H__ */ diff --git a/mate-session/org.mate.SessionManager.App.xml b/mate-session/org.mate.SessionManager.App.xml new file mode 100644 index 0000000..971eb7e --- /dev/null +++ b/mate-session/org.mate.SessionManager.App.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.mate.SessionManager.App"> + <method name="GetAppId"> + <arg type="s" name="app_id" direction="out"> + <doc:doc> + <doc:summary>The identifier for the application</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the application ID.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="GetStartupId"> + <arg type="s" name="startup_id" direction="out"> + <doc:doc> + <doc:summary>The startup identifier</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the startup ID associated with this application.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="GetPhase"> + <arg type="u" name="phase" direction="out"> + <doc:doc> + <doc:summary>The application startup phase</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the startup phase of this application.</doc:para> + </doc:description> + </doc:doc> + </method> + + </interface> +</node> diff --git a/mate-session/org.mate.SessionManager.Client.xml b/mate-session/org.mate.SessionManager.Client.xml new file mode 100644 index 0000000..0d51986 --- /dev/null +++ b/mate-session/org.mate.SessionManager.Client.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.mate.SessionManager.Client"> + <method name="GetAppId"> + <arg type="s" name="app_id" direction="out"> + <doc:doc> + <doc:summary>The identifier for the associated application</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the application ID associated with this client.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="GetStartupId"> + <arg type="s" name="startup_id" direction="out"> + <doc:doc> + <doc:summary>The startup identifier</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the startup ID associated with this client.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="GetRestartStyleHint"> + <arg type="u" name="hint" direction="out"> + <doc:doc> + <doc:summary>The restart style hint</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the restart style hint for this client.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="GetUnixProcessId"> + <arg type="u" name="pid" direction="out"> + <doc:doc> + <doc:summary>The Unix process identifier</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the Unix process identifier for this client.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="GetStatus"> + <arg type="u" name="status" direction="out"> + <doc:doc> + <doc:summary>The client status</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the status of this client.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="Stop"> + <doc:doc> + <doc:description> + <doc:para>Inititate a request to terminate this application via XSMP.</doc:para> + </doc:description> + </doc:doc> + </method> + </interface> +</node> diff --git a/mate-session/org.mate.SessionManager.ClientPrivate.xml b/mate-session/org.mate.SessionManager.ClientPrivate.xml new file mode 100644 index 0000000..6adedba --- /dev/null +++ b/mate-session/org.mate.SessionManager.ClientPrivate.xml @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.mate.SessionManager.ClientPrivate"> + <method name="EndSessionResponse"> + <arg name="is_ok" type="b" direction="in"> + <doc:doc> + <doc:summary>Whether or not it is OK to preceed</doc:summary> + </doc:doc> + </arg> + <arg name="reason" type="s" direction="in"> + <doc:doc> + <doc:summary>The reason string</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method should be used by the client in response to + the <doc:ref type="signal" to="org.mate.SessionManager.ClientPrivate::QueryEndSession">QueryEndSession</doc:ref> + and <doc:ref type="signal" to="org.mate.SessionManager.ClientPrivate::EndSession">EndSession</doc:ref> signals. + </doc:para> + </doc:description> + </doc:doc> + </method> + + <signal name="Stop"> + <doc:doc> + <doc:summary>Stop client</doc:summary> + <doc:description> + <doc:para> + The client should stop and remove itself from the session in + response to this signal. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="QueryEndSession"> + <arg name="flags" type="u"> + <doc:doc> + <doc:summary>Flags</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This signal is used to inform the client that the + session is about to end. The client must respond by + calling + <doc:ref type="method" to="org.mate.SessionManager.ClientPrivate.EndSessionResponse">EndSessionResponse</doc:ref> + within one second of the signal emission. + </doc:para> + <doc:para> + The flags may include: + <doc:list> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>Logout is forced. + <doc:ref type="method" to="org.mate.SessionManager.ClientPrivate.EndSessionResponse">EndSessionResponse</doc:ref> + reason and any inhibit from client will be + ignored.</doc:definition> + </doc:item> + </doc:list> + </doc:para> + <doc:para> + If the client responds with an EndSessionResponse is-ok + argument equal to FALSE and a reason then this reason may + be displayed to the user. + </doc:para> + <doc:para> + The client must not attempt to perform any actions or + interact with the user in response to this signal. Any + actions required for a clean shutdown should take place in + response to the + <doc:ref type="signal" to="org.mate.SessionManager.ClientPrivate::EndSession">EndSession</doc:ref> signal. + </doc:para> + <doc:para> + The client should limit operations until either a + <doc:ref type="signal" to="org.mate.SessionManager.ClientPrivate::EndSession">EndSession</doc:ref> + <doc:ref type="signal" to="org.mate.SessionManager.ClientPrivate::CancelEndSession">CancelEndSession</doc:ref> + signal is received. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="EndSession"> + <arg name="flags" type="u"> + <doc:doc> + <doc:summary>Flags</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This signal is used to inform the client that the + session is about to end. The client must respond by + calling + <doc:ref type="method" to="org.mate.SessionManager.ClientPrivate.EndSessionResponse">EndSessionResponse</doc:ref> + within ten seconds of the signal emission. + </doc:para> + <doc:para> + The client must not attempt to interact with the user in + response to this signal. The application will be given a + maxium of ten seconds to perform any actions required for + a clean shutdown. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="CancelEndSession"> + <doc:doc> + <doc:description> + <doc:para> + This signal indicates to the client that a previous emission of + <doc:ref type="signal" to="org.mate.SessionManager.ClientPrivate::QueryEndSession">QueryEndSession</doc:ref> + has been cancelled. The client should resume normal operations. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + </interface> +</node> diff --git a/mate-session/org.mate.SessionManager.Inhibitor.xml b/mate-session/org.mate.SessionManager.Inhibitor.xml new file mode 100644 index 0000000..4d1f128 --- /dev/null +++ b/mate-session/org.mate.SessionManager.Inhibitor.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.mate.SessionManager.Inhibitor"> + <method name="GetAppId"> + <arg type="s" name="app_id" direction="out"> + <doc:doc> + <doc:summary>The identifier for the associated application</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the application ID associated with this inhibit.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="GetClientId"> + <arg type="o" name="client_id" direction="out"> + <doc:doc> + <doc:summary>The object path of the associated client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the client object path associated with this inhibit.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="GetReason"> + <arg type="s" name="reason" direction="out"> + <doc:doc> + <doc:summary>The reason for the inhibit</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the reason for the inhibit</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="GetFlags"> + <arg type="u" name="flags" direction="out"> + <doc:doc> + <doc:summary>The flags that determine the scope of the inhibit</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the flags that determine the scope of the inhibit</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="GetToplevelXid"> + <arg type="u" name="xid" direction="out"> + <doc:doc> + <doc:summary>X11 toplevel window identifier associated with this inhibit. Zero if not set.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Return the X11 toplevel window identifier associated with this inhibit. Zero if not set.</doc:para> + </doc:description> + </doc:doc> + </method> + </interface> +</node> diff --git a/mate-session/org.mate.SessionManager.Presence.xml b/mate-session/org.mate.SessionManager.Presence.xml new file mode 100644 index 0000000..49f0620 --- /dev/null +++ b/mate-session/org.mate.SessionManager.Presence.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.mate.SessionManager.Presence"> + + <property name="status" type="u" access="readwrite"> + <doc:doc> + <doc:description> + <doc:para> + The status of the session. + </doc:para> + <doc:para> + The status parameter must be one of the following: + <doc:list> + <doc:item> + <doc:term>0</doc:term> + <doc:definition>Available</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>Invisible</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term> + <doc:definition>Busy</doc:definition> + </doc:item> + <doc:item> + <doc:term>3</doc:term> + <doc:definition>Idle</doc:definition> + </doc:item> + </doc:list> + </doc:para> + </doc:description> + </doc:doc> + </property> + <property name="status-text" type="s" access="readwrite"> + <doc:doc> + <doc:description> + <doc:para>The descriptive status for the session. + </doc:para> + </doc:description> + </doc:doc> + </property> + <method name="SetStatus"> + <arg type="u" name="status" direction="in"> + <doc:doc> + <doc:summary>The status value</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Set the status value of the session.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="SetStatusText"> + <arg type="s" name="status_text" direction="in"> + <doc:doc> + <doc:summary>The descriptive status for the session.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Set the descriptive status text for the session.</doc:para> + </doc:description> + </doc:doc> + </method> + + <signal name="StatusChanged"> + <arg name="status" type="u"> + <doc:doc> + <doc:summary>The new status value</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Indicates that the session status value has changed.</doc:para> + </doc:description> + </doc:doc> + </signal> + <signal name="StatusTextChanged"> + <arg name="status_text" type="s"> + <doc:doc> + <doc:summary>The new status text</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Indicates that the descriptive session status text has changed.</doc:para> + </doc:description> + </doc:doc> + </signal> + + </interface> +</node> diff --git a/mate-session/org.mate.SessionManager.xml b/mate-session/org.mate.SessionManager.xml new file mode 100644 index 0000000..9d693e6 --- /dev/null +++ b/mate-session/org.mate.SessionManager.xml @@ -0,0 +1,377 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.mate.SessionManager"> + + <!-- Initialization phase interfaces --> + + <method name="Setenv"> + <arg name="variable" type="s" direction="in"> + <doc:doc> + <doc:summary>The variable name</doc:summary> + </doc:doc> + </arg> + <arg name="value" type="s" direction="in"> + <doc:doc> + <doc:summary>The value</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Adds the variable name to the application launch environment with the specified value. May only be used during the Session Manager initialization phase.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="InitializationError"> + <arg name="message" type="s" direction="in"> + <doc:doc> + <doc:summary>The error message</doc:summary> + </doc:doc> + </arg> + <arg name="fatal" type="b" direction="in"> + <doc:doc> + <doc:summary>Whether the error should be treated as fatal</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>May be used by applications launched during the Session Manager initialization phase to indicate there was a problem.</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- Running phase interfaces --> + + <method name="RegisterClient"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="s" name="app_id" direction="in"> + <doc:doc> + <doc:summary>The application identifier</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="client_startup_id" direction="in"> + <doc:doc> + <doc:summary>Client startup identifier</doc:summary> + </doc:doc> + </arg> + <arg type="o" name="client_id" direction="out"> + <doc:doc> + <doc:summary>The object path of the newly registered client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Register the caller as a Session Management client.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="UnregisterClient"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="o" name="client_id" direction="in"> + <doc:doc> + <doc:summary>The object path of the client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Unregister the specified client from Session Management.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Inhibit"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="s" name="app_id" direction="in"> + <doc:doc> + <doc:summary>The application identifier</doc:summary> + </doc:doc> + </arg> + <arg type="u" name="toplevel_xid" direction="in"> + <doc:doc> + <doc:summary>The toplevel X window identifier</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="reason" direction="in"> + <doc:doc> + <doc:summary>The reason for the inhibit</doc:summary> + </doc:doc> + </arg> + <arg type="u" name="flags" direction="in"> + <doc:doc> + <doc:summary>Flags that spefify what should be inhibited</doc:summary> + </doc:doc> + </arg> + <arg type="u" name="inhibit_cookie" direction="out"> + <doc:doc> + <doc:summary>The cookie</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:summary> + Proactively indicates that the calling application is performing an action that should not be interrupted and sets a reason to be displayed to the user when an interruption is about to take placea. + </doc:summary> + <doc:description> + <doc:para>Applications should invoke this method when they begin an operation that + should not be interrupted, such as creating a CD or DVD. The types of actions + that may be blocked are specified by the flags parameter. When the application + completes the operation it should call <doc:ref type="method" to="org.mate.SessionManager.Uninhibit">Uninhibit()</doc:ref> + or disconnect from the session bus. + </doc:para> + <doc:para> + Applications should not expect that they will always be able to block the + action. In most cases, users will be given the option to force the action + to take place. + </doc:para> + <doc:para> + Reasons should be short and to the point. + </doc:para> + <doc:para> + The flags parameter must include at least one of the following: + <doc:list> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>Inhibit logging out</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term> + <doc:definition>Inhibit user switching</doc:definition> + </doc:item> + <doc:item> + <doc:term>4</doc:term> + <doc:definition>Inhibit suspending the session or computer</doc:definition> + </doc:item> + <doc:item> + <doc:term>8</doc:term> + <doc:definition>Inhibit the session being marked as idle</doc:definition> + </doc:item> + </doc:list> + Values for flags may be bitwise or'ed together. + </doc:para> + <doc:para> + The returned cookie is used to uniquely identify this request. It should be used + as an argument to <doc:ref type="method" to="org.mate.SessionManager.Uninhibit">Uninhibit()</doc:ref> in + order to remove the request. + </doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Uninhibit"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="u" name="inhibit_cookie" direction="in"> + <doc:doc> + <doc:summary>The cookie</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Cancel a previous call to <doc:ref type="method" to="org.mate.SessionManager.Inhibit">Inhibit()</doc:ref> identified by the cookie.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="IsInhibited"> + <arg type="u" name="flags" direction="in"> + <doc:doc> + <doc:summary>Flags that spefify what should be inhibited</doc:summary> + </doc:doc> + </arg> + <arg type="b" name="is_inhibited" direction="out"> + <doc:doc> + <doc:summary>Returns TRUE if any of the operations in the bitfield flags are inhibited</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Determine if operation(s) specified by the flags + are currently inhibited. Flags are same as those accepted + by the + <doc:ref type="method" to="org.mate.SessionManager.Inhibit">Inhibit()</doc:ref> + method.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="GetClients"> + <arg name="clients" direction="out" type="ao"> + <doc:doc> + <doc:summary>an array of client IDs</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This gets a list of all the <doc:ref type="interface" to="org.mate.SessionManager.Client">Clients</doc:ref> + that are currently known to the session manager.</doc:para> + <doc:para>Each Client ID is an D-Bus object path for the object that implements the + <doc:ref type="interface" to="org.mate.SessionManager.Client">Client</doc:ref> interface.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="org.mate.SessionManager.Client">org.mate.SessionManager.Client</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="GetInhibitors"> + <arg name="inhibitors" direction="out" type="ao"> + <doc:doc> + <doc:summary>an array of inhibitor IDs</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This gets a list of all the <doc:ref type="interface" to="org.mate.SessionManager.Inhibitor">Inhibitors</doc:ref> + that are currently known to the session manager.</doc:para> + <doc:para>Each Inhibitor ID is an D-Bus object path for the object that implements the + <doc:ref type="interface" to="org.mate.SessionManager.Inhibitor">Inhibitor</doc:ref> interface.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="org.mate.SessionManager.Inhibitor">org.mate.SessionManager.Inhibitor</doc:ref></doc:seealso> + </doc:doc> + </method> + + + <method name="IsAutostartConditionHandled"> + <arg name="condition" direction="in" type="s"> + <doc:doc> + <doc:summary>The autostart condition string</doc:summary> + </doc:doc> + </arg> + <arg name="handled" direction="out" type="b"> + <doc:doc> + <doc:summary>True if condition is handled, false otherwise</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Allows the caller to determine whether the session manager is + handling changes to the specified autostart condition.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Shutdown"> + <doc:doc> + <doc:description> + <doc:para>Request a shutdown dialog</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="CanShutdown"> + <arg name="is_available" direction="out" type="b"> + <doc:doc> + <doc:summary>True if shutdown is available to the user, false otherwise</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Allows the caller to determine whether or not it's okay to show + a shutdown option in the UI</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Logout"> + <arg name="mode" type="u" direction="in"> + <doc:doc> + <doc:summary>The type of logout that is being requested</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Request a logout dialog</doc:para> + <doc:para> + Allowed values for the mode parameter are: + <doc:list> + <doc:item> + <doc:term>0</doc:term> + <doc:definition>Normal.</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>No confirmation inferface should be shown.</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term> + <doc:definition>Forcefully logout. No confirmation will be shown and any inhibitors will be ignored.</doc:definition> + </doc:item> + </doc:list> + Values for flags may be bitwise or'ed together. + </doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- Signals --> + + <signal name="ClientAdded"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the added client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when a client has been added to the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + <signal name="ClientRemoved"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the removed client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when a client has been removed from the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="InhibitorAdded"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the added inhibitor</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when an inhibitor has been added to the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + <signal name="InhibitorRemoved"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the removed inhibitor</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when an inhibitor has been removed from the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="SessionRunning"> + <doc:doc> + <doc:description> + <doc:para>Indicates the session has entered the Running phase.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="SessionOver"> + <doc:doc> + <doc:description> + <doc:para>Indicates the session is about to end.</doc:para> + </doc:description> + </doc:doc> + </signal> + + </interface> +</node> diff --git a/mate-session/test-client-dbus.c b/mate-session/test-client-dbus.c new file mode 100644 index 0000000..2d6981f --- /dev/null +++ b/mate-session/test-client-dbus.c @@ -0,0 +1,261 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> +#include <dbus/dbus-glib.h> + +#define SM_DBUS_NAME "org.mate.SessionManager" +#define SM_DBUS_PATH "/org/mate/SessionManager" +#define SM_DBUS_INTERFACE "org.mate.SessionManager" + +#define SM_CLIENT_DBUS_INTERFACE "org.mate.SessionManager.ClientPrivate" + +static DBusGConnection *bus_connection = NULL; +static DBusGProxy *sm_proxy = NULL; +static char *client_id = NULL; +static DBusGProxy *client_proxy = NULL; +static GMainLoop *main_loop = NULL; + +static gboolean +session_manager_connect (void) +{ + + if (bus_connection == NULL) { + GError *error; + + error = NULL; + bus_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (bus_connection == NULL) { + g_message ("Failed to connect to the session bus: %s", + error->message); + g_error_free (error); + exit (1); + } + } + + sm_proxy = dbus_g_proxy_new_for_name (bus_connection, + SM_DBUS_NAME, + SM_DBUS_PATH, + SM_DBUS_INTERFACE); + return (sm_proxy != NULL); +} + +static void +on_client_query_end_session (DBusGProxy *proxy, + guint flags, + gpointer data) +{ + GError *error; + gboolean is_ok; + gboolean res; + const char *reason; + + is_ok = FALSE; + reason = "Unsaved files"; + + g_debug ("Got query end session signal flags=%u", flags); + + error = NULL; + res = dbus_g_proxy_call (proxy, + "EndSessionResponse", + &error, + G_TYPE_BOOLEAN, is_ok, + G_TYPE_STRING, reason, + G_TYPE_INVALID, + G_TYPE_INVALID); +} + +static void +on_client_end_session (DBusGProxy *proxy, + guint flags, + gpointer data) +{ + g_debug ("Got end session signal flags=%u", flags); +} + +static void +on_client_cancel_end_session (DBusGProxy *proxy, + gpointer data) +{ + g_debug ("Got end session cancelled signal"); +} + +static void +on_client_stop (DBusGProxy *proxy, + gpointer data) +{ + g_debug ("Got client stop signal"); + g_main_loop_quit (main_loop); +} + +static gboolean +register_client (void) +{ + GError *error; + gboolean res; + const char *startup_id; + const char *app_id; + + startup_id = g_getenv ("DESKTOP_AUTOSTART_ID"); + app_id = "gedit"; + + error = NULL; + res = dbus_g_proxy_call (sm_proxy, + "RegisterClient", + &error, + G_TYPE_STRING, app_id, + G_TYPE_STRING, startup_id, + G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, &client_id, + G_TYPE_INVALID); + if (! res) { + g_warning ("Failed to register client: %s", error->message); + g_error_free (error); + return FALSE; + } + + g_debug ("Client registered with session manager: %s", client_id); + client_proxy = dbus_g_proxy_new_for_name (bus_connection, + SM_DBUS_NAME, + client_id, + SM_CLIENT_DBUS_INTERFACE); + dbus_g_proxy_add_signal (client_proxy, + "QueryEndSession", + G_TYPE_UINT, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (client_proxy, + "EndSession", + G_TYPE_UINT, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (client_proxy, + "CancelEndSession", + G_TYPE_UINT, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (client_proxy, + "Stop", + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (client_proxy, + "QueryEndSession", + G_CALLBACK (on_client_query_end_session), + NULL, + NULL); + dbus_g_proxy_connect_signal (client_proxy, + "EndSession", + G_CALLBACK (on_client_end_session), + NULL, + NULL); + dbus_g_proxy_connect_signal (client_proxy, + "CancelEndSession", + G_CALLBACK (on_client_cancel_end_session), + NULL, + NULL); + dbus_g_proxy_connect_signal (client_proxy, + "Stop", + G_CALLBACK (on_client_stop), + NULL, + NULL); + + return TRUE; +} + +static gboolean +session_manager_disconnect (void) +{ + if (sm_proxy != NULL) { + g_object_unref (sm_proxy); + sm_proxy = NULL; + } + + return TRUE; +} + +static gboolean +unregister_client (void) +{ + GError *error; + gboolean res; + + error = NULL; + res = dbus_g_proxy_call (sm_proxy, + "UnregisterClient", + &error, + DBUS_TYPE_G_OBJECT_PATH, client_id, + G_TYPE_INVALID, + G_TYPE_INVALID); + if (! res) { + g_warning ("Failed to unregister client: %s", error->message); + g_error_free (error); + return FALSE; + } + + g_free (client_id); + client_id = NULL; + + return TRUE; +} + +static gboolean +quit_test (gpointer data) +{ + g_main_loop_quit (main_loop); + return FALSE; +} + +int +main (int argc, + char *argv[]) +{ + gboolean res; + + g_log_set_always_fatal (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); + + g_type_init (); + + res = session_manager_connect (); + if (! res) { + g_warning ("Unable to connect to session manager"); + exit (1); + } + + res = register_client (); + if (! res) { + g_warning ("Unable to register client with session manager"); + } + + main_loop = g_main_loop_new (NULL, FALSE); + + g_timeout_add_seconds (30, quit_test, NULL); + + g_main_loop_run (main_loop); + g_main_loop_unref (main_loop); + + unregister_client (); + session_manager_disconnect (); + + return 0; +} diff --git a/mate-session/test-inhibit.c b/mate-session/test-inhibit.c new file mode 100644 index 0000000..afd860c --- /dev/null +++ b/mate-session/test-inhibit.c @@ -0,0 +1,198 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <gtk/gtk.h> +#include <gdk/gdkx.h> +#include <dbus/dbus-glib.h> + +#define SM_DBUS_NAME "org.mate.SessionManager" +#define SM_DBUS_PATH "/org/mate/SessionManager" +#define SM_DBUS_INTERFACE "org.mate.SessionManager" + +static DBusGConnection *bus_connection = NULL; +static DBusGProxy *sm_proxy = NULL; +static guint cookie = 0; + +static gboolean +session_manager_connect (void) +{ + + if (bus_connection == NULL) { + GError *error; + + error = NULL; + bus_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (bus_connection == NULL) { + g_message ("Failed to connect to the session bus: %s", + error->message); + g_error_free (error); + exit (1); + } + } + + sm_proxy = dbus_g_proxy_new_for_name (bus_connection, + SM_DBUS_NAME, + SM_DBUS_PATH, + SM_DBUS_INTERFACE); + return (sm_proxy != NULL); +} + +typedef enum { + GSM_INHIBITOR_FLAG_LOGOUT = 1 << 0, + GSM_INHIBITOR_FLAG_SWITCH_USER = 1 << 1, + GSM_INHIBITOR_FLAG_SUSPEND = 1 << 2 +} GsmInhibitFlag; + +static gboolean +do_inhibit_for_window (GdkWindow *window) +{ + GError *error; + gboolean res; + const char *startup_id; + const char *app_id; + const char *reason; + guint toplevel_xid; + guint flags; + + startup_id = g_getenv ("DESKTOP_AUTOSTART_ID"); +#if 1 + app_id = "caja-cd-burner"; + reason = "A CD burn is in progress."; +#else + app_id = "caja"; + reason = "A file transfer is in progress."; +#endif + toplevel_xid = GDK_DRAWABLE_XID (window); + flags = GSM_INHIBITOR_FLAG_LOGOUT + | GSM_INHIBITOR_FLAG_SWITCH_USER + | GSM_INHIBITOR_FLAG_SUSPEND; + + error = NULL; + res = dbus_g_proxy_call (sm_proxy, + "Inhibit", + &error, + G_TYPE_STRING, app_id, + G_TYPE_UINT, toplevel_xid, + G_TYPE_STRING, reason, + G_TYPE_UINT, flags, + G_TYPE_INVALID, + G_TYPE_UINT, &cookie, + G_TYPE_INVALID); + if (! res) { + g_warning ("Failed to inhibit: %s", error->message); + g_error_free (error); + return FALSE; + } + + g_debug ("Inhibiting session manager: %u", cookie); + + return TRUE; +} +static gboolean +session_manager_disconnect (void) +{ + if (sm_proxy != NULL) { + g_object_unref (sm_proxy); + sm_proxy = NULL; + } + + return TRUE; +} + +static gboolean +do_uninhibit (void) +{ + GError *error; + gboolean res; + + error = NULL; + res = dbus_g_proxy_call (sm_proxy, + "Uninhibit", + &error, + G_TYPE_UINT, cookie, + G_TYPE_INVALID, + G_TYPE_INVALID); + if (! res) { + g_warning ("Failed to uninhibit: %s", error->message); + g_error_free (error); + return FALSE; + } + + cookie = 0; + + return TRUE; +} + +static void +on_widget_show (GtkWidget *dialog, + gpointer data) +{ + gboolean res; + + res = do_inhibit_for_window (gtk_widget_get_window (dialog)); + if (! res) { + g_warning ("Unable to register client with session manager"); + } +} + +int +main (int argc, + char *argv[]) +{ + gboolean res; + GtkWidget *dialog; + + g_log_set_always_fatal (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); + + gtk_init (&argc, &argv); + + res = session_manager_connect (); + if (! res) { + g_warning ("Unable to connect to session manager"); + exit (1); + } + + g_timeout_add_seconds (30, (GSourceFunc)gtk_main_quit, NULL); + + dialog = gtk_message_dialog_new (NULL, + 0, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CANCEL, + "Inhibiting logout, switch user, and suspend."); + + g_signal_connect (dialog, "response", G_CALLBACK (gtk_main_quit), NULL); + g_signal_connect (dialog, "show", G_CALLBACK (on_widget_show), NULL); + gtk_widget_show (dialog); + + gtk_main (); + + do_uninhibit (); + session_manager_disconnect (); + + return 0; +} |