diff options
Diffstat (limited to 'cut-n-paste-code')
| -rw-r--r-- | cut-n-paste-code/Makefile.am | 2 | ||||
| -rw-r--r-- | cut-n-paste-code/Makefile.in | 646 | ||||
| -rw-r--r-- | cut-n-paste-code/README | 21 | ||||
| -rw-r--r-- | cut-n-paste-code/libegg/Makefile.am | 46 | ||||
| -rw-r--r-- | cut-n-paste-code/libegg/Makefile.in | 657 | ||||
| -rw-r--r-- | cut-n-paste-code/libegg/eggdesktopfile.c | 1477 | ||||
| -rw-r--r-- | cut-n-paste-code/libegg/eggdesktopfile.h | 166 | ||||
| -rw-r--r-- | cut-n-paste-code/libegg/eggsmclient-private.h | 57 | ||||
| -rw-r--r-- | cut-n-paste-code/libegg/eggsmclient-xsmp.c | 1371 | ||||
| -rw-r--r-- | cut-n-paste-code/libegg/eggsmclient.c | 602 | ||||
| -rw-r--r-- | cut-n-paste-code/libegg/eggsmclient.h | 123 | ||||
| -rw-r--r-- | cut-n-paste-code/libegg/eggtreemultidnd.c | 415 | ||||
| -rw-r--r-- | cut-n-paste-code/libegg/eggtreemultidnd.h | 78 | ||||
| -rwxr-xr-x | cut-n-paste-code/libegg/update-from-egg.sh | 25 | 
14 files changed, 5686 insertions, 0 deletions
diff --git a/cut-n-paste-code/Makefile.am b/cut-n-paste-code/Makefile.am new file mode 100644 index 00000000..80f6abb0 --- /dev/null +++ b/cut-n-paste-code/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = libegg + diff --git a/cut-n-paste-code/Makefile.in b/cut-n-paste-code/Makefile.in new file mode 100644 index 00000000..abdf5846 --- /dev/null +++ b/cut-n-paste-code/Makefile.in @@ -0,0 +1,646 @@ +# 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@ +subdir = cut-n-paste-code +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gtk-doc.m4 \ +	$(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.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ +	$(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo "  GEN   " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ +	html-recursive info-recursive install-data-recursive \ +	install-dvi-recursive install-exec-recursive \ +	install-html-recursive install-info-recursive \ +	install-pdf-recursive install-ps-recursive install-recursive \ +	installcheck-recursive installdirs-recursive pdf-recursive \ +	ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\ +  distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ +	$(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ +	distdir +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ +  dir0=`pwd`; \ +  sed_first='s,^\([^/]*\)/.*$$,\1,'; \ +  sed_rest='s,^[^/]*/*,,'; \ +  sed_last='s,^.*/\([^/]*\)$$,\1,'; \ +  sed_butlast='s,/*[^/]*$$,,'; \ +  while test -n "$$dir1"; do \ +    first=`echo "$$dir1" | sed -e "$$sed_first"`; \ +    if test "$$first" != "."; then \ +      if test "$$first" = ".."; then \ +        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ +        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ +      else \ +        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ +        if test "$$first2" = "$$first"; then \ +          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ +        else \ +          dir2="../$$dir2"; \ +        fi; \ +        dir0="$$dir0"/"$$first"; \ +      fi; \ +    fi; \ +    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ +  done; \ +  reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_CFLAGS = @ALL_CFLAGS@ +ALL_LIBS = @ALL_LIBS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CAJA_EXTENSION_VERSION_INFO = @CAJA_EXTENSION_VERSION_INFO@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CORE_CFLAGS = @CORE_CFLAGS@ +CORE_LIBS = @CORE_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISABLE_DEPRECATED_CFLAGS = @DISABLE_DEPRECATED_CFLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_EMPTY_VIEW = @ENABLE_EMPTY_VIEW@ +ENABLE_PROFILER = @ENABLE_PROFILER@ +EXEEXT = @EXEEXT@ +EXEMPI_CFLAGS = @EXEMPI_CFLAGS@ +EXEMPI_LIBS = @EXEMPI_LIBS@ +EXEMPI_NEW_API_CFLAGS = @EXEMPI_NEW_API_CFLAGS@ +EXEMPI_NEW_API_LIBS = @EXEMPI_NEW_API_LIBS@ +EXIF_CFLAGS = @EXIF_CFLAGS@ +EXIF_LIBS = @EXIF_LIBS@ +FGREP = @FGREP@ +GAIL_REQUIRED = @GAIL_REQUIRED@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_REQUIRED = @GLIB_REQUIRED@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GTK_REQUIRED = @GTK_REQUIRED@ +HTML_DIR = @HTML_DIR@ +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@ +INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@ +INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ +INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@ +INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@ +INTROSPECTION_LIBS = @INTROSPECTION_LIBS@ +INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ +INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ +INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCAJA_EXTENSION_CFLAGS = @LIBCAJA_EXTENSION_CFLAGS@ +LIBCAJA_EXTENSION_LIBS = @LIBCAJA_EXTENSION_LIBS@ +LIBEGG_CFLAGS = @LIBEGG_CFLAGS@ +LIBEGG_LIBS = @LIBEGG_LIBS@ +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_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@ +MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@ +MATE_DESKTOP_REQUIRED = @MATE_DESKTOP_REQUIRED@ +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@ +PANGO_REQUIRED = @PANGO_REQUIRED@ +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@ +RENDER_LIBS = @RENDER_LIBS@ +SED = @SED@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +UNIQUE_CFLAGS = @UNIQUE_CFLAGS@ +UNIQUE_LIBS = @UNIQUE_LIBS@ +UPDATE_MIME_DATABASE = @UPDATE_MIME_DATABASE@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WARNING_CFLAGS = @WARNING_CFLAGS@ +XGETTEXT = @XGETTEXT@ +XMKMF = @XMKMF@ +XML_REQUIRED = @XML_REQUIRED@ +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@ +SUBDIRS = libegg +all: all-recursive + +.SUFFIXES: +$(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 cut-n-paste-code/Makefile'; \ +	$(am__cd) $(top_srcdir) && \ +	  $(AUTOMAKE) --gnu cut-n-paste-code/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): + +mostlyclean-libtool: +	-rm -f *.lo + +clean-libtool: +	-rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +#     (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): +	@fail= failcom='exit 1'; \ +	for f in x $$MAKEFLAGS; do \ +	  case $$f in \ +	    *=* | --[!k]*);; \ +	    *k*) failcom='fail=yes';; \ +	  esac; \ +	done; \ +	dot_seen=no; \ +	target=`echo $@ | sed s/-recursive//`; \ +	list='$(SUBDIRS)'; for subdir in $$list; do \ +	  echo "Making $$target in $$subdir"; \ +	  if test "$$subdir" = "."; then \ +	    dot_seen=yes; \ +	    local_target="$$target-am"; \ +	  else \ +	    local_target="$$target"; \ +	  fi; \ +	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ +	  || eval $$failcom; \ +	done; \ +	if test "$$dot_seen" = "no"; then \ +	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ +	fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): +	@fail= failcom='exit 1'; \ +	for f in x $$MAKEFLAGS; do \ +	  case $$f in \ +	    *=* | --[!k]*);; \ +	    *k*) failcom='fail=yes';; \ +	  esac; \ +	done; \ +	dot_seen=no; \ +	case "$@" in \ +	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ +	  *) list='$(SUBDIRS)' ;; \ +	esac; \ +	rev=''; for subdir in $$list; do \ +	  if test "$$subdir" = "."; then :; else \ +	    rev="$$subdir $$rev"; \ +	  fi; \ +	done; \ +	rev="$$rev ."; \ +	target=`echo $@ | sed s/-recursive//`; \ +	for subdir in $$rev; do \ +	  echo "Making $$target in $$subdir"; \ +	  if test "$$subdir" = "."; then \ +	    local_target="$$target-am"; \ +	  else \ +	    local_target="$$target"; \ +	  fi; \ +	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ +	  || eval $$failcom; \ +	done && test -z "$$fail" +tags-recursive: +	list='$(SUBDIRS)'; for subdir in $$list; do \ +	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ +	done +ctags-recursive: +	list='$(SUBDIRS)'; for subdir in $$list; do \ +	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ +	done + +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: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \ +		$(TAGS_FILES) $(LISP) +	set x; \ +	here=`pwd`; \ +	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ +	  include_option=--etags-include; \ +	  empty_fix=.; \ +	else \ +	  include_option=--include; \ +	  empty_fix=; \ +	fi; \ +	list='$(SUBDIRS)'; for subdir in $$list; do \ +	  if test "$$subdir" = .; then :; else \ +	    test ! -f $$subdir/TAGS || \ +	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ +	  fi; \ +	done; \ +	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: ctags-recursive $(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 +	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ +	  if test "$$subdir" = .; then :; else \ +	    test -d "$(distdir)/$$subdir" \ +	    || $(MKDIR_P) "$(distdir)/$$subdir" \ +	    || exit 1; \ +	  fi; \ +	done +	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ +	  if test "$$subdir" = .; then :; else \ +	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ +	    $(am__relativize); \ +	    new_distdir=$$reldir; \ +	    dir1=$$subdir; dir2="$(top_distdir)"; \ +	    $(am__relativize); \ +	    new_top_distdir=$$reldir; \ +	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ +	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ +	    ($(am__cd) $$subdir && \ +	      $(MAKE) $(AM_MAKEFLAGS) \ +	        top_distdir="$$new_top_distdir" \ +	        distdir="$$new_distdir" \ +		am__remove_distdir=: \ +		am__skip_length_check=: \ +		am__skip_mode_fix=: \ +	        distdir) \ +	      || exit 1; \ +	  fi; \ +	done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am +	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: +	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ +	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ +	  `test -z '$(STRIP)' || \ +	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: +	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) +	-test . = "$(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." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive +	-rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive +	-rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ +	install-am install-strip tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ +	all all-am check check-am clean clean-generic clean-libtool \ +	ctags ctags-recursive distclean distclean-generic \ +	distclean-libtool distclean-tags distdir dvi dvi-am html \ +	html-am info info-am install install-am install-data \ +	install-data-am install-dvi install-dvi-am install-exec \ +	install-exec-am install-html install-html-am install-info \ +	install-info-am install-man install-pdf install-pdf-am \ +	install-ps install-ps-am install-strip installcheck \ +	installcheck-am installdirs installdirs-am maintainer-clean \ +	maintainer-clean-generic mostlyclean mostlyclean-generic \ +	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ +	uninstall uninstall-am + + +# 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/cut-n-paste-code/README b/cut-n-paste-code/README new file mode 100644 index 00000000..b8f4406f --- /dev/null +++ b/cut-n-paste-code/README @@ -0,0 +1,21 @@ +README for caja/cut-n-paste-code + +The code in this directory hierarchy was cut-n-pasted from  +somewhere else. + +In the soon to come, Star Trek future, this code will be available +as part of standard libraries. + +For example, the code in libegg will be one day available as part +of Gtk+. + +Until that happens, DON'T HACK the code, unless you are updating  +from the original cut-n-paste source. + +Instead of hacking the code in cut-n-paste-code, create subclasses  +and put them in libcaja-extensions. + +If you have any specific questions, comments or complaints about this +setup, send mail to the caja mailing list at: + diff --git a/cut-n-paste-code/libegg/Makefile.am b/cut-n-paste-code/libegg/Makefile.am new file mode 100644 index 00000000..1ee4585d --- /dev/null +++ b/cut-n-paste-code/libegg/Makefile.am @@ -0,0 +1,46 @@ +NULL= + +noinst_LTLIBRARIES = libegg.la + +INCLUDES = $(LIBEGG_CFLAGS) + +EGG_TREE_DND_FILES = 		\ +	eggtreemultidnd.c	\ +	eggtreemultidnd.h	\ +	$(NULL) + +EGG_SMCLIENT_FILES = 		\ +	eggdesktopfile.c	\ +	eggdesktopfile.h	\ +	eggsmclient.c		\ +	eggsmclient.h		\ +	eggsmclient-private.h	\ +	eggsmclient-xsmp.c	\ +	$(NULL) + +libegg_la_SOURCES = 		\ +	$(EGG_TREE_DND_FILES)	\ +	$(EGG_SMCLIENT_FILES)	\ +	$(NULL) + +libegg_la_CFLAGS =				\ +	-DEGG_SM_CLIENT_BACKEND_XSMP		\ +	-DG_LOG_DOMAIN=\""EggSMClient"\"	\ +	$(LIBEGG_CFLAGS)			\ +	$(WARNING_CFLAGS)			\ +	$(DISABLE_DEPRECATED) + +libegg_la_LIBADD = 	\ +	$(LIBEGG_LIBS)	\ +	-lSM -lICE + +EXTRA_DIST = 			\ +	update-from-egg.sh	\ +	$(NULL) + +EGG_TREE_DND_DIR = $(srcdir)/../../../libegg/libegg/treeviewutils +EGG_SMCLIENT_DIR = $(srcdir)/../../../libegg/libegg/smclient + +regenerate-built-sources: +	EGGFILES="$(EGG_TREE_DND_FILES)" EGGDIR="$(EGG_TREE_DND_DIR)" $(srcdir)/update-from-egg.sh +	EGGFILES="$(EGG_SMCLIENT_FILES)" EGGDIR="$(EGG_SMCLIENT_DIR)" $(srcdir)/update-from-egg.sh diff --git a/cut-n-paste-code/libegg/Makefile.in b/cut-n-paste-code/libegg/Makefile.in new file mode 100644 index 00000000..f9fea4a5 --- /dev/null +++ b/cut-n-paste-code/libegg/Makefile.in @@ -0,0 +1,657 @@ +# 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@ +subdir = cut-n-paste-code/libegg +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gtk-doc.m4 \ +	$(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.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ +	$(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libegg_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am__objects_1 = +am__objects_2 = libegg_la-eggtreemultidnd.lo $(am__objects_1) +am__objects_3 = libegg_la-eggdesktopfile.lo libegg_la-eggsmclient.lo \ +	libegg_la-eggsmclient-xsmp.lo $(am__objects_1) +am_libegg_la_OBJECTS = $(am__objects_2) $(am__objects_3) \ +	$(am__objects_1) +libegg_la_OBJECTS = $(am_libegg_la_OBJECTS) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +libegg_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ +	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(libegg_la_CFLAGS) \ +	$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +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 = $(libegg_la_SOURCES) +DIST_SOURCES = $(libegg_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_CFLAGS = @ALL_CFLAGS@ +ALL_LIBS = @ALL_LIBS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CAJA_EXTENSION_VERSION_INFO = @CAJA_EXTENSION_VERSION_INFO@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CORE_CFLAGS = @CORE_CFLAGS@ +CORE_LIBS = @CORE_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISABLE_DEPRECATED_CFLAGS = @DISABLE_DEPRECATED_CFLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_EMPTY_VIEW = @ENABLE_EMPTY_VIEW@ +ENABLE_PROFILER = @ENABLE_PROFILER@ +EXEEXT = @EXEEXT@ +EXEMPI_CFLAGS = @EXEMPI_CFLAGS@ +EXEMPI_LIBS = @EXEMPI_LIBS@ +EXEMPI_NEW_API_CFLAGS = @EXEMPI_NEW_API_CFLAGS@ +EXEMPI_NEW_API_LIBS = @EXEMPI_NEW_API_LIBS@ +EXIF_CFLAGS = @EXIF_CFLAGS@ +EXIF_LIBS = @EXIF_LIBS@ +FGREP = @FGREP@ +GAIL_REQUIRED = @GAIL_REQUIRED@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_REQUIRED = @GLIB_REQUIRED@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GTK_REQUIRED = @GTK_REQUIRED@ +HTML_DIR = @HTML_DIR@ +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@ +INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@ +INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ +INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@ +INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@ +INTROSPECTION_LIBS = @INTROSPECTION_LIBS@ +INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ +INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ +INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCAJA_EXTENSION_CFLAGS = @LIBCAJA_EXTENSION_CFLAGS@ +LIBCAJA_EXTENSION_LIBS = @LIBCAJA_EXTENSION_LIBS@ +LIBEGG_CFLAGS = @LIBEGG_CFLAGS@ +LIBEGG_LIBS = @LIBEGG_LIBS@ +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_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@ +MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@ +MATE_DESKTOP_REQUIRED = @MATE_DESKTOP_REQUIRED@ +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@ +PANGO_REQUIRED = @PANGO_REQUIRED@ +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@ +RENDER_LIBS = @RENDER_LIBS@ +SED = @SED@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +UNIQUE_CFLAGS = @UNIQUE_CFLAGS@ +UNIQUE_LIBS = @UNIQUE_LIBS@ +UPDATE_MIME_DATABASE = @UPDATE_MIME_DATABASE@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WARNING_CFLAGS = @WARNING_CFLAGS@ +XGETTEXT = @XGETTEXT@ +XMKMF = @XMKMF@ +XML_REQUIRED = @XML_REQUIRED@ +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@ +NULL =  +noinst_LTLIBRARIES = libegg.la +INCLUDES = $(LIBEGG_CFLAGS) +EGG_TREE_DND_FILES = \ +	eggtreemultidnd.c	\ +	eggtreemultidnd.h	\ +	$(NULL) + +EGG_SMCLIENT_FILES = \ +	eggdesktopfile.c	\ +	eggdesktopfile.h	\ +	eggsmclient.c		\ +	eggsmclient.h		\ +	eggsmclient-private.h	\ +	eggsmclient-xsmp.c	\ +	$(NULL) + +libegg_la_SOURCES = \ +	$(EGG_TREE_DND_FILES)	\ +	$(EGG_SMCLIENT_FILES)	\ +	$(NULL) + +libegg_la_CFLAGS = \ +	-DEGG_SM_CLIENT_BACKEND_XSMP		\ +	-DG_LOG_DOMAIN=\""EggSMClient"\"	\ +	$(LIBEGG_CFLAGS)			\ +	$(WARNING_CFLAGS)			\ +	$(DISABLE_DEPRECATED) + +libegg_la_LIBADD = \ +	$(LIBEGG_LIBS)	\ +	-lSM -lICE + +EXTRA_DIST = \ +	update-from-egg.sh	\ +	$(NULL) + +EGG_TREE_DND_DIR = $(srcdir)/../../../libegg/libegg/treeviewutils +EGG_SMCLIENT_DIR = $(srcdir)/../../../libegg/libegg/smclient +all: 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 cut-n-paste-code/libegg/Makefile'; \ +	$(am__cd) $(top_srcdir) && \ +	  $(AUTOMAKE) --gnu cut-n-paste-code/libegg/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 +libegg.la: $(libegg_la_OBJECTS) $(libegg_la_DEPENDENCIES)  +	$(AM_V_CCLD)$(libegg_la_LINK)  $(libegg_la_OBJECTS) $(libegg_la_LIBADD) $(LIBS) + +mostlyclean-compile: +	-rm -f *.$(OBJEXT) + +distclean-compile: +	-rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libegg_la-eggdesktopfile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libegg_la-eggsmclient-xsmp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libegg_la-eggsmclient.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libegg_la-eggtreemultidnd.Plo@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 $@ $< + +libegg_la-eggtreemultidnd.lo: eggtreemultidnd.c +@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libegg_la_CFLAGS) $(CFLAGS) -MT libegg_la-eggtreemultidnd.lo -MD -MP -MF $(DEPDIR)/libegg_la-eggtreemultidnd.Tpo -c -o libegg_la-eggtreemultidnd.lo `test -f 'eggtreemultidnd.c' || echo '$(srcdir)/'`eggtreemultidnd.c +@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libegg_la-eggtreemultidnd.Tpo $(DEPDIR)/libegg_la-eggtreemultidnd.Plo +@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='eggtreemultidnd.c' object='libegg_la-eggtreemultidnd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libegg_la_CFLAGS) $(CFLAGS) -c -o libegg_la-eggtreemultidnd.lo `test -f 'eggtreemultidnd.c' || echo '$(srcdir)/'`eggtreemultidnd.c + +libegg_la-eggdesktopfile.lo: eggdesktopfile.c +@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libegg_la_CFLAGS) $(CFLAGS) -MT libegg_la-eggdesktopfile.lo -MD -MP -MF $(DEPDIR)/libegg_la-eggdesktopfile.Tpo -c -o libegg_la-eggdesktopfile.lo `test -f 'eggdesktopfile.c' || echo '$(srcdir)/'`eggdesktopfile.c +@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libegg_la-eggdesktopfile.Tpo $(DEPDIR)/libegg_la-eggdesktopfile.Plo +@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='eggdesktopfile.c' object='libegg_la-eggdesktopfile.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libegg_la_CFLAGS) $(CFLAGS) -c -o libegg_la-eggdesktopfile.lo `test -f 'eggdesktopfile.c' || echo '$(srcdir)/'`eggdesktopfile.c + +libegg_la-eggsmclient.lo: eggsmclient.c +@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libegg_la_CFLAGS) $(CFLAGS) -MT libegg_la-eggsmclient.lo -MD -MP -MF $(DEPDIR)/libegg_la-eggsmclient.Tpo -c -o libegg_la-eggsmclient.lo `test -f 'eggsmclient.c' || echo '$(srcdir)/'`eggsmclient.c +@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libegg_la-eggsmclient.Tpo $(DEPDIR)/libegg_la-eggsmclient.Plo +@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='eggsmclient.c' object='libegg_la-eggsmclient.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libegg_la_CFLAGS) $(CFLAGS) -c -o libegg_la-eggsmclient.lo `test -f 'eggsmclient.c' || echo '$(srcdir)/'`eggsmclient.c + +libegg_la-eggsmclient-xsmp.lo: eggsmclient-xsmp.c +@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libegg_la_CFLAGS) $(CFLAGS) -MT libegg_la-eggsmclient-xsmp.lo -MD -MP -MF $(DEPDIR)/libegg_la-eggsmclient-xsmp.Tpo -c -o libegg_la-eggsmclient-xsmp.lo `test -f 'eggsmclient-xsmp.c' || echo '$(srcdir)/'`eggsmclient-xsmp.c +@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libegg_la-eggsmclient-xsmp.Tpo $(DEPDIR)/libegg_la-eggsmclient-xsmp.Plo +@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='eggsmclient-xsmp.c' object='libegg_la-eggsmclient-xsmp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libegg_la_CFLAGS) $(CFLAGS) -c -o libegg_la-eggsmclient-xsmp.lo `test -f 'eggsmclient-xsmp.c' || echo '$(srcdir)/'`eggsmclient-xsmp.c + +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: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am +	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: +	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ +	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ +	  `test -z '$(STRIP)' || \ +	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: +	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) +	-test . = "$(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." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ +	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-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: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ +	clean-libtool clean-noinstLTLIBRARIES ctags distclean \ +	distclean-compile distclean-generic distclean-libtool \ +	distclean-tags distdir dvi dvi-am html html-am info info-am \ +	install install-am install-data install-data-am install-dvi \ +	install-dvi-am install-exec install-exec-am install-html \ +	install-html-am install-info install-info-am install-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 + + +regenerate-built-sources: +	EGGFILES="$(EGG_TREE_DND_FILES)" EGGDIR="$(EGG_TREE_DND_DIR)" $(srcdir)/update-from-egg.sh +	EGGFILES="$(EGG_SMCLIENT_FILES)" EGGDIR="$(EGG_SMCLIENT_DIR)" $(srcdir)/update-from-egg.sh + +# 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/cut-n-paste-code/libegg/eggdesktopfile.c b/cut-n-paste-code/libegg/eggdesktopfile.c new file mode 100644 index 00000000..6e9cf44d --- /dev/null +++ b/cut-n-paste-code/libegg/eggdesktopfile.c @@ -0,0 +1,1477 @@ +/* eggdesktopfile.c - Freedesktop.Org Desktop Files + * Copyright (C) 2007 Novell, Inc. + * + * Based on mate-desktop-item.c + * Copyright (C) 1999, 2000 Red Hat Inc. + * Copyright (C) 2001 George Lebl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 Lesser General Public + * License along with this library; see the file COPYING.LIB. 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 "eggdesktopfile.h" + +#include <string.h> +#include <unistd.h> + +#include <glib/gi18n.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +struct EggDesktopFile +{ +    GKeyFile           *key_file; +    char               *source; + +    char               *name, *icon; +    EggDesktopFileType  type; +    char                document_code; +}; + +/** + * egg_desktop_file_new: + * @desktop_file_path: path to a Freedesktop-style Desktop file + * @error: error pointer + * + * Creates a new #EggDesktopFile for @desktop_file. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new (const char *desktop_file_path, GError **error) +{ +    GKeyFile *key_file; + +    key_file = g_key_file_new (); +    if (!g_key_file_load_from_file (key_file, desktop_file_path, 0, error)) +    { +        g_key_file_free (key_file); +        return NULL; +    } + +    return egg_desktop_file_new_from_key_file (key_file, desktop_file_path, +            error); +} + +/** + * egg_desktop_file_new_from_data_dirs: + * @desktop_file_path: relative path to a Freedesktop-style Desktop file + * @error: error pointer + * + * Looks for @desktop_file_path in the paths returned from + * g_get_user_data_dir() and g_get_system_data_dirs(), and creates + * a new #EggDesktopFile from it. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_data_dirs (const char  *desktop_file_path, +                                     GError     **error) +{ +    EggDesktopFile *desktop_file; +    GKeyFile *key_file; +    char *full_path; + +    key_file = g_key_file_new (); +    if (!g_key_file_load_from_data_dirs (key_file, desktop_file_path, +                                         &full_path, 0, error)) +    { +        g_key_file_free (key_file); +        return NULL; +    } + +    desktop_file = egg_desktop_file_new_from_key_file (key_file, +                   full_path, +                   error); +    g_free (full_path); +    return desktop_file; +} + +/** + * egg_desktop_file_new_from_dirs: + * @desktop_file_path: relative path to a Freedesktop-style Desktop file + * @search_dirs: NULL-terminated array of directories to search + * @error: error pointer + * + * Looks for @desktop_file_path in the paths returned from + * g_get_user_data_dir() and g_get_system_data_dirs(), and creates + * a new #EggDesktopFile from it. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_dirs (const char  *desktop_file_path, +                                const char **search_dirs, +                                GError     **error) +{ +    EggDesktopFile *desktop_file; +    GKeyFile *key_file; +    char *full_path; + +    key_file = g_key_file_new (); +    if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs, +                                    &full_path, 0, error)) +    { +        g_key_file_free (key_file); +        return NULL; +    } + +    desktop_file = egg_desktop_file_new_from_key_file (key_file, +                   full_path, +                   error); +    g_free (full_path); +    return desktop_file; +} + +/** + * egg_desktop_file_new_from_key_file: + * @key_file: a #GKeyFile representing a desktop file + * @source: the path or URI that @key_file was loaded from, or %NULL + * @error: error pointer + * + * Creates a new #EggDesktopFile for @key_file. Assumes ownership of + * @key_file (on success or failure); you should consider @key_file to + * be freed after calling this function. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_key_file (GKeyFile    *key_file, +                                    const char  *source, +                                    GError     **error) +{ +    EggDesktopFile *desktop_file; +    char *version, *type; + +    if (!g_key_file_has_group (key_file, EGG_DESKTOP_FILE_GROUP)) +    { +        g_set_error (error, EGG_DESKTOP_FILE_ERROR, +                     EGG_DESKTOP_FILE_ERROR_INVALID, +                     _("File is not a valid .desktop file")); +        g_key_file_free (key_file); +        return NULL; +    } + +    version = g_key_file_get_value (key_file, EGG_DESKTOP_FILE_GROUP, +                                    EGG_DESKTOP_FILE_KEY_VERSION, +                                    NULL); +    if (version) +    { +        double version_num; +        char *end; + +        version_num = g_ascii_strtod (version, &end); +        if (*end) +        { +            g_warning ("Invalid Version string '%s' in %s", +                       version, source ? source : "(unknown)"); +        } +        else if (version_num > 1.0) +        { +            g_set_error (error, EGG_DESKTOP_FILE_ERROR, +                         EGG_DESKTOP_FILE_ERROR_INVALID, +                         _("Unrecognized desktop file Version '%s'"), version); +            g_free (version); +            g_key_file_free (key_file); +            return NULL; +        } +        g_free (version); +    } + +    desktop_file = g_new0 (EggDesktopFile, 1); +    desktop_file->key_file = key_file; + +    if (g_path_is_absolute (source)) +        desktop_file->source = g_filename_to_uri (source, NULL, NULL); +    else +        desktop_file->source = g_strdup (source); + +    desktop_file->name = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP, +                         EGG_DESKTOP_FILE_KEY_NAME, error); +    if (!desktop_file->name) +    { +        egg_desktop_file_free (desktop_file); +        return NULL; +    } + +    type = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP, +                                  EGG_DESKTOP_FILE_KEY_TYPE, error); +    if (!type) +    { +        egg_desktop_file_free (desktop_file); +        return NULL; +    } + +    if (!strcmp (type, "Application")) +    { +        char *exec, *p; + +        desktop_file->type = EGG_DESKTOP_FILE_TYPE_APPLICATION; + +        exec = g_key_file_get_string (key_file, +                                      EGG_DESKTOP_FILE_GROUP, +                                      EGG_DESKTOP_FILE_KEY_EXEC, +                                      error); +        if (!exec) +        { +            egg_desktop_file_free (desktop_file); +            g_free (type); +            return NULL; +        } + +        /* See if it takes paths or URIs or neither */ +        for (p = exec; *p; p++) +        { +            if (*p == '%') +            { +                if (p[1] == '\0' || strchr ("FfUu", p[1])) +                { +                    desktop_file->document_code = p[1]; +                    break; +                } +                p++; +            } +        } + +        g_free (exec); +    } +    else if (!strcmp (type, "Link")) +    { +        char *url; + +        desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK; + +        url = g_key_file_get_string (key_file, +                                     EGG_DESKTOP_FILE_GROUP, +                                     EGG_DESKTOP_FILE_KEY_URL, +                                     error); +        if (!url) +        { +            egg_desktop_file_free (desktop_file); +            g_free (type); +            return NULL; +        } +        g_free (url); +    } +    else if (!strcmp (type, "Directory")) +        desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY; +    else +        desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED; + +    g_free (type); + +    /* Check the Icon key */ +    desktop_file->icon = g_key_file_get_string (key_file, +                         EGG_DESKTOP_FILE_GROUP, +                         EGG_DESKTOP_FILE_KEY_ICON, +                         NULL); +    if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon)) +    { +        char *ext; + +        /* Lots of .desktop files still get this wrong */ +        ext = strrchr (desktop_file->icon, '.'); +        if (ext && (!strcmp (ext, ".png") || +                    !strcmp (ext, ".xpm") || +                    !strcmp (ext, ".svg"))) +        { +            g_warning ("Desktop file '%s' has malformed Icon key '%s'" +                       "(should not include extension)", +                       source ? source : "(unknown)", +                       desktop_file->icon); +            *ext = '\0'; +        } +    } + +    return desktop_file; +} + +/** + * egg_desktop_file_free: + * @desktop_file: an #EggDesktopFile + * + * Frees @desktop_file. + **/ +void +egg_desktop_file_free (EggDesktopFile *desktop_file) +{ +    g_key_file_free (desktop_file->key_file); +    g_free (desktop_file->source); +    g_free (desktop_file->name); +    g_free (desktop_file->icon); +    g_free (desktop_file); +} + +/** + * egg_desktop_file_get_source: + * @desktop_file: an #EggDesktopFile + * + * Gets the URI that @desktop_file was loaded from. + * + * Return value: @desktop_file's source URI + **/ +const char * +egg_desktop_file_get_source (EggDesktopFile *desktop_file) +{ +    return desktop_file->source; +} + +/** + * egg_desktop_file_get_desktop_file_type: + * @desktop_file: an #EggDesktopFile + * + * Gets the desktop file type of @desktop_file. + * + * Return value: @desktop_file's type + **/ +EggDesktopFileType +egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file) +{ +    return desktop_file->type; +} + +/** + * egg_desktop_file_get_name: + * @desktop_file: an #EggDesktopFile + * + * Gets the (localized) value of @desktop_file's "Name" key. + * + * Return value: the application/link name + **/ +const char * +egg_desktop_file_get_name (EggDesktopFile *desktop_file) +{ +    return desktop_file->name; +} + +/** + * egg_desktop_file_get_icon: + * @desktop_file: an #EggDesktopFile + * + * Gets the value of @desktop_file's "Icon" key. + * + * If the icon string is a full path (that is, if g_path_is_absolute() + * returns %TRUE when called on it), it points to a file containing an + * unthemed icon. If the icon string is not a full path, it is the + * name of a themed icon, which can be looked up with %GtkIconTheme, + * or passed directly to a theme-aware widget like %GtkImage or + * %GtkCellRendererPixbuf. + * + * Return value: the icon path or name + **/ +const char * +egg_desktop_file_get_icon (EggDesktopFile *desktop_file) +{ +    return desktop_file->icon; +} + +gboolean +egg_desktop_file_has_key (EggDesktopFile  *desktop_file, +                          const char      *key, +                          GError         **error) +{ +    return g_key_file_has_key (desktop_file->key_file, +                               EGG_DESKTOP_FILE_GROUP, key, +                               error); +} + +char * +egg_desktop_file_get_string (EggDesktopFile  *desktop_file, +                             const char      *key, +                             GError         **error) +{ +    return g_key_file_get_string (desktop_file->key_file, +                                  EGG_DESKTOP_FILE_GROUP, key, +                                  error); +} + +char * +egg_desktop_file_get_locale_string (EggDesktopFile  *desktop_file, +                                    const char      *key, +                                    const char      *locale, +                                    GError         **error) +{ +    return g_key_file_get_locale_string (desktop_file->key_file, +                                         EGG_DESKTOP_FILE_GROUP, key, locale, +                                         error); +} + +gboolean +egg_desktop_file_get_boolean (EggDesktopFile  *desktop_file, +                              const char      *key, +                              GError         **error) +{ +    return g_key_file_get_boolean (desktop_file->key_file, +                                   EGG_DESKTOP_FILE_GROUP, key, +                                   error); +} + +double +egg_desktop_file_get_numeric (EggDesktopFile  *desktop_file, +                              const char      *key, +                              GError         **error) +{ +    return g_key_file_get_double (desktop_file->key_file, +                                  EGG_DESKTOP_FILE_GROUP, key, +                                  error); +} + +char ** +egg_desktop_file_get_string_list (EggDesktopFile  *desktop_file, +                                  const char      *key, +                                  gsize           *length, +                                  GError         **error) +{ +    return g_key_file_get_string_list (desktop_file->key_file, +                                       EGG_DESKTOP_FILE_GROUP, key, length, +                                       error); +} + +char ** +egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file, +        const char      *key, +        const char      *locale, +        gsize           *length, +        GError         **error) +{ +    return g_key_file_get_locale_string_list (desktop_file->key_file, +            EGG_DESKTOP_FILE_GROUP, key, +            locale, length, +            error); +} + +/** + * egg_desktop_file_can_launch: + * @desktop_file: an #EggDesktopFile + * @desktop_environment: the name of the running desktop environment, + * or %NULL + * + * Tests if @desktop_file can/should be launched in the current + * environment. If @desktop_environment is non-%NULL, @desktop_file's + * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that + * this desktop_file is appropriate for the named environment. + * + * Furthermore, if @desktop_file has type + * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is + * also checked, to make sure the binary it points to exists. + * + * egg_desktop_file_can_launch() does NOT check the value of the + * "Hidden" key. + * + * Return value: %TRUE if @desktop_file can be launched + **/ +gboolean +egg_desktop_file_can_launch (EggDesktopFile *desktop_file, +                             const char     *desktop_environment) +{ +    char *try_exec, *found_program; +    char **only_show_in, **not_show_in; +    gboolean found; +    int i; + +    if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION && +            desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK) +        return FALSE; + +    if (desktop_environment) +    { +        only_show_in = g_key_file_get_string_list (desktop_file->key_file, +                       EGG_DESKTOP_FILE_GROUP, +                       EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN, +                       NULL, NULL); +        if (only_show_in) +        { +            for (i = 0, found = FALSE; only_show_in[i] && !found; i++) +            { +                if (!strcmp (only_show_in[i], desktop_environment)) +                    found = TRUE; +            } + +            g_strfreev (only_show_in); + +            if (!found) +                return FALSE; +        } + +        not_show_in = g_key_file_get_string_list (desktop_file->key_file, +                      EGG_DESKTOP_FILE_GROUP, +                      EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN, +                      NULL, NULL); +        if (not_show_in) +        { +            for (i = 0, found = FALSE; not_show_in[i] && !found; i++) +            { +                if (!strcmp (not_show_in[i], desktop_environment)) +                    found = TRUE; +            } + +            g_strfreev (not_show_in); + +            if (found) +                return FALSE; +        } +    } + +    if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION) +    { +        try_exec = g_key_file_get_string (desktop_file->key_file, +                                          EGG_DESKTOP_FILE_GROUP, +                                          EGG_DESKTOP_FILE_KEY_TRY_EXEC, +                                          NULL); +        if (try_exec) +        { +            found_program = g_find_program_in_path (try_exec); +            g_free (try_exec); + +            if (!found_program) +                return FALSE; +            g_free (found_program); +        } +    } + +    return TRUE; +} + +/** + * egg_desktop_file_accepts_documents: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file represents an application that can accept + * documents on the command line. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file) +{ +    return desktop_file->document_code != 0; +} + +/** + * egg_desktop_file_accepts_multiple: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file can accept multiple documents at once. + * + * If this returns %FALSE, you can still pass multiple documents to + * egg_desktop_file_launch(), but that will result in multiple copies + * of the application being launched. See egg_desktop_file_launch() + * for more details. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file) +{ +    return (desktop_file->document_code == 'F' || +            desktop_file->document_code == 'U'); +} + +/** + * egg_desktop_file_accepts_uris: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file can accept (non-"file:") URIs as documents to + * open. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file) +{ +    return (desktop_file->document_code == 'U' || +            desktop_file->document_code == 'u'); +} + +static void +append_quoted_word (GString    *str, +                    const char *s, +                    gboolean    in_single_quotes, +                    gboolean    in_double_quotes) +{ +    const char *p; + +    if (!in_single_quotes && !in_double_quotes) +        g_string_append_c (str, '\''); +    else if (!in_single_quotes && in_double_quotes) +        g_string_append (str, "\"'"); + +    if (!strchr (s, '\'')) +        g_string_append (str, s); +    else +    { +        for (p = s; *p != '\0'; p++) +        { +            if (*p == '\'') +                g_string_append (str, "'\\''"); +            else +                g_string_append_c (str, *p); +        } +    } + +    if (!in_single_quotes && !in_double_quotes) +        g_string_append_c (str, '\''); +    else if (!in_single_quotes && in_double_quotes) +        g_string_append (str, "'\""); +} + +static void +do_percent_subst (EggDesktopFile *desktop_file, +                  char            code, +                  GString        *str, +                  GSList        **documents, +                  gboolean        in_single_quotes, +                  gboolean        in_double_quotes) +{ +    GSList *d; +    char *doc; + +    switch (code) +    { +    case '%': +        g_string_append_c (str, '%'); +        break; + +    case 'F': +    case 'U': +        for (d = *documents; d; d = d->next) +        { +            doc = d->data; +            g_string_append (str, " "); +            append_quoted_word (str, doc, in_single_quotes, in_double_quotes); +        } +        *documents = NULL; +        break; + +    case 'f': +    case 'u': +        if (*documents) +        { +            doc = (*documents)->data; +            g_string_append (str, " "); +            append_quoted_word (str, doc, in_single_quotes, in_double_quotes); +            *documents = (*documents)->next; +        } +        break; + +    case 'i': +        if (desktop_file->icon) +        { +            g_string_append (str, "--icon "); +            append_quoted_word (str, desktop_file->icon, +                                in_single_quotes, in_double_quotes); +        } +        break; + +    case 'c': +        if (desktop_file->name) +        { +            append_quoted_word (str, desktop_file->name, +                                in_single_quotes, in_double_quotes); +        } +        break; + +    case 'k': +        if (desktop_file->source) +        { +            append_quoted_word (str, desktop_file->source, +                                in_single_quotes, in_double_quotes); +        } +        break; + +    case 'D': +    case 'N': +    case 'd': +    case 'n': +    case 'v': +    case 'm': +        /* Deprecated; skip */ +        break; + +    default: +        g_warning ("Unrecognized %%-code '%%%c' in Exec", code); +        break; +    } +} + +static char * +parse_exec (EggDesktopFile  *desktop_file, +            GSList         **documents, +            GError         **error) +{ +    char *exec, *p, *command; +    gboolean escape, single_quot, double_quot; +    GString *gs; + +    exec = g_key_file_get_string (desktop_file->key_file, +                                  EGG_DESKTOP_FILE_GROUP, +                                  EGG_DESKTOP_FILE_KEY_EXEC, +                                  error); +    if (!exec) +        return NULL; + +    /* Build the command */ +    gs = g_string_new (NULL); +    escape = single_quot = double_quot = FALSE; + +    for (p = exec; *p != '\0'; p++) +    { +        if (escape) +        { +            escape = FALSE; +            g_string_append_c (gs, *p); +        } +        else if (*p == '\\') +        { +            if (!single_quot) +                escape = TRUE; +            g_string_append_c (gs, *p); +        } +        else if (*p == '\'') +        { +            g_string_append_c (gs, *p); +            if (!single_quot && !double_quot) +                single_quot = TRUE; +            else if (single_quot) +                single_quot = FALSE; +        } +        else if (*p == '"') +        { +            g_string_append_c (gs, *p); +            if (!single_quot && !double_quot) +                double_quot = TRUE; +            else if (double_quot) +                double_quot = FALSE; +        } +        else if (*p == '%' && p[1]) +        { +            do_percent_subst (desktop_file, p[1], gs, documents, +                              single_quot, double_quot); +            p++; +        } +        else +            g_string_append_c (gs, *p); +    } + +    g_free (exec); +    command = g_string_free (gs, FALSE); + +    /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */ +    if (g_key_file_has_key (desktop_file->key_file, +                            EGG_DESKTOP_FILE_GROUP, +                            EGG_DESKTOP_FILE_KEY_TERMINAL, +                            NULL)) +    { +        GError *terminal_error = NULL; +        gboolean use_terminal = +            g_key_file_get_boolean (desktop_file->key_file, +                                    EGG_DESKTOP_FILE_GROUP, +                                    EGG_DESKTOP_FILE_KEY_TERMINAL, +                                    &terminal_error); +        if (terminal_error) +        { +            g_free (command); +            g_propagate_error (error, terminal_error); +            return NULL; +        } + +        if (use_terminal) +        { +            gs = g_string_new ("xdg-terminal "); +            append_quoted_word (gs, command, FALSE, FALSE); +            g_free (command); +            command = g_string_free (gs, FALSE); +        } +    } + +    return command; +} + +static GSList * +translate_document_list (EggDesktopFile *desktop_file, GSList *documents) +{ +    gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file); +    GSList *ret, *d; + +    for (d = documents, ret = NULL; d; d = d->next) +    { +        const char *document = d->data; +        gboolean is_uri = !g_path_is_absolute (document); +        char *translated; + +        if (accepts_uris) +        { +            if (is_uri) +                translated = g_strdup (document); +            else +                translated = g_filename_to_uri (document, NULL, NULL); +        } +        else +        { +            if (is_uri) +                translated = g_filename_from_uri (document, NULL, NULL); +            else +                translated = g_strdup (document); +        } + +        if (translated) +            ret = g_slist_prepend (ret, translated); +    } + +    return g_slist_reverse (ret); +} + +static void +free_document_list (GSList *documents) +{ +    GSList *d; + +    for (d = documents; d; d = d->next) +        g_free (d->data); +    g_slist_free (documents); +} + +/** + * egg_desktop_file_parse_exec: + * @desktop_file: a #EggDesktopFile + * @documents: a list of document paths or URIs + * @error: error pointer + * + * Parses @desktop_file's Exec key, inserting @documents into it, and + * returns the result. + * + * If @documents contains non-file: URIs and @desktop_file does not + * accept URIs, those URIs will be ignored. Likewise, if @documents + * contains more elements than @desktop_file accepts, the extra + * documents will be ignored. + * + * Return value: the parsed Exec string + **/ +char * +egg_desktop_file_parse_exec (EggDesktopFile  *desktop_file, +                             GSList          *documents, +                             GError         **error) +{ +    GSList *translated, *docs; +    char *command; + +    docs = translated = translate_document_list (desktop_file, documents); +    command = parse_exec (desktop_file, &docs, error); +    free_document_list (translated); + +    return command; +} + +static gboolean +parse_link (EggDesktopFile  *desktop_file, +            EggDesktopFile **app_desktop_file, +            GSList         **documents, +            GError         **error) +{ +    char *url; +    GKeyFile *key_file; + +    url = g_key_file_get_string (desktop_file->key_file, +                                 EGG_DESKTOP_FILE_GROUP, +                                 EGG_DESKTOP_FILE_KEY_URL, +                                 error); +    if (!url) +        return FALSE; +    *documents = g_slist_prepend (NULL, url); + +    /* FIXME: use gvfs */ +    key_file = g_key_file_new (); +    g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, +                           EGG_DESKTOP_FILE_KEY_NAME, +                           "xdg-open"); +    g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, +                           EGG_DESKTOP_FILE_KEY_TYPE, +                           "Application"); +    g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, +                           EGG_DESKTOP_FILE_KEY_EXEC, +                           "xdg-open %u"); +    *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL); +    return TRUE; +} + +#if GTK_CHECK_VERSION (2, 12, 0) +static char * +start_startup_notification (GdkDisplay     *display, +                            EggDesktopFile *desktop_file, +                            const char     *argv0, +                            int             screen, +                            int             workspace, +                            guint32         launch_time) +{ +    static int sequence = 0; +    char *startup_id; +    char *description, *wmclass; +    char *screen_str, *workspace_str; + +    if (g_key_file_has_key (desktop_file->key_file, +                            EGG_DESKTOP_FILE_GROUP, +                            EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY, +                            NULL)) +    { +        if (!g_key_file_get_boolean (desktop_file->key_file, +                                     EGG_DESKTOP_FILE_GROUP, +                                     EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY, +                                     NULL)) +            return NULL; +        wmclass = NULL; +    } +    else +    { +        wmclass = g_key_file_get_string (desktop_file->key_file, +                                         EGG_DESKTOP_FILE_GROUP, +                                         EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS, +                                         NULL); +        if (!wmclass) +            return NULL; +    } + +    if (launch_time == (guint32)-1) +        launch_time = gdk_x11_display_get_user_time (display); +    startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu", +                                  g_get_prgname (), +                                  (unsigned long)getpid (), +                                  g_get_host_name (), +                                  argv0, +                                  sequence++, +                                  (unsigned long)launch_time); + +    description = g_strdup_printf (_("Starting %s"), desktop_file->name); +    screen_str = g_strdup_printf ("%d", screen); +    workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace); + +    gdk_x11_display_broadcast_startup_message (display, "new", +            "ID", startup_id, +            "NAME", desktop_file->name, +            "SCREEN", screen_str, +            "BIN", argv0, +            "ICON", desktop_file->icon, +            "DESKTOP", workspace_str, +            "DESCRIPTION", description, +            "WMCLASS", wmclass, +            NULL); + +    g_free (description); +    g_free (wmclass); +    g_free (screen_str); +    g_free (workspace_str); + +    return startup_id; +} + +static void +end_startup_notification (GdkDisplay *display, +                          const char *startup_id) +{ +    gdk_x11_display_broadcast_startup_message (display, "remove", +            "ID", startup_id, +            NULL); +} + +#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */ * 1000) + +typedef struct +{ +    GdkDisplay *display; +    char *startup_id; +} StartupNotificationData; + +static gboolean +startup_notification_timeout (gpointer data) +{ +    StartupNotificationData *sn_data = data; + +    end_startup_notification (sn_data->display, sn_data->startup_id); +    g_object_unref (sn_data->display); +    g_free (sn_data->startup_id); +    g_free (sn_data); + +    return FALSE; +} + +static void +set_startup_notification_timeout (GdkDisplay *display, +                                  const char *startup_id) +{ +    StartupNotificationData *sn_data; + +    sn_data = g_new (StartupNotificationData, 1); +    sn_data->display = g_object_ref (display); +    sn_data->startup_id = g_strdup (startup_id); + +    g_timeout_add (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH, +                   startup_notification_timeout, sn_data); +} +#endif /* GTK 2.12 */ + +static GPtrArray * +array_putenv (GPtrArray *env, char *variable) +{ +    int i, keylen; + +    if (!env) +    { +        char **envp; + +        env = g_ptr_array_new (); + +        envp = g_listenv (); +        for (i = 0; envp[i]; i++) +        { +            const char *value; + +            value = g_getenv (envp[i]); +            g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i], +                                                   value ? value : "")); +        } +        g_strfreev (envp); +    } + +    keylen = strcspn (variable, "="); + +    /* Remove old value of key */ +    for (i = 0; i < env->len; i++) +    { +        char *envvar = env->pdata[i]; + +        if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=') +        { +            g_free (envvar); +            g_ptr_array_remove_index_fast (env, i); +            break; +        } +    } + +    /* Add new value */ +    g_ptr_array_add (env, g_strdup (variable)); + +    return env; +} + +static gboolean +egg_desktop_file_launchv (EggDesktopFile *desktop_file, +                          GSList *documents, va_list args, +                          GError **error) +{ +    EggDesktopFileLaunchOption option; +    GSList *translated_documents = NULL, *docs; +    char *command, **argv; +    int argc, i, screen_num; +    gboolean success, current_success; +    GdkDisplay *display; +    char *startup_id; + +    GPtrArray   *env = NULL; +    char       **variables = NULL; +    GdkScreen   *screen = NULL; +    int          workspace = -1; +    const char  *directory = NULL; +    guint32      launch_time = (guint32)-1; +    GSpawnFlags  flags = G_SPAWN_SEARCH_PATH; +    GSpawnChildSetupFunc setup_func = NULL; +    gpointer     setup_data = NULL; + +    GPid        *ret_pid = NULL; +    int         *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL; +    char       **ret_startup_id = NULL; + +    if (documents && desktop_file->document_code == 0) +    { +        g_set_error (error, EGG_DESKTOP_FILE_ERROR, +                     EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, +                     _("Application does not accept documents on command line")); +        return FALSE; +    } + +    /* Read the options: technically it's incorrect for the caller to +     * NULL-terminate the list of options (rather than 0-terminating +     * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED, +     * it's more consistent with other glib/gtk methods, and it will +     * work as long as sizeof (int) <= sizeof (NULL), and NULL is +     * represented as 0. (Which is true everywhere we care about.) +     */ +    while ((option = va_arg (args, EggDesktopFileLaunchOption))) +    { +        switch (option) +        { +        case EGG_DESKTOP_FILE_LAUNCH_CLEARENV: +            if (env) +                g_ptr_array_free (env, TRUE); +            env = g_ptr_array_new (); +            break; +        case EGG_DESKTOP_FILE_LAUNCH_PUTENV: +            variables = va_arg (args, char **); +            for (i = 0; variables[i]; i++) +                env = array_putenv (env, variables[i]); +            break; + +        case EGG_DESKTOP_FILE_LAUNCH_SCREEN: +            screen = va_arg (args, GdkScreen *); +            break; +        case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: +            workspace = va_arg (args, int); +            break; + +        case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: +            directory = va_arg (args, const char *); +            break; +        case EGG_DESKTOP_FILE_LAUNCH_TIME: +            launch_time = va_arg (args, guint32); +            break; +        case EGG_DESKTOP_FILE_LAUNCH_FLAGS: +            flags |= va_arg (args, GSpawnFlags); +            /* Make sure they didn't set any flags that don't make sense. */ +            flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO; +            break; +        case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC: +            setup_func = va_arg (args, GSpawnChildSetupFunc); +            setup_data = va_arg (args, gpointer); +            break; + +        case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID: +            ret_pid = va_arg (args, GPid *); +            break; +        case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE: +            ret_stdin = va_arg (args, int *); +            break; +        case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE: +            ret_stdout = va_arg (args, int *); +            break; +        case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE: +            ret_stderr = va_arg (args, int *); +            break; +        case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID: +            ret_startup_id = va_arg (args, char **); +            break; + +        default: +            g_set_error (error, EGG_DESKTOP_FILE_ERROR, +                         EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION, +                         _("Unrecognized launch option: %d"), +                         GPOINTER_TO_INT (option)); +            success = FALSE; +            goto out; +        } +    } + +    if (screen) +    { +        char *display_name = gdk_screen_make_display_name (screen); +        char *display_env = g_strdup_printf ("DISPLAY=%s", display_name); +        env = array_putenv (env, display_env); +        g_free (display_name); +        g_free (display_env); + +        display = gdk_screen_get_display (screen); +    } +    else +    { +        display = gdk_display_get_default (); +        screen = gdk_display_get_default_screen (display); +    } +    screen_num = gdk_screen_get_number (screen); + +    translated_documents = translate_document_list (desktop_file, documents); +    docs = translated_documents; + +    success = FALSE; + +    do +    { +        command = parse_exec (desktop_file, &docs, error); +        if (!command) +            goto out; + +        if (!g_shell_parse_argv (command, &argc, &argv, error)) +        { +            g_free (command); +            goto out; +        } +        g_free (command); + +#if GTK_CHECK_VERSION (2, 12, 0) +        startup_id = start_startup_notification (display, desktop_file, +                     argv[0], screen_num, +                     workspace, launch_time); +        if (startup_id) +        { +            char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s", +                                                    startup_id); +            env = array_putenv (env, startup_id_env); +            g_free (startup_id_env); +        } +#else +        startup_id = NULL; +#endif /* GTK 2.12 */ + +        if (env != NULL) +            g_ptr_array_add (env, NULL); + +        current_success = +            g_spawn_async_with_pipes (directory, +                                      argv, +                                      env ? (char **)(env->pdata) : NULL, +                                      flags, +                                      setup_func, setup_data, +                                      ret_pid, +                                      ret_stdin, ret_stdout, ret_stderr, +                                      error); +        g_strfreev (argv); + +        if (startup_id) +        { +#if GTK_CHECK_VERSION (2, 12, 0) +            if (current_success) +            { +                set_startup_notification_timeout (display, startup_id); + +                if (ret_startup_id) +                    *ret_startup_id = startup_id; +                else +                    g_free (startup_id); +            } +            else +#endif /* GTK 2.12 */ +                g_free (startup_id); +        } +        else if (ret_startup_id) +            *ret_startup_id = NULL; + +        if (current_success) +        { +            /* If we successfully launch any instances of the app, make +             * sure we return TRUE and don't set @error. +             */ +            success = TRUE; +            error = NULL; + +            /* Also, only set the output params on the first one */ +            ret_pid = NULL; +            ret_stdin = ret_stdout = ret_stderr = NULL; +            ret_startup_id = NULL; +        } +    } +    while (docs && current_success); + +out: +    if (env) +    { +        g_strfreev ((char **)env->pdata); +        g_ptr_array_free (env, FALSE); +    } +    free_document_list (translated_documents); + +    return success; +} + +/** + * egg_desktop_file_launch: + * @desktop_file: an #EggDesktopFile + * @documents: a list of URIs or paths to documents to open + * @error: error pointer + * @...: additional options + * + * Launches @desktop_file with the given arguments. Additional options + * can be specified as follows: + * + *   %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments) + *       clears the environment in the child process + *   %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables) + *       adds the NAME=VALUE strings in the given %NULL-terminated + *       array to the child process's environment + *   %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen) + *       causes the application to be launched on the given screen + *   %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace) + *       causes the application to be launched on the given workspace + *   %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir) + *       causes the application to be launched in the given directory + *   %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time) + *       sets the "launch time" for the application. If the user + *       interacts with another window after @launch_time but before + *       the launched application creates its first window, the window + *       manager may choose to not give focus to the new application. + *       Passing 0 for @launch_time will explicitly request that the + *       application not receive focus. + *   %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags) + *       Sets additional #GSpawnFlags to use. See g_spawn_async() for + *       more details. + *   %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer) + *       Sets the child setup callback and the data to pass to it. + *       (See g_spawn_async() for more details.) + * + *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid) + *       On a successful launch, sets *@pid to the PID of the launched + *       application. + *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id) + *       On a successful launch, sets *@startup_id to the Startup + *       Notification "startup id" of the launched application. + *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd) + *       On a successful launch, sets *@fd to the file descriptor of + *       a pipe connected to the application's stdin. + *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd) + *       On a successful launch, sets *@fd to the file descriptor of + *       a pipe connected to the application's stdout. + *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd) + *       On a successful launch, sets *@fd to the file descriptor of + *       a pipe connected to the application's stderr. + * + * The options should be terminated with a single %NULL. + * + * If @documents contains multiple documents, but + * egg_desktop_file_accepts_multiple() returns %FALSE for + * @desktop_file, then egg_desktop_file_launch() will actually launch + * multiple instances of the application. In that case, the return + * value (as well as any values passed via + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the + * first instance of the application that was launched (but the + * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each + * instance). + * + * Return value: %TRUE if the application was successfully launched. + **/ +gboolean +egg_desktop_file_launch (EggDesktopFile *desktop_file, +                         GSList *documents, GError **error, +                         ...) +{ +    va_list args; +    gboolean success; +    EggDesktopFile *app_desktop_file; + +    switch (desktop_file->type) +    { +    case EGG_DESKTOP_FILE_TYPE_APPLICATION: +        va_start (args, error); +        success = egg_desktop_file_launchv (desktop_file, documents, +                                            args, error); +        va_end (args); +        break; + +    case EGG_DESKTOP_FILE_TYPE_LINK: +        if (documents) +        { +            g_set_error (error, EGG_DESKTOP_FILE_ERROR, +                         EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, +                         _("Can't pass document URIs to a 'Type=Link' desktop entry")); +            return FALSE; +        } + +        if (!parse_link (desktop_file, &app_desktop_file, &documents, error)) +            return FALSE; + +        va_start (args, error); +        success = egg_desktop_file_launchv (app_desktop_file, documents, +                                            args, error); +        va_end (args); + +        egg_desktop_file_free (app_desktop_file); +        free_document_list (documents); +        break; + +    default: +        g_set_error (error, EGG_DESKTOP_FILE_ERROR, +                     EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, +                     _("Not a launchable item")); +        success = FALSE; +        break; +    } + +    return success; +} + + +GQuark +egg_desktop_file_error_quark (void) +{ +    return g_quark_from_static_string ("egg-desktop_file-error-quark"); +} + + +G_LOCK_DEFINE_STATIC (egg_desktop_file); +static EggDesktopFile *egg_desktop_file; + +/** + * egg_set_desktop_file: + * @desktop_file_path: path to the application's desktop file + * + * Creates an #EggDesktopFile for the application from the data at + * @desktop_file_path. This will also call g_set_application_name() + * with the localized application name from the desktop file, and + * gtk_window_set_default_icon_name() or + * gtk_window_set_default_icon_from_file() with the application's + * icon. Other code may use additional information from the desktop + * file. + * + * Note that for thread safety reasons, this function can only + * be called once. + **/ +void +egg_set_desktop_file (const char *desktop_file_path) +{ +    GError *error = NULL; + +    G_LOCK (egg_desktop_file); +    if (egg_desktop_file) +        egg_desktop_file_free (egg_desktop_file); + +    egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error); +    if (error) +    { +        g_warning ("Could not load desktop file '%s': %s", +                   desktop_file_path, error->message); +        g_error_free (error); +    } + +    /* Set localized application name and default window icon */ +    if (egg_desktop_file->name) +        g_set_application_name (egg_desktop_file->name); +    if (egg_desktop_file->icon) +    { +        if (g_path_is_absolute (egg_desktop_file->icon)) +            gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL); +        else +            gtk_window_set_default_icon_name (egg_desktop_file->icon); +    } + +    G_UNLOCK (egg_desktop_file); +} + +/** + * egg_get_desktop_file: + * + * Gets the application's #EggDesktopFile, as set by + * egg_set_desktop_file(). + * + * Return value: the #EggDesktopFile, or %NULL if it hasn't been set. + **/ +EggDesktopFile * +egg_get_desktop_file (void) +{ +    EggDesktopFile *retval; + +    G_LOCK (egg_desktop_file); +    retval = egg_desktop_file; +    G_UNLOCK (egg_desktop_file); + +    return retval; +} diff --git a/cut-n-paste-code/libegg/eggdesktopfile.h b/cut-n-paste-code/libegg/eggdesktopfile.h new file mode 100644 index 00000000..6a6d580a --- /dev/null +++ b/cut-n-paste-code/libegg/eggdesktopfile.h @@ -0,0 +1,166 @@ +/* eggdesktopfile.h - Freedesktop.Org Desktop Files + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 Lesser General Public + * License along with this library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - + * Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_DESKTOP_FILE_H__ +#define __EGG_DESKTOP_FILE_H__ + +#include <glib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +    typedef struct EggDesktopFile EggDesktopFile; + +    typedef enum +    { +        EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED, + +        EGG_DESKTOP_FILE_TYPE_APPLICATION, +        EGG_DESKTOP_FILE_TYPE_LINK, +        EGG_DESKTOP_FILE_TYPE_DIRECTORY +    } EggDesktopFileType; + +    EggDesktopFile     *egg_desktop_file_new                (const char   *desktop_file_path, +            GError      **error); + +    EggDesktopFile     *egg_desktop_file_new_from_data_dirs (const char   *desktop_file_path, +            GError      **error); +    EggDesktopFile     *egg_desktop_file_new_from_dirs      (const char   *desktop_file_path, +            const char  **search_dirs, +            GError      **error); +    EggDesktopFile     *egg_desktop_file_new_from_key_file  (GKeyFile     *key_file, +            const char   *source, +            GError      **error); + +    void                egg_desktop_file_free               (EggDesktopFile  *desktop_file); + +    const char         *egg_desktop_file_get_source         (EggDesktopFile  *desktop_file); + +    EggDesktopFileType  egg_desktop_file_get_desktop_file_type (EggDesktopFile  *desktop_file); + +    const char         *egg_desktop_file_get_name           (EggDesktopFile  *desktop_file); +    const char         *egg_desktop_file_get_icon           (EggDesktopFile  *desktop_file); + +    gboolean            egg_desktop_file_can_launch         (EggDesktopFile  *desktop_file, +            const char      *desktop_environment); + +    gboolean            egg_desktop_file_accepts_documents  (EggDesktopFile  *desktop_file); +    gboolean            egg_desktop_file_accepts_multiple   (EggDesktopFile  *desktop_file); +    gboolean            egg_desktop_file_accepts_uris       (EggDesktopFile  *desktop_file); + +    char               *egg_desktop_file_parse_exec         (EggDesktopFile  *desktop_file, +            GSList          *documents, +            GError         **error); + +    gboolean            egg_desktop_file_launch             (EggDesktopFile  *desktop_file, +            GSList          *documents, +            GError         **error, +            ...) G_GNUC_NULL_TERMINATED; + +    typedef enum +    { +        EGG_DESKTOP_FILE_LAUNCH_CLEARENV = 1, +        EGG_DESKTOP_FILE_LAUNCH_PUTENV, +        EGG_DESKTOP_FILE_LAUNCH_SCREEN, +        EGG_DESKTOP_FILE_LAUNCH_WORKSPACE, +        EGG_DESKTOP_FILE_LAUNCH_DIRECTORY, +        EGG_DESKTOP_FILE_LAUNCH_TIME, +        EGG_DESKTOP_FILE_LAUNCH_FLAGS, +        EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC, +        EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, +        EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE, +        EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE, +        EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE, +        EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID +    } EggDesktopFileLaunchOption; + +    /* Standard Keys */ +#define EGG_DESKTOP_FILE_GROUP			"Desktop Entry" + +#define EGG_DESKTOP_FILE_KEY_TYPE		"Type" +#define EGG_DESKTOP_FILE_KEY_VERSION		"Version" +#define EGG_DESKTOP_FILE_KEY_NAME		"Name" +#define EGG_DESKTOP_FILE_KEY_GENERIC_NAME	"GenericName" +#define EGG_DESKTOP_FILE_KEY_NO_DISPLAY		"NoDisplay" +#define EGG_DESKTOP_FILE_KEY_COMMENT		"Comment" +#define EGG_DESKTOP_FILE_KEY_ICON		"Icon" +#define EGG_DESKTOP_FILE_KEY_HIDDEN		"Hidden" +#define EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN	"OnlyShowIn" +#define EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN	"NotShowIn" +#define EGG_DESKTOP_FILE_KEY_TRY_EXEC		"TryExec" +#define EGG_DESKTOP_FILE_KEY_EXEC		"Exec" +#define EGG_DESKTOP_FILE_KEY_PATH		"Path" +#define EGG_DESKTOP_FILE_KEY_TERMINAL		"Terminal" +#define EGG_DESKTOP_FILE_KEY_MIME_TYPE		"MimeType" +#define EGG_DESKTOP_FILE_KEY_CATEGORIES		"Categories" +#define EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY	"StartupNotify" +#define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS	"StartupWMClass" +#define EGG_DESKTOP_FILE_KEY_URL		"URL" + +    /* Accessors */ +    gboolean  egg_desktop_file_has_key                (EggDesktopFile  *desktop_file, +            const char      *key, +            GError         **error); +    char     *egg_desktop_file_get_string             (EggDesktopFile  *desktop_file, +            const char      *key, +            GError         **error) G_GNUC_MALLOC; +    char     *egg_desktop_file_get_locale_string      (EggDesktopFile  *desktop_file, +            const char      *key, +            const char      *locale, +            GError         **error) G_GNUC_MALLOC; +    gboolean  egg_desktop_file_get_boolean            (EggDesktopFile  *desktop_file, +            const char      *key, +            GError         **error); +    double    egg_desktop_file_get_numeric            (EggDesktopFile  *desktop_file, +            const char      *key, +            GError         **error); +    char    **egg_desktop_file_get_string_list        (EggDesktopFile  *desktop_file, +            const char      *key, +            gsize           *length, +            GError         **error) G_GNUC_MALLOC; +    char    **egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file, +            const char      *key, +            const char      *locale, +            gsize           *length, +            GError         **error) G_GNUC_MALLOC; + + +    /* Errors */ +#define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark() + +    GQuark egg_desktop_file_error_quark (void); + +    typedef enum +    { +        EGG_DESKTOP_FILE_ERROR_INVALID, +        EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, +        EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION +    } EggDesktopFileError; + +    /* Global application desktop file */ +    void            egg_set_desktop_file (const char *desktop_file_path); +    EggDesktopFile *egg_get_desktop_file (void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __EGG_DESKTOP_FILE_H__ */ diff --git a/cut-n-paste-code/libegg/eggsmclient-private.h b/cut-n-paste-code/libegg/eggsmclient-private.h new file mode 100644 index 00000000..68f1c9ea --- /dev/null +++ b/cut-n-paste-code/libegg/eggsmclient-private.h @@ -0,0 +1,57 @@ +/* eggsmclient-private.h + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_SM_CLIENT_PRIVATE_H__ +#define __EGG_SM_CLIENT_PRIVATE_H__ + +#include <gdkconfig.h> +#include "eggsmclient.h" + +#ifdef __cplusplus +extern "C" { +#endif + +    GKeyFile *egg_sm_client_save_state     (EggSMClient *client); +    void      egg_sm_client_quit_requested (EggSMClient *client); +    void      egg_sm_client_quit_cancelled (EggSMClient *client); +    void      egg_sm_client_quit           (EggSMClient *client); + +#if defined (GDK_WINDOWING_X11) +# ifdef EGG_SM_CLIENT_BACKEND_XSMP +    GType        egg_sm_client_xsmp_get_type (void); +    EggSMClient *egg_sm_client_xsmp_new      (void); +# endif +# ifdef EGG_SM_CLIENT_BACKEND_DBUS +    GType        egg_sm_client_dbus_get_type (void); +    EggSMClient *egg_sm_client_dbus_new      (void); +# endif +#elif defined (GDK_WINDOWING_WIN32) +    GType        egg_sm_client_win32_get_type (void); +    EggSMClient *egg_sm_client_win32_new      (void); +#elif defined (GDK_WINDOWING_QUARTZ) +    GType        egg_sm_client_osx_get_type (void); +    EggSMClient *egg_sm_client_osx_new      (void); +#endif + +#ifdef __cplusplus +} +#endif + + +#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */ diff --git a/cut-n-paste-code/libegg/eggsmclient-xsmp.c b/cut-n-paste-code/libegg/eggsmclient-xsmp.c new file mode 100644 index 00000000..40d8c270 --- /dev/null +++ b/cut-n-paste-code/libegg/eggsmclient-xsmp.c @@ -0,0 +1,1371 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * + * Inspired by various other pieces of code including GsmClient (C) + * 2001 Havoc Pennington, MateClient (C) 1998 Carsten Schaar, and twm + * session code (C) 1998 The Open Group. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "eggsmclient.h" +#include "eggsmclient-private.h" + +#include "eggdesktopfile.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <X11/SM/SMlib.h> + +#include <gdk/gdk.h> + +#define EGG_TYPE_SM_CLIENT_XSMP            (egg_sm_client_xsmp_get_type ()) +#define EGG_SM_CLIENT_XSMP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP)) +#define EGG_SM_CLIENT_XSMP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass)) +#define EGG_IS_SM_CLIENT_XSMP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_XSMP)) +#define EGG_IS_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_XSMP)) +#define EGG_SM_CLIENT_XSMP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass)) + +typedef struct _EggSMClientXSMP        EggSMClientXSMP; +typedef struct _EggSMClientXSMPClass   EggSMClientXSMPClass; + +/* These mostly correspond to the similarly-named states in section + * 9.1 of the XSMP spec. Some of the states there aren't represented + * here, because we don't need them. SHUTDOWN_CANCELLED is slightly + * different from the spec; we use it when the client is IDLE after a + * ShutdownCancelled message, but the application is still interacting + * and doesn't know the shutdown has been cancelled yet. + */ +typedef enum +{ +    XSMP_STATE_IDLE, +    XSMP_STATE_SAVE_YOURSELF, +    XSMP_STATE_INTERACT_REQUEST, +    XSMP_STATE_INTERACT, +    XSMP_STATE_SAVE_YOURSELF_DONE, +    XSMP_STATE_SHUTDOWN_CANCELLED, +    XSMP_STATE_CONNECTION_CLOSED +} EggSMClientXSMPState; + +static const char *state_names[] = +{ +    "idle", +    "save-yourself", +    "interact-request", +    "interact", +    "save-yourself-done", +    "shutdown-cancelled", +    "connection-closed" +}; + +#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state]) + +struct _EggSMClientXSMP +{ +    EggSMClient parent; + +    SmcConn connection; +    char *client_id; + +    EggSMClientXSMPState state; +    char **restart_command; +    gboolean set_restart_command; +    int restart_style; + +    guint idle; + +    /* Current SaveYourself state */ +    guint expecting_initial_save_yourself : 1; +    guint need_save_state : 1; +    guint need_quit_requested : 1; +    guint interact_errors : 1; +    guint shutting_down : 1; + +    /* Todo list */ +    guint waiting_to_set_initial_properties : 1; +    guint waiting_to_emit_quit : 1; +    guint waiting_to_emit_quit_cancelled : 1; +    guint waiting_to_save_myself : 1; + +}; + +struct _EggSMClientXSMPClass +{ +    EggSMClientClass parent_class; + +}; + +static void     sm_client_xsmp_startup (EggSMClient *client, +                                        const char  *client_id); +static void     sm_client_xsmp_set_restart_command (EggSMClient  *client, +        int           argc, +        const char  **argv); +static void     sm_client_xsmp_will_quit (EggSMClient *client, +        gboolean     will_quit); +static gboolean sm_client_xsmp_end_session (EggSMClient         *client, +        EggSMClientEndStyle  style, +        gboolean  request_confirmation); + +static void xsmp_save_yourself      (SmcConn   smc_conn, +                                     SmPointer client_data, +                                     int       save_style, +                                     Bool      shutdown, +                                     int       interact_style, +                                     Bool      fast); +static void xsmp_die                (SmcConn   smc_conn, +                                     SmPointer client_data); +static void xsmp_save_complete      (SmcConn   smc_conn, +                                     SmPointer client_data); +static void xsmp_shutdown_cancelled (SmcConn   smc_conn, +                                     SmPointer client_data); +static void xsmp_interact           (SmcConn   smc_conn, +                                     SmPointer client_data); + +static SmProp *array_prop        (const char    *name, +                                  ...); +static SmProp *ptrarray_prop     (const char    *name, +                                  GPtrArray     *values); +static SmProp *string_prop       (const char    *name, +                                  const char    *value); +static SmProp *card8_prop        (const char    *name, +                                  unsigned char  value); + +static void set_properties         (EggSMClientXSMP *xsmp, ...); +static void delete_properties      (EggSMClientXSMP *xsmp, ...); + +static GPtrArray *generate_command (char       **restart_command, +                                    const char  *client_id, +                                    const char  *state_file); + +static void save_state            (EggSMClientXSMP *xsmp); +static void do_save_yourself      (EggSMClientXSMP *xsmp); +static void update_pending_events (EggSMClientXSMP *xsmp); + +static void     ice_init             (void); +static gboolean process_ice_messages (IceConn       ice_conn); +static void     smc_error_handler    (SmcConn       smc_conn, +                                      Bool          swap, +                                      int           offending_minor_opcode, +                                      unsigned long offending_sequence, +                                      int           error_class, +                                      int           severity, +                                      SmPointer     values); + +G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT) + +static void +egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp) +{ +    xsmp->state = XSMP_STATE_CONNECTION_CLOSED; +    xsmp->connection = NULL; +    xsmp->restart_style = SmRestartIfRunning; +} + +static void +egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass) +{ +    EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); + +    sm_client_class->startup             = sm_client_xsmp_startup; +    sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command; +    sm_client_class->will_quit           = sm_client_xsmp_will_quit; +    sm_client_class->end_session         = sm_client_xsmp_end_session; +} + +EggSMClient * +egg_sm_client_xsmp_new (void) +{ +    if (!g_getenv ("SESSION_MANAGER")) +        return NULL; + +    return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL); +} + +static gboolean +sm_client_xsmp_set_initial_properties (gpointer user_data) +{ +    EggSMClientXSMP *xsmp = user_data; +    EggDesktopFile *desktop_file; +    GPtrArray *clone, *restart; +    char pid_str[64]; + +    if (xsmp->idle) +    { +        g_source_remove (xsmp->idle); +        xsmp->idle = 0; +    } +    xsmp->waiting_to_set_initial_properties = FALSE; + +    if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART) +        xsmp->restart_style = SmRestartNever; + +    /* Parse info out of desktop file */ +    desktop_file = egg_get_desktop_file (); +    if (desktop_file) +    { +        GError *err = NULL; +        char *cmdline, **argv; +        int argc; + +        if (xsmp->restart_style == SmRestartIfRunning) +        { +            if (egg_desktop_file_get_boolean (desktop_file, +                                              "X-MATE-AutoRestart", NULL)) +                xsmp->restart_style = SmRestartImmediately; +        } + +        if (!xsmp->set_restart_command) +        { +            cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err); +            if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err)) +            { +                egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp), +                                                   argc, (const char **)argv); +                g_strfreev (argv); +            } +            else +            { +                g_warning ("Could not parse Exec line in desktop file: %s", +                           err->message); +                g_error_free (err); +            } +            g_free (cmdline); +        } +    } + +    if (!xsmp->set_restart_command) +        xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1); + +    clone = generate_command (xsmp->restart_command, NULL, NULL); +    restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); + +    g_debug ("Setting initial properties"); + +    /* Program, CloneCommand, RestartCommand, and UserID are required. +     * ProcessID isn't required, but the SM may be able to do something +     * useful with it. +     */ +    g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ()); +    set_properties (xsmp, +                    string_prop   (SmProgram, g_get_prgname ()), +                    ptrarray_prop (SmCloneCommand, clone), +                    ptrarray_prop (SmRestartCommand, restart), +                    string_prop   (SmUserID, g_get_user_name ()), +                    string_prop   (SmProcessID, pid_str), +                    card8_prop    (SmRestartStyleHint, xsmp->restart_style), +                    NULL); +    g_ptr_array_free (clone, TRUE); +    g_ptr_array_free (restart, TRUE); + +    if (desktop_file) +    { +        set_properties (xsmp, +                        string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)), +                        NULL); +    } + +    update_pending_events (xsmp); +    return FALSE; +} + +/* This gets called from two different places: xsmp_die() (when the + * server asks us to disconnect) and process_ice_messages() (when the + * server disconnects unexpectedly). + */ +static void +sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp) +{ +    SmcConn connection; + +    if (!xsmp->connection) +        return; + +    g_debug ("Disconnecting"); + +    connection = xsmp->connection; +    xsmp->connection = NULL; +    SmcCloseConnection (connection, 0, NULL); +    xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + +    xsmp->waiting_to_save_myself = FALSE; +    update_pending_events (xsmp); +} + +static void +sm_client_xsmp_startup (EggSMClient *client, +                        const char  *client_id) +{ +    EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; +    SmcCallbacks callbacks; +    char *ret_client_id; +    char error_string_ret[256]; + +    xsmp->client_id = g_strdup (client_id); + +    ice_init (); +    SmcSetErrorHandler (smc_error_handler); + +    callbacks.save_yourself.callback      = xsmp_save_yourself; +    callbacks.die.callback                = xsmp_die; +    callbacks.save_complete.callback      = xsmp_save_complete; +    callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled; + +    callbacks.save_yourself.client_data      = xsmp; +    callbacks.die.client_data                = xsmp; +    callbacks.save_complete.client_data      = xsmp; +    callbacks.shutdown_cancelled.client_data = xsmp; + +    client_id = NULL; +    error_string_ret[0] = '\0'; +    xsmp->connection = +        SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor, +                           SmcSaveYourselfProcMask | SmcDieProcMask | +                           SmcSaveCompleteProcMask | +                           SmcShutdownCancelledProcMask, +                           &callbacks, +                           xsmp->client_id, &ret_client_id, +                           sizeof (error_string_ret), error_string_ret); + +    if (!xsmp->connection) +    { +        g_warning ("Failed to connect to the session manager: %s\n", +                   error_string_ret[0] ? +                   error_string_ret : "no error message given"); +        xsmp->state = XSMP_STATE_CONNECTION_CLOSED; +        return; +    } + +    /* We expect a pointless initial SaveYourself if either (a) we +     * didn't have an initial client ID, or (b) we DID have an initial +     * client ID, but the server rejected it and gave us a new one. +     */ +    if (!xsmp->client_id || +            (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0)) +        xsmp->expecting_initial_save_yourself = TRUE; + +    if (ret_client_id) +    { +        g_free (xsmp->client_id); +        xsmp->client_id = g_strdup (ret_client_id); +        free (ret_client_id); + +        gdk_threads_enter (); +        gdk_set_sm_client_id (xsmp->client_id); +        gdk_threads_leave (); + +        g_debug ("Got client ID \"%s\"", xsmp->client_id); +    } + +    xsmp->state = XSMP_STATE_IDLE; + +    /* Do not set the initial properties until we reach the main loop, +     * so that the application has a chance to call +     * egg_set_desktop_file(). (This may also help the session manager +     * have a better idea of when the application is fully up and +     * running.) +     */ +    xsmp->waiting_to_set_initial_properties = TRUE; +    xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client); +} + +static void +sm_client_xsmp_set_restart_command (EggSMClient  *client, +                                    int           argc, +                                    const char  **argv) +{ +    EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; +    int i; + +    g_strfreev (xsmp->restart_command); + +    xsmp->restart_command = g_new (char *, argc + 1); +    for (i = 0; i < argc; i++) +        xsmp->restart_command[i] = g_strdup (argv[i]); +    xsmp->restart_command[i] = NULL; + +    xsmp->set_restart_command = TRUE; +} + +static void +sm_client_xsmp_will_quit (EggSMClient *client, +                          gboolean     will_quit) +{ +    EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + +    if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED) +    { +        /* The session manager has already exited! Schedule a quit +         * signal. +         */ +        xsmp->waiting_to_emit_quit = TRUE; +        update_pending_events (xsmp); +        return; +    } +    else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) +    { +        /* We received a ShutdownCancelled message while the application +         * was interacting; Schedule a quit_cancelled signal. +         */ +        xsmp->waiting_to_emit_quit_cancelled = TRUE; +        update_pending_events (xsmp); +        return; +    } + +    g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT); + +    g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True"); +    SmcInteractDone (xsmp->connection, !will_quit); + +    if (will_quit && xsmp->need_save_state) +        save_state (xsmp); + +    g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False"); +    SmcSaveYourselfDone (xsmp->connection, will_quit); +    xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +} + +static gboolean +sm_client_xsmp_end_session (EggSMClient         *client, +                            EggSMClientEndStyle  style, +                            gboolean             request_confirmation) +{ +    EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; +    int save_type; + +    /* To end the session via XSMP, we have to send a +     * SaveYourselfRequest. We aren't allowed to do that if anything +     * else is going on, but we don't want to expose this fact to the +     * application. So we do our best to patch things up here... +     * +     * In the worst case, this method might block for some length of +     * time in process_ice_messages, but the only time that code path is +     * honestly likely to get hit is if the application tries to end the +     * session as the very first thing it does, in which case it +     * probably won't actually block anyway. It's not worth gunking up +     * the API to try to deal nicely with the other 0.01% of cases where +     * this happens. +     */ + +    while (xsmp->state != XSMP_STATE_IDLE || +            xsmp->expecting_initial_save_yourself) +    { +        /* If we're already shutting down, we don't need to do anything. */ +        if (xsmp->shutting_down) +            return TRUE; + +        switch (xsmp->state) +        { +        case XSMP_STATE_CONNECTION_CLOSED: +            return FALSE; + +        case XSMP_STATE_SAVE_YOURSELF: +            /* Trying to log out from the save_state callback? Whatever. +             * Abort the save_state. +             */ +            SmcSaveYourselfDone (xsmp->connection, FALSE); +            xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +            break; + +        case XSMP_STATE_INTERACT_REQUEST: +        case XSMP_STATE_INTERACT: +        case XSMP_STATE_SHUTDOWN_CANCELLED: +            /* Already in a shutdown-related state, just ignore +             * the new shutdown request... +             */ +            return TRUE; + +        case XSMP_STATE_IDLE: +            if (xsmp->waiting_to_set_initial_properties) +                sm_client_xsmp_set_initial_properties (xsmp); + +            if (!xsmp->expecting_initial_save_yourself) +                break; +            /* else fall through */ + +        case XSMP_STATE_SAVE_YOURSELF_DONE: +            /* We need to wait for some response from the server.*/ +            process_ice_messages (SmcGetIceConnection (xsmp->connection)); +            break; + +        default: +            /* Hm... shouldn't happen */ +            return FALSE; +        } +    } + +    /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and +     * the user chooses to save the session. But mate-session will do +     * the wrong thing if we pass SmSaveBoth and the user chooses NOT to +     * save the session... Sigh. +     */ +    if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session")) +        save_type = SmSaveBoth; +    else +        save_type = SmSaveGlobal; + +    g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : ""); +    SmcRequestSaveYourself (xsmp->connection, +                            save_type, +                            True, /* shutdown */ +                            SmInteractStyleAny, +                            !request_confirmation, /* fast */ +                            True /* global */); +    return TRUE; +} + +static gboolean +idle_do_pending_events (gpointer data) +{ +    EggSMClientXSMP *xsmp = data; +    EggSMClient *client = data; + +    gdk_threads_enter (); + +    xsmp->idle = 0; + +    if (xsmp->waiting_to_emit_quit) +    { +        xsmp->waiting_to_emit_quit = FALSE; +        egg_sm_client_quit (client); +        goto out; +    } + +    if (xsmp->waiting_to_emit_quit_cancelled) +    { +        xsmp->waiting_to_emit_quit_cancelled = FALSE; +        egg_sm_client_quit_cancelled (client); +        xsmp->state = XSMP_STATE_IDLE; +    } + +    if (xsmp->waiting_to_save_myself) +    { +        xsmp->waiting_to_save_myself = FALSE; +        do_save_yourself (xsmp); +    } + +out: +    gdk_threads_leave (); +    return FALSE; +} + +static void +update_pending_events (EggSMClientXSMP *xsmp) +{ +    gboolean want_idle = +        xsmp->waiting_to_emit_quit || +        xsmp->waiting_to_emit_quit_cancelled || +        xsmp->waiting_to_save_myself; + +    if (want_idle) +    { +        if (xsmp->idle == 0) +            xsmp->idle = g_idle_add (idle_do_pending_events, xsmp); +    } +    else +    { +        if (xsmp->idle != 0) +            g_source_remove (xsmp->idle); +        xsmp->idle = 0; +    } +} + +static void +fix_broken_state (EggSMClientXSMP *xsmp, const char *message, +                  gboolean send_interact_done, +                  gboolean send_save_yourself_done) +{ +    g_warning ("Received XSMP %s message in state %s: client or server error", +               message, EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +    /* Forget any pending SaveYourself plans we had */ +    xsmp->waiting_to_save_myself = FALSE; +    update_pending_events (xsmp); + +    if (send_interact_done) +        SmcInteractDone (xsmp->connection, False); +    if (send_save_yourself_done) +        SmcSaveYourselfDone (xsmp->connection, True); + +    xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE; +} + +/* SM callbacks */ + +static void +xsmp_save_yourself (SmcConn   smc_conn, +                    SmPointer client_data, +                    int       save_type, +                    Bool      shutdown, +                    int       interact_style, +                    Bool      fast) +{ +    EggSMClientXSMP *xsmp = client_data; +    gboolean wants_quit_requested; + +    g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s", +             save_type == SmSaveLocal ? "SmSaveLocal" : +             save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth", +             shutdown ? "Shutdown" : "!Shutdown", +             interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : +             interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" : +             "SmInteractStyleNone", fast ? "Fast" : "!Fast", +             EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +    if (xsmp->state != XSMP_STATE_IDLE && +            xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED) +    { +        fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE); +        return; +    } + +    if (xsmp->waiting_to_set_initial_properties) +        sm_client_xsmp_set_initial_properties (xsmp); + +    /* If this is the initial SaveYourself, ignore it; we've already set +     * properties and there's no reason to actually save state too. +     */ +    if (xsmp->expecting_initial_save_yourself) +    { +        xsmp->expecting_initial_save_yourself = FALSE; + +        if (save_type == SmSaveLocal && +                interact_style == SmInteractStyleNone && +                !shutdown && !fast) +        { +            g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself"); +            SmcSaveYourselfDone (xsmp->connection, True); +            /* As explained in the comment at the end of +             * do_save_yourself(), SAVE_YOURSELF_DONE is the correct +             * state here, not IDLE. +             */ +            xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +            return; +        } +        else +            g_warning ("First SaveYourself was not the expected one!"); +    } + +    /* Even ignoring the "fast" flag completely, there are still 18 +     * different combinations of save_type, shutdown and interact_style. +     * We interpret them as follows: +     * +     *   Type  Shutdown  Interact	 Interpretation +     *     G      F       A/E/N  	 do nothing (1) +     *     G      T         N    	 do nothing (1)* +     *     G      T        A/E   	 quit_requested (2) +     *    L/B     F       A/E/N  	 save_state (3) +     *    L/B     T         N    	 save_state (3)* +     *    L/B     T        A/E   	 quit_requested, then save_state (4) +     * +     *   1. Do nothing, because the SM asked us to do something +     *      uninteresting (save open files, but then don't quit +     *      afterward) or rude (save open files without asking the user +     *      for confirmation). +     * +     *   2. Request interaction and then emit ::quit_requested. This +     *      perhaps isn't quite correct for the SmInteractStyleErrors +     *      case, but we don't care. +     * +     *   3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these +     *      rows essentially get demoted to SmSaveLocal, because their +     *      Global halves correspond to "do nothing". +     * +     *   4. Request interaction, emit ::quit_requested, and then emit +     *      ::save_state after interacting. This is the SmSaveBoth +     *      equivalent of #2, but we also promote SmSaveLocal shutdown +     *      SaveYourselfs to SmSaveBoth here, because we want to give +     *      the user a chance to save open files before quitting. +     * +     * (* It would be nice if we could do something useful when the +     * session manager sends a SaveYourself with shutdown True and +     * SmInteractStyleNone. But we can't, so we just pretend it didn't +     * even tell us it was shutting down. The docs for ::quit mention +     * that it might not always be preceded by ::quit_requested.) +     */ + +    /* As an optimization, we don't actually request interaction and +     * emit ::quit_requested if the application isn't listening to the +     * signal. +     */ +    wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE); + +    xsmp->need_save_state     = (save_type != SmSaveGlobal); +    xsmp->need_quit_requested = (shutdown && wants_quit_requested && +                                 interact_style != SmInteractStyleNone); +    xsmp->interact_errors     = (interact_style == SmInteractStyleErrors); + +    xsmp->shutting_down       = shutdown; + +    do_save_yourself (xsmp); +} + +static void +do_save_yourself (EggSMClientXSMP *xsmp) +{ +    if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) +    { +        /* The SM cancelled a previous SaveYourself, but we haven't yet +         * had a chance to tell the application, so we can't start +         * processing this SaveYourself yet. +         */ +        xsmp->waiting_to_save_myself = TRUE; +        update_pending_events (xsmp); +        return; +    } + +    if (xsmp->need_quit_requested) +    { +        xsmp->state = XSMP_STATE_INTERACT_REQUEST; + +        g_debug ("Sending InteractRequest(%s)", +                 xsmp->interact_errors ? "Error" : "Normal"); +        SmcInteractRequest (xsmp->connection, +                            xsmp->interact_errors ? SmDialogError : SmDialogNormal, +                            xsmp_interact, +                            xsmp); +        return; +    } + +    if (xsmp->need_save_state) +    { +        save_state (xsmp); + +        /* Though unlikely, the client could have been disconnected +         * while the application was saving its state. +         */ +        if (!xsmp->connection) +            return; +    } + +    g_debug ("Sending SaveYourselfDone(True)"); +    SmcSaveYourselfDone (xsmp->connection, True); + +    /* The client state diagram in the XSMP spec says that after a +     * non-shutdown SaveYourself, we go directly back to "idle". But +     * everything else in both the XSMP spec and the libSM docs +     * disagrees. +     */ +    xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +} + +static void +save_state (EggSMClientXSMP *xsmp) +{ +    GKeyFile *state_file; +    char *state_file_path, *data; +    EggDesktopFile *desktop_file; +    GPtrArray *restart; +    int offset, fd; + +    /* We set xsmp->state before emitting save_state, but our caller is +     * responsible for setting it back afterward. +     */ +    xsmp->state = XSMP_STATE_SAVE_YOURSELF; + +    state_file = egg_sm_client_save_state ((EggSMClient *)xsmp); +    if (!state_file) +    { +        restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); +        set_properties (xsmp, +                        ptrarray_prop (SmRestartCommand, restart), +                        NULL); +        g_ptr_array_free (restart, TRUE); +        delete_properties (xsmp, SmDiscardCommand, NULL); +        return; +    } + +    desktop_file = egg_get_desktop_file (); +    if (desktop_file) +    { +        GKeyFile *merged_file; +        char *desktop_file_path; + +        merged_file = g_key_file_new (); +        desktop_file_path = +            g_filename_from_uri (egg_desktop_file_get_source (desktop_file), +                                 NULL, NULL); +        if (desktop_file_path && +                g_key_file_load_from_file (merged_file, desktop_file_path, +                                           G_KEY_FILE_KEEP_COMMENTS | +                                           G_KEY_FILE_KEEP_TRANSLATIONS, NULL)) +        { +            guint g, k, i; +            char **groups, **keys, *value, *exec; + +            groups = g_key_file_get_groups (state_file, NULL); +            for (g = 0; groups[g]; g++) +            { +                keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL); +                for (k = 0; keys[k]; k++) +                { +                    value = g_key_file_get_value (state_file, groups[g], +                                                  keys[k], NULL); +                    if (value) +                    { +                        g_key_file_set_value (merged_file, groups[g], +                                              keys[k], value); +                        g_free (value); +                    } +                } +                g_strfreev (keys); +            } +            g_strfreev (groups); + +            g_key_file_free (state_file); +            state_file = merged_file; + +            /* Update Exec key using "--sm-client-state-file %k" */ +            restart = generate_command (xsmp->restart_command, +                                        NULL, "%k"); +            for (i = 0; i < restart->len; i++) +                restart->pdata[i] = g_shell_quote (restart->pdata[i]); +            g_ptr_array_add (restart, NULL); +            exec = g_strjoinv (" ", (char **)restart->pdata); +            g_strfreev ((char **)restart->pdata); +            g_ptr_array_free (restart, FALSE); + +            g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP, +                                   EGG_DESKTOP_FILE_KEY_EXEC, +                                   exec); +            g_free (exec); +        } +        else +            desktop_file = NULL; + +        g_free (desktop_file_path); +    } + +    /* Now write state_file to disk. (We can't use mktemp(), because +     * that requires the filename to end with "XXXXXX", and we want +     * it to end with ".desktop".) +     */ + +    data = g_key_file_to_data (state_file, NULL, NULL); +    g_key_file_free (state_file); + +    offset = 0; +    while (1) +    { +        state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s", +                                           g_get_user_config_dir (), +                                           G_DIR_SEPARATOR, G_DIR_SEPARATOR, +                                           g_get_prgname (), +                                           (long)time (NULL) + offset, +                                           desktop_file ? "desktop" : "state"); + +        fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644); +        if (fd == -1) +        { +            if (errno == EEXIST) +            { +                offset++; +                g_free (state_file_path); +                continue; +            } +            else if (errno == ENOTDIR || errno == ENOENT) +            { +                char *sep = strrchr (state_file_path, G_DIR_SEPARATOR); + +                *sep = '\0'; +                if (g_mkdir_with_parents (state_file_path, 0755) != 0) +                { +                    g_warning ("Could not create directory '%s'", +                               state_file_path); +                    g_free (state_file_path); +                    state_file_path = NULL; +                    break; +                } + +                continue; +            } + +            g_warning ("Could not create file '%s': %s", +                       state_file_path, g_strerror (errno)); +            g_free (state_file_path); +            state_file_path = NULL; +            break; +        } + +        close (fd); +        g_file_set_contents (state_file_path, data, -1, NULL); +        break; +    } +    g_free (data); + +    restart = generate_command (xsmp->restart_command, xsmp->client_id, +                                state_file_path); +    set_properties (xsmp, +                    ptrarray_prop (SmRestartCommand, restart), +                    NULL); +    g_ptr_array_free (restart, TRUE); + +    if (state_file_path) +    { +        set_properties (xsmp, +                        array_prop (SmDiscardCommand, +                                    "/bin/rm", "-rf", state_file_path, +                                    NULL), +                        NULL); +        g_free (state_file_path); +    } +} + +static void +xsmp_interact (SmcConn   smc_conn, +               SmPointer client_data) +{ +    EggSMClientXSMP *xsmp = client_data; +    EggSMClient *client = client_data; + +    g_debug ("Received Interact message in state %s", +             EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +    if (xsmp->state != XSMP_STATE_INTERACT_REQUEST) +    { +        fix_broken_state (xsmp, "Interact", TRUE, TRUE); +        return; +    } + +    xsmp->state = XSMP_STATE_INTERACT; +    egg_sm_client_quit_requested (client); +} + +static void +xsmp_die (SmcConn   smc_conn, +          SmPointer client_data) +{ +    EggSMClientXSMP *xsmp = client_data; +    EggSMClient *client = client_data; + +    g_debug ("Received Die message in state %s", +             EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +    sm_client_xsmp_disconnect (xsmp); +    egg_sm_client_quit (client); +} + +static void +xsmp_save_complete (SmcConn   smc_conn, +                    SmPointer client_data) +{ +    EggSMClientXSMP *xsmp = client_data; + +    g_debug ("Received SaveComplete message in state %s", +             EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +    if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) +        xsmp->state = XSMP_STATE_IDLE; +    else +        fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE); +} + +static void +xsmp_shutdown_cancelled (SmcConn   smc_conn, +                         SmPointer client_data) +{ +    EggSMClientXSMP *xsmp = client_data; +    EggSMClient *client = client_data; + +    g_debug ("Received ShutdownCancelled message in state %s", +             EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +    xsmp->shutting_down = FALSE; + +    if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) +    { +        /* We've finished interacting and now the SM has agreed to +         * cancel the shutdown. +         */ +        xsmp->state = XSMP_STATE_IDLE; +        egg_sm_client_quit_cancelled (client); +    } +    else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) +    { +        /* Hm... ok, so we got a shutdown SaveYourself, which got +         * cancelled, but the application was still interacting, so we +         * didn't tell it yet, and then *another* SaveYourself arrived, +         * which we must still be waiting to tell the app about, except +         * that now that SaveYourself has been cancelled too! Dizzy yet? +         */ +        xsmp->waiting_to_save_myself = FALSE; +        update_pending_events (xsmp); +    } +    else +    { +        g_debug ("Sending SaveYourselfDone(False)"); +        SmcSaveYourselfDone (xsmp->connection, False); + +        if (xsmp->state == XSMP_STATE_INTERACT) +        { +            /* The application is currently interacting, so we can't +             * tell it about the cancellation yet; we will wait until +             * after it calls egg_sm_client_will_quit(). +             */ +            xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED; +        } +        else +        { +            /* The shutdown was cancelled before the application got a +             * chance to interact. +             */ +            xsmp->state = XSMP_STATE_IDLE; +        } +    } +} + +/* Utilities */ + +/* Create a restart/clone/Exec command based on @restart_command. + * If @client_id is non-%NULL, add "--sm-client-id @client_id". + * If @state_file is non-%NULL, add "--sm-client-state-file @state_file". + * + * None of the input strings are g_strdup()ed; the caller must keep + * them around until it is done with the returned GPtrArray, and must + * then free the array, but not its contents. + */ +static GPtrArray * +generate_command (char **restart_command, const char *client_id, +                  const char *state_file) +{ +    GPtrArray *cmd; +    int i; + +    cmd = g_ptr_array_new (); +    g_ptr_array_add (cmd, restart_command[0]); + +    if (client_id) +    { +        g_ptr_array_add (cmd, "--sm-client-id"); +        g_ptr_array_add (cmd, (char *)client_id); +    } + +    if (state_file) +    { +        g_ptr_array_add (cmd, "--sm-client-state-file"); +        g_ptr_array_add (cmd, (char *)state_file); +    } + +    for (i = 1; restart_command[i]; i++) +        g_ptr_array_add (cmd, restart_command[i]); + +    return cmd; +} + +/* Takes a NULL-terminated list of SmProp * values, created by + * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and + * frees them. + */ +static void +set_properties (EggSMClientXSMP *xsmp, ...) +{ +    GPtrArray *props; +    SmProp *prop; +    va_list ap; +    guint i; + +    props = g_ptr_array_new (); + +    va_start (ap, xsmp); +    while ((prop = va_arg (ap, SmProp *))) +        g_ptr_array_add (props, prop); +    va_end (ap); + +    if (xsmp->connection) +    { +        SmcSetProperties (xsmp->connection, props->len, +                          (SmProp **)props->pdata); +    } + +    for (i = 0; i < props->len; i++) +    { +        prop = props->pdata[i]; +        g_free (prop->vals); +        g_free (prop); +    } +    g_ptr_array_free (props, TRUE); +} + +/* Takes a NULL-terminated list of property names and deletes them. */ +static void +delete_properties (EggSMClientXSMP *xsmp, ...) +{ +    GPtrArray *props; +    char *prop; +    va_list ap; + +    if (!xsmp->connection) +        return; + +    props = g_ptr_array_new (); + +    va_start (ap, xsmp); +    while ((prop = va_arg (ap, char *))) +        g_ptr_array_add (props, prop); +    va_end (ap); + +    SmcDeleteProperties (xsmp->connection, props->len, +                         (char **)props->pdata); + +    g_ptr_array_free (props, TRUE); +} + +/* Takes an array of strings and creates a LISTofARRAY8 property. The + * strings are neither dupped nor freed; they need to remain valid + * until you're done with the SmProp. + */ +static SmProp * +array_prop (const char *name, ...) +{ +    SmProp *prop; +    SmPropValue pv; +    GArray *vals; +    char *value; +    va_list ap; + +    prop = g_new (SmProp, 1); +    prop->name = (char *)name; +    prop->type = SmLISTofARRAY8; + +    vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); + +    va_start (ap, name); +    while ((value = va_arg (ap, char *))) +    { +        pv.length = strlen (value); +        pv.value = value; +        g_array_append_val (vals, pv); +    } + +    prop->num_vals = vals->len; +    prop->vals = (SmPropValue *)vals->data; + +    g_array_free (vals, FALSE); + +    return prop; +} + +/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property. + * The array contents are neither dupped nor freed; they need to + * remain valid until you're done with the SmProp. + */ +static SmProp * +ptrarray_prop (const char *name, GPtrArray *values) +{ +    SmProp *prop; +    SmPropValue pv; +    GArray *vals; +    guint i; + +    prop = g_new (SmProp, 1); +    prop->name = (char *)name; +    prop->type = SmLISTofARRAY8; + +    vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); + +    for (i = 0; i < values->len; i++) +    { +        pv.length = strlen (values->pdata[i]); +        pv.value = values->pdata[i]; +        g_array_append_val (vals, pv); +    } + +    prop->num_vals = vals->len; +    prop->vals = (SmPropValue *)vals->data; + +    g_array_free (vals, FALSE); + +    return prop; +} + +/* Takes a string and creates an ARRAY8 property. The string is + * neither dupped nor freed; it needs to remain valid until you're + * done with the SmProp. + */ +static SmProp * +string_prop (const char *name, const char *value) +{ +    SmProp *prop; + +    prop = g_new (SmProp, 1); +    prop->name = (char *)name; +    prop->type = SmARRAY8; + +    prop->num_vals = 1; +    prop->vals = g_new (SmPropValue, 1); + +    prop->vals[0].length = strlen (value); +    prop->vals[0].value = (char *)value; + +    return prop; +} + +/* Takes a char and creates a CARD8 property. */ +static SmProp * +card8_prop (const char *name, unsigned char value) +{ +    SmProp *prop; +    char *card8val; + +    /* To avoid having to allocate and free prop->vals[0], we cheat and +     * make vals a 2-element-long array and then use the second element +     * to store value. +     */ + +    prop = g_new (SmProp, 1); +    prop->name = (char *)name; +    prop->type = SmCARD8; + +    prop->num_vals = 1; +    prop->vals = g_new (SmPropValue, 2); +    card8val = (char *)(&prop->vals[1]); +    card8val[0] = value; + +    prop->vals[0].length = 1; +    prop->vals[0].value = card8val; + +    return prop; +} + +/* ICE code. This makes no effort to play nice with anyone else trying + * to use libICE. Fortunately, no one uses libICE for anything other + * than SM. (DCOP uses ICE, but it has its own private copy of + * libICE.) + * + * When this moves to gtk, it will need to be cleverer, to avoid + * tripping over old apps that use MateClient or that use libSM + * directly. + */ + +#include <X11/ICE/ICElib.h> +#include <fcntl.h> + +static void        ice_error_handler    (IceConn        ice_conn, +        Bool           swap, +        int            offending_minor_opcode, +        unsigned long  offending_sequence, +        int            error_class, +        int            severity, +        IcePointer     values); +static void        ice_io_error_handler (IceConn        ice_conn); +static void        ice_connection_watch (IceConn        ice_conn, +        IcePointer     client_data, +        Bool           opening, +        IcePointer    *watch_data); + +static void +ice_init (void) +{ +    IceSetIOErrorHandler (ice_io_error_handler); +    IceSetErrorHandler (ice_error_handler); +    IceAddConnectionWatch (ice_connection_watch, NULL); +} + +static gboolean +process_ice_messages (IceConn ice_conn) +{ +    IceProcessMessagesStatus status; + +    gdk_threads_enter (); +    status = IceProcessMessages (ice_conn, NULL, NULL); +    gdk_threads_leave (); + +    switch (status) +    { +    case IceProcessMessagesSuccess: +        return TRUE; + +    case IceProcessMessagesIOError: +        sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn)); +        return FALSE; + +    case IceProcessMessagesConnectionClosed: +        return FALSE; + +    default: +        g_assert_not_reached (); +    } +} + +static gboolean +ice_iochannel_watch (GIOChannel   *channel, +                     GIOCondition  condition, +                     gpointer      client_data) +{ +    return process_ice_messages (client_data); +} + +static void +ice_connection_watch (IceConn     ice_conn, +                      IcePointer  client_data, +                      Bool        opening, +                      IcePointer *watch_data) +{ +    guint watch_id; + +    if (opening) +    { +        GIOChannel *channel; +        int fd = IceConnectionNumber (ice_conn); + +        fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC); +        channel = g_io_channel_unix_new (fd); +        watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR, +                                   ice_iochannel_watch, ice_conn); +        g_io_channel_unref (channel); + +        *watch_data = GUINT_TO_POINTER (watch_id); +    } +    else +    { +        watch_id = GPOINTER_TO_UINT (*watch_data); +        g_source_remove (watch_id); +    } +} + +static void +ice_error_handler (IceConn       ice_conn, +                   Bool          swap, +                   int           offending_minor_opcode, +                   unsigned long offending_sequence, +                   int           error_class, +                   int           severity, +                   IcePointer    values) +{ +    /* Do nothing */ +} + +static void +ice_io_error_handler (IceConn ice_conn) +{ +    /* Do nothing */ +} + +static void +smc_error_handler (SmcConn       smc_conn, +                   Bool          swap, +                   int           offending_minor_opcode, +                   unsigned long offending_sequence, +                   int           error_class, +                   int           severity, +                   SmPointer     values) +{ +    /* Do nothing */ +} diff --git a/cut-n-paste-code/libegg/eggsmclient.c b/cut-n-paste-code/libegg/eggsmclient.c new file mode 100644 index 00000000..280c7ded --- /dev/null +++ b/cut-n-paste-code/libegg/eggsmclient.c @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <string.h> +#include <glib/gi18n.h> + +#include "eggsmclient.h" +#include "eggsmclient-private.h" + +static void egg_sm_client_debug_handler (const char *log_domain, +        GLogLevelFlags log_level, +        const char *message, +        gpointer user_data); + +enum +{ +    SAVE_STATE, +    QUIT_REQUESTED, +    QUIT_CANCELLED, +    QUIT, +    LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +struct _EggSMClientPrivate +{ +    GKeyFile *state_file; +}; + +#define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, EggSMClientPrivate)) + +G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT) + +static EggSMClient *global_client; +static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL; + +static void +egg_sm_client_init (EggSMClient *client) +{ +    ; +} + +static void +egg_sm_client_class_init (EggSMClientClass *klass) +{ +    GObjectClass *object_class = G_OBJECT_CLASS (klass); + +    g_type_class_add_private (klass, sizeof (EggSMClientPrivate)); + +    /** +     * EggSMClient::save_state: +     * @client: the client +     * @state_file: a #GKeyFile to save state information into +     * +     * Emitted when the session manager has requested that the +     * application save information about its current state. The +     * application should save its state into @state_file, and then the +     * session manager may then restart the application in a future +     * session and tell it to initialize itself from that state. +     * +     * You should not save any data into @state_file's "start group" +     * (ie, the %NULL group). Instead, applications should save their +     * data into groups with names that start with the application name, +     * and libraries that connect to this signal should save their data +     * into groups with names that start with the library name. +     * +     * Alternatively, rather than (or in addition to) using @state_file, +     * the application can save its state by calling +     * egg_sm_client_set_restart_command() during the processing of this +     * signal (eg, to include a list of files to open). +     **/ +    signals[SAVE_STATE] = +        g_signal_new ("save_state", +                      G_OBJECT_CLASS_TYPE (object_class), +                      G_SIGNAL_RUN_LAST, +                      G_STRUCT_OFFSET (EggSMClientClass, save_state), +                      NULL, NULL, +                      g_cclosure_marshal_VOID__POINTER, +                      G_TYPE_NONE, +                      1, G_TYPE_POINTER); + +    /** +     * EggSMClient::quit_requested: +     * @client: the client +     * +     * Emitted when the session manager requests that the application +     * exit (generally because the user is logging out). The application +     * should decide whether or not it is willing to quit (perhaps after +     * asking the user what to do with documents that have unsaved +     * changes) and then call egg_sm_client_will_quit(), passing %TRUE +     * or %FALSE to give its answer to the session manager. (It does not +     * need to give an answer before returning from the signal handler; +     * it can interact with the user asynchronously and then give its +     * answer later on.) If the application does not connect to this +     * signal, then #EggSMClient will automatically return %TRUE on its +     * behalf. +     * +     * The application should not save its session state as part of +     * handling this signal; if the user has requested that the session +     * be saved when logging out, then ::save_state will be emitted +     * separately. +     * +     * If the application agrees to quit, it should then wait for either +     * the ::quit_cancelled or ::quit signals to be emitted. +     **/ +    signals[QUIT_REQUESTED] = +        g_signal_new ("quit_requested", +                      G_OBJECT_CLASS_TYPE (object_class), +                      G_SIGNAL_RUN_LAST, +                      G_STRUCT_OFFSET (EggSMClientClass, quit_requested), +                      NULL, NULL, +                      g_cclosure_marshal_VOID__VOID, +                      G_TYPE_NONE, +                      0); + +    /** +     * EggSMClient::quit_cancelled: +     * @client: the client +     * +     * Emitted when the session manager decides to cancel a logout after +     * the application has already agreed to quit. After receiving this +     * signal, the application can go back to what it was doing before +     * receiving the ::quit_requested signal. +     **/ +    signals[QUIT_CANCELLED] = +        g_signal_new ("quit_cancelled", +                      G_OBJECT_CLASS_TYPE (object_class), +                      G_SIGNAL_RUN_LAST, +                      G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled), +                      NULL, NULL, +                      g_cclosure_marshal_VOID__VOID, +                      G_TYPE_NONE, +                      0); + +    /** +     * EggSMClient::quit: +     * @client: the client +     * +     * Emitted when the session manager wants the application to quit +     * (generally because the user is logging out). The application +     * should exit as soon as possible after receiving this signal; if +     * it does not, the session manager may choose to forcibly kill it. +     * +     * Normally a GUI application would only be sent a ::quit if it +     * agreed to quit in response to a ::quit_requested signal. However, +     * this is not guaranteed; in some situations the session manager +     * may decide to end the session without giving applications a +     * chance to object. +     **/ +    signals[QUIT] = +        g_signal_new ("quit", +                      G_OBJECT_CLASS_TYPE (object_class), +                      G_SIGNAL_RUN_LAST, +                      G_STRUCT_OFFSET (EggSMClientClass, quit), +                      NULL, NULL, +                      g_cclosure_marshal_VOID__VOID, +                      G_TYPE_NONE, +                      0); +} + +static gboolean sm_client_disable = FALSE; +static char *sm_client_state_file = NULL; +static char *sm_client_id = NULL; +static char *sm_config_prefix = NULL; + +static gboolean +sm_client_post_parse_func (GOptionContext  *context, +                           GOptionGroup    *group, +                           gpointer         data, +                           GError         **error) +{ +    EggSMClient *client = egg_sm_client_get (); + +    if (sm_client_id == NULL) +    { +        const gchar *desktop_autostart_id; + +        desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); + +        if (desktop_autostart_id != NULL) +            sm_client_id = g_strdup (desktop_autostart_id); +    } + +    /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to +     * use the same client id. */ +    g_unsetenv ("DESKTOP_AUTOSTART_ID"); + +    if (EGG_SM_CLIENT_GET_CLASS (client)->startup) +        EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id); +    return TRUE; +} + +/** + * egg_sm_client_get_option_group: + * + * Creates a %GOptionGroup containing the session-management-related + * options. You should add this group to the application's + * %GOptionContext if you want to use #EggSMClient. + * + * Return value: the %GOptionGroup + **/ +GOptionGroup * +egg_sm_client_get_option_group (void) +{ +    const GOptionEntry entries[] = +    { +        { +            "sm-client-disable", 0, 0, +            G_OPTION_ARG_NONE, &sm_client_disable, +            N_("Disable connection to session manager"), NULL +        }, +        { +            "sm-client-state-file", 0, 0, +            G_OPTION_ARG_FILENAME, &sm_client_state_file, +            N_("Specify file containing saved configuration"), N_("FILE") +        }, +        { +            "sm-client-id", 0, 0, +            G_OPTION_ARG_STRING, &sm_client_id, +            N_("Specify session management ID"), N_("ID") +        }, +        /* MateClient compatibility option */ +        { +            "sm-disable", 0, G_OPTION_FLAG_HIDDEN, +            G_OPTION_ARG_NONE, &sm_client_disable, +            NULL, NULL +        }, +        /* MateClient compatibility option. This is a dummy option that only +         * exists so that sessions saved by apps with MateClient can be restored +         * later when they've switched to EggSMClient. See bug #575308. +         */ +        { +            "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN, +            G_OPTION_ARG_STRING, &sm_config_prefix, +            NULL, NULL +        }, +        { NULL } +    }; +    GOptionGroup *group; + +    /* Use our own debug handler for the "EggSMClient" domain. */ +    g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, +                       egg_sm_client_debug_handler, NULL); + +    group = g_option_group_new ("sm-client", +                                _("Session management options:"), +                                _("Show session management options"), +                                NULL, NULL); +    g_option_group_add_entries (group, entries); +    g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func); + +    return group; +} + +/** + * egg_sm_client_set_mode: + * @mode: an #EggSMClient mode + * + * Sets the "mode" of #EggSMClient as follows: + * + *    %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely + *    disabled. The application will not even connect to the session + *    manager. (egg_sm_client_get() will still return an #EggSMClient, + *    but it will just be a dummy object.) + * + *    %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to + *    the session manager (and thus will receive notification when the + *    user is logging out, etc), but will request to not be + *    automatically restarted with saved state in future sessions. + * + *    %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will + *    function normally. + * + * This must be called before the application's main loop begins. + **/ +void +egg_sm_client_set_mode (EggSMClientMode mode) +{ +    global_client_mode = mode; +} + +/** + * egg_sm_client_get_mode: + * + * Gets the global #EggSMClientMode. See egg_sm_client_set_mode() + * for details. + * + * Return value: the global #EggSMClientMode + **/ +EggSMClientMode +egg_sm_client_get_mode (void) +{ +    return global_client_mode; +} + +/** + * egg_sm_client_get: + * + * Returns the master #EggSMClient for the application. + * + * On platforms that support saved sessions (ie, POSIX/X11), the + * application will only request to be restarted by the session + * manager if you call egg_set_desktop_file() to set an application + * desktop file. In particular, if the desktop file contains the key + * "X + * + * Return value: the master #EggSMClient. + **/ +EggSMClient * +egg_sm_client_get (void) +{ +    if (!global_client) +    { +        if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED && +                !sm_client_disable) +        { +#if defined (GDK_WINDOWING_WIN32) +            global_client = egg_sm_client_win32_new (); +#elif defined (GDK_WINDOWING_QUARTZ) +            global_client = egg_sm_client_osx_new (); +#else +            /* If both D-Bus and XSMP are compiled in, try XSMP first +             * (since it supports state saving) and fall back to D-Bus +             * if XSMP isn't available. +             */ +# ifdef EGG_SM_CLIENT_BACKEND_XSMP +            global_client = egg_sm_client_xsmp_new (); +# endif +# ifdef EGG_SM_CLIENT_BACKEND_DBUS +            if (!global_client) +                global_client = egg_sm_client_dbus_new (); +# endif +#endif +        } + +        /* Fallback: create a dummy client, so that callers don't have +         * to worry about a %NULL return value. +         */ +        if (!global_client) +            global_client = g_object_new (EGG_TYPE_SM_CLIENT, NULL); +    } + +    return global_client; +} + +/** + * egg_sm_client_is_resumed: + * @client: the client + * + * Checks whether or not the current session has been resumed from + * a previous saved session. If so, the application should call + * egg_sm_client_get_state_file() and restore its state from the + * returned #GKeyFile. + * + * Return value: %TRUE if the session has been resumed + **/ +gboolean +egg_sm_client_is_resumed (EggSMClient *client) +{ +    g_return_val_if_fail (client == global_client, FALSE); + +    return sm_client_state_file != NULL; +} + +/** + * egg_sm_client_get_state_file: + * @client: the client + * + * If the application was resumed by the session manager, this will + * return the #GKeyFile containing its state from the previous + * session. + * + * Note that other libraries and #EggSMClient itself may also store + * state in the key file, so if you call egg_sm_client_get_groups(), + * on it, the return value will likely include groups that you did not + * put there yourself. (It is also not guaranteed that the first + * group created by the application will still be the "start group" + * when it is resumed.) + * + * Return value: the #GKeyFile containing the application's earlier + * state, or %NULL on error. You should not free this key file; it + * is owned by @client. + **/ +GKeyFile * +egg_sm_client_get_state_file (EggSMClient *client) +{ +    EggSMClientPrivate *priv = EGG_SM_CLIENT_GET_PRIVATE (client); +    char *state_file_path; +    GError *err = NULL; + +    g_return_val_if_fail (client == global_client, NULL); + +    if (!sm_client_state_file) +        return NULL; +    if (priv->state_file) +        return priv->state_file; + +    if (!strncmp (sm_client_state_file, "file://", 7)) +        state_file_path = g_filename_from_uri (sm_client_state_file, NULL, NULL); +    else +        state_file_path = g_strdup (sm_client_state_file); + +    priv->state_file = g_key_file_new (); +    if (!g_key_file_load_from_file (priv->state_file, state_file_path, 0, &err)) +    { +        g_warning ("Could not load SM state file '%s': %s", +                   sm_client_state_file, err->message); +        g_clear_error (&err); +        g_key_file_free (priv->state_file); +        priv->state_file = NULL; +    } + +    g_free (state_file_path); +    return priv->state_file; +} + +/** + * egg_sm_client_set_restart_command: + * @client: the client + * @argc: the length of @argv + * @argv: argument vector + * + * Sets the command used to restart @client if it does not have a + * .desktop file that can be used to find its restart command. + * + * This can also be used when handling the ::save_state signal, to + * save the current state via an updated command line. (Eg, providing + * a list of filenames to open when the application is resumed.) + **/ +void +egg_sm_client_set_restart_command (EggSMClient  *client, +                                   int           argc, +                                   const char  **argv) +{ +    g_return_if_fail (EGG_IS_SM_CLIENT (client)); + +    if (EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command) +        EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command (client, argc, argv); +} + +/** + * egg_sm_client_will_quit: + * @client: the client + * @will_quit: whether or not the application is willing to quit + * + * This MUST be called in response to the ::quit_requested signal, to + * indicate whether or not the application is willing to quit. The + * application may call it either directly from the signal handler, or + * at some later point (eg, after asynchronously interacting with the + * user). + * + * If the application does not connect to ::quit_requested, + * #EggSMClient will call this method on its behalf (passing %TRUE + * for @will_quit). + * + * After calling this method, the application should wait to receive + * either ::quit_cancelled or ::quit. + **/ +void +egg_sm_client_will_quit (EggSMClient *client, +                         gboolean     will_quit) +{ +    g_return_if_fail (EGG_IS_SM_CLIENT (client)); + +    if (EGG_SM_CLIENT_GET_CLASS (client)->will_quit) +        EGG_SM_CLIENT_GET_CLASS (client)->will_quit (client, will_quit); +} + +/** + * egg_sm_client_end_session: + * @style: a hint at how to end the session + * @request_confirmation: whether or not the user should get a chance + * to confirm the action + * + * Requests that the session manager end the current session. @style + * indicates how the session should be ended, and + * @request_confirmation indicates whether or not the user should be + * given a chance to confirm the logout/reboot/shutdown. Both of these + * flags are merely hints though; the session manager may choose to + * ignore them. + * + * Return value: %TRUE if the request was sent; %FALSE if it could not + * be (eg, because it could not connect to the session manager). + **/ +gboolean +egg_sm_client_end_session (EggSMClientEndStyle  style, +                           gboolean             request_confirmation) +{ +    EggSMClient *client = egg_sm_client_get (); + +    g_return_val_if_fail (EGG_IS_SM_CLIENT (client), FALSE); + +    if (EGG_SM_CLIENT_GET_CLASS (client)->end_session) +    { +        return EGG_SM_CLIENT_GET_CLASS (client)->end_session (client, style, +                request_confirmation); +    } +    else +        return FALSE; +} + +/* Signal-emitting callbacks from platform-specific code */ + +GKeyFile * +egg_sm_client_save_state (EggSMClient *client) +{ +    GKeyFile *state_file; +    char *group; + +    g_return_val_if_fail (client == global_client, NULL); + +    state_file = g_key_file_new (); + +    g_debug ("Emitting save_state"); +    g_signal_emit (client, signals[SAVE_STATE], 0, state_file); +    g_debug ("Done emitting save_state"); + +    group = g_key_file_get_start_group (state_file); +    if (group) +    { +        g_free (group); +        return state_file; +    } +    else +    { +        g_key_file_free (state_file); +        return NULL; +    } +} + +void +egg_sm_client_quit_requested (EggSMClient *client) +{ +    g_return_if_fail (client == global_client); + +    if (!g_signal_has_handler_pending (client, signals[QUIT_REQUESTED], 0, FALSE)) +    { +        g_debug ("Not emitting quit_requested because no one is listening"); +        egg_sm_client_will_quit (client, TRUE); +        return; +    } + +    g_debug ("Emitting quit_requested"); +    g_signal_emit (client, signals[QUIT_REQUESTED], 0); +    g_debug ("Done emitting quit_requested"); +} + +void +egg_sm_client_quit_cancelled (EggSMClient *client) +{ +    g_return_if_fail (client == global_client); + +    g_debug ("Emitting quit_cancelled"); +    g_signal_emit (client, signals[QUIT_CANCELLED], 0); +    g_debug ("Done emitting quit_cancelled"); +} + +void +egg_sm_client_quit (EggSMClient *client) +{ +    g_return_if_fail (client == global_client); + +    g_debug ("Emitting quit"); +    g_signal_emit (client, signals[QUIT], 0); +    g_debug ("Done emitting quit"); + +    /* FIXME: should we just call gtk_main_quit() here? */ +} + +static void +egg_sm_client_debug_handler (const char *log_domain, +                             GLogLevelFlags log_level, +                             const char *message, +                             gpointer user_data) +{ +    static int debug = -1; + +    if (debug < 0) +        debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL); + +    if (debug) +        g_log_default_handler (log_domain, log_level, message, NULL); +} diff --git a/cut-n-paste-code/libegg/eggsmclient.h b/cut-n-paste-code/libegg/eggsmclient.h new file mode 100644 index 00000000..f025bb50 --- /dev/null +++ b/cut-n-paste-code/libegg/eggsmclient.h @@ -0,0 +1,123 @@ +/* eggsmclient.h + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_SM_CLIENT_H__ +#define __EGG_SM_CLIENT_H__ + +#include <glib-object.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define EGG_TYPE_SM_CLIENT            (egg_sm_client_get_type ()) +#define EGG_SM_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT, EggSMClient)) +#define EGG_SM_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT, EggSMClientClass)) +#define EGG_IS_SM_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT)) +#define EGG_IS_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT)) +#define EGG_SM_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT, EggSMClientClass)) + +    typedef struct _EggSMClient        EggSMClient; +    typedef struct _EggSMClientClass   EggSMClientClass; +    typedef struct _EggSMClientPrivate EggSMClientPrivate; + +    typedef enum +    { +        EGG_SM_CLIENT_END_SESSION_DEFAULT, +        EGG_SM_CLIENT_LOGOUT, +        EGG_SM_CLIENT_REBOOT, +        EGG_SM_CLIENT_SHUTDOWN +    } EggSMClientEndStyle; + +    typedef enum +    { +        EGG_SM_CLIENT_MODE_DISABLED, +        EGG_SM_CLIENT_MODE_NO_RESTART, +        EGG_SM_CLIENT_MODE_NORMAL +    } EggSMClientMode; + +    struct _EggSMClient +    { +        GObject parent; + +    }; + +    struct _EggSMClientClass +    { +        GObjectClass parent_class; + +        /* signals */ +        void (*save_state)       (EggSMClient *client, +                                  GKeyFile    *state_file); + +        void (*quit_requested)   (EggSMClient *client); +        void (*quit_cancelled)   (EggSMClient *client); +        void (*quit)             (EggSMClient *client); + +        /* virtual methods */ +        void	   (*startup)             (EggSMClient          *client, +                                           const char           *client_id); +        void	   (*set_restart_command) (EggSMClient          *client, +                                           int                   argc, +                                           const char          **argv); +        void	   (*will_quit)           (EggSMClient          *client, +                                           gboolean              will_quit); +        gboolean (*end_session)         (EggSMClient          *client, +                                         EggSMClientEndStyle   style, +                                         gboolean              request_confirmation); + +        /* Padding for future expansion */ +        void (*_egg_reserved1) (void); +        void (*_egg_reserved2) (void); +        void (*_egg_reserved3) (void); +        void (*_egg_reserved4) (void); +    }; + +    GType            egg_sm_client_get_type            (void) G_GNUC_CONST; + +    GOptionGroup    *egg_sm_client_get_option_group    (void); + +    /* Initialization */ +    void             egg_sm_client_set_mode            (EggSMClientMode mode); +    EggSMClientMode  egg_sm_client_get_mode            (void); +    EggSMClient     *egg_sm_client_get                 (void); + +    /* Resuming a saved session */ +    gboolean         egg_sm_client_is_resumed          (EggSMClient *client); +    GKeyFile        *egg_sm_client_get_state_file      (EggSMClient *client); + +    /* Alternate means of saving state */ +    void             egg_sm_client_set_restart_command (EggSMClient  *client, +            int           argc, +            const char  **argv); + +    /* Handling "quit_requested" signal */ +    void             egg_sm_client_will_quit           (EggSMClient *client, +            gboolean     will_quit); + +    /* Initiate a logout/reboot/shutdown */ +    gboolean         egg_sm_client_end_session         (EggSMClientEndStyle  style, +            gboolean             request_confirmation); + +#ifdef __cplusplus +} +#endif + + +#endif /* __EGG_SM_CLIENT_H__ */ diff --git a/cut-n-paste-code/libegg/eggtreemultidnd.c b/cut-n-paste-code/libegg/eggtreemultidnd.c new file mode 100644 index 00000000..ff62b9dd --- /dev/null +++ b/cut-n-paste-code/libegg/eggtreemultidnd.c @@ -0,0 +1,415 @@ +/* eggtreemultidnd.c + * Copyright (C) 2001  Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; 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 <string.h> +#include <gtk/gtk.h> +#include "eggtreemultidnd.h" + +#define EGG_TREE_MULTI_DND_STRING "EggTreeMultiDndString" + +typedef struct +{ +    guint pressed_button; +    gint x; +    gint y; +    guint motion_notify_handler; +    guint button_release_handler; +    guint drag_data_get_handler; +    GSList *event_list; +} EggTreeMultiDndData; + +/* CUT-N-PASTE from gtktreeview.c */ +typedef struct _TreeViewDragInfo TreeViewDragInfo; +struct _TreeViewDragInfo +{ +    GdkModifierType start_button_mask; +    GtkTargetList *source_target_list; +    GdkDragAction source_actions; + +    GtkTargetList *dest_target_list; + +    guint source_set : 1; +    guint dest_set : 1; +}; + + +GType +egg_tree_multi_drag_source_get_type (void) +{ +    static GType our_type = 0; + +    if (!our_type) +    { +        const GTypeInfo our_info = +        { +            sizeof (EggTreeMultiDragSourceIface), /* class_size */ +            NULL,		/* base_init */ +            NULL,		/* base_finalize */ +            NULL, +            NULL,		/* class_finalize */ +            NULL,		/* class_data */ +            0, +            0,              /* n_preallocs */ +            NULL +        }; + +        our_type = g_type_register_static (G_TYPE_INTERFACE, "EggTreeMultiDragSource", &our_info, 0); +    } + +    return our_type; +} + + +/** + * egg_tree_multi_drag_source_row_draggable: + * @drag_source: a #EggTreeMultiDragSource + * @path: row on which user is initiating a drag + * + * Asks the #EggTreeMultiDragSource whether a particular row can be used as + * the source of a DND operation. If the source doesn't implement + * this interface, the row is assumed draggable. + * + * Return value: %TRUE if the row can be dragged + **/ +gboolean +egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source, +        GList                  *path_list) +{ +    EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source); + +    g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE); +    g_return_val_if_fail (iface->row_draggable != NULL, FALSE); +    g_return_val_if_fail (path_list != NULL, FALSE); + +    if (iface->row_draggable) +        return (* iface->row_draggable) (drag_source, path_list); +    else +        return TRUE; +} + + +/** + * egg_tree_multi_drag_source_drag_data_delete: + * @drag_source: a #EggTreeMultiDragSource + * @path: row that was being dragged + * + * Asks the #EggTreeMultiDragSource to delete the row at @path, because + * it was moved somewhere else via drag-and-drop. Returns %FALSE + * if the deletion fails because @path no longer exists, or for + * some model-specific reason. Should robustly handle a @path no + * longer found in the model! + * + * Return value: %TRUE if the row was successfully deleted + **/ +gboolean +egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source, +        GList                  *path_list) +{ +    EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source); + +    g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE); +    g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE); +    g_return_val_if_fail (path_list != NULL, FALSE); + +    return (* iface->drag_data_delete) (drag_source, path_list); +} + +/** + * egg_tree_multi_drag_source_drag_data_get: + * @drag_source: a #EggTreeMultiDragSource + * @path: row that was dragged + * @selection_data: a #EggSelectionData to fill with data from the dragged row + * + * Asks the #EggTreeMultiDragSource to fill in @selection_data with a + * representation of the row at @path. @selection_data->target gives + * the required type of the data.  Should robustly handle a @path no + * longer found in the model! + * + * Return value: %TRUE if data of the required type was provided + **/ +gboolean +egg_tree_multi_drag_source_drag_data_get    (EggTreeMultiDragSource *drag_source, +        GList                  *path_list, +        GtkSelectionData  *selection_data) +{ +    EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source); + +    g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE); +    g_return_val_if_fail (iface->drag_data_get != NULL, FALSE); +    g_return_val_if_fail (path_list != NULL, FALSE); +    g_return_val_if_fail (selection_data != NULL, FALSE); + +    return (* iface->drag_data_get) (drag_source, path_list, selection_data); +} + +static void +stop_drag_check (GtkWidget *widget) +{ +    EggTreeMultiDndData *priv_data; +    GSList *l; + +    priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING); + +    for (l = priv_data->event_list; l != NULL; l = l->next) +        gdk_event_free (l->data); + +    g_slist_free (priv_data->event_list); +    priv_data->event_list = NULL; +    g_signal_handler_disconnect (widget, priv_data->motion_notify_handler); +    g_signal_handler_disconnect (widget, priv_data->button_release_handler); +} + +static gboolean +egg_tree_multi_drag_button_release_event (GtkWidget      *widget, +        GdkEventButton *event, +        gpointer        data) +{ +    EggTreeMultiDndData *priv_data; +    GSList *l; + +    priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING); + +    for (l = priv_data->event_list; l != NULL; l = l->next) +        gtk_propagate_event (widget, l->data); + +    stop_drag_check (widget); + +    return FALSE; +} + +static void +selection_foreach (GtkTreeModel *model, +                   GtkTreePath  *path, +                   GtkTreeIter  *iter, +                   gpointer      data) +{ +    GList **list_ptr; + +    list_ptr = (GList **) data; + +    *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path)); +} + +static void +path_list_free (GList *path_list) +{ +    g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL); +    g_list_free (path_list); +} + +static void +set_context_data (GdkDragContext *context, +                  GList          *path_list) +{ +    g_object_set_data_full (G_OBJECT (context), +                            "egg-tree-view-multi-source-row", +                            path_list, +                            (GDestroyNotify) path_list_free); +} + +static GList * +get_context_data (GdkDragContext *context) +{ +    return g_object_get_data (G_OBJECT (context), +                              "egg-tree-view-multi-source-row"); +} + +/* CUT-N-PASTE from gtktreeview.c */ +static TreeViewDragInfo* +get_info (GtkTreeView *tree_view) +{ +    return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info"); +} + + +static void +egg_tree_multi_drag_drag_data_get (GtkWidget        *widget, +                                   GdkDragContext   *context, +                                   GtkSelectionData *selection_data, +                                   guint             info, +                                   guint             time) +{ +    GtkTreeView *tree_view; +    GtkTreeModel *model; +    TreeViewDragInfo *di; +    GList *path_list; + +    tree_view = GTK_TREE_VIEW (widget); + +    model = gtk_tree_view_get_model (tree_view); + +    if (model == NULL) +        return; + +    di = get_info (GTK_TREE_VIEW (widget)); + +    if (di == NULL) +        return; + +    path_list = get_context_data (context); + +    if (path_list == NULL) +        return; + +    /* We can implement the GTK_TREE_MODEL_ROW target generically for +     * any model; for DragSource models there are some other targets +     * we also support. +     */ + +    if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model)) +    { +        egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model), +                path_list, +                selection_data); +    } +} + +static gboolean +egg_tree_multi_drag_motion_event (GtkWidget      *widget, +                                  GdkEventMotion *event, +                                  gpointer        data) +{ +    EggTreeMultiDndData *priv_data; + +    priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING); + +    if (gtk_drag_check_threshold (widget, +                                  priv_data->x, +                                  priv_data->y, +                                  event->x, +                                  event->y)) +    { +        GList *path_list = NULL; +        GtkTreeSelection *selection; +        GtkTreeModel *model; +        GdkDragContext *context; +        TreeViewDragInfo *di; + +        di = get_info (GTK_TREE_VIEW (widget)); + +        if (di == NULL) +            return FALSE; + +        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); +        stop_drag_check (widget); +        gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list); +        path_list = g_list_reverse (path_list); +        model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); +        if (egg_tree_multi_drag_source_row_draggable (EGG_TREE_MULTI_DRAG_SOURCE (model), path_list)) +        { + +            context = gtk_drag_begin (widget, +                                      gtk_drag_source_get_target_list (widget), +                                      di->source_actions, +                                      priv_data->pressed_button, +                                      (GdkEvent*)event); +            set_context_data (context, path_list); +            gtk_drag_set_icon_default (context); + +        } +        else +        { +            path_list_free (path_list); +        } +    } + +    return TRUE; +} + +static gboolean +egg_tree_multi_drag_button_press_event (GtkWidget      *widget, +                                        GdkEventButton *event, +                                        gpointer        data) +{ +    GtkTreeView *tree_view; +    GtkTreePath *path = NULL; +    GtkTreeViewColumn *column = NULL; +    gint cell_x, cell_y; +    GtkTreeSelection *selection; +    EggTreeMultiDndData *priv_data; + +    tree_view = GTK_TREE_VIEW (widget); +    priv_data = g_object_get_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING); +    if (priv_data == NULL) +    { +        priv_data = g_new0 (EggTreeMultiDndData, 1); +        g_object_set_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING, priv_data); +    } + +    if (g_slist_find (priv_data->event_list, event)) +        return FALSE; + +    if (priv_data->event_list) +    { +        /* save the event to be propagated in order */ +        priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event)); +        return TRUE; +    } + +    if (event->type == GDK_2BUTTON_PRESS) +        return FALSE; + +    gtk_tree_view_get_path_at_pos (tree_view, +                                   event->x, event->y, +                                   &path, &column, +                                   &cell_x, &cell_y); + +    selection = gtk_tree_view_get_selection (tree_view); + +    if (path && gtk_tree_selection_path_is_selected (selection, path)) +    { +        priv_data->pressed_button = event->button; +        priv_data->x = event->x; +        priv_data->y = event->y; +        priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event)); +        priv_data->motion_notify_handler = +            g_signal_connect (G_OBJECT (tree_view), "motion_notify_event", G_CALLBACK (egg_tree_multi_drag_motion_event), NULL); +        priv_data->button_release_handler = +            g_signal_connect (G_OBJECT (tree_view), "button_release_event", G_CALLBACK (egg_tree_multi_drag_button_release_event), NULL); + +        if (priv_data->drag_data_get_handler == 0) +        { +            priv_data->drag_data_get_handler = +                g_signal_connect (G_OBJECT (tree_view), "drag_data_get", G_CALLBACK (egg_tree_multi_drag_drag_data_get), NULL); +        } + +        gtk_tree_path_free (path); + +        return TRUE; +    } + +    if (path) +    { +        gtk_tree_path_free (path); +    } + +    return FALSE; +} + +void +egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view) +{ +    g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); +    g_signal_connect (G_OBJECT (tree_view), "button_press_event", G_CALLBACK (egg_tree_multi_drag_button_press_event), NULL); +} + diff --git a/cut-n-paste-code/libegg/eggtreemultidnd.h b/cut-n-paste-code/libegg/eggtreemultidnd.h new file mode 100644 index 00000000..f7b98e51 --- /dev/null +++ b/cut-n-paste-code/libegg/eggtreemultidnd.h @@ -0,0 +1,78 @@ +/* eggtreednd.h + * Copyright (C) 2001  Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_TREE_MULTI_DND_H__ +#define __EGG_TREE_MULTI_DND_H__ + +#include <gtk/gtk.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define EGG_TYPE_TREE_MULTI_DRAG_SOURCE            (egg_tree_multi_drag_source_get_type ()) +#define EGG_TREE_MULTI_DRAG_SOURCE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSource)) +#define EGG_IS_TREE_MULTI_DRAG_SOURCE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE)) +#define EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSourceIface)) + +    typedef struct _EggTreeMultiDragSource      EggTreeMultiDragSource; /* Dummy typedef */ +    typedef struct _EggTreeMultiDragSourceIface EggTreeMultiDragSourceIface; + +    struct _EggTreeMultiDragSourceIface +    { +        GTypeInterface g_iface; + +        /* VTable - not signals */ +        gboolean     (* row_draggable)        (EggTreeMultiDragSource   *drag_source, +                                               GList                    *path_list); + +        gboolean     (* drag_data_get)        (EggTreeMultiDragSource   *drag_source, +                                               GList                    *path_list, +                                               GtkSelectionData         *selection_data); + +        gboolean     (* drag_data_delete)     (EggTreeMultiDragSource *drag_source, +                                               GList                  *path_list); +    }; + +    GType    egg_tree_multi_drag_source_get_type         (void) G_GNUC_CONST; + +    /* Returns whether the given row can be dragged */ +    gboolean egg_tree_multi_drag_source_row_draggable    (EggTreeMultiDragSource *drag_source, +            GList                  *path_list); + +    /* Deletes the given row, or returns FALSE if it can't */ +    gboolean egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source, +            GList                  *path_list); + + +    /* Fills in selection_data with type selection_data->target based on the row +     * denoted by path, returns TRUE if it does anything +     */ +    gboolean egg_tree_multi_drag_source_drag_data_get    (EggTreeMultiDragSource *drag_source, +            GList                  *path_list, +            GtkSelectionData       *selection_data); +    void     egg_tree_multi_drag_add_drag_support        (GtkTreeView            *tree_view); + + + +#ifdef __cplusplus +} +#endif + +#endif /* __EGG_TREE_MULTI_DND_H__ */ diff --git a/cut-n-paste-code/libegg/update-from-egg.sh b/cut-n-paste-code/libegg/update-from-egg.sh new file mode 100755 index 00000000..9be68a9b --- /dev/null +++ b/cut-n-paste-code/libegg/update-from-egg.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +function die() { +  echo $* +  exit 1 +} + +if test -z "$EGGDIR"; then +   echo "Must set EGGDIR" +   exit 1 +fi + +if test -z "$EGGFILES"; then +   echo "Must set EGGFILES" +   exit 1 +fi + +for FILE in $EGGFILES; do +  if cmp -s $EGGDIR/$FILE $FILE; then +     echo "File $FILE is unchanged" +  else +     cp $EGGDIR/$FILE $FILE || die "Could not move $EGGDIR/$FILE to $FILE" +     echo "Updated $FILE" +  fi +done  | 
