diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a0f7905 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/gnome-session-3.26.1.tar.xz diff --git a/.gnome-session.metadata b/.gnome-session.metadata new file mode 100644 index 0000000..5f962ac --- /dev/null +++ b/.gnome-session.metadata @@ -0,0 +1 @@ +dcb7aca725d43baec706e72e7256ccc88495bb24 SOURCES/gnome-session-3.26.1.tar.xz diff --git a/README.md b/README.md deleted file mode 100644 index 0e7897f..0000000 --- a/README.md +++ /dev/null @@ -1,5 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 - -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/0001-Revert-Remove-all-references-to-gnome-session-proper.patch b/SOURCES/0001-Revert-Remove-all-references-to-gnome-session-proper.patch new file mode 100644 index 0000000..0552108 --- /dev/null +++ b/SOURCES/0001-Revert-Remove-all-references-to-gnome-session-proper.patch @@ -0,0 +1,2851 @@ +From 8ba34a8831751b0825cabbcfc552e14e3510af2b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 8 Mar 2017 14:08:09 -0500 +Subject: [PATCH 01/19] Revert "Remove all references to + gnome-session-properties" + +This reverts commit 0c6fe6ca14b65cdfc1cd039a5cc9cb83ea346d6b. +--- + configure.ac | 8 + + data/Makefile.am | 5 +- + data/icons/16x16/Makefile.am | 28 ++ + data/icons/16x16/session-properties.png | Bin 0 -> 595 bytes + data/icons/16x16/session-properties.svg | 394 ++++++++++++++++ + data/icons/22x22/Makefile.am | 27 ++ + data/icons/22x22/session-properties.png | Bin 0 -> 754 bytes + data/icons/22x22/session-properties.svg | 440 ++++++++++++++++++ + data/icons/24x24/Makefile.am | 25 + + data/icons/24x24/session-properties.png | Bin 0 -> 784 bytes + data/icons/32x32/Makefile.am | 27 ++ + data/icons/32x32/session-properties.png | Bin 0 -> 1109 bytes + data/icons/32x32/session-properties.svg | 490 ++++++++++++++++++++ + data/icons/48x48/Makefile.am | 25 + + data/icons/48x48/session-properties.png | Bin 0 -> 1839 bytes + data/icons/Makefile.am | 3 + + data/icons/scalable/Makefile.am | 25 + + data/icons/scalable/session-properties.svg | 515 +++++++++++++++++++++ + data/icons/symbolic/Makefile.am | 25 + + .../icons/symbolic/session-properties-symbolic.svg | 28 ++ + data/session-properties.ui | 323 +++++++++++++ + doc/man/gnome-session.1 | 2 + + po/POTFILES.in | 1 + + 23 files changed, 2390 insertions(+), 1 deletion(-) + create mode 100644 data/icons/16x16/Makefile.am + create mode 100644 data/icons/16x16/session-properties.png + create mode 100644 data/icons/16x16/session-properties.svg + create mode 100644 data/icons/22x22/Makefile.am + create mode 100644 data/icons/22x22/session-properties.png + create mode 100644 data/icons/22x22/session-properties.svg + create mode 100644 data/icons/24x24/Makefile.am + create mode 100644 data/icons/24x24/session-properties.png + create mode 100644 data/icons/32x32/Makefile.am + create mode 100644 data/icons/32x32/session-properties.png + create mode 100644 data/icons/32x32/session-properties.svg + create mode 100644 data/icons/48x48/Makefile.am + create mode 100644 data/icons/48x48/session-properties.png + create mode 100644 data/icons/Makefile.am + create mode 100644 data/icons/scalable/Makefile.am + create mode 100644 data/icons/scalable/session-properties.svg + create mode 100644 data/icons/symbolic/Makefile.am + create mode 100644 data/icons/symbolic/session-properties-symbolic.svg + create mode 100644 data/session-properties.ui + +diff --git a/configure.ac b/configure.ac +index ec41462e..f57dcf3d 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -343,60 +343,68 @@ if test $enable_ipv6 = yes; then + dnl ================================================================= + dnl Now we would check for specific function like getaddrinfo. + dnl ================================================================= + have_getaddrinfo=no + if test $have_ipv6=yes; then + AC_CHECK_FUNC(getaddrinfo, have_getaddrinfo=yes) + if test $have_getaddrinfo != yes; then + # getaddrinfo is not in the default libraries. See if it's in some other. + for lib in bsd socket inet; do + AC_CHECK_LIB($lib, getaddrinfo, [LIBS="$LIBS -l$lib";have_getaddrinfo=yes; break]) + done + fi + if test $have_getaddrinfo=yes; then + AC_DEFINE(ENABLE_IPV6, 1, [Define if IPV6 is supported]) + have_full_ipv6=yes + fi + fi + fi + dnl ============================================================================== + dnl End of IPv6 checks + dnl ============================================================================== + + AC_CONFIG_FILES([ + Makefile + doc/Makefile + doc/dbus/Makefile + doc/dbus/gnome-session.xml + doc/man/Makefile + data/Makefile + data/org.gnome.SessionManager.gschema.xml ++data/icons/Makefile ++data/icons/16x16/Makefile ++data/icons/22x22/Makefile ++data/icons/24x24/Makefile ++data/icons/32x32/Makefile ++data/icons/48x48/Makefile ++data/icons/scalable/Makefile ++data/icons/symbolic/Makefile + gnome-session/Makefile + tools/Makefile + po/Makefile.in + ]) + AC_OUTPUT + + dnl --------------------------------------------------------------------------- + dnl - Show summary + dnl --------------------------------------------------------------------------- + + echo " + gnome-session $VERSION + `echo gnome-session $VERSION | sed "s/./=/g"` + + prefix: ${prefix} + exec_prefix: ${exec_prefix} + libdir: ${libdir} + bindir: ${bindir} + sbindir: ${sbindir} + sysconfdir: ${sysconfdir} + localstatedir: ${localstatedir} + datadir: ${datadir} + source code location: ${srcdir} + compiler: ${CC} + cflags: ${CFLAGS} + Maintainer mode: ${USE_MAINTAINER_MODE} + Use *_DISABLE_DEPRECATED: ${enable_deprecation_flags} + + GConf support: ${enable_gconf} + Session tracking: ${session_tracking} +diff --git a/data/Makefile.am b/data/Makefile.am +index d8c45573..413279a2 100644 +--- a/data/Makefile.am ++++ b/data/Makefile.am +@@ -1,32 +1,35 @@ ++SUBDIRS = icons ++ + uidir = $(pkgdatadir) +-ui_DATA = ++ui_DATA = \ ++ session-properties.ui + + if BUILD_SESSION_SELECTOR + ui_DATA += session-selector.ui + endif + + hwcompatdir = $(pkgdatadir) + hwcompat_DATA = hardware-compatibility + + xsessiondir = $(datadir)/xsessions + xsession_in_files = gnome.desktop.in gnome-xorg.desktop.in + + if BUILD_SESSION_SELECTOR + xsession_in_files += gnome-custom-session.desktop.in + endif + + xsession_DATA = $(xsession_in_files:.desktop.in=.desktop) + + wayland_sessiondir = $(datadir)/wayland-sessions + wayland_session_in_files = gnome.desktop.in + wayland_session_DATA = $(wayland_session_in_files:.desktop.in=.desktop) + + sessiondir = $(datadir)/gnome-session/sessions + session_in_in_files = gnome.session.desktop.in.in gnome-dummy.session.desktop.in.in + session_in_files = $(session_in_in_files:.session.desktop.in.in=.session.desktop.in) + session_DATA = $(session_in_files:.session.desktop.in=.session) + + %.session.desktop.in: %.session.desktop.in.in Makefile + $(AM_V_GEN)sed \ + -e "s|\@LIBEXECDIR\@|$(libexecdir)|" \ + $< > $@ +diff --git a/data/icons/16x16/Makefile.am b/data/icons/16x16/Makefile.am +new file mode 100644 +index 00000000..d338f4c3 +--- /dev/null ++++ b/data/icons/16x16/Makefile.am +@@ -0,0 +1,28 @@ ++size = 16x16 ++ ++themedir = $(datadir)/icons/hicolor ++iconsdir = $(themedir)/$(size)/apps ++ ++icons_DATA = session-properties.png ++icons_SOURCE = session-properties.svg ++ ++gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor ++ ++install-data-hook: ++ @-if test -z "$(DESTDIR)"; then \ ++ echo "Updating Gtk icon cache."; \ ++ $(gtk_update_icon_cache); \ ++ else \ ++ echo "*** Icon cache not updated. After install, run this:"; \ ++ echo "*** $(gtk_update_icon_cache)"; \ ++ fi ++ ++uninstall-hook: ++ @rm -f $(DESTDIR)$(themedir)/icon-theme.cache ++ ++ ++EXTRA_DIST = \ ++ $(icons_DATA) \ ++ $(icons_SOURCE) ++ ++-include $(top_srcdir)/git.mk +diff --git a/data/icons/16x16/session-properties.png b/data/icons/16x16/session-properties.png +new file mode 100644 +index 0000000000000000000000000000000000000000..1367c20ae73a8b5e73d98f5dbcb6341cd0dc9ebe +GIT binary patch +literal 595 +zcmV-Z0<8UsP)5liy2|Q5eTR&-=cox8c~2 +zT2`*8E+SB&p~E)2@g|62&LHY4vde`2fUqAIbrnQ(5fMsI5DdE5O+<8)3nGl7Xd(&* +zq1IM`>7Zrq{n5poPS>=at8;js&-a|~^PD4B?+lHE1HU`?hY%420RcqF7tApudZ4MJ +zE8c~s=~abwC6-r?>+t&Bt7c<7Zxa4AL0x8@n8=B3Jba!bXKlr@-Brn-Gw*5G6{!mw +zx-k6mv!wxSGPJ6c%lz0AW=9{ddwPY@08FFo-*qhJvS~b}{D#-BFh)hqDJ?Mn;Sqax +z1UY%4pUYP+GX3;gE$H#$@o&f6A6pS{7YqD&bD!wJR(gAqhzK`t+(Oedlu{Umfl>;k +z6vbkZ$&Zr^-W$Se?b9@IZQiD@6K)K0JlTuXpCJG~pO5yocFvyeBealJnkzc71Mt88Ig(;l`&`*@Z~@%7txx)TWoE?xL5*x>ck8IN-~-nr5P)6mg9 +zK7!FRjAql!4BiEV-A4y%1ngf0M#hJkno0pnV_cr8VG@X(V$SseZVe2edwlqtk5MQt +z;yTunfU0^(A_Q7`2)1_DZ-59bhmMpjh+r#S^hDdv)kSOfYppnq%x;cY66;Uhwo3Ln +haR2i^x#&uN@CTAjj`{aKg8l#i002ovPDHLkV1gZB6^{S_ + +literal 0 +HcmV?d00001 + +diff --git a/data/icons/16x16/session-properties.svg b/data/icons/16x16/session-properties.svg +new file mode 100644 +index 00000000..4f7c37f2 +--- /dev/null ++++ b/data/icons/16x16/session-properties.svg +@@ -0,0 +1,394 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ image/svg+xml ++ ++ ++ ++ Jakub Steiner ++ ++ ++ http://jimmac.musichall.cz ++ ++ Gnome Session Properties ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/data/icons/22x22/Makefile.am b/data/icons/22x22/Makefile.am +new file mode 100644 +index 00000000..ae4931c2 +--- /dev/null ++++ b/data/icons/22x22/Makefile.am +@@ -0,0 +1,27 @@ ++size = 22x22 ++ ++themedir = $(datadir)/icons/hicolor ++iconsdir = $(themedir)/$(size)/apps ++ ++icons_DATA = session-properties.png ++icons_SOURCE = session-properties.svg ++ ++gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor ++ ++install-data-hook: ++ @-if test -z "$(DESTDIR)"; then \ ++ echo "Updating Gtk icon cache."; \ ++ $(gtk_update_icon_cache); \ ++ else \ ++ echo "*** Icon cache not updated. After install, run this:"; \ ++ echo "*** $(gtk_update_icon_cache)"; \ ++ fi ++ ++uninstall-hook: ++ @rm -f $(DESTDIR)$(themedir)/icon-theme.cache ++ ++EXTRA_DIST = \ ++ $(icons_DATA) \ ++ $(icons_SOURCE) ++ ++-include $(top_srcdir)/git.mk +diff --git a/data/icons/22x22/session-properties.png b/data/icons/22x22/session-properties.png +new file mode 100644 +index 0000000000000000000000000000000000000000..12225ee3c2abb7f1a4be7bb77743514cde5d08fe +GIT binary patch +literal 754 +zcmV*lgmp~Q5?rV=iJ8}9cNN| +z*^7v%7U@C>t-#cZ78aG(u1!U>>3?Wdi-=qVy&!035MdZp6cK`;6=)9%L}*o1+QXS< +zrhDh!x%ag2IgaI2wCD>54nNNCbAIQ0en0p}5&)+zUwyJH?$rySe=k`GKp@08Dn#(^ +zvwipEi6gtkmW~@G9owqD*VWWd-ZLw?sM;o^8GQX*Z?0{SrZk|A?`$gdTK +zaDO0~126$@_KpOjxkYBE3$4&W4q=!@f;D|lKLr4CGH98FJQi}5Oa^mqgu~>{)EJidMMJYuj62Y=848xeJ +z7{D+L1iH`)Q^hH(Zs*Q}TLfW9+rIrAJ$4v9M}N;&M3t~^?Yf_TUfIGGi0XFkJ?vs^ +zYz(b6)`qru!Jd<3_)R}Lm(2&;GX^ADxg2T^@_4WdfT}ec=LG{SSQ+D0UpJ69dHDkp +zt*9bia{#F-ih=@c+|kzyj@OzrDW#WF~*C6j%({ +kfc1x54{(9eiT!`39~bB4p9yeF3;+NC07*qoM6N<$f(k-YiU0rr + +literal 0 +HcmV?d00001 + +diff --git a/data/icons/22x22/session-properties.svg b/data/icons/22x22/session-properties.svg +new file mode 100644 +index 00000000..1d0afda2 +--- /dev/null ++++ b/data/icons/22x22/session-properties.svg +@@ -0,0 +1,440 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ image/svg+xml ++ ++ ++ ++ Jakub Steiner ++ ++ ++ http://jimmac.musichall.cz ++ ++ Gnome Session Properties ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/data/icons/24x24/Makefile.am b/data/icons/24x24/Makefile.am +new file mode 100644 +index 00000000..8c28888a +--- /dev/null ++++ b/data/icons/24x24/Makefile.am +@@ -0,0 +1,25 @@ ++size = 24x24 ++ ++themedir = $(datadir)/icons/hicolor ++iconsdir = $(themedir)/$(size)/apps ++ ++icons_DATA = session-properties.png ++ ++gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor ++ ++install-data-hook: ++ @-if test -z "$(DESTDIR)"; then \ ++ echo "Updating Gtk icon cache."; \ ++ $(gtk_update_icon_cache); \ ++ else \ ++ echo "*** Icon cache not updated. After install, run this:"; \ ++ echo "*** $(gtk_update_icon_cache)"; \ ++ fi ++ ++uninstall-hook: ++ @rm -f $(DESTDIR)$(themedir)/icon-theme.cache ++ ++EXTRA_DIST = \ ++ $(icons_DATA) ++ ++-include $(top_srcdir)/git.mk +diff --git a/data/icons/24x24/session-properties.png b/data/icons/24x24/session-properties.png +new file mode 100644 +index 0000000000000000000000000000000000000000..ac81c7ad3b604a056ed02c86bcb2db97c78f4aed +GIT binary patch +literal 784 +zcmV+r1MmEaP)KPZr(RZ;%TnP#T<=Dm6Ew$M?>9B0Z^(18c%aCzU)J?GqWFZ}B! +zvd+01w_dJ}IQ2rPKNAoF5C}0P=Og$s9(Z*2%!z|DfSs4`mR#Ob^|P+FelBy4NYVQQ +z6oVh$s2$rHGyns@86wbI>11TdkWpA{ar1q6G7!&XVmBh%Gb;>V{azyFWe~bi +zBtX&kYRt>V+T2Cc5DSRAjJ&vAbOxr8Bbf^b%v)3$huG^oxQUUX0fEH;ral|`u1##P +zlg5T^f7An0&#i*7P!%&3;P2j#V`BrK2a2i-_1 +ziAJMDA`!yjFyU~RP$-0yl3*~1X`1M|K9d1}uImU?{t_87LTODq51)47`95u}M>u)v +zI7$`1LV~h{>P^*!n_RklEuyBK$ItFFIXQ_^3Ufm23ZXCU1ygSN$Jd)cYW_>H3|I*Svg!SWIe-m}PW%6@u>Ar9X6B0{qPS!L +O0000jOCUOrWQ}?uexLlr3kGN|uWD_;#<|u5RK) +zY+`Mgak=WxnI0e^pt9nCR9kxlwNy<@njb{gPyyI%>YIck8xnvI;QB91>{F*GDcX}0 +z$O=7X^4Y)wNdc3yI{oAS4tWAH_omhWkdh{;@Y3f^@8I$|jizODC^81o)xt8D1ehy5 +zUbp}NqN_AFHy6a;+uIAJ+5J2P_+$w%lN)C)4`SPPPQ*e8&b;$_&O#Gh=>i<8Y};ny +z!CYPfa`jE;N*#fmGFq0!x7}~;nj{{NqbQ0iCRaGQcC3JDn(Txdzuv&MEgD~KO4`yp +z=5BCuB@e@wRsqvAJ;$xvx9Gcmjg^%Zmpz{f_`E70OR0-tSr&$2WCRZ19VQgI%iAYU +zp=p}SN0xJMWKJ&6Mznnd&T;3?ZAM0h>FoG``e*Ais+7+q+~GIOJc4b6LL-cgkJ8%G +zj;`yht*`ULkKgn2;6Tzx2$86Wc$mq!l{y|Xk<<#-kH3~Fz^Te9V01LZ#JvexTiVey +zjc7DV@NkeTmp*rM2;rL3*N~{}`1lx~T>2D6ys~=*48ve_EX3q*leC_0L(?>LT?b%! +zdD)d%k|bnV_8i-LLI{Kq)YR1Sazg{l!w=E~I4*{5+pL6F=>O_V`oH*^SS*$#|EDkv +zgFs+EMV6Kj?^OYS;Nc)APn@Eovkgg-C@syQ$>)f~lPz&+Gwy(0RL0e-R{@AbA}lN{ +z(%IF{xw9VvpzAv2<>js}=t<(ym~7joxVYG5%MdW{Z%FNygZktuQ}>5x@9Nk9> +z%JlRU-4`z)$qo_1HF-i&6l!bh2>AE81SH;OvUg~SKfuA~nwYsiL`PRA7ccZsRu&*E +zsxXTW?n38{#fOW?+6X9R0Kx}Ug1bBIBx3saA>M0wpQgq}Dr=774`zRSIAIzwRv*t3 +z|6`t*t^rg?fFE|QfKx>E^G(cXDt&#|Fr$&Y$N^+u5#`l&)EzyxZSwm7NnnTP9DXP(4sABZyPdr@{r2fTh{@!?W=uHM@4}{@6;R807W=7gCe`9}SA(iEpq7oiO +z;O1aK0wgSLd3LN7*c3o9Pyqx06|jLP3d)=vn_F`P2m^mP4_*aI00qbIxyOhD2C(4} +b^WWlc-6EIuR>CZG00000NkvXXu0mjfn7$9& + +literal 0 +HcmV?d00001 + +diff --git a/data/icons/32x32/session-properties.svg b/data/icons/32x32/session-properties.svg +new file mode 100644 +index 00000000..5bb14559 +--- /dev/null ++++ b/data/icons/32x32/session-properties.svg +@@ -0,0 +1,490 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ image/svg+xml ++ ++ ++ ++ Jakub Steiner ++ ++ ++ http://jimmac.musichall.cz ++ ++ Gnome Session Properties ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/data/icons/48x48/Makefile.am b/data/icons/48x48/Makefile.am +new file mode 100644 +index 00000000..13b0cf36 +--- /dev/null ++++ b/data/icons/48x48/Makefile.am +@@ -0,0 +1,25 @@ ++size = 48x48 ++ ++themedir = $(datadir)/icons/hicolor ++iconsdir = $(themedir)/$(size)/apps ++ ++icons_DATA = session-properties.png ++ ++gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor ++ ++install-data-hook: ++ @-if test -z "$(DESTDIR)"; then \ ++ echo "Updating Gtk icon cache."; \ ++ $(gtk_update_icon_cache); \ ++ else \ ++ echo "*** Icon cache not updated. After install, run this:"; \ ++ echo "*** $(gtk_update_icon_cache)"; \ ++ fi ++ ++uninstall-hook: ++ @rm -f $(DESTDIR)$(themedir)/icon-theme.cache ++ ++EXTRA_DIST = \ ++ $(icons_DATA) ++ ++-include $(top_srcdir)/git.mk +diff --git a/data/icons/48x48/session-properties.png b/data/icons/48x48/session-properties.png +new file mode 100644 +index 0000000000000000000000000000000000000000..32c2358ca45a07e86fbb03d75ec4742e8203091a +GIT binary patch +literal 1839 +zcmV+~2hjM5P)o(6r8k8?YI(DKwnU$ +zscQXx?!c!0iv$|d3jp#tKO3Mop|HfdwiG^%(Bv{YDXBhor03QDNj@<7u-QR}Ec +zVmpa1^{zMG*>esLv%B8Q?96WL#>pp*>^W!7Io~(`|9394!WhG?47u3^){9$SfL;3M +z;O`G6Kbf~)=uVkOY^kjqNkSk%G`=sEP%uKzEQ;faSmfJ3{?2z7p+dmQlJnw@ZOKQr +zboJpxqVWlr$ygr&M18@s@xn<$<*n9uUyaL=`79fO>e|+r^U%h+#PIN(` +zwf+>yR5$Xm?ILu%@)` +zg-9uhwY8C)y(IAbTNYs6I=^u3t{u6y{{F2p;AU7h32cP;`LSo46HJUh3X +zZvRKyeM6UVA~)ycly^06ou6a!>?~9YFvjRFfA4pNv(t*do?n2p}$TPH<;fLe87u(KE0yLT^AZk*}0*6iQE9~ua-f+8S=muIbb +zKuXDzN8a3!v=hKjAG<$fyz2Y<2(*wwB~1TszO@GHKyV5P(ej%##xQ+xo{czG2;ub? +zgg_HZ0h-p#X^i33rygsKX-I!Tu)<8NP4nj5w{IWDn2OEc`Vlr}pa(1|D5$lL!p!O5 +zy)mU!xlAdAWnKSX%70_GhCmMpArNAXa+o>qh*K2-r4)yreTE-A{-c&iS0Xfp!aPvf +zQJ6XJ*s&%+DaCV#pXKd$-d; +zc<331hITPJGFl!ypU)GCL;~9e^%;cPX|yH+Ysbd02)M4x^o5TY92{hHWRxpcu9OGg +zv> +zGm9Vj@rlC>4i1&yeCfI60Gw@=7%GY&M(a-23O4IPyFnW-kDJ4Fm{LmqNAV +zCw@C#&iP960GUjNOeW(?Qc4+k`8`uYJRWEJz;=H4{l|Iw(92OLZN^Vf-l|sMS2S#Ac~=D$^%L%j=bXszk(?L(*xc--Ck6e>=n +zl^#$^h0YE)3XbD|R#5yxR+B>h3z-mtN5An+&YV5NYsX)ur>AFK8@7!^{a&bniF3Ie +zU-*O4LoGjaFjEap>&qEQ*){>O~|H2~w$43tzIEeYwy^1YvkR4T>KfB6J6GnYciHVfOf8Q6X&0|TF9;P%hf*H_+DuF^^=(&=>7 +z!|8OIbUIBknIw@&aO%`wszeAOn3=i6gAa_<4WzYZYHEu4)Noy0Fbr2N`t)hvwsyxhoJgWY +z`UXs#LLtpU_5vr~II&h$E1-1RnTzd&*v^1n4Q}T(Ru|^T&d+dc@&q9G+QScf^>;st +zW!3LSEug=yG#FYd3hpY`vPh9&^)K~^yN(mwIwh+PwqRX?>FTU}&-d}zFft|5?ZtrNRV`B{Y)vYWpTt#pXUA=wS +z_Vt~}f{@9%96$4cTFERP1EN6G#`_qjKAd{$6)Au6nUDT^`CA&RtM%>;Mk|cgNISxY +zrcn_By^>qJ^rw$cjZXjtU{x4n5JE(NE}*@5C4e@-#S_@tI4yr3ZU_ye0R`lNERX?~ +zfLUXVCjcnzuiNkhwBdzF7aMLSl0_aao`5A_#T1DPzio0+j07;oY}#(E$iu>ms5U-i +deFfY~_&?#hc%)mzygmQ`002ovPDHLkV1i5*a;5+P + +literal 0 +HcmV?d00001 + +diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am +new file mode 100644 +index 00000000..8b6c5491 +--- /dev/null ++++ b/data/icons/Makefile.am +@@ -0,0 +1,3 @@ ++SUBDIRS = 16x16 22x22 24x24 32x32 48x48 scalable symbolic ++ ++-include $(top_srcdir)/git.mk +diff --git a/data/icons/scalable/Makefile.am b/data/icons/scalable/Makefile.am +new file mode 100644 +index 00000000..4ff67b7b +--- /dev/null ++++ b/data/icons/scalable/Makefile.am +@@ -0,0 +1,25 @@ ++size = scalable ++ ++themedir = $(datadir)/icons/hicolor ++iconsdir = $(themedir)/$(size)/apps ++ ++icons_DATA = session-properties.svg ++ ++gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor ++ ++install-data-hook: ++ @-if test -z "$(DESTDIR)"; then \ ++ echo "Updating Gtk icon cache."; \ ++ $(gtk_update_icon_cache); \ ++ else \ ++ echo "*** Icon cache not updated. After install, run this:"; \ ++ echo "*** $(gtk_update_icon_cache)"; \ ++ fi ++ ++uninstall-hook: ++ @rm -f $(DESTDIR)$(themedir)/icon-theme.cache ++ ++EXTRA_DIST = \ ++ $(icons_DATA) ++ ++-include $(top_srcdir)/git.mk +diff --git a/data/icons/scalable/session-properties.svg b/data/icons/scalable/session-properties.svg +new file mode 100644 +index 00000000..cec2c39e +--- /dev/null ++++ b/data/icons/scalable/session-properties.svg +@@ -0,0 +1,515 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ image/svg+xml ++ ++ ++ ++ Jakub Steiner ++ ++ ++ http://jimmac.musichall.cz ++ ++ Gnome Session Properties ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/data/icons/symbolic/Makefile.am b/data/icons/symbolic/Makefile.am +new file mode 100644 +index 00000000..670d0a5a +--- /dev/null ++++ b/data/icons/symbolic/Makefile.am +@@ -0,0 +1,25 @@ ++size = symbolic ++ ++themedir = $(datadir)/icons/hicolor ++iconsdir = $(themedir)/$(size)/apps ++ ++icons_DATA = session-properties-symbolic.svg ++ ++gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor ++ ++install-data-hook: ++ @-if test -z "$(DESTDIR)"; then \ ++ echo "Updating Gtk icon cache."; \ ++ $(gtk_update_icon_cache); \ ++ else \ ++ echo "*** Icon cache not updated. After install, run this:"; \ ++ echo "*** $(gtk_update_icon_cache)"; \ ++ fi ++ ++uninstall-hook: ++ @rm -f $(DESTDIR)$(themedir)/icon-theme.cache ++ ++EXTRA_DIST = \ ++ $(icons_DATA) ++ ++-include $(top_srcdir)/git.mk +diff --git a/data/icons/symbolic/session-properties-symbolic.svg b/data/icons/symbolic/session-properties-symbolic.svg +new file mode 100644 +index 00000000..e628ecd1 +--- /dev/null ++++ b/data/icons/symbolic/session-properties-symbolic.svg +@@ -0,0 +1,28 @@ ++ ++ ++ ++ ++ ++ ++ ++ image/svg+xml ++ ++ Gnome Symbolic Icon Theme ++ ++ ++ ++ ++ ++ ++ Gnome Symbolic Icon Theme ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/data/session-properties.ui b/data/session-properties.ui +new file mode 100644 +index 00000000..1f0cb9a5 +--- /dev/null ++++ b/data/session-properties.ui +@@ -0,0 +1,323 @@ ++ ++ ++ ++ ++ ++ True ++ True ++ 6 ++ ++ ++ True ++ 12 ++ vertical ++ 3 ++ ++ ++ True ++ 0 ++ 3 ++ 3 ++ Additional startup _programs: ++ True ++ session_properties_treeview ++ ++ ++ False ++ 0 ++ ++ ++ ++ ++ True ++ 6 ++ ++ ++ True ++ True ++ never ++ automatic ++ etched-in ++ ++ ++ 210 ++ True ++ True ++ ++ ++ ++ ++ 0 ++ ++ ++ ++ ++ True ++ 6 ++ start ++ ++ ++ gtk-add ++ True ++ True ++ True ++ True ++ ++ ++ False ++ False ++ 0 ++ ++ ++ ++ ++ gtk-remove ++ True ++ False ++ True ++ True ++ True ++ ++ ++ False ++ False ++ 1 ++ ++ ++ ++ ++ gtk-edit ++ True ++ False ++ True ++ True ++ True ++ ++ ++ False ++ False ++ 2 ++ ++ ++ ++ ++ False ++ False ++ 1 ++ ++ ++ ++ ++ 1 ++ ++ ++ ++ ++ ++ ++ True ++ Startup Programs ++ ++ ++ False ++ ++ ++ ++ ++ True ++ 12 ++ vertical ++ 6 ++ ++ ++ _Automatically remember running applications when logging out ++ True ++ True ++ False ++ True ++ True ++ ++ ++ False ++ False ++ 0 ++ ++ ++ ++ ++ True ++ ++ ++ True ++ True ++ ++ ++ True ++ 4 ++ ++ ++ True ++ gtk-save ++ ++ ++ False ++ False ++ 0 ++ ++ ++ ++ ++ True ++ _Remember Currently Running Applications ++ True ++ ++ ++ 1 ++ ++ ++ ++ ++ ++ ++ False ++ False ++ 0 ++ ++ ++ ++ ++ False ++ False ++ 1 ++ ++ ++ ++ ++ 1 ++ ++ ++ ++ ++ True ++ Options ++ ++ ++ 1 ++ False ++ ++ ++ ++ ++ True ++ 6 ++ 3 ++ 2 ++ 12 ++ 6 ++ ++ ++ True ++ 12 ++ ++ ++ True ++ True ++ ++ ++ ++ 0 ++ ++ ++ ++ ++ Browse… ++ True ++ True ++ True ++ ++ ++ False ++ False ++ 1 ++ ++ ++ ++ ++ 1 ++ 2 ++ 1 ++ 2 ++ GTK_FILL ++ ++ ++ ++ ++ True ++ True ++ ++ ++ ++ 1 ++ 2 ++ 2 ++ 3 ++ GTK_FILL ++ ++ ++ ++ ++ True ++ True ++ ++ ++ ++ 1 ++ 2 ++ GTK_FILL ++ ++ ++ ++ ++ True ++ 0 ++ Comm_ent: ++ True ++ label2 ++ ++ ++ 2 ++ 3 ++ GTK_FILL ++ GTK_FILL ++ ++ ++ ++ ++ True ++ 0 ++ Co_mmand: ++ True ++ session_properties_command_entry ++ ++ ++ 1 ++ 2 ++ GTK_FILL ++ GTK_FILL ++ ++ ++ ++ ++ True ++ 0 ++ _Name: ++ True ++ session_properties_name_entry ++ ++ ++ GTK_FILL ++ GTK_FILL ++ ++ ++ ++ +diff --git a/doc/man/gnome-session.1 b/doc/man/gnome-session.1 +index bf9cf808..35c062c5 100644 +--- a/doc/man/gnome-session.1 ++++ b/doc/man/gnome-session.1 +@@ -67,46 +67,48 @@ Name=GNOME + RequiredComponents=gnome-shell;gnome-settings-daemon; + .in + .fi + .PP + The \fB.session\fP files are looked for in + \fB$XDG_CONFIG_HOME/gnome-session/sessions\fP, + \fB$XDG_CONFIG_DIRS/gnome-session/sessions\fP and + \fB$XDG_DATA_DIRS/gnome-session/sessions\fP. + .SH ENVIRONMENT + \fIgnome-session\fP sets several environment variables for the use of + its child processes: + .PP + .B SESSION_MANAGER + .IP + This variable is used by session-manager aware clients to contact + gnome-session. + .PP + .B DISPLAY + .IP + This variable is set to the X display being used by + \fIgnome-session\fP. Note that if the \fI--display\fP option is used + this might be different from the setting of the environment variable + when gnome-session is invoked. + .SH FILES + .PP + .B $XDG_CONFIG_HOME/config/autostart + .B $XDG_CONFIG_DIRS/config/autostart + .B /usr/share/gnome/autostart + .IP + The applications defined in those directories will be started on login. ++\fIgnome-session-properties(1)\fP can be used to easily configure them. + .PP + .B $XDG_CONFIG_HOME/gnome-session/sessions + .B $XDG_CONFIG_DIRS/gnome-session/sessions + .B $XDG_DATA_DIRS/gnome-session/sessions + .IP + These directories contain the \fB.session\fP files that can be used + with the \fI--session\fP option. + .PP + .B $XDG_CONFIG_HOME/gnome-session/saved-session + .IP + This directory contains the list of applications of the saved session. + .SH BUGS + If you find bugs in the \fIgnome-session\fP program, please report + these on https://bugzilla.gnome.org. + .SH SEE ALSO ++.BR gnome-session-properties(1) + .BR gnome-session-quit(1) +diff --git a/po/POTFILES.in b/po/POTFILES.in +index cc1170aa..76882645 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -1,18 +1,19 @@ + # List of source files containing translatable strings. + # Please keep this file sorted alphabetically. + data/gnome-custom-session.desktop.in + data/gnome.desktop.in + data/gnome-xorg.desktop.in + data/gnome-dummy.session.desktop.in.in + data/gnome.session.desktop.in.in + [type: gettext/glade]data/session-selector.ui ++[type: gettext/glade]data/session-properties.ui + gnome-session/gsm-fail-whale-dialog.c + gnome-session/gsm-manager.c + gnome-session/gsm-process-helper.c + gnome-session/gsm-util.c + gnome-session/gsm-xsmp-client.c + gnome-session/gsm-xsmp-server.c + gnome-session/main.c + tools/gnome-session-inhibit.c + tools/gnome-session-selector.c + tools/gnome-session-quit.c +-- +2.14.2 + diff --git a/SOURCES/0001-main-don-t-call-into-gdbus-before-setting-all-enviro.patch b/SOURCES/0001-main-don-t-call-into-gdbus-before-setting-all-enviro.patch new file mode 100644 index 0000000..8c7dcf1 --- /dev/null +++ b/SOURCES/0001-main-don-t-call-into-gdbus-before-setting-all-enviro.patch @@ -0,0 +1,119 @@ +From 2c0087930a188684e61e71d5b5459e4363471196 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Wed, 14 Feb 2018 09:50:56 -0500 +Subject: [PATCH] main: don't call into gdbus before setting all environment + variables + +setenv () is not multi-thread safe so we need to avoid gsm_util_setenv +calls (which fire off the glib worker thread) before we finish +doing all our setenv() work. +--- + gnome-session/main.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/gnome-session/main.c b/gnome-session/main.c +index e2c3efe..e7a1614 100644 +--- a/gnome-session/main.c ++++ b/gnome-session/main.c +@@ -244,91 +244,96 @@ initialize_gio (void) + disable_fuse = g_strdup (g_getenv ("GVFS_DISABLE_FUSE")); + use_vfs = g_strdup (g_getenv ("GIO_USE_VFS")); + + g_setenv ("GVFS_DISABLE_FUSE", "1", TRUE); + g_setenv ("GIO_USE_VFS", "local", TRUE); + g_vfs_get_default (); + + if (use_vfs) { + g_setenv ("GIO_USE_VFS", use_vfs, TRUE); + g_free (use_vfs); + } else { + g_unsetenv ("GIO_USE_VFS"); + } + + if (disable_fuse) { + g_setenv ("GVFS_DISABLE_FUSE", disable_fuse, TRUE); + g_free (disable_fuse); + } else { + g_unsetenv ("GVFS_DISABLE_FUSE"); + } + } + + int + main (int argc, char **argv) + { + GError *error = NULL; + static char **override_autostart_dirs = NULL; + static char *opt_session_name = NULL; + const char *debug_string = NULL; + gboolean gl_failed = FALSE; ++ gboolean needs_current_desktop_setenv = FALSE; + guint name_owner_id; + GOptionContext *options; + static GOptionEntry entries[] = { + { "autostart", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &override_autostart_dirs, N_("Override standard autostart directories"), N_("AUTOSTART_DIR") }, + { "session", 0, 0, G_OPTION_ARG_STRING, &opt_session_name, N_("Session to use"), N_("SESSION_NAME") }, + { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable debugging code"), NULL }, + { "failsafe", 'f', 0, G_OPTION_ARG_NONE, &failsafe, N_("Do not load user-specified applications"), NULL }, + { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL }, + /* Translators: the 'fail whale' is the black dialog we show when something goes seriously wrong */ + { "whale", 0, 0, G_OPTION_ARG_NONE, &please_fail, N_("Show the fail whale dialog for testing"), NULL }, + { "disable-acceleration-check", 0, 0, G_OPTION_ARG_NONE, &disable_acceleration_check, N_("Disable hardware acceleration check"), NULL }, + { NULL, 0, 0, 0, NULL, NULL, NULL } + }; + + /* Make sure that we have a session bus */ + if (!require_dbus_session (argc, argv, &error)) { + gsm_util_init_error (TRUE, "%s", error->message); + } + + /* From 3.14 GDM sets XDG_CURRENT_DESKTOP. For compatibility with + * older versions of GDM, other display managers, and startx, + * set a fallback value if we don't find it set. + */ + if (g_getenv ("XDG_CURRENT_DESKTOP") == NULL) { +- g_setenv("XDG_CURRENT_DESKTOP", "GNOME", TRUE); +- gsm_util_setenv ("XDG_CURRENT_DESKTOP", "GNOME"); ++ g_setenv ("XDG_CURRENT_DESKTOP", "GNOME", TRUE); ++ needs_current_desktop_setenv = TRUE; + } + + /* Make sure we initialize gio in a way that does not autostart any daemon */ + initialize_gio (); + ++ if (needs_current_desktop_setenv) { ++ gsm_util_setenv ("XDG_CURRENT_DESKTOP", "GNOME"); ++ } ++ + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + debug_string = g_getenv ("GNOME_SESSION_DEBUG"); + if (debug_string != NULL) { + debug = rpmatch (debug_string) == TRUE || atoi (debug_string) == 1; + } + + error = NULL; + options = g_option_context_new (_(" — the GNOME session manager")); + g_option_context_add_main_entries (options, entries, GETTEXT_PACKAGE); + g_option_context_parse (options, &argc, &argv, &error); + if (error != NULL) { + g_warning ("%s", error->message); + exit (1); + } + + g_option_context_free (options); + + /* Rebind stdout/stderr to the journal explicitly, so that + * journald picks ups the nicer "gnome-session" as the program + * name instead of whatever shell script GDM happened to use. + */ + #ifdef HAVE_SYSTEMD + if (!debug) { + int journalfd; + + journalfd = sd_journal_stream_fd (PACKAGE, LOG_INFO, 0); +-- +2.14.3 + diff --git a/SOURCES/0001-save-make-sure-app-state-is-written-into-desktop-fil.patch b/SOURCES/0001-save-make-sure-app-state-is-written-into-desktop-fil.patch new file mode 100644 index 0000000..c23dc01 --- /dev/null +++ b/SOURCES/0001-save-make-sure-app-state-is-written-into-desktop-fil.patch @@ -0,0 +1,1186 @@ +From 5e8c5967d65f61a58241c6429eb79650870fa7d0 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 27 Nov 2017 15:40:54 -0500 +Subject: [PATCH 1/2] save: make sure app state is written into desktop file + +There are a number of important bits of app state written into +an applications desktop file that needs to be restored when +the session is saved. For instance, the phase in which the client +should get started. + +That state is currently stored on the GsmApp object, which is +inaccessible, from the client save function. + +This commit adds the neccesary plumbing to route the GsmApp object +associated with a client to the client save function, and also +adds code to allow the app object to augment the client keyfile at +save time. + +https://bugzilla.gnome.org/show_bug.cgi?id=790913 + +https://bugzilla.redhat.com/show_bug.cgi?id=1529175 +--- + gnome-session/gsm-app.c | 9 +++++ + gnome-session/gsm-app.h | 8 +++++ + gnome-session/gsm-autostart-app.c | 70 +++++++++++++++++++++++++++++++++++++++ + gnome-session/gsm-client.c | 3 +- + gnome-session/gsm-client.h | 3 ++ + gnome-session/gsm-dbus-client.c | 1 + + gnome-session/gsm-manager.c | 4 +-- + gnome-session/gsm-session-save.c | 41 +++++++++++++++++------ + gnome-session/gsm-session-save.h | 1 + + gnome-session/gsm-xsmp-client.c | 9 +++++ + 10 files changed, 135 insertions(+), 14 deletions(-) + +diff --git a/gnome-session/gsm-app.c b/gnome-session/gsm-app.c +index 845e067a..d1ef89a8 100644 +--- a/gnome-session/gsm-app.c ++++ b/gnome-session/gsm-app.c +@@ -531,30 +531,39 @@ gsm_app_exited (GsmApp *app, + } + + void + gsm_app_died (GsmApp *app, + int signal) + { + g_return_if_fail (GSM_IS_APP (app)); + + g_signal_emit (app, signals[DIED], 0, signal); + } + + gboolean + gsm_app_get_registered (GsmApp *app) + { + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + + return app->priv->registered; + } + + void + gsm_app_set_registered (GsmApp *app, + gboolean registered) + { + g_return_if_fail (GSM_IS_APP (app)); + + if (app->priv->registered != registered) { + app->priv->registered = registered; + g_object_notify (G_OBJECT (app), "registered"); + } + } ++ ++gboolean ++gsm_app_save_to_keyfile (GsmApp *app, ++ GKeyFile *keyfile, ++ GError **error) ++{ ++ g_debug ("Saving app: %s", app->priv->id); ++ return GSM_APP_GET_CLASS (app)->impl_save_to_keyfile (app, keyfile, error); ++} +diff --git a/gnome-session/gsm-app.h b/gnome-session/gsm-app.h +index 14a9f94b..f38b3be4 100644 +--- a/gnome-session/gsm-app.h ++++ b/gnome-session/gsm-app.h +@@ -47,80 +47,88 @@ struct _GsmApp + }; + + struct _GsmAppClass + { + GObjectClass parent_class; + + /* signals */ + void (*exited) (GsmApp *app, + guchar exit_code); + void (*died) (GsmApp *app, + int signal); + + /* virtual methods */ + gboolean (*impl_start) (GsmApp *app, + GError **error); + gboolean (*impl_restart) (GsmApp *app, + GError **error); + gboolean (*impl_stop) (GsmApp *app, + GError **error); + gboolean (*impl_provides) (GsmApp *app, + const char *service); + char ** (*impl_get_provides) (GsmApp *app); + gboolean (*impl_has_autostart_condition) (GsmApp *app, + const char *service); + gboolean (*impl_is_running) (GsmApp *app); + + gboolean (*impl_get_autorestart) (GsmApp *app); + const char *(*impl_get_app_id) (GsmApp *app); + gboolean (*impl_is_disabled) (GsmApp *app); + gboolean (*impl_is_conditionally_disabled) (GsmApp *app); ++ ++ gboolean (*impl_save_to_keyfile) (GsmApp *app, ++ GKeyFile *keyfile, ++ GError **error); + }; + + typedef enum + { + GSM_APP_ERROR_GENERAL = 0, + GSM_APP_ERROR_RESTART_LIMIT, + GSM_APP_ERROR_START, + GSM_APP_ERROR_STOP, + GSM_APP_NUM_ERRORS + } GsmAppError; + + #define GSM_APP_ERROR gsm_app_error_quark () + + GQuark gsm_app_error_quark (void); + GType gsm_app_get_type (void) G_GNUC_CONST; + + gboolean gsm_app_peek_autorestart (GsmApp *app); + + const char *gsm_app_peek_id (GsmApp *app); + const char *gsm_app_peek_app_id (GsmApp *app); + const char *gsm_app_peek_startup_id (GsmApp *app); + GsmManagerPhase gsm_app_peek_phase (GsmApp *app); + gboolean gsm_app_peek_is_disabled (GsmApp *app); + gboolean gsm_app_peek_is_conditionally_disabled (GsmApp *app); + + gboolean gsm_app_start (GsmApp *app, + GError **error); + gboolean gsm_app_restart (GsmApp *app, + GError **error); + gboolean gsm_app_stop (GsmApp *app, + GError **error); + gboolean gsm_app_is_running (GsmApp *app); + + void gsm_app_exited (GsmApp *app, + guchar exit_code); + void gsm_app_died (GsmApp *app, + int signal); + + gboolean gsm_app_provides (GsmApp *app, + const char *service); + char **gsm_app_get_provides (GsmApp *app); + gboolean gsm_app_has_autostart_condition (GsmApp *app, + const char *condition); + gboolean gsm_app_get_registered (GsmApp *app); + void gsm_app_set_registered (GsmApp *app, + gboolean registered); + ++gboolean gsm_app_save_to_keyfile (GsmApp *app, ++ GKeyFile *keyfile, ++ GError **error); ++ + G_END_DECLS + + #endif /* __GSM_APP_H__ */ +diff --git a/gnome-session/gsm-autostart-app.c b/gnome-session/gsm-autostart-app.c +index 870b1516..9eb1db5b 100644 +--- a/gnome-session/gsm-autostart-app.c ++++ b/gnome-session/gsm-autostart-app.c +@@ -1400,86 +1400,156 @@ gsm_autostart_app_get_autorestart (GsmApp *app) + static const char * + gsm_autostart_app_get_app_id (GsmApp *app) + { + if (GSM_AUTOSTART_APP (app)->priv->app_info == NULL) { + return NULL; + } + + return g_app_info_get_id (G_APP_INFO (GSM_AUTOSTART_APP (app)->priv->app_info)); + } + + static gboolean + gsm_autostart_app_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) + { + GsmAutostartApp *app = GSM_AUTOSTART_APP (initable); + + g_assert (app->priv->desktop_filename != NULL); + app->priv->app_info = g_desktop_app_info_new_from_filename (app->priv->desktop_filename); + if (app->priv->app_info == NULL) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Could not parse desktop file %s or it references a not found TryExec binary", app->priv->desktop_id); + return FALSE; + } + + load_desktop_file (app); + + return TRUE; + } + ++static gboolean ++gsm_autostart_app_save_to_keyfile (GsmApp *base_app, ++ GKeyFile *keyfile, ++ GError **error) ++{ ++ GsmAutostartApp *app = GSM_AUTOSTART_APP (base_app); ++ char **provides = NULL; ++ char *dbus_name; ++ char *phase; ++ gboolean res; ++ ++ provides = gsm_app_get_provides (base_app); ++ if (provides != NULL) { ++ g_key_file_set_string_list (keyfile, ++ G_KEY_FILE_DESKTOP_GROUP, ++ GSM_AUTOSTART_APP_PROVIDES_KEY, ++ (const char * const *) ++ provides, ++ g_strv_length (provides)); ++ g_strfreev (provides); ++ } ++ ++ phase = g_desktop_app_info_get_string (app->priv->app_info, ++ GSM_AUTOSTART_APP_PHASE_KEY); ++ if (phase != NULL) { ++ g_key_file_set_string (keyfile, ++ G_KEY_FILE_DESKTOP_GROUP, ++ GSM_AUTOSTART_APP_PHASE_KEY, ++ phase); ++ g_free (phase); ++ } ++ ++ dbus_name = g_desktop_app_info_get_string (app->priv->app_info, ++ GSM_AUTOSTART_APP_DBUS_NAME_KEY); ++ if (dbus_name != NULL) { ++ g_key_file_set_string (keyfile, ++ G_KEY_FILE_DESKTOP_GROUP, ++ GSM_AUTOSTART_APP_DBUS_NAME_KEY, ++ dbus_name); ++ g_free (dbus_name); ++ } ++ ++ res = g_desktop_app_info_has_key (app->priv->app_info, ++ GSM_AUTOSTART_APP_AUTORESTART_KEY); ++ if (res) { ++ g_key_file_set_boolean (keyfile, ++ G_KEY_FILE_DESKTOP_GROUP, ++ GSM_AUTOSTART_APP_AUTORESTART_KEY, ++ g_desktop_app_info_get_boolean (app->priv->app_info, ++ GSM_AUTOSTART_APP_AUTORESTART_KEY)); ++ } ++ ++ res = g_desktop_app_info_has_key (app->priv->app_info, ++ GSM_AUTOSTART_APP_AUTORESTART_KEY); ++ if (res) { ++ char *autostart_condition; ++ ++ autostart_condition = g_desktop_app_info_get_string (app->priv->app_info, "AutostartCondition"); ++ ++ g_key_file_set_string (keyfile, ++ G_KEY_FILE_DESKTOP_GROUP, ++ "AutostartCondition", ++ autostart_condition); ++ g_free (autostart_condition); ++ } ++ ++ return TRUE; ++} ++ + static void + gsm_autostart_app_initable_iface_init (GInitableIface *iface) + { + iface->init = gsm_autostart_app_initable_init; + } + + static void + gsm_autostart_app_class_init (GsmAutostartAppClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GsmAppClass *app_class = GSM_APP_CLASS (klass); + + object_class->set_property = gsm_autostart_app_set_property; + object_class->get_property = gsm_autostart_app_get_property; + object_class->dispose = gsm_autostart_app_dispose; + + app_class->impl_is_disabled = is_disabled; + app_class->impl_is_conditionally_disabled = is_conditionally_disabled; + app_class->impl_is_running = is_running; + app_class->impl_start = gsm_autostart_app_start; + app_class->impl_restart = gsm_autostart_app_restart; + app_class->impl_stop = gsm_autostart_app_stop; + app_class->impl_provides = gsm_autostart_app_provides; + app_class->impl_get_provides = gsm_autostart_app_get_provides; + app_class->impl_has_autostart_condition = gsm_autostart_app_has_autostart_condition; + app_class->impl_get_app_id = gsm_autostart_app_get_app_id; + app_class->impl_get_autorestart = gsm_autostart_app_get_autorestart; ++ app_class->impl_save_to_keyfile = gsm_autostart_app_save_to_keyfile; + + g_object_class_install_property (object_class, + PROP_DESKTOP_FILENAME, + g_param_spec_string ("desktop-filename", + "Desktop filename", + "Freedesktop .desktop file", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + signals[CONDITION_CHANGED] = + g_signal_new ("condition-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmAutostartAppClass, condition_changed), + NULL, NULL, NULL, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); + + g_type_class_add_private (object_class, sizeof (GsmAutostartAppPrivate)); + } + + GsmApp * + gsm_autostart_app_new (const char *desktop_file, + GError **error) + { + return (GsmApp*) g_initable_new (GSM_TYPE_AUTOSTART_APP, NULL, error, + "desktop-filename", desktop_file, + NULL); + } +diff --git a/gnome-session/gsm-client.c b/gnome-session/gsm-client.c +index 7b78d9e1..3f216b22 100644 +--- a/gnome-session/gsm-client.c ++++ b/gnome-session/gsm-client.c +@@ -526,47 +526,48 @@ gsm_client_end_session (GsmClient *client, + return GSM_CLIENT_GET_CLASS (client)->impl_end_session (client, flags, error); + } + + gboolean + gsm_client_stop (GsmClient *client, + GError **error) + { + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_stop (client, error); + } + + void + gsm_client_disconnected (GsmClient *client) + { + g_signal_emit (client, signals[DISCONNECTED], 0); + } + + gboolean + gsm_client_request_save (GsmClient *client, + guint flags, + GError **error) + { + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_request_save (client, flags, error); + } + + GKeyFile * + gsm_client_save (GsmClient *client, ++ GsmApp *app, + GError **error) + { + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + +- return GSM_CLIENT_GET_CLASS (client)->impl_save (client, error); ++ return GSM_CLIENT_GET_CLASS (client)->impl_save (client, app, error); + } + + void + gsm_client_end_session_response (GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason) + { + g_signal_emit (client, signals[END_SESSION_RESPONSE], 0, + is_ok, do_last, cancel, reason); + } +diff --git a/gnome-session/gsm-client.h b/gnome-session/gsm-client.h +index f79896b3..19c9cd8d 100644 +--- a/gnome-session/gsm-client.h ++++ b/gnome-session/gsm-client.h +@@ -6,60 +6,61 @@ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + + #ifndef __GSM_CLIENT_H__ + #define __GSM_CLIENT_H__ + + #include + #include + #include + + G_BEGIN_DECLS + + #define GSM_TYPE_CLIENT (gsm_client_get_type ()) + #define GSM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_CLIENT, GsmClient)) + #define GSM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_CLIENT, GsmClientClass)) + #define GSM_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_CLIENT)) + #define GSM_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_CLIENT)) + #define GSM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_CLIENT, GsmClientClass)) + ++typedef struct _GsmApp GsmApp; + typedef struct _GsmClient GsmClient; + typedef struct _GsmClientClass GsmClientClass; + + typedef struct GsmClientPrivate GsmClientPrivate; + + typedef enum { + GSM_CLIENT_UNREGISTERED = 0, + GSM_CLIENT_REGISTERED, + GSM_CLIENT_FINISHED, + GSM_CLIENT_FAILED + } GsmClientStatus; + + typedef enum { + GSM_CLIENT_RESTART_NEVER = 0, + GSM_CLIENT_RESTART_IF_RUNNING, + GSM_CLIENT_RESTART_ANYWAY, + GSM_CLIENT_RESTART_IMMEDIATELY + } GsmClientRestartStyle; + + typedef enum { + GSM_CLIENT_END_SESSION_FLAG_FORCEFUL = 1 << 0, + GSM_CLIENT_END_SESSION_FLAG_SAVE = 1 << 1, + GSM_CLIENT_END_SESSION_FLAG_LAST = 1 << 2 + } GsmClientEndSessionFlag; + + struct _GsmClient + { + GObject parent; + GsmClientPrivate *priv; + }; +@@ -67,91 +68,93 @@ struct _GsmClient + struct _GsmClientClass + { + GObjectClass parent_class; + + /* signals */ + void (*disconnected) (GsmClient *client); + void (*end_session_response) (GsmClient *client, + gboolean ok, + gboolean do_last, + gboolean cancel, + const char *reason); + + /* virtual methods */ + char * (*impl_get_app_name) (GsmClient *client); + GsmClientRestartStyle (*impl_get_restart_style_hint) (GsmClient *client); + guint (*impl_get_unix_process_id) (GsmClient *client); + gboolean (*impl_query_end_session) (GsmClient *client, + GsmClientEndSessionFlag flags, + GError **error); + gboolean (*impl_end_session) (GsmClient *client, + GsmClientEndSessionFlag flags, + GError **error); + gboolean (*impl_cancel_end_session) (GsmClient *client, + GError **error); + gboolean (*impl_stop) (GsmClient *client, + GError **error); + gboolean (*impl_request_save) (GsmClient *client, + guint flags, + GError **error); + GKeyFile * (*impl_save) (GsmClient *client, ++ GsmApp *app, + GError **error); + }; + + typedef enum + { + GSM_CLIENT_ERROR_GENERAL = 0, + GSM_CLIENT_ERROR_NOT_REGISTERED, + GSM_CLIENT_NUM_ERRORS + } GsmClientError; + + #define GSM_CLIENT_ERROR gsm_client_error_quark () + GQuark gsm_client_error_quark (void); + + GType gsm_client_get_type (void) G_GNUC_CONST; + + const char *gsm_client_peek_id (GsmClient *client); + + + const char * gsm_client_peek_startup_id (GsmClient *client); + const char * gsm_client_peek_app_id (GsmClient *client); + guint gsm_client_peek_restart_style_hint (GsmClient *client); + guint gsm_client_peek_status (GsmClient *client); + + + char *gsm_client_get_app_name (GsmClient *client); + void gsm_client_set_app_id (GsmClient *client, + const char *app_id); + void gsm_client_set_status (GsmClient *client, + guint status); + + gboolean gsm_client_end_session (GsmClient *client, + guint flags, + GError **error); + gboolean gsm_client_query_end_session (GsmClient *client, + guint flags, + GError **error); + gboolean gsm_client_cancel_end_session (GsmClient *client, + GError **error); + + void gsm_client_disconnected (GsmClient *client); + + gboolean gsm_client_request_save (GsmClient *client, + guint flags, + GError **error); + GKeyFile *gsm_client_save (GsmClient *client, ++ GsmApp *app, + GError **error); + + gboolean gsm_client_stop (GsmClient *client, + GError **error); + + /* private */ + + void gsm_client_end_session_response (GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason); + + G_END_DECLS + + #endif /* __GSM_CLIENT_H__ */ +diff --git a/gnome-session/gsm-dbus-client.c b/gnome-session/gsm-dbus-client.c +index 050ea18f..5793f830 100644 +--- a/gnome-session/gsm-dbus-client.c ++++ b/gnome-session/gsm-dbus-client.c +@@ -315,60 +315,61 @@ gsm_dbus_client_finalize (GObject *object) + + if (client->priv->skeleton != NULL) { + g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (client->priv->skeleton), + client->priv->connection); + g_clear_object (&client->priv->skeleton); + } + + g_clear_object (&client->priv->connection); + + if (client->priv->watch_id != 0) + g_bus_unwatch_name (client->priv->watch_id); + + G_OBJECT_CLASS (gsm_dbus_client_parent_class)->finalize (object); + } + + static gboolean + dbus_client_request_save (GsmClient *client, + guint flags, + GError **error) + { + g_debug ("GsmDBusClient: sending save request to client with id %s", + gsm_client_peek_id (client)); + + /* FIXME: The protocol does not support this */ + + return FALSE; + } + + static GKeyFile * + dbus_client_save (GsmClient *client, ++ GsmApp *app, + GError **error) + { + g_debug ("GsmDBusClient: saving client with id %s", + gsm_client_peek_id (client)); + + /* FIXME: We still don't support client saving for D-Bus + * session clients */ + + return NULL; + } + + static gboolean + dbus_client_stop (GsmClient *client, + GError **error) + { + GsmDBusClient *dbus_client = (GsmDBusClient *) client; + gsm_exported_client_private_emit_stop (dbus_client->priv->skeleton); + return TRUE; + } + + static char * + dbus_client_get_app_name (GsmClient *client) + { + /* Always use app-id instead */ + return NULL; + } + + static GsmClientRestartStyle + dbus_client_get_restart_style_hint (GsmClient *client) + { +diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c +index 29c3054d..e7f0d7f8 100644 +--- a/gnome-session/gsm-manager.c ++++ b/gnome-session/gsm-manager.c +@@ -1260,61 +1260,61 @@ finish_pending_save_invocations (GsmManager *manager) + g_slist_free (manager->priv->pending_save_invocations); + manager->priv->pending_save_invocations = NULL; + } + + static void + query_save_session_complete (GsmManager *manager) + { + GError *error = NULL; + + if (g_slist_length (manager->priv->next_query_clients) > 0) { + ClientEndSessionData data; + + data.manager = manager; + data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST; + + g_slist_foreach (manager->priv->next_query_clients, + (GFunc)_client_request_save, + &data); + + g_slist_free (manager->priv->next_query_clients); + manager->priv->next_query_clients = NULL; + + return; + } + + if (manager->priv->query_timeout_id > 0) { + g_source_remove (manager->priv->query_timeout_id); + manager->priv->query_timeout_id = 0; + } + +- gsm_session_save (manager->priv->clients, manager->priv->session_name, &error); ++ gsm_session_save (manager->priv->clients, manager->priv->apps, manager->priv->session_name, &error); + + if (error) { + g_warning ("Error saving session: %s", error->message); + fail_pending_save_invocations (manager, error); + g_error_free (error); + } else { + finish_pending_save_invocations (manager); + } + } + + static guint32 + generate_cookie (void) + { + guint32 cookie; + + cookie = (guint32)g_random_int_range (1, G_MAXINT32); + + return cookie; + } + + static guint32 + _generate_unique_cookie (GsmManager *manager) + { + guint32 cookie; + + do { + cookie = generate_cookie (); + } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL); + + return cookie; +@@ -1963,61 +1963,61 @@ on_xsmp_client_register_confirmed (GsmXSMPClient *client, + } + } + + static gboolean + auto_save_is_enabled (GsmManager *manager) + { + return g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE_ONE_SHOT) + || g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE); + } + + static void + maybe_save_session (GsmManager *manager) + { + GError *error; + + if (gsm_system_is_login_session (manager->priv->system)) + return; + + /* We only allow session saving when session is running or when + * logging out */ + if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING && + manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) { + return; + } + + if (!auto_save_is_enabled (manager)) { + return; + } + + error = NULL; +- gsm_session_save (manager->priv->clients, manager->priv->session_name, &error); ++ gsm_session_save (manager->priv->clients, manager->priv->apps, manager->priv->session_name, &error); + + if (error) { + g_warning ("Error saving session: %s", error->message); + g_error_free (error); + } + } + + static void + _handle_client_end_session_response (GsmManager *manager, + GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason) + { + /* just ignore if we are not yet running */ + if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) { + return; + } + + g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :""); + + if (manager->priv->phase == GSM_MANAGER_PHASE_RUNNING) { + /* Ignore responses when no requests were sent */ + if (manager->priv->query_clients == NULL) { + return; + } + + manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client); + +diff --git a/gnome-session/gsm-session-save.c b/gnome-session/gsm-session-save.c +index 78b64197..35ffaae0 100644 +--- a/gnome-session/gsm-session-save.c ++++ b/gnome-session/gsm-session-save.c +@@ -1,234 +1,253 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * gsm-session-save.c + * Copyright (C) 2008 Lucas Rocha. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + + #include + + #include + + #include + #include + #include + ++#include "gsm-app.h" + #include "gsm-util.h" + #include "gsm-autostart-app.h" + #include "gsm-client.h" + + #include "gsm-session-save.h" + + #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" + #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" + + + static gboolean gsm_session_clear_saved_session (const char *directory, + GHashTable *discard_hash); + + typedef struct { + char *dir; + GHashTable *discard_hash; ++ GsmStore *app_store; + GError **error; + } SessionSaveData; + + static void + clear_session_type (const char *save_dir) + { + char *file; + + file = g_build_filename (save_dir, "type", NULL); + + g_unlink (file); + + g_free (file); + } + + static void + set_session_type (const char *save_dir, + const char *type) + { + char *file; + GError *error; + + file = g_build_filename (save_dir, "type", NULL); + + error = NULL; + g_file_set_contents (file, type, strlen (type), &error); + if (error != NULL) + g_warning ("couldn't save session type to %s: %s", + type, error->message); + + g_free (file); + } + ++static gboolean ++_app_has_app_id (const char *id, ++ GsmApp *app, ++ const char *app_id_a) ++{ ++ const char *app_id_b; ++ ++ app_id_b = gsm_app_peek_app_id (app); ++ return g_strcmp0 (app_id_a, app_id_b) == 0; ++} ++ + static gboolean + save_one_client (char *id, + GObject *object, + SessionSaveData *data) + { + GsmClient *client; + GKeyFile *keyfile; ++ GsmApp *app = NULL; + const char *app_id; + char *path = NULL; + char *filename = NULL; + char *contents = NULL; + gsize length = 0; + char *discard_exec; + GError *local_error; + + client = GSM_CLIENT (object); + + local_error = NULL; + +- keyfile = gsm_client_save (client, &local_error); ++ app_id = gsm_client_peek_app_id (client); ++ if (!IS_STRING_EMPTY (app_id)) { ++ if (g_str_has_suffix (app_id, ".desktop")) ++ filename = g_strdup (app_id); ++ else ++ filename = g_strdup_printf ("%s.desktop", app_id); ++ ++ path = g_build_filename (data->dir, filename, NULL); ++ ++ app = (GsmApp *)gsm_store_find (data->app_store, ++ (GsmStoreFunc)_app_has_app_id, ++ (char *)app_id); ++ } ++ keyfile = gsm_client_save (client, app, &local_error); + + if (keyfile == NULL || local_error) { + goto out; + } + + contents = g_key_file_to_data (keyfile, &length, &local_error); + + if (local_error) { + goto out; + } + +- app_id = gsm_client_peek_app_id (client); +- if (!IS_STRING_EMPTY (app_id)) { +- if (g_str_has_suffix (app_id, ".desktop")) +- filename = g_strdup (app_id); +- else +- filename = g_strdup_printf ("%s.desktop", app_id); +- +- path = g_build_filename (data->dir, filename, NULL); +- } +- + if (!path || g_file_test (path, G_FILE_TEST_EXISTS)) { + if (filename) + g_free (filename); + if (path) + g_free (path); + + filename = g_strdup_printf ("%s.desktop", + gsm_client_peek_startup_id (client)); + path = g_build_filename (data->dir, filename, NULL); + } + + g_file_set_contents (path, + contents, + length, + &local_error); + + if (local_error) { + goto out; + } + + discard_exec = g_key_file_get_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + GSM_AUTOSTART_APP_DISCARD_KEY, + NULL); + if (discard_exec) { + g_hash_table_insert (data->discard_hash, + discard_exec, discard_exec); + } + + g_debug ("GsmSessionSave: saved client %s to %s", id, filename); + + out: + if (keyfile != NULL) { + g_key_file_free (keyfile); + } + + g_free (contents); + g_free (filename); + g_free (path); + + /* in case of any error, stop saving session */ + if (local_error) { + g_propagate_error (data->error, local_error); + g_error_free (local_error); + + return TRUE; + } + + return FALSE; + } + + void + gsm_session_save (GsmStore *client_store, ++ GsmStore *app_store, + const char *type, + GError **error) + { + GSettings *settings; + const char *save_dir; + char *tmp_dir; + SessionSaveData data; + + g_debug ("GsmSessionSave: Saving session"); + + /* Clear one shot key autosave in the event its set (so that it's actually + * one shot only) + */ + settings = g_settings_new (GSM_MANAGER_SCHEMA); + g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE); + g_object_unref (settings); + + save_dir = gsm_util_get_saved_session_dir (); + if (save_dir == NULL) { + g_warning ("GsmSessionSave: cannot create saved session directory"); + return; + } + + tmp_dir = gsm_util_get_empty_tmp_session_dir (); + if (tmp_dir == NULL) { + g_warning ("GsmSessionSave: cannot create new saved session directory"); + return; + } + + /* save the session in a temp directory, and remember the discard + * commands */ + data.dir = tmp_dir; + data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + data.error = error; ++ data.app_store = app_store; + + gsm_store_foreach (client_store, + (GsmStoreFunc) save_one_client, + &data); + + if (!*error) { + char *session_dir; + + if (g_file_test (save_dir, G_FILE_TEST_IS_SYMLINK)) + session_dir = g_file_read_link (save_dir, error); + else + session_dir = g_strdup (save_dir); + + if (session_dir != NULL) { + char *absolute_session_dir; + + set_session_type (tmp_dir, type); + + if (g_path_is_absolute (session_dir)) { + absolute_session_dir = g_strdup (session_dir); + } else { + char *parent_dir; + + parent_dir = g_path_get_dirname (save_dir); + absolute_session_dir = g_build_filename (parent_dir, session_dir, NULL); + g_free (parent_dir); + } + g_free (session_dir); + + /* remove the old saved session */ +diff --git a/gnome-session/gsm-session-save.h b/gnome-session/gsm-session-save.h +index c91b5615..b32673c4 100644 +--- a/gnome-session/gsm-session-save.h ++++ b/gnome-session/gsm-session-save.h +@@ -1,34 +1,35 @@ + /* gsm-session-save.h + * Copyright (C) 2008 Lucas Rocha. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + + #ifndef __GSM_SESSION_SAVE_H__ + #define __GSM_SESSION_SAVE_H__ + + #include + + #include "gsm-store.h" + + G_BEGIN_DECLS + + void gsm_session_save (GsmStore *client_store, ++ GsmStore *app_store, + const char *type, + GError **error); + void gsm_session_save_clear (void); + + G_END_DECLS + + #endif /* __GSM_SESSION_SAVE_H__ */ +diff --git a/gnome-session/gsm-xsmp-client.c b/gnome-session/gsm-xsmp-client.c +index 2846d9b3..cbecd68c 100644 +--- a/gnome-session/gsm-xsmp-client.c ++++ b/gnome-session/gsm-xsmp-client.c +@@ -612,118 +612,127 @@ set_desktop_file_keys_from_client (GsmClient *client, + + g_free (comment); + } + + static GKeyFile * + create_client_key_file (GsmClient *client, + const char *desktop_file_path, + GError **error) { + GKeyFile *keyfile; + + keyfile = g_key_file_new (); + + if (desktop_file_path != NULL) { + g_key_file_load_from_file (keyfile, + desktop_file_path, + G_KEY_FILE_KEEP_COMMENTS | + G_KEY_FILE_KEEP_TRANSLATIONS, + error); + } else { + set_desktop_file_keys_from_client (client, keyfile); + } + + return keyfile; + } + + static GsmClientRestartStyle + xsmp_get_restart_style_hint (GsmClient *client); + + static GKeyFile * + xsmp_save (GsmClient *client, ++ GsmApp *app, + GError **error) + { + GsmClientRestartStyle restart_style; + + GKeyFile *keyfile = NULL; + char *desktop_file_path = NULL; + char *exec_program = NULL; + char *exec_discard = NULL; + char *startup_id = NULL; + GError *local_error; + + g_debug ("GsmXSMPClient: saving client with id %s", + gsm_client_peek_id (client)); + + local_error = NULL; + + restart_style = xsmp_get_restart_style_hint (client); + if (restart_style == GSM_CLIENT_RESTART_NEVER) { + goto out; + } + + exec_program = xsmp_get_restart_command (client); + if (!exec_program) { + goto out; + } + + desktop_file_path = get_desktop_file_path (GSM_XSMP_CLIENT (client)); + + /* this can accept desktop_file_path == NULL */ + keyfile = create_client_key_file (client, + desktop_file_path, + &local_error); + + if (local_error) { + goto out; + } + + g_object_get (client, + "startup-id", &startup_id, + NULL); + + g_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + GSM_AUTOSTART_APP_STARTUP_ID_KEY, + startup_id); + + g_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + G_KEY_FILE_DESKTOP_KEY_EXEC, + exec_program); + + exec_discard = xsmp_get_discard_command (client); + if (exec_discard) + g_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_GROUP, + GSM_AUTOSTART_APP_DISCARD_KEY, + exec_discard); + ++ if (app != NULL) { ++ gsm_app_save_to_keyfile (app, keyfile, &local_error); ++ ++ if (local_error) { ++ goto out; ++ } ++ } ++ + out: + g_free (desktop_file_path); + g_free (exec_program); + g_free (exec_discard); + g_free (startup_id); + + if (local_error != NULL) { + g_propagate_error (error, local_error); + g_key_file_free (keyfile); + + return NULL; + } + + return keyfile; + } + + static gboolean + xsmp_stop (GsmClient *client, + GError **error) + { + GsmXSMPClient *xsmp = (GsmXSMPClient *) client; + + g_debug ("GsmXSMPClient: xsmp_stop ('%s')", xsmp->priv->description); + + if (xsmp->priv->conn == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Client is not registered"); + return FALSE; +-- +2.14.3 + diff --git a/SOURCES/0002-Revert-Remove-gnome-session-properties.patch b/SOURCES/0002-Revert-Remove-gnome-session-properties.patch new file mode 100644 index 0000000..05d282b --- /dev/null +++ b/SOURCES/0002-Revert-Remove-gnome-session-properties.patch @@ -0,0 +1,4147 @@ +From e18747f7c52f7d7d56f7d9490f934d59d84f8af9 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 8 May 2015 16:24:59 -0400 +Subject: [PATCH 02/19] Revert "Remove gnome-session-properties" + +This reverts commit ea285af9962313ee2675fff27d3a852bb61e936a. +--- + Makefile.am | 1 + + capplet/Makefile.am | 31 + + capplet/gsm-app-dialog.c | 540 +++++++++++++ + capplet/gsm-app-dialog.h | 66 ++ + capplet/gsm-properties-dialog.c | 774 +++++++++++++++++++ + capplet/gsm-properties-dialog.h | 57 ++ + capplet/gsp-app-manager.c | 593 ++++++++++++++ + capplet/gsp-app-manager.h | 81 ++ + capplet/gsp-app.c | 1102 +++++++++++++++++++++++++++ + capplet/gsp-app.h | 108 +++ + capplet/gsp-keyfile.c | 201 +++++ + capplet/gsp-keyfile.h | 65 ++ + capplet/main.c | 108 +++ + configure.ac | 2 + + data/Makefile.am | 8 +- + data/gnome-session-properties.desktop.in.in | 15 + + doc/man/Makefile.am | 1 + + doc/man/gnome-session-properties.1 | 24 + + po/POTFILES.in | 5 + + 19 files changed, 3781 insertions(+), 1 deletion(-) + create mode 100644 capplet/Makefile.am + create mode 100644 capplet/gsm-app-dialog.c + create mode 100644 capplet/gsm-app-dialog.h + create mode 100644 capplet/gsm-properties-dialog.c + create mode 100644 capplet/gsm-properties-dialog.h + create mode 100644 capplet/gsp-app-manager.c + create mode 100644 capplet/gsp-app-manager.h + create mode 100644 capplet/gsp-app.c + create mode 100644 capplet/gsp-app.h + create mode 100644 capplet/gsp-keyfile.c + create mode 100644 capplet/gsp-keyfile.h + create mode 100644 capplet/main.c + create mode 100644 data/gnome-session-properties.desktop.in.in + create mode 100644 doc/man/gnome-session-properties.1 + +diff --git a/Makefile.am b/Makefile.am +index 6560a4ec..6b6a47bf 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,32 +1,33 @@ + SUBDIRS = \ + gnome-session \ ++ capplet \ + tools \ + data \ + doc \ + po + + ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} + + EXTRA_DIST = \ + HACKING \ + MAINTAINERS + + MAINTAINERCLEANFILES = \ + $(srcdir)/INSTALL \ + $(srcdir)/aclocal.m4 \ + $(srcdir)/config.guess \ + $(srcdir)/config.h.in \ + $(srcdir)/config.sub \ + $(srcdir)/depcomp \ + $(srcdir)/install-sh \ + $(srcdir)/ltmain.sh \ + $(srcdir)/missing \ + $(srcdir)/mkinstalldirs \ + $(srcdir)/configure \ + $(srcdir)/m4/intltool.m4 \ + `find "$(srcdir)" -type f -name Makefile.in -print` + + CHANGELOG_GIT_RANGE = GNOME_SESSION_2_26_1.. + dist-hook: + $(AM_V_GEN)if test -d "$(srcdir)/.git"; then \ + ( echo '# Generated by Makefile. Do not edit.'; echo; \ +diff --git a/capplet/Makefile.am b/capplet/Makefile.am +new file mode 100644 +index 00000000..c2e563cf +--- /dev/null ++++ b/capplet/Makefile.am +@@ -0,0 +1,31 @@ ++bin_PROGRAMS = gnome-session-properties ++ ++AM_CPPFLAGS = \ ++ $(SESSION_PROPERTIES_CFLAGS) \ ++ $(GCONF_CFLAGS) \ ++ -I$(top_srcdir)/gnome-session \ ++ -DLOCALE_DIR=\""$(datadir)/locale"\" \ ++ -DGTKBUILDER_DIR=\""$(pkgdatadir)"\" \ ++ $(DISABLE_DEPRECATED_CFLAGS) ++ ++AM_CFLAGS = $(WARN_CFLAGS) ++ ++gnome_session_properties_SOURCES = \ ++ main.c \ ++ gsm-properties-dialog.h \ ++ gsm-properties-dialog.c \ ++ gsm-app-dialog.h \ ++ gsm-app-dialog.c \ ++ gsp-app.h \ ++ gsp-app.c \ ++ gsp-app-manager.h \ ++ gsp-app-manager.c \ ++ gsp-keyfile.h \ ++ gsp-keyfile.c ++ ++gnome_session_properties_LDADD = \ ++ $(SESSION_PROPERTIES_LIBS) \ ++ $(top_builddir)/gnome-session/libgsmutil.la \ ++ $(GCONF_LIBS) ++ ++-include $(top_srcdir)/git.mk +diff --git a/capplet/gsm-app-dialog.c b/capplet/gsm-app-dialog.c +new file mode 100644 +index 00000000..e7369dda +--- /dev/null ++++ b/capplet/gsm-app-dialog.c +@@ -0,0 +1,540 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2008 William Jon McCann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "gsm-util.h" ++ ++#include "gsm-app-dialog.h" ++ ++#define GTKBUILDER_FILE "session-properties.ui" ++ ++#define CAPPLET_NAME_ENTRY_WIDGET_NAME "session_properties_name_entry" ++#define CAPPLET_COMMAND_ENTRY_WIDGET_NAME "session_properties_command_entry" ++#define CAPPLET_COMMENT_ENTRY_WIDGET_NAME "session_properties_comment_entry" ++#define CAPPLET_BROWSE_WIDGET_NAME "session_properties_browse_button" ++ ++ ++#define GSM_APP_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_APP_DIALOG, GsmAppDialogPrivate)) ++ ++struct GsmAppDialogPrivate ++{ ++ GtkWidget *name_entry; ++ GtkWidget *command_entry; ++ GtkWidget *comment_entry; ++ GtkWidget *browse_button; ++ char *name; ++ char *command; ++ char *comment; ++}; ++ ++static void gsm_app_dialog_class_init (GsmAppDialogClass *klass); ++static void gsm_app_dialog_init (GsmAppDialog *app_dialog); ++static void gsm_app_dialog_finalize (GObject *object); ++ ++enum { ++ PROP_0, ++ PROP_NAME, ++ PROP_COMMAND, ++ PROP_COMMENT ++}; ++ ++G_DEFINE_TYPE (GsmAppDialog, gsm_app_dialog, GTK_TYPE_DIALOG) ++ ++static char * ++make_exec_uri (const char *exec) ++{ ++ GString *str; ++ const char *c; ++ ++ if (exec == NULL) { ++ return g_strdup (""); ++ } ++ ++ if (strchr (exec, ' ') == NULL) { ++ return g_strdup (exec); ++ } ++ ++ str = g_string_new_len (NULL, strlen (exec)); ++ ++ str = g_string_append_c (str, '"'); ++ for (c = exec; *c != '\0'; c++) { ++ /* FIXME: GKeyFile will add an additional backslach so we'll ++ * end up with toto\\" instead of toto\" ++ * We could use g_key_file_set_value(), but then we don't ++ * benefit from the other escaping that glib is doing... ++ */ ++ if (*c == '"') { ++ str = g_string_append (str, "\\\""); ++ } else { ++ str = g_string_append_c (str, *c); ++ } ++ } ++ str = g_string_append_c (str, '"'); ++ ++ return g_string_free (str, FALSE); ++} ++ ++static void ++on_browse_button_clicked (GtkWidget *widget, ++ GsmAppDialog *dialog) ++{ ++ GtkWidget *chooser; ++ int response; ++ ++ chooser = gtk_file_chooser_dialog_new ("", ++ GTK_WINDOW (dialog), ++ GTK_FILE_CHOOSER_ACTION_OPEN, ++ GTK_STOCK_CANCEL, ++ GTK_RESPONSE_CANCEL, ++ GTK_STOCK_OPEN, ++ GTK_RESPONSE_ACCEPT, ++ NULL); ++ ++ gtk_window_set_transient_for (GTK_WINDOW (chooser), ++ GTK_WINDOW (dialog)); ++ ++ gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE); ++ ++ gtk_window_set_title (GTK_WINDOW (chooser), _("Select Command")); ++ ++ gtk_widget_show (chooser); ++ ++ response = gtk_dialog_run (GTK_DIALOG (chooser)); ++ ++ if (response == GTK_RESPONSE_ACCEPT) { ++ char *text; ++ char *uri; ++ ++ text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); ++ ++ uri = make_exec_uri (text); ++ ++ g_free (text); ++ ++ gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), uri); ++ ++ g_free (uri); ++ } ++ ++ gtk_widget_destroy (chooser); ++} ++ ++static void ++on_entry_activate (GtkEntry *entry, ++ GsmAppDialog *dialog) ++{ ++ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); ++} ++ ++static void ++setup_dialog (GsmAppDialog *dialog) ++{ ++ GtkWidget *content_area; ++ GtkWidget *widget; ++ GtkBuilder *xml; ++ GError *error; ++ ++ xml = gtk_builder_new (); ++ gtk_builder_set_translation_domain (xml, GETTEXT_PACKAGE); ++ ++ error = NULL; ++ if (!gtk_builder_add_from_file (xml, ++ GTKBUILDER_DIR "/" GTKBUILDER_FILE, ++ &error)) { ++ if (error) { ++ g_warning ("Could not load capplet UI file: %s", ++ error->message); ++ g_error_free (error); ++ } else { ++ g_warning ("Could not load capplet UI file."); ++ } ++ } ++ ++ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); ++ widget = GTK_WIDGET (gtk_builder_get_object (xml, "main-table")); ++ gtk_container_add (GTK_CONTAINER (content_area), widget); ++ ++ gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); ++ gtk_window_set_icon_name (GTK_WINDOW (dialog), "session-properties"); ++ ++ g_object_set (dialog, ++ "allow-shrink", FALSE, ++ "allow-grow", FALSE, ++ NULL); ++ ++ gtk_dialog_add_button (GTK_DIALOG (dialog), ++ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); ++ ++ if (dialog->priv->name == NULL ++ && dialog->priv->command == NULL ++ && dialog->priv->comment == NULL) { ++ gtk_window_set_title (GTK_WINDOW (dialog), _("Add Startup Program")); ++ gtk_dialog_add_button (GTK_DIALOG (dialog), ++ GTK_STOCK_ADD, GTK_RESPONSE_OK); ++ } else { ++ gtk_window_set_title (GTK_WINDOW (dialog), _("Edit Startup Program")); ++ gtk_dialog_add_button (GTK_DIALOG (dialog), ++ GTK_STOCK_SAVE, GTK_RESPONSE_OK); ++ } ++ ++ dialog->priv->name_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_NAME_ENTRY_WIDGET_NAME)); ++ g_signal_connect (dialog->priv->name_entry, ++ "activate", ++ G_CALLBACK (on_entry_activate), ++ dialog); ++ if (dialog->priv->name != NULL) { ++ gtk_entry_set_text (GTK_ENTRY (dialog->priv->name_entry), dialog->priv->name); ++ } ++ ++ dialog->priv->browse_button = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_BROWSE_WIDGET_NAME)); ++ g_signal_connect (dialog->priv->browse_button, ++ "clicked", ++ G_CALLBACK (on_browse_button_clicked), ++ dialog); ++ ++ dialog->priv->command_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_COMMAND_ENTRY_WIDGET_NAME)); ++ g_signal_connect (dialog->priv->command_entry, ++ "activate", ++ G_CALLBACK (on_entry_activate), ++ dialog); ++ if (dialog->priv->command != NULL) { ++ gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), dialog->priv->command); ++ } ++ ++ dialog->priv->comment_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_COMMENT_ENTRY_WIDGET_NAME)); ++ g_signal_connect (dialog->priv->comment_entry, ++ "activate", ++ G_CALLBACK (on_entry_activate), ++ dialog); ++ if (dialog->priv->comment != NULL) { ++ gtk_entry_set_text (GTK_ENTRY (dialog->priv->comment_entry), dialog->priv->comment); ++ } ++ ++ if (xml != NULL) { ++ g_object_unref (xml); ++ } ++} ++ ++static GObject * ++gsm_app_dialog_constructor (GType type, ++ guint n_construct_app, ++ GObjectConstructParam *construct_app) ++{ ++ GsmAppDialog *dialog; ++ ++ dialog = GSM_APP_DIALOG (G_OBJECT_CLASS (gsm_app_dialog_parent_class)->constructor (type, ++ n_construct_app, ++ construct_app)); ++ ++ setup_dialog (dialog); ++ ++ gtk_widget_show_all (GTK_WIDGET (dialog)); ++ ++ return G_OBJECT (dialog); ++} ++ ++static void ++gsm_app_dialog_dispose (GObject *object) ++{ ++ GsmAppDialog *dialog; ++ ++ g_return_if_fail (object != NULL); ++ g_return_if_fail (GSM_IS_APP_DIALOG (object)); ++ ++ dialog = GSM_APP_DIALOG (object); ++ ++ g_free (dialog->priv->name); ++ dialog->priv->name = NULL; ++ g_free (dialog->priv->command); ++ dialog->priv->command = NULL; ++ g_free (dialog->priv->comment); ++ dialog->priv->comment = NULL; ++ ++ G_OBJECT_CLASS (gsm_app_dialog_parent_class)->dispose (object); ++} ++ ++static void ++gsm_app_dialog_set_name (GsmAppDialog *dialog, ++ const char *name) ++{ ++ g_return_if_fail (GSM_IS_APP_DIALOG (dialog)); ++ ++ g_free (dialog->priv->name); ++ ++ dialog->priv->name = g_strdup (name); ++ g_object_notify (G_OBJECT (dialog), "name"); ++} ++ ++static void ++gsm_app_dialog_set_command (GsmAppDialog *dialog, ++ const char *name) ++{ ++ g_return_if_fail (GSM_IS_APP_DIALOG (dialog)); ++ ++ g_free (dialog->priv->command); ++ ++ dialog->priv->command = g_strdup (name); ++ g_object_notify (G_OBJECT (dialog), "command"); ++} ++ ++static void ++gsm_app_dialog_set_comment (GsmAppDialog *dialog, ++ const char *name) ++{ ++ g_return_if_fail (GSM_IS_APP_DIALOG (dialog)); ++ ++ g_free (dialog->priv->comment); ++ ++ dialog->priv->comment = g_strdup (name); ++ g_object_notify (G_OBJECT (dialog), "comment"); ++} ++ ++const char * ++gsm_app_dialog_get_name (GsmAppDialog *dialog) ++{ ++ g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL); ++ return gtk_entry_get_text (GTK_ENTRY (dialog->priv->name_entry)); ++} ++ ++const char * ++gsm_app_dialog_get_command (GsmAppDialog *dialog) ++{ ++ g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL); ++ return gtk_entry_get_text (GTK_ENTRY (dialog->priv->command_entry)); ++} ++ ++const char * ++gsm_app_dialog_get_comment (GsmAppDialog *dialog) ++{ ++ g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL); ++ return gtk_entry_get_text (GTK_ENTRY (dialog->priv->comment_entry)); ++} ++ ++static void ++gsm_app_dialog_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ GsmAppDialog *dialog = GSM_APP_DIALOG (object); ++ ++ switch (prop_id) { ++ case PROP_NAME: ++ gsm_app_dialog_set_name (dialog, g_value_get_string (value)); ++ break; ++ case PROP_COMMAND: ++ gsm_app_dialog_set_command (dialog, g_value_get_string (value)); ++ break; ++ case PROP_COMMENT: ++ gsm_app_dialog_set_comment (dialog, g_value_get_string (value)); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gsm_app_dialog_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GsmAppDialog *dialog = GSM_APP_DIALOG (object); ++ ++ switch (prop_id) { ++ case PROP_NAME: ++ g_value_set_string (value, dialog->priv->name); ++ break; ++ case PROP_COMMAND: ++ g_value_set_string (value, dialog->priv->command); ++ break; ++ case PROP_COMMENT: ++ g_value_set_string (value, dialog->priv->comment); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gsm_app_dialog_class_init (GsmAppDialogClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->get_property = gsm_app_dialog_get_property; ++ object_class->set_property = gsm_app_dialog_set_property; ++ object_class->constructor = gsm_app_dialog_constructor; ++ object_class->dispose = gsm_app_dialog_dispose; ++ object_class->finalize = gsm_app_dialog_finalize; ++ ++ g_object_class_install_property (object_class, ++ PROP_NAME, ++ g_param_spec_string ("name", ++ "name", ++ "name", ++ NULL, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ g_object_class_install_property (object_class, ++ PROP_COMMAND, ++ g_param_spec_string ("command", ++ "command", ++ "command", ++ NULL, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ g_object_class_install_property (object_class, ++ PROP_COMMENT, ++ g_param_spec_string ("comment", ++ "comment", ++ "comment", ++ NULL, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ g_type_class_add_private (klass, sizeof (GsmAppDialogPrivate)); ++} ++ ++static void ++gsm_app_dialog_init (GsmAppDialog *dialog) ++{ ++ ++ dialog->priv = GSM_APP_DIALOG_GET_PRIVATE (dialog); ++} ++ ++static void ++gsm_app_dialog_finalize (GObject *object) ++{ ++ GsmAppDialog *dialog; ++ ++ g_return_if_fail (object != NULL); ++ g_return_if_fail (GSM_IS_APP_DIALOG (object)); ++ ++ dialog = GSM_APP_DIALOG (object); ++ ++ g_return_if_fail (dialog->priv != NULL); ++ ++ G_OBJECT_CLASS (gsm_app_dialog_parent_class)->finalize (object); ++} ++ ++GtkWidget * ++gsm_app_dialog_new (const char *name, ++ const char *command, ++ const char *comment) ++{ ++ GObject *object; ++ ++ object = g_object_new (GSM_TYPE_APP_DIALOG, ++ "name", name, ++ "command", command, ++ "comment", comment, ++ NULL); ++ ++ return GTK_WIDGET (object); ++} ++ ++gboolean ++gsm_app_dialog_run (GsmAppDialog *dialog, ++ char **name_p, ++ char **command_p, ++ char **comment_p) ++{ ++ gboolean retval; ++ ++ retval = FALSE; ++ ++ while (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { ++ const char *name; ++ const char *exec; ++ const char *comment; ++ const char *error_msg; ++ GError *error; ++ char **argv; ++ int argc; ++ ++ name = gsm_app_dialog_get_name (GSM_APP_DIALOG (dialog)); ++ exec = gsm_app_dialog_get_command (GSM_APP_DIALOG (dialog)); ++ comment = gsm_app_dialog_get_comment (GSM_APP_DIALOG (dialog)); ++ ++ error = NULL; ++ error_msg = NULL; ++ ++ if (gsm_util_text_is_blank (exec)) { ++ error_msg = _("The startup command cannot be empty"); ++ } else { ++ if (!g_shell_parse_argv (exec, &argc, &argv, &error)) { ++ if (error != NULL) { ++ error_msg = error->message; ++ } else { ++ error_msg = _("The startup command is not valid"); ++ } ++ } ++ } ++ ++ if (error_msg != NULL) { ++ GtkWidget *msgbox; ++ ++ msgbox = gtk_message_dialog_new (GTK_WINDOW (dialog), ++ GTK_DIALOG_MODAL, ++ GTK_MESSAGE_ERROR, ++ GTK_BUTTONS_CLOSE, ++ "%s", error_msg); ++ ++ if (error != NULL) { ++ g_error_free (error); ++ } ++ ++ gtk_dialog_run (GTK_DIALOG (msgbox)); ++ ++ gtk_widget_destroy (msgbox); ++ ++ continue; ++ } ++ ++ if (gsm_util_text_is_blank (name)) { ++ name = argv[0]; ++ } ++ ++ if (name_p) { ++ *name_p = g_strdup (name); ++ } ++ ++ g_strfreev (argv); ++ ++ if (command_p) { ++ *command_p = g_strdup (exec); ++ } ++ ++ if (comment_p) { ++ *comment_p = g_strdup (comment); ++ } ++ ++ retval = TRUE; ++ break; ++ } ++ ++ gtk_widget_destroy (GTK_WIDGET (dialog)); ++ ++ return retval; ++} +diff --git a/capplet/gsm-app-dialog.h b/capplet/gsm-app-dialog.h +new file mode 100644 +index 00000000..ced0628c +--- /dev/null ++++ b/capplet/gsm-app-dialog.h +@@ -0,0 +1,66 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2008 William Jon McCann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++#ifndef __GSM_APP_DIALOG_H ++#define __GSM_APP_DIALOG_H ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define GSM_TYPE_APP_DIALOG (gsm_app_dialog_get_type ()) ++#define GSM_APP_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_APP_DIALOG, GsmAppDialog)) ++#define GSM_APP_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_APP_DIALOG, GsmAppDialogClass)) ++#define GSM_IS_APP_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_APP_DIALOG)) ++#define GSM_IS_APP_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_APP_DIALOG)) ++#define GSM_APP_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_APP_DIALOG, GsmAppDialogClass)) ++ ++typedef struct GsmAppDialogPrivate GsmAppDialogPrivate; ++ ++typedef struct ++{ ++ GtkDialog parent; ++ GsmAppDialogPrivate *priv; ++} GsmAppDialog; ++ ++typedef struct ++{ ++ GtkDialogClass parent_class; ++} GsmAppDialogClass; ++ ++GType gsm_app_dialog_get_type (void); ++ ++GtkWidget * gsm_app_dialog_new (const char *name, ++ const char *command, ++ const char *comment); ++ ++gboolean gsm_app_dialog_run (GsmAppDialog *dialog, ++ char **name_p, ++ char **command_p, ++ char **comment_p); ++ ++const char * gsm_app_dialog_get_name (GsmAppDialog *dialog); ++const char * gsm_app_dialog_get_command (GsmAppDialog *dialog); ++const char * gsm_app_dialog_get_comment (GsmAppDialog *dialog); ++ ++G_END_DECLS ++ ++#endif /* __GSM_APP_DIALOG_H */ +diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c +new file mode 100644 +index 00000000..33812b8b +--- /dev/null ++++ b/capplet/gsm-properties-dialog.c +@@ -0,0 +1,774 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 1999 Free Software Foundation, Inc. ++ * Copyright (C) 2007 Vincent Untz. ++ * Copyright (C) 2008 Lucas Rocha. ++ * Copyright (C) 2008 William Jon McCann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "gsm-properties-dialog.h" ++#include "gsm-app-dialog.h" ++#include "gsm-util.h" ++#include "gsp-app.h" ++#include "gsp-app-manager.h" ++ ++#define GSM_PROPERTIES_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogPrivate)) ++ ++#define GTKBUILDER_FILE "session-properties.ui" ++ ++#define CAPPLET_TREEVIEW_WIDGET_NAME "session_properties_treeview" ++#define CAPPLET_ADD_WIDGET_NAME "session_properties_add_button" ++#define CAPPLET_DELETE_WIDGET_NAME "session_properties_delete_button" ++#define CAPPLET_EDIT_WIDGET_NAME "session_properties_edit_button" ++#define CAPPLET_SAVE_WIDGET_NAME "session_properties_save_button" ++#define CAPPLET_REMEMBER_WIDGET_NAME "session_properties_remember_toggle" ++ ++#define STARTUP_APP_ICON "system-run" ++ ++#define SPC_SETTINGS_SCHEMA "org.gnome.SessionManager" ++#define SPC_SETTINGS_AUTOSAVE_KEY "auto-save-session" ++ ++struct GsmPropertiesDialogPrivate ++{ ++ GtkBuilder *xml; ++ GtkListStore *list_store; ++ GtkTreeModel *tree_filter; ++ ++ GtkTreeView *treeview; ++ GtkWidget *add_button; ++ GtkWidget *delete_button; ++ GtkWidget *edit_button; ++ ++ GSettings *settings; ++ ++ GspAppManager *manager; ++}; ++ ++enum { ++ STORE_COL_VISIBLE = 0, ++ STORE_COL_ENABLED, ++ STORE_COL_GICON, ++ STORE_COL_DESCRIPTION, ++ STORE_COL_APP, ++ STORE_COL_SEARCH, ++ NUMBER_OF_COLUMNS ++}; ++ ++static void gsm_properties_dialog_class_init (GsmPropertiesDialogClass *klass); ++static void gsm_properties_dialog_init (GsmPropertiesDialog *properties_dialog); ++static void gsm_properties_dialog_finalize (GObject *object); ++ ++G_DEFINE_TYPE (GsmPropertiesDialog, gsm_properties_dialog, GTK_TYPE_DIALOG) ++ ++static gboolean ++find_by_app (GtkTreeModel *model, ++ GtkTreeIter *iter, ++ GspApp *app) ++{ ++ GspApp *iter_app = NULL; ++ ++ if (!gtk_tree_model_get_iter_first (model, iter)) { ++ return FALSE; ++ } ++ ++ do { ++ gtk_tree_model_get (model, iter, ++ STORE_COL_APP, &iter_app, ++ -1); ++ ++ if (iter_app == app) { ++ g_object_unref (iter_app); ++ return TRUE; ++ } ++ } while (gtk_tree_model_iter_next (model, iter)); ++ ++ return FALSE; ++} ++ ++static void ++_fill_iter_from_app (GtkListStore *list_store, ++ GtkTreeIter *iter, ++ GspApp *app) ++{ ++ gboolean hidden; ++ gboolean display; ++ gboolean enabled; ++ gboolean shown; ++ GIcon *icon; ++ const char *description; ++ const char *app_name; ++ ++ hidden = gsp_app_get_hidden (app); ++ display = gsp_app_get_display (app); ++ enabled = gsp_app_get_enabled (app); ++ shown = gsp_app_get_shown (app); ++ icon = gsp_app_get_icon (app); ++ description = gsp_app_get_description (app); ++ app_name = gsp_app_get_name (app); ++ ++ if (G_IS_THEMED_ICON (icon)) { ++ GtkIconTheme *theme; ++ const char * const *icon_names; ++ ++ theme = gtk_icon_theme_get_default (); ++ icon_names = g_themed_icon_get_names (G_THEMED_ICON (icon)); ++ if (icon_names[0] == NULL || ++ !gtk_icon_theme_has_icon (theme, icon_names[0])) { ++ g_object_unref (icon); ++ icon = NULL; ++ } ++ } else if (G_IS_FILE_ICON (icon)) { ++ GFile *iconfile; ++ ++ iconfile = g_file_icon_get_file (G_FILE_ICON (icon)); ++ if (!g_file_query_exists (iconfile, NULL)) { ++ g_object_unref (icon); ++ icon = NULL; ++ } ++ } ++ ++ if (icon == NULL) { ++ icon = g_themed_icon_new (STARTUP_APP_ICON); ++ } ++ ++ gtk_list_store_set (list_store, iter, ++ STORE_COL_VISIBLE, !hidden && shown && display, ++ STORE_COL_ENABLED, enabled, ++ STORE_COL_GICON, icon, ++ STORE_COL_DESCRIPTION, description, ++ STORE_COL_APP, app, ++ STORE_COL_SEARCH, app_name, ++ -1); ++ g_object_unref (icon); ++} ++ ++static void ++_app_changed (GsmPropertiesDialog *dialog, ++ GspApp *app) ++{ ++ GtkTreeIter iter; ++ ++ if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store), ++ &iter, app)) { ++ return; ++ } ++ ++ _fill_iter_from_app (dialog->priv->list_store, &iter, app); ++} ++ ++static void ++append_app (GsmPropertiesDialog *dialog, ++ GspApp *app) ++{ ++ GtkTreeIter iter; ++ ++ gtk_list_store_append (dialog->priv->list_store, &iter); ++ _fill_iter_from_app (dialog->priv->list_store, &iter, app); ++ ++ g_signal_connect_swapped (app, "changed", ++ G_CALLBACK (_app_changed), dialog); ++} ++ ++static void ++_app_added (GsmPropertiesDialog *dialog, ++ GspApp *app, ++ GspAppManager *manager) ++{ ++ append_app (dialog, app); ++} ++ ++static void ++_app_removed (GsmPropertiesDialog *dialog, ++ GspApp *app, ++ GspAppManager *manager) ++{ ++ GtkTreeIter iter; ++ ++ if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store), ++ &iter, app)) { ++ return; ++ } ++ ++ g_signal_handlers_disconnect_by_func (app, ++ _app_changed, ++ dialog); ++ gtk_list_store_remove (dialog->priv->list_store, &iter); ++} ++ ++static void ++populate_model (GsmPropertiesDialog *dialog) ++{ ++ GSList *apps; ++ GSList *l; ++ ++ apps = gsp_app_manager_get_apps (dialog->priv->manager); ++ for (l = apps; l != NULL; l = l->next) { ++ append_app (dialog, GSP_APP (l->data)); ++ } ++ g_slist_free (apps); ++} ++ ++static void ++on_selection_changed (GtkTreeSelection *selection, ++ GsmPropertiesDialog *dialog) ++{ ++ gboolean sel; ++ ++ sel = gtk_tree_selection_get_selected (selection, NULL, NULL); ++ ++ gtk_widget_set_sensitive (dialog->priv->edit_button, sel); ++ gtk_widget_set_sensitive (dialog->priv->delete_button, sel); ++} ++ ++static void ++on_startup_enabled_toggled (GtkCellRendererToggle *cell_renderer, ++ char *path, ++ GsmPropertiesDialog *dialog) ++{ ++ GtkTreeIter iter; ++ GspApp *app; ++ gboolean active; ++ ++ if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dialog->priv->tree_filter), ++ &iter, path)) { ++ return; ++ } ++ ++ app = NULL; ++ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter), ++ &iter, ++ STORE_COL_APP, &app, ++ -1); ++ ++ active = gtk_cell_renderer_toggle_get_active (cell_renderer); ++ active = !active; ++ ++ if (app) { ++ gsp_app_set_enabled (app, active); ++ g_object_unref (app); ++ } ++} ++ ++static void ++on_drag_data_received (GtkWidget *widget, ++ GdkDragContext *drag_context, ++ gint x, ++ gint y, ++ GtkSelectionData *data, ++ guint info, ++ guint time, ++ GsmPropertiesDialog *dialog) ++{ ++ gboolean dnd_success; ++ ++ dnd_success = FALSE; ++ ++ if (data != NULL) { ++ char **filenames; ++ int i; ++ ++ filenames = gtk_selection_data_get_uris (data); ++ ++ for (i = 0; filenames[i] && filenames[i][0]; i++) { ++ /* Return success if at least one file succeeded */ ++ gboolean file_success; ++ file_success = gsp_app_copy_desktop_file (filenames[i]); ++ dnd_success = dnd_success || file_success; ++ } ++ ++ g_strfreev (filenames); ++ } ++ ++ gtk_drag_finish (drag_context, dnd_success, FALSE, time); ++ g_signal_stop_emission_by_name (widget, "drag_data_received"); ++} ++ ++static void ++on_drag_begin (GtkWidget *widget, ++ GdkDragContext *context, ++ GsmPropertiesDialog *dialog) ++{ ++ GtkTreePath *path; ++ GtkTreeIter iter; ++ GspApp *app; ++ ++ gtk_tree_view_get_cursor (GTK_TREE_VIEW (widget), &path, NULL); ++ gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->priv->tree_filter), ++ &iter, path); ++ gtk_tree_path_free (path); ++ ++ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter), ++ &iter, ++ STORE_COL_APP, &app, ++ -1); ++ ++ if (app) { ++ g_object_set_data_full (G_OBJECT (context), "gsp-app", ++ g_object_ref (app), g_object_unref); ++ g_object_unref (app); ++ } ++ ++} ++ ++static void ++on_drag_data_get (GtkWidget *widget, ++ GdkDragContext *context, ++ GtkSelectionData *selection_data, ++ guint info, ++ guint time, ++ GsmPropertiesDialog *dialog) ++{ ++ GspApp *app; ++ ++ app = g_object_get_data (G_OBJECT (context), "gsp-app"); ++ if (app) { ++ const char *uris[2]; ++ char *uri; ++ ++ uri = g_filename_to_uri (gsp_app_get_path (app), NULL, NULL); ++ ++ uris[0] = uri; ++ uris[1] = NULL; ++ gtk_selection_data_set_uris (selection_data, (char **) uris); ++ ++ g_free (uri); ++ } ++} ++ ++static void ++on_add_app_clicked (GtkWidget *widget, ++ GsmPropertiesDialog *dialog) ++{ ++ GtkWidget *add_dialog; ++ char *name; ++ char *exec; ++ char *comment; ++ ++ add_dialog = gsm_app_dialog_new (NULL, NULL, NULL); ++ gtk_window_set_transient_for (GTK_WINDOW (add_dialog), ++ GTK_WINDOW (dialog)); ++ ++ if (gsm_app_dialog_run (GSM_APP_DIALOG (add_dialog), ++ &name, &exec, &comment)) { ++ gsp_app_create (name, comment, exec); ++ g_free (name); ++ g_free (exec); ++ g_free (comment); ++ } ++} ++ ++static void ++on_delete_app_clicked (GtkWidget *widget, ++ GsmPropertiesDialog *dialog) ++{ ++ GtkTreeSelection *selection; ++ GtkTreeIter iter; ++ GspApp *app; ++ ++ selection = gtk_tree_view_get_selection (dialog->priv->treeview); ++ ++ if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { ++ return; ++ } ++ ++ app = NULL; ++ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter), ++ &iter, ++ STORE_COL_APP, &app, ++ -1); ++ ++ if (app) { ++ gsp_app_delete (app); ++ g_object_unref (app); ++ } ++} ++ ++static void ++on_edit_app_clicked (GtkWidget *widget, ++ GsmPropertiesDialog *dialog) ++{ ++ GtkTreeSelection *selection; ++ GtkTreeIter iter; ++ GspApp *app; ++ ++ selection = gtk_tree_view_get_selection (dialog->priv->treeview); ++ ++ if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { ++ return; ++ } ++ ++ app = NULL; ++ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter), ++ &iter, ++ STORE_COL_APP, &app, ++ -1); ++ ++ if (app) { ++ GtkWidget *edit_dialog; ++ char *name; ++ char *exec; ++ char *comment; ++ ++ edit_dialog = gsm_app_dialog_new (gsp_app_get_name (app), ++ gsp_app_get_exec (app), ++ gsp_app_get_comment (app)); ++ gtk_window_set_transient_for (GTK_WINDOW (edit_dialog), ++ GTK_WINDOW (dialog)); ++ ++ if (gsm_app_dialog_run (GSM_APP_DIALOG (edit_dialog), ++ &name, &exec, &comment)) { ++ gsp_app_update (app, name, comment, exec); ++ g_free (name); ++ g_free (exec); ++ g_free (comment); ++ } ++ ++ g_object_unref (app); ++ } ++} ++ ++static void ++on_row_activated (GtkTreeView *tree_view, ++ GtkTreePath *path, ++ GtkTreeViewColumn *column, ++ GsmPropertiesDialog *dialog) ++{ ++ on_edit_app_clicked (NULL, dialog); ++} ++ ++static void ++on_save_session_clicked (GtkWidget *widget, ++ GsmPropertiesDialog *dialog) ++{ ++ g_debug ("Session saving is not implemented yet!"); ++} ++ ++static void ++setup_dialog (GsmPropertiesDialog *dialog) ++{ ++ GtkTreeView *treeview; ++ GtkWidget *button; ++ GtkTreeModel *tree_filter; ++ GtkTreeViewColumn *column; ++ GtkCellRenderer *renderer; ++ GtkTreeSelection *selection; ++ GtkTargetList *targetlist; ++ ++ gtk_dialog_add_buttons (GTK_DIALOG (dialog), ++ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, ++ NULL); ++ ++ dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS, ++ G_TYPE_BOOLEAN, ++ G_TYPE_BOOLEAN, ++ G_TYPE_ICON, ++ G_TYPE_STRING, ++ G_TYPE_OBJECT, ++ G_TYPE_STRING); ++ tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store), ++ NULL); ++ g_object_unref (dialog->priv->list_store); ++ dialog->priv->tree_filter = tree_filter; ++ ++ gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter), ++ STORE_COL_VISIBLE); ++ ++ treeview = GTK_TREE_VIEW (gtk_builder_get_object (dialog->priv->xml, ++ CAPPLET_TREEVIEW_WIDGET_NAME)); ++ dialog->priv->treeview = treeview; ++ ++ gtk_tree_view_set_model (treeview, tree_filter); ++ g_object_unref (tree_filter); ++ ++ gtk_tree_view_set_headers_visible (treeview, FALSE); ++ g_signal_connect (treeview, ++ "row-activated", ++ G_CALLBACK (on_row_activated), ++ dialog); ++ ++ selection = gtk_tree_view_get_selection (treeview); ++ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); ++ g_signal_connect (selection, ++ "changed", ++ G_CALLBACK (on_selection_changed), ++ dialog); ++ ++ /* CHECKBOX COLUMN */ ++ renderer = gtk_cell_renderer_toggle_new (); ++ column = gtk_tree_view_column_new_with_attributes (_("Enabled"), ++ renderer, ++ "active", STORE_COL_ENABLED, ++ NULL); ++ gtk_tree_view_append_column (treeview, column); ++ g_signal_connect (renderer, ++ "toggled", ++ G_CALLBACK (on_startup_enabled_toggled), ++ dialog); ++ ++ /* ICON COLUMN */ ++ renderer = gtk_cell_renderer_pixbuf_new (); ++ column = gtk_tree_view_column_new_with_attributes (_("Icon"), ++ renderer, ++ "gicon", STORE_COL_GICON, ++ "sensitive", STORE_COL_ENABLED, ++ NULL); ++ g_object_set (renderer, ++ "stock-size", GSM_PROPERTIES_ICON_SIZE, ++ NULL); ++ gtk_tree_view_append_column (treeview, column); ++ ++ /* NAME COLUMN */ ++ renderer = gtk_cell_renderer_text_new (); ++ column = gtk_tree_view_column_new_with_attributes (_("Program"), ++ renderer, ++ "markup", STORE_COL_DESCRIPTION, ++ "sensitive", STORE_COL_ENABLED, ++ NULL); ++ g_object_set (renderer, ++ "ellipsize", PANGO_ELLIPSIZE_END, ++ NULL); ++ gtk_tree_view_append_column (treeview, column); ++ ++ ++ gtk_tree_view_column_set_sort_column_id (column, STORE_COL_DESCRIPTION); ++ gtk_tree_view_set_search_column (treeview, STORE_COL_SEARCH); ++ gtk_tree_view_set_rules_hint (treeview, TRUE); ++ ++ gtk_tree_view_enable_model_drag_source (treeview, ++ GDK_BUTTON1_MASK|GDK_BUTTON2_MASK, ++ NULL, 0, ++ GDK_ACTION_COPY); ++ gtk_drag_source_add_uri_targets (GTK_WIDGET (treeview)); ++ ++ gtk_drag_dest_set (GTK_WIDGET (treeview), ++ GTK_DEST_DEFAULT_ALL, ++ NULL, 0, ++ GDK_ACTION_COPY); ++ ++ gtk_drag_dest_add_uri_targets (GTK_WIDGET (treeview)); ++ /* we don't want to accept drags coming from this widget */ ++ targetlist = gtk_drag_dest_get_target_list (GTK_WIDGET (treeview)); ++ if (targetlist != NULL) { ++ GtkTargetEntry *targets; ++ gint n_targets; ++ gint i; ++ ++ targets = gtk_target_table_new_from_list (targetlist, &n_targets); ++ for (i = 0; i < n_targets; i++) ++ targets[i].flags = GTK_TARGET_OTHER_WIDGET; ++ ++ targetlist = gtk_target_list_new (targets, n_targets); ++ gtk_drag_dest_set_target_list (GTK_WIDGET (treeview), targetlist); ++ gtk_target_list_unref (targetlist); ++ ++ gtk_target_table_free (targets, n_targets); ++ } ++ ++ g_signal_connect (treeview, "drag_begin", ++ G_CALLBACK (on_drag_begin), ++ dialog); ++ g_signal_connect (treeview, "drag_data_get", ++ G_CALLBACK (on_drag_data_get), ++ dialog); ++ g_signal_connect (treeview, "drag_data_received", ++ G_CALLBACK (on_drag_data_received), ++ dialog); ++ ++ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->priv->list_store), ++ STORE_COL_DESCRIPTION, ++ GTK_SORT_ASCENDING); ++ ++ ++ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, ++ CAPPLET_ADD_WIDGET_NAME)); ++ dialog->priv->add_button = button; ++ g_signal_connect (button, ++ "clicked", ++ G_CALLBACK (on_add_app_clicked), ++ dialog); ++ ++ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, ++ CAPPLET_DELETE_WIDGET_NAME)); ++ dialog->priv->delete_button = button; ++ g_signal_connect (button, ++ "clicked", ++ G_CALLBACK (on_delete_app_clicked), ++ dialog); ++ ++ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, ++ CAPPLET_EDIT_WIDGET_NAME)); ++ dialog->priv->edit_button = button; ++ g_signal_connect (button, ++ "clicked", ++ G_CALLBACK (on_edit_app_clicked), ++ dialog); ++ ++ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, ++ CAPPLET_REMEMBER_WIDGET_NAME)); ++ g_settings_bind (dialog->priv->settings, SPC_SETTINGS_AUTOSAVE_KEY, ++ button, "active", G_SETTINGS_BIND_DEFAULT); ++ ++ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, ++ CAPPLET_SAVE_WIDGET_NAME)); ++ g_signal_connect (button, ++ "clicked", ++ G_CALLBACK (on_save_session_clicked), ++ dialog); ++ ++ dialog->priv->manager = gsp_app_manager_get (); ++ gsp_app_manager_fill (dialog->priv->manager); ++ g_signal_connect_swapped (dialog->priv->manager, "added", ++ G_CALLBACK (_app_added), dialog); ++ g_signal_connect_swapped (dialog->priv->manager, "removed", ++ G_CALLBACK (_app_removed), dialog); ++ ++ populate_model (dialog); ++} ++ ++static GObject * ++gsm_properties_dialog_constructor (GType type, ++ guint n_construct_properties, ++ GObjectConstructParam *construct_properties) ++{ ++ GsmPropertiesDialog *dialog; ++ ++ dialog = GSM_PROPERTIES_DIALOG (G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->constructor (type, ++ n_construct_properties, ++ construct_properties)); ++ ++ setup_dialog (dialog); ++ ++ gtk_widget_show (GTK_WIDGET (dialog)); ++ ++ return G_OBJECT (dialog); ++} ++ ++static void ++gsm_properties_dialog_dispose (GObject *object) ++{ ++ GsmPropertiesDialog *dialog; ++ ++ g_return_if_fail (object != NULL); ++ g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object)); ++ ++ dialog = GSM_PROPERTIES_DIALOG (object); ++ ++ if (dialog->priv->xml != NULL) { ++ g_object_unref (dialog->priv->xml); ++ dialog->priv->xml = NULL; ++ } ++ ++ if (dialog->priv->settings != NULL) { ++ g_object_unref (dialog->priv->settings); ++ dialog->priv->settings = NULL; ++ } ++ ++ G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->dispose (object); ++ ++ /* it's important to do this after chaining to the parent dispose ++ * method because we want to make sure the treeview has been disposed ++ * and removed all its references to GspApp objects */ ++ if (dialog->priv->manager != NULL) { ++ g_object_unref (dialog->priv->manager); ++ dialog->priv->manager = NULL; ++ } ++} ++ ++static void ++gsm_properties_dialog_class_init (GsmPropertiesDialogClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->constructor = gsm_properties_dialog_constructor; ++ object_class->dispose = gsm_properties_dialog_dispose; ++ object_class->finalize = gsm_properties_dialog_finalize; ++ ++ g_type_class_add_private (klass, sizeof (GsmPropertiesDialogPrivate)); ++} ++ ++static void ++gsm_properties_dialog_init (GsmPropertiesDialog *dialog) ++{ ++ GtkWidget *content_area; ++ GtkWidget *widget; ++ GError *error; ++ ++ dialog->priv = GSM_PROPERTIES_DIALOG_GET_PRIVATE (dialog); ++ ++ dialog->priv->settings = g_settings_new (SPC_SETTINGS_SCHEMA); ++ ++ dialog->priv->xml = gtk_builder_new (); ++ gtk_builder_set_translation_domain (dialog->priv->xml, GETTEXT_PACKAGE); ++ ++ error = NULL; ++ if (!gtk_builder_add_from_file (dialog->priv->xml, ++ GTKBUILDER_DIR "/" GTKBUILDER_FILE, ++ &error)) { ++ if (error) { ++ g_warning ("Could not load capplet UI file: %s", ++ error->message); ++ g_error_free (error); ++ } else { ++ g_warning ("Could not load capplet UI file."); ++ } ++ } ++ ++ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); ++ widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, ++ "main-notebook")); ++ gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0); ++ ++ gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 450); ++ gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); ++ gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); ++ gtk_box_set_spacing (GTK_BOX (content_area), 2); ++ gtk_window_set_icon_name (GTK_WINDOW (dialog), "session-properties"); ++ gtk_window_set_title (GTK_WINDOW (dialog), _("Startup Applications Preferences")); ++} ++ ++static void ++gsm_properties_dialog_finalize (GObject *object) ++{ ++ GsmPropertiesDialog *dialog; ++ ++ g_return_if_fail (object != NULL); ++ g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object)); ++ ++ dialog = GSM_PROPERTIES_DIALOG (object); ++ ++ g_return_if_fail (dialog->priv != NULL); ++ ++ G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->finalize (object); ++} ++ ++GtkWidget * ++gsm_properties_dialog_new (void) ++{ ++ GObject *object; ++ ++ object = g_object_new (GSM_TYPE_PROPERTIES_DIALOG, ++ NULL); ++ ++ return GTK_WIDGET (object); ++} +diff --git a/capplet/gsm-properties-dialog.h b/capplet/gsm-properties-dialog.h +new file mode 100644 +index 00000000..df4915ea +--- /dev/null ++++ b/capplet/gsm-properties-dialog.h +@@ -0,0 +1,57 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2008 William Jon McCann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++#ifndef __GSM_PROPERTIES_DIALOG_H ++#define __GSM_PROPERTIES_DIALOG_H ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define GSM_TYPE_PROPERTIES_DIALOG (gsm_properties_dialog_get_type ()) ++#define GSM_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialog)) ++#define GSM_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogClass)) ++#define GSM_IS_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_PROPERTIES_DIALOG)) ++#define GSM_IS_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_PROPERTIES_DIALOG)) ++#define GSM_PROPERTIES_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogClass)) ++ ++typedef struct GsmPropertiesDialogPrivate GsmPropertiesDialogPrivate; ++ ++typedef struct ++{ ++ GtkDialog parent; ++ GsmPropertiesDialogPrivate *priv; ++} GsmPropertiesDialog; ++ ++typedef struct ++{ ++ GtkDialogClass parent_class; ++} GsmPropertiesDialogClass; ++ ++GType gsm_properties_dialog_get_type (void); ++ ++GtkWidget * gsm_properties_dialog_new (void); ++ ++#define GSM_PROPERTIES_ICON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR ++ ++G_END_DECLS ++ ++#endif /* __GSM_PROPERTIES_DIALOG_H */ +diff --git a/capplet/gsp-app-manager.c b/capplet/gsp-app-manager.c +new file mode 100644 +index 00000000..bcd6d402 +--- /dev/null ++++ b/capplet/gsp-app-manager.c +@@ -0,0 +1,593 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 1999 Free Software Foundation, Inc. ++ * Copyright (C) 2007, 2009 Vincent Untz. ++ * Copyright (C) 2008 Lucas Rocha. ++ * Copyright (C) 2008 William Jon McCann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++#include ++ ++#include "gsm-util.h" ++#include "gsp-app.h" ++ ++#include "gsp-app-manager.h" ++ ++static GspAppManager *manager = NULL; ++ ++typedef struct { ++ char *dir; ++ int index; ++ GFileMonitor *monitor; ++} GspXdgDir; ++ ++struct _GspAppManagerPrivate { ++ GSList *apps; ++ GSList *dirs; ++}; ++ ++#define GSP_APP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSP_TYPE_APP_MANAGER, GspAppManagerPrivate)) ++ ++ ++enum { ++ ADDED, ++ REMOVED, ++ LAST_SIGNAL ++}; ++ ++static guint gsp_app_manager_signals[LAST_SIGNAL] = { 0 }; ++ ++ ++G_DEFINE_TYPE (GspAppManager, gsp_app_manager, G_TYPE_OBJECT) ++ ++static void gsp_app_manager_dispose (GObject *object); ++static void gsp_app_manager_finalize (GObject *object); ++static void _gsp_app_manager_app_unref (GspApp *app, ++ GspAppManager *manager); ++static void _gsp_app_manager_app_removed (GspAppManager *manager, ++ GspApp *app); ++ ++static GspXdgDir * ++_gsp_xdg_dir_new (const char *dir, ++ int index) ++{ ++ GspXdgDir *xdgdir; ++ ++ xdgdir = g_slice_new (GspXdgDir); ++ ++ xdgdir->dir = g_strdup (dir); ++ xdgdir->index = index; ++ xdgdir->monitor = NULL; ++ ++ return xdgdir; ++} ++ ++static void ++_gsp_xdg_dir_free (GspXdgDir *xdgdir) ++{ ++ if (xdgdir->dir) { ++ g_free (xdgdir->dir); ++ xdgdir->dir = NULL; ++ } ++ ++ if (xdgdir->monitor) { ++ g_file_monitor_cancel (xdgdir->monitor); ++ g_object_unref (xdgdir->monitor); ++ xdgdir->monitor = NULL; ++ } ++ ++ g_slice_free (GspXdgDir, xdgdir); ++} ++ ++static void ++gsp_app_manager_class_init (GspAppManagerClass *class) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (class); ++ ++ gobject_class->dispose = gsp_app_manager_dispose; ++ gobject_class->finalize = gsp_app_manager_finalize; ++ ++ gsp_app_manager_signals[ADDED] = ++ g_signal_new ("added", ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GspAppManagerClass, ++ added), ++ NULL, ++ NULL, ++ g_cclosure_marshal_VOID__OBJECT, ++ G_TYPE_NONE, 1, G_TYPE_OBJECT); ++ ++ gsp_app_manager_signals[REMOVED] = ++ g_signal_new ("removed", ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GspAppManagerClass, ++ removed), ++ NULL, ++ NULL, ++ g_cclosure_marshal_VOID__OBJECT, ++ G_TYPE_NONE, 1, G_TYPE_OBJECT); ++ ++ g_type_class_add_private (class, sizeof (GspAppManagerPrivate)); ++} ++ ++static void ++gsp_app_manager_init (GspAppManager *manager) ++{ ++ manager->priv = GSP_APP_MANAGER_GET_PRIVATE (manager); ++ ++ memset (manager->priv, 0, sizeof (GspAppManagerPrivate)); ++} ++ ++static void ++gsp_app_manager_dispose (GObject *object) ++{ ++ GspAppManager *manager; ++ ++ g_return_if_fail (object != NULL); ++ g_return_if_fail (GSP_IS_APP_MANAGER (object)); ++ ++ manager = GSP_APP_MANAGER (object); ++ ++ /* we unref GspApp objects in dispose since they might need to ++ * reference us during their dispose/finalize */ ++ g_slist_foreach (manager->priv->apps, ++ (GFunc) _gsp_app_manager_app_unref, manager); ++ g_slist_free (manager->priv->apps); ++ manager->priv->apps = NULL; ++ ++ G_OBJECT_CLASS (gsp_app_manager_parent_class)->dispose (object); ++} ++ ++static void ++gsp_app_manager_finalize (GObject *object) ++{ ++ GspAppManager *manager; ++ ++ g_return_if_fail (object != NULL); ++ g_return_if_fail (GSP_IS_APP_MANAGER (object)); ++ ++ manager = GSP_APP_MANAGER (object); ++ ++ g_slist_foreach (manager->priv->dirs, ++ (GFunc) _gsp_xdg_dir_free, NULL); ++ g_slist_free (manager->priv->dirs); ++ manager->priv->dirs = NULL; ++ ++ G_OBJECT_CLASS (gsp_app_manager_parent_class)->finalize (object); ++ ++ manager = NULL; ++} ++ ++static void ++_gsp_app_manager_emit_added (GspAppManager *manager, ++ GspApp *app) ++{ ++ g_signal_emit (G_OBJECT (manager), gsp_app_manager_signals[ADDED], ++ 0, app); ++} ++ ++static void ++_gsp_app_manager_emit_removed (GspAppManager *manager, ++ GspApp *app) ++{ ++ g_signal_emit (G_OBJECT (manager), gsp_app_manager_signals[REMOVED], ++ 0, app); ++} ++ ++/* ++ * Directories ++ */ ++ ++static int ++gsp_app_manager_get_dir_index (GspAppManager *manager, ++ const char *dir) ++{ ++ GSList *l; ++ GspXdgDir *xdgdir; ++ ++ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), -1); ++ g_return_val_if_fail (dir != NULL, -1); ++ ++ for (l = manager->priv->dirs; l != NULL; l = l->next) { ++ xdgdir = l->data; ++ if (strcmp (dir, xdgdir->dir) == 0) { ++ return xdgdir->index; ++ } ++ } ++ ++ return -1; ++} ++ ++const char * ++gsp_app_manager_get_dir (GspAppManager *manager, ++ unsigned int index) ++{ ++ GSList *l; ++ GspXdgDir *xdgdir; ++ ++ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL); ++ ++ for (l = manager->priv->dirs; l != NULL; l = l->next) { ++ xdgdir = l->data; ++ if (index == xdgdir->index) { ++ return xdgdir->dir; ++ } ++ } ++ ++ return NULL; ++} ++ ++static int ++_gsp_app_manager_find_dir_with_basename (GspAppManager *manager, ++ const char *basename, ++ int minimum_index) ++{ ++ GSList *l; ++ GspXdgDir *xdgdir; ++ char *path; ++ GKeyFile *keyfile; ++ int result = -1; ++ ++ path = NULL; ++ keyfile = g_key_file_new (); ++ ++ for (l = manager->priv->dirs; l != NULL; l = l->next) { ++ xdgdir = l->data; ++ ++ if (xdgdir->index <= minimum_index) { ++ continue; ++ } ++ ++ g_free (path); ++ path = g_build_filename (xdgdir->dir, basename, NULL); ++ if (!g_file_test (path, G_FILE_TEST_EXISTS)) { ++ continue; ++ } ++ ++ if (!g_key_file_load_from_file (keyfile, path, ++ G_KEY_FILE_NONE, NULL)) { ++ continue; ++ } ++ ++ /* the file exists and is readable */ ++ if (result == -1) { ++ result = xdgdir->index; ++ } else { ++ result = MIN (result, xdgdir->index); ++ } ++ } ++ ++ g_key_file_free (keyfile); ++ g_free (path); ++ ++ return result; ++} ++ ++static void ++_gsp_app_manager_handle_delete (GspAppManager *manager, ++ GspApp *app, ++ const char *basename, ++ int index) ++{ ++ unsigned int position; ++ unsigned int system_position; ++ ++ position = gsp_app_get_xdg_position (app); ++ system_position = gsp_app_get_xdg_system_position (app); ++ ++ if (system_position < index) { ++ /* it got deleted, but we don't even care about it */ ++ return; ++ } ++ ++ if (index < position) { ++ /* it got deleted, but in a position earlier than the current ++ * one. This happens when the user file was changed and became ++ * identical to the system file; in this case, the user file is ++ * simply removed. */ ++ g_assert (index == 0); ++ return; ++ } ++ ++ if (position == index && ++ (system_position == index || system_position == G_MAXUINT)) { ++ /* the file used by the user was deleted, and there's no other ++ * file in system directories. So it really got deleted. */ ++ _gsp_app_manager_app_removed (manager, app); ++ return; ++ } ++ ++ if (system_position == index) { ++ /* then we know that position != index; we just hae to tell ++ * GspApp if there's still a system directory containing this ++ * basename */ ++ int new_system; ++ ++ new_system = _gsp_app_manager_find_dir_with_basename (manager, ++ basename, ++ index); ++ if (new_system < 0) { ++ gsp_app_set_xdg_system_position (app, G_MAXUINT); ++ } else { ++ gsp_app_set_xdg_system_position (app, new_system); ++ } ++ ++ return; ++ } ++ ++ if (position == index) { ++ /* then we know that system_position != G_MAXUINT; we need to ++ * tell GspApp to change position to system_position */ ++ const char *dir; ++ ++ dir = gsp_app_manager_get_dir (manager, system_position); ++ if (dir) { ++ char *path; ++ ++ path = g_build_filename (dir, basename, NULL); ++ gsp_app_reload_at (app, path, ++ (unsigned int) system_position); ++ g_free (path); ++ } else { ++ _gsp_app_manager_app_removed (manager, app); ++ } ++ ++ return; ++ } ++ ++ g_assert_not_reached (); ++} ++ ++static gboolean ++gsp_app_manager_xdg_dir_monitor (GFileMonitor *monitor, ++ GFile *child, ++ GFile *other_file, ++ GFileMonitorEvent flags, ++ gpointer data) ++{ ++ GspAppManager *manager; ++ GspApp *old_app; ++ GspApp *app; ++ GFile *parent; ++ char *basename; ++ char *dir; ++ char *path; ++ int index; ++ ++ manager = GSP_APP_MANAGER (data); ++ ++ basename = g_file_get_basename (child); ++ if (!g_str_has_suffix (basename, ".desktop")) { ++ /* not a desktop file, we can ignore */ ++ g_free (basename); ++ return TRUE; ++ } ++ old_app = gsp_app_manager_find_app_with_basename (manager, basename); ++ ++ parent = g_file_get_parent (child); ++ dir = g_file_get_path (parent); ++ g_object_unref (parent); ++ ++ index = gsp_app_manager_get_dir_index (manager, dir); ++ if (index < 0) { ++ /* not a directory we know; should never happen, though */ ++ g_free (dir); ++ return TRUE; ++ } ++ ++ path = g_file_get_path (child); ++ ++ switch (flags) { ++ case G_FILE_MONITOR_EVENT_CHANGED: ++ case G_FILE_MONITOR_EVENT_CREATED: ++ /* we just do as if it was a new file: GspApp is clever enough ++ * to do the right thing */ ++ app = gsp_app_new (path, (unsigned int) index); ++ ++ /* we didn't have this app before, so add it */ ++ if (old_app == NULL && app != NULL) { ++ gsp_app_manager_add (manager, app); ++ g_object_unref (app); ++ } ++ /* else: it was just updated, GspApp took care of ++ * sending the event */ ++ break; ++ case G_FILE_MONITOR_EVENT_DELETED: ++ if (!old_app) { ++ /* it got deleted, but we don't know about it, so ++ * nothing to do */ ++ break; ++ } ++ ++ _gsp_app_manager_handle_delete (manager, old_app, ++ basename, index); ++ break; ++ default: ++ break; ++ } ++ ++ g_free (path); ++ g_free (dir); ++ g_free (basename); ++ ++ return TRUE; ++} ++ ++/* ++ * Initialization ++ */ ++ ++static void ++_gsp_app_manager_fill_from_dir (GspAppManager *manager, ++ GspXdgDir *xdgdir) ++{ ++ GFile *file; ++ GDir *dir; ++ const char *name; ++ ++ file = g_file_new_for_path (xdgdir->dir); ++ xdgdir->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, ++ NULL, NULL); ++ g_object_unref (file); ++ ++ if (xdgdir->monitor) { ++ g_signal_connect (xdgdir->monitor, "changed", ++ G_CALLBACK (gsp_app_manager_xdg_dir_monitor), ++ manager); ++ } ++ ++ dir = g_dir_open (xdgdir->dir, 0, NULL); ++ if (!dir) { ++ return; ++ } ++ ++ while ((name = g_dir_read_name (dir))) { ++ GspApp *app; ++ char *desktop_file_path; ++ ++ if (!g_str_has_suffix (name, ".desktop")) { ++ continue; ++ } ++ ++ desktop_file_path = g_build_filename (xdgdir->dir, name, NULL); ++ app = gsp_app_new (desktop_file_path, xdgdir->index); ++ ++ if (app != NULL) { ++ gsp_app_manager_add (manager, app); ++ g_object_unref (app); ++ } ++ ++ g_free (desktop_file_path); ++ } ++ ++ g_dir_close (dir); ++} ++ ++void ++gsp_app_manager_fill (GspAppManager *manager) ++{ ++ char **autostart_dirs; ++ int i; ++ ++ if (manager->priv->apps != NULL) ++ return; ++ ++ autostart_dirs = gsm_util_get_autostart_dirs (); ++ /* we always assume that the first directory is the user one */ ++ g_assert (g_str_has_prefix (autostart_dirs[0], ++ g_get_user_config_dir ())); ++ ++ for (i = 0; autostart_dirs[i] != NULL; i++) { ++ GspXdgDir *xdgdir; ++ ++ if (gsp_app_manager_get_dir_index (manager, ++ autostart_dirs[i]) >= 0) { ++ continue; ++ } ++ ++ xdgdir = _gsp_xdg_dir_new (autostart_dirs[i], i); ++ manager->priv->dirs = g_slist_prepend (manager->priv->dirs, ++ xdgdir); ++ ++ _gsp_app_manager_fill_from_dir (manager, xdgdir); ++ } ++ ++ g_strfreev (autostart_dirs); ++} ++ ++/* ++ * App handling ++ */ ++ ++static void ++_gsp_app_manager_app_unref (GspApp *app, ++ GspAppManager *manager) ++{ ++ g_signal_handlers_disconnect_by_func (app, ++ _gsp_app_manager_app_removed, ++ manager); ++ g_object_unref (app); ++} ++ ++static void ++_gsp_app_manager_app_removed (GspAppManager *manager, ++ GspApp *app) ++{ ++ _gsp_app_manager_emit_removed (manager, app); ++ manager->priv->apps = g_slist_remove (manager->priv->apps, app); ++ _gsp_app_manager_app_unref (app, manager); ++} ++ ++void ++gsp_app_manager_add (GspAppManager *manager, ++ GspApp *app) ++{ ++ g_return_if_fail (GSP_IS_APP_MANAGER (manager)); ++ g_return_if_fail (GSP_IS_APP (app)); ++ ++ manager->priv->apps = g_slist_prepend (manager->priv->apps, ++ g_object_ref (app)); ++ g_signal_connect_swapped (app, "removed", ++ G_CALLBACK (_gsp_app_manager_app_removed), ++ manager); ++ _gsp_app_manager_emit_added (manager, app); ++} ++ ++GspApp * ++gsp_app_manager_find_app_with_basename (GspAppManager *manager, ++ const char *basename) ++{ ++ GSList *l; ++ GspApp *app; ++ ++ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL); ++ g_return_val_if_fail (basename != NULL, NULL); ++ ++ for (l = manager->priv->apps; l != NULL; l = l->next) { ++ app = GSP_APP (l->data); ++ if (strcmp (basename, gsp_app_get_basename (app)) == 0) ++ return app; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Singleton ++ */ ++ ++GspAppManager * ++gsp_app_manager_get (void) ++{ ++ if (manager == NULL) { ++ manager = g_object_new (GSP_TYPE_APP_MANAGER, NULL); ++ return manager; ++ } else { ++ return g_object_ref (manager); ++ } ++} ++ ++GSList * ++gsp_app_manager_get_apps (GspAppManager *manager) ++{ ++ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL); ++ ++ return g_slist_copy (manager->priv->apps); ++} +diff --git a/capplet/gsp-app-manager.h b/capplet/gsp-app-manager.h +new file mode 100644 +index 00000000..777f8d6c +--- /dev/null ++++ b/capplet/gsp-app-manager.h +@@ -0,0 +1,81 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 1999 Free Software Foundation, Inc. ++ * Copyright (C) 2007, 2009 Vincent Untz. ++ * Copyright (C) 2008 Lucas Rocha. ++ * Copyright (C) 2008 William Jon McCann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++#ifndef __GSP_APP_MANAGER_H ++#define __GSP_APP_MANAGER_H ++ ++#include ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define GSP_TYPE_APP_MANAGER (gsp_app_manager_get_type ()) ++#define GSP_APP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSP_TYPE_APP_MANAGER, GspAppManager)) ++#define GSP_APP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSP_TYPE_APP_MANAGER, GspAppManagerClass)) ++#define GSP_IS_APP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSP_TYPE_APP_MANAGER)) ++#define GSP_IS_APP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSP_TYPE_APP_MANAGER)) ++#define GSP_APP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSP_TYPE_APP_MANAGER, GspAppManagerClass)) ++ ++typedef struct _GspAppManager GspAppManager; ++typedef struct _GspAppManagerClass GspAppManagerClass; ++ ++typedef struct _GspAppManagerPrivate GspAppManagerPrivate; ++ ++struct _GspAppManagerClass ++{ ++ GObjectClass parent_class; ++ ++ void (* added) (GspAppManager *manager, ++ GspApp *app); ++ void (* removed) (GspAppManager *manager, ++ GspApp *app); ++}; ++ ++struct _GspAppManager ++{ ++ GObject parent_instance; ++ ++ GspAppManagerPrivate *priv; ++}; ++ ++GType gsp_app_manager_get_type (void); ++ ++GspAppManager *gsp_app_manager_get (void); ++ ++void gsp_app_manager_fill (GspAppManager *manager); ++ ++GSList *gsp_app_manager_get_apps (GspAppManager *manager); ++ ++GspApp *gsp_app_manager_find_app_with_basename (GspAppManager *manager, ++ const char *basename); ++ ++const char *gsp_app_manager_get_dir (GspAppManager *manager, ++ unsigned int index); ++ ++void gsp_app_manager_add (GspAppManager *manager, ++ GspApp *app); ++ ++G_END_DECLS ++ ++#endif /* __GSP_APP_MANAGER_H */ +diff --git a/capplet/gsp-app.c b/capplet/gsp-app.c +new file mode 100644 +index 00000000..c92b8dad +--- /dev/null ++++ b/capplet/gsp-app.c +@@ -0,0 +1,1102 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 1999 Free Software Foundation, Inc. ++ * Copyright (C) 2007, 2009 Vincent Untz. ++ * Copyright (C) 2008 Lucas Rocha. ++ * Copyright (C) 2008 William Jon McCann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "gsm-app-dialog.h" ++#include "gsm-properties-dialog.h" ++#include "gsm-util.h" ++#include "gsp-app-manager.h" ++#include "gsp-keyfile.h" ++ ++#include "gsp-app.h" ++ ++#define GSP_APP_SAVE_DELAY 2 ++ ++#define GSP_ASP_SAVE_MASK_HIDDEN 0x0001 ++#define GSP_ASP_SAVE_MASK_ENABLED 0x0002 ++#define GSP_ASP_SAVE_MASK_NAME 0x0004 ++#define GSP_ASP_SAVE_MASK_EXEC 0x0008 ++#define GSP_ASP_SAVE_MASK_COMMENT 0x0010 ++#define GSP_ASP_SAVE_MASK_NO_DISPLAY 0x0020 ++#define GSP_ASP_SAVE_MASK_ALL 0xffff ++ ++struct _GspAppPrivate { ++ char *basename; ++ char *path; ++ ++ gboolean hidden; ++ gboolean no_display; ++ gboolean enabled; ++ gboolean shown; ++ ++ char *name; ++ char *exec; ++ char *comment; ++ char *icon; ++ ++ GIcon *gicon; ++ char *description; ++ ++ /* position of the directory in the XDG environment variable */ ++ unsigned int xdg_position; ++ /* position of the first system directory in the XDG env var containing ++ * this autostart app too (G_MAXUINT means none) */ ++ unsigned int xdg_system_position; ++ ++ unsigned int save_timeout; ++ /* mask of what has changed */ ++ unsigned int save_mask; ++ /* path that contains the original file that needs to be saved */ ++ char *old_system_path; ++ /* after writing to file, we skip the next file monitor event of type ++ * CHANGED */ ++ gboolean skip_next_monitor_event; ++}; ++ ++#define GSP_APP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSP_TYPE_APP, GspAppPrivate)) ++ ++ ++enum { ++ CHANGED, ++ REMOVED, ++ LAST_SIGNAL ++}; ++ ++static guint gsp_app_signals[LAST_SIGNAL] = { 0 }; ++ ++ ++G_DEFINE_TYPE (GspApp, gsp_app, G_TYPE_OBJECT) ++ ++static void gsp_app_dispose (GObject *object); ++static void gsp_app_finalize (GObject *object); ++static gboolean _gsp_app_save (gpointer data); ++ ++ ++static gboolean ++_gsp_str_equal (const char *a, ++ const char *b) ++{ ++ if (g_strcmp0 (a, b) == 0) { ++ return TRUE; ++ } ++ ++ if (a && !b && a[0] == '\0') { ++ return TRUE; ++ } ++ ++ if (b && !a && b[0] == '\0') { ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++ ++static void ++gsp_app_class_init (GspAppClass *class) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (class); ++ ++ gobject_class->dispose = gsp_app_dispose; ++ gobject_class->finalize = gsp_app_finalize; ++ ++ gsp_app_signals[CHANGED] = ++ g_signal_new ("changed", ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GspAppClass, ++ changed), ++ NULL, ++ NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ ++ gsp_app_signals[REMOVED] = ++ g_signal_new ("removed", ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GspAppClass, ++ removed), ++ NULL, ++ NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ ++ g_type_class_add_private (class, sizeof (GspAppPrivate)); ++} ++ ++static void ++gsp_app_init (GspApp *app) ++{ ++ app->priv = GSP_APP_GET_PRIVATE (app); ++ ++ memset (app->priv, 0, sizeof (GspAppPrivate)); ++ app->priv->xdg_position = G_MAXUINT; ++ app->priv->xdg_system_position = G_MAXUINT; ++} ++ ++static void ++_gsp_app_free_reusable_data (GspApp *app) ++{ ++ if (app->priv->path) { ++ g_free (app->priv->path); ++ app->priv->path = NULL; ++ } ++ ++ if (app->priv->name) { ++ g_free (app->priv->name); ++ app->priv->name = NULL; ++ } ++ ++ if (app->priv->exec) { ++ g_free (app->priv->exec); ++ app->priv->exec = NULL; ++ } ++ ++ if (app->priv->comment) { ++ g_free (app->priv->comment); ++ app->priv->comment = NULL; ++ } ++ ++ if (app->priv->icon) { ++ g_free (app->priv->icon); ++ app->priv->icon = NULL; ++ } ++ ++ if (app->priv->gicon) { ++ g_object_unref (app->priv->gicon); ++ app->priv->gicon = NULL; ++ } ++ ++ if (app->priv->description) { ++ g_free (app->priv->description); ++ app->priv->description = NULL; ++ } ++ ++ if (app->priv->old_system_path) { ++ g_free (app->priv->old_system_path); ++ app->priv->old_system_path = NULL; ++ } ++} ++ ++static void ++gsp_app_dispose (GObject *object) ++{ ++ GspApp *app; ++ ++ g_return_if_fail (object != NULL); ++ g_return_if_fail (GSP_IS_APP (object)); ++ ++ app = GSP_APP (object); ++ ++ /* we save in dispose since we might need to reference GspAppManager */ ++ if (app->priv->save_timeout) { ++ g_source_remove (app->priv->save_timeout); ++ app->priv->save_timeout = 0; ++ ++ /* save now */ ++ _gsp_app_save (app); ++ } ++ ++ G_OBJECT_CLASS (gsp_app_parent_class)->dispose (object); ++} ++ ++static void ++gsp_app_finalize (GObject *object) ++{ ++ GspApp *app; ++ ++ g_return_if_fail (object != NULL); ++ g_return_if_fail (GSP_IS_APP (object)); ++ ++ app = GSP_APP (object); ++ ++ if (app->priv->basename) { ++ g_free (app->priv->basename); ++ app->priv->basename = NULL; ++ } ++ ++ _gsp_app_free_reusable_data (app); ++ ++ G_OBJECT_CLASS (gsp_app_parent_class)->finalize (object); ++} ++ ++static void ++_gsp_app_emit_changed (GspApp *app) ++{ ++ g_signal_emit (G_OBJECT (app), gsp_app_signals[CHANGED], 0); ++} ++ ++static void ++_gsp_app_emit_removed (GspApp *app) ++{ ++ g_signal_emit (G_OBJECT (app), gsp_app_signals[REMOVED], 0); ++} ++ ++static void ++_gsp_app_update_description (GspApp *app) ++{ ++ const char *primary; ++ const char *secondary; ++ ++ if (!gsm_util_text_is_blank (app->priv->name)) { ++ primary = app->priv->name; ++ } else if (!gsm_util_text_is_blank (app->priv->exec)) { ++ primary = app->priv->exec; ++ } else { ++ primary = _("No name"); ++ } ++ ++ if (!gsm_util_text_is_blank (app->priv->comment)) { ++ secondary = app->priv->comment; ++ } else { ++ secondary = _("No description"); ++ } ++ ++ g_free (app->priv->description); ++ app->priv->description = g_markup_printf_escaped ("%s\n%s", ++ primary, ++ secondary); ++} ++ ++/* ++ * Saving ++ */ ++ ++static void ++_gsp_ensure_user_autostart_dir (void) ++{ ++ char *dir; ++ ++ dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL); ++ g_mkdir_with_parents (dir, S_IRWXU); ++ ++ g_free (dir); ++} ++ ++static gboolean ++_gsp_app_user_equal_system (GspApp *app, ++ char **system_path) ++{ ++ GspAppManager *manager; ++ const char *system_dir; ++ char *path; ++ char *str; ++ GKeyFile *keyfile; ++ ++ manager = gsp_app_manager_get (); ++ system_dir = gsp_app_manager_get_dir (manager, ++ app->priv->xdg_system_position); ++ g_object_unref (manager); ++ if (!system_dir) { ++ return FALSE; ++ } ++ ++ path = g_build_filename (system_dir, app->priv->basename, NULL); ++ ++ keyfile = g_key_file_new (); ++ if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { ++ g_free (path); ++ g_key_file_free (keyfile); ++ return FALSE; ++ } ++ ++ if (gsp_key_file_get_boolean (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_HIDDEN, ++ FALSE) != app->priv->hidden || ++ gsp_key_file_get_boolean (keyfile, ++ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED, ++ TRUE) != app->priv->enabled || ++ gsp_key_file_get_shown (keyfile, ++ gsm_util_get_current_desktop ()) != app->priv->shown) { ++ g_free (path); ++ g_key_file_free (keyfile); ++ return FALSE; ++ } ++ ++ if (gsp_key_file_get_boolean (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, ++ FALSE) != app->priv->no_display) { ++ g_free (path); ++ g_key_file_free (keyfile); ++ return FALSE; ++ } ++ ++ str = gsp_key_file_get_locale_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_NAME); ++ if (!_gsp_str_equal (str, app->priv->name)) { ++ g_free (str); ++ g_free (path); ++ g_key_file_free (keyfile); ++ return FALSE; ++ } ++ g_free (str); ++ ++ str = gsp_key_file_get_locale_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_COMMENT); ++ if (!_gsp_str_equal (str, app->priv->comment)) { ++ g_free (str); ++ g_free (path); ++ g_key_file_free (keyfile); ++ return FALSE; ++ } ++ g_free (str); ++ ++ str = gsp_key_file_get_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_EXEC); ++ if (!_gsp_str_equal (str, app->priv->exec)) { ++ g_free (str); ++ g_free (path); ++ g_key_file_free (keyfile); ++ return FALSE; ++ } ++ g_free (str); ++ ++ str = gsp_key_file_get_locale_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_ICON); ++ if (!_gsp_str_equal (str, app->priv->icon)) { ++ g_free (str); ++ g_free (path); ++ g_key_file_free (keyfile); ++ return FALSE; ++ } ++ g_free (str); ++ ++ g_key_file_free (keyfile); ++ ++ *system_path = path; ++ ++ return TRUE; ++} ++ ++static inline void ++_gsp_app_save_done_success (GspApp *app) ++{ ++ app->priv->save_mask = 0; ++ ++ if (app->priv->old_system_path) { ++ g_free (app->priv->old_system_path); ++ app->priv->old_system_path = NULL; ++ } ++} ++ ++static gboolean ++_gsp_app_save (gpointer data) ++{ ++ GspApp *app; ++ char *use_path; ++ GKeyFile *keyfile; ++ GError *error; ++ ++ app = GSP_APP (data); ++ ++ /* first check if removing the data from the user dir and using the ++ * data from the system dir is enough -- this helps us keep clean the ++ * user config dir by removing unneeded files */ ++ if (_gsp_app_user_equal_system (app, &use_path)) { ++ if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) { ++ g_remove (app->priv->path); ++ } ++ ++ g_free (app->priv->path); ++ app->priv->path = use_path; ++ ++ app->priv->xdg_position = app->priv->xdg_system_position; ++ ++ _gsp_app_save_done_success (app); ++ return FALSE; ++ } ++ ++ if (app->priv->old_system_path) ++ use_path = app->priv->old_system_path; ++ else ++ use_path = app->priv->path; ++ ++ keyfile = g_key_file_new (); ++ ++ error = NULL; ++ g_key_file_load_from_file (keyfile, use_path, ++ G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, ++ &error); ++ ++ if (error) { ++ g_error_free (error); ++ gsp_key_file_populate (keyfile); ++ } ++ ++ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_HIDDEN) { ++ gsp_key_file_set_boolean (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_HIDDEN, ++ app->priv->hidden); ++ } ++ ++ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_NO_DISPLAY) { ++ gsp_key_file_set_boolean (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, ++ app->priv->no_display); ++ } ++ ++ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_ENABLED) { ++ gsp_key_file_set_boolean (keyfile, ++ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED, ++ app->priv->enabled); ++ } ++ ++ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_NAME) { ++ gsp_key_file_set_locale_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_NAME, ++ app->priv->name); ++ gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_NAME); ++ } ++ ++ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_COMMENT) { ++ gsp_key_file_set_locale_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_COMMENT, ++ app->priv->comment); ++ gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_COMMENT); ++ } ++ ++ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_EXEC) { ++ gsp_key_file_set_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_EXEC, ++ app->priv->exec); ++ } ++ ++ _gsp_ensure_user_autostart_dir (); ++ if (gsp_key_file_to_file (keyfile, app->priv->path, NULL)) { ++ app->priv->skip_next_monitor_event = TRUE; ++ _gsp_app_save_done_success (app); ++ } else { ++ g_warning ("Could not save %s file", app->priv->path); ++ } ++ ++ g_key_file_free (keyfile); ++ ++ app->priv->save_timeout = 0; ++ return FALSE; ++} ++ ++static void ++_gsp_app_queue_save (GspApp *app) ++{ ++ if (app->priv->save_timeout) { ++ g_source_remove (app->priv->save_timeout); ++ app->priv->save_timeout = 0; ++ } ++ ++ /* if the file was not in the user directory, then we'll create a copy ++ * there */ ++ if (app->priv->xdg_position != 0) { ++ app->priv->xdg_position = 0; ++ ++ if (app->priv->old_system_path == NULL) { ++ app->priv->old_system_path = app->priv->path; ++ /* if old_system_path was not NULL, then it means we ++ * tried to save and we failed; in that case, we want ++ * to try again and use the old file as a basis again */ ++ } ++ ++ app->priv->path = g_build_filename (g_get_user_config_dir (), ++ "autostart", ++ app->priv->basename, NULL); ++ } ++ ++ app->priv->save_timeout = g_timeout_add_seconds (GSP_APP_SAVE_DELAY, ++ _gsp_app_save, ++ app); ++} ++ ++/* ++ * Accessors ++ */ ++ ++const char * ++gsp_app_get_basename (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), NULL); ++ ++ return app->priv->basename; ++} ++ ++const char * ++gsp_app_get_path (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), NULL); ++ ++ return app->priv->path; ++} ++ ++gboolean ++gsp_app_get_hidden (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), FALSE); ++ ++ return app->priv->hidden; ++} ++ ++gboolean ++gsp_app_get_display (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), FALSE); ++ ++ return !app->priv->no_display; ++} ++ ++gboolean ++gsp_app_get_enabled (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), FALSE); ++ ++ return app->priv->enabled; ++} ++ ++void ++gsp_app_set_enabled (GspApp *app, ++ gboolean enabled) ++{ ++ g_return_if_fail (GSP_IS_APP (app)); ++ ++ if (enabled == app->priv->enabled) { ++ return; ++ } ++ ++ app->priv->enabled = enabled; ++ app->priv->save_mask |= GSP_ASP_SAVE_MASK_ENABLED; ++ ++ _gsp_app_queue_save (app); ++ _gsp_app_emit_changed (app); ++} ++ ++gboolean ++gsp_app_get_shown (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), FALSE); ++ ++ return app->priv->shown; ++} ++ ++const char * ++gsp_app_get_name (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), NULL); ++ ++ return app->priv->name; ++} ++ ++const char * ++gsp_app_get_exec (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), NULL); ++ ++ return app->priv->exec; ++} ++ ++const char * ++gsp_app_get_comment (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), NULL); ++ ++ return app->priv->comment; ++} ++ ++GIcon * ++gsp_app_get_icon (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), NULL); ++ ++ if (app->priv->gicon) { ++ return g_object_ref (app->priv->gicon); ++ } else { ++ return NULL; ++ } ++} ++ ++unsigned int ++gsp_app_get_xdg_position (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT); ++ ++ return app->priv->xdg_position; ++} ++ ++unsigned int ++gsp_app_get_xdg_system_position (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT); ++ ++ return app->priv->xdg_system_position; ++} ++ ++void ++gsp_app_set_xdg_system_position (GspApp *app, ++ unsigned int position) ++{ ++ g_return_if_fail (GSP_IS_APP (app)); ++ ++ app->priv->xdg_system_position = position; ++} ++ ++const char * ++gsp_app_get_description (GspApp *app) ++{ ++ g_return_val_if_fail (GSP_IS_APP (app), NULL); ++ ++ return app->priv->description; ++} ++ ++/* ++ * High-level edition ++ */ ++ ++void ++gsp_app_update (GspApp *app, ++ const char *name, ++ const char *comment, ++ const char *exec) ++{ ++ gboolean changed; ++ ++ g_return_if_fail (GSP_IS_APP (app)); ++ ++ changed = FALSE; ++ ++ if (!_gsp_str_equal (name, app->priv->name)) { ++ changed = TRUE; ++ g_free (app->priv->name); ++ app->priv->name = g_strdup (name); ++ app->priv->save_mask |= GSP_ASP_SAVE_MASK_NAME; ++ } ++ ++ if (!_gsp_str_equal (comment, app->priv->comment)) { ++ changed = TRUE; ++ g_free (app->priv->comment); ++ app->priv->comment = g_strdup (comment); ++ app->priv->save_mask |= GSP_ASP_SAVE_MASK_COMMENT; ++ } ++ ++ if (changed) { ++ _gsp_app_update_description (app); ++ } ++ ++ if (!_gsp_str_equal (exec, app->priv->exec)) { ++ changed = TRUE; ++ g_free (app->priv->exec); ++ app->priv->exec = g_strdup (exec); ++ app->priv->save_mask |= GSP_ASP_SAVE_MASK_EXEC; ++ } ++ ++ if (changed) { ++ _gsp_app_queue_save (app); ++ _gsp_app_emit_changed (app); ++ } ++} ++ ++void ++gsp_app_delete (GspApp *app) ++{ ++ g_return_if_fail (GSP_IS_APP (app)); ++ ++ if (app->priv->xdg_position == 0 && ++ app->priv->xdg_system_position == G_MAXUINT) { ++ /* exists in user directory only */ ++ if (app->priv->save_timeout) { ++ g_source_remove (app->priv->save_timeout); ++ app->priv->save_timeout = 0; ++ } ++ ++ if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) { ++ g_remove (app->priv->path); ++ } ++ ++ /* for extra safety */ ++ app->priv->hidden = TRUE; ++ app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN; ++ ++ _gsp_app_emit_removed (app); ++ } else { ++ /* also exists in system directory, so we have to keep a file ++ * in the user directory */ ++ app->priv->hidden = TRUE; ++ app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN; ++ ++ _gsp_app_queue_save (app); ++ _gsp_app_emit_changed (app); ++ } ++} ++ ++/* ++ * New autostart app ++ */ ++ ++void ++gsp_app_reload_at (GspApp *app, ++ const char *path, ++ unsigned int xdg_position) ++{ ++ g_return_if_fail (GSP_IS_APP (app)); ++ ++ app->priv->xdg_position = G_MAXUINT; ++ gsp_app_new (path, xdg_position); ++} ++ ++GspApp * ++gsp_app_new (const char *path, ++ unsigned int xdg_position) ++{ ++ GspAppManager *manager; ++ GspApp *app; ++ GKeyFile *keyfile; ++ char *basename; ++ gboolean new; ++ ++ basename = g_path_get_basename (path); ++ ++ manager = gsp_app_manager_get (); ++ app = gsp_app_manager_find_app_with_basename (manager, basename); ++ g_object_unref (manager); ++ ++ new = (app == NULL); ++ ++ if (!new) { ++ if (app->priv->xdg_position == xdg_position) { ++ if (app->priv->skip_next_monitor_event) { ++ app->priv->skip_next_monitor_event = FALSE; ++ return NULL; ++ } ++ /* else: the file got changed but not by us, we'll ++ * update our data from disk */ ++ } ++ ++ if (app->priv->xdg_position < xdg_position || ++ app->priv->save_timeout != 0) { ++ /* we don't really care about this file, since we ++ * already have something with a higher priority, or ++ * we're going to write something in the user config ++ * anyway. ++ * Note: xdg_position >= 1 so it's a system dir */ ++ app->priv->xdg_system_position = MIN (xdg_position, ++ app->priv->xdg_system_position); ++ return NULL; ++ } ++ } ++ ++ keyfile = g_key_file_new (); ++ if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { ++ g_key_file_free (keyfile); ++ g_free (basename); ++ return NULL; ++ } ++ ++ if (new) { ++ app = g_object_new (GSP_TYPE_APP, NULL); ++ app->priv->basename = basename; ++ } else { ++ g_free (basename); ++ _gsp_app_free_reusable_data (app); ++ } ++ ++ app->priv->path = g_strdup (path); ++ ++ app->priv->hidden = gsp_key_file_get_boolean (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_HIDDEN, ++ FALSE); ++ app->priv->no_display = gsp_key_file_get_boolean (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, ++ FALSE); ++ app->priv->enabled = gsp_key_file_get_boolean (keyfile, ++ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED, ++ TRUE); ++ app->priv->shown = gsp_key_file_get_shown (keyfile, ++ gsm_util_get_current_desktop ()); ++ ++ app->priv->name = gsp_key_file_get_locale_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_NAME); ++ app->priv->exec = gsp_key_file_get_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_EXEC); ++ app->priv->comment = gsp_key_file_get_locale_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_COMMENT); ++ ++ if (gsm_util_text_is_blank (app->priv->name)) { ++ g_free (app->priv->name); ++ app->priv->name = g_strdup (app->priv->exec); ++ } ++ ++ app->priv->icon = gsp_key_file_get_locale_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_ICON); ++ ++ if (app->priv->icon) { ++ /* look at icon and see if it's a themed icon or not */ ++ if (g_path_is_absolute (app->priv->icon)) { ++ GFile *iconfile; ++ ++ iconfile = g_file_new_for_path (app->priv->icon); ++ app->priv->gicon = g_file_icon_new (iconfile); ++ g_object_unref (iconfile); ++ } else { ++ app->priv->gicon = g_themed_icon_new (app->priv->icon); ++ } ++ } else { ++ app->priv->gicon = NULL; ++ } ++ ++ g_key_file_free (keyfile); ++ ++ _gsp_app_update_description (app); ++ ++ if (xdg_position > 0) { ++ g_assert (xdg_position <= app->priv->xdg_system_position); ++ app->priv->xdg_system_position = xdg_position; ++ } ++ /* else we keep the old value (which is G_MAXUINT if it wasn't set) */ ++ app->priv->xdg_position = xdg_position; ++ ++ g_assert (!new || app->priv->save_timeout == 0); ++ app->priv->save_timeout = 0; ++ app->priv->old_system_path = NULL; ++ app->priv->skip_next_monitor_event = FALSE; ++ ++ if (!new) { ++ _gsp_app_emit_changed (app); ++ } ++ ++ return app; ++} ++ ++static char * ++_gsp_find_free_basename (const char *suggested_basename) ++{ ++ GspAppManager *manager; ++ char *base_path; ++ char *filename; ++ char *basename; ++ int i; ++ ++ if (g_str_has_suffix (suggested_basename, ".desktop")) { ++ char *basename_no_ext; ++ ++ basename_no_ext = g_strndup (suggested_basename, ++ strlen (suggested_basename) - strlen (".desktop")); ++ base_path = g_build_filename (g_get_user_config_dir (), ++ "autostart", ++ basename_no_ext, NULL); ++ g_free (basename_no_ext); ++ } else { ++ base_path = g_build_filename (g_get_user_config_dir (), ++ "autostart", ++ suggested_basename, NULL); ++ } ++ ++ filename = g_strdup_printf ("%s.desktop", base_path); ++ basename = g_path_get_basename (filename); ++ ++ manager = gsp_app_manager_get (); ++ ++ i = 1; ++#define _GSP_FIND_MAX_TRY 10000 ++ while (gsp_app_manager_find_app_with_basename (manager, ++ basename) != NULL && ++ g_file_test (filename, G_FILE_TEST_EXISTS) && ++ i < _GSP_FIND_MAX_TRY) { ++ g_free (filename); ++ g_free (basename); ++ ++ filename = g_strdup_printf ("%s-%d.desktop", base_path, i); ++ basename = g_path_get_basename (filename); ++ ++ i++; ++ } ++ ++ g_object_unref (manager); ++ ++ g_free (base_path); ++ g_free (filename); ++ ++ if (i == _GSP_FIND_MAX_TRY) { ++ g_free (basename); ++ return NULL; ++ } ++ ++ return basename; ++} ++ ++void ++gsp_app_create (const char *name, ++ const char *comment, ++ const char *exec) ++{ ++ GspAppManager *manager; ++ GspApp *app; ++ char *basename; ++ char **argv; ++ int argc; ++ ++ g_return_if_fail (!gsm_util_text_is_blank (exec)); ++ ++ if (!g_shell_parse_argv (exec, &argc, &argv, NULL)) { ++ return; ++ } ++ ++ basename = _gsp_find_free_basename (argv[0]); ++ g_strfreev (argv); ++ if (basename == NULL) { ++ return; ++ } ++ ++ app = g_object_new (GSP_TYPE_APP, NULL); ++ ++ app->priv->basename = basename; ++ app->priv->path = g_build_filename (g_get_user_config_dir (), ++ "autostart", ++ app->priv->basename, NULL); ++ ++ app->priv->hidden = FALSE; ++ app->priv->no_display = FALSE; ++ app->priv->enabled = TRUE; ++ app->priv->shown = TRUE; ++ ++ if (!gsm_util_text_is_blank (name)) { ++ app->priv->name = g_strdup (name); ++ } else { ++ app->priv->name = g_strdup (exec); ++ } ++ app->priv->exec = g_strdup (exec); ++ app->priv->comment = g_strdup (comment); ++ app->priv->icon = NULL; ++ ++ app->priv->gicon = NULL; ++ _gsp_app_update_description (app); ++ ++ /* by definition */ ++ app->priv->xdg_position = 0; ++ app->priv->xdg_system_position = G_MAXUINT; ++ ++ app->priv->save_timeout = 0; ++ app->priv->save_mask |= GSP_ASP_SAVE_MASK_ALL; ++ app->priv->old_system_path = NULL; ++ app->priv->skip_next_monitor_event = FALSE; ++ ++ _gsp_app_queue_save (app); ++ ++ manager = gsp_app_manager_get (); ++ gsp_app_manager_add (manager, app); ++ g_object_unref (app); ++ g_object_unref (manager); ++} ++ ++gboolean ++gsp_app_copy_desktop_file (const char *uri) ++{ ++ GspAppManager *manager; ++ GspApp *app; ++ GFile *src_file; ++ char *src_basename; ++ char *dst_basename; ++ char *dst_path; ++ GFile *dst_file; ++ gboolean changed; ++ ++ g_return_val_if_fail (uri != NULL, FALSE); ++ ++ src_file = g_file_new_for_uri (uri); ++ src_basename = g_file_get_basename (src_file); ++ ++ if (src_basename == NULL) { ++ g_object_unref (src_file); ++ return FALSE; ++ } ++ ++ dst_basename = _gsp_find_free_basename (src_basename); ++ g_free (src_basename); ++ ++ if (dst_basename == NULL) { ++ g_object_unref (src_file); ++ return FALSE; ++ } ++ ++ dst_path = g_build_filename (g_get_user_config_dir (), ++ "autostart", ++ dst_basename, NULL); ++ g_free (dst_basename); ++ ++ dst_file = g_file_new_for_path (dst_path); ++ ++ _gsp_ensure_user_autostart_dir (); ++ if (!g_file_copy (src_file, dst_file, G_FILE_COPY_NONE, ++ NULL, NULL, NULL, NULL)) { ++ g_object_unref (src_file); ++ g_object_unref (dst_file); ++ g_free (dst_path); ++ return FALSE; ++ } ++ ++ g_object_unref (src_file); ++ g_object_unref (dst_file); ++ ++ app = gsp_app_new (dst_path, 0); ++ if (!app) { ++ g_remove (dst_path); ++ g_free (dst_path); ++ return FALSE; ++ } ++ ++ g_free (dst_path); ++ ++ changed = FALSE; ++ if (app->priv->hidden) { ++ changed = TRUE; ++ app->priv->hidden = FALSE; ++ app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN; ++ } ++ ++ if (app->priv->no_display) { ++ changed = TRUE; ++ app->priv->no_display = FALSE; ++ app->priv->save_mask |= GSP_ASP_SAVE_MASK_NO_DISPLAY; ++ } ++ ++ if (!app->priv->enabled) { ++ changed = TRUE; ++ app->priv->enabled = TRUE; ++ app->priv->save_mask |= GSP_ASP_SAVE_MASK_ENABLED; ++ } ++ ++ if (changed) { ++ _gsp_app_queue_save (app); ++ } ++ ++ manager = gsp_app_manager_get (); ++ gsp_app_manager_add (manager, app); ++ g_object_unref (app); ++ g_object_unref (manager); ++ ++ return TRUE; ++} +diff --git a/capplet/gsp-app.h b/capplet/gsp-app.h +new file mode 100644 +index 00000000..a1997955 +--- /dev/null ++++ b/capplet/gsp-app.h +@@ -0,0 +1,108 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 1999 Free Software Foundation, Inc. ++ * Copyright (C) 2007, 2009 Vincent Untz. ++ * Copyright (C) 2008 Lucas Rocha. ++ * Copyright (C) 2008 William Jon McCann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++#ifndef __GSP_APP_H ++#define __GSP_APP_H ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define GSP_TYPE_APP (gsp_app_get_type ()) ++#define GSP_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSP_TYPE_APP, GspApp)) ++#define GSP_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSP_TYPE_APP, GspAppClass)) ++#define GSP_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSP_TYPE_APP)) ++#define GSP_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSP_TYPE_APP)) ++#define GSP_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSP_TYPE_APP, GspAppClass)) ++ ++typedef struct _GspApp GspApp; ++typedef struct _GspAppClass GspAppClass; ++ ++typedef struct _GspAppPrivate GspAppPrivate; ++ ++struct _GspAppClass ++{ ++ GObjectClass parent_class; ++ ++ void (* changed) (GspApp *app); ++ void (* removed) (GspApp *app); ++}; ++ ++struct _GspApp ++{ ++ GObject parent_instance; ++ ++ GspAppPrivate *priv; ++}; ++ ++GType gsp_app_get_type (void); ++ ++void gsp_app_create (const char *name, ++ const char *comment, ++ const char *exec); ++void gsp_app_update (GspApp *app, ++ const char *name, ++ const char *comment, ++ const char *exec); ++ ++gboolean gsp_app_copy_desktop_file (const char *uri); ++ ++void gsp_app_delete (GspApp *app); ++ ++const char *gsp_app_get_basename (GspApp *app); ++const char *gsp_app_get_path (GspApp *app); ++ ++gboolean gsp_app_get_hidden (GspApp *app); ++gboolean gsp_app_get_display (GspApp *app); ++ ++gboolean gsp_app_get_enabled (GspApp *app); ++void gsp_app_set_enabled (GspApp *app, ++ gboolean enabled); ++ ++gboolean gsp_app_get_shown (GspApp *app); ++ ++const char *gsp_app_get_name (GspApp *app); ++const char *gsp_app_get_exec (GspApp *app); ++const char *gsp_app_get_comment (GspApp *app); ++ ++const char *gsp_app_get_description (GspApp *app); ++GIcon *gsp_app_get_icon (GspApp *app); ++ ++/* private interface for GspAppManager only */ ++ ++GspApp *gsp_app_new (const char *path, ++ unsigned int xdg_position); ++ ++void gsp_app_reload_at (GspApp *app, ++ const char *path, ++ unsigned int xdg_position); ++ ++unsigned int gsp_app_get_xdg_position (GspApp *app); ++unsigned int gsp_app_get_xdg_system_position (GspApp *app); ++void gsp_app_set_xdg_system_position (GspApp *app, ++ unsigned int position); ++ ++G_END_DECLS ++ ++#endif /* __GSP_APP_H */ +diff --git a/capplet/gsp-keyfile.c b/capplet/gsp-keyfile.c +new file mode 100644 +index 00000000..de9fac0e +--- /dev/null ++++ b/capplet/gsp-keyfile.c +@@ -0,0 +1,201 @@ ++/* ++ * gsp-keyfile.c: GKeyFile extensions ++ * ++ * Copyright (C) 2008, 2009 Novell, Inc. ++ * ++ * Based on code from panel-keyfile.c (from gnome-panel) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ++ * 02111-1307, USA. ++ * ++ * Authors: ++ * Vincent Untz ++ */ ++ ++#include ++ ++#include ++ ++#include "gsp-keyfile.h" ++ ++void ++gsp_key_file_populate (GKeyFile *keyfile) ++{ ++ gsp_key_file_set_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_TYPE, ++ "Application"); ++ ++ gsp_key_file_set_string (keyfile, ++ G_KEY_FILE_DESKTOP_KEY_EXEC, ++ "/bin/false"); ++} ++ ++//FIXME: kill this when bug #309224 is fixed ++gboolean ++gsp_key_file_to_file (GKeyFile *keyfile, ++ const gchar *path, ++ GError **error) ++{ ++ GError *write_error; ++ gchar *data; ++ gsize length; ++ gboolean res; ++ ++ g_return_val_if_fail (keyfile != NULL, FALSE); ++ g_return_val_if_fail (path != NULL, FALSE); ++ ++ write_error = NULL; ++ data = g_key_file_to_data (keyfile, &length, &write_error); ++ ++ if (write_error) { ++ g_propagate_error (error, write_error); ++ return FALSE; ++ } ++ ++ res = g_file_set_contents (path, data, length, &write_error); ++ g_free (data); ++ ++ if (write_error) { ++ g_propagate_error (error, write_error); ++ return FALSE; ++ } ++ ++ return res; ++} ++ ++gboolean ++gsp_key_file_get_boolean (GKeyFile *keyfile, ++ const gchar *key, ++ gboolean default_value) ++{ ++ GError *error; ++ gboolean retval; ++ ++ error = NULL; ++ retval = g_key_file_get_boolean (keyfile, G_KEY_FILE_DESKTOP_GROUP, ++ key, &error); ++ if (error != NULL) { ++ retval = default_value; ++ g_error_free (error); ++ } ++ ++ return retval; ++} ++ ++gboolean ++gsp_key_file_get_shown (GKeyFile *keyfile, ++ const char *current_desktop) ++{ ++ char **only_show_in, **not_show_in; ++ gboolean found; ++ int i; ++ ++ if (!current_desktop) ++ return TRUE; ++ ++ only_show_in = g_key_file_get_string_list (keyfile, G_KEY_FILE_DESKTOP_GROUP, ++ G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, ++ NULL, NULL); ++ ++ if (only_show_in) { ++ found = FALSE; ++ for (i = 0; only_show_in[i] != NULL; i++) { ++ if (g_strcmp0 (current_desktop, only_show_in[i]) == 0) { ++ found = TRUE; ++ break; ++ } ++ } ++ ++ g_strfreev (only_show_in); ++ ++ if (!found) ++ return FALSE; ++ } ++ ++ not_show_in = g_key_file_get_string_list (keyfile, G_KEY_FILE_DESKTOP_GROUP, ++ G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, ++ NULL, NULL); ++ ++ if (not_show_in) { ++ found = FALSE; ++ for (i = 0; not_show_in[i] != NULL; i++) { ++ if (g_strcmp0 (current_desktop, not_show_in[i]) == 0) { ++ found = TRUE; ++ break; ++ } ++ } ++ ++ g_strfreev (not_show_in); ++ ++ if (found) ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++void ++gsp_key_file_set_locale_string (GKeyFile *keyfile, ++ const gchar *key, ++ const gchar *value) ++{ ++ const char *locale; ++ const char * const *langs_pointer; ++ int i; ++ ++ if (value == NULL) { ++ value = ""; ++ } ++ ++ locale = NULL; ++ langs_pointer = g_get_language_names (); ++ for (i = 0; langs_pointer[i] != NULL; i++) { ++ /* find first without encoding */ ++ if (strchr (langs_pointer[i], '.') == NULL) { ++ locale = langs_pointer[i]; ++ break; ++ } ++ } ++ ++ if (locale != NULL) { ++ g_key_file_set_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, ++ key, locale, value); ++ } else { ++ g_key_file_set_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, ++ key, value); ++ } ++} ++ ++void ++gsp_key_file_ensure_C_key (GKeyFile *keyfile, ++ const char *key) ++{ ++ char *C_value; ++ char *buffer; ++ ++ /* Make sure we set the "C" locale strings to the terms we set here. ++ * This is so that if the user logs into another locale they get their ++ * own description there rather then empty. It is not the C locale ++ * however, but the user created this entry herself so it's OK */ ++ C_value = gsp_key_file_get_string (keyfile, key); ++ if (C_value == NULL || C_value [0] == '\0') { ++ buffer = gsp_key_file_get_locale_string (keyfile, key); ++ if (buffer) { ++ gsp_key_file_set_string (keyfile, key, buffer); ++ g_free (buffer); ++ } ++ } ++ g_free (C_value); ++} +diff --git a/capplet/gsp-keyfile.h b/capplet/gsp-keyfile.h +new file mode 100644 +index 00000000..d94f6672 +--- /dev/null ++++ b/capplet/gsp-keyfile.h +@@ -0,0 +1,65 @@ ++/* ++ * gsp-keyfile.h: GKeyFile extensions ++ * ++ * Copyright (C) 2008, 2009 Novell, Inc. ++ * ++ * Based on code from panel-keyfile.h (from gnome-panel) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ++ * 02111-1307, USA. ++ * ++ * Authors: ++ * Vincent Untz ++ */ ++ ++#ifndef GSP_KEYFILE_H ++#define GSP_KEYFILE_H ++ ++#include "glib.h" ++ ++G_BEGIN_DECLS ++ ++#define GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED "X-GNOME-Autostart-enabled" ++ ++void gsp_key_file_populate (GKeyFile *keyfile); ++ ++gboolean gsp_key_file_to_file (GKeyFile *keyfile, ++ const gchar *path, ++ GError **error); ++ ++gboolean gsp_key_file_get_boolean (GKeyFile *keyfile, ++ const gchar *key, ++ gboolean default_value); ++gboolean gsp_key_file_get_shown (GKeyFile *keyfile, ++ const char *current_desktop); ++#define gsp_key_file_get_string(key_file, key) \ ++ g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL) ++#define gsp_key_file_get_locale_string(key_file, key) \ ++ g_key_file_get_locale_string(key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL) ++ ++#define gsp_key_file_set_boolean(key_file, key, value) \ ++ g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, key, value) ++#define gsp_key_file_set_string(key_file, key, value) \ ++ g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, key, value) ++void gsp_key_file_set_locale_string (GKeyFile *keyfile, ++ const gchar *key, ++ const gchar *value); ++ ++void gsp_key_file_ensure_C_key (GKeyFile *keyfile, ++ const char *key); ++ ++G_END_DECLS ++ ++#endif /* GSP_KEYFILE_H */ +diff --git a/capplet/main.c b/capplet/main.c +new file mode 100644 +index 00000000..3c7177bc +--- /dev/null ++++ b/capplet/main.c +@@ -0,0 +1,108 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * main.c ++ * Copyright (C) 1999 Free Software Foundation, Inc. ++ * Copyright (C) 2008 Lucas Rocha. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ++ * 02111-1307, USA. ++ */ ++ ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "gsm-properties-dialog.h" ++ ++static gboolean show_version = FALSE; ++ ++static GOptionEntry options[] = { ++ { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL }, ++ { NULL, 0, 0, 0, NULL, NULL, NULL } ++}; ++ ++static void ++dialog_response (GsmPropertiesDialog *dialog, ++ guint response_id, ++ gpointer data) ++{ ++ GdkScreen *screen; ++ GError *error; ++ ++ if (response_id == GTK_RESPONSE_HELP) { ++ screen = gtk_widget_get_screen (GTK_WIDGET (dialog)); ++ ++ error = NULL; ++ gtk_show_uri (screen, "ghelp:user-guide?gosstartsession-2", ++ gtk_get_current_event_time (), &error); ++ ++ if (error != NULL) { ++ GtkWidget *d; ++ d = gtk_message_dialog_new (GTK_WINDOW (dialog), ++ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, ++ GTK_MESSAGE_ERROR, ++ GTK_BUTTONS_CLOSE, ++ "%s", ++ _("Could not display help document")); ++ gtk_message_dialog_format_secondary_text ( ++ GTK_MESSAGE_DIALOG (d), ++ "%s", error->message); ++ g_error_free (error); ++ ++ gtk_dialog_run (GTK_DIALOG (d)); ++ gtk_widget_destroy (d); ++ } ++ } else { ++ gtk_widget_destroy (GTK_WIDGET (dialog)); ++ gtk_main_quit (); ++ } ++} ++ ++int ++main (int argc, char *argv[]) ++{ ++ GError *error; ++ GtkWidget *dialog; ++ ++ bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); ++ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); ++ textdomain (GETTEXT_PACKAGE); ++ ++ error = NULL; ++ if (! gtk_init_with_args (&argc, &argv, " - GNOME Session Properties", options, GETTEXT_PACKAGE, &error)) { ++ g_warning ("Unable to start: %s", error->message); ++ g_error_free (error); ++ return 1; ++ } ++ ++ if (show_version) { ++ g_print ("%s %s\n", argv [0], VERSION); ++ return 0; ++ } ++ ++ dialog = gsm_properties_dialog_new (); ++ g_signal_connect (dialog, ++ "response", ++ G_CALLBACK (dialog_response), ++ NULL); ++ gtk_widget_show (dialog); ++ ++ gtk_main (); ++ ++ return 0; ++} +diff --git a/configure.ac b/configure.ac +index f57dcf3d..b3f285ed 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -337,65 +337,67 @@ if test $enable_ipv6 = yes; then + ]])], + [have_ipv6=yes], + [have_ipv6=no] + ) + AC_MSG_RESULT($have_ipv6) + + dnl ================================================================= + dnl Now we would check for specific function like getaddrinfo. + dnl ================================================================= + have_getaddrinfo=no + if test $have_ipv6=yes; then + AC_CHECK_FUNC(getaddrinfo, have_getaddrinfo=yes) + if test $have_getaddrinfo != yes; then + # getaddrinfo is not in the default libraries. See if it's in some other. + for lib in bsd socket inet; do + AC_CHECK_LIB($lib, getaddrinfo, [LIBS="$LIBS -l$lib";have_getaddrinfo=yes; break]) + done + fi + if test $have_getaddrinfo=yes; then + AC_DEFINE(ENABLE_IPV6, 1, [Define if IPV6 is supported]) + have_full_ipv6=yes + fi + fi + fi + dnl ============================================================================== + dnl End of IPv6 checks + dnl ============================================================================== + + AC_CONFIG_FILES([ + Makefile ++capplet/Makefile + doc/Makefile + doc/dbus/Makefile + doc/dbus/gnome-session.xml + doc/man/Makefile + data/Makefile ++data/gnome-session-properties.desktop.in + data/org.gnome.SessionManager.gschema.xml + data/icons/Makefile + data/icons/16x16/Makefile + data/icons/22x22/Makefile + data/icons/24x24/Makefile + data/icons/32x32/Makefile + data/icons/48x48/Makefile + data/icons/scalable/Makefile + data/icons/symbolic/Makefile + gnome-session/Makefile + tools/Makefile + po/Makefile.in + ]) + AC_OUTPUT + + dnl --------------------------------------------------------------------------- + dnl - Show summary + dnl --------------------------------------------------------------------------- + + echo " + gnome-session $VERSION + `echo gnome-session $VERSION | sed "s/./=/g"` + + prefix: ${prefix} + exec_prefix: ${exec_prefix} + libdir: ${libdir} + bindir: ${bindir} + sbindir: ${sbindir} + sysconfdir: ${sysconfdir} + localstatedir: ${localstatedir} +diff --git a/data/Makefile.am b/data/Makefile.am +index 413279a2..b5c3f5ec 100644 +--- a/data/Makefile.am ++++ b/data/Makefile.am +@@ -1,67 +1,73 @@ + SUBDIRS = icons + + uidir = $(pkgdatadir) + ui_DATA = \ + session-properties.ui + + if BUILD_SESSION_SELECTOR + ui_DATA += session-selector.ui + endif + + hwcompatdir = $(pkgdatadir) + hwcompat_DATA = hardware-compatibility + + xsessiondir = $(datadir)/xsessions + xsession_in_files = gnome.desktop.in gnome-xorg.desktop.in + + if BUILD_SESSION_SELECTOR + xsession_in_files += gnome-custom-session.desktop.in + endif + + xsession_DATA = $(xsession_in_files:.desktop.in=.desktop) + + wayland_sessiondir = $(datadir)/wayland-sessions + wayland_session_in_files = gnome.desktop.in + wayland_session_DATA = $(wayland_session_in_files:.desktop.in=.desktop) + ++desktopdir = $(datadir)/applications ++desktop_in_files = gnome-session-properties.desktop.in ++desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) ++ + sessiondir = $(datadir)/gnome-session/sessions + session_in_in_files = gnome.session.desktop.in.in gnome-dummy.session.desktop.in.in + session_in_files = $(session_in_in_files:.session.desktop.in.in=.session.desktop.in) + session_DATA = $(session_in_files:.session.desktop.in=.session) + + %.session.desktop.in: %.session.desktop.in.in Makefile + $(AM_V_GEN)sed \ + -e "s|\@LIBEXECDIR\@|$(libexecdir)|" \ + $< > $@ + + %.session: %.session.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@ + + @INTLTOOL_DESKTOP_RULE@ + @INTLTOOL_XML_NOMERGE_RULE@ + + gsettings_SCHEMAS = org.gnome.SessionManager.gschema.xml + @GSETTINGS_RULES@ + + migrationdir = $(datadir)/GConf/gsettings + dist_migration_DATA = gnome-session.convert + + EXTRA_DIST = \ + $(xsession_in_files) \ + $(session_in_in_files) \ + $(wayland_session_in_files) \ + $(gsettings_SCHEMAS:.xml=.xml.in) \ + session-selector.ui \ + gnome-custom-session.desktop.in \ + $(ui_DATA) \ + $(hwcompat_DATA) + + CLEANFILES = \ + $(gsettings_SCHEMAS) \ + $(xsession_DATA) \ + $(wayland_session_DATA) \ ++ $(desktop_DATA) \ + $(session_DATA) + + DISTCLEANFILES = \ +- $(gsettings_SCHEMAS) ++ $(gsettings_SCHEMAS) \ ++ $(desktop_in_files) + + -include $(top_srcdir)/git.mk +diff --git a/data/gnome-session-properties.desktop.in.in b/data/gnome-session-properties.desktop.in.in +new file mode 100644 +index 00000000..3dc7b033 +--- /dev/null ++++ b/data/gnome-session-properties.desktop.in.in +@@ -0,0 +1,15 @@ ++[Desktop Entry] ++_Name=Startup Applications ++_Comment=Choose what applications to start when you log in ++Exec=gnome-session-properties ++Icon=session-properties ++Terminal=false ++Type=Application ++StartupNotify=true ++Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings; ++OnlyShowIn=GNOME;Unity; ++NoDisplay=true ++X-GNOME-Bugzilla-Bugzilla=GNOME ++X-GNOME-Bugzilla-Product=gnome-session ++X-GNOME-Bugzilla-Component=gnome-session-properties ++X-GNOME-Bugzilla-Version=@VERSION@ +diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am +index 02e5c62a..28794233 100644 +--- a/doc/man/Makefile.am ++++ b/doc/man/Makefile.am +@@ -1,29 +1,30 @@ + XSLTPROC_FLAGS = \ + --nonet \ + --stringparam man.output.quietly 1 \ + --stringparam funcsynopsis.style ansi \ + --stringparam man.th.extra1.suppress 1 \ + --stringparam man.authors.section.enabled 0 \ + --stringparam man.copyright.section.enabled 0 + + .xml.1: + $(AM_V_GEN) $(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< + + man_MANS = \ + gnome-session.1 \ ++ gnome-session-properties.1 \ + gnome-session-quit.1 \ + gnome-session-inhibit.1 + + if BUILD_SESSION_SELECTOR + man_MANS += gnome-session-selector.1 + endif + + EXTRA_DIST = \ + gnome-session-inhibit.xml \ + gnome-session-selector.xml \ + $(man_MANS) + + CLEANFILES = \ + gnome-session-inhibit.1 + + -include $(top_srcdir)/git.mk +diff --git a/doc/man/gnome-session-properties.1 b/doc/man/gnome-session-properties.1 +new file mode 100644 +index 00000000..c7ef1af3 +--- /dev/null ++++ b/doc/man/gnome-session-properties.1 +@@ -0,0 +1,24 @@ ++.\" ++.\" gnome-session-properties manual page. ++.\" (C) 2009-2010 Vincent Untz (vuntz@gnome.org) ++.\" ++.TH GNOME-SESSION-PROPERTIES 1 "GNOME" ++.SH NAME ++gnome-session-properties \- Configure applications to start on login ++.SH SYNOPSIS ++.B gnome-session-properties ++.SH DESCRIPTION ++.PP ++The \fIgnome-session-properties\fP program enables the users to ++configure what applications should be started on login, in addition to ++the default startup applications configured on the system. ++.PP ++It also proposes an interface to save a snapshot of the currently ++running applications so that they can automatically be restored to ++their current state on your next GNOME session. ++.SH BUGS ++If you find bugs in the \fIgnome-session-properties\fP program, please report ++these on https://bugzilla.gnome.org. ++.SH SEE ALSO ++.BR gnome-session(1) ++.BR gnome-session-quit(1) +diff --git a/po/POTFILES.in b/po/POTFILES.in +index 76882645..5cb5123d 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -1,19 +1,24 @@ + # List of source files containing translatable strings. + # Please keep this file sorted alphabetically. ++capplet/gsm-app-dialog.c ++capplet/gsm-properties-dialog.c ++capplet/gsp-app.c ++capplet/main.c + data/gnome-custom-session.desktop.in + data/gnome.desktop.in + data/gnome-xorg.desktop.in + data/gnome-dummy.session.desktop.in.in + data/gnome.session.desktop.in.in ++data/gnome-session-properties.desktop.in.in + [type: gettext/glade]data/session-selector.ui + [type: gettext/glade]data/session-properties.ui + gnome-session/gsm-fail-whale-dialog.c + gnome-session/gsm-manager.c + gnome-session/gsm-process-helper.c + gnome-session/gsm-util.c + gnome-session/gsm-xsmp-client.c + gnome-session/gsm-xsmp-server.c + gnome-session/main.c + tools/gnome-session-inhibit.c + tools/gnome-session-selector.c + tools/gnome-session-quit.c +-- +2.14.2 + diff --git a/SOURCES/0002-autostart-ensure-gnome-shell-and-mutter-get-right-au.patch b/SOURCES/0002-autostart-ensure-gnome-shell-and-mutter-get-right-au.patch new file mode 100644 index 0000000..6784c45 --- /dev/null +++ b/SOURCES/0002-autostart-ensure-gnome-shell-and-mutter-get-right-au.patch @@ -0,0 +1,96 @@ +From 622e54d54c65eba7c5d8e4172f8aae8a970126f9 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 18 Jan 2018 10:09:36 -0500 +Subject: [PATCH 2/2] autostart: ensure gnome-shell and mutter get right + autostart phase + +Previous versions of gnome-shell neglected to save the autostart phases +for required components in the session state. While that is now fixed, +users may still have old saved state that lack the autostart phase. + +In order to ease upgrades, this commit manually adds in the phases that +are important for a functioning GNOME desktop. +--- + gnome-session/gsm-autostart-app.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/gnome-session/gsm-autostart-app.c b/gnome-session/gsm-autostart-app.c +index 9eb1db5b..5617e549 100644 +--- a/gnome-session/gsm-autostart-app.c ++++ b/gnome-session/gsm-autostart-app.c +@@ -621,61 +621,71 @@ load_desktop_file (GsmAutostartApp *app) + char *startup_id; + char *phase_str; + int phase; + gboolean res; + + g_assert (app->priv->app_info != NULL); + + phase_str = g_desktop_app_info_get_string (app->priv->app_info, + GSM_AUTOSTART_APP_PHASE_KEY); + if (phase_str != NULL) { + if (strcmp (phase_str, "EarlyInitialization") == 0) { + phase = GSM_MANAGER_PHASE_EARLY_INITIALIZATION; + } else if (strcmp (phase_str, "PreDisplayServer") == 0) { + phase = GSM_MANAGER_PHASE_PRE_DISPLAY_SERVER; + } else if (strcmp (phase_str, "DisplayServer") == 0) { + phase = GSM_MANAGER_PHASE_DISPLAY_SERVER; + } else if (strcmp (phase_str, "Initialization") == 0) { + phase = GSM_MANAGER_PHASE_INITIALIZATION; + } else if (strcmp (phase_str, "WindowManager") == 0) { + phase = GSM_MANAGER_PHASE_WINDOW_MANAGER; + } else if (strcmp (phase_str, "Panel") == 0) { + phase = GSM_MANAGER_PHASE_PANEL; + } else if (strcmp (phase_str, "Desktop") == 0) { + phase = GSM_MANAGER_PHASE_DESKTOP; + } else { + phase = GSM_MANAGER_PHASE_APPLICATION; + } + + g_free (phase_str); + } else { +- phase = GSM_MANAGER_PHASE_APPLICATION; ++ const char *app_id; ++ ++ app_id = g_app_info_get_id (G_APP_INFO (app->priv->app_info)); ++ ++ /* These hardcoded checks are to keep upgrades working */ ++ if (app_id != NULL && g_str_has_prefix (app_id, "org.gnome.Shell")) ++ phase = GSM_MANAGER_PHASE_DISPLAY_SERVER; ++ else if (app_id != NULL && g_str_has_prefix (app_id, "org.gnome.SettingsDaemon")) ++ phase = GSM_MANAGER_PHASE_INITIALIZATION; ++ else ++ phase = GSM_MANAGER_PHASE_APPLICATION; + } + + dbus_name = g_desktop_app_info_get_string (app->priv->app_info, + GSM_AUTOSTART_APP_DBUS_NAME_KEY); + if (dbus_name != NULL) { + app->priv->launch_type = AUTOSTART_LAUNCH_ACTIVATE; + } else { + app->priv->launch_type = AUTOSTART_LAUNCH_SPAWN; + } + + /* this must only be done on first load */ + switch (app->priv->launch_type) { + case AUTOSTART_LAUNCH_SPAWN: + startup_id = + g_desktop_app_info_get_string (app->priv->app_info, + GSM_AUTOSTART_APP_STARTUP_ID_KEY); + + if (startup_id == NULL) { + startup_id = gsm_util_generate_startup_id (); + } + break; + case AUTOSTART_LAUNCH_ACTIVATE: + startup_id = g_strdup (dbus_name); + break; + default: + g_assert_not_reached (); + } + + res = g_desktop_app_info_has_key (app->priv->app_info, + GSM_AUTOSTART_APP_AUTORESTART_KEY); +-- +2.14.3 + diff --git a/SOURCES/0003-Revert-Rename-the-desktop-file-to-gnome-session-prop.patch b/SOURCES/0003-Revert-Rename-the-desktop-file-to-gnome-session-prop.patch new file mode 100644 index 0000000..d52b1be --- /dev/null +++ b/SOURCES/0003-Revert-Rename-the-desktop-file-to-gnome-session-prop.patch @@ -0,0 +1,195 @@ +From 808e1598dc50484f62998b4a9e94e956f028d362 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 8 May 2015 16:27:15 -0400 +Subject: [PATCH 03/19] Revert "Rename the desktop file to + gnome-session-properties.desktop" + +This reverts commit ac9fd0dc97a17674cb082f80df0b1fcc45bc92bf. +--- + configure.ac | 2 +- + data/Makefile.am | 2 +- + ...ession-properties.desktop.in.in => session-properties.desktop.in.in} | 0 + po/POTFILES.in | 1 + + po/POTFILES.skip | 1 + + 5 files changed, 4 insertions(+), 2 deletions(-) + rename data/{gnome-session-properties.desktop.in.in => session-properties.desktop.in.in} (100%) + +diff --git a/configure.ac b/configure.ac +index b3f285ed..5182c09e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -343,61 +343,61 @@ if test $enable_ipv6 = yes; then + dnl ================================================================= + dnl Now we would check for specific function like getaddrinfo. + dnl ================================================================= + have_getaddrinfo=no + if test $have_ipv6=yes; then + AC_CHECK_FUNC(getaddrinfo, have_getaddrinfo=yes) + if test $have_getaddrinfo != yes; then + # getaddrinfo is not in the default libraries. See if it's in some other. + for lib in bsd socket inet; do + AC_CHECK_LIB($lib, getaddrinfo, [LIBS="$LIBS -l$lib";have_getaddrinfo=yes; break]) + done + fi + if test $have_getaddrinfo=yes; then + AC_DEFINE(ENABLE_IPV6, 1, [Define if IPV6 is supported]) + have_full_ipv6=yes + fi + fi + fi + dnl ============================================================================== + dnl End of IPv6 checks + dnl ============================================================================== + + AC_CONFIG_FILES([ + Makefile + capplet/Makefile + doc/Makefile + doc/dbus/Makefile + doc/dbus/gnome-session.xml + doc/man/Makefile + data/Makefile +-data/gnome-session-properties.desktop.in ++data/session-properties.desktop.in + data/org.gnome.SessionManager.gschema.xml + data/icons/Makefile + data/icons/16x16/Makefile + data/icons/22x22/Makefile + data/icons/24x24/Makefile + data/icons/32x32/Makefile + data/icons/48x48/Makefile + data/icons/scalable/Makefile + data/icons/symbolic/Makefile + gnome-session/Makefile + tools/Makefile + po/Makefile.in + ]) + AC_OUTPUT + + dnl --------------------------------------------------------------------------- + dnl - Show summary + dnl --------------------------------------------------------------------------- + + echo " + gnome-session $VERSION + `echo gnome-session $VERSION | sed "s/./=/g"` + + prefix: ${prefix} + exec_prefix: ${exec_prefix} + libdir: ${libdir} + bindir: ${bindir} + sbindir: ${sbindir} + sysconfdir: ${sysconfdir} + localstatedir: ${localstatedir} +diff --git a/data/Makefile.am b/data/Makefile.am +index b5c3f5ec..0bb25c06 100644 +--- a/data/Makefile.am ++++ b/data/Makefile.am +@@ -1,58 +1,58 @@ + SUBDIRS = icons + + uidir = $(pkgdatadir) + ui_DATA = \ + session-properties.ui + + if BUILD_SESSION_SELECTOR + ui_DATA += session-selector.ui + endif + + hwcompatdir = $(pkgdatadir) + hwcompat_DATA = hardware-compatibility + + xsessiondir = $(datadir)/xsessions + xsession_in_files = gnome.desktop.in gnome-xorg.desktop.in + + if BUILD_SESSION_SELECTOR + xsession_in_files += gnome-custom-session.desktop.in + endif + + xsession_DATA = $(xsession_in_files:.desktop.in=.desktop) + + wayland_sessiondir = $(datadir)/wayland-sessions + wayland_session_in_files = gnome.desktop.in + wayland_session_DATA = $(wayland_session_in_files:.desktop.in=.desktop) + + desktopdir = $(datadir)/applications +-desktop_in_files = gnome-session-properties.desktop.in ++desktop_in_files = session-properties.desktop.in + desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) + + sessiondir = $(datadir)/gnome-session/sessions + session_in_in_files = gnome.session.desktop.in.in gnome-dummy.session.desktop.in.in + session_in_files = $(session_in_in_files:.session.desktop.in.in=.session.desktop.in) + session_DATA = $(session_in_files:.session.desktop.in=.session) + + %.session.desktop.in: %.session.desktop.in.in Makefile + $(AM_V_GEN)sed \ + -e "s|\@LIBEXECDIR\@|$(libexecdir)|" \ + $< > $@ + + %.session: %.session.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@ + + @INTLTOOL_DESKTOP_RULE@ + @INTLTOOL_XML_NOMERGE_RULE@ + + gsettings_SCHEMAS = org.gnome.SessionManager.gschema.xml + @GSETTINGS_RULES@ + + migrationdir = $(datadir)/GConf/gsettings + dist_migration_DATA = gnome-session.convert + + EXTRA_DIST = \ + $(xsession_in_files) \ + $(session_in_in_files) \ + $(wayland_session_in_files) \ + $(gsettings_SCHEMAS:.xml=.xml.in) \ + session-selector.ui \ + gnome-custom-session.desktop.in \ +diff --git a/data/gnome-session-properties.desktop.in.in b/data/session-properties.desktop.in.in +similarity index 100% +rename from data/gnome-session-properties.desktop.in.in +rename to data/session-properties.desktop.in.in +diff --git a/po/POTFILES.in b/po/POTFILES.in +index 5cb5123d..08d2eb06 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -1,24 +1,25 @@ + # List of source files containing translatable strings. + # Please keep this file sorted alphabetically. + capplet/gsm-app-dialog.c + capplet/gsm-properties-dialog.c + capplet/gsp-app.c + capplet/main.c + data/gnome-custom-session.desktop.in + data/gnome.desktop.in + data/gnome-xorg.desktop.in + data/gnome-dummy.session.desktop.in.in + data/gnome.session.desktop.in.in + data/gnome-session-properties.desktop.in.in + [type: gettext/glade]data/session-selector.ui ++data/session-properties.desktop.in.in + [type: gettext/glade]data/session-properties.ui + gnome-session/gsm-fail-whale-dialog.c + gnome-session/gsm-manager.c + gnome-session/gsm-process-helper.c + gnome-session/gsm-util.c + gnome-session/gsm-xsmp-client.c + gnome-session/gsm-xsmp-server.c + gnome-session/main.c + tools/gnome-session-inhibit.c + tools/gnome-session-selector.c + tools/gnome-session-quit.c +diff --git a/po/POTFILES.skip b/po/POTFILES.skip +index 91b41569..e6470914 100644 +--- a/po/POTFILES.skip ++++ b/po/POTFILES.skip +@@ -1,5 +1,6 @@ + # List of source files containing translatable strings that should not be + # translated. + # Please keep this file sorted alphabetically. + data/gnome-dummy.session.desktop.in + data/gnome.session.desktop.in ++data/session-properties.desktop.in +-- +2.14.2 + diff --git a/SOURCES/0004-stop-using-gsm_util_get_current_desktop.patch b/SOURCES/0004-stop-using-gsm_util_get_current_desktop.patch new file mode 100644 index 0000000..67eb7e8 --- /dev/null +++ b/SOURCES/0004-stop-using-gsm_util_get_current_desktop.patch @@ -0,0 +1,441 @@ +From 2a087ede1b20e8dcac1c37c0b280f9bf6be7c93b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 13 May 2015 10:56:09 -0400 +Subject: [PATCH 04/19] stop using gsm_util_get_current_desktop + +It no longer exists. +--- + capplet/gsp-app.c | 111 +++++++++++++++++++++++++++++------------------------- + 1 file changed, 60 insertions(+), 51 deletions(-) + +diff --git a/capplet/gsp-app.c b/capplet/gsp-app.c +index c92b8dad..123ab217 100644 +--- a/capplet/gsp-app.c ++++ b/capplet/gsp-app.c +@@ -3,60 +3,61 @@ + * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 2007, 2009 Vincent Untz. + * Copyright (C) 2008 Lucas Rocha. + * Copyright (C) 2008 William Jon McCann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + #ifdef HAVE_CONFIG_H + #include + #endif + + #include + #include + + #include + #include ++#include + + #include "gsm-app-dialog.h" + #include "gsm-properties-dialog.h" + #include "gsm-util.h" + #include "gsp-app-manager.h" + #include "gsp-keyfile.h" + + #include "gsp-app.h" + + #define GSP_APP_SAVE_DELAY 2 + + #define GSP_ASP_SAVE_MASK_HIDDEN 0x0001 + #define GSP_ASP_SAVE_MASK_ENABLED 0x0002 + #define GSP_ASP_SAVE_MASK_NAME 0x0004 + #define GSP_ASP_SAVE_MASK_EXEC 0x0008 + #define GSP_ASP_SAVE_MASK_COMMENT 0x0010 + #define GSP_ASP_SAVE_MASK_NO_DISPLAY 0x0020 + #define GSP_ASP_SAVE_MASK_ALL 0xffff + + struct _GspAppPrivate { + char *basename; + char *path; + + gboolean hidden; + gboolean no_display; + gboolean enabled; + gboolean shown; + + char *name; + char *exec; +@@ -281,148 +282,145 @@ _gsp_app_update_description (GspApp *app) + } else { + secondary = _("No description"); + } + + g_free (app->priv->description); + app->priv->description = g_markup_printf_escaped ("%s\n%s", + primary, + secondary); + } + + /* + * Saving + */ + + static void + _gsp_ensure_user_autostart_dir (void) + { + char *dir; + + dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL); + g_mkdir_with_parents (dir, S_IRWXU); + + g_free (dir); + } + + static gboolean + _gsp_app_user_equal_system (GspApp *app, + char **system_path) + { + GspAppManager *manager; +- const char *system_dir; +- char *path; +- char *str; +- GKeyFile *keyfile; ++ gboolean return_value = FALSE; ++ const char *system_dir = NULL; ++ char *path = NULL; ++ char *str = NULL; ++ GKeyFile *keyfile = NULL; ++ GDesktopAppInfo *app_info = NULL; + + manager = gsp_app_manager_get (); + system_dir = gsp_app_manager_get_dir (manager, + app->priv->xdg_system_position); + g_object_unref (manager); + if (!system_dir) { +- return FALSE; ++ goto out; + } + + path = g_build_filename (system_dir, app->priv->basename, NULL); + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { +- g_free (path); +- g_key_file_free (keyfile); +- return FALSE; ++ goto out; + } + +- if (gsp_key_file_get_boolean (keyfile, +- G_KEY_FILE_DESKTOP_KEY_HIDDEN, +- FALSE) != app->priv->hidden || +- gsp_key_file_get_boolean (keyfile, +- GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED, +- TRUE) != app->priv->enabled || +- gsp_key_file_get_shown (keyfile, +- gsm_util_get_current_desktop ()) != app->priv->shown) { +- g_free (path); +- g_key_file_free (keyfile); +- return FALSE; ++ app_info = g_desktop_app_info_new_from_keyfile (keyfile); ++ ++ if (!app_info) { ++ goto out; + } + +- if (gsp_key_file_get_boolean (keyfile, +- G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, +- FALSE) != app->priv->no_display) { +- g_free (path); +- g_key_file_free (keyfile); +- return FALSE; ++ if (g_desktop_app_info_get_is_hidden (app_info)) { ++ goto out; ++ } ++ ++ if (g_desktop_app_info_has_key (app_info, ++ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED) && ++ !g_desktop_app_info_get_boolean (app_info, ++ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED)) { ++ goto out; ++ } ++ ++ if (!g_desktop_app_info_get_show_in (app_info, NULL)) { ++ goto out; ++ } ++ ++ if (g_desktop_app_info_get_nodisplay (app_info)) { ++ goto out; + } + + str = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_NAME); + if (!_gsp_str_equal (str, app->priv->name)) { +- g_free (str); +- g_free (path); +- g_key_file_free (keyfile); +- return FALSE; ++ goto out; + } +- g_free (str); ++ g_clear_pointer (&str, g_free); + + str = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_COMMENT); + if (!_gsp_str_equal (str, app->priv->comment)) { +- g_free (str); +- g_free (path); +- g_key_file_free (keyfile); +- return FALSE; ++ goto out; + } +- g_free (str); ++ g_clear_pointer (&str, g_free); + + str = gsp_key_file_get_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_EXEC); + if (!_gsp_str_equal (str, app->priv->exec)) { +- g_free (str); +- g_free (path); +- g_key_file_free (keyfile); +- return FALSE; ++ goto out; + } +- g_free (str); ++ g_clear_pointer (&str, g_free); + + str = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_ICON); + if (!_gsp_str_equal (str, app->priv->icon)) { +- g_free (str); +- g_free (path); +- g_key_file_free (keyfile); +- return FALSE; ++ goto out; + } +- g_free (str); +- +- g_key_file_free (keyfile); ++ g_clear_pointer (&str, g_free); + + *system_path = path; +- +- return TRUE; ++ path = NULL; ++ return_value = TRUE; ++out: ++ g_clear_pointer (&path, g_free); ++ g_clear_pointer (&str, g_free); ++ g_clear_object (&app_info); ++ g_clear_pointer (&keyfile, g_key_file_free); ++ ++ return return_value; + } + + static inline void + _gsp_app_save_done_success (GspApp *app) + { + app->priv->save_mask = 0; + + if (app->priv->old_system_path) { + g_free (app->priv->old_system_path); + app->priv->old_system_path = NULL; + } + } + + static gboolean + _gsp_app_save (gpointer data) + { + GspApp *app; + char *use_path; + GKeyFile *keyfile; + GError *error; + + app = GSP_APP (data); + + /* first check if removing the data from the user dir and using the + * data from the system dir is enough -- this helps us keep clean the + * user config dir by removing unneeded files */ + if (_gsp_app_user_equal_system (app, &use_path)) { + if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) { + g_remove (app->priv->path); + } +@@ -748,153 +746,164 @@ gsp_app_delete (GspApp *app) + app->priv->hidden = TRUE; + app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN; + + _gsp_app_queue_save (app); + _gsp_app_emit_changed (app); + } + } + + /* + * New autostart app + */ + + void + gsp_app_reload_at (GspApp *app, + const char *path, + unsigned int xdg_position) + { + g_return_if_fail (GSP_IS_APP (app)); + + app->priv->xdg_position = G_MAXUINT; + gsp_app_new (path, xdg_position); + } + + GspApp * + gsp_app_new (const char *path, + unsigned int xdg_position) + { + GspAppManager *manager; + GspApp *app; + GKeyFile *keyfile; ++ GDesktopAppInfo *app_info; + char *basename; + gboolean new; + + basename = g_path_get_basename (path); + + manager = gsp_app_manager_get (); + app = gsp_app_manager_find_app_with_basename (manager, basename); + g_object_unref (manager); + + new = (app == NULL); + + if (!new) { + if (app->priv->xdg_position == xdg_position) { + if (app->priv->skip_next_monitor_event) { + app->priv->skip_next_monitor_event = FALSE; + return NULL; + } + /* else: the file got changed but not by us, we'll + * update our data from disk */ + } + + if (app->priv->xdg_position < xdg_position || + app->priv->save_timeout != 0) { + /* we don't really care about this file, since we + * already have something with a higher priority, or + * we're going to write something in the user config + * anyway. + * Note: xdg_position >= 1 so it's a system dir */ + app->priv->xdg_system_position = MIN (xdg_position, + app->priv->xdg_system_position); + return NULL; + } + } + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { + g_key_file_free (keyfile); + g_free (basename); + return NULL; + } + ++ app_info = g_desktop_app_info_new_from_keyfile (keyfile); ++ ++ if (!app_info) { ++ g_object_unref (app_info); ++ g_key_file_free (keyfile); ++ g_free (basename); ++ return NULL; ++ } ++ + if (new) { + app = g_object_new (GSP_TYPE_APP, NULL); + app->priv->basename = basename; + } else { + g_free (basename); + _gsp_app_free_reusable_data (app); + } + ++ + app->priv->path = g_strdup (path); + + app->priv->hidden = gsp_key_file_get_boolean (keyfile, + G_KEY_FILE_DESKTOP_KEY_HIDDEN, + FALSE); + app->priv->no_display = gsp_key_file_get_boolean (keyfile, + G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, + FALSE); + app->priv->enabled = gsp_key_file_get_boolean (keyfile, + GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED, + TRUE); +- app->priv->shown = gsp_key_file_get_shown (keyfile, +- gsm_util_get_current_desktop ()); ++ app->priv->shown = g_desktop_app_info_get_show_in (app_info, NULL); + + app->priv->name = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_NAME); + app->priv->exec = gsp_key_file_get_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_EXEC); + app->priv->comment = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_COMMENT); + + if (gsm_util_text_is_blank (app->priv->name)) { + g_free (app->priv->name); + app->priv->name = g_strdup (app->priv->exec); + } + + app->priv->icon = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_ICON); + + if (app->priv->icon) { + /* look at icon and see if it's a themed icon or not */ + if (g_path_is_absolute (app->priv->icon)) { + GFile *iconfile; + + iconfile = g_file_new_for_path (app->priv->icon); + app->priv->gicon = g_file_icon_new (iconfile); + g_object_unref (iconfile); + } else { + app->priv->gicon = g_themed_icon_new (app->priv->icon); + } + } else { + app->priv->gicon = NULL; + } + ++ g_object_unref (app_info); + g_key_file_free (keyfile); + + _gsp_app_update_description (app); + + if (xdg_position > 0) { + g_assert (xdg_position <= app->priv->xdg_system_position); + app->priv->xdg_system_position = xdg_position; + } + /* else we keep the old value (which is G_MAXUINT if it wasn't set) */ + app->priv->xdg_position = xdg_position; + + g_assert (!new || app->priv->save_timeout == 0); + app->priv->save_timeout = 0; + app->priv->old_system_path = NULL; + app->priv->skip_next_monitor_event = FALSE; + + if (!new) { + _gsp_app_emit_changed (app); + } + + return app; + } + + static char * + _gsp_find_free_basename (const char *suggested_basename) + { + GspAppManager *manager; + char *base_path; + char *filename; + char *basename; +-- +2.14.2 + diff --git a/SOURCES/0005-session-properties-get-out-of-Other.patch b/SOURCES/0005-session-properties-get-out-of-Other.patch new file mode 100644 index 0000000..cc10d03 --- /dev/null +++ b/SOURCES/0005-session-properties-get-out-of-Other.patch @@ -0,0 +1,34 @@ +From 119ac97cea3b362e53aaa236f643f9bb916a03cb Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Dec 2013 15:07:35 -0500 +Subject: [PATCH 05/19] session-properties: get out of Other + +Put it in the menus next to Settings and Software +--- + data/session-properties.desktop.in.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/data/session-properties.desktop.in.in b/data/session-properties.desktop.in.in +index 3dc7b033..dcfe2f84 100644 +--- a/data/session-properties.desktop.in.in ++++ b/data/session-properties.desktop.in.in +@@ -1,15 +1,15 @@ + [Desktop Entry] + _Name=Startup Applications + _Comment=Choose what applications to start when you log in + Exec=gnome-session-properties + Icon=session-properties + Terminal=false + Type=Application + StartupNotify=true +-Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings; ++Categories=GNOME;GTK;System; + OnlyShowIn=GNOME;Unity; + NoDisplay=true + X-GNOME-Bugzilla-Bugzilla=GNOME + X-GNOME-Bugzilla-Product=gnome-session + X-GNOME-Bugzilla-Component=gnome-session-properties + X-GNOME-Bugzilla-Version=@VERSION@ +-- +2.14.2 + diff --git a/SOURCES/0006-session-properties-refresh-from-recent-glade.patch b/SOURCES/0006-session-properties-refresh-from-recent-glade.patch new file mode 100644 index 0000000..036e50b --- /dev/null +++ b/SOURCES/0006-session-properties-refresh-from-recent-glade.patch @@ -0,0 +1,377 @@ +From 805c9f995da83c173f9323f55a3f26b627410553 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Dec 2013 10:53:33 -0500 +Subject: [PATCH 06/19] session-properties: refresh from recent glade + +The ui file is rather old. This commit just opens it up in a recent +glade and resaves it, so we have a fresh starting point to make +changes. +--- + data/session-properties.ui | 43 ++++++++++++++++++++++++++++++++++--------- + 1 file changed, 34 insertions(+), 9 deletions(-) + +diff --git a/data/session-properties.ui b/data/session-properties.ui +index 1f0cb9a5..47a30f78 100644 +--- a/data/session-properties.ui ++++ b/data/session-properties.ui +@@ -1,323 +1,348 @@ +- ++ + +- +- ++ + + True + True + 6 + + + True ++ False + 12 +- vertical + 3 + + + True ++ False + 0 + 3 + 3 + Additional startup _programs: + True + session_properties_treeview + + + False ++ True + 0 + + + + + True ++ False + 6 + + + True + True + never +- automatic + etched-in + + + 210 + True + True ++ ++ ++ + + + + ++ True ++ True + 0 + + + + + True ++ False + 6 + start + + + gtk-add + True + True + True + True + + + False + False + 0 + + + + + gtk-remove + True + False + True + True + True + + + False + False + 1 + + + + + gtk-edit + True + False + True + True + True + + + False + False + 2 + + + + + False + False + 1 + + + + ++ True ++ True + 1 + + + + + + + True ++ False + Startup Programs + + + False + + + + + True ++ False + 12 +- vertical + 6 + + + _Automatically remember running applications when logging out + True + True + False + True ++ 0.5 + True + + + False + False + 0 + + + + + True ++ False + + + True + True + + + True ++ False + 4 + + + True ++ False + gtk-save + + + False + False + 0 + + + + + True ++ False + _Remember Currently Running Applications + True + + ++ True ++ True + 1 + + + + + + + False + False + 0 + + + + + False + False + 1 + + + + + 1 + + + + + True ++ False + Options + + + 1 + False + + + + + True ++ False + 6 + 3 + 2 + 12 + 6 + + + True ++ False + 12 + + + True + True +- ++ + + ++ True ++ True + 0 + + + + + Browse… + True + True + True + + + False + False + 1 + + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + True +- ++ + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + True + True +- ++ + + + 1 + 2 + GTK_FILL + + + + + True ++ False + 0 + Comm_ent: + True + label2 + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True ++ False + 0 + Co_mmand: + True + session_properties_command_entry + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True ++ False + 0 + _Name: + True + session_properties_name_entry + + + GTK_FILL + GTK_FILL + + + + +-- +2.14.2 + diff --git a/SOURCES/0007-manager-Don-t-clear-saved-session-if-autosaving-is-d.patch b/SOURCES/0007-manager-Don-t-clear-saved-session-if-autosaving-is-d.patch new file mode 100644 index 0000000..a2cd28a --- /dev/null +++ b/SOURCES/0007-manager-Don-t-clear-saved-session-if-autosaving-is-d.patch @@ -0,0 +1,81 @@ +From eb9b29eeb55c47d691b65b046e31ede815e3d22c Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Dec 2013 16:14:16 -0500 +Subject: [PATCH 07/19] manager: Don't clear saved session if autosaving is + disabled + +Now that we support on-demand saving again, we need to make sure +we don't wipe that away at log out. +--- + gnome-session/gsm-manager.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c +index bdba38e8..e2fad3b1 100644 +--- a/gnome-session/gsm-manager.c ++++ b/gnome-session/gsm-manager.c +@@ -1828,61 +1828,60 @@ on_xsmp_client_register_confirmed (GsmXSMPClient *client, + app = find_app_for_startup_id (manager, id); + + if (app != NULL) { + gsm_app_set_registered (app, TRUE); + } + } + + static gboolean + auto_save_is_enabled (GsmManager *manager) + { + return g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE_ONE_SHOT) + || g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE); + } + + static void + maybe_save_session (GsmManager *manager) + { + GError *error; + + if (gsm_system_is_login_session (manager->priv->system)) + return; + + /* We only allow session saving when session is running or when + * logging out */ + if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING && + manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) { + return; + } + + if (!auto_save_is_enabled (manager)) { +- gsm_session_save_clear (); + return; + } + + error = NULL; + gsm_session_save (manager->priv->clients, &error); + + if (error) { + g_warning ("Error saving session: %s", error->message); + g_error_free (error); + } + } + + static void + _handle_client_end_session_response (GsmManager *manager, + GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason) + { + /* just ignore if received outside of shutdown */ + if (manager->priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) { + return; + } + + g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :""); + + if (cancel) { + cancel_end_session (manager); + return; +-- +2.14.2 + diff --git a/SOURCES/0008-Add-Remember-Currently-Running-Applications-button.patch b/SOURCES/0008-Add-Remember-Currently-Running-Applications-button.patch new file mode 100644 index 0000000..c9ec2ac --- /dev/null +++ b/SOURCES/0008-Add-Remember-Currently-Running-Applications-button.patch @@ -0,0 +1,1724 @@ +From cae19300c8035b84c71f6ba5fb17d86add2465dd Mon Sep 17 00:00:00 2001 +From: Josselin Mouette +Date: Mon, 21 Jun 2010 15:22:23 -0400 +Subject: [PATCH 08/19] Add "Remember Currently Running Applications" button + +This adds back session saving that's not at logout. +--- + capplet/gsm-properties-dialog.c | 63 +++++++++++- + configure.ac | 1 + + data/session-properties.ui | 12 +++ + gnome-session/gsm-client.c | 10 ++ + gnome-session/gsm-client.h | 6 ++ + gnome-session/gsm-dbus-client.c | 14 +++ + gnome-session/gsm-manager.c | 150 ++++++++++++++++++++++++++++- + gnome-session/gsm-manager.h | 3 + + gnome-session/gsm-xsmp-client.c | 37 +++++++ + gnome-session/gsm-xsmp-client.h | 3 +- + gnome-session/org.gnome.SessionManager.xml | 8 ++ + 11 files changed, 303 insertions(+), 4 deletions(-) + +diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c +index 33812b8b..d2be778b 100644 +--- a/capplet/gsm-properties-dialog.c ++++ b/capplet/gsm-properties-dialog.c +@@ -5,70 +5,77 @@ + * Copyright (C) 2008 Lucas Rocha. + * Copyright (C) 2008 William Jon McCann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + #include "config.h" + + #include + #include + #include + + #include "gsm-properties-dialog.h" + #include "gsm-app-dialog.h" + #include "gsm-util.h" + #include "gsp-app.h" + #include "gsp-app-manager.h" ++#include ++#include ++ ++#define GSM_SERVICE_DBUS "org.gnome.SessionManager" ++#define GSM_PATH_DBUS "/org/gnome/SessionManager" ++#define GSM_INTERFACE_DBUS "org.gnome.SessionManager" + + #define GSM_PROPERTIES_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogPrivate)) + + #define GTKBUILDER_FILE "session-properties.ui" + + #define CAPPLET_TREEVIEW_WIDGET_NAME "session_properties_treeview" + #define CAPPLET_ADD_WIDGET_NAME "session_properties_add_button" + #define CAPPLET_DELETE_WIDGET_NAME "session_properties_delete_button" + #define CAPPLET_EDIT_WIDGET_NAME "session_properties_edit_button" + #define CAPPLET_SAVE_WIDGET_NAME "session_properties_save_button" ++#define CAPPLET_SESSION_SAVED_WIDGET_NAME "session_properties_session_saved_label" + #define CAPPLET_REMEMBER_WIDGET_NAME "session_properties_remember_toggle" + + #define STARTUP_APP_ICON "system-run" + + #define SPC_SETTINGS_SCHEMA "org.gnome.SessionManager" + #define SPC_SETTINGS_AUTOSAVE_KEY "auto-save-session" + + struct GsmPropertiesDialogPrivate + { + GtkBuilder *xml; + GtkListStore *list_store; + GtkTreeModel *tree_filter; + + GtkTreeView *treeview; + GtkWidget *add_button; + GtkWidget *delete_button; + GtkWidget *edit_button; + + GSettings *settings; + + GspAppManager *manager; + }; + + enum { + STORE_COL_VISIBLE = 0, + STORE_COL_ENABLED, + STORE_COL_GICON, + STORE_COL_DESCRIPTION, + STORE_COL_APP, + STORE_COL_SEARCH, +@@ -430,65 +437,119 @@ on_edit_app_clicked (GtkWidget *widget, + char *exec; + char *comment; + + edit_dialog = gsm_app_dialog_new (gsp_app_get_name (app), + gsp_app_get_exec (app), + gsp_app_get_comment (app)); + gtk_window_set_transient_for (GTK_WINDOW (edit_dialog), + GTK_WINDOW (dialog)); + + if (gsm_app_dialog_run (GSM_APP_DIALOG (edit_dialog), + &name, &exec, &comment)) { + gsp_app_update (app, name, comment, exec); + g_free (name); + g_free (exec); + g_free (comment); + } + + g_object_unref (app); + } + } + + static void + on_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GsmPropertiesDialog *dialog) + { + on_edit_app_clicked (NULL, dialog); + } + ++static void ++session_saved_message (GsmPropertiesDialog *dialog, ++ const char *msg, ++ gboolean is_error) ++{ ++ GtkLabel *label; ++ gchar *markup; ++ label = GTK_LABEL (gtk_builder_get_object (dialog->priv->xml, CAPPLET_SESSION_SAVED_WIDGET_NAME)); ++ if (is_error) ++ markup = g_markup_printf_escaped ("%s", msg); ++ else ++ markup = g_markup_escape_text (msg, -1); ++ gtk_label_set_markup (label, markup); ++ g_free (markup); ++} ++ ++static void ++session_saved_cb (DBusGProxy *proxy, ++ DBusGProxyCall *call_id, ++ void *user_data) ++{ ++ gboolean res; ++ GsmPropertiesDialog *dialog = user_data; ++ ++ res = dbus_g_proxy_end_call (proxy, call_id, NULL, G_TYPE_INVALID); ++ if (res) ++ session_saved_message (dialog, _("Your session has been saved."), FALSE); ++ else ++ session_saved_message (dialog, _("Failed to save session"), TRUE); ++ ++ g_object_unref (proxy); ++} ++ + static void + on_save_session_clicked (GtkWidget *widget, + GsmPropertiesDialog *dialog) + { +- g_debug ("Session saving is not implemented yet!"); ++ DBusGConnection *conn; ++ DBusGProxy *proxy; ++ DBusGProxyCall *call; ++ ++ conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); ++ if (conn == NULL) { ++ session_saved_message (dialog, _("Could not connect to the session bus"), TRUE); ++ return; ++ } ++ ++ proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS); ++ if (proxy == NULL) { ++ session_saved_message (dialog, _("Could not connect to the session manager"), TRUE); ++ return; ++ } ++ ++ call = dbus_g_proxy_begin_call (proxy, "SaveSession", session_saved_cb, dialog, NULL, G_TYPE_INVALID); ++ if (call == NULL) { ++ session_saved_message (dialog, _("Failed to save session"), TRUE); ++ g_object_unref (proxy); ++ return; ++ } + } + + static void + setup_dialog (GsmPropertiesDialog *dialog) + { + GtkTreeView *treeview; + GtkWidget *button; + GtkTreeModel *tree_filter; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkTargetList *targetlist; + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_ICON, + G_TYPE_STRING, + G_TYPE_OBJECT, + G_TYPE_STRING); + tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store), + NULL); + g_object_unref (dialog->priv->list_store); + dialog->priv->tree_filter = tree_filter; + + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter), +diff --git a/configure.ac b/configure.ac +index 5182c09e..73e69bc7 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -51,60 +51,61 @@ if test "$enable_session_selector" = yes; then + PKG_CHECK_MODULES(SESSION_SELECTOR, gtk+-3.0 gio-2.0) + fi + + dnl ==================================================================== + dnl Dependency Checks + dnl ==================================================================== + + dnl Standard vertical stacks + PKG_CHECK_MODULES(GIO, gio-2.0) + PKG_CHECK_MODULES(GIOUNIX, gio-unix-2.0 >= $GLIB_REQUIRED) + PKG_CHECK_MODULES(GTK3, gtk+-3.0 >= $GTK3_REQUIRED) + + PKG_CHECK_MODULES(GNOME_SESSION, + glib-2.0 >= $GLIB_REQUIRED + gio-2.0 >= $GLIB_REQUIRED + json-glib-1.0 >= $JSON_GLIB_REQUIRED + gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED + ) + + dnl We can only support old upower + dnl https://bugzilla.gnome.org/show_bug.cgi?id=710383 + PKG_CHECK_MODULES(UPOWER, upower-glib < 0.99.0, have_old_upower=yes, have_old_upower=no) + AS_IF([test x$have_old_upower = xyes], [ + AC_DEFINE([HAVE_OLD_UPOWER], [1], [Define if we have an older upower]) + ]) + AM_CONDITIONAL(HAVE_OLD_UPOWER, test x$have_old_upower = xyes) + + PKG_CHECK_MODULES(SESSION_PROPERTIES, + glib-2.0 >= $GLIB_REQUIRED + gtk+-3.0 >= $GTK3_REQUIRED ++ dbus-glib-1 >= $DBUS_GLIB_REQUIRED + ) + + PKG_CHECK_MODULES(X11, x11) + PKG_CHECK_MODULES(SM, sm) + PKG_CHECK_MODULES(ICE, ice) + PKG_CHECK_MODULES(XEXT, xext xau) + + PKG_CHECK_MODULES(GL_TEST, xcomposite gl glib-2.0 epoxy) + PKG_CHECK_MODULES(GLES_TEST, egl glesv2) + + dnl ==================================================================== + dnl Check for gconf + dnl ==================================================================== + AC_ARG_ENABLE([gconf], + AS_HELP_STRING([--enable-gconf], [Support gconf-based autostart]), + [enable_gconf=$enableval], + [enable_gconf=auto]) + + if test x$enable_gconf != xno ; then + PKG_CHECK_MODULES(GCONF, gconf-2.0, [have_gconf=yes], [have_gconf=no]) + + if test x$enable_gconf = xyes -a x$have_gconf = xno ; then + AC_MSG_ERROR([GConf support explicitly required, but gconf not found]) + fi + + if test x$have_gconf = xyes ; then + AC_DEFINE([HAVE_GCONF], [1], [Define if we support gconf-based autostart]) + fi + fi + +diff --git a/data/session-properties.ui b/data/session-properties.ui +index 47a30f78..b43759ff 100644 +--- a/data/session-properties.ui ++++ b/data/session-properties.ui +@@ -133,108 +133,120 @@ + + + + + True + False + 12 + 6 + + + _Automatically remember running applications when logging out + True + True + False + True + 0.5 + True + + + False + False + 0 + + + + + True + False + + ++ True + True + True + + + True + False + 4 + + + True + False + gtk-save + + + False + False + 0 + + + + + True + False + _Remember Currently Running Applications + True + + + True + True + 1 + + + + + + + False + False + 0 + + + + + False + False + 1 + + ++ ++ ++ True ++ True ++ ++ ++ False ++ False ++ 2 ++ ++ + + + 1 + + + + + True + False + Options + + + 1 + False + + + + + True + False + 6 + 3 + 2 + 12 + 6 + + + True + False + 12 +diff --git a/gnome-session/gsm-client.c b/gnome-session/gsm-client.c +index 75edbbff..7b78d9e1 100644 +--- a/gnome-session/gsm-client.c ++++ b/gnome-session/gsm-client.c +@@ -514,49 +514,59 @@ gsm_client_query_end_session (GsmClient *client, + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_query_end_session (client, flags, error); + } + + gboolean + gsm_client_end_session (GsmClient *client, + GsmClientEndSessionFlag flags, + GError **error) + { + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_end_session (client, flags, error); + } + + gboolean + gsm_client_stop (GsmClient *client, + GError **error) + { + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_stop (client, error); + } + + void + gsm_client_disconnected (GsmClient *client) + { + g_signal_emit (client, signals[DISCONNECTED], 0); + } + ++gboolean ++gsm_client_request_save (GsmClient *client, ++ guint flags, ++ GError **error) ++{ ++ g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); ++ ++ return GSM_CLIENT_GET_CLASS (client)->impl_request_save (client, flags, error); ++} ++ + GKeyFile * + gsm_client_save (GsmClient *client, + GError **error) + { + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_save (client, error); + } + + void + gsm_client_end_session_response (GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason) + { + g_signal_emit (client, signals[END_SESSION_RESPONSE], 0, + is_ok, do_last, cancel, reason); + } +diff --git a/gnome-session/gsm-client.h b/gnome-session/gsm-client.h +index cd7c06d8..f79896b3 100644 +--- a/gnome-session/gsm-client.h ++++ b/gnome-session/gsm-client.h +@@ -63,89 +63,95 @@ struct _GsmClient + GObject parent; + GsmClientPrivate *priv; + }; + + struct _GsmClientClass + { + GObjectClass parent_class; + + /* signals */ + void (*disconnected) (GsmClient *client); + void (*end_session_response) (GsmClient *client, + gboolean ok, + gboolean do_last, + gboolean cancel, + const char *reason); + + /* virtual methods */ + char * (*impl_get_app_name) (GsmClient *client); + GsmClientRestartStyle (*impl_get_restart_style_hint) (GsmClient *client); + guint (*impl_get_unix_process_id) (GsmClient *client); + gboolean (*impl_query_end_session) (GsmClient *client, + GsmClientEndSessionFlag flags, + GError **error); + gboolean (*impl_end_session) (GsmClient *client, + GsmClientEndSessionFlag flags, + GError **error); + gboolean (*impl_cancel_end_session) (GsmClient *client, + GError **error); + gboolean (*impl_stop) (GsmClient *client, + GError **error); ++ gboolean (*impl_request_save) (GsmClient *client, ++ guint flags, ++ GError **error); + GKeyFile * (*impl_save) (GsmClient *client, + GError **error); + }; + + typedef enum + { + GSM_CLIENT_ERROR_GENERAL = 0, + GSM_CLIENT_ERROR_NOT_REGISTERED, + GSM_CLIENT_NUM_ERRORS + } GsmClientError; + + #define GSM_CLIENT_ERROR gsm_client_error_quark () + GQuark gsm_client_error_quark (void); + + GType gsm_client_get_type (void) G_GNUC_CONST; + + const char *gsm_client_peek_id (GsmClient *client); + + + const char * gsm_client_peek_startup_id (GsmClient *client); + const char * gsm_client_peek_app_id (GsmClient *client); + guint gsm_client_peek_restart_style_hint (GsmClient *client); + guint gsm_client_peek_status (GsmClient *client); + + + char *gsm_client_get_app_name (GsmClient *client); + void gsm_client_set_app_id (GsmClient *client, + const char *app_id); + void gsm_client_set_status (GsmClient *client, + guint status); + + gboolean gsm_client_end_session (GsmClient *client, + guint flags, + GError **error); + gboolean gsm_client_query_end_session (GsmClient *client, + guint flags, + GError **error); + gboolean gsm_client_cancel_end_session (GsmClient *client, + GError **error); + + void gsm_client_disconnected (GsmClient *client); + ++gboolean gsm_client_request_save (GsmClient *client, ++ guint flags, ++ GError **error); + GKeyFile *gsm_client_save (GsmClient *client, + GError **error); + + gboolean gsm_client_stop (GsmClient *client, + GError **error); + + /* private */ + + void gsm_client_end_session_response (GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason); + + G_END_DECLS + + #endif /* __GSM_CLIENT_H__ */ +diff --git a/gnome-session/gsm-dbus-client.c b/gnome-session/gsm-dbus-client.c +index dcf96f0b..050ea18f 100644 +--- a/gnome-session/gsm-dbus-client.c ++++ b/gnome-session/gsm-dbus-client.c +@@ -300,60 +300,73 @@ gsm_dbus_client_get_property (GObject *object, + case PROP_BUS_NAME: + g_value_set_string (value, self->priv->bus_name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + } + + static void + gsm_dbus_client_finalize (GObject *object) + { + GsmDBusClient *client = (GsmDBusClient *) object; + + g_free (client->priv->bus_name); + + if (client->priv->skeleton != NULL) { + g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (client->priv->skeleton), + client->priv->connection); + g_clear_object (&client->priv->skeleton); + } + + g_clear_object (&client->priv->connection); + + if (client->priv->watch_id != 0) + g_bus_unwatch_name (client->priv->watch_id); + + G_OBJECT_CLASS (gsm_dbus_client_parent_class)->finalize (object); + } + ++static gboolean ++dbus_client_request_save (GsmClient *client, ++ guint flags, ++ GError **error) ++{ ++ g_debug ("GsmDBusClient: sending save request to client with id %s", ++ gsm_client_peek_id (client)); ++ ++ /* FIXME: The protocol does not support this */ ++ ++ return FALSE; ++} ++ + static GKeyFile * + dbus_client_save (GsmClient *client, + GError **error) + { + g_debug ("GsmDBusClient: saving client with id %s", + gsm_client_peek_id (client)); + + /* FIXME: We still don't support client saving for D-Bus + * session clients */ + + return NULL; + } + + static gboolean + dbus_client_stop (GsmClient *client, + GError **error) + { + GsmDBusClient *dbus_client = (GsmDBusClient *) client; + gsm_exported_client_private_emit_stop (dbus_client->priv->skeleton); + return TRUE; + } + + static char * + dbus_client_get_app_name (GsmClient *client) + { + /* Always use app-id instead */ + return NULL; + } + + static GsmClientRestartStyle +@@ -393,60 +406,61 @@ static gboolean + dbus_client_end_session (GsmClient *client, + GsmClientEndSessionFlag flags, + GError **error) + { + GsmDBusClient *dbus_client = (GsmDBusClient *) client; + + gsm_exported_client_private_emit_end_session (dbus_client->priv->skeleton, flags); + return TRUE; + } + + static gboolean + dbus_client_cancel_end_session (GsmClient *client, + GError **error) + { + GsmDBusClient *dbus_client = (GsmDBusClient *) client; + gsm_exported_client_private_emit_cancel_end_session (dbus_client->priv->skeleton); + return TRUE; + } + + static void + gsm_dbus_client_class_init (GsmDBusClientClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GsmClientClass *client_class = GSM_CLIENT_CLASS (klass); + + object_class->finalize = gsm_dbus_client_finalize; + object_class->constructor = gsm_dbus_client_constructor; + object_class->get_property = gsm_dbus_client_get_property; + object_class->set_property = gsm_dbus_client_set_property; + ++ client_class->impl_request_save = dbus_client_request_save; + client_class->impl_save = dbus_client_save; + client_class->impl_stop = dbus_client_stop; + client_class->impl_query_end_session = dbus_client_query_end_session; + client_class->impl_end_session = dbus_client_end_session; + client_class->impl_cancel_end_session = dbus_client_cancel_end_session; + client_class->impl_get_app_name = dbus_client_get_app_name; + client_class->impl_get_restart_style_hint = dbus_client_get_restart_style_hint; + client_class->impl_get_unix_process_id = dbus_client_get_unix_process_id; + + g_object_class_install_property (object_class, + PROP_BUS_NAME, + g_param_spec_string ("bus-name", + "bus-name", + "bus-name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GsmDBusClientPrivate)); + } + + GsmClient * + gsm_dbus_client_new (const char *startup_id, + const char *bus_name) + { + GsmDBusClient *client; + + client = g_object_new (GSM_TYPE_DBUS_CLIENT, + "startup-id", startup_id, + "bus-name", bus_name, + NULL); +diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c +index e2fad3b1..825a6846 100644 +--- a/gnome-session/gsm-manager.c ++++ b/gnome-session/gsm-manager.c +@@ -54,60 +54,61 @@ + #include "gsm-dbus-client.h" + + #include "gsm-autostart-app.h" + + #include "gsm-util.h" + #include "gsm-icon-names.h" + #include "gsm-system.h" + #include "gsm-session-save.h" + #include "gsm-shell-extensions.h" + #include "gsm-fail-whale.h" + + #define GSM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_MANAGER, GsmManagerPrivate)) + + /* UUIDs for log messages */ + #define GSM_MANAGER_STARTUP_SUCCEEDED_MSGID "0ce153587afa4095832d233c17a88001" + #define GSM_MANAGER_UNRECOVERABLE_FAILURE_MSGID "10dd2dc188b54a5e98970f56499d1f73" + + #define GSM_MANAGER_DBUS_PATH "/org/gnome/SessionManager" + #define GSM_MANAGER_DBUS_NAME "org.gnome.SessionManager" + #define GSM_MANAGER_DBUS_IFACE "org.gnome.SessionManager" + + /* Probably about the longest amount of time someone could reasonably + * want to wait, at least for something happening more than once. + * We can get deployed on very slow media though like CDROM devices, + * often with complex stacking/compressing filesystems on top, which + * is not a recipie for speed. Particularly now that we throw up + * a fail whale if required components don't show up quickly enough, + * let's make this fairly long. + */ + #define GSM_MANAGER_PHASE_TIMEOUT 90 /* seconds */ ++#define GSM_MANAGER_SAVE_SESSION_TIMEOUT 2 + + #define GDM_FLEXISERVER_COMMAND "gdmflexiserver" + #define GDM_FLEXISERVER_ARGS "--startnew Standard" + + #define SESSION_SCHEMA "org.gnome.desktop.session" + #define KEY_IDLE_DELAY "idle-delay" + #define KEY_SESSION_NAME "session-name" + + #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" + #define KEY_AUTOSAVE "auto-save-session" + #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" + #define KEY_LOGOUT_PROMPT "logout-prompt" + #define KEY_SHOW_FALLBACK_WARNING "show-fallback-warning" + + #define SCREENSAVER_SCHEMA "org.gnome.desktop.screensaver" + #define KEY_SLEEP_LOCK "lock-enabled" + + #define LOCKDOWN_SCHEMA "org.gnome.desktop.lockdown" + #define KEY_DISABLE_LOG_OUT "disable-log-out" + #define KEY_DISABLE_USER_SWITCHING "disable-user-switching" + + static void app_registered (GsmApp *app, GParamSpec *spec, GsmManager *manager); + + typedef enum + { + GSM_MANAGER_LOGOUT_NONE, + GSM_MANAGER_LOGOUT_LOGOUT, + GSM_MANAGER_LOGOUT_REBOOT, + GSM_MANAGER_LOGOUT_REBOOT_INTERACT, + GSM_MANAGER_LOGOUT_SHUTDOWN, +@@ -1172,60 +1173,123 @@ end_session_or_show_shell_dialog (GsmManager *manager) + end_phase (manager); + } + break; + + case GSM_MANAGER_LOGOUT_MODE_FORCE: + end_phase (manager); + break; + default: + g_assert_not_reached (); + break; + } + + } + + static void + query_end_session_complete (GsmManager *manager) + { + + g_debug ("GsmManager: query end session complete"); + + /* Remove the timeout since this can be called from outside the timer + * and we don't want to have it called twice */ + if (manager->priv->query_timeout_id > 0) { + g_source_remove (manager->priv->query_timeout_id); + manager->priv->query_timeout_id = 0; + } + + end_session_or_show_shell_dialog (manager); + } + ++static gboolean ++_client_request_save (GsmClient *client, ++ ClientEndSessionData *data) ++{ ++ gboolean ret; ++ GError *error; ++ ++ error = NULL; ++ ret = gsm_client_request_save (client, data->flags, &error); ++ if (ret) { ++ g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client)); ++ data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients, ++ client); ++ } else if (error) { ++ g_debug ("GsmManager: unable to query client: %s", error->message); ++ g_error_free (error); ++ } ++ ++ return FALSE; ++} ++ ++static gboolean ++_client_request_save_helper (const char *id, ++ GsmClient *client, ++ ClientEndSessionData *data) ++{ ++ return _client_request_save (client, data); ++} ++ ++static void ++query_save_session_complete (GsmManager *manager) ++{ ++ GError *error = NULL; ++ ++ if (g_slist_length (manager->priv->next_query_clients) > 0) { ++ ClientEndSessionData data; ++ ++ data.manager = manager; ++ data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST; ++ ++ g_slist_foreach (manager->priv->next_query_clients, ++ (GFunc)_client_request_save, ++ &data); ++ ++ g_slist_free (manager->priv->next_query_clients); ++ manager->priv->next_query_clients = NULL; ++ ++ return; ++ } ++ ++ if (manager->priv->query_timeout_id > 0) { ++ g_source_remove (manager->priv->query_timeout_id); ++ manager->priv->query_timeout_id = 0; ++ } ++ ++ gsm_session_save (manager->priv->clients, &error); ++ ++ if (error) { ++ g_warning ("Error saving session: %s", error->message); ++ g_error_free (error); ++ } ++} ++ + static guint32 + generate_cookie (void) + { + guint32 cookie; + + cookie = (guint32)g_random_int_range (1, G_MAXINT32); + + return cookie; + } + + static guint32 + _generate_unique_cookie (GsmManager *manager) + { + guint32 cookie; + + do { + cookie = generate_cookie (); + } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL); + + return cookie; + } + + static gboolean + _on_query_end_session_timeout (GsmManager *manager) + { + GSList *l; + + manager->priv->query_timeout_id = 0; + + g_debug ("GsmManager: query end session timed out"); +@@ -1252,60 +1316,75 @@ _on_query_end_session_timeout (GsmManager *manager) + bus_name = NULL; + } + + app_id = g_strdup (gsm_client_peek_app_id (l->data)); + if (IS_STRING_EMPTY (app_id)) { + /* XSMP clients don't give us an app id unless we start them */ + g_free (app_id); + app_id = gsm_client_get_app_name (l->data); + } + + cookie = _generate_unique_cookie (manager); + inhibitor = gsm_inhibitor_new_for_client (gsm_client_peek_id (l->data), + app_id, + GSM_INHIBITOR_FLAG_LOGOUT, + _("Not responding"), + bus_name, + cookie); + g_free (app_id); + gsm_store_add (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor)); + g_object_unref (inhibitor); + } + + g_slist_free (manager->priv->query_clients); + manager->priv->query_clients = NULL; + + query_end_session_complete (manager); + + return FALSE; + } + ++static gboolean ++_on_query_save_session_timeout (GsmManager *manager) ++{ ++ manager->priv->query_timeout_id = 0; ++ ++ g_debug ("GsmManager: query to save session timed out"); ++ ++ g_slist_free (manager->priv->query_clients); ++ manager->priv->query_clients = NULL; ++ ++ query_save_session_complete (manager); ++ ++ return FALSE; ++} ++ + static void + do_phase_query_end_session (GsmManager *manager) + { + ClientEndSessionData data; + + data.manager = manager; + data.flags = 0; + + if (manager->priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) { + data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL; + } + /* We only query if an app is ready to log out, so we don't use + * GSM_CLIENT_END_SESSION_FLAG_SAVE here. + */ + + debug_clients (manager); + g_debug ("GsmManager: sending query-end-session to clients (logout mode: %s)", + manager->priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_NORMAL? "normal" : + manager->priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE? "forceful": + "no confirmation"); + gsm_store_foreach (manager->priv->clients, + (GsmStoreFunc)_client_query_end_session, + &data); + + /* This phase doesn't time out unless logout is forced. Typically, this + * separate timer is only used to show UI. */ + manager->priv->query_timeout_id = g_timeout_add_seconds (1, (GSourceFunc)_on_query_end_session_timeout, manager); + } + + static void +@@ -1848,67 +1927,86 @@ maybe_save_session (GsmManager *manager) + return; + + /* We only allow session saving when session is running or when + * logging out */ + if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING && + manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) { + return; + } + + if (!auto_save_is_enabled (manager)) { + return; + } + + error = NULL; + gsm_session_save (manager->priv->clients, &error); + + if (error) { + g_warning ("Error saving session: %s", error->message); + g_error_free (error); + } + } + + static void + _handle_client_end_session_response (GsmManager *manager, + GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason) + { +- /* just ignore if received outside of shutdown */ +- if (manager->priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) { ++ /* just ignore if we are not yet running */ ++ if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) { + return; + } + + g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :""); + ++ if (manager->priv->phase == GSM_MANAGER_PHASE_RUNNING) { ++ /* Ignore responses when no requests were sent */ ++ if (manager->priv->query_clients == NULL) { ++ return; ++ } ++ ++ manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client); ++ ++ if (do_last) { ++ manager->priv->next_query_clients = g_slist_prepend (manager->priv->next_query_clients, ++ client); ++ } ++ ++ if (manager->priv->query_clients == NULL) { ++ query_save_session_complete (manager); ++ } ++ return; ++ } ++ + if (cancel) { + cancel_end_session (manager); + return; + } + + manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client); + + if (! is_ok && manager->priv->logout_mode != GSM_MANAGER_LOGOUT_MODE_FORCE) { + guint cookie; + GsmInhibitor *inhibitor; + char *app_id; + const char *bus_name; + + /* FIXME: do we support updating the reason? */ + + /* Create JIT inhibit */ + if (GSM_IS_DBUS_CLIENT (client)) { + bus_name = gsm_dbus_client_get_bus_name (GSM_DBUS_CLIENT (client)); + } else { + bus_name = NULL; + } + + app_id = g_strdup (gsm_client_peek_app_id (client)); + if (IS_STRING_EMPTY (app_id)) { + /* XSMP clients don't give us an app id unless we start them */ + g_free (app_id); + app_id = gsm_client_get_app_name (client); + } + + cookie = _generate_unique_cookie (manager); +@@ -1968,85 +2066,98 @@ on_client_end_session_response (GsmClient *client, + _handle_client_end_session_response (manager, + client, + is_ok, + do_last, + cancel, + reason); + } + + static void + on_xsmp_client_logout_request (GsmXSMPClient *client, + gboolean show_dialog, + GsmManager *manager) + { + GError *error; + int logout_mode; + + if (show_dialog) { + logout_mode = GSM_MANAGER_LOGOUT_MODE_NORMAL; + } else { + logout_mode = GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION; + } + + error = NULL; + gsm_manager_logout (manager, logout_mode, &error); + if (error != NULL) { + g_warning ("Unable to logout: %s", error->message); + g_error_free (error); + } + } + ++static void ++on_xsmp_client_save_request (GsmXSMPClient *client, ++ gboolean show_dialog, ++ GsmManager *manager) ++{ ++ g_debug ("GsmManager: save_request"); ++ gsm_manager_save_session (manager, NULL); ++} ++ + static void + on_store_client_added (GsmStore *store, + const char *id, + GsmManager *manager) + { + GsmClient *client; + + g_debug ("GsmManager: Client added: %s", id); + + client = (GsmClient *)gsm_store_lookup (store, id); + + /* a bit hacky */ + if (GSM_IS_XSMP_CLIENT (client)) { + g_signal_connect (client, + "register-request", + G_CALLBACK (on_xsmp_client_register_request), + manager); + g_signal_connect (client, + "register-confirmed", + G_CALLBACK (on_xsmp_client_register_confirmed), + manager); + g_signal_connect (client, + "logout-request", + G_CALLBACK (on_xsmp_client_logout_request), + manager); ++ g_signal_connect (client, ++ "save-request", ++ G_CALLBACK (on_xsmp_client_save_request), ++ manager); + } + + g_signal_connect (client, + "end-session-response", + G_CALLBACK (on_client_end_session_response), + manager); + + gsm_exported_manager_emit_client_added (manager->priv->skeleton, id); + /* FIXME: disconnect signal handler */ + } + + static void + on_store_client_removed (GsmStore *store, + const char *id, + GsmManager *manager) + { + g_debug ("GsmManager: Client removed: %s", id); + + gsm_exported_manager_emit_client_removed (manager->priv->skeleton, id); + } + + static void + gsm_manager_set_client_store (GsmManager *manager, + GsmStore *store) + { + g_return_if_fail (GSM_IS_MANAGER (manager)); + + if (store != NULL) { + g_object_ref (store); + } +@@ -2625,60 +2736,95 @@ gsm_manager_initialization_error (GsmExportedManager *skeleton, + gboolean fatal, + GsmManager *manager) + { + if (manager->priv->phase != GSM_MANAGER_PHASE_INITIALIZATION) { + g_dbus_method_invocation_return_error (invocation, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION, + "InitializationError interface is only available during the Initialization phase"); + return TRUE; + } + + gsm_util_init_error (fatal, "%s", message); + gsm_exported_manager_complete_initialization_error (skeleton, invocation); + + return TRUE; + } + + static void + user_logout (GsmManager *manager, + GsmManagerLogoutMode mode) + { + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) { + manager->priv->logout_mode = mode; + end_session_or_show_shell_dialog (manager); + return; + } + + request_logout (manager, mode); + } + ++gboolean ++gsm_manager_save_session (GsmManager *manager, ++ GError **error) ++{ ++ ClientEndSessionData data; ++ ++ g_debug ("GsmManager: SaveSession called"); ++ ++ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); ++ ++ if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { ++ g_set_error (error, ++ GSM_MANAGER_ERROR, ++ GSM_MANAGER_ERROR_NOT_IN_RUNNING, ++ "SaveSession interface is only available during the Running phase"); ++ return FALSE; ++ } ++ ++ data.manager = manager; ++ data.flags = 0; ++ gsm_store_foreach (manager->priv->clients, ++ (GsmStoreFunc)_client_request_save_helper, ++ &data); ++ ++ if (manager->priv->query_clients) { ++ manager->priv->query_timeout_id = g_timeout_add_seconds (GSM_MANAGER_SAVE_SESSION_TIMEOUT, ++ (GSourceFunc)_on_query_save_session_timeout, ++ manager); ++ return TRUE; ++ } else { ++ g_debug ("GsmManager: Nothing to save"); ++ return FALSE; ++ } ++} ++ + gboolean + gsm_manager_logout (GsmManager *manager, + guint logout_mode, + GError **error) + { + if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_RUNNING, + "Logout interface is only available after the Running phase starts"); + return FALSE; + } + + if (_log_out_is_locked_down (manager)) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_LOCKED_DOWN, + "Logout has been locked down"); + return FALSE; + } + + switch (logout_mode) { + case GSM_MANAGER_LOGOUT_MODE_NORMAL: + case GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION: + case GSM_MANAGER_LOGOUT_MODE_FORCE: + user_logout (manager, logout_mode); + break; + + default: + g_debug ("Unknown logout mode option"); +diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h +index bc440cb0..4d14aa34 100644 +--- a/gnome-session/gsm-manager.h ++++ b/gnome-session/gsm-manager.h +@@ -96,40 +96,43 @@ GQuark gsm_manager_error_quark (void); + GType gsm_manager_get_type (void); + + GsmManager * gsm_manager_new (GsmStore *client_store, + gboolean failsafe); + GsmManager * gsm_manager_get (void); + + gboolean gsm_manager_get_failsafe (GsmManager *manager); + + gboolean gsm_manager_add_autostart_app (GsmManager *manager, + const char *path, + const char *provides); + gboolean gsm_manager_add_required_app (GsmManager *manager, + const char *path, + const char *provides); + gboolean gsm_manager_add_autostart_apps_from_dir (GsmManager *manager, + const char *path); + gboolean gsm_manager_add_legacy_session_apps (GsmManager *manager, + const char *path); + + void gsm_manager_start (GsmManager *manager); + + const char * _gsm_manager_get_default_session (GsmManager *manager); + + void _gsm_manager_set_active_session (GsmManager *manager, + const char *session_name, + gboolean is_fallback); + + void _gsm_manager_set_renderer (GsmManager *manager, + const char *renderer); + ++gboolean gsm_manager_save_session (GsmManager *manager, ++ GError **error); ++ + gboolean gsm_manager_logout (GsmManager *manager, + guint logout_mode, + GError **error); + + gboolean gsm_manager_set_phase (GsmManager *manager, + GsmManagerPhase phase); + + G_END_DECLS + + #endif /* __GSM_MANAGER_H */ +diff --git a/gnome-session/gsm-xsmp-client.c b/gnome-session/gsm-xsmp-client.c +index 9358f94c..2846d9b3 100644 +--- a/gnome-session/gsm-xsmp-client.c ++++ b/gnome-session/gsm-xsmp-client.c +@@ -39,60 +39,61 @@ + #define GsmDesktopFile "_GSM_DesktopFile" + + #define GSM_XSMP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientPrivate)) + + struct GsmXSMPClientPrivate + { + + SmsConn conn; + IceConn ice_connection; + + guint watch_id; + + char *description; + GPtrArray *props; + + /* SaveYourself state */ + int current_save_yourself; + int next_save_yourself; + guint next_save_yourself_allow_interact : 1; + }; + + enum { + PROP_0, + PROP_ICE_CONNECTION + }; + + enum { + REGISTER_REQUEST, + REGISTER_CONFIRMED, + LOGOUT_REQUEST, ++ SAVE_REQUEST, + LAST_SIGNAL + }; + + static guint signals[LAST_SIGNAL] = { 0 }; + + G_DEFINE_TYPE (GsmXSMPClient, gsm_xsmp_client, GSM_TYPE_CLIENT) + + static gboolean + client_iochannel_watch (GIOChannel *channel, + GIOCondition condition, + GsmXSMPClient *client) + { + gboolean keep_going; + + g_object_ref (client); + switch (IceProcessMessages (client->priv->ice_connection, NULL, NULL)) { + case IceProcessMessagesSuccess: + keep_going = TRUE; + break; + + case IceProcessMessagesIOError: + g_debug ("GsmXSMPClient: IceProcessMessagesIOError on '%s'", client->priv->description); + gsm_client_set_status (GSM_CLIENT (client), GSM_CLIENT_FAILED); + /* Emitting "disconnected" will eventually cause + * IceCloseConnection() to be called. + */ + gsm_client_disconnected (GSM_CLIENT (client)); + keep_going = FALSE; + break; + +@@ -472,60 +473,84 @@ xsmp_interact (GsmClient *client) + + SmsInteract (xsmp->priv->conn); + } + + static gboolean + xsmp_cancel_end_session (GsmClient *client, + GError **error) + { + GsmXSMPClient *xsmp = (GsmXSMPClient *) client; + + g_debug ("GsmXSMPClient: xsmp_cancel_end_session ('%s')", xsmp->priv->description); + + if (xsmp->priv->conn == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Client is not registered"); + return FALSE; + } + + SmsShutdownCancelled (xsmp->priv->conn); + + /* reset the state */ + xsmp->priv->current_save_yourself = -1; + xsmp->priv->next_save_yourself = -1; + xsmp->priv->next_save_yourself_allow_interact = FALSE; + + return TRUE; + } + ++static gboolean ++xsmp_request_save (GsmClient *client, ++ guint flags, ++ GError **error) ++{ ++ GsmXSMPClient *xsmp = (GsmXSMPClient *) client; ++ ++ g_debug ("GsmXSMPClient: xsmp_request_save ('%s')", xsmp->priv->description); ++ ++ if (xsmp->priv->conn == NULL) { ++ g_set_error (error, ++ GSM_CLIENT_ERROR, ++ GSM_CLIENT_ERROR_NOT_REGISTERED, ++ "Client is not registered"); ++ return FALSE; ++ } ++ ++ if (flags & GSM_CLIENT_END_SESSION_FLAG_LAST) ++ xsmp_save_yourself_phase2 (client); ++ else ++ do_save_yourself (xsmp, SmSaveLocal, FALSE); ++ ++ return TRUE; ++} + static char * + get_desktop_file_path (GsmXSMPClient *client) + { + SmProp *prop; + char *desktop_file_path = NULL; + const char *program_name; + + /* XSMP clients using eggsmclient defines a special property + * pointing to their respective desktop entry file */ + prop = find_property (client, GsmDesktopFile, NULL); + + if (prop) { + GFile *file = g_file_new_for_uri (prop->vals[0].value); + desktop_file_path = g_file_get_path (file); + g_object_unref (file); + goto out; + } + + /* If we can't get desktop file from GsmDesktopFile then we + * try to find the desktop file from its program name */ + prop = find_property (client, SmProgram, NULL); + + if (!prop) { + goto out; + } + + program_name = prop->vals[0].value; + desktop_file_path = + gsm_util_find_desktop_file_for_app_name (program_name, + TRUE, FALSE); +@@ -955,100 +980,112 @@ xsmp_get_unix_process_id (GsmClient *client) + gboolean res; + + g_debug ("GsmXSMPClient: getting pid"); + + prop = find_property (GSM_XSMP_CLIENT (client), SmProcessID, NULL); + + if (!prop || strcmp (prop->type, SmARRAY8) != 0) { + return 0; + } + + pid = 0; + res = _parse_value_as_uint ((char *)prop->vals[0].value, &pid); + if (! res) { + pid = 0; + } + + return pid; + } + + static void + gsm_xsmp_client_class_init (GsmXSMPClientClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GsmClientClass *client_class = GSM_CLIENT_CLASS (klass); + + object_class->finalize = gsm_xsmp_client_finalize; + object_class->constructor = gsm_xsmp_client_constructor; + object_class->get_property = gsm_xsmp_client_get_property; + object_class->set_property = gsm_xsmp_client_set_property; + ++ client_class->impl_request_save = xsmp_request_save; + client_class->impl_save = xsmp_save; + client_class->impl_stop = xsmp_stop; + client_class->impl_query_end_session = xsmp_query_end_session; + client_class->impl_end_session = xsmp_end_session; + client_class->impl_cancel_end_session = xsmp_cancel_end_session; + client_class->impl_get_app_name = xsmp_get_app_name; + client_class->impl_get_restart_style_hint = xsmp_get_restart_style_hint; + client_class->impl_get_unix_process_id = xsmp_get_unix_process_id; + + signals[REGISTER_REQUEST] = + g_signal_new ("register-request", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmXSMPClientClass, register_request), + _boolean_handled_accumulator, + NULL, + NULL, + G_TYPE_BOOLEAN, + 1, G_TYPE_POINTER); + signals[REGISTER_CONFIRMED] = + g_signal_new ("register-confirmed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmXSMPClientClass, register_confirmed), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, G_TYPE_POINTER); + signals[LOGOUT_REQUEST] = + g_signal_new ("logout-request", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmXSMPClientClass, logout_request), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, G_TYPE_BOOLEAN); + ++ signals[SAVE_REQUEST] = ++ g_signal_new ("save-request", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GsmXSMPClientClass, save_request), ++ NULL, ++ NULL, ++ g_cclosure_marshal_VOID__BOOLEAN, ++ G_TYPE_NONE, ++ 1, G_TYPE_BOOLEAN); ++ + g_object_class_install_property (object_class, + PROP_ICE_CONNECTION, + g_param_spec_pointer ("ice-connection", + "ice-connection", + "ice-connection", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (klass, sizeof (GsmXSMPClientPrivate)); + } + + GsmClient * + gsm_xsmp_client_new (IceConn ice_conn) + { + GsmXSMPClient *xsmp; + + xsmp = g_object_new (GSM_TYPE_XSMP_CLIENT, + "ice-connection", ice_conn, + NULL); + + return GSM_CLIENT (xsmp); + } + + static Status + register_client_callback (SmsConn conn, + SmPointer manager_data, + char *previous_id) + { + GsmXSMPClient *client = manager_data; + gboolean handled; + char *id; +diff --git a/gnome-session/gsm-xsmp-client.h b/gnome-session/gsm-xsmp-client.h +index 1bb27975..6b95c51b 100644 +--- a/gnome-session/gsm-xsmp-client.h ++++ b/gnome-session/gsm-xsmp-client.h +@@ -27,61 +27,62 @@ G_BEGIN_DECLS + + #define GSM_TYPE_XSMP_CLIENT (gsm_xsmp_client_get_type ()) + #define GSM_XSMP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClient)) + #define GSM_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass)) + #define GSM_IS_XSMP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_XSMP_CLIENT)) + #define GSM_IS_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_XSMP_CLIENT)) + #define GSM_XSMP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass)) + + typedef struct _GsmXSMPClient GsmXSMPClient; + typedef struct _GsmXSMPClientClass GsmXSMPClientClass; + + typedef struct GsmXSMPClientPrivate GsmXSMPClientPrivate; + + struct _GsmXSMPClient + { + GsmClient parent; + GsmXSMPClientPrivate *priv; + }; + + struct _GsmXSMPClientClass + { + GsmClientClass parent_class; + + /* signals */ + gboolean (*register_request) (GsmXSMPClient *client, + char **client_id); + void (*register_confirmed) (GsmXSMPClient *client, + const char *client_id); + gboolean (*logout_request) (GsmXSMPClient *client, + gboolean prompt); +- ++ gboolean (*save_request) (GsmXSMPClient *client, ++ gboolean prompt); + + void (*saved_state) (GsmXSMPClient *client); + + void (*request_phase2) (GsmXSMPClient *client); + + void (*request_interaction) (GsmXSMPClient *client); + void (*interaction_done) (GsmXSMPClient *client, + gboolean cancel_shutdown); + + void (*save_yourself_done) (GsmXSMPClient *client); + + }; + + GType gsm_xsmp_client_get_type (void) G_GNUC_CONST; + + GsmClient *gsm_xsmp_client_new (IceConn ice_conn); + + void gsm_xsmp_client_connect (GsmXSMPClient *client, + SmsConn conn, + unsigned long *mask_ret, + SmsCallbacks *callbacks_ret); + + void gsm_xsmp_client_save_state (GsmXSMPClient *client); + void gsm_xsmp_client_save_yourself (GsmXSMPClient *client, + gboolean save_state); + void gsm_xsmp_client_save_yourself_phase2 (GsmXSMPClient *client); + void gsm_xsmp_client_interact (GsmXSMPClient *client); + void gsm_xsmp_client_shutdown_cancelled (GsmXSMPClient *client); + + G_END_DECLS +diff --git a/gnome-session/org.gnome.SessionManager.xml b/gnome-session/org.gnome.SessionManager.xml +index 580ec356..29eb0990 100644 +--- a/gnome-session/org.gnome.SessionManager.xml ++++ b/gnome-session/org.gnome.SessionManager.xml +@@ -255,60 +255,68 @@ + + + + True if condition is handled, false otherwise + + + + + Allows the caller to determine whether the session manager is + handling changes to the specified autostart condition. + + + + + + + + Request a shutdown dialog. + + + + + + + + Request a reboot dialog. + + + + ++ ++ ++ ++ Request to save session ++ ++ ++ ++ + + + + True if shutdown is available to the user, false otherwise + + + + + Allows the caller to determine whether or not it's okay to show + a shutdown option in the UI + + + + + + + + The type of logout that is being requested + + + + + Request a logout dialog + + Allowed values for the mode parameter are: + + + 0 + Normal. + +-- +2.14.2 + diff --git a/SOURCES/0009-Revert-Allow-saved-session-to-be-a-symlink.patch b/SOURCES/0009-Revert-Allow-saved-session-to-be-a-symlink.patch new file mode 100644 index 0000000..2cb0afd --- /dev/null +++ b/SOURCES/0009-Revert-Allow-saved-session-to-be-a-symlink.patch @@ -0,0 +1,272 @@ +From abf8b3509a0debaf124669d9626a6d9883a1a0d3 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Dec 2013 11:22:07 -0500 +Subject: [PATCH 09/19] Revert "Allow saved-session to be a symlink" + +This reverts commit b733c2ee519b65c3c4eab0d0e93056412f995f3f. +--- + gnome-session/gsm-session-save.c | 32 ++++++++++++++++++++++++++++---- + gnome-session/gsm-util.c | 6 ++++++ + 2 files changed, 34 insertions(+), 4 deletions(-) + +diff --git a/gnome-session/gsm-session-save.c b/gnome-session/gsm-session-save.c +index d6000e09..eebc5ff1 100644 +--- a/gnome-session/gsm-session-save.c ++++ b/gnome-session/gsm-session-save.c +@@ -9,61 +9,61 @@ + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + + #include + + #include + #include + #include + + #include "gsm-util.h" + #include "gsm-autostart-app.h" + #include "gsm-client.h" + + #include "gsm-session-save.h" + + #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" + #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" + + + static gboolean gsm_session_clear_saved_session (const char *directory, + GHashTable *discard_hash); + + typedef struct { +- const char *dir; ++ char *dir; + GHashTable *discard_hash; + GError **error; + } SessionSaveData; + + static gboolean + save_one_client (char *id, + GObject *object, + SessionSaveData *data) + { + GsmClient *client; + GKeyFile *keyfile; + const char *app_id; + char *path = NULL; + char *filename = NULL; + char *contents = NULL; + gsize length = 0; + char *discard_exec; + GError *local_error; + + client = GSM_CLIENT (object); + + local_error = NULL; + + keyfile = gsm_client_save (client, &local_error); + + if (keyfile == NULL || local_error) { + goto out; + } + + contents = g_key_file_to_data (keyfile, &length, &local_error); +@@ -112,89 +112,113 @@ save_one_client (char *id, + } + + g_debug ("GsmSessionSave: saved client %s to %s", id, filename); + + out: + if (keyfile != NULL) { + g_key_file_free (keyfile); + } + + g_free (contents); + g_free (filename); + g_free (path); + + /* in case of any error, stop saving session */ + if (local_error) { + g_propagate_error (data->error, local_error); + g_error_free (local_error); + + return TRUE; + } + + return FALSE; + } + + void + gsm_session_save (GsmStore *client_store, + GError **error) + { + GSettings *settings; + const char *save_dir; ++ char *tmp_dir; + SessionSaveData data; + + g_debug ("GsmSessionSave: Saving session"); + + /* Clear one shot key autosave in the event its set (so that it's actually + * one shot only) + */ + settings = g_settings_new (GSM_MANAGER_SCHEMA); + g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE); + g_object_unref (settings); + + save_dir = gsm_util_get_saved_session_dir (); + if (save_dir == NULL) { + g_warning ("GsmSessionSave: cannot create saved session directory"); + return; + } + +- data.dir = save_dir; ++ tmp_dir = gsm_util_get_empty_tmp_session_dir (); ++ if (tmp_dir == NULL) { ++ g_warning ("GsmSessionSave: cannot create new saved session directory"); ++ return; ++ } ++ ++ /* save the session in a temp directory, and remember the discard ++ * commands */ ++ data.dir = tmp_dir; + data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); +- /* remove old saved session */ +- gsm_session_clear_saved_session (save_dir, data.discard_hash); + data.error = error; + + gsm_store_foreach (client_store, + (GsmStoreFunc) save_one_client, + &data); + ++ if (!*error) { ++ /* remove the old saved session */ ++ gsm_session_clear_saved_session (save_dir, data.discard_hash); ++ ++ /* rename the temp session dir */ ++ if (g_file_test (save_dir, G_FILE_TEST_IS_DIR)) ++ g_rmdir (save_dir); ++ g_rename (tmp_dir, save_dir); ++ } else { ++ g_warning ("GsmSessionSave: error saving session: %s", (*error)->message); ++ /* FIXME: we should create a hash table filled with the discard ++ * commands that are in desktop files from save_dir. */ ++ gsm_session_clear_saved_session (tmp_dir, NULL); ++ g_rmdir (tmp_dir); ++ } ++ + g_hash_table_destroy (data.discard_hash); ++ g_free (tmp_dir); + } + + static gboolean + gsm_session_clear_one_client (const char *filename, + GHashTable *discard_hash) + { + gboolean result = TRUE; + GKeyFile *key_file; + char *discard_exec = NULL; + char **envp; + + g_debug ("GsmSessionSave: removing '%s' from saved session", filename); + + envp = (char **) gsm_util_listenv (); + key_file = g_key_file_new (); + if (g_key_file_load_from_file (key_file, filename, + G_KEY_FILE_NONE, NULL)) { + char **argv; + int argc; + + discard_exec = g_key_file_get_string (key_file, + G_KEY_FILE_DESKTOP_GROUP, + GSM_AUTOSTART_APP_DISCARD_KEY, + NULL); + if (!discard_exec) + goto out; + + if (discard_hash && g_hash_table_lookup (discard_hash, discard_exec)) + goto out; + +diff --git a/gnome-session/gsm-util.c b/gnome-session/gsm-util.c +index 4772c6e6..30edb577 100644 +--- a/gnome-session/gsm-util.c ++++ b/gnome-session/gsm-util.c +@@ -71,63 +71,69 @@ gsm_util_find_desktop_file_for_app_name (const char *name, + g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path); + } + + /* look for gnome vendor prefix */ + if (app_path == NULL) { + g_free (desktop_file); + desktop_file = g_strdup_printf ("gnome-%s.desktop", name); + + g_key_file_load_from_dirs (key_file, + desktop_file, + (const char **) app_dirs, + &app_path, + G_KEY_FILE_NONE, + NULL); + if (app_path != NULL) { + g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path); + } + } + + g_free (desktop_file); + g_key_file_free (key_file); + + g_strfreev (app_dirs); + + return app_path; + } + + static gboolean + ensure_dir_exists (const char *dir) + { ++ if (g_file_test (dir, G_FILE_TEST_IS_DIR)) ++ return TRUE; ++ + if (g_mkdir_with_parents (dir, 0755) == 0) + return TRUE; + ++ if (errno == EEXIST) ++ return g_file_test (dir, G_FILE_TEST_IS_DIR); ++ + g_warning ("GsmSessionSave: Failed to create directory %s: %s", dir, strerror (errno)); + + return FALSE; + } + + gchar * + gsm_util_get_empty_tmp_session_dir (void) + { + char *tmp; + gboolean exists; + + tmp = g_build_filename (g_get_user_config_dir (), + "gnome-session", + "saved-session.new", + NULL); + + exists = ensure_dir_exists (tmp); + + if (G_UNLIKELY (!exists)) { + g_warning ("GsmSessionSave: could not create directory for saved session: %s", tmp); + g_free (tmp); + return NULL; + } else { + /* make sure it's empty */ + GDir *dir; + const char *filename; + + dir = g_dir_open (tmp, 0, NULL); + if (dir) { + while ((filename = g_dir_read_name (dir))) { +-- +2.14.2 + diff --git a/SOURCES/0010-Allow-saved-session-directory-to-be-a-symlink.patch b/SOURCES/0010-Allow-saved-session-directory-to-be-a-symlink.patch new file mode 100644 index 0000000..c026ffa --- /dev/null +++ b/SOURCES/0010-Allow-saved-session-directory-to-be-a-symlink.patch @@ -0,0 +1,191 @@ +From 7c05176e0db2b4d00971a5baf940ad075a088c70 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Dec 2013 11:22:53 -0500 +Subject: [PATCH 10/19] Allow saved-session directory to be a symlink + +This gives us the option of adding a rudimentary session +chooser later. +--- + gnome-session/gsm-session-save.c | 36 ++++++++++++++++++++++++++++++------ + gnome-session/gsm-util.c | 6 ------ + 2 files changed, 30 insertions(+), 12 deletions(-) + +diff --git a/gnome-session/gsm-session-save.c b/gnome-session/gsm-session-save.c +index eebc5ff1..66914b57 100644 +--- a/gnome-session/gsm-session-save.c ++++ b/gnome-session/gsm-session-save.c +@@ -148,67 +148,91 @@ gsm_session_save (GsmStore *client_store, + * one shot only) + */ + settings = g_settings_new (GSM_MANAGER_SCHEMA); + g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE); + g_object_unref (settings); + + save_dir = gsm_util_get_saved_session_dir (); + if (save_dir == NULL) { + g_warning ("GsmSessionSave: cannot create saved session directory"); + return; + } + + tmp_dir = gsm_util_get_empty_tmp_session_dir (); + if (tmp_dir == NULL) { + g_warning ("GsmSessionSave: cannot create new saved session directory"); + return; + } + + /* save the session in a temp directory, and remember the discard + * commands */ + data.dir = tmp_dir; + data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + data.error = error; + + gsm_store_foreach (client_store, + (GsmStoreFunc) save_one_client, + &data); + + if (!*error) { +- /* remove the old saved session */ +- gsm_session_clear_saved_session (save_dir, data.discard_hash); ++ char *session_dir; + +- /* rename the temp session dir */ +- if (g_file_test (save_dir, G_FILE_TEST_IS_DIR)) +- g_rmdir (save_dir); +- g_rename (tmp_dir, save_dir); ++ if (g_file_test (save_dir, G_FILE_TEST_IS_SYMLINK)) ++ session_dir = g_file_read_link (save_dir, error); ++ else ++ session_dir = g_strdup (save_dir); ++ ++ if (session_dir != NULL) { ++ ++ char *absolute_session_dir; ++ ++ if (g_path_is_absolute (session_dir)) { ++ absolute_session_dir = g_strdup (session_dir); ++ } else { ++ char *parent_dir; ++ ++ parent_dir = g_path_get_dirname (save_dir); ++ absolute_session_dir = g_build_filename (parent_dir, session_dir, NULL); ++ g_free (parent_dir); ++ } ++ g_free (session_dir); ++ ++ /* remove the old saved session */ ++ gsm_session_clear_saved_session (absolute_session_dir, data.discard_hash); ++ ++ if (g_file_test (absolute_session_dir, G_FILE_TEST_IS_DIR)) ++ g_rmdir (absolute_session_dir); ++ g_rename (tmp_dir, absolute_session_dir); ++ ++ g_free (absolute_session_dir); ++ } + } else { + g_warning ("GsmSessionSave: error saving session: %s", (*error)->message); + /* FIXME: we should create a hash table filled with the discard + * commands that are in desktop files from save_dir. */ + gsm_session_clear_saved_session (tmp_dir, NULL); + g_rmdir (tmp_dir); + } + + g_hash_table_destroy (data.discard_hash); + g_free (tmp_dir); + } + + static gboolean + gsm_session_clear_one_client (const char *filename, + GHashTable *discard_hash) + { + gboolean result = TRUE; + GKeyFile *key_file; + char *discard_exec = NULL; + char **envp; + + g_debug ("GsmSessionSave: removing '%s' from saved session", filename); + + envp = (char **) gsm_util_listenv (); + key_file = g_key_file_new (); + if (g_key_file_load_from_file (key_file, filename, + G_KEY_FILE_NONE, NULL)) { + char **argv; + int argc; + +diff --git a/gnome-session/gsm-util.c b/gnome-session/gsm-util.c +index 30edb577..4772c6e6 100644 +--- a/gnome-session/gsm-util.c ++++ b/gnome-session/gsm-util.c +@@ -71,69 +71,63 @@ gsm_util_find_desktop_file_for_app_name (const char *name, + g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path); + } + + /* look for gnome vendor prefix */ + if (app_path == NULL) { + g_free (desktop_file); + desktop_file = g_strdup_printf ("gnome-%s.desktop", name); + + g_key_file_load_from_dirs (key_file, + desktop_file, + (const char **) app_dirs, + &app_path, + G_KEY_FILE_NONE, + NULL); + if (app_path != NULL) { + g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path); + } + } + + g_free (desktop_file); + g_key_file_free (key_file); + + g_strfreev (app_dirs); + + return app_path; + } + + static gboolean + ensure_dir_exists (const char *dir) + { +- if (g_file_test (dir, G_FILE_TEST_IS_DIR)) +- return TRUE; +- + if (g_mkdir_with_parents (dir, 0755) == 0) + return TRUE; + +- if (errno == EEXIST) +- return g_file_test (dir, G_FILE_TEST_IS_DIR); +- + g_warning ("GsmSessionSave: Failed to create directory %s: %s", dir, strerror (errno)); + + return FALSE; + } + + gchar * + gsm_util_get_empty_tmp_session_dir (void) + { + char *tmp; + gboolean exists; + + tmp = g_build_filename (g_get_user_config_dir (), + "gnome-session", + "saved-session.new", + NULL); + + exists = ensure_dir_exists (tmp); + + if (G_UNLIKELY (!exists)) { + g_warning ("GsmSessionSave: could not create directory for saved session: %s", tmp); + g_free (tmp); + return NULL; + } else { + /* make sure it's empty */ + GDir *dir; + const char *filename; + + dir = g_dir_open (tmp, 0, NULL); + if (dir) { + while ((filename = g_dir_read_name (dir))) { +-- +2.14.2 + diff --git a/SOURCES/0011-Tie-session-selector-to-properties-dialog.patch b/SOURCES/0011-Tie-session-selector-to-properties-dialog.patch new file mode 100644 index 0000000..fc1e2ce --- /dev/null +++ b/SOURCES/0011-Tie-session-selector-to-properties-dialog.patch @@ -0,0 +1,1162 @@ +From 63b277e801d8bce92abe4e52f7de4cf059f1f896 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Dec 2013 11:28:53 -0500 +Subject: [PATCH 11/19] Tie session selector to properties dialog + +--- + capplet/gsm-properties-dialog.c | 30 +++++- + configure.ac | 3 +- + data/session-selector.ui | 2 +- + tools/Makefile.am | 1 + + tools/gnome-session-selector.c | 211 ++++++++++++++++++++++++++++++++-------- + 5 files changed, 200 insertions(+), 47 deletions(-) + +diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c +index d2be778b..51fa5106 100644 +--- a/capplet/gsm-properties-dialog.c ++++ b/capplet/gsm-properties-dialog.c +@@ -471,87 +471,113 @@ session_saved_message (GsmPropertiesDialog *dialog, + { + GtkLabel *label; + gchar *markup; + label = GTK_LABEL (gtk_builder_get_object (dialog->priv->xml, CAPPLET_SESSION_SAVED_WIDGET_NAME)); + if (is_error) + markup = g_markup_printf_escaped ("%s", msg); + else + markup = g_markup_escape_text (msg, -1); + gtk_label_set_markup (label, markup); + g_free (markup); + } + + static void + session_saved_cb (DBusGProxy *proxy, + DBusGProxyCall *call_id, + void *user_data) + { + gboolean res; + GsmPropertiesDialog *dialog = user_data; + + res = dbus_g_proxy_end_call (proxy, call_id, NULL, G_TYPE_INVALID); + if (res) + session_saved_message (dialog, _("Your session has been saved."), FALSE); + else + session_saved_message (dialog, _("Failed to save session"), TRUE); + + g_object_unref (proxy); + } + + static void +-on_save_session_clicked (GtkWidget *widget, +- GsmPropertiesDialog *dialog) ++save_session_directly (GsmPropertiesDialog *dialog) + { + DBusGConnection *conn; + DBusGProxy *proxy; + DBusGProxyCall *call; + + conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + if (conn == NULL) { + session_saved_message (dialog, _("Could not connect to the session bus"), TRUE); + return; + } + + proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS); + if (proxy == NULL) { + session_saved_message (dialog, _("Could not connect to the session manager"), TRUE); + return; + } + + call = dbus_g_proxy_begin_call (proxy, "SaveSession", session_saved_cb, dialog, NULL, G_TYPE_INVALID); + if (call == NULL) { + session_saved_message (dialog, _("Failed to save session"), TRUE); + g_object_unref (proxy); + return; + } + } + ++static void ++save_session_from_selector (GsmPropertiesDialog *dialog, ++ const char *program_path) ++{ ++ char *command_line = g_strdup_printf ("%s --action save", program_path); ++ ++ g_spawn_command_line_sync (command_line, NULL, NULL, NULL, NULL); ++ ++ g_free (command_line); ++} ++ ++static void ++on_save_session_clicked (GtkWidget *widget, ++ GsmPropertiesDialog *dialog) ++{ ++ char *program_path; ++ ++ program_path = g_find_program_in_path ("gnome-session-selector"); ++ ++ if (program_path != NULL) { ++ save_session_from_selector (dialog, program_path); ++ g_free (program_path); ++ } else { ++ save_session_directly (dialog); ++ } ++} ++ + static void + setup_dialog (GsmPropertiesDialog *dialog) + { + GtkTreeView *treeview; + GtkWidget *button; + GtkTreeModel *tree_filter; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkTargetList *targetlist; + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_ICON, + G_TYPE_STRING, + G_TYPE_OBJECT, + G_TYPE_STRING); + tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store), + NULL); + g_object_unref (dialog->priv->list_store); + dialog->priv->tree_filter = tree_filter; + + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter), + STORE_COL_VISIBLE); + +diff --git a/configure.ac b/configure.ac +index 73e69bc7..d0eeab8a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -21,91 +21,90 @@ LT_PREREQ([2.2.6]) + LT_INIT([dlopen disable-static]) + + GNOME_MAINTAINER_MODE_DEFINES + GNOME_COMPILE_WARNINGS([maximum]) + + AC_ARG_ENABLE(deprecation_flags, + [AS_HELP_STRING([--enable-deprecation-flags], + [use *_DISABLE_DEPRECATED flags @<:@default=no@:>@])],, + [enable_deprecation_flags=no]) + + if test "x$enable_deprecation_flags" = "xyes"; then + DISABLE_DEPRECATED_CFLAGS=$DISABLE_DEPRECATED + AC_SUBST([DISABLE_DEPRECATED_CFLAGS]) + fi + + GLIB_REQUIRED=2.46.0 + GTK3_REQUIRED=3.18.0 + DBUS_GLIB_REQUIRED=0.76 + UPOWER_REQUIRED=0.9.0 + JSON_GLIB_REQUIRED=0.10 + GNOME_DESKTOP_REQUIRED=3.18.0 + + AC_ARG_ENABLE(session-selector, AS_HELP_STRING([--enable-session-selector], + [enable building a custom session selector dialog]), + enable_session_selector=$enableval,enable_session_selector=no) + + AM_CONDITIONAL(BUILD_SESSION_SELECTOR, + [test "$enable_session_selector" = yes]) + + if test "$enable_session_selector" = yes; then +- PKG_CHECK_MODULES(SESSION_SELECTOR, gtk+-3.0 gio-2.0) ++ PKG_CHECK_MODULES(SESSION_SELECTOR, gtk+-3.0 gio-2.0 dbus-glib-1 >= $DBUS_GLIB_REQUIRED) + fi + + dnl ==================================================================== + dnl Dependency Checks + dnl ==================================================================== + + dnl Standard vertical stacks + PKG_CHECK_MODULES(GIO, gio-2.0) + PKG_CHECK_MODULES(GIOUNIX, gio-unix-2.0 >= $GLIB_REQUIRED) + PKG_CHECK_MODULES(GTK3, gtk+-3.0 >= $GTK3_REQUIRED) + + PKG_CHECK_MODULES(GNOME_SESSION, + glib-2.0 >= $GLIB_REQUIRED + gio-2.0 >= $GLIB_REQUIRED + json-glib-1.0 >= $JSON_GLIB_REQUIRED + gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED + ) + + dnl We can only support old upower + dnl https://bugzilla.gnome.org/show_bug.cgi?id=710383 + PKG_CHECK_MODULES(UPOWER, upower-glib < 0.99.0, have_old_upower=yes, have_old_upower=no) + AS_IF([test x$have_old_upower = xyes], [ + AC_DEFINE([HAVE_OLD_UPOWER], [1], [Define if we have an older upower]) + ]) + AM_CONDITIONAL(HAVE_OLD_UPOWER, test x$have_old_upower = xyes) + + PKG_CHECK_MODULES(SESSION_PROPERTIES, + glib-2.0 >= $GLIB_REQUIRED + gtk+-3.0 >= $GTK3_REQUIRED +- dbus-glib-1 >= $DBUS_GLIB_REQUIRED + ) + + PKG_CHECK_MODULES(X11, x11) + PKG_CHECK_MODULES(SM, sm) + PKG_CHECK_MODULES(ICE, ice) + PKG_CHECK_MODULES(XEXT, xext xau) + + PKG_CHECK_MODULES(GL_TEST, xcomposite gl glib-2.0 epoxy) + PKG_CHECK_MODULES(GLES_TEST, egl glesv2) + + dnl ==================================================================== + dnl Check for gconf + dnl ==================================================================== + AC_ARG_ENABLE([gconf], + AS_HELP_STRING([--enable-gconf], [Support gconf-based autostart]), + [enable_gconf=$enableval], + [enable_gconf=auto]) + + if test x$enable_gconf != xno ; then + PKG_CHECK_MODULES(GCONF, gconf-2.0, [have_gconf=yes], [have_gconf=no]) + + if test x$enable_gconf = xyes -a x$have_gconf = xno ; then + AC_MSG_ERROR([GConf support explicitly required, but gconf not found]) + fi + + if test x$have_gconf = xyes ; then + AC_DEFINE([HAVE_GCONF], [1], [Define if we support gconf-based autostart]) + fi + fi + +diff --git a/data/session-selector.ui b/data/session-selector.ui +index 1c55712d..1534a746 100644 +--- a/data/session-selector.ui ++++ b/data/session-selector.ui +@@ -20,61 +20,61 @@ + + + True + 0.5 + out + + + True + 12 + + + True + vertical + 6 + + + + True + other + + + + True + vertical + 0 + + + True + 0.0 + 0.5 +- Please select a custom session to run ++ Please select a custom session to use + + + True + True + 0 + + + + + + + False + True + 0 + + + + + True + vertical + 12 + + + True + 12 + + + True + True + never +diff --git a/tools/Makefile.am b/tools/Makefile.am +index d3b8bf3f..d6784d1b 100644 +--- a/tools/Makefile.am ++++ b/tools/Makefile.am +@@ -68,37 +68,38 @@ gnome_session_check_accelerated_gl_helper_CPPFLAGS = \ + + gnome_session_check_accelerated_gl_helper_LDADD = \ + $(GL_TEST_LIBS) \ + $(X11_LIBS) + + gnome_session_check_accelerated_SOURCES = \ + gnome-session-check-accelerated-common.h \ + gnome-session-check-accelerated.c + + gnome_session_check_accelerated_CPPFLAGS = \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + $(AM_CPPFLAGS) \ + $(GTK3_CFLAGS) \ + $(GL_TEST_CFLAGS) + + gnome_session_check_accelerated_LDADD = \ + $(GTK3_LIBS) \ + $(X11_LIBS) \ + $(GL_TEST_LIBS) + + if BUILD_SESSION_SELECTOR + gnome_session_selector_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(GNOME_SESSION_CFLAGS) \ + $(DBUS_GLIB_CFLAGS) \ + -DGTKBUILDER_DIR=\""$(pkgdatadir)"\" \ + -DLOCALE_DIR=\""$(datadir)/locale"\" \ + $(DISABLE_DEPRECATED_CFLAGS) + + gnome_session_selector_LDADD = \ ++ $(DBUS_GLIB_CFLAGS) \ + $(SESSION_SELECTOR_LIBS) + + gnome_session_selector_SOURCES = \ + gnome-session-selector.c + endif + + -include $(top_srcdir)/git.mk +diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c +index 71892c43..53822f6c 100644 +--- a/tools/gnome-session-selector.c ++++ b/tools/gnome-session-selector.c +@@ -7,126 +7,133 @@ + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: Matthias Clasen + */ + + #include "config.h" + + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #include + #include + ++#include ++#include ++ ++#define GSM_SERVICE_DBUS "org.gnome.SessionManager" ++#define GSM_PATH_DBUS "/org/gnome/SessionManager" ++#define GSM_INTERFACE_DBUS "org.gnome.SessionManager" ++ + #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" + #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" + + static GtkBuilder *builder; + static GtkWidget *session_list; + static GtkListStore *store; + static GtkTreeModelSort *sort_model; ++static char *info_text; + + static void select_session (const char *name); ++static gboolean make_session_current (const char *name); + + static char * + get_session_path (const char *name) + { + return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); + } + + static char * + find_new_session_name (void) + { + char *name; + char *path; + int i; + + for (i = 1; i < 20; i++) { + name = g_strdup_printf (_("Session %d"), i); + path = get_session_path (name); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + g_free (path); + return name; + } + g_free (path); + g_free (name); + } + + return NULL; + } + + static gboolean + is_valid_session_name (const char *name) + { + GtkTreeIter iter; + char *n; +- const char *info_text; + char *warning_text; + gboolean user_tried_dot; + gboolean user_tried_slash; + GtkWidget *info_bar; + GtkWidget *label; + + if (name[0] == 0) { + return FALSE; + } + + if (name[0] == '.') { + user_tried_dot = TRUE; + } else { + user_tried_dot = FALSE; + } + + if (strchr (name, '/') != NULL) { + user_tried_slash = TRUE; + } else { + user_tried_slash = FALSE; + } + +- info_text = _("Please select a custom session to run"); + warning_text = NULL; + if (user_tried_dot && user_tried_slash) { + warning_text = g_strdup_printf ("%s\nNote: %s", + info_text, + _("Session names are not allowed to start with “.” or contain “/” characters")); + } else if (user_tried_dot) { + warning_text = g_strdup_printf ("%s\nNote: %s", + info_text, + _("Session names are not allowed to start with “.”")); + } else if (user_tried_slash) { + warning_text = g_strdup_printf ("%s\nNote: %s", + info_text, + _("Session names are not allowed to contain “/” characters")); + } + + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); + do { + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &n, -1); + if (strcmp (n, name) == 0) { + char *message; + message = g_strdup_printf (_("A session named “%s” already exists"), name); + warning_text = g_strdup_printf ("%s\nNote: %s", info_text, message); + g_free (message); + g_free (n); + break; + } + g_free (n); + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); + + info_bar = (GtkWidget *) gtk_builder_get_object (builder, "info-bar"); +@@ -181,518 +188,638 @@ populate_session_list (GtkWidget *session_list) + + default_name = NULL; + if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) { + default_name = g_path_get_basename (last_session); + } + + while ((name = g_dir_read_name (dir)) != NULL) { + if (strcmp (name, "saved-session") == 0) + continue; + + gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1); + + if (g_strcmp0 (default_name, name) == 0) { + GtkTreeSelection *selection; + GtkTreeIter child_iter; + + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &child_iter, &iter); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + gtk_tree_selection_select_iter (selection, &child_iter); + } + } + + g_free (default_name); + g_dir_close (dir); + + out: + g_free (saved_session); + g_free (path); + } + ++static char * ++get_last_session (void) ++{ ++ char *saved_session; ++ char last_session[PATH_MAX] = ""; ++ char *name = NULL; ++ ++ saved_session = get_session_path ("saved-session"); ++ ++ if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) { ++ name = g_path_get_basename (last_session); ++ } ++ ++ g_free (saved_session); ++ ++ return name; ++} ++ + static char * + get_selected_session (void) + { + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + gchar *name; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_tree_model_get (model, &iter, 0, &name, -1); + return name; + } + + return NULL; + } + + static void + remove_session (const char *name) + { + char *path1, *path2; + char *n, *path; + const char *d; + GDir *dir; + GError *error; + + path1 = get_session_path ("saved-session"); + path2 = get_session_path (name); + + error = NULL; + n = g_file_read_link (path1, &error); + if (n == NULL) { + g_warning ("Failed to read link: %s", error->message); + g_error_free (error); + } + else if (strcmp (n, name) == 0) { + unlink (path1); + } + g_free (n); + + dir = g_dir_open (path2, 0, NULL); + while ((d = g_dir_read_name (dir)) != NULL) { + path = g_build_filename (path2, d, NULL); + unlink (path); + g_free (path); + } + g_dir_close (dir); + + remove (path2); + + g_free (path1); + g_free (path2); + } + ++static gboolean ++make_session_current (const char *name) ++{ ++ char *path1; ++ gboolean ret = TRUE; ++ ++ path1 = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", NULL); ++ ++ unlink (path1); ++ if (symlink (name, path1) < 0) { ++ g_warning ("Failed to make session '%s' current", name); ++ ret = FALSE; ++ } ++ ++ g_free (path1); ++ ++ return ret; ++} ++ + static void + on_remove_session_clicked (GtkButton *button, + gpointer data) + { + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + char *name; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + GtkTreeIter child_iter; + gtk_tree_model_get (model, &iter, 0, &name, -1); + remove_session (name); + g_free (name); + + gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model), &child_iter, &iter); + gtk_list_store_remove (GTK_LIST_STORE (store), &child_iter); + + if (!gtk_tree_selection_get_selected (selection, NULL, NULL)) { + gtk_tree_model_get_iter_first (model, &iter); + gtk_tree_model_get (model, &iter, 0, &name, -1); + select_session (name); ++ make_session_current (name); + g_free (name); + } + } + } + + static void + begin_rename (void) + { + GtkTreePath *path; + GtkTreeViewColumn *column; + GList *cells; + + gtk_widget_grab_focus (session_list); + + gtk_tree_view_get_cursor (GTK_TREE_VIEW (session_list), + &path, &column); + + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); + + if (cells != NULL) { + GtkCellRenderer *cell; + + cell = (GtkCellRenderer *) cells->data; + g_list_free (cells); + + g_object_set (cell, "editable", TRUE, NULL); + gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (session_list), path, + column, cell, TRUE); + } + gtk_tree_path_free (path); + } + + static void + on_rename_session_clicked (GtkButton *button, + gpointer data) + { + begin_rename (); + } + + static void + on_continue_clicked (GtkButton *button, + gpointer data) + { + char *name; + + name = get_selected_session (); + g_free (name); + + gtk_main_quit (); + } + + static void + create_session (const char *name) + { + char *path; + GtkTreeIter iter; + + path = get_session_path (name); + +- if (mkdir (path, 0755) < 0) { ++ if (g_mkdir_with_parents (path, 0755) < 0) { + g_warning ("Failed to create directory %s", path); + } + else { + char *marker; + + gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1); + + marker = g_build_filename (path, ".new-session", NULL); + creat (marker, 0600); + g_free (marker); + } + + g_free (path); + } + + static gboolean + rename_session (const char *old_name, + const char *new_name) + { + char *old_path, *new_path; + int result; + + if (g_strcmp0 (old_name, new_name) == 0) { + return TRUE; + } + + if (!is_valid_session_name (new_name)) { + return FALSE; + } + + old_path = get_session_path (old_name); + new_path = get_session_path (new_name); + + result = g_rename (old_path, new_path); + + if (result < 0) { + g_warning ("Failed to rename session from '%s' to '%s': %m", old_name, new_name); ++ } else { ++ char *last_session; ++ last_session = get_last_session (); ++ if (g_strcmp0 (old_name, last_session) == 0) { ++ make_session_current (new_name); ++ } ++ g_free (last_session); + } + + g_free (old_path); + g_free (new_path); +- + return result == 0; + } + +-static gboolean +-make_session_current (const char *name) +-{ +- char *path1; +- gboolean ret = TRUE; +- +- path1 = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", NULL); +- +- unlink (path1); +- if (symlink (name, path1) < 0) { +- g_warning ("Failed to make session '%s' current", name); +- ret = FALSE; +- } +- +- g_free (path1); +- +- return ret; +-} +- + static gboolean + create_and_select_session (const char *name) + { + gchar *path; + + if (name[0] == 0 || name[0] == '.' || strchr (name, '/')) { + g_warning ("Invalid session name"); + return FALSE; + } + + path = g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); + if (!g_file_test (path, G_FILE_TEST_IS_DIR)) { +- if (mkdir (path, 0755) < 0) { ++ if (g_mkdir_with_parents (path, 0755) < 0) { + g_warning ("Failed to create directory %s", path); + g_free (path); + return FALSE; + } + } + + g_free (path); + + return make_session_current (name); + } + + static void + select_session (const char *name) + { + GtkTreeIter iter; + char *n; + +- make_session_current (name); +- + /* now select it in the list */ + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sort_model), &iter); + do { + gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &iter, 0, &n, -1); + if (strcmp (n, name) == 0) { + GtkTreePath *path; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sort_model), &iter); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (session_list), path, NULL, FALSE); + gtk_tree_path_free (path); + g_free (n); + break; + } + g_free (n); + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (sort_model), &iter)); + } + + static void +-on_new_session_clicked (GtkButton *button, +- gpointer data) ++create_session_and_begin_rename (void) + { + gchar *name; + + name = find_new_session_name (); + create_session (name); + select_session (name); + + begin_rename (); + } + ++static void ++on_new_session_clicked (GtkButton *button, ++ gpointer data) ++{ ++ create_session_and_begin_rename (); ++} ++ + static void + on_selection_changed (GtkTreeSelection *selection, + gpointer data) + { + char *name; + + name = get_selected_session (); + + if (name == NULL) { + return; + } + +- make_session_current (name); +- + g_free (name); + } + + static void + update_remove_button (void) + { + GtkWidget *button; + + button = (GtkWidget *)gtk_builder_get_object (builder, "remove-session"); + if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) > 1) { + gtk_widget_set_sensitive (button, TRUE); + } else { + gtk_widget_set_sensitive (button, FALSE); + } + } + + static void + on_row_edited (GtkCellRendererText *cell, + const char *path_string, + const char *new_name, + gpointer data) + { + GtkTreePath *path; + GtkTreeIter sort_iter, items_iter; + char *old_name; + gboolean was_renamed; + + path = gtk_tree_path_new_from_string (path_string); + gtk_tree_model_get_iter (GTK_TREE_MODEL (sort_model), &sort_iter, path); + + gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &sort_iter, 0, &old_name, -1); + + was_renamed = rename_session (old_name, new_name); + + if (was_renamed) { + gtk_tree_model_sort_convert_iter_to_child_iter (sort_model, &items_iter, &sort_iter); + + gtk_list_store_set (store, &items_iter, 0, g_strdup (new_name), -1); + g_free (old_name); +- make_session_current (new_name); + } else { + begin_rename (); + } + + gtk_tree_path_free (path); + + g_object_set (cell, "editable", FALSE, NULL); + } + + static void + on_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer data) + { + update_remove_button (); + } + + static void + on_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) + { + update_remove_button (); + } + + static void + on_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data) + { +- char *name; +- +- name = get_selected_session (); +- g_free (name); +- + gtk_main_quit (); + } + + static void + auto_save_next_session (void) + { + GSettings *settings; + + settings = g_settings_new (GSM_MANAGER_SCHEMA); + g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, TRUE); + g_object_unref (settings); + } + + static void + auto_save_next_session_if_needed (void) + { + char *marker; + + marker = g_build_filename (g_get_user_config_dir (), + "gnome-session", "saved-session", + ".new-session", NULL); + + if (g_file_test (marker, G_FILE_TEST_EXISTS)) { + auto_save_next_session (); + unlink (marker); + } + g_free (marker); + } + ++static void ++save_session (void) ++{ ++ DBusGConnection *conn; ++ DBusGProxy *proxy; ++ GError *error; ++ ++ conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); ++ if (conn == NULL) { ++ g_warning ("Could not connect to the session bus"); ++ return; ++ } ++ ++ proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS); ++ if (proxy == NULL) { ++ g_warning ("Could not connect to the session manager"); ++ return; ++ } ++ ++ error = NULL; ++ if (!dbus_g_proxy_call (proxy, "SaveSession", &error, G_TYPE_INVALID, G_TYPE_INVALID)) { ++ g_warning ("Failed to save session: %s", error->message); ++ g_error_free (error); ++ return; ++ } ++ ++ g_object_unref (proxy); ++} ++ + static int + compare_sessions (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data) + { + char *name_a, *name_b; + int result; + + gtk_tree_model_get (model, a, 0, &name_a, -1); + gtk_tree_model_get (model, b, 0, &name_b, -1); + + result = g_utf8_collate (name_a, name_b); + + g_free (name_a); + g_free (name_b); + + return result; + } + + static void + on_map (GtkWidget *widget, + gpointer data) + { + gdk_window_focus (gtk_widget_get_window (widget), GDK_CURRENT_TIME); + } + + int + main (int argc, char *argv[]) + { + GtkWidget *window; + GtkWidget *widget; ++ GtkWidget *label; + GtkCellRenderer *cell; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + GError *error; ++ char *selected_session; ++ ++ static char *action = NULL; ++ static char **remaining_args = NULL; ++ static GOptionEntry entries[] = { ++ {"action", '\0', 0, G_OPTION_ARG_STRING, &action, N_("What to do with session selection (save|load|print)"), NULL}, ++{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &remaining_args, N_("[session-name]"), NULL} ++ }; ++ ++ if (action == NULL) { ++ if (getenv ("SESSION_MANAGER") != NULL) ++ action = "print"; ++ else ++ action = "load"; ++ } + +- if (getenv ("SESSION_MANAGER") != NULL) ++ if (getenv ("SESSION_MANAGER") != NULL && strcmp (action, "load") == 0) { ++ g_warning ("Cannot load new session when session currently loaded"); + return 1; ++ } ++ ++ if (getenv ("SESSION_MANAGER") == NULL && strcmp (action, "save") == 0) { ++ g_warning ("Can only save session when session loaded"); ++ return 1; ++ } ++ ++ if (strcmp (action, "load") != 0 && strcmp (action, "save") != 0 && strcmp (action, "print") != 0) { ++ g_warning ("'%s' is not a supported action. Supported actions are load, save, and print.\n", action); ++ return 1; ++ } + +- gtk_init (&argc, &argv); +- if (argc > 1) { +- g_print ("create and select session\n"); +- if (!create_and_select_session (argv[1])) ++ error = NULL; ++ gtk_init_with_args (&argc, &argv, ++ NULL, entries, GETTEXT_PACKAGE, &error); ++ ++ if (remaining_args != NULL) { ++ if (g_strv_length (remaining_args) > 1) { ++ g_warning ("gnome-session-selector takes at most one session argument"); ++ return 1; ++ } ++ ++ if (!create_and_select_session (remaining_args[0])) + return 1; + else + return 0; + } + + builder = gtk_builder_new (); + gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE); + + error = NULL; + if (!gtk_builder_add_from_file (builder, GTKBUILDER_DIR "/" "session-selector.ui", &error)) { + g_warning ("Could not load file 'session-selector.ui': %s", error->message); + exit (1); + } + + window = (GtkWidget *) gtk_builder_get_object (builder, "main-window"); + + store = (GtkListStore *) gtk_builder_get_object (builder, "session-store"); + sort_model = (GtkTreeModelSort *) gtk_builder_get_object (builder, "sort-model"); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model), + 0, compare_sessions, NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_ASCENDING); + g_signal_connect (store, "row-deleted", G_CALLBACK (on_row_deleted), NULL); + g_signal_connect (store, "row-inserted", G_CALLBACK (on_row_inserted), NULL); + session_list = (GtkWidget *) gtk_builder_get_object (builder, "session-list"); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + populate_session_list (session_list); + + cell = gtk_cell_renderer_text_new (); + g_signal_connect (cell, "edited", G_CALLBACK (on_row_edited), NULL); + + column = gtk_tree_view_column_new_with_attributes ("", cell, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (session_list), GTK_TREE_VIEW_COLUMN (column)); + + g_signal_connect (session_list, "row-activated", G_CALLBACK (on_row_activated), NULL); + + g_signal_connect (selection, "changed", + G_CALLBACK (on_selection_changed), NULL); + + widget = (GtkWidget *) gtk_builder_get_object (builder, "new-session"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_new_session_clicked), NULL); + widget = (GtkWidget *) gtk_builder_get_object (builder, "remove-session"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_remove_session_clicked), NULL); + widget = (GtkWidget *) gtk_builder_get_object (builder, "rename-session"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_rename_session_clicked), NULL); + widget = (GtkWidget *) gtk_builder_get_object (builder, "continue-button"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_continue_clicked), NULL); + + g_signal_connect (window, "map", G_CALLBACK (on_map), NULL); + gtk_widget_show (window); + ++ if (g_strcmp0 (action, "load") == 0) { ++ info_text = _("Please select a custom session to run"); ++ } else if (g_strcmp0 (action, "print") == 0) { ++ info_text = _("Please select a session to use"); ++ } else if (g_strcmp0 (action, "save") == 0) { ++ info_text = _("Please select a session to save to"); ++ } ++ ++ label = (GtkWidget*) gtk_builder_get_object (builder, "info-label"); ++ gtk_label_set_markup (GTK_LABEL (label), info_text); ++ ++ selected_session = get_selected_session (); ++ ++ if (selected_session == NULL) { ++ create_session_and_begin_rename (); ++ } else { ++ g_free (selected_session); ++ } ++ + gtk_main (); + +- auto_save_next_session_if_needed (); ++ selected_session = get_selected_session (); ++ ++ if (g_strcmp0 (action, "load") == 0) { ++ make_session_current (selected_session); ++ auto_save_next_session_if_needed (); ++ } else if (g_strcmp0 (action, "save") == 0) { ++ char *last_session; ++ ++ last_session = get_last_session (); ++ make_session_current (selected_session); ++ save_session (); ++ if (last_session != NULL) ++ make_session_current (last_session); ++ } else if (g_strcmp0 (action, "print") == 0) { ++ g_print ("%s\n", selected_session); ++ } ++ g_free (selected_session); + + return 0; + } +-- +2.14.2 + diff --git a/SOURCES/0012-make-save-session-stall-until-it-finishes.patch b/SOURCES/0012-make-save-session-stall-until-it-finishes.patch new file mode 100644 index 0000000..5267a52 --- /dev/null +++ b/SOURCES/0012-make-save-session-stall-until-it-finishes.patch @@ -0,0 +1,429 @@ +From 7553acfe86151ed6bc6649f3e16e2a42c6435930 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Dec 2013 11:32:52 -0500 +Subject: [PATCH 12/19] make save-session stall until it finishes + +--- + gnome-session/gsm-manager.c | 58 ++++++++++++++++++++++++++---- + gnome-session/gsm-manager.h | 2 +- + gnome-session/org.gnome.SessionManager.xml | 1 + + 3 files changed, 53 insertions(+), 8 deletions(-) + +diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c +index 825a6846..6630aab8 100644 +--- a/gnome-session/gsm-manager.c ++++ b/gnome-session/gsm-manager.c +@@ -113,60 +113,61 @@ typedef enum + GSM_MANAGER_LOGOUT_REBOOT_INTERACT, + GSM_MANAGER_LOGOUT_SHUTDOWN, + GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT, + } GsmManagerLogoutType; + + struct GsmManagerPrivate + { + gboolean failsafe; + GsmStore *clients; + GsmStore *inhibitors; + GsmInhibitorFlag inhibited_actions; + GsmStore *apps; + GsmPresence *presence; + GsmXsmpServer *xsmp_server; + + char *session_name; + gboolean is_fallback_session : 1; + + /* Current status */ + GsmManagerPhase phase; + guint phase_timeout_id; + GSList *required_apps; + GSList *pending_apps; + GsmManagerLogoutMode logout_mode; + GSList *query_clients; + guint query_timeout_id; + /* This is used for GSM_MANAGER_PHASE_END_SESSION only at the moment, + * since it uses a sublist of all running client that replied in a + * specific way */ + GSList *next_query_clients; ++ GSList *pending_save_invocations; + /* This is the action that will be done just before we exit */ + GsmManagerLogoutType logout_type; + + /* List of clients which were disconnected due to disabled condition + * and shouldn't be automatically restarted */ + GSList *condition_clients; + + GSList *pending_end_session_tasks; + GCancellable *end_session_cancellable; + + GSettings *settings; + GSettings *session_settings; + GSettings *screensaver_settings; + GSettings *lockdown_settings; + + GsmSystem *system; + GDBusConnection *connection; + GsmExportedManager *skeleton; + gboolean dbus_disconnected : 1; + + GsmShell *shell; + guint shell_end_session_dialog_canceled_id; + guint shell_end_session_dialog_open_failed_id; + guint shell_end_session_dialog_confirmed_logout_id; + guint shell_end_session_dialog_confirmed_shutdown_id; + guint shell_end_session_dialog_confirmed_reboot_id; + }; + + enum { + PROP_0, +@@ -1202,91 +1203,125 @@ query_end_session_complete (GsmManager *manager) + + static gboolean + _client_request_save (GsmClient *client, + ClientEndSessionData *data) + { + gboolean ret; + GError *error; + + error = NULL; + ret = gsm_client_request_save (client, data->flags, &error); + if (ret) { + g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client)); + data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients, + client); + } else if (error) { + g_debug ("GsmManager: unable to query client: %s", error->message); + g_error_free (error); + } + + return FALSE; + } + + static gboolean + _client_request_save_helper (const char *id, + GsmClient *client, + ClientEndSessionData *data) + { + return _client_request_save (client, data); + } + ++static void ++fail_pending_save_invocations (GsmManager *manager, ++ GError *error) ++{ ++ GSList *l; ++ ++ for (l = manager->priv->pending_save_invocations; l != NULL; l = l->next) { ++ DBusGMethodInvocation *context = l->data; ++ ++ dbus_g_method_return_error (context, error); ++ } ++ ++ g_slist_free (manager->priv->pending_save_invocations); ++ manager->priv->pending_save_invocations = NULL; ++} ++ ++static void ++finish_pending_save_invocations (GsmManager *manager) ++{ ++ GSList *l; ++ ++ for (l = manager->priv->pending_save_invocations; l != NULL; l = l->next) { ++ DBusGMethodInvocation *context = l->data; ++ ++ dbus_g_method_return (context); ++ } ++ ++ g_slist_free (manager->priv->pending_save_invocations); ++ manager->priv->pending_save_invocations = NULL; ++} ++ + static void + query_save_session_complete (GsmManager *manager) + { + GError *error = NULL; + + if (g_slist_length (manager->priv->next_query_clients) > 0) { + ClientEndSessionData data; + + data.manager = manager; + data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST; + + g_slist_foreach (manager->priv->next_query_clients, + (GFunc)_client_request_save, + &data); + + g_slist_free (manager->priv->next_query_clients); + manager->priv->next_query_clients = NULL; + + return; + } + + if (manager->priv->query_timeout_id > 0) { + g_source_remove (manager->priv->query_timeout_id); + manager->priv->query_timeout_id = 0; + } + + gsm_session_save (manager->priv->clients, &error); + + if (error) { + g_warning ("Error saving session: %s", error->message); ++ fail_pending_save_invocations (manager, error); + g_error_free (error); ++ } else { ++ finish_pending_save_invocations (manager); + } + } + + static guint32 + generate_cookie (void) + { + guint32 cookie; + + cookie = (guint32)g_random_int_range (1, G_MAXINT32); + + return cookie; + } + + static guint32 + _generate_unique_cookie (GsmManager *manager) + { + guint32 cookie; + + do { + cookie = generate_cookie (); + } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL); + + return cookie; + } + + static gboolean + _on_query_end_session_timeout (GsmManager *manager) + { + GSList *l; + +@@ -2737,92 +2772,101 @@ gsm_manager_initialization_error (GsmExportedManager *skeleton, + GsmManager *manager) + { + if (manager->priv->phase != GSM_MANAGER_PHASE_INITIALIZATION) { + g_dbus_method_invocation_return_error (invocation, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION, + "InitializationError interface is only available during the Initialization phase"); + return TRUE; + } + + gsm_util_init_error (fatal, "%s", message); + gsm_exported_manager_complete_initialization_error (skeleton, invocation); + + return TRUE; + } + + static void + user_logout (GsmManager *manager, + GsmManagerLogoutMode mode) + { + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) { + manager->priv->logout_mode = mode; + end_session_or_show_shell_dialog (manager); + return; + } + + request_logout (manager, mode); + } + + gboolean +-gsm_manager_save_session (GsmManager *manager, +- GError **error) ++gsm_manager_save_session (GsmManager *manager, ++ DBusGMethodInvocation *context) + { + ClientEndSessionData data; ++ GError *error; + + g_debug ("GsmManager: SaveSession called"); + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { +- g_set_error (error, +- GSM_MANAGER_ERROR, +- GSM_MANAGER_ERROR_NOT_IN_RUNNING, +- "SaveSession interface is only available during the Running phase"); ++ error = g_error_new (GSM_MANAGER_ERROR, ++ GSM_MANAGER_ERROR_NOT_IN_RUNNING, ++ "SaveSession interface is only available during the Running phase"); ++ dbus_g_method_return_error (context, error); ++ g_error_free (error); + return FALSE; + } + + data.manager = manager; + data.flags = 0; + gsm_store_foreach (manager->priv->clients, + (GsmStoreFunc)_client_request_save_helper, + &data); + + if (manager->priv->query_clients) { + manager->priv->query_timeout_id = g_timeout_add_seconds (GSM_MANAGER_SAVE_SESSION_TIMEOUT, + (GSourceFunc)_on_query_save_session_timeout, + manager); ++ ++ manager->priv->pending_save_invocations = g_slist_prepend (manager->priv->pending_save_invocations, ++ context); ++ + return TRUE; + } else { + g_debug ("GsmManager: Nothing to save"); +- return FALSE; ++ dbus_g_method_return (context); ++ return TRUE; + } ++ ++ return TRUE; + } + + gboolean + gsm_manager_logout (GsmManager *manager, + guint logout_mode, + GError **error) + { + if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_RUNNING, + "Logout interface is only available after the Running phase starts"); + return FALSE; + } + + if (_log_out_is_locked_down (manager)) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_LOCKED_DOWN, + "Logout has been locked down"); + return FALSE; + } + + switch (logout_mode) { + case GSM_MANAGER_LOGOUT_MODE_NORMAL: + case GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION: + case GSM_MANAGER_LOGOUT_MODE_FORCE: + user_logout (manager, logout_mode); + break; + +diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h +index 4d14aa34..a8de58de 100644 +--- a/gnome-session/gsm-manager.h ++++ b/gnome-session/gsm-manager.h +@@ -97,42 +97,42 @@ GType gsm_manager_get_type (void); + + GsmManager * gsm_manager_new (GsmStore *client_store, + gboolean failsafe); + GsmManager * gsm_manager_get (void); + + gboolean gsm_manager_get_failsafe (GsmManager *manager); + + gboolean gsm_manager_add_autostart_app (GsmManager *manager, + const char *path, + const char *provides); + gboolean gsm_manager_add_required_app (GsmManager *manager, + const char *path, + const char *provides); + gboolean gsm_manager_add_autostart_apps_from_dir (GsmManager *manager, + const char *path); + gboolean gsm_manager_add_legacy_session_apps (GsmManager *manager, + const char *path); + + void gsm_manager_start (GsmManager *manager); + + const char * _gsm_manager_get_default_session (GsmManager *manager); + + void _gsm_manager_set_active_session (GsmManager *manager, + const char *session_name, + gboolean is_fallback); + + void _gsm_manager_set_renderer (GsmManager *manager, + const char *renderer); + + gboolean gsm_manager_save_session (GsmManager *manager, +- GError **error); ++ DBusGMethodInvocation *context); + + gboolean gsm_manager_logout (GsmManager *manager, + guint logout_mode, + GError **error); + + gboolean gsm_manager_set_phase (GsmManager *manager, + GsmManagerPhase phase); + + G_END_DECLS + + #endif /* __GSM_MANAGER_H */ +diff --git a/gnome-session/org.gnome.SessionManager.xml b/gnome-session/org.gnome.SessionManager.xml +index 29eb0990..ac73adc9 100644 +--- a/gnome-session/org.gnome.SessionManager.xml ++++ b/gnome-session/org.gnome.SessionManager.xml +@@ -256,60 +256,61 @@ + + + True if condition is handled, false otherwise + + + + + Allows the caller to determine whether the session manager is + handling changes to the specified autostart condition. + + + + + + + + Request a shutdown dialog. + + + + + + + + Request a reboot dialog. + + + + + ++ + + + Request to save session + + + + + + + + True if shutdown is available to the user, false otherwise + + + + + Allows the caller to determine whether or not it's okay to show + a shutdown option in the UI + + + + + + + + The type of logout that is being requested + + + + + Request a logout dialog +-- +2.14.2 + diff --git a/SOURCES/0013-manager-save-session-type-in-session-dir.patch b/SOURCES/0013-manager-save-session-type-in-session-dir.patch new file mode 100644 index 0000000..1159639 --- /dev/null +++ b/SOURCES/0013-manager-save-session-type-in-session-dir.patch @@ -0,0 +1,662 @@ +From b92add119aa5b9813556db26477170dd39eca5b6 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Dec 2013 15:32:04 -0500 +Subject: [PATCH 13/19] manager: save session type in session dir + +If a user saved their session when in classic mode, make sure we +record that information so subsequent calls to gnome-session will +restore classic mode. +--- + gnome-session/gsm-manager.c | 21 ++++++++++++++++++-- + gnome-session/gsm-manager.h | 1 + + gnome-session/gsm-session-save.c | 41 +++++++++++++++++++++++++++++++++++++--- + gnome-session/gsm-session-save.h | 5 +++-- + gnome-session/main.c | 11 ++++++++++- + 5 files changed, 71 insertions(+), 8 deletions(-) + +diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c +index 6630aab8..135392fd 100644 +--- a/gnome-session/gsm-manager.c ++++ b/gnome-session/gsm-manager.c +@@ -1260,61 +1260,61 @@ finish_pending_save_invocations (GsmManager *manager) + g_slist_free (manager->priv->pending_save_invocations); + manager->priv->pending_save_invocations = NULL; + } + + static void + query_save_session_complete (GsmManager *manager) + { + GError *error = NULL; + + if (g_slist_length (manager->priv->next_query_clients) > 0) { + ClientEndSessionData data; + + data.manager = manager; + data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST; + + g_slist_foreach (manager->priv->next_query_clients, + (GFunc)_client_request_save, + &data); + + g_slist_free (manager->priv->next_query_clients); + manager->priv->next_query_clients = NULL; + + return; + } + + if (manager->priv->query_timeout_id > 0) { + g_source_remove (manager->priv->query_timeout_id); + manager->priv->query_timeout_id = 0; + } + +- gsm_session_save (manager->priv->clients, &error); ++ gsm_session_save (manager->priv->clients, manager->priv->session_name, &error); + + if (error) { + g_warning ("Error saving session: %s", error->message); + fail_pending_save_invocations (manager, error); + g_error_free (error); + } else { + finish_pending_save_invocations (manager); + } + } + + static guint32 + generate_cookie (void) + { + guint32 cookie; + + cookie = (guint32)g_random_int_range (1, G_MAXINT32); + + return cookie; + } + + static guint32 + _generate_unique_cookie (GsmManager *manager) + { + guint32 cookie; + + do { + cookie = generate_cookie (); + } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL); + + return cookie; +@@ -1528,60 +1528,77 @@ debug_app_summary (GsmManager *manager) + + g_debug ("GsmManager: App startup summary"); + for (phase = GSM_MANAGER_PHASE_EARLY_INITIALIZATION; phase < GSM_MANAGER_PHASE_RUNNING; phase++) { + g_debug ("GsmManager: Phase %s", phase_num_to_name (phase)); + gsm_store_foreach (manager->priv->apps, + (GsmStoreFunc)_debug_app_for_phase, + GUINT_TO_POINTER (phase)); + } + } + + void + gsm_manager_start (GsmManager *manager) + { + g_debug ("GsmManager: GSM starting to manage"); + + g_return_if_fail (GSM_IS_MANAGER (manager)); + + gsm_xsmp_server_start (manager->priv->xsmp_server); + gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_EARLY_INITIALIZATION); + debug_app_summary (manager); + start_phase (manager); + } + + const char * + _gsm_manager_get_default_session (GsmManager *manager) + { + return g_settings_get_string (manager->priv->session_settings, + KEY_SESSION_NAME); + } + ++char * ++_gsm_manager_get_saved_session (GsmManager *manager) ++{ ++ char *file; ++ char *type; ++ gboolean loaded; ++ ++ file = g_build_filename (gsm_util_get_saved_session_dir (), "type", NULL); ++ loaded = g_file_get_contents (file, &type, NULL, NULL); ++ g_free (file); ++ ++ if (!loaded) ++ return NULL; ++ ++ return type; ++} ++ + void + _gsm_manager_set_active_session (GsmManager *manager, + const char *session_name, + gboolean is_fallback) + { + g_free (manager->priv->session_name); + manager->priv->session_name = g_strdup (session_name); + manager->priv->is_fallback_session = is_fallback; + + gsm_exported_manager_set_session_name (manager->priv->skeleton, session_name); + } + + void + _gsm_manager_set_renderer (GsmManager *manager, + const char *renderer) + { + gsm_exported_manager_set_renderer (manager->priv->skeleton, renderer); + } + + static gboolean + _app_has_app_id (const char *id, + GsmApp *app, + const char *app_id_a) + { + const char *app_id_b; + + app_id_b = gsm_app_peek_app_id (app); + return (app_id_b != NULL && strcmp (app_id_a, app_id_b) == 0); + } + +@@ -1946,61 +1963,61 @@ on_xsmp_client_register_confirmed (GsmXSMPClient *client, + } + } + + static gboolean + auto_save_is_enabled (GsmManager *manager) + { + return g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE_ONE_SHOT) + || g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE); + } + + static void + maybe_save_session (GsmManager *manager) + { + GError *error; + + if (gsm_system_is_login_session (manager->priv->system)) + return; + + /* We only allow session saving when session is running or when + * logging out */ + if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING && + manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) { + return; + } + + if (!auto_save_is_enabled (manager)) { + return; + } + + error = NULL; +- gsm_session_save (manager->priv->clients, &error); ++ gsm_session_save (manager->priv->clients, manager->priv->session_name, &error); + + if (error) { + g_warning ("Error saving session: %s", error->message); + g_error_free (error); + } + } + + static void + _handle_client_end_session_response (GsmManager *manager, + GsmClient *client, + gboolean is_ok, + gboolean do_last, + gboolean cancel, + const char *reason) + { + /* just ignore if we are not yet running */ + if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) { + return; + } + + g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :""); + + if (manager->priv->phase == GSM_MANAGER_PHASE_RUNNING) { + /* Ignore responses when no requests were sent */ + if (manager->priv->query_clients == NULL) { + return; + } + + manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client); + +diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h +index a8de58de..fcf36019 100644 +--- a/gnome-session/gsm-manager.h ++++ b/gnome-session/gsm-manager.h +@@ -88,51 +88,52 @@ typedef enum + GSM_MANAGER_ERROR_INVALID_OPTION, + GSM_MANAGER_ERROR_LOCKED_DOWN, + GSM_MANAGER_NUM_ERRORS + } GsmManagerError; + + #define GSM_MANAGER_ERROR gsm_manager_error_quark () + GQuark gsm_manager_error_quark (void); + + GType gsm_manager_get_type (void); + + GsmManager * gsm_manager_new (GsmStore *client_store, + gboolean failsafe); + GsmManager * gsm_manager_get (void); + + gboolean gsm_manager_get_failsafe (GsmManager *manager); + + gboolean gsm_manager_add_autostart_app (GsmManager *manager, + const char *path, + const char *provides); + gboolean gsm_manager_add_required_app (GsmManager *manager, + const char *path, + const char *provides); + gboolean gsm_manager_add_autostart_apps_from_dir (GsmManager *manager, + const char *path); + gboolean gsm_manager_add_legacy_session_apps (GsmManager *manager, + const char *path); + + void gsm_manager_start (GsmManager *manager); + + const char * _gsm_manager_get_default_session (GsmManager *manager); ++char * _gsm_manager_get_saved_session (GsmManager *manager); + + void _gsm_manager_set_active_session (GsmManager *manager, + const char *session_name, + gboolean is_fallback); + + void _gsm_manager_set_renderer (GsmManager *manager, + const char *renderer); + + gboolean gsm_manager_save_session (GsmManager *manager, + DBusGMethodInvocation *context); + + gboolean gsm_manager_logout (GsmManager *manager, + guint logout_mode, + GError **error); + + gboolean gsm_manager_set_phase (GsmManager *manager, + GsmManagerPhase phase); + + G_END_DECLS + + #endif /* __GSM_MANAGER_H */ +diff --git a/gnome-session/gsm-session-save.c b/gnome-session/gsm-session-save.c +index 66914b57..78b64197 100644 +--- a/gnome-session/gsm-session-save.c ++++ b/gnome-session/gsm-session-save.c +@@ -1,73 +1,105 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * gsm-session-save.c + * Copyright (C) 2008 Lucas Rocha. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + + #include + ++#include ++ + #include + #include + #include + + #include "gsm-util.h" + #include "gsm-autostart-app.h" + #include "gsm-client.h" + + #include "gsm-session-save.h" + + #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" + #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" + + + static gboolean gsm_session_clear_saved_session (const char *directory, + GHashTable *discard_hash); + + typedef struct { + char *dir; + GHashTable *discard_hash; + GError **error; + } SessionSaveData; + ++static void ++clear_session_type (const char *save_dir) ++{ ++ char *file; ++ ++ file = g_build_filename (save_dir, "type", NULL); ++ ++ g_unlink (file); ++ ++ g_free (file); ++} ++ ++static void ++set_session_type (const char *save_dir, ++ const char *type) ++{ ++ char *file; ++ GError *error; ++ ++ file = g_build_filename (save_dir, "type", NULL); ++ ++ error = NULL; ++ g_file_set_contents (file, type, strlen (type), &error); ++ if (error != NULL) ++ g_warning ("couldn't save session type to %s: %s", ++ type, error->message); ++ ++ g_free (file); ++} ++ + static gboolean + save_one_client (char *id, + GObject *object, + SessionSaveData *data) + { + GsmClient *client; + GKeyFile *keyfile; + const char *app_id; + char *path = NULL; + char *filename = NULL; + char *contents = NULL; + gsize length = 0; + char *discard_exec; + GError *local_error; + + client = GSM_CLIENT (object); + + local_error = NULL; + + keyfile = gsm_client_save (client, &local_error); + + if (keyfile == NULL || local_error) { + goto out; + } + + contents = g_key_file_to_data (keyfile, &length, &local_error); + + if (local_error) { + goto out; + } +@@ -107,112 +139,114 @@ save_one_client (char *id, + GSM_AUTOSTART_APP_DISCARD_KEY, + NULL); + if (discard_exec) { + g_hash_table_insert (data->discard_hash, + discard_exec, discard_exec); + } + + g_debug ("GsmSessionSave: saved client %s to %s", id, filename); + + out: + if (keyfile != NULL) { + g_key_file_free (keyfile); + } + + g_free (contents); + g_free (filename); + g_free (path); + + /* in case of any error, stop saving session */ + if (local_error) { + g_propagate_error (data->error, local_error); + g_error_free (local_error); + + return TRUE; + } + + return FALSE; + } + + void +-gsm_session_save (GsmStore *client_store, +- GError **error) ++gsm_session_save (GsmStore *client_store, ++ const char *type, ++ GError **error) + { + GSettings *settings; + const char *save_dir; + char *tmp_dir; + SessionSaveData data; + + g_debug ("GsmSessionSave: Saving session"); + + /* Clear one shot key autosave in the event its set (so that it's actually + * one shot only) + */ + settings = g_settings_new (GSM_MANAGER_SCHEMA); + g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE); + g_object_unref (settings); + + save_dir = gsm_util_get_saved_session_dir (); + if (save_dir == NULL) { + g_warning ("GsmSessionSave: cannot create saved session directory"); + return; + } + + tmp_dir = gsm_util_get_empty_tmp_session_dir (); + if (tmp_dir == NULL) { + g_warning ("GsmSessionSave: cannot create new saved session directory"); + return; + } + + /* save the session in a temp directory, and remember the discard + * commands */ + data.dir = tmp_dir; + data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + data.error = error; + + gsm_store_foreach (client_store, + (GsmStoreFunc) save_one_client, + &data); + + if (!*error) { + char *session_dir; + + if (g_file_test (save_dir, G_FILE_TEST_IS_SYMLINK)) + session_dir = g_file_read_link (save_dir, error); + else + session_dir = g_strdup (save_dir); + + if (session_dir != NULL) { +- + char *absolute_session_dir; + ++ set_session_type (tmp_dir, type); ++ + if (g_path_is_absolute (session_dir)) { + absolute_session_dir = g_strdup (session_dir); + } else { + char *parent_dir; + + parent_dir = g_path_get_dirname (save_dir); + absolute_session_dir = g_build_filename (parent_dir, session_dir, NULL); + g_free (parent_dir); + } + g_free (session_dir); + + /* remove the old saved session */ + gsm_session_clear_saved_session (absolute_session_dir, data.discard_hash); + + if (g_file_test (absolute_session_dir, G_FILE_TEST_IS_DIR)) + g_rmdir (absolute_session_dir); + g_rename (tmp_dir, absolute_session_dir); + + g_free (absolute_session_dir); + } + } else { + g_warning ("GsmSessionSave: error saving session: %s", (*error)->message); + /* FIXME: we should create a hash table filled with the discard + * commands that are in desktop files from save_dir. */ + gsm_session_clear_saved_session (tmp_dir, NULL); + g_rmdir (tmp_dir); + } + + g_hash_table_destroy (data.discard_hash); + g_free (tmp_dir); +@@ -294,31 +328,32 @@ gsm_session_clear_saved_session (const char *directory, + + while ((filename = g_dir_read_name (dir))) { + char *path = g_build_filename (directory, + filename, NULL); + + result = gsm_session_clear_one_client (path, discard_hash) + && result; + + g_free (path); + } + + g_dir_close (dir); + + return result; + } + + void + gsm_session_save_clear (void) + { + const char *save_dir; + + g_debug ("GsmSessionSave: Clearing saved session"); + + save_dir = gsm_util_get_saved_session_dir (); + if (save_dir == NULL) { + g_warning ("GsmSessionSave: cannot create saved session directory"); + return; + } + + gsm_session_clear_saved_session (save_dir, NULL); ++ clear_session_type (save_dir); + } +diff --git a/gnome-session/gsm-session-save.h b/gnome-session/gsm-session-save.h +index e623260f..c91b5615 100644 +--- a/gnome-session/gsm-session-save.h ++++ b/gnome-session/gsm-session-save.h +@@ -1,33 +1,34 @@ + /* gsm-session-save.h + * Copyright (C) 2008 Lucas Rocha. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + + #ifndef __GSM_SESSION_SAVE_H__ + #define __GSM_SESSION_SAVE_H__ + + #include + + #include "gsm-store.h" + + G_BEGIN_DECLS + +-void gsm_session_save (GsmStore *client_store, +- GError **error); ++void gsm_session_save (GsmStore *client_store, ++ const char *type, ++ GError **error); + void gsm_session_save_clear (void); + + G_END_DECLS + + #endif /* __GSM_SESSION_SAVE_H__ */ +diff --git a/gnome-session/main.c b/gnome-session/main.c +index e2c3efef..6e697678 100644 +--- a/gnome-session/main.c ++++ b/gnome-session/main.c +@@ -119,61 +119,70 @@ sigusr2_cb (gpointer data) + static gboolean + sigusr1_cb (gpointer data) + { + gdm_log_toggle_debug (); + return TRUE; + } + + static void + on_name_acquired (GDBusConnection *connection, + const char *name, + gpointer data) + { + gsm_manager_start (manager); + } + + static void + create_manager (void) + { + GsmStore *client_store; + + client_store = gsm_store_new (); + manager = gsm_manager_new (client_store, failsafe); + g_object_unref (client_store); + + g_unix_signal_add (SIGTERM, term_or_int_signal_cb, manager); + g_unix_signal_add (SIGINT, term_or_int_signal_cb, manager); + g_unix_signal_add (SIGUSR1, sigusr1_cb, manager); + g_unix_signal_add (SIGUSR2, sigusr2_cb, manager); + + if (IS_STRING_EMPTY (session_name)) { +- session_name = _gsm_manager_get_default_session (manager); ++ char *saved_session_name; ++ ++ saved_session_name = _gsm_manager_get_saved_session (manager); ++ ++ if (IS_STRING_EMPTY (saved_session_name)) ++ session_name = _gsm_manager_get_default_session (manager); ++ else ++ session_name = g_steal_pointer (&saved_session_name); ++ ++ g_free (saved_session_name); + } + + if (!gsm_session_fill (manager, session_name)) { + gsm_fail_whale_dialog_we_failed (FALSE, TRUE, NULL); + } + + _gsm_manager_set_renderer (manager, gl_renderer); + } + + static void + on_bus_acquired (GDBusConnection *connection, + const char *name, + gpointer data) + { + create_manager (); + } + + static guint + acquire_name (void) + { + return g_bus_own_name (G_BUS_TYPE_SESSION, + GSM_DBUS_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, NULL); + } + + static gboolean +-- +2.14.2 + diff --git a/SOURCES/0014-session-selector-restore-saved-session-mode.patch b/SOURCES/0014-session-selector-restore-saved-session-mode.patch new file mode 100644 index 0000000..2519a00 --- /dev/null +++ b/SOURCES/0014-session-selector-restore-saved-session-mode.patch @@ -0,0 +1,42 @@ +From 2a15240b8653b8fe0f0c1e2804d7c2fe8452a604 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 7 Jan 2014 21:16:23 -0500 +Subject: [PATCH 14/19] session-selector: restore saved session mode + +When using the custom session selector, we need to know +whether to use classic mode or not. + +This commit makes us use whatever mode was saved with the +session. +--- + tools/gnome-session-custom-session | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +diff --git a/tools/gnome-session-custom-session b/tools/gnome-session-custom-session +index 07fdb0cc..358aee01 100644 +--- a/tools/gnome-session-custom-session ++++ b/tools/gnome-session-custom-session +@@ -1,4 +1,19 @@ + #! /bin/sh + + gnome-session-selector +-exec gnome-session ++ ++type_file="${XDG_CONFIG_HOME:-$HOME/.config}/gnome-session/saved-session/type" ++ ++session_type="" ++if [ -e "$type_file" ]; then ++ read session_type < "$type_file" ++fi ++ ++session_type_argument="" ++[ -n "$session_type" ] && session_type_argument="--session=$session_type" ++ ++if [ "$session_type" = "gnome-classic" ]; then ++ export GNOME_SHELL_SESSION_MODE="classic" ++fi ++ ++exec gnome-session "$session_type_argument" +-- +2.14.2 + diff --git a/SOURCES/0015-session-selector-refresh-from-recent-glade.patch b/SOURCES/0015-session-selector-refresh-from-recent-glade.patch new file mode 100644 index 0000000..2b592ba --- /dev/null +++ b/SOURCES/0015-session-selector-refresh-from-recent-glade.patch @@ -0,0 +1,269 @@ +From 3e35abd4c7d0aae97d868db6e66706c2ccdeeb11 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Dec 2013 10:53:33 -0500 +Subject: [PATCH 15/19] session-selector: refresh from recent glade + +The ui file is rather old. This commit just opens it up in a recent +glade and resaves it, so we have a fresh starting point to make +changes. +--- + data/session-selector.ui | 80 ++++++++++++++++++++++++++++++++---------------- + 1 file changed, 54 insertions(+), 26 deletions(-) + +diff --git a/data/session-selector.ui b/data/session-selector.ui +index 1534a746..4d1e3009 100644 +--- a/data/session-selector.ui ++++ b/data/session-selector.ui +@@ -1,195 +1,223 @@ +- ++ + +- +- +- +- +- +- +- +- +- +- session-store +- ++ + ++ False + Custom Session + center + 500 + 310 + False + + + True ++ False + 0.5 + out + + + True ++ False + 12 + + + True +- vertical ++ False + 6 +- + + + True +- other +- ++ False ++ other + +- ++ + True ++ False + vertical +- 0 + + + True +- 0.0 +- 0.5 ++ False ++ 0 + Please select a custom session to use + + +- True +- True ++ False ++ False + 0 + + + ++ ++ False ++ False ++ 0 ++ ++ ++ ++ ++ False ++ ++ ++ False ++ True ++ 1 ++ + + + + False + True + 0 + + + + + True +- vertical ++ False + 12 + + + True ++ False + 12 + + + True + True + never +- automatic + in + + + True + True ++ sort-model + False + 0 +- sort-model ++ ++ ++ + + + + ++ True ++ True + 0 + + + + + True +- vertical ++ False + 6 + start + + + _New Session + True + True + True + True + + + False + False + 0 + + + + + _Remove Session + True + True + True + True + + + False + False + 1 + + + + + Rena_me Session + True + True + True + True + + + False + False + 2 + + + + + False ++ True + 1 + + + + ++ True ++ True + 1 + + + + ++ True ++ True + 1 + + + + + True ++ False + 6 + end + + + _Continue + True + True + True + True + True + True + + + False + False + 0 + + + + + False ++ True + 2 + + + + + + + + + ++ ++ ++ ++ ++ ++ ++ ++ session-store ++ + +-- +2.14.2 + diff --git a/SOURCES/0016-session-selector-add-toggle-for-classic-normal-selec.patch b/SOURCES/0016-session-selector-add-toggle-for-classic-normal-selec.patch new file mode 100644 index 0000000..b6b80cf --- /dev/null +++ b/SOURCES/0016-session-selector-add-toggle-for-classic-normal-selec.patch @@ -0,0 +1,604 @@ +From f0f0898da1c1e28d4ad6d6c17ba559e59780bd21 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 7 Jan 2014 21:02:02 -0500 +Subject: [PATCH 16/19] session-selector: add toggle for classic/normal + selection + +Since we offer both classic mode and regular mode when +not using the session selector, we should also offer it +when using the session selector. +--- + data/session-selector.ui | 39 ++++++++++++++- + tools/gnome-session-selector.c | 106 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 143 insertions(+), 2 deletions(-) + +diff --git a/data/session-selector.ui b/data/session-selector.ui +index 4d1e3009..beab73a1 100644 +--- a/data/session-selector.ui ++++ b/data/session-selector.ui +@@ -153,71 +153,106 @@ + False + 2 + + + + + False + True + 1 + + + + + True + True + 1 + + + + + True + True + 1 + + + + + True + False + 6 +- end ++ ++ ++ True ++ False ++ 6 ++ ++ ++ True ++ False ++ Classic Experience ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ True ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ False ++ True ++ 0 ++ ++ + + + _Continue + True + True + True + True + True + True + + + False + False +- 0 ++ 1 ++ True + + + + + False + True + 2 + + + + + + + + + + + + + + + + + session-store + + +diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c +index 53822f6c..a7361a5b 100644 +--- a/tools/gnome-session-selector.c ++++ b/tools/gnome-session-selector.c +@@ -16,60 +16,61 @@ + * along with this program; if not, see . + * + * Written by: Matthias Clasen + */ + + #include "config.h" + + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #include + #include + + #include + #include + + #define GSM_SERVICE_DBUS "org.gnome.SessionManager" + #define GSM_PATH_DBUS "/org/gnome/SessionManager" + #define GSM_INTERFACE_DBUS "org.gnome.SessionManager" + + #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" + #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" ++#define DEFAULT_SESSION_NAME "gnome" + + static GtkBuilder *builder; + static GtkWidget *session_list; + static GtkListStore *store; + static GtkTreeModelSort *sort_model; + static char *info_text; + + static void select_session (const char *name); + static gboolean make_session_current (const char *name); + + static char * + get_session_path (const char *name) + { + return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); + } + + static char * + find_new_session_name (void) + { + char *name; + char *path; + int i; + + for (i = 1; i < 20; i++) { + name = g_strdup_printf (_("Session %d"), i); + path = get_session_path (name); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + g_free (path); + return name; + } +@@ -125,104 +126,126 @@ is_valid_session_name (const char *name) + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); + do { + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &n, -1); + if (strcmp (n, name) == 0) { + char *message; + message = g_strdup_printf (_("A session named “%s” already exists"), name); + warning_text = g_strdup_printf ("%s\nNote: %s", info_text, message); + g_free (message); + g_free (n); + break; + } + g_free (n); + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); + + info_bar = (GtkWidget *) gtk_builder_get_object (builder, "info-bar"); + label = (GtkWidget*) gtk_builder_get_object (builder, "info-label"); + + if (warning_text != NULL) { + gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING); + gtk_label_set_markup (GTK_LABEL (label), warning_text); + g_free (warning_text); + return FALSE; + } + + gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_OTHER); + gtk_label_set_markup (GTK_LABEL (label), info_text); + + return TRUE; + } + ++static char * ++get_session_type_from_file (const char *name) ++{ ++ char *file; ++ char *type; ++ gboolean loaded; ++ ++ file = g_build_filename (g_get_user_config_dir (), "gnome-session", name, "type", NULL); ++ loaded = g_file_get_contents (file, &type, NULL, NULL); ++ g_free (file); ++ ++ if (!loaded) ++ return g_strdup (DEFAULT_SESSION_NAME); ++ ++ return type; ++} ++ + static void + populate_session_list (GtkWidget *session_list) + { + GtkTreeIter iter; + char *path; + const char *name; + GDir *dir; + GError *error; + char *saved_session; + char *default_session; + char *default_name; + char last_session[PATH_MAX] = ""; + + saved_session = get_session_path ("saved-session"); + + if (!g_file_test (saved_session, G_FILE_TEST_IS_SYMLINK)) { + default_name = find_new_session_name (); + default_session = get_session_path (default_name); + rename (saved_session, default_session); + if (symlink (default_name, saved_session) < 0) + g_warning ("Failed to convert saved-session to symlink"); + g_free (default_name); + g_free (default_session); + } + + path = g_build_filename (g_get_user_config_dir (), "gnome-session", NULL); + error = NULL; + dir = g_dir_open (path, 0, &error); + if (dir == NULL) { + g_warning ("Failed to open %s: %s", path, error->message); + g_error_free (error); + goto out; + } + + default_name = NULL; + if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) { + default_name = g_path_get_basename (last_session); + } + + while ((name = g_dir_read_name (dir)) != NULL) { ++ char *session_type; ++ + if (strcmp (name, "saved-session") == 0) + continue; + ++ session_type = get_session_type_from_file (name); ++ + gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1); ++ g_free (session_type); + + if (g_strcmp0 (default_name, name) == 0) { + GtkTreeSelection *selection; + GtkTreeIter child_iter; + + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &child_iter, &iter); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + gtk_tree_selection_select_iter (selection, &child_iter); + } + } + + g_free (default_name); + g_dir_close (dir); + + out: + g_free (saved_session); + g_free (path); + } + + static char * + get_last_session (void) + { + char *saved_session; + char last_session[PATH_MAX] = ""; + char *name = NULL; + + saved_session = get_session_path ("saved-session"); + + if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) { + name = g_path_get_basename (last_session); +@@ -260,60 +283,136 @@ remove_session (const char *name) + GError *error; + + path1 = get_session_path ("saved-session"); + path2 = get_session_path (name); + + error = NULL; + n = g_file_read_link (path1, &error); + if (n == NULL) { + g_warning ("Failed to read link: %s", error->message); + g_error_free (error); + } + else if (strcmp (n, name) == 0) { + unlink (path1); + } + g_free (n); + + dir = g_dir_open (path2, 0, NULL); + while ((d = g_dir_read_name (dir)) != NULL) { + path = g_build_filename (path2, d, NULL); + unlink (path); + g_free (path); + } + g_dir_close (dir); + + remove (path2); + + g_free (path1); + g_free (path2); + } + ++static const char * ++get_session_type_from_switch (void) ++{ ++ GtkWidget *mode_switch; ++ gboolean is_classic_mode; ++ ++ mode_switch = (GtkWidget *)gtk_builder_get_object (builder, "classic-mode-switch"); ++ ++ is_classic_mode = gtk_switch_get_active (GTK_SWITCH (mode_switch)); ++ ++ if (is_classic_mode) { ++ return "gnome-classic"; ++ } else { ++ return "gnome"; ++ } ++} ++ ++static void ++set_mode_switch_from_session_type_file (const char *name) ++{ ++ GtkWidget *mode_switch; ++ gboolean is_classic_mode = FALSE; ++ char *type; ++ ++ mode_switch = (GtkWidget *)gtk_builder_get_object (builder, "classic-mode-switch"); ++ ++ type = get_session_type_from_file (name); ++ is_classic_mode = strcmp (type, "gnome-classic") == 0; ++ g_free (type); ++ ++ gtk_switch_set_active (GTK_SWITCH (mode_switch), is_classic_mode); ++} ++ ++static void ++save_session_type (const char *save_dir, ++ const char *type) ++{ ++ char *file; ++ GError *error; ++ ++ file = g_build_filename (save_dir, "type", NULL); ++ ++ error = NULL; ++ g_file_set_contents (file, type, strlen (type), &error); ++ if (error != NULL) ++ g_warning ("couldn't save session type to %s: %s", ++ type, error->message); ++ ++ g_free (file); ++} ++ ++static void ++save_session_type_from_switch (void) ++{ ++ char *name, *path; ++ const char *session_type; ++ ++ name = get_selected_session (); ++ ++ if (name == NULL) { ++ return; ++ } ++ ++ path = get_session_path (name); ++ g_free (name); ++ ++ session_type = get_session_type_from_switch (); ++ save_session_type (path, session_type); ++} ++ ++static void ++on_mode_switched (GtkSwitch *mode_switch) ++{ ++ save_session_type_from_switch (); ++} ++ + static gboolean + make_session_current (const char *name) + { + char *path1; + gboolean ret = TRUE; + + path1 = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", NULL); + + unlink (path1); + if (symlink (name, path1) < 0) { + g_warning ("Failed to make session '%s' current", name); + ret = FALSE; + } + + g_free (path1); + + return ret; + } + + static void + on_remove_session_clicked (GtkButton *button, + gpointer data) + { + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + char *name; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { +@@ -492,60 +591,62 @@ static void + create_session_and_begin_rename (void) + { + gchar *name; + + name = find_new_session_name (); + create_session (name); + select_session (name); + + begin_rename (); + } + + static void + on_new_session_clicked (GtkButton *button, + gpointer data) + { + create_session_and_begin_rename (); + } + + static void + on_selection_changed (GtkTreeSelection *selection, + gpointer data) + { + char *name; + + name = get_selected_session (); + + if (name == NULL) { + return; + } + ++ set_mode_switch_from_session_type_file (name); ++ + g_free (name); + } + + static void + update_remove_button (void) + { + GtkWidget *button; + + button = (GtkWidget *)gtk_builder_get_object (builder, "remove-session"); + if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) > 1) { + gtk_widget_set_sensitive (button, TRUE); + } else { + gtk_widget_set_sensitive (button, FALSE); + } + } + + static void + on_row_edited (GtkCellRendererText *cell, + const char *path_string, + const char *new_name, + gpointer data) + { + GtkTreePath *path; + GtkTreeIter sort_iter, items_iter; + char *old_name; + gboolean was_renamed; + + path = gtk_tree_path_new_from_string (path_string); + gtk_tree_model_get_iter (GTK_TREE_MODEL (sort_model), &sort_iter, path); + +@@ -751,75 +852,80 @@ main (int argc, char *argv[]) + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_ASCENDING); + g_signal_connect (store, "row-deleted", G_CALLBACK (on_row_deleted), NULL); + g_signal_connect (store, "row-inserted", G_CALLBACK (on_row_inserted), NULL); + session_list = (GtkWidget *) gtk_builder_get_object (builder, "session-list"); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + populate_session_list (session_list); + + cell = gtk_cell_renderer_text_new (); + g_signal_connect (cell, "edited", G_CALLBACK (on_row_edited), NULL); + + column = gtk_tree_view_column_new_with_attributes ("", cell, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (session_list), GTK_TREE_VIEW_COLUMN (column)); + + g_signal_connect (session_list, "row-activated", G_CALLBACK (on_row_activated), NULL); + + g_signal_connect (selection, "changed", + G_CALLBACK (on_selection_changed), NULL); + + widget = (GtkWidget *) gtk_builder_get_object (builder, "new-session"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_new_session_clicked), NULL); + widget = (GtkWidget *) gtk_builder_get_object (builder, "remove-session"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_remove_session_clicked), NULL); + widget = (GtkWidget *) gtk_builder_get_object (builder, "rename-session"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_rename_session_clicked), NULL); + widget = (GtkWidget *) gtk_builder_get_object (builder, "continue-button"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_continue_clicked), NULL); ++ widget = (GtkWidget *) gtk_builder_get_object (builder, "classic-mode-switch"); ++ g_signal_connect (widget, "notify::active", G_CALLBACK (on_mode_switched), NULL); + + g_signal_connect (window, "map", G_CALLBACK (on_map), NULL); + gtk_widget_show (window); + + if (g_strcmp0 (action, "load") == 0) { + info_text = _("Please select a custom session to run"); + } else if (g_strcmp0 (action, "print") == 0) { + info_text = _("Please select a session to use"); + } else if (g_strcmp0 (action, "save") == 0) { + info_text = _("Please select a session to save to"); + } + + label = (GtkWidget*) gtk_builder_get_object (builder, "info-label"); + gtk_label_set_markup (GTK_LABEL (label), info_text); + + selected_session = get_selected_session (); + + if (selected_session == NULL) { + create_session_and_begin_rename (); + } else { ++ set_mode_switch_from_session_type_file (selected_session); + g_free (selected_session); + } + + gtk_main (); + + selected_session = get_selected_session (); + + if (g_strcmp0 (action, "load") == 0) { + make_session_current (selected_session); ++ save_session_type_from_switch (); + auto_save_next_session_if_needed (); + } else if (g_strcmp0 (action, "save") == 0) { + char *last_session; + + last_session = get_last_session (); + make_session_current (selected_session); + save_session (); ++ save_session_type_from_switch (); + if (last_session != NULL) + make_session_current (last_session); + } else if (g_strcmp0 (action, "print") == 0) { + g_print ("%s\n", selected_session); + } + g_free (selected_session); + + return 0; + } +-- +2.14.2 + diff --git a/SOURCES/0017-session-selector-use-classic-mode-by-default.patch b/SOURCES/0017-session-selector-use-classic-mode-by-default.patch new file mode 100644 index 0000000..2e33fa4 --- /dev/null +++ b/SOURCES/0017-session-selector-use-classic-mode-by-default.patch @@ -0,0 +1,79 @@ +From 12df4f92bff325fbffcaa4eb0ee0392511f6ebb6 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 8 Jan 2014 10:15:29 -0500 +Subject: [PATCH 17/19] session-selector: use classic mode by default + +--- + tools/gnome-session-selector.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c +index a7361a5b..a41cd260 100644 +--- a/tools/gnome-session-selector.c ++++ b/tools/gnome-session-selector.c +@@ -16,61 +16,61 @@ + * along with this program; if not, see . + * + * Written by: Matthias Clasen + */ + + #include "config.h" + + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #include + #include + + #include + #include + + #define GSM_SERVICE_DBUS "org.gnome.SessionManager" + #define GSM_PATH_DBUS "/org/gnome/SessionManager" + #define GSM_INTERFACE_DBUS "org.gnome.SessionManager" + + #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" + #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" +-#define DEFAULT_SESSION_NAME "gnome" ++#define DEFAULT_SESSION_NAME "gnome-classic" + + static GtkBuilder *builder; + static GtkWidget *session_list; + static GtkListStore *store; + static GtkTreeModelSort *sort_model; + static char *info_text; + + static void select_session (const char *name); + static gboolean make_session_current (const char *name); + + static char * + get_session_path (const char *name) + { + return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); + } + + static char * + find_new_session_name (void) + { + char *name; + char *path; + int i; + + for (i = 1; i < 20; i++) { + name = g_strdup_printf (_("Session %d"), i); + path = get_session_path (name); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + g_free (path); + return name; + } +-- +2.14.2 + diff --git a/SOURCES/0018-manager-port-away-from-dbus-glib-to-GDBus.patch b/SOURCES/0018-manager-port-away-from-dbus-glib-to-GDBus.patch new file mode 100644 index 0000000..a5f88d5 --- /dev/null +++ b/SOURCES/0018-manager-port-away-from-dbus-glib-to-GDBus.patch @@ -0,0 +1,756 @@ +From 916d9ba86cf2020ffedff331397ed02b5e53f030 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 8 Mar 2017 16:36:44 -0500 +Subject: [PATCH 18/19] manager: port away from dbus-glib to GDBus + +--- + capplet/gsm-properties-dialog.c | 52 +++++++++++++++++++++-------------------- + configure.ac | 2 +- + gnome-session/gsm-manager.c | 16 ++++++------- + gnome-session/gsm-manager.h | 3 ++- + tools/gnome-session-selector.c | 48 ++++++++++++++++++++----------------- + 5 files changed, 64 insertions(+), 57 deletions(-) + +diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c +index 51fa5106..04452c1a 100644 +--- a/capplet/gsm-properties-dialog.c ++++ b/capplet/gsm-properties-dialog.c +@@ -1,66 +1,66 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 2007 Vincent Untz. + * Copyright (C) 2008 Lucas Rocha. + * Copyright (C) 2008 William Jon McCann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + #include "config.h" + + #include + #include + #include + ++#include ++ + #include "gsm-properties-dialog.h" + #include "gsm-app-dialog.h" + #include "gsm-util.h" + #include "gsp-app.h" + #include "gsp-app-manager.h" +-#include +-#include + + #define GSM_SERVICE_DBUS "org.gnome.SessionManager" + #define GSM_PATH_DBUS "/org/gnome/SessionManager" + #define GSM_INTERFACE_DBUS "org.gnome.SessionManager" + + #define GSM_PROPERTIES_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogPrivate)) + + #define GTKBUILDER_FILE "session-properties.ui" + + #define CAPPLET_TREEVIEW_WIDGET_NAME "session_properties_treeview" + #define CAPPLET_ADD_WIDGET_NAME "session_properties_add_button" + #define CAPPLET_DELETE_WIDGET_NAME "session_properties_delete_button" + #define CAPPLET_EDIT_WIDGET_NAME "session_properties_edit_button" + #define CAPPLET_SAVE_WIDGET_NAME "session_properties_save_button" + #define CAPPLET_SESSION_SAVED_WIDGET_NAME "session_properties_session_saved_label" + #define CAPPLET_REMEMBER_WIDGET_NAME "session_properties_remember_toggle" + + #define STARTUP_APP_ICON "system-run" + + #define SPC_SETTINGS_SCHEMA "org.gnome.SessionManager" + #define SPC_SETTINGS_AUTOSAVE_KEY "auto-save-session" + + struct GsmPropertiesDialogPrivate + { + GtkBuilder *xml; + GtkListStore *list_store; + GtkTreeModel *tree_filter; + + GtkTreeView *treeview; + GtkWidget *add_button; +@@ -454,101 +454,103 @@ on_edit_app_clicked (GtkWidget *widget, + g_object_unref (app); + } + } + + static void + on_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GsmPropertiesDialog *dialog) + { + on_edit_app_clicked (NULL, dialog); + } + + static void + session_saved_message (GsmPropertiesDialog *dialog, + const char *msg, + gboolean is_error) + { + GtkLabel *label; + gchar *markup; + label = GTK_LABEL (gtk_builder_get_object (dialog->priv->xml, CAPPLET_SESSION_SAVED_WIDGET_NAME)); + if (is_error) + markup = g_markup_printf_escaped ("%s", msg); + else + markup = g_markup_escape_text (msg, -1); + gtk_label_set_markup (label, markup); + g_free (markup); + } + + static void +-session_saved_cb (DBusGProxy *proxy, +- DBusGProxyCall *call_id, +- void *user_data) ++session_saved_cb (GDBusConnection *conn, ++ GAsyncResult *result, ++ gpointer user_data) + { +- gboolean res; ++ GVariant *reply; + GsmPropertiesDialog *dialog = user_data; ++ GError *error = NULL; + +- res = dbus_g_proxy_end_call (proxy, call_id, NULL, G_TYPE_INVALID); +- if (res) ++ reply = g_dbus_connection_call_finish (conn, result, &error); ++ if (error == NULL) + session_saved_message (dialog, _("Your session has been saved."), FALSE); + else + session_saved_message (dialog, _("Failed to save session"), TRUE); + +- g_object_unref (proxy); ++ g_clear_error (&error); ++ ++ g_variant_unref (reply); + } + + static void + save_session_directly (GsmPropertiesDialog *dialog) + { +- DBusGConnection *conn; +- DBusGProxy *proxy; +- DBusGProxyCall *call; ++ GDBusConnection *conn; + +- conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); ++ conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + if (conn == NULL) { + session_saved_message (dialog, _("Could not connect to the session bus"), TRUE); + return; + } + +- proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS); +- if (proxy == NULL) { +- session_saved_message (dialog, _("Could not connect to the session manager"), TRUE); +- return; +- } +- +- call = dbus_g_proxy_begin_call (proxy, "SaveSession", session_saved_cb, dialog, NULL, G_TYPE_INVALID); +- if (call == NULL) { +- session_saved_message (dialog, _("Failed to save session"), TRUE); +- g_object_unref (proxy); +- return; +- } ++ g_dbus_connection_call (conn, ++ GSM_SERVICE_DBUS, ++ GSM_PATH_DBUS, ++ GSM_INTERFACE_DBUS, ++ "SaveSession", ++ NULL, ++ NULL, ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ NULL, ++ (GAsyncReadyCallback) ++ session_saved_cb, ++ dialog); + } + + static void + save_session_from_selector (GsmPropertiesDialog *dialog, + const char *program_path) + { + char *command_line = g_strdup_printf ("%s --action save", program_path); + + g_spawn_command_line_sync (command_line, NULL, NULL, NULL, NULL); + + g_free (command_line); + } + + static void + on_save_session_clicked (GtkWidget *widget, + GsmPropertiesDialog *dialog) + { + char *program_path; + + program_path = g_find_program_in_path ("gnome-session-selector"); + + if (program_path != NULL) { + save_session_from_selector (dialog, program_path); + g_free (program_path); + } else { + save_session_directly (dialog); + } + } + + static void +diff --git a/configure.ac b/configure.ac +index d0eeab8a..5182c09e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -21,61 +21,61 @@ LT_PREREQ([2.2.6]) + LT_INIT([dlopen disable-static]) + + GNOME_MAINTAINER_MODE_DEFINES + GNOME_COMPILE_WARNINGS([maximum]) + + AC_ARG_ENABLE(deprecation_flags, + [AS_HELP_STRING([--enable-deprecation-flags], + [use *_DISABLE_DEPRECATED flags @<:@default=no@:>@])],, + [enable_deprecation_flags=no]) + + if test "x$enable_deprecation_flags" = "xyes"; then + DISABLE_DEPRECATED_CFLAGS=$DISABLE_DEPRECATED + AC_SUBST([DISABLE_DEPRECATED_CFLAGS]) + fi + + GLIB_REQUIRED=2.46.0 + GTK3_REQUIRED=3.18.0 + DBUS_GLIB_REQUIRED=0.76 + UPOWER_REQUIRED=0.9.0 + JSON_GLIB_REQUIRED=0.10 + GNOME_DESKTOP_REQUIRED=3.18.0 + + AC_ARG_ENABLE(session-selector, AS_HELP_STRING([--enable-session-selector], + [enable building a custom session selector dialog]), + enable_session_selector=$enableval,enable_session_selector=no) + + AM_CONDITIONAL(BUILD_SESSION_SELECTOR, + [test "$enable_session_selector" = yes]) + + if test "$enable_session_selector" = yes; then +- PKG_CHECK_MODULES(SESSION_SELECTOR, gtk+-3.0 gio-2.0 dbus-glib-1 >= $DBUS_GLIB_REQUIRED) ++ PKG_CHECK_MODULES(SESSION_SELECTOR, gtk+-3.0 gio-2.0) + fi + + dnl ==================================================================== + dnl Dependency Checks + dnl ==================================================================== + + dnl Standard vertical stacks + PKG_CHECK_MODULES(GIO, gio-2.0) + PKG_CHECK_MODULES(GIOUNIX, gio-unix-2.0 >= $GLIB_REQUIRED) + PKG_CHECK_MODULES(GTK3, gtk+-3.0 >= $GTK3_REQUIRED) + + PKG_CHECK_MODULES(GNOME_SESSION, + glib-2.0 >= $GLIB_REQUIRED + gio-2.0 >= $GLIB_REQUIRED + json-glib-1.0 >= $JSON_GLIB_REQUIRED + gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED + ) + + dnl We can only support old upower + dnl https://bugzilla.gnome.org/show_bug.cgi?id=710383 + PKG_CHECK_MODULES(UPOWER, upower-glib < 0.99.0, have_old_upower=yes, have_old_upower=no) + AS_IF([test x$have_old_upower = xyes], [ + AC_DEFINE([HAVE_OLD_UPOWER], [1], [Define if we have an older upower]) + ]) + AM_CONDITIONAL(HAVE_OLD_UPOWER, test x$have_old_upower = xyes) + + PKG_CHECK_MODULES(SESSION_PROPERTIES, + glib-2.0 >= $GLIB_REQUIRED + gtk+-3.0 >= $GTK3_REQUIRED + ) +diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c +index 135392fd..29c3054d 100644 +--- a/gnome-session/gsm-manager.c ++++ b/gnome-session/gsm-manager.c +@@ -1210,78 +1210,78 @@ _client_request_save (GsmClient *client, + + error = NULL; + ret = gsm_client_request_save (client, data->flags, &error); + if (ret) { + g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client)); + data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients, + client); + } else if (error) { + g_debug ("GsmManager: unable to query client: %s", error->message); + g_error_free (error); + } + + return FALSE; + } + + static gboolean + _client_request_save_helper (const char *id, + GsmClient *client, + ClientEndSessionData *data) + { + return _client_request_save (client, data); + } + + static void + fail_pending_save_invocations (GsmManager *manager, + GError *error) + { + GSList *l; + + for (l = manager->priv->pending_save_invocations; l != NULL; l = l->next) { +- DBusGMethodInvocation *context = l->data; ++ GDBusMethodInvocation *invocation = l->data; + +- dbus_g_method_return_error (context, error); ++ g_dbus_method_invocation_return_gerror (invocation, error); + } + + g_slist_free (manager->priv->pending_save_invocations); + manager->priv->pending_save_invocations = NULL; + } + + static void + finish_pending_save_invocations (GsmManager *manager) + { + GSList *l; + + for (l = manager->priv->pending_save_invocations; l != NULL; l = l->next) { +- DBusGMethodInvocation *context = l->data; ++ GDBusMethodInvocation *invocation = l->data; + +- dbus_g_method_return (context); ++ g_dbus_method_invocation_return_value (invocation, NULL); + } + + g_slist_free (manager->priv->pending_save_invocations); + manager->priv->pending_save_invocations = NULL; + } + + static void + query_save_session_complete (GsmManager *manager) + { + GError *error = NULL; + + if (g_slist_length (manager->priv->next_query_clients) > 0) { + ClientEndSessionData data; + + data.manager = manager; + data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST; + + g_slist_foreach (manager->priv->next_query_clients, + (GFunc)_client_request_save, + &data); + + g_slist_free (manager->priv->next_query_clients); + manager->priv->next_query_clients = NULL; + + return; + } + + if (manager->priv->query_timeout_id > 0) { + g_source_remove (manager->priv->query_timeout_id); + manager->priv->query_timeout_id = 0; +@@ -2790,96 +2790,96 @@ gsm_manager_initialization_error (GsmExportedManager *skeleton, + { + if (manager->priv->phase != GSM_MANAGER_PHASE_INITIALIZATION) { + g_dbus_method_invocation_return_error (invocation, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION, + "InitializationError interface is only available during the Initialization phase"); + return TRUE; + } + + gsm_util_init_error (fatal, "%s", message); + gsm_exported_manager_complete_initialization_error (skeleton, invocation); + + return TRUE; + } + + static void + user_logout (GsmManager *manager, + GsmManagerLogoutMode mode) + { + if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) { + manager->priv->logout_mode = mode; + end_session_or_show_shell_dialog (manager); + return; + } + + request_logout (manager, mode); + } + + gboolean + gsm_manager_save_session (GsmManager *manager, +- DBusGMethodInvocation *context) ++ GDBusMethodInvocation *invocation) + { + ClientEndSessionData data; + GError *error; + + g_debug ("GsmManager: SaveSession called"); + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { + error = g_error_new (GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_RUNNING, + "SaveSession interface is only available during the Running phase"); +- dbus_g_method_return_error (context, error); ++ g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + return FALSE; + } + + data.manager = manager; + data.flags = 0; + gsm_store_foreach (manager->priv->clients, + (GsmStoreFunc)_client_request_save_helper, + &data); + + if (manager->priv->query_clients) { + manager->priv->query_timeout_id = g_timeout_add_seconds (GSM_MANAGER_SAVE_SESSION_TIMEOUT, + (GSourceFunc)_on_query_save_session_timeout, + manager); + + manager->priv->pending_save_invocations = g_slist_prepend (manager->priv->pending_save_invocations, +- context); ++ invocation); + + return TRUE; + } else { + g_debug ("GsmManager: Nothing to save"); +- dbus_g_method_return (context); ++ g_dbus_method_invocation_return_value (invocation, NULL); + return TRUE; + } + + return TRUE; + } + + gboolean + gsm_manager_logout (GsmManager *manager, + guint logout_mode, + GError **error) + { + if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_RUNNING, + "Logout interface is only available after the Running phase starts"); + return FALSE; + } + + if (_log_out_is_locked_down (manager)) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_LOCKED_DOWN, + "Logout has been locked down"); + return FALSE; + } + + switch (logout_mode) { + case GSM_MANAGER_LOGOUT_MODE_NORMAL: + case GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION: +diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h +index fcf36019..88a88ccc 100644 +--- a/gnome-session/gsm-manager.h ++++ b/gnome-session/gsm-manager.h +@@ -1,54 +1,55 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + + + #ifndef __GSM_MANAGER_H + #define __GSM_MANAGER_H + + #include ++#include + + #include "gsm-store.h" + #include "gsm-manager-logout-mode.h" + + G_BEGIN_DECLS + + #define GSM_TYPE_MANAGER (gsm_manager_get_type ()) + #define GSM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_MANAGER, GsmManager)) + #define GSM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_MANAGER, GsmManagerClass)) + #define GSM_IS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_MANAGER)) + #define GSM_IS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_MANAGER)) + #define GSM_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_MANAGER, GsmManagerClass)) + + typedef struct GsmManagerPrivate GsmManagerPrivate; + + typedef struct + { + GObject parent; + GsmManagerPrivate *priv; + } GsmManager; + + typedef struct + { + GObjectClass parent_class; + + void (* phase_changed) (GsmManager *manager, + const char *phase); + } GsmManagerClass; + + typedef enum { +@@ -98,42 +99,42 @@ GType gsm_manager_get_type (void); + GsmManager * gsm_manager_new (GsmStore *client_store, + gboolean failsafe); + GsmManager * gsm_manager_get (void); + + gboolean gsm_manager_get_failsafe (GsmManager *manager); + + gboolean gsm_manager_add_autostart_app (GsmManager *manager, + const char *path, + const char *provides); + gboolean gsm_manager_add_required_app (GsmManager *manager, + const char *path, + const char *provides); + gboolean gsm_manager_add_autostart_apps_from_dir (GsmManager *manager, + const char *path); + gboolean gsm_manager_add_legacy_session_apps (GsmManager *manager, + const char *path); + + void gsm_manager_start (GsmManager *manager); + + const char * _gsm_manager_get_default_session (GsmManager *manager); + char * _gsm_manager_get_saved_session (GsmManager *manager); + + void _gsm_manager_set_active_session (GsmManager *manager, + const char *session_name, + gboolean is_fallback); + + void _gsm_manager_set_renderer (GsmManager *manager, + const char *renderer); + + gboolean gsm_manager_save_session (GsmManager *manager, +- DBusGMethodInvocation *context); ++ GDBusMethodInvocation *context); + + gboolean gsm_manager_logout (GsmManager *manager, + guint logout_mode, + GError **error); + + gboolean gsm_manager_set_phase (GsmManager *manager, + GsmManagerPhase phase); + + G_END_DECLS + + #endif /* __GSM_MANAGER_H */ +diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c +index a41cd260..6ad307b0 100644 +--- a/tools/gnome-session-selector.c ++++ b/tools/gnome-session-selector.c +@@ -7,63 +7,60 @@ + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: Matthias Clasen + */ + + #include "config.h" + + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #include + #include + +-#include +-#include +- + #define GSM_SERVICE_DBUS "org.gnome.SessionManager" + #define GSM_PATH_DBUS "/org/gnome/SessionManager" + #define GSM_INTERFACE_DBUS "org.gnome.SessionManager" + + #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" + #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" + #define DEFAULT_SESSION_NAME "gnome-classic" + + static GtkBuilder *builder; + static GtkWidget *session_list; + static GtkListStore *store; + static GtkTreeModelSort *sort_model; + static char *info_text; + + static void select_session (const char *name); + static gboolean make_session_current (const char *name); + + static char * + get_session_path (const char *name) + { + return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); + } + + static char * + find_new_session_name (void) + { + char *name; + char *path; + int i; + +@@ -694,86 +691,93 @@ on_row_activated (GtkTreeView *tree_view, + gtk_main_quit (); + } + + static void + auto_save_next_session (void) + { + GSettings *settings; + + settings = g_settings_new (GSM_MANAGER_SCHEMA); + g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, TRUE); + g_object_unref (settings); + } + + static void + auto_save_next_session_if_needed (void) + { + char *marker; + + marker = g_build_filename (g_get_user_config_dir (), + "gnome-session", "saved-session", + ".new-session", NULL); + + if (g_file_test (marker, G_FILE_TEST_EXISTS)) { + auto_save_next_session (); + unlink (marker); + } + g_free (marker); + } + + static void +-save_session (void) ++session_saved_cb (GDBusConnection *conn, ++ GAsyncResult *result) + { +- DBusGConnection *conn; +- DBusGProxy *proxy; +- GError *error; ++ GVariant *reply; + +- conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); +- if (conn == NULL) { +- g_warning ("Could not connect to the session bus"); +- return; +- } ++ reply = g_dbus_connection_call_finish (conn, result, NULL); + +- proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS); +- if (proxy == NULL) { +- g_warning ("Could not connect to the session manager"); +- return; +- } ++ g_variant_unref (reply); ++} + +- error = NULL; +- if (!dbus_g_proxy_call (proxy, "SaveSession", &error, G_TYPE_INVALID, G_TYPE_INVALID)) { +- g_warning ("Failed to save session: %s", error->message); +- g_error_free (error); ++static void ++save_session (void) ++{ ++ GDBusConnection *conn; ++ ++ conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); ++ if (conn == NULL) { + return; + } + +- g_object_unref (proxy); ++ g_dbus_connection_call (conn, ++ GSM_SERVICE_DBUS, ++ GSM_PATH_DBUS, ++ GSM_INTERFACE_DBUS, ++ "SaveSession", ++ NULL, ++ NULL, ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ NULL, ++ (GAsyncReadyCallback) ++ session_saved_cb, ++ NULL); + } + + static int + compare_sessions (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data) + { + char *name_a, *name_b; + int result; + + gtk_tree_model_get (model, a, 0, &name_a, -1); + gtk_tree_model_get (model, b, 0, &name_b, -1); + + result = g_utf8_collate (name_a, name_b); + + g_free (name_a); + g_free (name_b); + + return result; + } + + static void + on_map (GtkWidget *widget, + gpointer data) + { + gdk_window_focus (gtk_widget_get_window (widget), GDK_CURRENT_TIME); + } + + int +-- +2.14.2 + diff --git a/SOURCES/0019-capplet-fix-disable-check-items.patch b/SOURCES/0019-capplet-fix-disable-check-items.patch new file mode 100644 index 0000000..267e0e7 --- /dev/null +++ b/SOURCES/0019-capplet-fix-disable-check-items.patch @@ -0,0 +1,91 @@ +From 0dfb463e126353b17d0c6ec63b99f10bae3fd919 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 27 Mar 2017 15:34:51 -0400 +Subject: [PATCH 19/19] capplet: fix disable check items + +An optimzation that tries to prevent transient desktop files from +accumulating in the user's homedirectory inadvertently broke the +ability for a user to disable startup applications. + +This commit restores the broken functionality. +--- + capplet/gsp-app.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/capplet/gsp-app.c b/capplet/gsp-app.c +index 123ab217..1a0580e6 100644 +--- a/capplet/gsp-app.c ++++ b/capplet/gsp-app.c +@@ -315,64 +315,65 @@ _gsp_app_user_equal_system (GspApp *app, + char *str = NULL; + GKeyFile *keyfile = NULL; + GDesktopAppInfo *app_info = NULL; + + manager = gsp_app_manager_get (); + system_dir = gsp_app_manager_get_dir (manager, + app->priv->xdg_system_position); + g_object_unref (manager); + if (!system_dir) { + goto out; + } + + path = g_build_filename (system_dir, app->priv->basename, NULL); + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { + goto out; + } + + app_info = g_desktop_app_info_new_from_keyfile (keyfile); + + if (!app_info) { + goto out; + } + + if (g_desktop_app_info_get_is_hidden (app_info)) { + goto out; + } + + if (g_desktop_app_info_has_key (app_info, +- GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED) && +- !g_desktop_app_info_get_boolean (app_info, +- GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED)) { +- goto out; ++ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED)) { ++ if (app->priv->enabled != g_desktop_app_info_get_boolean (app_info, GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED)) ++ goto out; ++ } else if (!app->priv->enabled) { ++ goto out; + } + + if (!g_desktop_app_info_get_show_in (app_info, NULL)) { + goto out; + } + + if (g_desktop_app_info_get_nodisplay (app_info)) { + goto out; + } + + str = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_NAME); + if (!_gsp_str_equal (str, app->priv->name)) { + goto out; + } + g_clear_pointer (&str, g_free); + + str = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_COMMENT); + if (!_gsp_str_equal (str, app->priv->comment)) { + goto out; + } + g_clear_pointer (&str, g_free); + + str = gsp_key_file_get_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_EXEC); + if (!_gsp_str_equal (str, app->priv->exec)) { + goto out; + } + g_clear_pointer (&str, g_free); +-- +2.14.2 + diff --git a/SOURCES/gnome-session-3.3.92-nv30.patch b/SOURCES/gnome-session-3.3.92-nv30.patch new file mode 100644 index 0000000..dcafc82 --- /dev/null +++ b/SOURCES/gnome-session-3.3.92-nv30.patch @@ -0,0 +1,52 @@ +From b524466482c9c64c11a55fb5d0174080471a1d5a Mon Sep 17 00:00:00 2001 +From: Adam Williamson +Date: Fri, 8 May 2015 16:41:38 -0400 +Subject: [PATCH] blacklist NV30 adapter on nouveau until #745202 is fixed + +--- + data/hardware-compatibility | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/data/hardware-compatibility b/data/hardware-compatibility +index 48b7946..3daef68 100644 +--- a/data/hardware-compatibility ++++ b/data/hardware-compatibility +@@ -1,32 +1,35 @@ + ## + ## This file contains a list of blacklist/whitelist regular expressions for + ## renderer strings. + ## + ## The regular expressions are case-insensitive POSIX Extended Regular + ## Expressions. See regex(7) for details. + ## + ## Syntax: + ## - Comment lines start with '#' + ## - Lines starting with '+' are whitelisting. + ## - Lines starting with '-' are blacklisting. + ## - Lines not starting with '#', '+', '-' are ignored. + ## + + # Intel 830-865 + -Intel\(R\) 8[[:digit:]]{2,2}[^[:digit:]] + + # Intel IGD + -Intel IGD + + # Pre-R300 radeon + -Mesa DRI R[12]00[^[:digit:]] + -Mesa DRI R[12]00$ + ++# NV30 family on Nouveau: https://bugzilla.redhat.com/show_bug.cgi?id=745202 ++-Gallium .* on NV3[0-9A-F]$ ++ + # Old Mesa software GL renderer + -software rasterizer + + # Gallium has softpipe; we explicitly enable llvmpipe + -softpipe + + # nouveau vieux NV25 doesn't work too well + -Mesa DRI nv25 +-- +2.3.7 + diff --git a/SOURCES/gnome-session-3.6.2-swrast.patch b/SOURCES/gnome-session-3.6.2-swrast.patch new file mode 100644 index 0000000..7d505bb --- /dev/null +++ b/SOURCES/gnome-session-3.6.2-swrast.patch @@ -0,0 +1,56 @@ +From 69c18aa6324e7d3fd6f93ea7a79ce99bed1a452d Mon Sep 17 00:00:00 2001 +From: Adam Jackson +Date: Fri, 8 May 2015 16:46:47 -0400 +Subject: [PATCH] Allow running on the classic software renderer + +No effect on arches where we build llvmpipe, +but on ppc/s390 classic swrast is marginally +less painful than softpipe. +--- + data/hardware-compatibility | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/data/hardware-compatibility b/data/hardware-compatibility +index 3daef68..fd06230 100644 +--- a/data/hardware-compatibility ++++ b/data/hardware-compatibility +@@ -1,35 +1,35 @@ + ## + ## This file contains a list of blacklist/whitelist regular expressions for + ## renderer strings. + ## + ## The regular expressions are case-insensitive POSIX Extended Regular + ## Expressions. See regex(7) for details. + ## + ## Syntax: + ## - Comment lines start with '#' + ## - Lines starting with '+' are whitelisting. + ## - Lines starting with '-' are blacklisting. + ## - Lines not starting with '#', '+', '-' are ignored. + ## + + # Intel 830-865 + -Intel\(R\) 8[[:digit:]]{2,2}[^[:digit:]] + + # Intel IGD + -Intel IGD + + # Pre-R300 radeon + -Mesa DRI R[12]00[^[:digit:]] + -Mesa DRI R[12]00$ + + # NV30 family on Nouveau: https://bugzilla.redhat.com/show_bug.cgi?id=745202 + -Gallium .* on NV3[0-9A-F]$ + + # Old Mesa software GL renderer +--software rasterizer ++#software rasterizer + + # Gallium has softpipe; we explicitly enable llvmpipe + -softpipe + + # nouveau vieux NV25 doesn't work too well + -Mesa DRI nv25 +-- +2.3.7 + diff --git a/SPECS/gnome-session.spec b/SPECS/gnome-session.spec new file mode 100644 index 0000000..9a2bcf7 --- /dev/null +++ b/SPECS/gnome-session.spec @@ -0,0 +1,1397 @@ +%global _changelog_trimtime %(date +%s -d "1 year ago") + +%define po_package gnome-session-3.0 + +Summary: GNOME session manager +Name: gnome-session +Version: 3.26.1 +Release: 11%{?dist} +URL: http://www.gnome.org +#VCS: git:git://git.gnome.org/gnome-session +Source0: http://download.gnome.org/sources/gnome-session/3.26/%{name}-%{version}.tar.xz + +# Blacklist NV30: https://bugzilla.redhat.com/show_bug.cgi?id=745202 +Patch00:gnome-session-3.3.92-nv30.patch +Patch01: gnome-session-3.6.2-swrast.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1031182 +# https://bugzilla.redhat.com/show_bug.cgi?id=1031188 +Patch02: 0001-Revert-Remove-all-references-to-gnome-session-proper.patch +Patch03: 0002-Revert-Remove-gnome-session-properties.patch +Patch04: 0003-Revert-Rename-the-desktop-file-to-gnome-session-prop.patch +Patch05: 0004-stop-using-gsm_util_get_current_desktop.patch +Patch06: 0005-session-properties-get-out-of-Other.patch +Patch07: 0006-session-properties-refresh-from-recent-glade.patch +Patch08: 0007-manager-Don-t-clear-saved-session-if-autosaving-is-d.patch +Patch09: 0008-Add-Remember-Currently-Running-Applications-button.patch +Patch10: 0009-Revert-Allow-saved-session-to-be-a-symlink.patch +Patch11: 0010-Allow-saved-session-directory-to-be-a-symlink.patch +Patch12: 0011-Tie-session-selector-to-properties-dialog.patch +Patch13: 0012-make-save-session-stall-until-it-finishes.patch +Patch14: 0013-manager-save-session-type-in-session-dir.patch +Patch15: 0014-session-selector-restore-saved-session-mode.patch +Patch16: 0015-session-selector-refresh-from-recent-glade.patch +Patch17: 0016-session-selector-add-toggle-for-classic-normal-selec.patch +Patch18: 0017-session-selector-use-classic-mode-by-default.patch +Patch19: 0018-manager-port-away-from-dbus-glib-to-GDBus.patch +Patch20: 0019-capplet-fix-disable-check-items.patch + +Patch30: 0001-save-make-sure-app-state-is-written-into-desktop-fil.patch +Patch31: 0002-autostart-ensure-gnome-shell-and-mutter-get-right-au.patch + +Patch40: 0001-main-don-t-call-into-gdbus-before-setting-all-enviro.patch + +License: GPLv2+ +Group: User Interface/Desktops + +Requires: system-logos +# Needed for gnome-settings-daemon +Requires: control-center-filesystem + +Requires: gsettings-desktop-schemas >= 0.1.7 + +# pull in dbus-x11, see bug 209924 +Requires: dbus-x11 + +# https://bugzilla.redhat.com/show_bug.cgi?id=1072801 +Requires: mesa-dri-drivers + +BuildRequires: pkgconfig(egl) +BuildRequires: pkgconfig(epoxy) +BuildRequires: pkgconfig(gl) +BuildRequires: pkgconfig(glesv2) +BuildRequires: pkgconfig(gnome-desktop-3.0) +BuildRequires: pkgconfig(gtk+-3.0) +BuildRequires: pkgconfig(libsystemd) +BuildRequires: pkgconfig(ice) +BuildRequires: pkgconfig(json-glib-1.0) +BuildRequires: pkgconfig(sm) +BuildRequires: pkgconfig(x11) +BuildRequires: pkgconfig(xau) +BuildRequires: pkgconfig(xcomposite) +BuildRequires: pkgconfig(xext) +BuildRequires: pkgconfig(xrender) +BuildRequires: pkgconfig(xtrans) +BuildRequires: pkgconfig(xtst) +BuildRequires: GConf2-devel +BuildRequires: pango-devel +BuildRequires: desktop-file-utils +BuildRequires: libXrandr-devel +BuildRequires: librsvg2-devel + +# this is so the configure checks find /usr/bin/halt etc. +BuildRequires: usermode + +BuildRequires: intltool, autoconf, automake +BuildRequires: libtool +BuildRequires: gettext +BuildRequires: libX11-devel libXt-devel +BuildRequires: libXtst-devel +BuildRequires: xmlto +BuildRequires: upower-devel +BuildRequires: gnome-common +BuildRequires: systemd-devel +BuildRequires: polkit-devel +BuildRequires: git + +# an artificial requires to make sure we get dconf, for now +Requires: dconf + +%description +gnome-session manages a GNOME desktop or GDM login session. It starts up +the other core GNOME components and handles logout and saving the session. + +%package wayland-session +Summary: Desktop file for gnome-session (Wayland) +Group: User Interface/Desktops +Requires: gnome-session = %{version}-%{release} +Requires: xorg-x11-server-Xwayland + +%description wayland-session +Desktop file to add GNOME (Wayland) to display manager session menu. + +%package xsession +Summary: Desktop file for gnome-session +Group: User Interface/Desktops +Requires: gnome-session = %{version}-%{release} + +%description xsession +Desktop file to add GNOME to display manager session menu. + +%package custom-session +Summary: A facility to select and store saved sessions +Group: User Interface/Desktop +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description custom-session +Installs a 'Custom' entry in the display manager session menu that +lets the user manage multiple saved sessions. + +%prep +%autosetup -S git + +echo "ACLOCAL_AMFLAGS = -I m4" >> Makefile.am + +autoreconf -i -f + +%build +%configure --enable-docbook-docs \ + --enable-session-selector \ + --enable-systemd +make %{?_smp_mflags} V=1 + +%install + +%make_install + +mv $RPM_BUILD_ROOT%{_datadir}/xsessions/gnome-xorg.desktop \ + $RPM_BUILD_ROOT%{_datadir}/wayland-sessions/gnome-wayland.desktop +sed -i -e 's/Xorg/Wayland/g' $RPM_BUILD_ROOT%{_datadir}/wayland-sessions/gnome-wayland.desktop + +%find_lang %{po_package} + +%post +/sbin/ldconfig +touch --no-create %{_datadir}/icons/hicolor &>/dev/null || : + +%postun +/sbin/ldconfig +if [ $1 -eq 0 ] ; then + touch --no-create %{_datadir}/icons/hicolor &>/dev/null || : + gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : + glib-compile-schemas %{_datadir}/glib-2.0/schemas &>/dev/null || : +fi + +%posttrans +gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : +glib-compile-schemas %{_datadir}/glib-2.0/schemas &>/dev/null || : + +%files wayland-session +%{_datadir}/wayland-sessions/* + +%files xsession +%{_datadir}/xsessions/* + +%files custom-session +%{_datadir}/xsessions/gnome-custom-session.desktop +%{_bindir}/gnome-session-selector +%{_bindir}/gnome-session-custom-session +%{_datadir}/gnome-session/session-selector.ui +%doc %{_mandir}/man*/gnome-session-selector* + +%files -f %{po_package}.lang +%doc AUTHORS COPYING NEWS README +%doc %{_mandir}/man*/* +%{_bindir}/* +%{_libexecdir}/gnome-session-binary +%{_libexecdir}/gnome-session-check-accelerated +%{_libexecdir}/gnome-session-check-accelerated-gl-helper +%{_libexecdir}/gnome-session-check-accelerated-gles-helper +%{_libexecdir}/gnome-session-failed +%{_datadir}/applications/session-properties.desktop +%{_datadir}/gnome-session/ +%{_datadir}/doc/gnome-session/dbus/gnome-session.html +%{_datadir}/icons/hicolor/*/apps/session-properties.png +%{_datadir}/icons/hicolor/symbolic/apps/session-properties-symbolic.svg +%{_datadir}/icons/hicolor/scalable/apps/session-properties.svg +%{_datadir}/GConf/gsettings/gnome-session.convert +%{_datadir}/glib-2.0/schemas/org.gnome.SessionManager.gschema.xml + +%changelog +* Wed Feb 14 2018 Ray Strode - 3.26.1-11 +- Fix rare crash at start up for VNC sessions + Resolves: #1545234 + +* Fri Jan 19 2018 Ray Strode - 3.26.1-10 +- Correct app id test in upgrade compat fix for wayland + sessions. + Related: #1529175 + +* Thu Jan 18 2018 Ray Strode - 3.26.1-9 +- Fix wayland when user has saved sessions + Resolves: #1529175 + +* Fri Nov 10 2017 Ray Strode 3.26.1-8 +- New sed incantation is still overzealous. Rework how + gnome-wayland session file is generated. + Resolves: #1510391 + +* Tue Nov 07 2017 Ray Strode - 3.26.1-7 +- Fix sed script so we don't change DesktopNames (oops) + Related: #1482140 + Resolves: 1510391 + +* Mon Nov 06 2017 Ray Strode - 3.26.1-6 +- Add Requires for Xwayland to wayland subpackage + Related: #1482140 + Resolves: 1509247 + +* Thu Oct 19 2017 Ray Strode - 3.26.1-1 +- add wayland session file + Resolves: #1482140 +- update to 3.26.1 to work with wayland sessions + Resolves: 1504156 + +* Tue May 30 2017 Ray Strode - 3.22.3-4 +- fix crash in fail whale + Resolves: #1392970 + +* Mon Mar 27 2017 Ray Strode - 3.22.3-3 +- Fix the ability to disable a service from session properties + Resolves: #1310975 + +* Thu Mar 16 2017 Ray Strode - 3.22.3-2 +- Drop gnome-xorg.desktop + Related: #1386957 + +* Wed Mar 08 2017 Ray Strode - 3.22.3-1 +- Rebase to 3.22.3 + Resolves: #1386957 + +* Mon Jun 27 2016 Ray Strode - 3.14.0-6 +- update ko and it translations + Related: #1272374 + +* Mon Apr 4 2016 Matthias Clasen 3.14.0-5 +- Update translations + Resolves: #1272374 + +* Fri Jul 03 2015 Ray Strode 3.14.0-4 +- Fix up custom sessions patch more + Resolves: #1091453 + +* Wed May 13 2015 Ray Strode 3.14.0-3 +- Fix up custom sessions patch +- Related: #1174597 + +* Mon Mar 23 2015 Richard Hughes - 3.14.0-1 +- Update to 3.14.0 +- Resolves: #1174597 + +* Mon Mar 10 2014 Ray Strode 3.8.4-11 +- Require mesa-dri-drivers be installed so software GL is + available. + Resolves: #1072801 + +* Wed Jan 29 2014 Ray Strode 3.8.4-10 +- more translation updates + Resolves: #1054583 + +* Wed Jan 29 2014 Ray Strode 3.8.4-9 +- Fix crash when all clients exit + Resolves: #1036038 + +* Tue Jan 28 2014 Ray Strode 3.8.4-8 +- Fix crasher in session selector if ~/.config/gnome-session + doesn't exist + Related: #1031182 + +* Fri Jan 24 2014 Daniel Mach - 3.8.4-7 +- Mass rebuild 2014-01-24 + +* Wed Jan 08 2014 Ray Strode 3.8.4-6 +- Add back custom session selector from RHEL 6 + Resolves: #1031188 + Resolves: #1031182 + +* Fri Dec 27 2013 Daniel Mach - 3.8.4-5 +- Mass rebuild 2013-12-27 + +* Thu Dec 12 2013 Matthias Clasen - 3.8.4-4 +- Update translations +- Resolves: #1030346 + +* Wed Nov 27 2013 Florian Müllner - 3.8.4-3 +- Fix mispositioned fail-whale dialog + Resolves: #1031117 + +* Fri Nov 1 2013 Matthias Clasen - 3.8.4-2 +- Fix a possible crash in the presence interface +- Resolves: #1022521 + +* Tue Jul 30 2013 Ray Strode - 3.8.4-1 +- Update to 3.8.4 + +* Tue Jul 30 2013 Matthias Clasen - 3.8.3-1 +- Fix a crash when talking to systemd (#977575) + +* Sat Jun 22 2013 Matthias Clasen - 3.8.2.1-2 +- Trim %%changelog + +* Wed May 15 2013 Matthias Clasen - 3.8.2.1-1 +- Update to 3.8.2.1 +- Conditionally build session selector + +* Mon May 13 2013 Matthias Clasen - 3.8.2-1 +- Update to 3.8.2 + +* Tue Apr 30 2013 Kalev Lember - 3.8.1-2 +- Use the upstream xsession desktop file +- Drop the fallback mode authentication agent autostart file + +* Mon Apr 15 2013 Kalev Lember - 3.8.1-1 +- Update to 3.8.1 + +* Thu Apr 11 2013 Adam Jackson 3.8.0-2 +- gnome-session-3.6.2-swrast.patch: Allow running on the classic software + renderer. No effect on arches where we build llvmpipe, but on ppc/s390 + classic swrast is marginally less painful than softpipe. + +* Tue Mar 26 2013 Kalev Lember - 3.8.0-1 +- Update to 3.8.0 + +* Wed Mar 20 2013 Richard Hughes - 3.7.92-1 +- Update to 3.7.92 + +* Wed Mar 6 2013 Matthias Clasen - 3.7.91-1 +- Update to 3.7.91 + +* Sun Feb 24 2013 Matthias Clasen - 3.7.90-2 +- Drop obsolete requires (polkit-gnome, polkit-desktop-policy, + notification-daemon) + +* Wed Feb 20 2013 Richard Hughes - 3.7.90-1 +- Update to 3.7.90 + +* Wed Feb 06 2013 Kalev Lember - 3.7.4-1 +- Update to 3.7.4 + +* Thu Dec 20 2012 Kalev Lember - 3.7.3-1 +- Update to 3.7.3 +- Drop the upstreamed llvmpipe patch +- Adjust buildrequires + +* Tue Nov 20 2012 Richard Hughes - 3.7.2-1 +- Update to 3.7.2 + +* Fri Nov 09 2012 Kalev Lember - 3.7.1-1 +- Update to 3.7.1 + +* Thu Oct 18 2012 Florian Müllner - 3.6.1-2 +- Set XDG_MENU_PREFIX to pick the correct menu layout in + gnome-shell and alacarte + +* Tue Oct 16 2012 Kalev Lember - 3.6.1-1 +- Update to 3.6.1 + +* Tue Sep 25 2012 Matthias Clasen - 3.6.0-1 +- Update to 3.6.0 + +* Thu Sep 06 2012 Richard Hughes - 3.5.91-1 +- Update to 3.5.91 + +* Tue Aug 07 2012 Richard Hughes - 3.5.5-1 +- Update to 3.5.5 + +* Fri Jul 27 2012 Fedora Release Engineering - 3.5.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Tue Jul 17 2012 Richard Hughes - 3.5.4-1 +- Update to 3.5.4 + +* Thu Jun 07 2012 Richard Hughes - 3.5.2-1 +- Update to 3.5.2 + +* Fri May 18 2012 Richard Hughes - 3.4.2-1 +- Update to 3.4.2 + +* Tue Apr 24 2012 Kalev Lember - 3.4.1-2 +- Silence rpm scriptlet output + +* Tue Apr 17 2012 Kalev Lember - 3.4.1-1 +- Update to 3.4.1 +- Dropped upstreamed systemd patch + +* Thu Apr 5 2012 Matthias Clasen - 3.4.0-2 +- Fix a looping PolicyKit dialog on shutdown + +* Tue Mar 27 2012 Richard Hughes - 3.4.0-1 +- Update to 3.4.0 + +* Thu Mar 22 2012 Adam Williamson - 3.3.92-2 +- blacklist NV30 family until RH #745202 is resolved + +* Wed Mar 21 2012 Kalev Lember - 3.3.92-1 +- Update to 3.3.92 + +* Sun Feb 26 2012 Matthias Clasen - 3.3.90-1 +- Update to 3.3.90 + +* Thu Feb 9 2012 Matthias Clasen - 3.3.5-2 +- Fix a bug in the new system interface registration + +* Tue Feb 7 2012 Matthias Clasen - 3.3.5-1 +- Update to 3.3.5 +- Use systemd for session tracking + +* Fri Jan 13 2012 Fedora Release Engineering - 3.3.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Wed Dec 21 2011 Matthias Clasen 3.3.3-1 +- Update to 3.3.3 + +* Tue Dec 13 2011 Adam Jackson 3.3.2-2 +- gnome-session-3.3.2-radeon.patch: Blacklist pre-R300 radeons (#758422) + +* Wed Nov 23 2011 Matthias Clasen 3.3.2-1 +- Update to 3.3.2 + +* Thu Nov 03 2011 Adam Jackson 3.3.1-2 +- gnome-session-3.3.1-llvmpipe.patch: Don't consider llvmpipe unsupported. + +* Wed Nov 2 2011 Matthias Clasen - 3.3.1-1 +- Update to 3.3.1 + +* Wed Oct 26 2011 Fedora Release Engineering - 3.2.1-2 +- Rebuilt for glibc bug#747377 + +* Tue Oct 18 2011 Matthias Clasen - 3.2.1-1 +- Update to 3.2.1 + +* Tue Sep 27 2011 Ray - 3.2.0-1 +- Update to 3.2.0 + +* Tue Sep 20 2011 Matthias Clasen 3.1.92-1 +- Update to 3.1.92 + +* Fri Sep 9 2011 Matthias Clasen 3.1.91-3 +- Some fixes to make gdm fallback mode login work + +* Thu Sep 8 2011 Matthias Clasen 3.1.91-2 +- Drop GConf2-gtk dep + +* Tue Sep 6 2011 Matthias Clasen 3.1.91-1 +- Update to 3.1.91 + +* Wed Aug 31 2011 Matthias Clasen 3.1.90-1 +- Update to 3.1.90 + +* Wed Aug 17 2011 Matthias Clasen 3.1.5-1 +- Update to 3.1.5 + +* Mon Jul 04 2011 Bastien Nocera 3.1.3-1 +- Update to 3.1.3 + +* Wed Jun 15 2011 Tomas Bzatek - 3.1.2-1 +- Update to 3.1.2 + +* Wed Apr 27 2011 Owen Taylor - 3.0.1-2 +- Add a quick-and-dirty blacklist for Radeon R100, R200, Intel 8xx + +* Tue Apr 26 2011 Matthias Clasen 3.0.1-1 +- Update to 3.0.1 + +* Mon Apr 4 2011 Matthias Clasen 3.0.0-1 +- Update to 3.0.0 + +* Mon Mar 28 2011 Matthias Clasen 2.91.94-1 +- Update to 2.91.94 + +* Wed Mar 23 2011 Ray Strode 2.91.93-1 +- Update to 2.91.93 + +* Wed Mar 23 2011 Matthias Clasen 2.91.92-1 +- Update to 2.91.92 + +* Wed Mar 9 2011 Matthias Clasen 2.91.91.3-1 +- Update to 2.91.91.3 + +* Wed Mar 9 2011 Matthias Clasen 2.91.91.2-1 +- Update to 2.91.91.2 + +* Tue Mar 8 2011 Matthias Clasen 2.91.91-2 +- Fix the check-accel utility exit status + +* Mon Mar 7 2011 Matthias Clasen 2.91.91-1 +- Update to 2.91.91 + +* Mon Feb 28 2011 Matthias Clasen 2.91.90-5 +- Make the new if-session Autostart condition work + +* Mon Feb 28 2011 Matthias Clasen 2.91.90-4 +- Fix the autostart syntax + +* Tue Feb 22 2011 Matthias Clasen 2.91.90-3 +- Install an autostart file for the authentication agent + in the fallback session + +* Tue Feb 22 2011 Ray Strode 2.91.90-2 +- Fix crashity crash crash + +* Mon Feb 21 2011 Matthias Clasen 2.91.90-1 +- Update to 2.91.90 + +* Fri Feb 11 2011 Matthias Clasen 2.91.6-3 +- Rebuild against newer gtk + +* Tue Feb 08 2011 Fedora Release Engineering - 2.91.6-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Wed Feb 2 2011 Matthias Clasen 2.91.6-1 +- 2.91.6 + +* Tue Jan 25 2011 Matthias Clasen 2.91.4-3 +- Just require control-center-filesystem (#661565) + +* Fri Jan 14 2011 Matthias Clasen 2.91.4-2 +- Don't run the shell on softpipe + +* Sun Jan 9 2011 Matthias Clasen 2.91.4-1 +- Update to 2.91.4 + +* Fri Dec 3 2010 Matthias Clasen 2.91.0-7 +- Rebuild against new gtk + +* Sun Nov 07 2010 Ray Strode 2.91.0-6 +- Fix some cases where the inhibitor dialog shows up when it isn't + supposed to. + +* Tue Nov 2 2010 Matthias Clasen - 2.91.0-5 +- Prepare for libnotify 0.7.0 + +* Mon Nov 1 2010 Matthias Clasen - 2.91.0-4 +- Rebuild against newer gtk3 + +* Tue Oct 26 2010 Parag Nemade - 2.91.0-3 +- Gconf2 scriptlet accepts schema file names without file extension. + +* Fri Oct 15 2010 Parag Nemade - 2.91.0-2 +- Merge-review cleanup (#225835) + +* Wed Oct 6 2010 Matthias Clasen - 2.91.0-1 +- Update to 2.91.0 + +* Thu Sep 30 2010 Matthias Clasen - 2.32.0-1 +- Update to 2.32.0 + +* Wed Sep 29 2010 jkeating - 2.31.6-3 +- Rebuilt for gcc bug 634757 + +* Tue Sep 21 2010 Ray Strode - 2.31.6-2 +- build fixes + +* Fri Aug 6 2010 Matthias Clasen - 2.31.6-1 +- Update to 2.31.6 + +* Thu Jul 8 2010 Matthias Clasen - 2.31.2-2 +- Require dconf + +* Thu May 27 2010 Matthias Clasen - 2.31.2-1 +- Update to 2.31.2 + +* Fri May 07 2010 Colin Walters - 2.30.0-2 +- Use upstream commit for library linking + +* Mon Mar 29 2010 Matthias Clasen - 2.30.0-1 +- Update to 2.30.0 + +* Tue Mar 09 2010 Bastien Nocera 2.29.92-1 +- Update to 2.29.92 + +* Thu Feb 11 2010 Matthias Clasen - 2.29.6-1 +- Update to 2.29.6 + +* Fri Jan 15 2010 Ray Strode - 2.28.0-4 +- Nag user if they try to log in as root + +* Fri Nov 6 2009 Matthias Clasen - 2.28.0-3 +- Add ability to perform actions after a period of idleness + +* Fri Oct 23 2009 Matthias Clasen - 2.28.0-2 +- Avoid a crash on certain xsmp error conditions + +* Wed Sep 23 2009 Matthias Clasen - 2.28.0-1 +- Update to 2.28.0 + +* Wed Sep 9 2009 Matthias Clasen - 2.27.92-1 +- Update to 2.27.92 + +* Thu Aug 13 2009 Matthias Clasen - 2.27.5-2 +- Require polkit-desktop-policy + +* Tue Jul 28 2009 Matthias Clasen - 2.27.5-1 +- Update to 2.27.5 + +* Fri Jul 24 2009 Fedora Release Engineering - 2.27.4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Mon Jul 20 2009 Matthias Clasen - 2.27.4-2 +- Require polkit-gnome, we need an authentication agent in the session + +* Wed Jul 15 2009 Matthias Clasen - 2.27.4-1 +- Update to 2.27.4 + +* Fri Jul 10 2009 Matthias Clasen - 2.26.1-5 +- Avoid pointless warnings + +* Sun Jun 14 2009 Matthias Clasen - 2.26.1-4 +- Drop unused files + +* Fri Jun 12 2009 Matthias Clasen - 2.26.1-3 +- Fix some oversights in the PolicyKit port + +* Tue May 12 2009 Matthias Clasen - 2.26.1-2 +- Port to PolicyKit 1 + +* Tue Apr 14 2009 Matthias Clasen - 2.26.1-1 +- Update to 2.26.1 +- See http://download.gnome.org/sources/gnome-session/2.26/gnome-session-2.26.1.news + +* Wed Apr 8 2009 Matthias Clasen - 2.26.0.90-1 +- Update to 2.26.0.90 + +* Sun Apr 5 2009 Matthias Clasen - 2.26.0-2 +- Avoid some warnings (#493688) + +* Mon Mar 16 2009 Matthias Clasen - 2.26.0-1 +- Update to 2.26.0 + +* Fri Mar 6 2009 Matthias Clasen - 2.25.92-2 +- Turn off excessive debug spew + +* Tue Mar 3 2009 Matthias Clasen - 2.25.92-1 +- Update to 2.25.92 + +* Thu Feb 26 2009 Matthias Clasen - 2.25.91-4 +- Make -xsession arch again +- Fix xsync usage + +* Tue Feb 24 2009 Matthias Clasen - 2.25.91-3 +- Make -xsession noarch + +* Tue Feb 24 2009 Fedora Release Engineering - 2.25.91-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Wed Feb 18 2009 Matthias Clasen - 2.25.91-1 +- Update to 2.25.91 + +* Tue Feb 3 2009 Matthias Clasen - 2.25.90-1 +- Update to 2.25.90 + +* Tue Jan 20 2009 Matthias Clasen - 2.25.5-2 +- Update to 2.25.5 +- Fix BuildRequires + +* Wed Dec 17 2008 Matthias Clasen - 2.25.3-1 +- Update to 2.25.3 + +* Thu Dec 4 2008 Matthias Clasen - 2.25.2-2 +- Update to 2.25.2 + +* Tue Nov 25 2008 Matthias Clasen - 2.24.1-5 +- Spec file cleanups + +* Mon Nov 10 2008 Matthias Clasen - 2.24.1-4 +- Fix client registration in some cases + +* Sun Oct 26 2008 Matthias Clasen - 2.24.1-3 +- Make the capplet resizable (#468577) + +* Wed Oct 22 2008 Matthias Clasen - 2.24.1-1 +- Update to 2.24.1 +- Drop upstreamed patches + +* Wed Oct 15 2008 Ray Strode - 2.24.0-11 +- Remove some dubious code to fix panel race at startup that + would make shutdown menu item disappear for some users. + +* Fri Oct 10 2008 Ray Strode - 2.24.0-10 +- Rewrite patch another time leverage better api and be more + terse + +* Fri Oct 10 2008 Ray Strode - 2.24.0-9 +- Bring shutdown menu item back. More fallout from my buggy + patch introduced in -7 + +* Thu Oct 9 2008 Ray Strode - 2.24.0-8 +- Fix assertion failure in last patch + +* Thu Oct 9 2008 Ray Strode - 2.24.0-7 +- Add new api for panel to figure out whether or not to show + Shutdown menu item. + +* Fri Oct 3 2008 Matthias Clasen - 2.24.0-6 +- Fix missing translations in the capplet +- Fix small UI issues in the capplet + +* Sun Sep 28 2008 Matthias Clasen - 2.24.0-5 +- BR xorg-x11-xtrans-devel (#464316) + +* Fri Sep 26 2008 Ray Strode - 2.24.0-4 +- Make the new xsession subpackage require the version of + gnome-session it's built against + +* Thu Sep 25 2008 Ray Strode - 2.24.0-3 +- Split gnome-session.desktop off into subpackage + +* Mon Sep 22 2008 Matthias Clasen - 2.24.0-2 +- Update to 2.24.0 +- Drop upstreamed patches + +* Thu Sep 18 2008 Matthias Clasen - 2.23.92-6 +- Plug memory leaks + +* Thu Sep 18 2008 Matthias Clasen - 2.23.92-5 +- Plug memory leaks + +* Mon Sep 15 2008 Matthias Clasen - 2.23.92-4 +- Plug memory leaks + +* Sun Sep 14 2008 Matthias Clasen - 2.23.92-3 +- Plug memory leaks + +* Sun Sep 14 2008 Matthias Clasen - 2.23.92-2 +- Plug memory leaks + +* Mon Sep 8 2008 Jon McCann - 2.23.92-1 +- Update to 2.23.92 + +* Tue Sep 2 2008 Matthias Clasen - 2.23.91-1 +- Update to 2.23.91 + +* Thu Aug 28 2008 Jon McCann - 2.23.91.0.2008.08.28.1 +- Update to snapshot + +* Fri Aug 22 2008 Matthias Clasen - 2.23.90-1 +- Update to 2.23.90 + +* Thu Aug 14 2008 Lennart Poettering - 2.23.6-4 +- Drop login/logout sound scripts since we do this now in libcanberra + +* Tue Aug 12 2008 Matthias Clasen - 2.23.6-3 +- Fix a crash in the at-spi-registryd-wrapper + +* Thu Aug 7 2008 Matthias Clasen - 2.23.6-2 +- Fix another icon name + +* Tue Aug 5 2008 Matthias Clasen - 2.23.6-1 +- Update to 2.23.6 + +* Wed Jul 30 2008 Jon McCann - 2.23.6.0.2008.07.30.1 +- New snapshot from DBus branch + +* Mon Jul 29 2008 Jon McCann - 2.23.6.0.2008.07.29.1 +- New snapshot from DBus branch + +* Mon Jul 28 2008 Jon McCann - 2.23.5.0.2008.07.28.1 +- New snapshot from DBus branch + +* Thu Jul 24 2008 Matthias Clasen - 2.23.5.0.2008.07.21.4 +- Fix a crash + +* Mon Jul 22 2008 Jon McCann - 2.23.5.0.2008.07.21.3 +- Add BuildRequires PolicyKit-gnome-devel + +* Mon Jul 21 2008 Jon McCann - 2.23.5.0.2008.07.21.2 +- New snapshot to fix build + +* Mon Jul 21 2008 Jon McCann - 2.23.5.0.2008.07.21.1 +- Snapshot from DBus branch + +* Wed Jul 9 2008 Matthias Clasen - 2.23.4.1-4 +- Fix a typo in the previous patch + +* Wed Jul 9 2008 Matthias Clasen - 2.23.4.1-3 +- Use more standard icon names + +* Tue Jul 8 2008 Matthias Clasen - 2.23.4.1-2 +- Escape comments for markup + +* Wed Jun 18 2008 Matthias Clasen - 2.23.4.1-1 +- Update to 2.23.4.1 + +* Wed Jun 4 2008 Matthias Clasen - 2.23.3-1 +- Update to 2.23.3 + +* Fri May 16 2008 Matthias Clasen - 2.23.2.2-3 +- Make nautilus start again + +* Thu May 15 2008 Matthias Clasen - 2.23.2.2-2 +- Don't crash while handling legacy sessions + +* Wed May 14 2008 Matthias Clasen - 2.23.2.2-1 +- Update to 2.23.2.2 + +* Fri Apr 25 2008 Matthias Clasen - 2.23.1-1 +- Update to 2.23.1 + +* Thu Apr 10 2008 Matthias Clasen - 2.22.1.1-1 +- Update to 2.22.1.1 (fixes a crash in the trash migration code) + +* Mon Apr 7 2008 Matthias Clasen - 2.22.1-1 +- Update to 2.22.1 + +* Sun Apr 6 2008 Matthias Clasen - 2.22.0-2 +- Drop gnome-volume-manager from the default session + +* Mon Mar 10 2008 Matthias Clasen - 2.22.0-1 +- Update to 2.22.0 + +* Thu Mar 06 2008 - Bastien Nocera - 2.21.93-1 +- Update to 2.21.93, drop esound dependencies and patches + +* Tue Feb 26 2008 Matthias Clasen - 2.21.92-1 +- Update to 2.21.92 + +* Tue Feb 12 2008 Matthias Clasen - 2.21.91-1 +- Update to 2.21.91 + +* Mon Feb 11 2008 - Bastien Nocera - 2.21.90-2 +- Add patch to make login sounds work +- Remove unneeded patch to launch gnome-user-share, it launches + using autostart now + +* Tue Jan 29 2008 Matthias Clasen - 2.21.90-1 +- Update to 2.21.90 + +* Tue Jan 15 2008 Matthias Clasen - 2.21.5-1 +- Update to 2.21.5 + +* Tue Nov 27 2007 Matthias Clasen - 2.20.2-1 +- Update to 2.20.2 (translation updates) + +* Tue Oct 30 2007 - Bastien Nocera - 2.20.1-2 +- Enable sound by default, without looking at the prefs + +* Mon Oct 15 2007 Matthias Clasen - 2.20.1-1 +- Update to 2.20.1 (translation updates) + +* Thu Sep 27 2007 Ray Strode - 2.20.0-2 +- drop redhat-artwork dep. We don't need it. + +* Mon Sep 17 2007 Matthias Clasen - 2.20.0-1 +- Update to 2.20.0 + +* Tue Sep 11 2007 Matthias Clasen - 2.19.92-3 +- Plug memory leaks in the ICE code + +* Wed Sep 5 2007 Kristian Høgsberg - 2.19.92-2 +- Update gnome-session-2.17.5-window-manager.patch to apply (remove + chunks that are now upstream). + +* Tue Sep 4 2007 Matthias Clasen - 2.19.92-1 +- Update to 2.19.92 + +* Thu Aug 23 2007 Adam Jackson - 2.19.90-2 +- Rebuild for build ID + +* Mon Aug 13 2007 Matthias Clasen - 2.19.90-1 +- Update to 2.19.90 + +* Fri Aug 10 2007 Kristian Høgsberg - 2.19.6-5 +- Edit window manager patch again to add 'glib' to compiz launch. + +* Thu Aug 9 2007 Kristian Høgsberg - 2.19.6-4 +- Edit the right window manager patch and delete the old one. + +* Thu Aug 9 2007 Kristian Høgsberg - 2.19.6-3 +- Export LIBGL_ALWAYS_INDIRECT before starting compiz. + +* Fri Aug 3 2007 Matthias Clasen - 2.19.6-2 +- Update license field + +* Mon Jul 30 2007 Matthias Clasen - 2.19.6-1 +- Update to 2.19.6 + +* Sun Jul 8 2007 Matthias Clasen - 2.19.5-1 +- Update to 2.19.5 + +* Fri Jul 6 2007 Matthias Clasen - 2.19.4-3 +- Move /usr/share/gnome/wm-properties to control-center-filesystem + +* Tue Jun 19 2007 Matthias Clasen - 2.19.4-2 +- Fix a hang on login with a11y + +* Sun Jun 17 2007 Matthias Clasen - 2.19.4-1 +- Update to 2.19.4 + +* Mon Jun 4 2007 Matthias Clasen - 2.19.3-1 +- Update to 2.19.3 +- Drop upstreamed patch + +* Tue May 22 2007 - Bastien Nocera - 2.19.2-2 +- Fix up logic in iris patch + +* Sun May 20 2007 Matthias Clasen - 2.19.2-1 +- Update to 2.19.2 + +* Tue May 15 2007 Ray Strode - 2.18.0-7 +- Don't show iris animation when using compiz (bug 237842) + +* Sun May 6 2007 Matthias Clasen - 2.18.0-6 +- Don't own /usr/share/applications + +* Sat Apr 14 2007 Matthias Clasen - 2.18.0-5 +- Add a dependency on dbus-x11 + +* Thu Apr 12 2007 David Zeuthen - 2.18.0-4 +- start same kind of AT's in session as started in gdm (#229912) + +* Fri Mar 30 2007 Ray Strode - 2.18.0-3 +- remove xdg autostart dir since it's part of filesystem now + +* Wed Mar 21 2007 Ray Strode - 2.18.0-2 +- remove eggcups from default session (bug 233261) + +* Tue Mar 13 2007 Matthias Clasen - 2.18.0-1 +- Update to 2.18.0 + +* Wed Feb 28 2007 Matthias Clasen - 2.17.92-1 +- Update to 2.17.92 + +* Fri Feb 23 2007 Jeremy Katz - 2.17.91-2 +- disable a11y registry timeout so that we don't get the popup with + the livecd (#227214) + +* Tue Feb 13 2007 Matthisa Clasen - 2.17.91-1 +- Update to 2.17.91 + +* Tue Feb 6 2007 Kristian Høgsberg - 2.17.90.1-3 +- Update gnome-session-2.15.90-window-manager.patch to start + gtk-window-decorator instead of gnome-window-decorator for compiz. + + [ Update: the patch is not applied and upstream gnome-session does + the right thing. ] + +* Mon Feb 5 2007 Matthias Clasen - 2.17.90.1-2 +- Require GConf2-gtk for gconf-sanity-check + +* Tue Jan 23 2007 Matthias Clasen - 2.17.90.1-1 +- Update to 2.17.90.1 + +* Sun Jan 21 2007 Matthias Clasen - 2.17.90-1 +- Update to 2.17.90 +- Clean up BuildRequires + +* Wed Jan 10 2007 Matthias Clasen - 2.17.5-1 +- Update to 2.17.5 + +* Mon Nov 27 2006 Ray Strode - 2.17.2-6 +- don't set http_proxy variable if proxy requires password (bug + 217332) + +* Wed Nov 22 2006 Matthias Clasen - 2.17.2-4 +- Own the /usr/share/gnome/wm-properties directory (#216514) + +* Mon Nov 20 2006 Ray Strode - 2.17.2-3 +- don't make gnome.desktop executable (bug 196105) + +* Sat Nov 11 2006 Matthias Clasen - 2.17.2-2 +- Fix gnome-wm for compiz + +* Tue Nov 7 2006 Matthias Clasen - 2.17.2-1 +- Update to 2.17.2 + +* Thu Oct 26 2006 Ray Strode - 2.16.1-2.fc7 +- don't hose users with a stale http_proxy variable if they + use autoconfiguration and uses to use manual configuration. + Patch by Mark McLoughlin (bug 212319) + +* Sat Oct 21 2006 Matthias Clasen - 2.16.1-1 +- Update to 2.16.1 + +* Wed Oct 18 2006 Matthias Clasen - 2.16.0-4 +- Fix scripts according to the packaging guidelines + +* Thu Sep 7 2006 Matthias Clasen - 2.16.0-3.fc6 +- Fix position of icons in the splash screen (#205508) + +* Wed Sep 6 2006 Ray Strode - 2.16.0-2.fc6 +- set http_proxy environment variable from GNOME settings + (bug 190041) + +* Mon Sep 4 2006 Matthias Clasen - 2.16.0-1.fc6 +- Update to 2.16.0 + +* Mon Aug 21 2006 Matthias Clasen - 2.15.92-1.fc6 +- Update to 2.15.92 +- Add %%preun and %%postun scripts + +* Mon Aug 14 2006 Ray Strode - 2.15.91-1.fc6 +- Update to 2.15.91 + +* Sun Aug 13 2006 Ray Strode - 2.15.90-4.fc6 +- fix window manager launching script. Patch from + Tim Vismor (bug 202312) + +* Fri Aug 11 2006 Ray Strode - 2.15.90-3.fc6 +- start gnome-window-decorator and pass "gconf" when invoking + compiz + +* Thu Aug 10 2006 Ray Strode - 2.15.90-2.fc6 +- update patch from 2.15.4-3 to be more session friendly (bug 201473) + +* Fri Aug 4 2006 Matthias Clasen - 2.15.90-1.fc6 +- Update to 2.15.90 + +* Thu Aug 3 2006 Soren Sandmann - 2.15.4-3 +- Add patch to (a) add configuration option for window manager, (b) start the window + manager, and (c) disable splash screen by default. + +* Wed Jul 19 2006 John (J5) Palmieri - 2.15.4-2 +- Add BR for dbus-glib-devel + +* Thu Jul 13 2006 Ray Strode - 2.15.4-1 +- Update to 2.15.4 + +* Wed Jul 12 2006 Jesse Keating - 2.15.1-5.1 +- rebuild + +* Mon Jun 12 2006 Bill Nottingham 2.15.1-5 +- remove obsolete automake14 buildreq + +* Fri Jun 9 2006 Matthias Clasen 2.15.1-4 +- Add more missing BuildRequires + +* Tue Jun 6 2006 Matthias Clasen 2.15.1-3 +- Add BuildRequires: intltool, autoconf, automake + +* Mon Jun 5 2006 Matthias Clasen - 2.15.1-2 +- Require system-logos, not fedora-logos + +* Wed May 10 2006 Matthias Clasen - 2.15.1-1 +- Update to 2.15.1 + +* Mon Apr 10 2006 Matthias Clasen - 2.14.1-2 +- Update to 2.14.1 + +* Mon Mar 13 2006 Matthias Clasen - 2.14.0-1 +- Update to 2.14.0 + +* Thu Mar 09 2006 Ray Strode - 2.13.92-5 +- fix up path creation functions + +* Thu Mar 09 2006 Ray Strode - 2.13.92-4 +- create ~/.config/autostart before trying to migrate + session-manual to it (bug 179602). + +* Mon Mar 06 2006 Ray Strode - 2.13.92-3 +- Patch from Vincent Untz to fix session editing (upstream bug 333641) +- Desensitize buttons for operations that the user isn't allowed + to do (bug 179479). + +* Wed Mar 01 2006 Karsten Hopp 2.13.92-2 +- BuildRequires: gnome-desktop-devel, libX11-devel, libXt-devel + +* Tue Feb 28 2006 Ray Strode - 2.13.92-1 +- Update to 2.13.92 +- Add patch from CVS HEAD to maintain compatibility with + version 2.13.91 + +* Thu Feb 23 2006 Ray Strode - 2.13.91-2 +- take ownership of autostart dir (bug 182335) + +* Mon Feb 13 2006 Matthias Clasen - 2.13.91-1 +- Update to 2.13.91 + +* Fri Feb 10 2006 Jesse Keating - 2.13.90-1.2 +- bump again for double-long bug on ppc(64) + +* Tue Feb 07 2006 Jesse Keating - 2.13.90-1.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Sat Jan 28 2006 Matthias Clasen - 2.13.90-1 +- Update to 2.13.90 + +* Tue Jan 17 2006 Matthias Clasen - 2.13.5-1 +- Update to 2.13.5 + +* Mon Jan 16 2006 Matthias Clasen - 2.13.4-2 +- Disable the fatal-criticals, since it crashes too much + +* Fri Jan 13 2006 Matthias Clasen - 2.13.4-1 +- Update to 2.13.4 + +* Thu Jan 12 2006 Ray Strode - 2.12.0-6 +- Fix screen corruption around splash screen shape (bug 177502) + +* Tue Dec 20 2005 John (J5) Palmieri - 2.12.0-5 +- Handle shaped window for splash screen + +* Fri Dec 09 2005 Jesse Keating +- rebuilt + +* Wed Nov 9 2005 Alexander Larsson - 2.12.0-4 +- Add gnome-user-share patch + +* Tue Nov 8 2005 Ray Strode - 2.12.0-3 +- fix up the dummy client ids to match the id passed initially + passed to the programs in the default session + (broke in last update). + +* Mon Oct 31 2005 Ray Strode - 2.12.0-2 +- remove rhn applet from default session +- s/magicdev/gnome-volume-manager/ + +* Thu Sep 8 2005 Matthias Clasen - 2.12.0-1 +- Update to 2.12.0 + +* Tue Sep 6 2005 Ray Strode - 2.11.91-3 +- Don't take ownership of /usr/share/xsessions (bug 145791). + +* Tue Aug 16 2005 Warren Togami - 2.11.91-2 +- rebuild for new cairo + +* Tue Aug 9 2005 Ray Strode - 2.11.91-1 +- Update to upstream version 2.11.91 (fixes bug 165357). +- drop some patches + +* Thu Apr 18 2005 Ray Strode - 2.10.0-2 +- Install gnome.desktop to /usr/share/xsessions (bug 145791) + +* Thu Mar 17 2005 Ray Strode - 2.10.0-1 +- Update to upstream version 2.10.0 + +* Wed Feb 2 2005 Matthias Clasen 2.9.4-1 +- Update to 2.9.4 + +* Mon Dec 20 2004 Daniel Reed 2.8.0-7 +- rebuild for new libhowl.so.0 library (for GnomeMeeting 1.2) (this was a mistake) + +* Tue Nov 02 2004 Ray Strode 2.8.0-6 +- Rebuild for devel branch + + * Tue Nov 02 2004 Ray Strode 2.8.0-5 +- Convert Tamil translation to UTF8 + (Patch from Lawrence Lim , bug 135351) + +* Fri Oct 08 2004 Ray Strode 2.8.0-4 +- Add g-v-m to default session since it wasn't already (?). +- Remove g-v-m from default session on s390 + +* Thu Oct 07 2004 Ray Strode 2.8.0-3 +- Check for NULL program name when looking for client + match in session. + +* Fri Sep 24 2004 Ray Strode 2.8.0-2 +- Add "Session" item to More Preferences menu + +* Fri Sep 17 2004 Ray Strode 2.8.0-1 +- Update to 2.8.0 +- Remove "Session" item from Preferences menu + +* Fri Sep 03 2004 Ray Strode 2.7.91-2 +- Fix from Federico for infamous hanging splash screen problem. + (http://bugzilla.gnome.org/show_bug.cgi?id=151664) + +* Tue Aug 31 2004 Ray Strode 2.7.91-1 +- Update to 2.7.91 + +* Wed Aug 18 2004 Ray Strode 2.7.4-3 +- Change folder name from "autostart" to more aptly named + "session-upgrades" from suggestion by Colin Walters. +- put non-upstream gconf key in rh_extensions + +* Wed Aug 18 2004 Ray Strode 2.7.4-2 +- Provide drop-a-desktop-file method of adding programs + to the user's session. + +* Fri Jul 30 2004 Ray Strode 2.7.4-1 +- Update to 2.7.4 + +* Wed Jul 14 2004 root - 2.6.0-7 +- Add patch to activate vino based on the "remote_access/enabled" + preference + +* Tue Jun 15 2004 Elliot Lee +- rebuilt + +* Mon Jun 14 2004 Ray Strode 2.6.0-5 +- Prevent having duplicate packages in different collections + +* Mon Jun 14 2004 Ray Strode 2.6.0-4 +- Use desktop-file-install instead of patching + session-properties.desktop. Add X-Red-Hat-Base category. + +* Thu Jun 10 2004 Ray Strode 2.6.0-3 +- Add terminating list delimiter to OnlyShowIn entry of + session-properties.desktop + +* Fri Apr 16 2004 Warren Togami 2.6.0-2 +- #110725 BR automake14 autoconf gettext + +* Wed Mar 31 2004 Mark McLoughlin 2.6.0-1 +- Update to 2.6.0 + +* Wed Mar 10 2004 Mark McLoughlin +- Update to 2.5.91 + +* Tue Feb 24 2004 Mark McLoughlin 2.5.90-1 +- Update to 2.5.90 +- Remove extraneous fontconfig BuildRequires +- Resolve conflicts with the icons and splash-repaint patches + +* Fri Feb 13 2004 Elliot Lee +- rebuilt + +* Mon Jan 26 2004 Alexander Larsson 2.5.3-1 +- Update to 2.5.3 + +* Wed Nov 05 2003 Than Ngo 2.4.0-2 +- don't show gnome-session-properties in KDE (bug #102533) + +* Fri Aug 29 2003 Alexander Larsson 2.3.7-3 +- fix up gnome.desktop location + +* Fri Aug 29 2003 Alexander Larsson 2.3.7-2 +- add gnome.desktop session for new gdm + +* Wed Aug 27 2003 Alexander Larsson 2.3.7-1 +- update to 2.3.7 +- require control-center (#100562) + +* Fri Aug 15 2003 Alexander Larsson 2.3.6.2-1 +- update for gnome 2.3 + +* Sun Aug 10 2003 Elliot Lee 2.2.2-4 +- Rebuild + +* Tue Jul 22 2003 Jonathan Blandford +- at-startup patch to add let at's start + +* Wed Jun 04 2003 Elliot Lee +- rebuilt + +* Tue Jun 3 2003 Jeff Johnson +- add explicit epoch's where needed. + +* Tue May 27 2003 Alexander Larsson 2.2.2-1 +- Update to 2.2.2 +- Add XRandR backport +- Fix up old patches, patch7 was upstream + +* Mon Feb 24 2003 Owen Taylor 2.2.0.2-5 +- Wait for GSD to start before continuing with session + +* Tue Feb 18 2003 Havoc Pennington 2.2.0.2-4 +- repaint proper area of text in splash screen, #84527 + +* Tue Feb 18 2003 Havoc Pennington 2.2.0.2-3 +- change icon for magicdev to one that exists in Bluecurve theme + (part of #84491) + +* Thu Feb 13 2003 Havoc Pennington 2.2.0.2-2 +- load icons from icon theme + +* Wed Feb 5 2003 Havoc Pennington 2.2.0.2-1 +- 2.2.0.2 + +* Tue Feb 4 2003 Jonathan Blandford +- remove extraneous separator. Still ugly. + +* Wed Jan 29 2003 Havoc Pennington +- add icons for the stuff in the default session #81489 + +* Wed Jan 22 2003 Tim Powers +- rebuilt + +* Sat Jan 11 2003 Havoc Pennington +- 2.1.90 +- drop purgedelay patch, as it was increased upstream (though only to 2 minutes instead of 5) + +* Fri Dec 6 2002 Tim Waugh 2.1.2-2 +- Add eggcups to default session. + +* Wed Nov 13 2002 Havoc Pennington +- 2.1.2 + +* Tue Sep 3 2002 Owen Taylor +- Up purge delay for session manager to 5 minutes to avoid problem + with openoffice.org timing out + +* Wed Aug 28 2002 Havoc Pennington +- put gdm session in here, conflict with old gdm +- use DITHER_MAX for dithering to make splash screen look good in 16 + bit + +* Tue Aug 27 2002 Havoc Pennington +- fix missing icons and misaligned text in splash + +* Fri Aug 23 2002 Tim Waugh +- Fix login sound disabling (bug #71664). + +* Wed Aug 14 2002 Havoc Pennington +- put rhn applet in default session + +* Wed Aug 14 2002 Havoc Pennington +- fix the session file, should speed up login a lot +- put magicdev in default session + +* Thu Aug 8 2002 Havoc Pennington +- 2.0.5 with more translations + +* Tue Aug 6 2002 Havoc Pennington +- 2.0.4 +- remove gnome-settings-daemon from default session + +* Wed Jul 31 2002 Havoc Pennington +- 2.0.3 +- remove splash screen, require redhat-artwork instead + +* Wed Jul 24 2002 Owen Taylor +- Set GTK_RC_FILES so we can change the gtk1 theme + +* Tue Jul 16 2002 Havoc Pennington +- pass --with-halt-command=/usr/bin/poweroff + --with-reboot-command=/usr/bin/reboot + +* Tue Jun 25 2002 Owen Taylor +- Version 2.0.1, fixing missing po files + +* Wed Jun 19 2002 Havoc Pennington +- put in new default session with pam-panel-icon +- disable schema install in make install, fixes rebuild failure. + +* Sun Jun 16 2002 Havoc Pennington +- rebuild with new libraries + +* Thu Jun 13 2002 Havoc Pennington +- rebuild in different environment + +* Thu Jun 13 2002 Havoc Pennington +- add fix from Nalin to build require usermode + +* Tue Jun 11 2002 Havoc Pennington +- 2.0.0 + +* Mon Jun 10 2002 Havoc Pennington +- install the schemas, so we get a logout dialog and splash +- put in the splash from 7.3 + +* Sun Jun 09 2002 Havoc Pennington +- rebuild in different environment + +* Sun Jun 09 2002 Havoc Pennington +- rebuild in new environment, require newer gtk2 + +* Sun Jun 9 2002 Havoc Pennington +- remove obsoletes/provides gnome-core + +* Fri Jun 07 2002 Havoc Pennington +- rebuild in different environment + +* Wed Jun 5 2002 Havoc Pennington +- 1.5.21 + +* Sun May 26 2002 Tim Powers +- automated rebuild + +* Tue May 21 2002 Havoc Pennington +- rebuild in different environment + +* Tue May 21 2002 Havoc Pennington +- 1.5.19 +- add more build reqs to chill out build system +- provide gnome-core + +* Fri May 3 2002 Havoc Pennington +- obsolete gnome-core +- 1.5.18 + +* Fri Apr 19 2002 Havoc Pennington +- default to metacity + +* Tue Apr 16 2002 Havoc Pennington +- Initial build. + +