pax_global_header00006660000000000000000000000064116431415050014512gustar00rootroot0000000000000052 comment=767798713a2051d48e909681e810de00f0fb82fc xscreensaver-5.15/000077500000000000000000000000001164314150500141545ustar00rootroot00000000000000xscreensaver-5.15/INSTALL000066400000000000000000000172301164314150500152100ustar00rootroot00000000000000Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.in' is used to create `configure' by a program called `autoconf'. You only need `configure.in' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Operation Controls ================== `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. xscreensaver-5.15/Makefile.in000066400000000000000000000307571164314150500162350ustar00rootroot00000000000000# Makefile.in --- xscreensaver, Copyright (c) 1999-2010 Jamie Zawinski. # the `../configure' script generates `Makefile' from this file. @SET_MAKE@ srcdir = @srcdir@ VPATH = @srcdir@ SHELL = /bin/sh SUBDIRS = utils driver hacks hacks/glx po #SUBDIRS = utils driver hacks hacks/glx SUBDIRS2 = $(SUBDIRS) OSX TARFILES = README README.hacking README.VMS INSTALL \ configure configure.in Makefile.in config.h.in \ config.h-vms install-sh setup.com config.guess aclocal.m4 \ config.sub makevms.com \ intltool-merge.in intltool-extract.in intltool-update.in \ xscreensaver.spec \ xscreensaver.xcodeproj/project.pbxproj TAR = tar MAKE_SUBDIR = for dir in $(SUBDIRS); do (cd $$dir; $(MAKE) $@) || exit 5; done MAKE_SUBDIR2 = for dir in $(SUBDIRS2);do (cd $$dir; $(MAKE) $@) || exit 5; done default:: @$(MAKE_SUBDIR) all:: @$(MAKE_SUBDIR) install:: @$(MAKE_SUBDIR) install-program:: @$(MAKE_SUBDIR) install-man:: @$(MAKE_SUBDIR) install-strip:: @$(MAKE_SUBDIR) uninstall:: @$(MAKE_SUBDIR) uninstall-program:: @$(MAKE_SUBDIR) uninstall-man:: @$(MAKE_SUBDIR) depend:: @$(MAKE_SUBDIR) distdepend:: @$(MAKE) update_spec_version @$(MAKE_SUBDIR2) @cd po ; $(MAKE) update-po TAGS:: tags tags:: @$(MAKE_SUBDIR) clean:: @$(MAKE_SUBDIR2) distclean:: clean -rm -f config.h Makefile config.status config.cache config.log TAGS *~ "#"* intltool-extract intltool-merge intltool-update @$(MAKE_SUBDIR2) dist:: tar # This really makes me sick... tar:: @ \ sh config.status ; \ rm -f configure ; \ $(MAKE) configure ; \ $(MAKE) version-date distdepend ; \ VERS=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^. ]*\).*/\1/p' utils/version.h` ; \ NAME="xscreensaver-$$VERS" ; \ rm -rf $$NAME ; ln -s . $$NAME ; \ FILES= ; \ ADIR=archive/ ; \ for subdir in $(SUBDIRS2) ; do \ d=`pwd` ; \ cd $$subdir ; \ FILES="$$FILES `$(MAKE) echo_tarfiles \ | grep -v '^.*make\[' \ | sed \"s|^|$$subdir/|g;s| | $$subdir/|g\" \ ` "; \ cd $$d ; done ; \ echo creating tar file $$ADIR$$NAME.tar.gz... ; \ export COPYFILE_DISABLE=true ; \ GZIP="-9v" $(TAR) -vchzf $$ADIR$$NAME.tar.gz \ `echo $(TARFILES) $$FILES | sed "s|^|$$NAME/|g; s| | $$NAME/|g" ` ; \ rm $$NAME # This also makes me sick... # autoconf generates a configure script that begins with a very hard to read, # nearly impossible to customize --help blurb. This horrid set of regexps # go through and clean up the help text, by inserting whitespace and ripping # out options we don't use. Odds are good that this will fail with any version # of autoconf other than the ones I've tried (2.12 and 2.13.) # # NOTE: we now require autoconf 2.63 or earlier, because later versions have # the "Expanded-Before-Required" change and I can't make any sense of it. # If someone wants to send me a patch to make configure.in work with 2.64 # or later, feel free. Personally, I can't be bothered. # configure:: autoconf263 autoheader263 @TMP=configure.$$$$ ; \ echo "munging configure's --help message..." ; \ ( perl -e ' \ my $$file=""; \ while (<>) { $$file .= $$_; } \ $$_ = $$file; \ \ s/^(Configuration:)$$/\n$$1\n/m; \ s/^(Directory and file names:)$$/\n$$1\n/m; \ s/^ --sbindir=.*\n//m; \ s/^ --sysconfdir.*\n//m; \ s/^ --sharedstatedir.*\n.*\n//m; \ s/^ --localstatedir.*\n//m; \ s/^ --infodir.*\n//m; \ s/^(Host type:)$$/\n$$1\n/m; \ s/\nFeatures and packages:\n.*library files are in DIR\n/\n/s;\ s/--enable and --with options recognized://m; \ s/\n --with-x .*?(["\n])/$$1/s; \ s/\n(Installation options:\n)/$$1/s; \ \ s/^ --oldincludedir=.*$$/ \ --x-includes=DIR X include files are in DIR\n \ --x-libraries=DIR X library files are in DIR/m; \ \ s@mandir=.\$${prefix}/man.@mandir=\\\$${datadir}/man@; \ \ s@rm -f conftest@rm -rf conftest@g; \ \ print;' \ < configure \ > $$TMP && \ cat $$TMP > configure ) ; \ rm -f $$TMP bump-version:: @ \ SRC=utils/version.h ; \ VERS=`sed -n 's/[^0-9]*\([0-9]\)\.\([0-9][^. ]*\).*/\1 \2/p' $$SRC` ; \ set - $$VERS ; \ MAJOR="$$1"; MINOR="$$2"; \ NEW=`echo $$MINOR + 1 | bc` ; \ NEW=`echo $$NEW | sed 's/^\([0-9]\)$$/0\1/'` ; \ D=`date '+%d-%b-%Y'`; \ ADIR=archive/ ; \ if [ ! -f $${ADIR}xscreensaver-$$MAJOR.$$MINOR.tar.gz ]; then \ echo "WARNING: $${ADIR}xscreensaver-$$MAJOR.$$MINOR.tar.gz does not exist.";\ fi ; \ if [ -f $${ADIR}xscreensaver-$$MAJOR.$$NEW.tar.gz ]; then \ echo "WARNING: $${ADIR}xscreensaver-$$MAJOR.$$NEW.tar.gz already exists.";\ fi ; \ /bin/echo -n "Bumping $$MAJOR.$$MINOR to $$MAJOR.$$NEW ($$D), ok? "; \ read line; \ if [ "x$$line" != "xyes" -a "x$$line" != "xy" ]; then \ exit 1 ; \ fi ; \ TMP=/tmp/bv.$$ ; \ sed -e "s/\([0-9]\.[0-9][0-9]*\)/$$MAJOR.$$NEW/" \ -e "s/\(([0-9][0-9]*-[A-Za-z][a-z][a-z]-[0-9][0-9][0-9]*\))/($$D)/" \ $$SRC > $$TMP ; \ /bin/echo -n "New version and date are "; \ sed -n "s/[^0-9]*\([0-9]\.[0-9][0-9]*\) (\([-A-Za-z0-9]*\)).*/\1, \2./p" \ $$TMP; \ cat $$TMP > $$SRC ; \ rm -f $$TMP; \ echo "overwrote $$SRC"; \ ls -lFd $$SRC bump_version:: bump-version tick-version:: bump-version tick_version:: bump-version version-date:: @ \ SRC=utils/version.h ; \ D=`date '+%d-%b-%Y'`; \ TMP=/tmp/bv.$$ ; \ sed -e "s/([0-9][^()]*)/($$D)/" < $$SRC > $$TMP ; \ /bin/echo -n "Updating date in $$SRC to \"$$D\"... " ; \ if cmp -s $$SRC $$TMP ; then \ echo "unchanged." ; \ else \ cat $$TMP > $$SRC ; \ echo "done." ; \ fi ; \ rm -f $$TMP update_spec_version:: @S=$(srcdir)/xscreensaver.spec ; \ U=$(srcdir)/utils/version.h ; \ VERS=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^. ]*\).*/\1/p' < $$U` ; \ /bin/echo -n "Updating $$S to \"$$VERS\"... " ; \ T=/tmp/xs.$$$$ ; \ sed "s/^\(%define.version[^0-9]*\)\(.*\)/\1$$VERS/" \ < $$S > $$T ; \ if cmp -s $$S $$T ; then \ echo "unchanged." ; \ else \ cat $$T > $$S ; \ echo "done." ; \ fi ; \ rm $$T rpm:: @ \ VERS=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^. ]*\).*/\1/p' utils/version.h` ; \ DIR=`pwd`/rpm_build ; \ ARCH=`rpm --showrc | sed -n 's/^build arch *: //p'` ; \ ADIR=archive/ ; \ TGZ=xscreensaver-$$VERS.tar.gz ; \ if [ ! -f $${ADIR}$$TGZ ]; then \ echo "$${ADIR}$$TGZ does not exist! Did you forget to \`make tar'?" ; \ exit 1 ; \ fi ; \ rm -rf /var/tmp/xscreensaver-$$VERS-root ; \ rm -rf $$DIR ; \ mkdir $$DIR ; \ ( cd $$DIR; mkdir BUILD RPMS RPMS/$$ARCH SOURCES SPECS SRPMS ) ; \ cp -p $${ADIR}$$TGZ $$DIR/SOURCES/ ; \ rpmbuild --define "_topdir $$DIR" \ --define "USE_GL yes" \ -v -ba xscreensaver.spec ; \ echo '' ; \ echo 'RPM build complete' ; \ echo '' ; \ rm -f $$DIR/$$TGZ ; \ rm -rf $$DIR/BUILD/xscreensaver-$$VERS ; \ mv $$DIR/SRPMS/xscreensaver*-$$VERS-*.rpm . ; \ mv $$DIR/RPMS/$$ARCH/xscreensaver*-$$VERS-*.rpm . ; \ rm -rf $$DIR ; \ echo '' ; \ ls -lFG xscreensaver*-$$VERS-*.rpm test-tar:: @ \ VERS=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^. ]*\).*/\1/p' utils/version.h` ; \ D=xscreensaver-$$VERS ; \ ADIR=archive/ ; \ NAME="$${ADIR}$$D.tar.gz" ; \ if [ ! -f $$NAME ]; then \ echo "$$NAME does not exist! Did you forget to \`make tar'?" ; \ exit 1 ; \ fi ; \ \ set -e ; \ set -x ; \ \ if [ -d $$D ]; then \ chmod -R u+w $$D ; \ fi ; \ rm -rf $$D ; \ zcat $${ADIR}$$D.tar.gz | tar -xf - ; \ cd $$D ; \ chmod -R a-w . ; \ chmod u+w . ; \ mkdir BIN ; \ mkdir BIN/motif ; \ mkdir BIN/lesstif ; \ chmod a-w . ; \ \ ( cd BIN/motif ; \ CC=cc ; \ export CC ; \ ../../configure --without-xpm --without-xdbe --without-xshm \ --with-motif=/usr/local/motif ; \ echo --------------------------------------------------------------- ; \ gmake all ; \ ( cd driver; gmake tests ) ; \ echo --------------------------------------------------------------- ); \ \ ( cd BIN/lesstif ; \ CC=cc ; \ export CC ; \ ../../configure --with-motif=/usr/local/lesstif --without-gnome ; \ echo --------------------------------------------------------------- ; \ ( cd utils; gmake all ) ; \ ( cd driver; gmake all ) ; \ echo --------------------------------------------------------------- ); \ \ chmod -R u+w . dmg:: cd OSX ; $(MAKE) release dmg www:: @ \ DEST=$$HOME/www/xscreensaver ; \ VERS=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^. ]*\).*/\1/p' utils/version.h` ; \ HEAD="xscreensaver-$$VERS" ; \ ADIR=archive/ ; \ BNAME="$$HEAD.tar.gz" ; \ NAME="$$ADIR$$BNAME" ; \ DNAME="$$DEST/$$HEAD.tar.gz" ; \ BNAME2="$$HEAD.dmg" ; \ NAME2="$$ADIR$$BNAME2" ; \ DNAME2="$$DEST/$$HEAD.dmg" ; \ \ if [ ! -f $$NAME ]; then \ echo "$$NAME does not exist! Did you forget to \`make tar'?" ; \ exit 1 ; \ fi ; \ if [ ! -f $$NAME2 ]; then \ echo "$$NAME2 does not exist! Did you forget to \`make dmg'?" ; \ exit 1 ; \ fi ; \ chmod a-w $$NAME ; \ if [ -f $$DNAME ]; then \ /bin/echo -n "WARNING: $$DNAME already exists! Overwrite? "; \ read line; \ if [ "x$$line" != "xyes" -a "x$$line" != "xy" ]; then \ exit 1 ; \ fi ; \ fi ; \ if [ -f $$DNAME2 ]; then \ /bin/echo -n "WARNING: $$DNAME2 already exists! Overwrite? "; \ read line; \ if [ "x$$line" != "xyes" -a "x$$line" != "xy" ]; then \ exit 1 ; \ fi ; \ fi ; \ cp -p $$NAME $$DNAME ; \ cp -p $$NAME2 $$DNAME2 ; \ chmod u+w $$DNAME $$DNAME2 ; \ cd $$DEST ; \ \ TMP=/tmp/xd.$$$$ ; \ sed "s/xscreensaver-5\.[0-9][0-9ab]*/$$HEAD/g" download.html > $$TMP ; \ echo '' ; \ diff -U0 download.html $$TMP ; \ echo '' ; \ \ for EXT in tar.gz dmg ; do \ OLDEST=`ls xscreensaver*.$$EXT | head -n 1` ; \ /bin/echo -n "Delete $$DEST/$$OLDEST? "; \ read line; \ if [ "x$$line" = "xyes" -o "x$$line" = "xy" ]; then \ set -x ; \ rm $$OLDEST ; \ cvs remove $$OLDEST ; \ set +x ; \ fi ; \ done ; \ set -x ; \ cvs add -kb $$BNAME $$BNAME2 ; \ cat $$TMP > download.html ; \ rm -f $$TMP ; \ \ (cd ..; $(MAKE) xscreensaver/changelog.html \ xscreensaver/screenshots/index.html ); \ cvs diff -U0 changelog.html ; \ set +x ; \ \ /bin/echo -n "Ok? "; \ read line; \ if [ "x$$line" != "xyes" -a "x$$line" != "xy" ]; then \ exit 1 ; \ fi ; \ \ cvs commit -m "$$VERS" count:: @ \ /bin/echo -n "Current hack count: " ; \ ( ( cd hacks; make -s INSTALL=true install-program install-scripts ) ; \ ( cd hacks/glx; make -s INSTALL=true install-program ) ) | \ grep true | \ grep -v helper | \ grep -v ljlatest | \ wc -l xscreensaver-5.15/OSX/000077500000000000000000000000001164314150500146255ustar00rootroot00000000000000xscreensaver-5.15/OSX/English.lproj/000077500000000000000000000000001164314150500173435ustar00rootroot00000000000000xscreensaver-5.15/OSX/English.lproj/InfoPlist.strings000066400000000000000000000001341164314150500226630ustar00rootroot00000000000000ţ˙/* Localized versions of Info.plist keys */ xscreensaver-5.15/OSX/English.lproj/SaverRunner.nib/000077500000000000000000000000001164314150500223645ustar00rootroot00000000000000xscreensaver-5.15/OSX/English.lproj/SaverRunner.nib/designable.nib000066400000000000000000002343511164314150500251630ustar00rootroot00000000000000 1050 10K549 851 1038.36 461.00 com.apple.InterfaceBuilder.CocoaPlugin 851 com.apple.InterfaceBuilder.CocoaPlugin PluginDependencyRecalculationVersion NSApplication FirstResponder NSApplication MainMenu XScreenSaver 1048576 2147483647 NSImage NSMenuCheckmark NSImage NSMenuMixedState submenuAction: XScreenSaver About XScreenSaver 2147483647 YES YES 1048576 2147483647 YES Preferences… , 1048576 2147483647 YES YES 1048576 2147483647 Services 1048576 2147483647 submenuAction: Services _NSServicesMenu YES YES 1048576 2147483647 Hide XScreenSaver h 1048576 2147483647 Hide Others h 1572864 2147483647 Show All 1048576 2147483647 YES YES 1048576 2147483647 Quit XScreenSaver q 1048576 2147483647 _NSAppleMenu File 1048576 2147483647 submenuAction: File Close w 1048576 2147483647 YES YES 1048576 2147483647 Page Setup… P 1048576 2147483647 Print… p 1048576 2147483647 Edit 1048576 2147483647 submenuAction: Edit Undo z 1048576 2147483647 Redo Z 1048576 2147483647 YES YES 1048576 2147483647 Cut x 1048576 2147483647 Copy c 1048576 2147483647 Paste v 1048576 2147483647 Paste and Match Style V 1572864 2147483647 Delete 1048576 2147483647 Select All a 1048576 2147483647 YES YES 1048576 2147483647 Find 1048576 2147483647 submenuAction: Find Find… f 1048576 2147483647 1 Find Next g 1048576 2147483647 2 Find Previous G 1048576 2147483647 3 Use Selection for Find e 1048576 2147483647 7 Jump to Selection j 1048576 2147483647 Spelling 1048576 2147483647 submenuAction: Spelling Spelling… : 1048576 2147483647 Check Spelling ; 1048576 2147483647 Check Spelling as You Type 1048576 2147483647 Window 1048576 2147483647 submenuAction: Window Minimize m 1048576 2147483647 Zoom 1048576 2147483647 YES YES 1048576 2147483647 Bring All to Front 1048576 2147483647 _NSWindowsMenu Help 1048576 2147483647 submenuAction: Help YES XScreenSaver Help ? 1048576 2147483647 _NSMainMenu SaverRunner performMiniaturize: 37 arrangeInFront: 39 print: 86 runPageLayout: 87 showHelp: 122 terminate: 139 hideOtherApplications: 146 hide: 152 unhideAllApplications: 153 cut: 175 paste: 176 redo: 178 selectAll: 179 undo: 180 copy: 181 showGuessPanel: 188 checkSpelling: 190 toggleContinuousSpellChecking: 192 performClose: 193 delete: 195 performZoom: 198 performFindPanelAction: 199 performFindPanelAction: 200 performFindPanelAction: 201 performFindPanelAction: 202 centerSelectionInVisibleArea: 203 pasteAsPlainText: 205 delegate 207 menubar 209 openPreferences: 212 aboutPanel: 213 0 -2 File's Owner -1 First Responder -3 Application 29 MainMenu 19 24 5 23 92 197 56 57 58 129 131 130 134 136 143 144 145 149 150 196 83 81 73 74 77 78 103 106 111 163 169 156 157 158 160 164 168 159 154 155 161 162 167 171 172 173 174 184 185 187 189 191 204 206 SaverRunner com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin {{72, 1365}, {344, 20}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin {{84, 1182}, {225, 183}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin 213 SaverRunner NSObject id id aboutPanel: id openPreferences: id menubar NSMenu menubar menubar NSMenu IBProjectSource OSX/SaverRunner.h NSApplication NSResponder IBFrameworkSource AppKit.framework/Headers/NSApplication.h NSApplication IBFrameworkSource AppKit.framework/Headers/NSApplicationScripting.h NSApplication IBFrameworkSource AppKit.framework/Headers/NSColorPanel.h NSApplication IBFrameworkSource AppKit.framework/Headers/NSHelpManager.h NSApplication IBFrameworkSource AppKit.framework/Headers/NSPageLayout.h NSBrowser NSControl IBFrameworkSource AppKit.framework/Headers/NSBrowser.h NSControl NSView IBFrameworkSource AppKit.framework/Headers/NSControl.h NSDocument NSObject id id id id id id printDocument: id revertDocumentToSaved: id runPageLayout: id saveDocument: id saveDocumentAs: id saveDocumentTo: id IBFrameworkSource AppKit.framework/Headers/NSDocument.h NSDocument IBFrameworkSource AppKit.framework/Headers/NSDocumentScripting.h NSMatrix NSControl IBFrameworkSource AppKit.framework/Headers/NSMatrix.h NSMenu NSObject IBFrameworkSource AppKit.framework/Headers/NSMenu.h NSMenuItem NSObject IBFrameworkSource AppKit.framework/Headers/NSMenuItem.h NSMovieView NSView IBFrameworkSource AppKit.framework/Headers/NSMovieView.h NSObject IBFrameworkSource AppKit.framework/Headers/NSAccessibility.h NSObject IBFrameworkSource AppKit.framework/Headers/NSAlert.h NSObject IBFrameworkSource AppKit.framework/Headers/NSAnimation.h NSObject NSObject NSObject NSObject NSObject IBFrameworkSource AppKit.framework/Headers/NSComboBox.h NSObject IBFrameworkSource AppKit.framework/Headers/NSComboBoxCell.h NSObject NSObject IBFrameworkSource AppKit.framework/Headers/NSDatePickerCell.h NSObject IBFrameworkSource AppKit.framework/Headers/NSDragging.h NSObject IBFrameworkSource AppKit.framework/Headers/NSDrawer.h NSObject IBFrameworkSource AppKit.framework/Headers/NSFontManager.h NSObject IBFrameworkSource AppKit.framework/Headers/NSFontPanel.h NSObject IBFrameworkSource AppKit.framework/Headers/NSImage.h NSObject IBFrameworkSource AppKit.framework/Headers/NSKeyValueBinding.h NSObject NSObject IBFrameworkSource AppKit.framework/Headers/NSNibLoading.h NSObject IBFrameworkSource AppKit.framework/Headers/NSOutlineView.h NSObject IBFrameworkSource AppKit.framework/Headers/NSPasteboard.h NSObject IBFrameworkSource AppKit.framework/Headers/NSSavePanel.h NSObject IBFrameworkSource AppKit.framework/Headers/NSSound.h NSObject IBFrameworkSource AppKit.framework/Headers/NSSpeechRecognizer.h NSObject IBFrameworkSource AppKit.framework/Headers/NSSpeechSynthesizer.h NSObject IBFrameworkSource AppKit.framework/Headers/NSSplitView.h NSObject IBFrameworkSource AppKit.framework/Headers/NSTabView.h NSObject IBFrameworkSource AppKit.framework/Headers/NSTableView.h NSObject IBFrameworkSource AppKit.framework/Headers/NSText.h NSObject IBFrameworkSource AppKit.framework/Headers/NSTextStorage.h NSObject IBFrameworkSource AppKit.framework/Headers/NSTextView.h NSObject IBFrameworkSource AppKit.framework/Headers/NSTokenField.h NSObject IBFrameworkSource AppKit.framework/Headers/NSTokenFieldCell.h NSObject IBFrameworkSource AppKit.framework/Headers/NSToolbar.h NSObject IBFrameworkSource AppKit.framework/Headers/NSToolbarItem.h NSObject IBFrameworkSource AppKit.framework/Headers/NSView.h NSObject IBFrameworkSource AppKit.framework/Headers/NSWindow.h NSResponder IBFrameworkSource AppKit.framework/Headers/NSInterfaceStyle.h NSResponder NSObject IBFrameworkSource AppKit.framework/Headers/NSResponder.h NSTableView NSControl NSText NSView NSView IBFrameworkSource AppKit.framework/Headers/NSClipView.h NSView IBFrameworkSource AppKit.framework/Headers/NSRulerView.h NSView NSResponder NSWindow NSWindow NSResponder NSWindow IBFrameworkSource AppKit.framework/Headers/NSWindowScripting.h 0 IBCocoaFramework com.apple.InterfaceBuilder.CocoaPlugin.macosx com.apple.InterfaceBuilder.CocoaPlugin.macosx YES ../../xscreensaver.xcodeproj 3 {9, 8} {7, 2} xscreensaver-5.15/OSX/English.lproj/SaverRunner.nib/keyedobjects.nib000066400000000000000000000326131164314150500255360ustar00rootroot00000000000000bplist00Ô X$versionX$objectsY$archiverT$top† Ż}-15<?@DHjruvw{€‰‘¤Ą¦«¬­°´µ¶»ÉŃŇŐâěíîů $-./89BCNWXYZefgknu~€‹”•–ź ˇ¬­®˛µÄÍÎĎŘŮÚĺîďđůúű$%&1<=>BEMXYZdefpqr|}~‡‰”•–š ©Ş«´µ¶żŔËĚÍŇŐÜĺćçđńü&'()*./378=>BCGHLMQRWX]^cdijoptuyz~„‰Ť‘•–šžźŁ¤¨©­®˛ł·¸Ľ˝ÁÂý>z¶·¸ąş»Ľ˝ľżŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâăäĺćçčéęëěíđóN©Ş«¬­®Ż°±˛ł´µ¶·¸ąş»Ľ˝ľżŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙ U$nullß  !"#$%&'()*+,_NSAccessibilityOidsKeys_NSVisibleWindows]NSObjectsKeys_NSClassesValues[NSFrameworkVNSRoot_NSAccessibilityOidsValues]NSClassesKeys\NSOidsValuesV$classYNSNextOidZNSOidsKeys]NSConnections_NSObjectsValues[NSNamesKeys_NSAccessibilityConnectors]NSFontManager]NSNamesValuesz€€ß€€{ |×€ €ă€äy€€ĺŇ./0[NSClassName€€Ň234YNS.string€]NSApplicationŇ6789Z$classnameX$classes_NSMutableStringŁ8:;XNSStringXNSObjectŇ67=>^NSCustomObject˘=;_IBCocoaFrameworkŇABCZNS.objects€ Ň67EF\NSMutableSetŁEG;UNSSetŇAIJ€-ŻKLMNOPQRSTUVWXYZ[\]^_`abcdefghi€ €€§€Ş€¬€®€°€˛€´€¶€¸€ş€Ľ€ľ€Ŕ€Â€Ä€Ć€Č€Ę€Ë€Ě€Î€Ď€Ń€Ó€Ő€×€Ů€Ű€ÝÔklmnop WNSLabel]NSDestinationXNSSource€€ € €Ň./t€€ [SaverRunnerXdelegateŇ67xy_NSNibOutletConnectorŁxz;^NSNibConnectorÔklmn}~p€€¦€€ Ô‚„…†‡[NSMenuItemsVNSNameWNSTitle€€/€Ą€XMainMenuŇAI‹€-ĄŚŤŽŹ€€=€M€Ž€ťÚ’“”•–—™š›ś~žź ˇ˘ŁXNSAction_NSKeyEquivModMaskYNSSubmenuVNSMenuYNSOnImageZNSKeyEquiv]NSMnemonicLoc\NSMixedImage€€€€€!€˙˙˙€€\XScreenSaverPÓ§.¨©Ş^NSResourceName€€€WNSImage_NSMenuCheckmarkŇ67®Ż_NSCustomResource˘®;Ó§.¨˛Ş€€€_NSMenuMixedState^submenuAction:Ô‚·…ąŁ€€/€<€ŇAI˝€-«ľżŔÁÂĂÄĹĆÇČ€€"€#€&€'€0€1€4€6€8€9ו–—™śžź Î˘Đ€€€!€˙˙˙€€ _About XScreenSaverŇ67ÓÔZNSMenuItem˘Ó;Ú™“•–—Öעٜžź ŢŢŕ ]NSIsSeparator\NSIsDisabled€€€€!€ ˙˙˙€Ů™“•–—ע䜞źčŢę뀀€€!€% ˙˙˙€$lPreferences &Q,Ú™“•–—Öע𜞟 ŢŢ÷ €€€€!€ ˙˙˙€Ú’“”•–—™úűüśžź ˘€)€*€€€!€˙˙˙€€(XServices^submenuAction:Ô‚…  €,€/€.€+Ň23 €XServicesŇAI€- Ň67^NSMutableArrayŁ;WNSArray__NSServicesMenuŇ67VNSMenu˘;Ú™“•–—Öעśžź ŢŢ" €€€€!€ ˙˙˙€Ř“•–—™%śžź)*˘,€€€!€3˙˙˙€€2_Hide XScreenSaverQhŘ“•–—™0śžź)5˘7€€€!€3˙˙˙€€5[Hide OthersŘ“•–—™:śžź ?˘A€€€!€˙˙˙€€7XShow AllÚ™“•–—ÖעEśžź ŢŢL €€€€!€ ˙˙˙€Ř“•–—™OśžźST˘V€€€!€;˙˙˙€€:_Quit XScreenSaverQq\_NSAppleMenuÚ’“”•–—™[\]~žź b˘d€?€@€€€!€˙˙˙€€>TFile^submenuAction:Óh…j€B€/€AŇ23m€TFileŇAIp€-¤qrst€C€F€G€JŘ“•–—™v]žźz{˘}€@€€!€E˙˙˙€€DUCloseQwÚ™“•–—Öע‚]žź Ţމ €€@€€!€ ˙˙˙€Ř“•–—™Ś]žź‘˘“€@€€!€I˙˙˙€€HkPage Setup &QPŘ“•–—™—]žź›ś˘ž€@€€!€L˙˙˙€€KfPrint &QpÚ’“”•–—™˘Ł¤~žź ©˘«€O€P€€€!€˙˙˙€€NTEdit^submenuAction:ÓŻ…±€R€/€QŇ23´€TEditŇAI·€-¬¸ąş»Ľ˝ľżŔÁÂĂ€S€V€Y€Z€]€`€c€f€h€k€l€Ř“•–—™Ĺ¤žźÉʢĚ€P€€!€U˙˙˙€€TTUndoQzŘ“•–—™Đ¤žźÔբ׀P€€!€X˙˙˙€€WTRedoQZÚ™“•–—Öעܤžź ŢŢă €€P€€!€ ˙˙˙€Ř“•–—™ć¤žźęë˘í€P€€!€\˙˙˙€€[SCutQxŘ“•–—™ń¤žźőö˘ř€P€€!€_˙˙˙€€^TCopyQcŘ“•–—™ü¤žź˘€P€€!€b˙˙˙€€aUPasteQvŘ“•–—™¤žź  ˘€P€€!€e˙˙˙€€d_Paste and Match StyleQVŘ“•–—™¤žź ˘€P€€!€˙˙˙€€gVDeleteŘ“•–—™¤žź !˘#€P€€!€j˙˙˙€€iZSelect AllQaÚ™“•–—Öע(¤žź ŢŢ/ €€P€€!€ ˙˙˙€Ú’“”•–—™234¤žź 9˘;€n€o€P€€!€˙˙˙€€mTFind^submenuAction:Ó?…A€q€/€pŇ23D€TFindŇAIG€-ĄHIJKL€r€u€x€{€~ŮN“•–—™OP4žźTU˘WUNSTag€o€€!€t˙˙˙€€seFind &QfŮN“•–—™[\4žź`a˘c€o€€!€w˙˙˙€€vYFind NextQgŮN“•–—™gh4žźlm˘o€o€€!€z˙˙˙€€y]Find PreviousQGŮN“•–—™st4žźxy˘{€o€€!€}˙˙˙€€|_Use Selection for FindQeŘ“•–—™4žź„˘†€o€€!€€˙˙˙€€_Jump to SelectionQjÚ’“”•–—™Š‹Ś¤žź ‘˘“€€„€P€€!€˙˙˙€€‚XSpelling^submenuAction:Ó—…“€…€/€‚ŇAIś€-Łťžꀆ€‰€ŚŘ“•–—™ˇŚžźĄ¦˘¨€„€€!€˙˙˙€€‡iSpelling &Q:Ř“•–—™¬Śžź°±˘ł€„€€!€‹˙˙˙€€Š^Check SpellingQ;Ř“•–—™·Śžź Ľ˘ľ€„€€!€˙˙˙€€Ť_Check Spelling as You TypeÚ’“”•–—™ÁÂĂ~žź ȢĘ€€‘€€€!€˙˙˙€€ŹVWindow^submenuAction:Ô‚Î…ĐŃ€“€/€ś€’Ň23Ô€VWindowŇAI×€-¤ŘŮÚŰ€”€—€™€šŘ“•–—™ÝĂžźáâ˘ä€‘€€!€–˙˙˙€€•XMinimizeQmŘ“•–—™čĂžź í˘ď€‘€€!€˙˙˙€€TZoomÚ™“•–—ÖעóĂžź ŢŢú €€‘€€!€ ˙˙˙€Ř“•–—™ýĂžź ˘€‘€€!€˙˙˙€€›_Bring All to Front^_NSWindowsMenuÚ’“”•–—™  ~žź ˘€ź€ €€€!€˙˙˙€€žTHelp^submenuAction:Ó…€ˇ€/€žŇAI€-ˇ€˘Ů™“•–—ע žź"Ţ$%€€ €€!€¤ ˙˙˙€Ł_XScreenSaver HelpQ?[_NSMainMenuWmenubarÓkm+,Ű€©€¨€š_arrangeInFront:Ň6701_NSNibControlConnectorŁ02;^NSNibConnectorÓkm+5Ř€©€«€”_performMiniaturize:Ôklm+:pľ€©€­€ €[aboutPanel:Ókm+@q€©€Ż€C]performClose:Ókm+Es€©€±€G^runPageLayout:Ókm+Jt€©€ł€JVprint:Ókm+O€©€µ€˘YshowHelp:Ôklm+TpŔ€©€·€ €#_openPreferences:Ôklm+Z Ä€©€ą€€1Uhide:Ôklm+` Č€©€»€€9Zterminate:Ôklm+f Ĺ€©€˝€€4_hideOtherApplications:Ôklm+l Ć€©€ż€€6_unhideAllApplications:Ókm+rH€©€Á€r_performFindPanelAction:Ókm+wL€©€Ă€~_centerSelectionInVisibleArea:Ókm+|Ľ€©€Ĺ€]Ucopy:Ókm+¸€©€Ç€SUundo:Ókm+†»€©€É€ZTcut:Ókm+rK€©€Á€{Ókm+rJ€©€Á€xÓkm+“ż€©€Í€fWdelete:Ókm+rI€©€Á€uÓkm+ś˝€©€Đ€`Vpaste:Ókm+ˇŔ€©€Ň€hZselectAll:Ókm+¦ą€©€Ô€VUredo:Ókm+«ť€©€Ö€†_showGuessPanel:Ókm+°ž€©€Ř€‰^checkSpelling:Ókm+µꀩ€Ú€Ś_toggleContinuousSpellChecking:Ókm+şŮ€©€Ü€—\performZoom:Ókm+żľ€©€Ţ€c_pasteAsPlainText:ŇAĂÄ€âŻ8rù؞şÁÂĂ˝ĽŔ»¸ŰźLŔÁÄĂÂÇČĆĹżÚŽŹżHIľ~4ľťŚ¤ŤJstśüqŮ ř]ŚpK€F€‘€V€”€‰€Y€&€'€0€`€]€#€Z€S€š€Ś€˘€~€h€k€1€€l€8€9€6€4€f€™€M€Ž€"€r€u€c€€o€€†€ť€€P€=€x€G€J€€*€C€—€ €ŕ€@€„€ €{Ň./˙€€á]NSApplicationŇ67˘;ŇAĂ€âŻ8]Ź¤ĂŚ¤śśś¤¤ś¤¤ĂŚ 4¤¤ś¤¤śśśś¤Ă~~ś44¤ ÂśŚ~~Ž~4]]ŚÂ]Ă ŤĂ 4€@€Ž€P€‘€„€P€€€€P€P€€P€P€‘€„€ €o€P€P€€P€P€€€€€P€‘€€€€o€o€P€€l€€„€€€M€€o€@€@€€'€@€‘€ť€€=€€€oŇAĂ@€âŻ9rù؞şÁÂĂ˝Ľ»Ŕ¸ŰźżŔÁÄĂÂÇČĆ ĹLÚŽŹżHIľ~4ľťŚ¤ŤJstśüqŮ ř]ŚpK€F€‘€V€”€‰€Y€&€'€0€`€]€Z€#€S€š€Ś€˘€f€h€k€1€€l€8€9€6€€4€~€™€M€Ž€"€r€u€c€€o€€†€ť€€P€=€x€G€J€€*€C€—€ €ŕ€@€„€ €{ŇAĂ|€âŻ9}~€‚„…†‡‰Š‹ŚŤŽŹ‘’“”•–—™š›śťžź ‡˘Ł¤Ą¦§¨©Ş«¬­®Ż°±˛łtµ€ć€ç€č€é€ę€ë€ě€í€î€ď€đ€ń€ň€ó€ô€ő€ö€÷€ř€ů€ú€ű€ü€ý€ţ€˙ €    € [Separator-7]Menu (Window)_Menu Item (Redo)_Menu Item (Minimize)_Menu Item (Check Spelling)YSeparator[Separator-3_Menu Item (Services)[Separator-4_Menu Item (Paste)_Menu Item (Copy)_Menu Item (Cut)oMenu Item (Preferences &)_Menu Item (Undo)_Menu Item (Bring All to Front)_&Menu Item (Check Spelling as You Type)_Menu Item (XScreenSaver Help)_Menu Item (Delete)_Menu Item (Select All)[Separator-1_Menu Item (Hide XScreenSaver)_Menu Item (Spelling)_Menu Item (Find)[Separator-5_Menu Item (Quit XScreenSaver)_Menu Item (Show All)\File's Owner_Menu Item (Hide Others)_Menu Item (Jump to Selection)[Separator-2_Menu Item (Edit)_Menu Item (Window)[Separator-6oMenu Item (Find &)_Menu Item (Find Next)_!Menu Item (Paste and Match Style)[Menu (Find)_Menu Item (About XScreenSaver)oMenu Item (Spelling &)_Menu Item (Help)_Menu Item (XScreenSaver)[Menu (Edit)_Menu Item (File)_Menu Item (Find Previous)oMenu Item (Page Setup &)oMenu Item (Print &)_Menu (XScreenSaver)_Menu (Services)_Menu Item (Close)_Menu Item (Zoom)[Menu (Help)[Application[Menu (File)_Menu (Spelling)_"Menu Item (Use Selection for Find)ŇAĂď€â ŇAĂň€â ŇAĂő€âŻXPgTr4\eŘ_ş˝hOcźľs[ÂVÁUXqĹČŤŹĂI`d]ZąKĽa MÁüLbŔĂŚK^Ĥǜ]pŽHQľžťżř J»ŮtŔRøżSÂÚ~WŰYŚĆifNL€®€Ů€¶€F€o€Ć€Ő€”€Ë€Y€`€Ű€¬€Ń€Ś€€G€Ä€'€ş€k€¸€ľ€C€˘€4€9€=€Ž€‘€u€Ě€Ó€Č€Â€V€ €]€Î€ €§€&€*€~€ť€Ď€h€€„€{€Ę€1€P€8€€@€ €M€r€°€c€‰€†€"€ŕ€€x€Z€—€J€#€˛€0€S€f€´€l€™€€Ľ€š€Ŕ€€6€Ý€×€Ş€ŇAĂP€âŻXQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚„…†‡‰Š‹ŚŤŽŹ‘’“”•–—™š›śťžź ˇ˘Ł¤Ą¦§¨!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxÁŔÔJź´ĽÉś«ĆŐłż:Mµ‹®™Io‘S§Ă˛ŻË­ĎťČj'Ź‚›g°¬¸ąˇĘ†©•9QÎŁšWĚ˝»Ä˙˙˙˙˙˙˙ýÖ˘ ĹNVž¤z¨\’Ç8–Íľ%ŃŇAI€- ŇAĂ€â ŇAĂ €â Ň67  ^NSIBObjectData˘ ;_NSKeyedArchiverŃ]IB.objectdata€"+5:?=Cލ»ÉŰçî %,6AOam‰—Ą¨Ş¬Ż±ł¶ąĽżÁÄĆČĘÍĎŃÚćčęóý˙ !*<CLU^mr…Ž™›śĄ˛ążČĘ  !#%')+-/13579;=?ACEGIZbpy{}ŠŚŽšŁ¬ĂĘŮęěîđň "$&/8:EGIKMOx•ꦰ»ÉÖŘÝßáăĺçěîđýţ  (:CV[hjlnˇŁĄ§©˛´ËÍĎŃÓŐ×ŮŰÝßáţ     $ - 8 = f t Š Ś Ž ‘ ’ — ™ ľ Ŕ Ĺ Ç É Ë Í Î Ó Ő î đ   " $ & ( ) * / 1 Z \ a c e g i k p r t } Ś ť ź ˇ Ł Ą ® ° ą Â Ä Ĺ Î Ý ä ě ţ    < > C E G I K L M R T u z | ~ € ‚ ‡ ‰ ‹ ź ˇ Â Ç É Ë Í Ď Ô Ö Ř ä        $ M O T V X Z \ ] ^ c e † ‹ Ť Ź ‘ “ š ś ° ˛ ż č ę ď ń ó ő ÷ ů ţ    # % ' ) 2 4 9 B D M O Q S U v { }  Š Ś ’ ” ˝ ż Ä Ć Č Ę Ě Í Î Ó Ő ö ű ý ˙  #%FKMOQSXZ\ik”–›ťźˇŁĄŞ¬®łÂĎŃÓŐŢŕĺîđ   !BGIKMOTVX]_€…‡‰‹Ť’”–›ťĆČÍĎŃÓŐÖ×ÜŢ˙  <ACEGINPRWYz…‡ŚŽ–ąľŔÂÄĆËÍĎçé  'HMOQSUZ\^ik”–›ťźˇŁ¤ĄŞ¬Ő×ÜŢŕâäćëíďô!&/1<>@BDFkqsxz|~€…‡‰”–»˝ÂÄĆČĘĎŃÓÝß  *,QSXZ\^`egi‚„ĄŞ¬®°˛·ą»ĎŃúü  ,9;=?HJQSUWx}…ŠŚŽˇŁÄÉËÍĎŃÖŘÚéë  "?hjoqsuwy~€‚‰©«­Ż±şĽĂĚÎ×ŮŰÝß   !BGIKMOTVX]†ŤŹ‘“•–—śžżÄĆČĘĚŃÓŐęů"$)+-/138:<AP]_aclnqsšźˇŁĄ§¨­ŻĂĹŃŮćčęěţ&5BDFH^oqsuw’”–¤±łµ·ĆÓŐ×Ůŕíďńóý):<>@BHY[]_al}…žŻ±łµ·ĐÝßáăý  0=?ACIVXZ\boqsuz‡‰‹Ťšśž ­Ż±ł»ČĘĚÎŰÝßáčő÷ůű,.02DQSUWfsuwyš§©«­şÇÉËÍáęě_acegikmoqsuwy{}…‡‰‹ŤŹ‘“•—™›ťźˇŁĄ§©«­Ż±łµ·ą»˝żÁĂĹÇÉËÍĎŘÚÜęóřvxz|~€‚„†ŠŚŽ’”–šśž ˘¤¦¨Ş¬®°˛´¶¸şĽľŔÂÄĆČĘĚÎĐŇÔÖŘÚÜŢŕâäćďńfhjlnprtvxz|~€‚„†ŠŚŽ’”–šśž ˘¤¦¨Ş¬®°˛´¶¸şĽľŔÂÄĆČĘĚÎĐŇÔÖŘáăXZ\^`bdfhjlnprtvxz|~€‚„†ŠŚŹ’•›žˇ¤§Ş¬Ż˛µ¸»ľÁÄÇĘÍĐÓÖŮÜßâäçó   + H R ^ u • ¨ ş í!!!!J!j!!!¤!Ä!Ű!î!ú""1">"X"x"„"—"¬"¸"Ý"ő##%#F#s#†#ˇ#­#Ŕ#Ü$ $4$J$\$p$$Ź$›$§$ą$Ţ$ç$é$ę$ó$ő$ö$˙%%´%¶%¸%ş%Ľ%ľ%Ŕ%Â%Ä%Ć%Č%Ę%Ě%Î%Đ%Ň%Ô%Ö%Ř%Ú%Ü%Ţ%ŕ%â%ä%ć%č%ę%ě%î%đ%ň%ô%ö%ř%ú%ü%ţ&&&&&& & &&&&&&&&&& &"&$&&&(&*&,&.&0&2&4&6&8&:&<&>&@&B&D&F&H&J&L&N&P&R&T&V&X&Z&\&^&`&b&d&m&o'"'%'('+'.'1'4'7':'='@'C'F'I'L'O'R'U'X'['^'a'd'g'j'm'p's'v'y'|''‚'…''‹'Ž'‘'”'—'š'ť' 'Ł'¦'©'¬'Ż'˛'µ'¸'»'ľ'Á'Ä'Ç'Ę'Í'Đ'Ó'Ö'Ů'Ü'ß'â'ĺ'č'ë'î'ń'ô'÷'ú'ý(((( ( (((((((!($('(*(,(.(0(2(4(6(8(:(<(>(@(B(D(F(H(J(L(N(P(R(T(V(X(Z(\(^(`(b(d(f(h(j(l(n(p(r(t(v(x(z(|(~(€(‚(„(†((Š(Ś(Ž((’(”(–((š(ś(ž( (˘(¤(¦(¨(Ş(ł(µ(·(ą(»(˝(ż(Á(Ă(Ĺ(Ç(É(Ë(Í(Ď(Ń(Ó(Ő(×(Ů(Ű(Ý(ß(á(ę(ě(í(ö(ř(ů))))))")4)9)G)Ixscreensaver-5.15/OSX/InvertedSlider.h000066400000000000000000000013351164314150500177230ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 2006 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * This is a subclass of NSSlider that is flipped horizontally: * the high value is on the left and the low value is on the right. */ #import @interface InvertedSlider : NSSlider { } @end xscreensaver-5.15/OSX/InvertedSlider.m000066400000000000000000000046551164314150500177400ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 2006-2010 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * This is a subclass of NSSlider that is flipped horizontally: * the high value is on the left and the low value is on the right. */ #import "InvertedSlider.h" @implementation InvertedSlider -(double) transformValue:(double) value { double low = [self minValue]; double high = [self maxValue]; double range = high - low; double off = value - low; double trans = low + (range - off); // NSLog (@" ... %.1f -> %.1f [%.1f - %.1f]", value, trans, low, high); return trans; } -(double) doubleValue; { return [self transformValue:[super doubleValue]]; } -(void) setDoubleValue:(double)v { return [super setDoubleValue:[self transformValue:v]]; } /* Implement all accessor methods in terms of "doubleValue" above. */ -(float) floatValue; { return (float) [self doubleValue]; } -(int) intValue; { return (int) [self doubleValue]; } -(id) objectValue; { return [NSNumber numberWithDouble:[self doubleValue]]; } -(NSString *) stringValue; { return [NSString stringWithFormat:@"%f", [self floatValue]]; } - (NSAttributedString *)attributedStringValue; { // #### "Build and Analyze" says this leaks. Unsure whether this is true. return [[NSAttributedString alloc] initWithString:[self stringValue]]; } /* Implment all setter methods in terms of "setDoubleValue", above. */ -(void) setFloatValue:(float)v { [self setDoubleValue:(double)v]; } -(void) setIntValue:(int)v { [self setDoubleValue:(double)v]; } -(void) setObjectValue:(id )v { NSAssert2((v == nil) || [(NSObject *) v respondsToSelector:@selector(doubleValue)], @"argument %@ to %s does not respond to doubleValue", v, __PRETTY_FUNCTION__); [self setDoubleValue:[((NSNumber *) v) doubleValue]]; } -(void) setStringValue:(NSString *)v { [self setDoubleValue:[v doubleValue]]; } -(void) setAttributedStringValue:(NSAttributedString *)v { [self setStringValue:[v string]]; } @end xscreensaver-5.15/OSX/Makefile000066400000000000000000000116201164314150500162650ustar00rootroot00000000000000# XScreenSaver for MacOS X, Copyright (c) 2006-2011 by Jamie Zawinski. XCODE_TARGET = "All Savers" XCODEBUILD=/Developer/usr/bin/xcodebuild default: release all: debug release clean: -rm -rf build # cd ..; $(XCODEBUILD) -target $(XCODE_TARGET) clean distclean: -rm -f config.status config.cache config.log \ *.bak *.rej TAGS *~ "#"* -rm -rf autom4te*.cache -rm -rf build distdepend:: update_plist_version debug: distdepend cd ..; $(XCODEBUILD) -target $(XCODE_TARGET) -configuration Debug build release:: distdepend cd ..; $(XCODEBUILD) -target $(XCODE_TARGET) -configuration Release build release:: check_versions release:: sign sign: @for f in build/Release/*.{saver,app} ; do \ codesign -vfs 'Jamie Zawinski' $$f ; \ done check_versions: @\ SRC=../utils/version.h ; \ V=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^. ]*\).*/\1/p' $$SRC` ; \ DIR=build/Release ; \ RESULT=0 ; \ for S in $$DIR/*.{saver,app} ; do \ for P in $$S/Contents/Info.plist ; do \ V2=`perl -0000 -n -e \ 'm@CFBundleVersion\s*(.*?)@si \ && print $$1' < $$P` ; \ if [ "$$V2" != "$$V" ] ; then \ echo "Wrong version: $$S ($$V2)" ; \ RESULT=1 ; \ fi ; \ done ; \ done ; \ if [ "$$RESULT" = 0 ]; then echo "Versions match ($$V2)" ; fi ; \ exit $$RESULT echo_tarfiles: @echo `find . \ \( \( -name '.??*' -o -name build -o -name CVS -o -name '*~*' \ -o -name 'jwz.*' \) \ -prune \) \ -o -type f -print \ | sed 's@^\./@@' \ | sort` update_plist_version: @ \ SRC=../utils/version.h ; \ V=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^. ]*\).*/\1/p' $$SRC` ; \ T=/tmp/xs.$$$$ ; \ for S in XScreenSaver.plist SaverRunner.plist ; do \ /bin/echo -n "Updating version number in $$S to \"$$V\"... " ; \ KEYS="CFBundleVersion|CFBundleShortVersionString" ; \ perl -0777 -pne \ "s@(($$KEYS)\s*)[^<>]+()@\$${1}$$V\$${3}@g" \ < $$S > $$T ; \ if cmp -s $$S $$T ; then \ echo "unchanged." ; \ else \ cat $$T > $$S ; \ echo "done." ; \ fi ; \ done ; \ rm $$T # -format UDBZ saves 4% (~1.2 MB) over UDZO. dmg:: distdepend check_versions @ \ set -e ; \ SRC=../utils/version.h ; \ V=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^. ]*\).*/\1/p' $$SRC` ; \ TMPDIR="build" ; \ SRC="build/Release" ; \ EXTRAS=../../xdaliclock/OSX/build/Release/*.saver ; \ BASE="xscreensaver-$$V" ; \ OUTDIR="../archive" ; \ DMG="$$OUTDIR/$$BASE.dmg" ; \ TMPDMG="$$TMPDIR/tmp.dmg" ; \ VOLNAME="XScreenSaver $$V" ; \ STAGE="$$TMPDIR/dmg_stage" ; \ rm -f "$$DMG" ; \ rm -rf "$$STAGE" ; \ echo + mkdir "$$STAGE" ; \ mkdir "$$STAGE" ; \ \ retired=`perl -0 -ne \ 's/\\\\\\n//g; m/^RETIRED_EXES\s*=\s*(.*)$$/m && print "$$1\n"' \ ../hacks/Makefile.in ; \ perl -0 -ne \ 's/\\\\\\n//g; m/^RETIRED_GL_EXES\s*=\s*(.*)$$/m && print "$$1\n"' \ ../hacks/glx/Makefile.in ; \ echo savertester` ; \ \ for f in $$SRC/*.{saver,app} $$EXTRAS ; do \ ok=yes ; \ ff=`echo $$f | perl -e '$$_=<>; s@^.*/(.*)\..*$$@\L$$1@; print'`; \ for r in $$retired ; do \ if [ "$$ff" = "$$r" ]; then ok=no ; fi ; \ done ; \ if [ "$$ok" = yes ]; then \ echo + cp -pr "$$f" "$$STAGE/" ; \ cp -pr "$$f" "$$STAGE/" ; \ else \ echo skipping "$$f" ; \ fi ; \ done ; \ set -x ; \ cp -p bindist.rtf "$$STAGE/ READ ME.rtf" ; \ cp -p bindist-DS_Store "$$STAGE/.DS_Store" ; \ cp -p XScreenSaverDMG.icns "$$STAGE/.VolumeIcon.icns" ; \ /Developer/Tools/SetFile -a C "$$STAGE" ; \ /Developer/Tools/SetFile -a E "$$STAGE/ READ ME.rtf" ; \ seticon -d ../../xdaliclock/OSX/daliclockSaver.icns $$STAGE/DaliClock.saver;\ hdiutil makehybrid -quiet -ov -hfs -hfs-volume-name "$$VOLNAME" \ -hfs-openfolder "$$STAGE" "$$STAGE" -o "$$TMPDMG" ; \ rm -rf "$$STAGE" ; \ hdiutil convert -quiet -ov -format UDBZ -imagekey zlib-level=9 \ "$$TMPDMG" -o "$$DMG" ; \ rm -f "$$TMPDMG" ; \ ls -ldhgF "$$DMG" # Adding this is cute: # hdiutil internet-enable -yes -quiet "$$DMG" # but means that nobody will ever see the display settings I used! # When finder copies the .dmg to a folder, it doesn't preserve them. xscreensaver-5.15/OSX/PrefsReader.h000066400000000000000000000027121164314150500172020ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 2006 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* This implements the substrate of the xscreensaver preferences code: It does this by writing defaults to, and reading values from, the NSUserDefaultsController (and ScreenSaverDefaults/NSUserDefaults) and thereby reading the preferences that may have been edited by the UI (XScreenSaverConfigSheet). */ #import #import "jwxyz.h" @interface PrefsReader : NSObject { NSUserDefaultsController *userDefaultsController; NSUserDefaults *userDefaults; // this is actually a 'ScreenSaverDefaults' } - (id) initWithName: (NSString *) name xrmKeys: (const XrmOptionDescRec *) opts defaults: (const char * const *) defs; - (NSUserDefaultsController *) userDefaultsController; - (char *) getStringResource: (const char *) name; - (double) getFloatResource: (const char *) name; - (int) getIntegerResource: (const char *) name; - (BOOL) getBooleanResource: (const char *) name; @end xscreensaver-5.15/OSX/PrefsReader.m000066400000000000000000000235121164314150500172100ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 2006-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* This implements the substrate of the xscreensaver preferences code: It does this by writing defaults to, and reading values from, the NSUserDefaultsController (and ScreenSaverDefaults/NSUserDefaults) and thereby reading the preferences that may have been edited by the UI (XScreenSaverConfigSheet). */ #import #import "PrefsReader.h" #import "screenhackI.h" @implementation PrefsReader /* Converts an array of "key:value" strings to an NSDictionary. */ - (NSDictionary *) defaultsToDict: (const char * const *) defs { NSDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:20]; while (*defs) { char *line = strdup (*defs); char *key, *val; key = line; while (*key == '.' || *key == '*' || *key == ' ' || *key == '\t') key++; val = key; while (*val && *val != ':') val++; if (*val != ':') abort(); *val++ = 0; while (*val == ' ' || *val == '\t') val++; int L = strlen(val); while (L > 0 && (val[L-1] == ' ' || val[L-1] == '\t')) val[--L] = 0; // When storing into preferences, look at the default string and // decide whether it's a boolean, int, float, or string, and store // an object of the appropriate type in the prefs. // NSString *nskey = [NSString stringWithCString:key encoding:NSUTF8StringEncoding]; NSObject *nsval; int dd; double ff; char cc; if (!strcasecmp (val, "true") || !strcasecmp (val, "yes")) nsval = [NSNumber numberWithBool:YES]; else if (!strcasecmp (val, "false") || !strcasecmp (val, "no")) nsval = [NSNumber numberWithBool:NO]; else if (1 == sscanf (val, " %d %c", &dd, &cc)) nsval = [NSNumber numberWithInt:dd]; else if (1 == sscanf (val, " %lf %c", &ff, &cc)) nsval = [NSNumber numberWithDouble:ff]; else nsval = [NSString stringWithCString:val encoding:NSUTF8StringEncoding]; // NSLog (@"default: \"%@\" = \"%@\" [%@]", nskey, nsval, [nsval class]); [dict setValue:nsval forKey:nskey]; free (line); defs++; } return dict; } /* Initialize the Cocoa preferences database: - sets the default preferences values from the 'defaults' array; - binds 'self' to each preference as an observer; - ensures that nothing is mentioned in 'options' and not in 'defaults'; - ensures that nothing is mentioned in 'defaults' and not in 'options'. */ - (void) registerXrmKeys: (const XrmOptionDescRec *) opts defaults: (const char * const *) defs { // Store the contents of 'defaults' into the real preferences database. NSDictionary *defsdict = [self defaultsToDict:defs]; [userDefaults registerDefaults:defsdict]; userDefaultsController = [[NSUserDefaultsController alloc] initWithDefaults:userDefaults initialValues:defsdict]; NSDictionary *optsdict = [NSMutableDictionary dictionaryWithCapacity:20]; while (opts[0].option) { //const char *option = opts->option; const char *resource = opts->specifier; while (*resource == '.' || *resource == '*') resource++; NSString *nsresource = [NSString stringWithCString:resource encoding:NSUTF8StringEncoding]; // make sure there's no resource mentioned in options and not defaults. if (![defsdict objectForKey:nsresource]) { if (! (!strcmp(resource, "font") || // don't warn about these !strcmp(resource, "textLiteral") || !strcmp(resource, "textFile") || !strcmp(resource, "textURL") || !strcmp(resource, "textProgram") || !strcmp(resource, "imageDirectory"))) NSLog (@"warning: \"%s\" is in options but not defaults", resource); } [optsdict setValue:nsresource forKey:nsresource]; opts++; } #if 0 // make sure there's no resource mentioned in defaults and not options. NSEnumerator *enumerator = [defsdict keyEnumerator]; NSString *key; while ((key = [enumerator nextObject])) { if (! [optsdict objectForKey:key]) if (! ([key isEqualToString:@"foreground"] || // don't warn about these [key isEqualToString:@"background"] || [key isEqualToString:@"Background"] || [key isEqualToString:@"geometry"] || [key isEqualToString:@"font"] || [key isEqualToString:@"dontClearRoot"] || // fps.c settings [key isEqualToString:@"fpsSolid"] || [key isEqualToString:@"fpsTop"] || [key isEqualToString:@"titleFont"] || // analogtv.c settings [key isEqualToString:@"TVBrightness"] || [key isEqualToString:@"TVColor"] || [key isEqualToString:@"TVContrast"] || [key isEqualToString:@"TVTint"] )) NSLog (@"warning: \"%@\" is in defaults but not options", key); } #endif /* 0 */ } - (NSUserDefaultsController *) userDefaultsController { NSAssert(userDefaultsController, @"userDefaultsController uninitialized"); return userDefaultsController; } - (NSObject *) getObjectResource: (const char *) name { while (1) { NSString *key = [NSString stringWithCString:name encoding:NSUTF8StringEncoding]; NSObject *obj = [userDefaults objectForKey:key]; if (obj) return obj; // If key is "foo.bar.baz", check "foo.bar.baz", "bar.baz", and "baz". // const char *dot = strchr (name, '.'); if (dot && dot[1]) name = dot + 1; else return nil; } } - (char *) getStringResource: (const char *) name { NSObject *o = [self getObjectResource:name]; //NSLog(@"%s = %@",name,o); if (o == nil) { if (! (!strcmp(name, "eraseMode") || // erase.c // xlockmore.c reads all of these whether used or not... !strcmp(name, "right3d") || !strcmp(name, "left3d") || !strcmp(name, "both3d") || !strcmp(name, "none3d") || !strcmp(name, "font") || !strcmp(name, "labelFont") || // grabclient.c !strcmp(name, "titleFont") || !strcmp(name, "fpsFont") || // fps.c !strcmp(name, "foreground") || // fps.c !strcmp(name, "background") )) NSLog(@"warning: no preference \"%s\" [string]", name); return NULL; } if (! [o isKindOfClass:[NSString class]]) { NSLog(@"asked for %s as a string, but it is a %@", name, [o class]); o = [(NSNumber *) o stringValue]; } NSString *os = (NSString *) o; char *result = strdup ([os cStringUsingEncoding:NSUTF8StringEncoding]); // Kludge: if the string is surrounded with single-quotes, remove them. // This happens when the .xml file says things like arg="-foo 'bar baz'" if (result[0] == '\'' && result[strlen(result)-1] == '\'') { result[strlen(result)-1] = 0; strcpy (result, result+1); } // Kludge: assume that any string that begins with "~" and has a "/" // anywhere in it should be expanded as if it is a pathname. if (result[0] == '~' && strchr (result, '/')) { os = [NSString stringWithCString:result encoding:NSUTF8StringEncoding]; free (result); result = strdup ([[os stringByExpandingTildeInPath] cStringUsingEncoding:NSUTF8StringEncoding]); } return result; } - (double) getFloatResource: (const char *) name { NSObject *o = [self getObjectResource:name]; if (o == nil) { // xlockmore.c reads all of these whether used or not... if (! (!strcmp(name, "cycles") || !strcmp(name, "size") || !strcmp(name, "use3d") || !strcmp(name, "delta3d") || !strcmp(name, "wireframe") || !strcmp(name, "showFPS") || !strcmp(name, "fpsSolid") || !strcmp(name, "fpsTop") || !strcmp(name, "mono") || !strcmp(name, "count") || !strcmp(name, "ncolors") || !strcmp(name, "doFPS") || // fps.c !strcmp(name, "eraseSeconds") // erase.c )) NSLog(@"warning: no preference \"%s\" [float]", name); return 0.0; } if ([o isKindOfClass:[NSString class]]) { return [(NSString *) o doubleValue]; } else if ([o isKindOfClass:[NSNumber class]]) { return [(NSNumber *) o doubleValue]; } else { NSAssert2(0, @"%s = \"%@\" but should have been an NSNumber", name, o); abort(); } } - (int) getIntegerResource: (const char *) name { // Sliders might store float values for integral resources; round them. float v = [self getFloatResource:name]; int i = (int) (v + (v < 0 ? -0.5 : 0.5)); // ignore sign or -1 rounds to 0 // if (i != v) NSLog(@"%s: rounded %.3f to %d", name, v, i); return i; } - (BOOL) getBooleanResource: (const char *) name { int n = [self getIntegerResource:name]; if (n == 0) return NO; else if (n == 1) return YES; else { NSAssert2(0, @"%s = %d but should have been 0 or 1", name, n); abort(); } } - (id) initWithName: (NSString *) name xrmKeys: (const XrmOptionDescRec *) opts defaults: (const char * const *) defs { self = [self init]; if (!self) return nil; userDefaults = [ScreenSaverDefaults defaultsForModuleWithName:name]; [self registerXrmKeys:opts defaults:defs]; return self; } - (void) dealloc { [userDefaultsController release]; [super dealloc]; } @end xscreensaver-5.15/OSX/README000066400000000000000000000005671164314150500155150ustar00rootroot00000000000000 This directory contains the MacOS-specific code for building a Cocoa version of xscreensaver without using X11. To build it, just type "make", or use the included XCode project. The executables will show up in the "build/Release/" and/or "build/Debug/" directories. To build these programs, XCode 2.4 or later is required. To run them, MacOS 10.4.0 or later is required. xscreensaver-5.15/OSX/SaverRunner.h000066400000000000000000000015211164314150500172470ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 2006-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #import #import @interface SaverRunner : NSObject { NSString *saverDir; NSArray *saverNames; NSArray *windows; NSBundle *saverBundle; IBOutlet NSMenu *menubar; } - (IBAction) openPreferences: (id)sender; - (IBAction) aboutPanel: (id)sender; @end xscreensaver-5.15/OSX/SaverRunner.icns000066400000000000000000001721321164314150500177630ustar00rootroot00000000000000icnsôZic09ôF jP ‡ ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2c˙O˙Q2˙R ˙\ PXX`XX`XX`XXXPPX˙dKakadu-v5.2.1˙ óY˙“ĎĂAŔ1¦óR!ĄŤ‡ &z1˘Ü¬ĄĂ5…-R†s©fůCg yĹ]˛ŇŐX¸×iy «Ü)p7@÷ ŹkU }ťŃ¸ćĺK 7‡ČUµżĂâ Už*$~«ĹY˘TĹÉ*5ŢAŐ§†Ý8şĚ=N‹ ŕ>Hć»…ÖµuA›í¸ŢćS–ÇNÍ%B‹Cők•É‘ĹńüXmě#QNB~ś@,ę$±8˘ćţ0± S$ôô¬LÔy5đkťç‹čĹĂ6®Ăś íäř.şĐ"ŽđHĺ Ű8ĺ‡ĐM0ŘW3zbzŤ¶i5Ö&ö±SódÇݸ”Ҕޞő>ĎŰÓývn ďSk&ćĂęT‚şL0›Á’ăoÜPűů»ń/ŕ‡DAÍîÂěfVl(z›7¬(ČM6YušŁJ8V1á¤`Ĺ0’)óĹT¶Ů ™%EqĐEýĚ"Lť†!ŮQ+˝ßťˇ°Č7‚}yN+6ŰFCrb,ń¦K4OÝô¦‘ľćÇxZşDTĄ˛ĚŐ¦ˇW”Ţ]7~5™4Ś{łîÖDő_uubN¤Č_OŐč6RZ4óúÂŻIö`Z€Äü!Śť(Ł5nήĂ/uóD14Ź|iôY˝ÁŃ“ó\ YĎÂÍ1§ŠňN¬ÄÎN"•Sń/¸§Ĺůqyq‚YątľW$ąEă4ŮďIŁ6›d|Ý ˙[ř@ ĄK÷…ćFŠľI.¤Ź¶ Đ9sPb†ÔfffK‚z»h˘ë1Z·m^A­ PO)Ă(Ĥç9Î’ŞŔpé˛S‹÷^D+ÍH¬§Ţ‰ł‰Ę™@çśšmŰĎCţčN0´ěqÇf;CpĹíč–ŤßR˙€ţŠkŽgęčGI˛ 6¸ˇ0łZAŃXŤ,Ő ő;˘ÇáhĎÎćáY€oźÜ6Ě2ę#mCöyeF©+¶5Ĺ)Ś\…QšA»9€Ş=é€+¤€şQw#öÝÜ&Ó˙7âúy8ŤťÎ!R&äŇö'R”ĽŁązŹĺűG‘ínŮś¶ŠŹJŠëcĺG<>·‘¸ŠzÄŮ8›6ÍNa—V†ĚÍCöŔĆ®`źLËeD˘éŔĄłÎµŐ>mëÜâ0¶iâĆ[qpĎ\őE!O/öÍçĽű“ŕöhÓ>ć<öĄ5q‰l,›X±ĄÁÖ?cj6iý sj(Ĺm‘PŞ<‹CyÖŹyůzyÓX!ŇK@ѬtŇé|2‹¬ éŢć§…ó§‹6™vCń*hڻӻ>(ĺÜ”dŞC,Ň'`™őĚZnč=“X&˙#E¦áß*4éP‰ćýÜbNNďß Žy¨Îw#ÔYSÝ*™TŹČ·.ţ˛d<.Ľ ž%±›ňTK.5›á7~˛řÇi_˘tuřUŻPÇŕŻÔ—Ú°WxÇBç7T§3Ě`DbčZ¦ů—Ę®.b`ÇÝO1Ţůuiôżj×÷D|A—°1ÜÁD*ó:.0ŇÜ3ŔJ7ŮĽîŮČXźÖŻAµĺ!× S[@‰IĄrÍľÝ*Ç, ë¶ţkw<‚e»ŇďuŐWČ÷G“Lz˝-­M\’mLsüľíˇwß”!7ÜAaó$>gGĚ;v!†qáNŽ‚)I€v°ŃĘ.R÷凅ÍĄčl-%ed˙>Kd¸™9IŐ˙UŃ,§ˇýÉö‹|éXXLsúŢîĽy¦÷YPOhăˇiÚĎÍî?Č~@z#únx}(î•…âl›Řö´Í‡„}=8ŰgÎ XÝ6üW$ÂÖČC˝ÓŐW!čŚéţřfW¸yY;ú>â» ­Śž®–ü™â-9ądÇáÄśDB^!č¨o‹A·fň†@Ţ;?wúFěxtŢiů5´¤u_},óRóbLáGŕîĆËECłTŞŮ{"'.Ŕ~Ňm,Ž9(i)f& čŕ-t 0Ţ 1-?Rđ[¨Ň­În~ý<â% ˘»§§[&áĎÍW`őą:}, Ëűń;ň.ŕa™Ńŕ4Q[ą­‚*ţÂ"ŔŮţ‰Ľě@†łbŽŞký4m…đ…/»^żg~źÔĹóm@O–4Z±!˛Ń„|-?ĎN ăŚäżď E–UÇ_!,¸ĹkOČđpŞÁ¶y»]F†myóĎúÎá…ÍmĚűÍ‹Çŕą‚„?Ŕ*zŁ•rŢö{ W#ü ůjIZ&©›¶ýC˘7‡+ł˝ 5.šCl’© Aá®+~ç_Ś˝46ÇmcĂ:¶—aF¸«ó!yt4I“¬·Éa€ľ}‹ČTBiL$ý”ÍÂgg*㥚 űĄ‘ÄX 5k“ľ;&69ő d…«[“üđŮŇú ŠOšžl*9ŇŁ«˘Í.®k¤JÝć9­ĺ™l`Ň0‰[mďomZ*›öő˙TGíL±-çřˇU@‘Ź 3 Ő~mzťËNx´Iżâ™F, h 6ďÖš–ĎĎŁéůôžŘƨqD“´DŘŔ>Ľ\I3ŢŔjmÍ%Ćţ¤şâđÎű4މe›˛ZôÎĆ»¨¶ !¸ĐNÔ+/Ç2óŽ×âĘ=“%©k±WÉÖÄúˇřENřŇW۱îŃXýŃ4ŮeY‹@“ŇA9«¶č†ÄěyAB4ć®ţB}őOk·¸ewë[HH¦ÍC:¨ŔȬĘŰş›¶ţŕzLK¦Yk( U›n{4ů„¨ Aí˘Ř^H@ç‚ĎIŔoë±Äżńáe…9"¬lčĎčz;™éĹ9áF÷€Hă9|`Đl—XFY3EBEÝ´3N®ä顳/°6Ľ¤ŁED¤dÍę¦uÍ©•Łť`őrźuš±ô˘z"đýş¨o«{›śłóÔ(â™ĐôřącpőşŃB_Pȉ’ .ţ Oďrś\ďŘĚ’şhMŁHőgĚÖůYJ|=Ľ›>‘j1N.¸ęÍža_ĺ«ű€ Q#Âz#E˙IY7µHQ»Ą74Íü›Ë9˙$Wë—Tv˙q:&Ţ Ĺ6­…‰҉[9”'ŹDG´ŇR·¦PŚ5Ř_A&\ NÄđ5˛|/ËŻâ~NĆwÁůÉ˝-úP}Çg„oÔńď×9›ůü"`Ő5ôŹŞŕ{‹S'Ďĺ˛WŹŤ(’‹ĹJżňx± [ż÷Đ``jĽKż˙ÜzŤ‡G”ű+űË­čÔé.öăZs1ÂŻwőÁJ‹Ĺ2űś‹†]ű˘±žuó˙b:n±ұiŇtW˝şPé»áyw^-cTďŐő¤txc­|]Ň„  a‘«aë)+Ţm2ş(UĘ÷PT뉳°Ş˙%Ă€É ďĐ âb˙mź† o,ýXÝô}Źă´ńČě‡ăŞÝźż¶áeôZĘŁOŽ»čAůťÜ$’ÇŠÂś‡če—wĘü6¶üż—(2XóÁ4{ĄˇĆK{ń¶g˛)»ÁÁ>3i¶Xi†”Ä&X ˇ^7ăńTć[z,|âŕP ¸ęZôÜ ČMç4jmůzc„5ěŮü>E .ĽD4ůtÁ¦;˙b“żÓ`[MłęŻ÷˙Łç»lQR¶2 ΛÜő¨×ŕש Š`óóĐOkj i°5oŔţPPĽ˘*ş’ ™b¶ĄöśQX*ä9a˛Ś>ó ŹĆŃ/·®l=Ő Pm6-§Ľ“EôDaş9úv^Q»=‡ź‰©¬×ćq¶|3+‚Eź±›ŞÚ‚˙=Ź/ĐĽÚżo,€J·ůýóa»Üߤ{ăĽ˙ŹË¶\Ϭ#Ź#z×j9¶0ýNćTâ ěČi«ýŘ_ÄěT3J8e^EF„ĎÔ±ş˘)É0'o¬ýŢě}/¶­Ĺ´ă@ĎĚż!ÓG"‚ĂZ'”¦!q™Ě,‘Ň\܉K˘Ó9¸Ęö)ŽĆ̤¶čQjˇZ?ĘŃ[% ńŤÎ• ť._‰Ýl±çđ†$Żp'c­S ;E–4Űá’˘šďkčoři'U 3łźl/aŢ]FR5:8nč(ö‘‡×šI ÎłăěéŹH§“ć%ĺđy˝6€)fíűaő¬Ţö˛ÂyÖ¸~®ˇ#’Öٸ7ń6ÎęIŮĺöNMĐ#tĎ%šé±wţSa& ?yâĺ7‡¨¤ôń—Ůńµ9ú!aҲÉbîZD„Ľ˙Qz?Ň~e%´Á‡nE«|:pńüb?Á*˘l Öł NŘKá©›Iů]7ÔčT‹xĘ»·”W­ZĽŮ¦Q”¬xEiĐÔ°˙ ¨ü­ś@0ôtíí¦¤rŮF‰'ŔĚŮČ'Ň«Żó™č€—ż pŽę|n¤¤ł5X™©Ţ.'Ć[ ©iîeﺀŮJĚűT ż893»±[b§€¦~m®©S%· Ŕe..\ńIuĹ_­aŞŇł~)Î`ÁŐ´ëĂi™ ¬ą{Žfdy ˙$JŃ5kß=»·˘/WµxŤÉTz(ÇáoAőV°ü-0›©–|B Ć”. `ŞšdAř·ĄŞ#jÄőÍĹ_ ?[ŇŚőéů‚}ëOÁ¦¤4¬‹jáÉ˙uárJ%ł_Q©D¶Üľ*Śľ‹ ůG\Ć.ň*Ůç›'ŢénŤMî5xmGßć/4Ň›Ěe‹žÔTAŚţx!¬Ź(’zřHĺ$ÄŘ5ąĹÔ=˘­ř—b´ î,e„¬é´ćV;[ÇbĘ)ôí¶˘8Š;śIž*/łëĆY߬éÝ@d[«Aµ˝ďÜş»Oۤ/s[~áěOF.Ž'Xę¤ 0pc{3Ł­›9ޱIĽt›ćJ(HQx»mw¬·Oŕ1’ Hü†w×3&:u–g°ą©…Ö۲Ű'çA/WŘBéűéQ=ޞ\zúp A61şÝŠĄäZ‡“ž)F´<5őlćďćEÁe˙NÂk†ĽžŐ –ďB-˙jŽP™şžůb Żs2ÝCdş¸W𱞀2K„ą`”eQˇ=XSČ$}´üy ˙>­Ăfâjôq,}źą[Uo¤ÇŚ€Ŕ¬" ŻúÚÜť=Ąäv„íĐ@-¨×+bŕśU™Ó¸Âhíź ŠźŹłGbąŰF1즸ň©Ź“1ĽÉcCF_u®]8-ĽŽľçA =&µ,~˘xÍž¬ŹV-bĄ¸_¸ëE~ńBŰPŐ['e0á^¬­«Ţ ć–{˘ęúç{eŠÇ.íą|Ôeg¶iŞň«!ĄC|Ë)é@yÚgÜeḊÖXěU°:ň_˘7 dÎß&đşăÇjÖÄôńTĽ§\a«oó«†ćrÇá¤qřh$? RP¤ÄÍ*xň0ë´ZXťfľĎ1]¬ÍÚ=_¨ĆěÉŃ<čéüüLŇpĺ*z{puť:‚âkşq©ńš¦ű‘,çĺÜüŃŔ{(sĺ3^VW_+Äę‘´¬Ŕ×áň®őÇń.Ą©ÉĂŢŁČÉ^3ńkď˛vĺĎಒ·Ş\˛h żę±‰Ęňť9FXşĚhÓ`7a‘Śž«šBˉ›”-»‚Yî;™M`˛ăŽ<^#ÖĺďpľŻ%2KÎ}Ô,čžç.L}Bă=ĽĐĆ’ ó!%Ü\$Ôź ně˙A©?WiË ys­ć˱ąéA —-Čxh&Ťz¦Ľ–a~Ő'?’Ę΀§Ô˝FÂŰŇßę!étďvîĹ[ŕS^ ¤$Üš±ůš(żCĆß_ Ͳü#€ş‰Ę–­Ţ–\Rô–˙?jt§=yz¬7Ć•–"âT(ĄÎ/´!Í ěs<,—ĄëŇîo«ëŠóĄÂWž“lĚ q§¦›¨ď:2bd[bţŞđýćŹ?é÷Ŕ´t `Ziٱ<1¬µűŇl_ń#›­7Jdú˙áw<'JţD«ý"ĽçÎ’Űf ŃśU÷Hô—EÔ€®Ĺôó­F¶€r†śgŔRŕŰvŞ"JˇŔsř ‰ś­ĘŰTGŽń|FGŁR~úŕ5§$&ĽN•Ś\™±p"Źd"ř1ŐvĎ×4ŤĐ`Í•ś9ŕ>D:QµXk¨Pucň!«7r˝Üë>cÚ„´@áDŻfsfzv:“Ż- ů~† ?mäFkM]ÔĐ­çyĐđrĽŰ'e|íK'ťnů<~´§^ăQôr€ĎçîúăÔž&g©aPf§îĘoĚpGŮPŔĎÍăRď•Uľ}Čt|<9éÔŁî6sĺGŠŔšĂŇĐ —ťO~Śr>;ŕ.<­ jcFĘXEHĢËŘ]6Z;±$»‡°†Śö®9fř¦śČÍ|<qüOĘ3śR”sĘă´ ·]őCg":—´Ü-仆ťŮ¦-ŤŚS©y¬ĂřY­ąéěv€ä©ŮĆddv@ }«ODTDóí’ŐćÓÓţ®áŢÇáWĎÎť‡ŕŇL@=—ŘѮ݅O)ŕ°ż˙SâŇ$ÖGŔðpü±Ń‚›9ÎÁ´)ÇYpqŞé?2)5€CŃg©¸RŐoOnU(ĂdÖ2EĄŞ˝qâ M™’rôżĘ!Ó.$éőŘ_…řCÁH“‰`x<îCĺJŕýć1oIoGÇyŹłĄgŚ`}J-Đ@\JšjéHKvű¶áń– >ż×‚žŰbCŠđ¤e»/‰~Áf,0d…Šá˝úNCTűŁYŕȬ,Öţ€ţ~~ŇG5@áÎÔĚDś—ÇÇ !î^˝_śrÉÎ"łŮöEóŐšř$H +ůSxuCě$!j©4Lpß„h-d ŇtúÓp—T ť3%čŐµŤ´^ yŃ!YÓĎBśÜ†"ă ŻĘçś´ä´F˝R|ŇżÎhş”é»ÍÉ=EwN§ 9pJV>=űé3`LÍÁŐwĚf­Vh¶ Ě´s™Mß}«ĄS±= ë›ŕ^e´ß¨ú°ů ş±.$SZ/F8`Eź~ŇQ÷Ę•±ĺi~˙g?š{–÷ŰΧdăžäĄ&~d†0őů ĎĎŐÚ~~›ńů÷d‚jqwŤv}12‘ěs´›XV•Ś?ľ'Z•áfD GÉŃÖÁUÎ2ŕĘ9»7\eu.̆döýłł*é"Fo´cÚŇŢÚ/úÚ1Vf¶«Őn/ęţŘé  ›F[üŮâ“v"NUuOčü/M8uWpN†@Éôé÷#˛Ąę\=U"G1c-ę6íÉ€LL¦ÜKJ«Ćőé Éé8f >bÔĚO{2"kËĎË·uš\ę€Č0ꎞG‹ ç¨JY«ßSŔc n—¬%iŮ–ŁÁeWř…śÍěŚZZ™Yˇ–$?ô#â9Q-ńr÷·„iW6n%_ú)=ÖŘ!HpÂĹ[ëŔ ÄkĄb™F şEÁµVżi›BG«đЉ´ ÷űÉüysx‡PKw¤Ş+ŔU™ „źčŠŽK„ó BŐ˘/«±€óúôó{÷v€ŮéfL+«ő—geű8m2MxAéśš‚·˙6Yň¤D¸,D÷˝ö %˛,ÂqäĚŻO'Ţ!ó%DŁ&NU;Ô7ŠTČSäo™­…_÷>ś8ý5Ľäýf҇:6oRř×ć˙‚-Ü#™d°Ú‰ć‰ŁäÖÝĹ/޶Łţt1Č3“¤PĐ•ţÇ”ˇM^sţgŮéďpî˛;ßQ~]¸L¤QŞ]˝«BrŰíuśŁ¦TÔűyľjűb7Ś8)‹ĹD~ń‹pnę2Ł×‰x·ĚF!¶oÁ¬ĺ2§{˙-ć[ŇB@”vaA€a´†`mŘĽ¸Ő,Ŕ6ľZ,˝YÓëX†Řë%ô»Uěk§‡V¶4ÂÎ7ÁÍ˙ujgF§»Ą3Í(.ŠńŘO4ßDrn&K8'F•‰¦Áń÷Ď­M”W_ `\čŇD­6őďľWçËΛýÔNQŽJ·ŕ,6|9WO–)OŽ™Ţ”QÄRg3úŠ«šŤ)0D©Š#âó˙–Ůë#crîý5űöꬖHG›:–{\djM8|(Ýęnϲł‰04˘$¶x÷cÜ2*G)@-(\3vfö4…ńR"``ŕ:`ţ)«üŚňŢ´r|SW0Yî}UŹđ {Nţ"\NäŕśĆń˝˙e—{Äcg˙(­ţhoâ6 :wA´‰ DŻ>îá[ÄŠîś )ř'îEŚtńÂGcu6jđýoÓ ŕî'°@X¸‹Ĺr“ä‘"–eą]&ĺŇ$×éŠóëű%8†‹Žm \q-™ęÝQˇ›~ ăHŘś}ŠcŰ}ˇ6†őb˙wÓűdö-óp>Š®Ľ6xő5Ëyß®oką^N9ńź ”«-’ÚŰf4V!Ç'>‘´Mz±¸‘zeŃ6zŮŤ€®~•:YvJŕÝúW DeW­ŮÓÔÄĆ‹KÍÇňWC-ÄxtŁďKdÇá(#‰Î#ů$‚@–ľ0Ó’ 1='Ô¨۱ͱcč¨Ţţ®¸Jú˘ç|dc íŇĆZ<‘s”GŻű´ŹEIJďź°Fa #ë,ťSÂ]˛7á äy˘yŽžĄ}ő>žXâŽD¶KÉťÇ8¤d›X,čŮÇ"â»Ň¶N`mĎÇËŠ,ru, îVFůZĽKä•°‰%¶`1µŮł»|‰âq!\ˇĂŁal +±©I”•A¤‰ÎŤóOÄŔY!Ž»eňąĂÝ–â»HldB9ëĄ=ďPDZ™Ŕ¸˘÷çá´+2Ö/$µć4ë7X”ôĂŻKb=Ăm:§Źëâ_Gz隥©8°ć)ŐŢԚݟŹŰóţ’×·kŔdBák¦¤zÉƬXăů 5ŽŐě±Ö‘kŐ¶jnI$’GwëlRä’I$’]¬xů„vMf ýŃž‘bí±A&o c ĺŠńí¶—Ł•f"AdOĂ91śµęŤŠyűnŰm¶Űm¶ßĚ>2˘'ĘjÁ)•e'¸F1äź°ő5›bm˛Ötl¬ó…aWĽĹöË'OU»yYą…J!˛÷ ĚzĺčěT^L͉*fă–”5¸53ňĆG¶×…†rÚ?“MŰ““ŰK¬łë 1M Ű’CT>uŢôlÖŰ’I$›Ąu›=$ßćzLŕ#7`Đ4…j×A˘K3BƲ˙'ť¬ ďםµŠŠ! Ć÷ř1´ŁSČáU)9Îsśç9ĚF#J,¤ç9Îsśç9Îsśç9Ď9›ÔŕËőZYěťlÇHĽD©kŃ\˙Ú3m˘Tß(‹ÂŃŐU “}ä]Ji+±Ďśś¤…bk;ËöÓ[…ůţ>ŐĆ˝ 2Ýí± š‰5«âţéőŮţ ŤĚ®CŰ’Hß9Í(f.đŘAmÉ$’Čěő­Ľłbß$\eăp»Ľ\¶°p ×­â•´7Řk­,ńż¶_0PŻĚ•:Tőiĺř&ę ţró2ŽmYo¦pוOY$Łś ę\đ&m×*A¨8ď©d .W»LĐŻQmo+ő5čhQ_uJ-°ŰŔű|ŘG „±>†éĽKI´Vťç1Pž)©µďZ ęÝË[łwȰă¬ňź__ŽäwŚ*Ú¨ăţu»HětĂrI$7#ÖGf\Éä’I$‘§ˇýă` »D˘L4U€‡}hQźnSs›‰@YC"_gĺ$O4Flö"ßP/lÖBôĂ.oW AnCô1Şi›śt)Qď˘SކŰm0|ö«Ďa¶Űm¶Űm·'Í#m‚!‰G äď µť^WjJТ±ŽVNĹ›wĚ..6ÇK‡Ş€ 4ÇĚ–Ä CťQDm‹c€îł$'(SUDIÓ š{‘ŢUޱça¦á‰ř?»ÄĎÂt¤˛,LçĐOŠőŘ-š 9bJwq@L¤‘T‡6•2ţ’×ω#ďV9‹č»®\öÓéô2mÖ)…*ďŕóîQˇsw!>pl79W*Ć’Tďl,6#R4őÁ ůŕE;QHż ąä‰|ÍÓDî­JżýŔ5çÎVP¶Č÷‡[ Fw¶DG1ŇţGçIŮ’Ëű˛ŁlV YÇŤŹµôŃkÔ@/śčżsÇ`ę*¦öiXÎěĆ3Řż‰ő‘÷=aN^°śž/Q‹U¨‹·ŹžÝźű%A*+Źß’âtÎ6 lů‰ę”ŕŰ)^ýe<čJxévżŢâRîĹN{ß~Â]´˛V^Qx ~Ú2Í-ôăÎŕ4˘B—/™H9^ÖUíĎ_Ąܫͤnčö#:§®é_éŹ cGŮDÔYĘĽŘN»lő‹¬63¤îŕÖøXŔłŔ“Z$[0UV>XĎ y›‚Ż˘Ö÷ðÝÍšLͬâ%¸óôٯҬ ăXY0Ł´]?q!&CµŕG–d4ŕ vCa‰–W%ÔČş] "”gşu…ĎőŃ˝^í–Óß2ű@Ç€‰ŰŤÚˡn[yž‡•×HëMhHóôY»‹ąYçZSBD/÷Dĺ«‹&)ÂÚT'“­öy~ií&Ňct¶y A˝`4šr†‡Ý0}x‘aHQÍ­_‚†ôbW$ńo°L‰Ď‰RŁöĚ%ň@ł…^Ĺđ ¤PšÜY’üęŚéšq …ĄX¦·ěěâŰű°L¶šHW´q١>I‰…´'őŹQĂéŃe”_ôK.úŁ´«W›Řő ö5Ő -6R]ÉŞś‰s“0ÍÁŃ ÚÖŢ€´zÄţ·:Ź×7ęÎw´Ă›VÁ(˙L;a€ULIÂs AAB ˦ÂLk`̆ÍgF:9hÉWĹF;Mü¤­;:Ż#5eŔ ÁAnË:W0śPÄ<ŢAžšĹ ¦č—˝)źâęs˛u;’‘ažkXdÍZřP<©:©$©Ďůhź"©rQíB*ý<^ënFýď*QŐĂÄőŐ¨®E{ďýz‰ĺ ß ʰ é"»˙( ŞőAç(Úű¬ńŤ­ÔĽdÇáż‘řlÔ? ÎÔ=4Q?kmoĹĄ˝b¶Ňîĺ|3I÷ĎX€)rď;ę/†10GÔŁpĆĐŢ”ÖŐ ¦îđ<ZwőÇDźö­NÚd’R72Fń°:Ŕ¬C`ĄPÎl8[QüiÄçVBÚ˙L„ůd€‹4mŽd62*óš™®m7úČLő°Á˝mlOÂxÝ_íů+ŤÓŰ®&®(‘±#†µý%ťť'ąîČ>‹Ń…7ËGA7¶Ţ7Ä:ýO=_.Y{ŕQ—uGĎěj2DnŮXĄ bôÄËŽĄ©Á’a9•fšŇŃč/ŤŰż%-¨˝†FăĺA ˙iŻ!É®˛Á1ź“X'Í„cD™ţŹśż°,\ V‡{ßÓ7őŰŘ©DXIÇż"|ë¤8Ú< qÖö©†–ÄŠ-4ń{; ž×=±p$KElţłÓĄ#,8úĽ X©˛™¸@‚ëhßâWťţĽ,^iŰL6Awťeř„ś%MşwbúźMÝ"»tDŇQ´cµ† ,ťéÔîDi°çdşÂ†Ě~X]±Ły—áĆ?i<ł¨Ş›JĽĄ"``Łk®lŤs63pŕ¬ęąŹŢ˛ü˛˛ĂáÓĂTăÎZµ›T6ĂĘ›;âęL;´íő H˝¨T9˛@§*`˘š^)|âö÷{î×*?î5Ű•›;éäŤŢ\“3 łŤjÁ™IĐS¸ĺ,Ł]ü®óĽíäi˛˛ßĺ@‹^FĚYťxŕć–» Ě$Z ŮęŻ^č6’Uxж¬#żő’í›ć(čŚůěçGáİčę¬kŔ®i’!¶S1˙Dy9 ‡*ĂŞ*’ÝAď2§ĚŚĆ&čhâđˇ:,"×âíe6_ëĆČđ=Ö0$ˇBLţL’„aŁ 4grľ%ý“®2V.Z‘pW ]ËË]!·ë Šr¦mý»—¸gÍNĘŃExúW9ą/¸gťô¸\|ĐĘß%.÷;Eh"ĆďÉ í>ČGřűh›á[îŚđ¬´”%‘»ŇŰPĺCĎU§1z© JG?›ďŁŘlÁx`=ĺ0aÚŮ!YňMb"EőĂĎÇ‚·ýłŮ!ѡR hŐŽ:Ű•ÖQˇľI ‚Çmá}GÖß“śÉÎďQÁ.‡*\Yy…âé§ĽnwŤ`ÉPŔŁX:Ră¶“9ŕĹËŮ?-!éfař'Ŕ(ÇáŐ\~!Cđę Áľö/x2HˇŞĄ=üL‰*|/ŘPJq3´KKL !^ĽpîB™ŃX5¨¶ł·$ \ŻÄH¦zôĽ}"` cdâÖZȜͨúdI{-ŮxM@ŽM~%ÔËM~EĆŔ¶ś°ž‚FŢÂŞjšŘGšv Uzéd98®×ýŔę,żŕI7|{dhCÔ_ Ěz 4oŠŚhvíńË|s@ČăŘřľžČ^něÇ=«+u7ďRşW&`ěaŘĚŐ>•919ř…”űĚ…NÔÔş¬ÍöJ˝AxˇĽ¨¤rľPđôIzćń1@ë¸ę~ŇŐĺ= ™ţ’‘rK˘Ú’…~ń7ۡťčOn, ĐňË+Şďj¤SP75’ΙŃř9ţĚĹĺöCú°Ń]GŚWí%ĚŽż%¸­-Vb}¸ô—śťt“îŤ[ή㺩 éŻ;´ĺű|N€/ a•ŐĆbÓšo¸-ą…K:Ŕş :č^}®´y±\şşĆŔ›ţąČ`ł¦śWM]PŻă“ňm^ ŞćÜGÓîJŽęUµÍĺą7a9ÜýĚĘłŻ‘›yIĽOI†´–1ţ]YzÍŻ¸ŔYw[ĂN‘ˇy¦ÍgěZľâÜöőJ ŞŇ’Śn¸–4Śž°ĄÜ`´ă–±;ĎÚWě`ËkďâlwFfÔX5 Ľi·żg(#˘K¬ /ç!şň\Ąd:l:=”i#´ÁÓq‡ hľäŇĘ:±üč’Gég>çëĐ$ťŤˇçFQçĎŞš"10›[ó3o÷Áw˙tČI€c¨¤ÍŘ=Ý ą-š3ĂMÂA0Úв¶ ąÓI†$‘ĐhhĂN(U`"o°öŽć“15*=YîČ#¸şýő<íÂčRb÷b†7Öc0qÂľŰ +ň‚J4‰Ć|Ą-/üŤÁř^őÜáüŐlÍ1—1qW *0«9;ĹÖ1§¬”B˙PFM7ţÜrÄ&Ošŕ• Rő€°`0[G©cřb´×zä‡ÖčśëZUN±qôzó0lł„ Ť$´®ň‹‘áY ~äŢŠN9Ź8‡ĚŔŞˇ0»áŚŽ|ÉÖFëâˇwĐžDţ]ž>ßî‰i4Ń•¬ÄÎ}»a‚đĚŢsîUőž¸l¶Ó”´ZśÄ?J›“\Žr)_ X?"žÎp„ÄŁvř9ŞSiČR¦šMÉÚ9µ,Šá Š…•0˝Ćµ8ŐÇwݱŘdĆ $żw¦~•śÂMciĽń„T¤Q­ŕqąźÁěŮŢ#h©5sŐŻô‡î`ý#Ěľ±g˝ĐF.™ţśŤčfÍcw~oŇäH7xSc  M?ţ$ŰâéäĎ"±)qîj´|qk‡Žl-đę a#”đTźNy^%]pơ hö"Ăť8~ç•–#ö`JhDp—ŐěN˘€2dtQgBáăínRÁŢ~1 SçőČĚ÷Čő%h77Ľó{ăCś‹Ďáf1‘]ČFËűLٸ] TDja" NawýµŃ?%b;égëkNc@oeYö ›–źÇöŞe@”Fśđ$őÂC …LĄ™Ů#±1•ŢÚrÓâôěĄ €÷˙`ţÝÇpůĎő·Ĺ‰đ. -ěŽP9ŇLÎ\·Ç€ ôž’¤\ĘPĆę·™UL(˛ľ’mčgöuEňË(Ѭ5&_dɶĺ†Éz:#îźP(ř÷*X„«®~ýÖěFǶń#ěSzäDT o Ë!űtě„™(‹őކ4eÜŃ1ý2{óáăď}|pi'Í6p DUÇś`ˇoŠ ¦3ĹöýT%‡JŘÁSirÄ Â‡ĄÉ ¦' ,,*2gwĐťđ›ŘZ.čŇy€÷Ł'M€Y¦¨›NĄçI‡=ż±î˙Rtz(!ťČƬN˝ßůJzp;Ňz4+µ˝üHŃÓ)”• nŞé@őťCx.»\©Ů$[řó Á™ďÎs÷˛gVqµÚňgÔń•˘ŔŽđN“eĺWź˛)Í©—ąß¬*†ËäEĆ*-·¤EX»‘#ŘÜev9‚'Gě8qôÁs›[ą/RęÜů@vË&_9¤3ü.„ťĆűâ'®-}Öěę ”Ů› |›ůÓ 9˛[őbĚ[ĺèh¸Íy-<7°Á.Ú8béJ5§Ć\笤nwZ*Ňéö•×OĽ˙-k`` F¶ĐMdÝŹöɲ3z眕—°‘÷ź2ćć-rúÂë­˘HBµňBd—ďń©?[g29śŘĆM\wŻä®r„]?Ä}&űĎG 7ş†*ü7Ě®„q=]4˙ůz´o‰Ő·ŕHn„<˝ /·y޸H‹›š*úUDčqŮ÷ľ·ń\ôzޤ9ŁYżžłä”8‚weΚ5›Săá*“:ŁżŹę1«)–M°pa]EЇLŠ™łNHďb?}:ťč=žÎL+ĂL4ă]Ý:äUís1’ÇüˇożúFÉS‚ť°¬™óte[§x:ĹČH“1sňQ6ĘPˇdNřsÉýá–.މ4U÷f×~¤N?ŢŰíi·ĄŃÄŐš+âřÎ~“ń Wĺű42ň¶ŰiR™ěŮë u+bulłd´&9‡•Ţ )k¨JwíśŔ7Ĺ\Ú|7¨~uś˘°Íq]!Đ= —ű+TĂ& ÉgÇ:Ö™`x,Mc›¬łŽ¨ŕ#ź&÷ĹĹßp&“Fé˘|ůÓ[p‰ÚMY&­|śHýL0ν·Š°Ľĺ×^á:műL†qN88öt×ŃĄ-étZă°™L1jß%éąÇ5˛áwüÄéľc=ÔúÁji¦Š•áFŐ+WA¤÷ź·(č¨ŕ 9«jL6HĹ”#Ńô¤ÖĘU𾊮Γ*V•jy:3\n€¦y@čLĺą ÓütTĺ2ÉÉ(á'u<Ť¬Ô›Rš_k_G[±€B¸hQ€+7čdh ÜcĂînt§5 ?żb9s6TűŁßi%ôyçmÔy ú übB7ËuvÜ&j?Ħ^íŤÜv]Ľ<¦­z(ZÓ>›…¶ź?‹Ş-j:×L"•úwXî?°8CĚB?Z}ijsTeŃ`Ď#ľ›”Znłô O R€¦_íÜćÖÜ/W¸nßĘ@jkqćUřOÜŽ,0BĺXü‹ÝáQ­(ŤÔ#™ŐÍÎó’0˙<20ř†BTr ťFAĄýÓ|÷ě‰q*ŐŰ<Šś9 ¨20k/·Ĺ^ř ŘÇ-o njRŞhóŰQLk®–ĹráżëÓFn1ĘʨÇ@»  Č×jśüM ë#ČăµNćJUݵRŹĐÎPŘb‰\Đ2$-Žż±zĆH$ń·Ż„÷ |)d‹‰+"§/BjÄ–‹ !·kGşě¶JßD¬y–AĹ7¨¸¸Ő©:%gkŘ<Ś ő¨ ’Ç4ĺm~–qĆW«H!-âë‹Ďf5l7eiEcär˛JßQĄ+ŕŃ*Ž[LÉ•[Oʦ˝“ eGÉY• ÔěU~˝&[áeUĸÖŻßçĂ|Ô‘^fý-!’"­óßţËpŤŮťŘw…Źń|äµâZxŞfĎ˸9cÝq(ľ9Ń hÚťŠ#=úąTÜQ¬ö /pĆZÉŇBđ:Ë€×g@Ýd§ÍĚt&T+ţĘ—Ý<îÓo X>¨·m•Ś{´źN\žýx~Y˙g]Eně!Vj%„ŚO_˝–Ľłöş°z”ZtĆ(÷»Ş3¨])˘il»şî„„ŕŰ'=W:Š’ŚÖX]/âąQ–c Î7Fy˝˙VŮÉîlŞţ_«Ź5Á•7Ŕěɶ‘ťN°ÝňŁÜ±Ďůí;‡l„Í'z`üŐLöĄšč°f@˛úPâŰĄÂgUÂ/r0Ţ;>·}H/Jáöq`%ô[¨Xě”z‡°jŇz5€9P˙q·›^ĘGř<ąÄŤdď×OuÍp=˧Ě …`¸ŕ#ę|y!| áęŐ¶lŹý żu ÷XJŔľ ő†^_{… ěQx=şb ađ<ű„Ae٬GŞŁÉyzíŞ™< ăšÇgŰ_ ń17Îú%‰Ó|_a#¤ě%GD!Ű8żÝČPÄk7Îđű6~á ΑŤç¦®1ęz¶„IÄ?,öxXÇI‰ đËUŹŕYś%?•·=Ę”ĽWa0b¬2eŘĂđŹĄmo9aPúv“Á4ĂeoüzËÍŐäň‰EY\5Ň}™—aĺ Ş‘(™2î@ DĹŘ9Đý¸ş=Á;XBz)=/íŕĹA~±`±FAwŇk‚ŚľńIMíęÓrl<©?çŕ­ě6q”´]Ţ<‚GÁµú´ŢŻ ËáS@kË^”wŽ»f<˛v>î˙o»Ý—2|ŢŻ¸űĂ_Ĺ~äUXsI›fFŔ˛'›™YAŁěîĽÜß«5żÖěuKuCŮ™ë˛ru¨Áňpô1*Ú…nK ){öh’p¨@żÜ«Sç´Ýż>&“Ţ´XŘʞ}M´]ďŽ_v±qÝ%ŔŕjąˇÁ\‡Îć«_ťw¸0­¨ÜTđĆ«˛ŰŔ8Đ_Śg´îĘÄŐËŽĽPwq0\CšĹŞ–+Ń·g•yTÔ&+‡ś?Q  ÖÉj±iĎuúÂ(qg‹Ô3ůŇ—ĆhďÔî¨Úě‰#FU ‘#˝no4\ŮTŇ–Xb—ČÓ{ ‰C„đäżžšz­Ŕĺa•Ł1“ýPV')ŕaŹW}!؉q8’9‚ňŤ$·7CŤÜ‰Ó´5¬7›@a0Š| q™ (e/â,1¬Â3˙xłw5ţBľs?ď)¤‰®Y-Ó˝˘Ő4â!ÂëdŐćôĽIŰ (˘IaŃϤ’I$’I$’I$‘žŔŇě˙)¨öa“Žd üc S=ůGÇţů˛Mű6qÇO-@渣“Vű6Ţł±M¦H['q` Ě…Í'j'o)¦Áß2ßţö°ŃĘ•GŰĺ–®i]…ä’I$’I$’I$tk±úüőc]g¤šSN/b ¦Ţ•u3¦ÔŁ­yÉ đVô¬Ŕ|:–‚óGýŮ9b«‚YśwIOÖ©Ž%Żj/ĘÔo:A­Ds;¬üŘ ,#*7y:Ü­P Ç«•R l† Ŕ'Ń5«"aúnĺ}čHWtâ4&_š ĺĽúÖlš¨qŚßŻí#SMT†7{­{O$’I$’I$’I$dDĽťiw‡z˛IŤŔW˙dQ[ĆzĽ‚ĹÄEڎÖäsmoéţ+ĚâŃ_~JgJrîžVĐ`Á͇07ĆK|ő}°VT­•Ď Ń?4-·śˇu·U3á¦Ću´ ȲďËŽŠC@/×kö4 +<÷î€ÎËMŰĹŔţ0˘ÖÔp=Ă—r8] Pt®&`¬•q"§‰Ăő˘©¬ľ‚¶N°<źý®=8nI$’I$’I$ŤCp\Dű“ý«x‰ MOë6XíÇ—WĚŻÉĄuG'Ç®‰<8Y±ÜaD‘&şŽ‡ľŃ^őż÷‰URşG ߦ˛,XČá)_Ţ?˙uHRô„źĘ{ žĹ{rąĚë0!ű!E,^n˘ĚË({ą€`Iň6çug˝ů·;pS Ű:O¨ËJ¸‰8v ×™Tfô˙˙˙˙˙˙˙˙'°z]ćVš N[–RÄw `o8ŁĚúöć‡(’TtBüčZAĄ˙0ŰÄgZ@*ĽČ$˝‡Śľm´?Ő›Auâ#j,µ* ’Ř?úUđť?¨đˇ ó‹éŮ«fż"<0wz5ýsR~â13ŽÓ%6Ť™r#¸†ťć'o{5]yÚth6MpUĘÉýµŘĚ Ś(yU8Š|Y¶ Gc$’I$’I$’I$’I$‘…8_ęiÜÔj)/+ŔEe™áś^ÝRÁ ĂzÂĽKśś^ĄA@$ÝJrŠ«ČWţ­MÓ–@†Ěâ­K¶oë ˘Ýů~"!BFŹ Hň“m¶Űm¶Űm¶Űm¶Öéµf_…×ĺwV&<©¸0WŮH»·6ńˇfÓPoř•>G>:…yvÝŠ·Š;ơsIy‹ŽÓ›””Gę§jťü´ú:S´Vś±™ĎM«ňX 9$’I$’I$’I$’I‹9ťŕ¶ ňą·ľršĂ)F“·ż­şvÖ»ŕR¸“ęÚf‰=_ׯlä/ŁĺĂ{Wň3Ő3 pbżľ]Ý÷´r¬2Ç!XUŞ3^-4ô_k^I$’I$’I$’I$’IÄqŔbÉ|PľpUc0Hżâ«u°Ü(žˇ•BiuOŐĂ‹JÜ«ŕř^Ć„é5Ú‘&9Ś:íó.)9Ą[ĄĄž‡ĂÂćă˘ŘŽB˘Ń^ą„ăë ËsÉ&k ´Ú=óC¨Ü8v)‰ś5$67 >ĺR4ŰŮĹÓ#Řż˛gn0ŽůIMfš Ź<§Q>q ±3>ąË‰'Jţ$ [{d4ćq! ĚS&§{cŚ3ĹŐH č)ňTp#ĚŤÚÉő«Űb®ęď°Żě™4-QÉS§˙JŹ[s}¦6BkĽ ”'ßöGµâ€ĄľćSv´Ł#ŇPĘ6‡ú×ÎQiŹwAꆟśwŁ)¦źĐ Ś…·¦‘` ŽF¸¬—hĽýÍ}Ą~”,Źó, ¨“PĆĆg–‡/Ýz†`sâMťŤŐ¶¤HĄ0‡áź)Ř[jś2Üł9B§«\z÷_jüű†ÝoŐř1dÎs(PŞW‘–ÍťyÓ°/‰ŚCv—h’űľ·0ým †˙5;[ŤE+”íMKO†Zăă¶H@˘$Ó¶‰x„áŃQž—ĺ&Űm¶Űm¶ŰoAY ŻŹféZ3\dNę ·©¨Y«há/kźëŽśĂák‹C‡Xű‰ç°W…ôIŹó- ĺ_ŮiµďË•«w~záÔx÷Kő\îŇ®jňÓ‚9Ć,~ĄP‡§qQ ¤łŮ0ZĄ:ąEęĂP )Š‚6bü¤Űm¶Űm¶ŰnöÝŞA‚óE„¨Ľ0˛J{ăC}…Jęp2H~éÁ üžâ˘ş|öÝ;f)Š®!ÉťéIŽż‰rxWşÝ0Űx©cťAmŃ É(h»ťótĘó^=Z¨e”Ř„Ť­KÔ‰ JĚ_”›m¶Űm¶ŰmöÂÄUţxřOľ„ *$žÜ4°tü˙LOĆ(§Ă Ŕd°ęHkÄĚ Gćcę×ÝÔ‡šaPGýUÓˇ‚®S«‰6‡ĽÉ[c“ý/!AŃŹ?äkŠÜ$˙IýÓ+ĂEŔL#ŞVGęgăŹF÷ţŚ»RucöĆO˝Šű€ DAS¨ÖŻ”ń÷ÄwúÖ]w6â żea}RJ®- ëkŠŔtÔ›aŻnqŃrň%äöv`ĺÇ·Q+‹ß  Č­ňUO˘Ĺí˝¦ţ•F$Ś<42ýmSŞĘ9őR =‰2â•‚'Řś_-AĹăůy •aŤů˘Č¶*dyG`Q-”zűKĹ˝ţí"ĹËçzHhyďłç ‚?˙ fä®ÂŇÜłó˘A‹BDÁś­y>‰ä§Ę~ŢYČ’Ë®„ČëŔđSm¦ĺŞe˛ ¸ă¶čú> ·ăŁZTWN‰a«íżS[†F_Ţź~ą\îÍ.Üľ™‚’{¸¦ÇrćMVp݉çÇŃ N‚fZčËiÓBJŁ´,)fę‹Ík_áBŻĄş˝~µÝ–l/ŹH]Z+umö±¨i˛ ŕk•ülÂ,8Żi‘jp4t9ËĐş|żÍ’GXîG†{ČŤúQçN˘Şî›‡ álűyó{žG§ĺMŁâ?Ĺ„ŚJíp˝Aš;ű@ÍźŤ-´ŃěyedďNđîúŤÇâF^ĎBčĘ&’߸ćžÓoMŞk~u¸^%˝µĆÍÁ+±Ň% –¨ăý…‰fŕ!}†‘‹áą[Ô–‡ią !šWűš' ŔPĹŽFËŚSKĎ&ÂŘÄʵĽĄž®Z˛Rý;E›'˘ĘÍĺÂđnvW(űfBŮ@O¤ ‹ţó7ňýňY\ŤżM'Žľ$ö×Hú™#kÜŚ±Żť7Źňú2@ś’©ˇěEV›Úf”ŽÍ~ßaŰ­ăß<}Ĺtăą^RbĎÝb  F¶>Éz$˛ĆAĺ†z킊…SŰw3ĄVăů€Ś»‘8ÎłÁhŃn&fl?ŹČ.pÁÂđPfČ‚˙eeë,8×-€Ô{fĐ«2뎾ŐSµŁ#čĐ'\]ᯇҲĽöÓ©Ť§6á6”$f\µĆş) óâmă˙ ¦ećză0ű­UxźBśb$ôŚ'#/Đ;Oi~`Ů›f¤s-ŐţâÇ Y¸}ĂŢŔ”ŢAµYȸ\ŔţŮ«Ő  řh‰'톑đzÎ/a(x˘·/yе鏴 C°wŇmèöŘ#Ę?:cWkŤDlTŇy•V^XUË6u˙iĚŹG*j6{˙l Óóކ)'ë´é&x` z‘pxgNć[2„%Ńťv[cdo”yŕeÜJ]_ó¶ŞĄd]–¨J¦@ô˝ľmÔÍłM+kůŁŤÖbü¦äi|·%0KźRĎçyŤ\fu4á-ř DŢ^ËŤ¨v¬alřVćŢs ş‡đŕP łT±K¸Ţ¶âűwŞűlŻáq˙y‰őj<|úŽ}VOđ§±ü6nűk«í¸/¶®Úašbú*áPÇj]xl— äÜ€ć$e¦j囩Őë­70?Ă6‹qÄäN,xéđ¶€ßúĚézĚxZéuĂĚ«2ßí™n”ű{¶"ëłE‡obRIž%ŚÖĆŚÖľťŤĂω1 ‰ľűýŻxŮU‹g‰Ł_üëeăPĚż. ďIŽmJ-„.^Ýpří]ö˛ďF§:2Ý'ü5č­¦2®>ţSÂa Đdµ)8Vă×»˘iµt,ˇo6…_ćAśůšö,˙ ?  ›·°©Q%iVů"/µU’2şâ&Ľ‚‘¨ý„ô ěgZ¤ÁÎß\6npďV~EÝĹť*ąŠXE<"Ö×ŕL? Šrňâ*´I_ď¶Ůë…5ú•AÜ´ [xhQ”6©äúxäŽúĚ5)Bíu­CUÖs~(k„)Śž®ŚGP°‰¦Ť{vźB0ě*HJx_s!B$ 6H,rĚXÜçô)ů»A¶Ž µ±ţ\zJ_ź«OF@(ľŁ’qđd˘Ő ÂśźňĽówĂú¦ Ĺű/2˝˙Ł-(n—]—PCB&ň®Ýžm}Űń"Żť!sUFPOɵŇţčE~TéÄλ_ľJä›°žl´8ľíůĄq äĂf‰o* ěL#«uřÓ:}MÁŔŚ'0Qm?OÖ—>ŁçĎŕ‡jćĺąVŁÎá‹çúyŻEąU1ĐŞŘUcc}͸ďÁÂąw6”p•Ă™Rř(ë2"PË”Up#%ČôŘĹj ˇ˙IP«žřWnîýĂrŚY­Ă=h@“C~¦HĚďKXvń&błaöOŇ…ďŁCkčنő=AĎkcăĆ`/JFҸ!^-±Ü€„E}7;÷ÎvężĘ¶ĹtiwÁ÷ˇCÁ’+ˇdYŘCĆ7I†âgvEĘW1‘mĚĐQŤŔi>.AË`cŹ8ôL»ËŕY Uśđl߉аf‘OŽgeyČdÔab¨_ى§HÓź+TďT­_ zěĎóđš=kžD †psyá‹%2“٢âĆČĽŽË5‡!)ΞŘ5ý0ú®€«őŇ}Ă~Ůwí®„bÖ0ąŽSüHĽĆ2Źnc¦ţ•— <3xˇk]Ć|UÓÝ_Ů%'%Xť’Ăͧ˛qVąĚ·ntu”‚)ţ†¤Ů-¬â&¸+*ž™¸®4ôŻz(™Ć-€ŠQt¨¦;©[…ű§A(ě‡:ĘÚÓz‘%áÜk§â9ŘÚňîŽŮD΂®C1ɰ°qމ¤é›ŮÂr;@^Ăns:’6µy ‰ë2¬í¬q}`BBŰ!6čÚ,} Aö|¸×ď#„‚IćęŠOB1´÷¶eByéÔTŁŐ b%ŘS˙mŞsÓt©űTXĺE=Đ« Ází2–Ü­µuŐÍĂ Č‘.&'7˝,˙żBâŕ9é‘ÇěżąÓÄ?DşGję®ŰżyČ*FX$‚ڦX{żŃťQ…đ»öżž V )kÇ'NJ¦€P"L.jW5ó–.»Qŕe›[Őłţ ]aüĺ®ňî«ę›:µÜY? 3ţ*¶ô‡ť.Čťtč0 Ây¨b±ě…÷<ç¶ţ}}zőQče—g+ť§°Y—`Á‰b˘;oňܤ€'YŘx*żď-2 9…pŽ B r°®}H#!.¬Nń#ĄäfĹ9ÖśĽ3NĎÁá‡55zqŞ>Î=~đcb÷˝< ÍŮ„VŠLíĘ>nĎv{3rv—íôĹîR­y¤8ŕcp¤Ľ[f3}ť1G¬Š #ěá )¤ľlŁAGAőĐŚÁyRŢši˝C}›pßëB–_Ço‘—cAÄţ˛3düŮÔ“¶Ýk:¸Ń şâěPĆ'¶–Ł~¶ Řě0]ŤŰ§ĆRĘŇĄJ@жXű¶í®‹Sm)ÓZc / |f™ymv «ľ‡ŮŠ„ě‡X™{© íč'¨Qfgl» Č]Ć(OmĹě ™¸úE@ą Ĺ¨>Úü«ťl©nŞ#)Ú:ů˝`RŞŽë!«¤.[‰ýµ@¶ćyŢBXLŕBí;©\«'j.ŤcšmŘ<ŐQ FgÇ-ĹÔ[©'‘ęGs†CŽ[輙é§ăď¦ ¶¤dZ<ÁSťR•úÉé«”vرŃnş? %ÄŇřú!žđ±bwuę䕎ĄËB( ŤKóěˇ%UşżÖy§ôś';«Ź(¨ÚáÚQz@ ĐôBFާqřŠm!-¤)R„±ŘŮ˙ZńÁ˙ aË_ş=kdë\±Ó<­5 ź ©4DGW€7%žz§ś N7FA¦ő“śť3’‰ńŐó™Răő—Ě1^ŢHyš(0b_µf5S\#ÔŮ[e˛'TuĹ:KđÜŻëG_‚%“ ekç­‚›)Éť­`°¦ň3F;":Îa#–xÝóL^ă©WOćľÁ)`Vťź)ńçÁ «őĄ şU6ŠŐ ©¸8Ç{…Ž:ĚŚÜsg›„m8›a—ß&ŤüT6 Dťćo9ýÄÂ^ĺÚRÓ¶)tWó31öŘm­ahPŞ~Ô8bpyM[să/©‰ěܵ´ţ;ë÷±%âď&łŔW˝~ŚŤv˙4–ÓjŞ.Łľ¤d,őµs´hAEÎâŔ+ČÜűÎadú!?‰šż ^·ę¤ć¶ł…¨1l°”Á—ü`÷©sUćĄűő`ż@?„&°hżőu¶ĄĂ§1Ůö{áY:ţ&á•~K.¤6x@ďű§ä5<¦MďŮ䀞ÝB\čž1םöŃ@uCz=§UFY<ďS»ŢH#MnM‰SrŐË<QjźŞÖ8ˇ>Î\~Ó+ŘľfôH±¬r;ÎH<Š~€ I95Ů(0»ó'· iRm«öú¨„äu ą X…b›l‚Öän_ç&@ T\IY3VB˘ĘÔ®c=ŰĄV%cć 2 fY١)Ł lS öÜé73ÉQ"5WšAšO)ß$–4tŃĹa„'Ž@ż@¤3~7A‹I—†*n883Ćů_•ħ®ó~ ĐźďŘč,čýe¸6e?źąŐ’?ťµ‰˙;ÉÝ8O-2Ý×ŃQîţr eúŠČ”3BÉĂľK°-c@©ëĹpďU,َł5•ľîŻBűŁă·`‘€ľe?§ˇś×«$SîsEdăěŇŠ‰ţµ ŇUŤ~1ý‹Ö$Öܰë¸j)ЬĘL±ČÚ¶ú.u—¸"yXÍ>W:‰y¦]±ëôkSK–,QQg¨©[ăA;ë…?ÝEĆTŞQĆ-mžŤâ˘~q6V×Ů»ŔŠęŠ,MĄN” ˛G'ÓCÓ~Ć@‰HL}…ÚÖ”Ě: «¸xtR¤Äg2V¨0 LNśe­Í+@=«}×ß}z™…Ź‚”XžĄÖÖă3X*TřőłňNĐ-ySĆłfׄIz§VeŚ8,±1ş˝h")ۄτ3şgËdüß #eZRę?fÔÂS@­ďËîŇ­1ĄmÉ Ť—ŮaąüΤ٠\wQŢřĘNJxŽx–bŔéQź@¤ ŚElC€«íĽŁ×°-ĽÂk‡Ä°,„Ůťě"ďDîćzş7 n¬Ú?ÍfCü´‹fü`E?í‡ÂRŃ]޸‡×©sËw´™4d‹®Ţ.ŔÜĚ7Ôu·WŢ€í`-m&čÝ7*¬}U:CŔaţúřł;L»§.‡c’ńCśđBQÇŚJŞžÝČOŹ3oÍĐ#ÓŢŮť|LPćŤp°<ëzcÓ0oDVŃĆ ± µĎµ¨3!·|ăřuoü4řk?đľ±}˝OŞÔţ­ü-|–˙Béţ§ü-ĐÇňôë+XÇs —#_ýĚ—Ć ůŘ <6MřߣždTűos}ť˛‚ÚG–*0wΦHś:V¸đż'ŤRĸ®54vȨšš!l™©<óáź]p ¸îJÝp[{3-7ÄŘíĐÝýć`˛QMmd@ÍWČgQ˘ŕ¤ˇŚ;rV f´Ý‚Y>á[Ňüź–Në•a©QěŮĆŘ=ʬ÷p>˝ĹK,uŕ±má”CłvÓîČnj·B’[&Ą˙m©•Ć:GW\ëh‹˙]xÇžşP€›¶-¦O2§ż±×˙2˛Ő@-{0Łó|çéMŐ‹zU[PŽńáĎ>Žá‡śGIéUü]żo]Y˛=…ć‘7éÄšę··­ůG µ¶ąVÄŢzŚÍQ÷ş¦>ŞG í¨Ňű~‚ăżî¤ ß”2é>ČdűF2r.FŃľ÷°ÖűÂÇÇÉž ř‹š–ăëVˇ;웏¦ÜeV©™Ň4lpÖ·DŢcoqh7ąMŤ±˘78P9ň©}¤ž{ ©ţÉ·śş±ďˉčĎLß•ě˛ěYdÔ Ż+ŞeC‘±WşXą ěKů‹-±~řBZ%˛Ľů/›ŽërăüëĎĂbĘĆőwIlěMŁPđŰ%¦w°zź(s’ †*Đ‘żî,3»˙q_ŽtýŘĆÚ8;§®¸ÍE 4űO¸ŢĺU"„^©aŐ ďŕrĺٰ˘HŁ >ĘÎüÁ‹J}ybŁ\¬®Én?Á,u»Ś`QŘMA¶ă„d7Ą7üo)÷ÜbĎĆ1aZذ%M ˝:©bEsÜέô8#=[Š81g®>˛2Čň,ľâ©7ąµBř€`˝Ě•”b…ä!o4™oó'ĐžĎMHÚ4|¶‰kă$e"ŕxČ çč ujÄôWVŢĐH÷—~ ĐWXšŞęĆ ”ůtČ}…ŇXjdť2íxDç€2!ŃDX«›€Ö\Ô×Áú´h vkr’'K1Í Gvgcžž!n´kjÎŘ-ó‰ĚÜö=ÁŐöCč\Bl˛dÁqÓľ p\ťŇýżJ˝Oµň;Us%÷ú ROů‚±ąĽ|Űaµçm„-n¤×XámfxkőşĽPq'펌H5é0¦+/çŃg—Á8—?­<ťIJć\Ąů+”âô×`Ôk«W$®^-Ý{ŇMšż“Ĺŕ– JJľĐe˘wŰľS:ľhDĘ6>g`ńÓěßľČHŹËL¨L~kĎűPqM÷¸:żˇhú˛ŹN[ĄĹ8€Ë¬»»ÇSŐ)c§Ôa™­ŔÜÓ{VöUĺ×< ŁT”«2\ŠÍĂ?Ěc ŚÍÇŹ´›ű”p˛…”íôr‚ ÉžĹsč·„ 3Ś×6†61Ĺ–(}`O:Ŕćö¦ ™Á%äRŤ’gHM9‰iXř?„GŻU~–ćJ%sw鼣żE¦ů=Bˇg•5vŇŔäjąRY6.1»¦^ €ÜÁmę/̢ä/ś;»i/«Mçľ 91'HĆAĹ7üŹĹłh2B8\K°ńB«‹ ^FőţŔ¨8:Ńöë~ąĂ(adv!WŇqÉÇxósĺl<ҸÇŃ»óŐŻĆí¤ ‚[|bµVśŰÚÂ˙ ?r*÷ľ&r/]Í*({yꏀz{ Ą4ߏǥ~%żőŔ A]ď €­ď0—ČůŐ”ŔŢq©]#őŹ-uĺ“#ž’ oŔ’HÂá‘€ľ'Ky«5ľăcéň.ě jzŻ"XbĐâFómÍVěáUěÉďj›Ňîó6Ţ Ţő˙}ůň®W%ę~{ěAçŃhî7Ş5Äę{“µ9ő a«—ś&, "ý,ȱěw¸%ăžďůÉ×Îż| Šň!xOűâ`˛Ďî릧r>Ă#>`$őłÜţ–>ÄqsŤlłú˛S}µéČŹĐ*07ĚÔáÉáRń˝ňI[ëĆA,ťéăJhłCîGipSXČudíç/ÔěŚů ‡6ŮD‡÷'ŇźĂLRŕ·|rÇ)ˇ•ď÷ ™•8̢.°2ň÷nBłŚTtŰq 9*O—’đç_ŮKRôpć~¶ŇślűGrÔ×r:űA» ć°±bF5(˘ Dü2Ł÷ĆÍ®oŞ´hëZŕ ßť)IĐ|‰·ü/‡+iëß0ô@¶ŰÜcJŮRZĆóIˇgŕ–D°©±č`‹ąUCJUCn,ńŕ÷Ŕ›FPůóŮKXîOć:îĺ<‘ UtbŔÁď ĺVD*O‰}Ďä”"5ó^6ţ›°8?¸Ç|óAkÄ֤ٿ—Ł'¨ŮžŮÓZäóéÖ€ăâWMŢŤąµş;81_X[á„§Ľóôµť~îí*ČN7č÷äĎ)G÷" ĆĹćŹ DË‹¦¤7OjS÷v„ú{çbr[ŚqmĹ’·t:›µJX4†a€{̡Eűš(ź`Ä~îUro(Ű'Ŕ·“°ăÚě±] ˙Wňýła1Č‚&‚•áuÉ>·lj ~A…‚ĺ]ÚÎÜ%LdżíPâ!‹ÇǬ ĚžCřűqUąŐś x•gČÖĺ<ččŤr奩W´=Mi¦›&Hż±´:+›ůlbbxw¤QKĎ%wÁĘëśŘw€QÇpIµ¤HĽméňŤŁ÷!::ÔôÄhRŻĚHD¸1BLż8ÎZěž[‡úźř/c6ŐűD'28Iř\ń: ż2úÉćzR-w—#ąŔ-‰!xPJąM˝˙q䡚tHţĐÔţĎ…Ş»ł  _k:Ű pÍw˛ś"Amô;ňp©€|ŢÄür’ÖɆă6->©é9*?›~îq•3$[…±’>â›gÚ˛ť/R)Ç Ď ·TřjW*2—Ăç$U}’ť©ŰTÓłĂŰ˙T€Ž4e)şËîČÖ`âž~úšE˝|ŘćÁR6®óµźšé_Ř—â¦âŐ*/áa»ôř}Şo7eWĽSÎËČMü`{”$4V<1őöMŞlÉŠ,ˇ«şśc#Ś‚+í`¸…Ŕ¸}MpLë•Î…ÎC˛ű9—€KćáC5!®$ř~?ODÄÂü›¶CT‰A€Ô´™n}vŻfň}ÜćÓ“č=ĺĹ͢ĚĹ`TוÝoeQĆ,;RBÔdcźăŕ5׏ÓóTć˝vőÄĺi¦'—ž-„—ą‰4ţVm6ë¶Ib“ě’ŇÝ]cäôűóÚUĹ›5u!,˛ľá®Ţx9q¸řzŤ®H$˙@}¸BSc-Ŕ¸°Ťö` Ô(âáŐmé1c2¸`ĚĂ­/†<Ź|ĚHđľqĎ"iB¸©ßZęő:K%“~eˆÓhTüđv­2sÂňÄuw’ŹÜb¤žâeˇRHwĺ˛ďíËY9Ĺ®ňG´|>±E8˛qé zę@ýň¶9źWQvŤbř:“…|;łĆÔĹ„ż2Nĺ!D4—É6¸v&ŕ~ŮĂ-<"Şć8ý¦z%FŤťŘD} r–#ć^ ›źĄ´ĺ_a˝u®HQLś «˝4wpe,LtčL Bçď®™Ăö9őŇń—qşę˝#H2^őž:?BîŁ5í ‡ŢßÍ]ë oň’Ążm ¨éDdľĘ"ĎgąÚjÂď‘y|Š©â®Úه;Ž,‹öťj>°G«TÜü6ŁŹŇ©f/Í}Ěň0…ţdŘ3ńµ¶w)Ý“íLtíc2¤…"S~Îm%¤IĐç•ű¨ň@*¶0ëëŃ7pBU““Ú‹;‘”ňo` ;#—˝ěfxp—Ų®ŁíމiđW©ëmŔk˛ŰXťőŤP§kéŐ=ąTę­Z]NhBQ8!7MŚK}”JMś~eH/›ć<_$îeöíĆ ,:ŇPła׼‰šőzúĎ®xgXÚĺFhŘ™şŮe{ţĚŐĎď Ń»»0Ĺ‚¶4—ŮXMÝg @´¦ŽlăGȆ»«@L*¶đc {`ŕ˙34áÎúźąÉ?ÓNť; Ł·šgM& aö¤ŞFÝ…íŤçkÉÖ,4§ĂBZËVqŠB«\}ĹźSăôŇyj7×m&·äżŢ}P‡¨{'4ĹCÚQ‰·ĽĂŚŁyŰ5Ö4Z[ŕAo šmjÓ*˙B­ sś˛‰Tmá×kôy@{r[Ó¸»›GŔ‘iÚ’ŢC”îůzşâň…›¶¤¨ZÖo3t;p›ô É“ÎzŕüťĆŘCv·<ňý:«aÍ+¶´Đć§<—-üÍřą€nlţ­JFBɲLFŕ`Ď ¸a.úőcŤcć‡0 |2, ˘wÁYolťÚ•—źÖÚ‰§§eÇúéťwć|ŞüZ†ecg5¨¤`â™Ĺ$$…,w™bśă¨ă@ٰ$ZQďwĐÁ ÝŽ[łř4ÄLĹa2˛Ô Çďu5ÍŞú3}¬K˘?7*=ę%ËH LäÁzŘ‘ĂŕO CöŢr•\M\äż´ztO±şČ'«JĎ~Ř%gC†łŕB(_Ěd¦2dţ ÷ßKŞźZ˘íGŇn8YO‘}DŽ+>epjRUçŻůO¸ö+š¶ŐjÍ˝Kx*1â|ź2\Ľußfß%óp%3Úż®­ŮđS&šoX„,®éó¶çy•]‰$ÎŽT-xmáľŐî|¶?"ť'< 2¤˙t eĂżů5?~č7Ľ9ßďłŢk®=ń$Hő]q8 ś”§µ–łŢů˙N(š0ĽHݬ™PĺW_áŤtź"ŇQd ¨:~”ř(vŤ­˙)x\¬)dbwཥJňDůňmä]X‚ěş’ßJŤ0v'y ŞČ×;‹pßMű±ďĂx9Âä%©’w?Ô˙†zŇxüúr` (A#§?˙;Ú_Šm"ľ¬‘L«™ťÁ %{»>’ďË;k…‡d±OĐŘĘa“ÎFČ`ŽV=‚>)ßwăŃíĚô _Źž¸ \j÷Ng˙Cđ®&Lů‚"ć ;…g„Ě(Ž˘%“ŠĂÔ ´bk°‚srÓe˙\‰1ZtnĆ%t‘0ˤh"Ă…řż»ňo骡<śqú9cÇR˘ 5€ĂiÁ*dG€®g´ „&_„sߏťčăhdQÉčý€ŇxúDhó ô* 'DS8ş};żµ·üâlІÝĹ@I,ćá˝>ľ<‘‡ůś,q˘>!Ô˝ÜĐÁ±ţ::ź!ŁÜ±ĎúşËÎ`0n/|¶‰kz+€+›0†Ú$rćĘnëçÇQ?Ű©ZŁíÇQßuđ{V”뉂oŔłF4šz­ĎĚ=61¶šp~T ăÓ €†xÝţ–Đ Ż¸PĎSBsRŞů­sÝ6ŹÂE¶đ».Ją]˙'µ·8%n®űr0ţé::‰qNnřd <…O¬Ď#ňŰŐ%ÜČâN`.ÚűÖ§‰s/ä°ýtm_=8‚3äk$·Űi pżô\˘رivůÁL4:$ŻiÖĐ€˝­˘n7ÇŻç&čBgĘň`^ý’O(bť2Ҷʊ+í•P5L›vJMëPg8ŢőÂőj¦µ ”ý«Ça›Úé@=Ćô¦eY·SŐLug¤-ß§Ŕ86Sľr?°“ŠŘŞx“äŽďó[Ş<ä·ôvxˇä¸ý"OŞ hÇ©a<­>ü˙ŻJşŮÎë˙2Ć„0N•ť¤ÁŔ”jâÖű€ÇS‹2·6ÚŹ@[ ~Ž©‘ )Öh­xú9-Ś_ß’d+‰+N>§‘J™c}•Ż}®Ě)ľ„‰~GĺĹ—:§›]Ň Ň(Oő¸z-_ó!oř]ž„î¬Ú܉`HmČ,1zń°@ $ ý‘Ű®G‹±…ĎŐ/÷A%©2ÂĽĆŤKíßM ÝĂřOľ„îĄ °µtѬô™8^Řź÷šŮ(رnz=Ô4łvÉ@úlŚéő’ţűÂl¬#ă{yE;Ż•îľŘČžŞgLÁĺÎ[­±őXŐWůŔ"şJ1ô`]€d‰ă|¤ĺt#•_ú!ÝůŁhjąźT<ÉQ©;8EiŢéüŕv(˛ů-Ĺni?|–¶°ĐŮ«‡ŹąťęŮ[>tĺ}ärbľÍ"ý‰ÄË…/'lĚńâ&nÝGÚ«ůهIÓ)6¬#FŘ!‹¬‹©n.ś ˝Mş.‹T†L±ż&,¸ţtľAcG!5śt©=’â‘jJ¬¸™||Ä*Šl$çÎĆ“ě‡"îžăšăćÖŞöŻlźCťTők÷pd%™ż…ľáó:FÔwŢGßůcĎjľ ÚÂLÝGQ§´s¦…G^±gűńźďĂű¸ ‰=¨őD–T>.B)Kwáôo­‰ľŃ_á\˙Î˙˝˙Çř˛żá¨źŔÖűXż‡n—۵÷Úmßk'đÜořlĹö‰żázßmĂü@řb­áďI@÷‹FHÍŐ-GN‘ÖżŕůS||‡ö[›q6ü´ŁˇRPž}úŔϨ¶C`Üo}Áʶžµł!ĺ<µđ%‹)3ö­_‚OßýPWŮ©•hµxw îřĂ·âüĐţźiµ}0ěč'ă%h®%Óv ˙+’‰ŁËŽtdÔ›­P\DxZmďGZC7Ö–›Ţ¤‘ˇTdAD—QÄ+ŔÉKsy¨Ó`kH˝ýźÜeÂÂnăË»o1tçˇEŘ;pšÎu.ÄYăůŽ}Č-Ď5E×) `2Ëx˛Ëšž›ÉŰŐ@Ö@Ý·v«sLÓ–7ż`[«ţěTož•¤Ń+®»ÎM„bÚgÇm…r4ý¶bˣŮdz,ĄFÂJ#Rł±¶™ţšéź=/OŇ'·Ěš7tN{t™)3îŮ_#ĂEfoc#Š7‹v⦠Ů8×›©ÎšJđُÝ»’˙)= ŔŻc|ě†ĎÇ’”ĂŘm™Ý}áGÓvÔ%hŕ †?"'bCx}?nx—~łäĄ ´öŠjôBi}‰LZwÇ t”ő•l#"ć•«Ű4–›„{ŚÁZ-¶jPţP=HÚ"‰„gYlä`æŞy]Nşˇą®`SWL'>˘’Ĺ溨óˇR Żöšş+ŃŰ ĎéXŽŐ0j®ćĹźg‚ťíD˛… óŁßas0p.Aje×.*eűXŻËîÎ&ŞˇA vwjVŃŮl˝]ŇĘsňX©e󇹄 y˘7óŁCÄ#­1+Ŕtˇ ngJŃórŕĄBŰ[y!~ĹKńět P‹[7ł5Ő–ç?Ś+ććŽ?űŞ ü꤉ŕéĆ%÷Ć"RCłz&HözKHUW0:6=+T×pŮ…ySÖŻÍ‹_ŻťÉYżeŁÇDďJąŠ}§úĆp){˝ŕG"öS(äqT3őÝDLë^ Đ^·¬ňŕŠ‹<~LQ€ý>a{uÂX2Ú¡Îovő‚ł?ăŐ—A«Â'tv¤(_;(cŮ´ż´żQI¤‹çř»9WcĘţKÓ[g­ęžZ©’Đůľ–Ň`_*ó‚3ŰS>1ŇŐKÝĘŘSĎËĹń‰?ĽGŚżÂh­Iőă1ŮCMÉ®G޶3 ÝÔîx’[–ščCë´zpN6‘lńzX~N-/?Äňs‹¨ßKĂ×äúÄż=f˝ť»Ťy—LÎÜř5bІĚŃ4 X*µßćłcUAí dĐ(hű',Ľń†łĹ˘Jhۤşn:T”JČČ&×KĎe˙02`ŢźxőÉSGoé“+ĚČM_aQ)ł˛ŁUŠ/¸‹’Ó)i,IJĎsŞ\/ÎS °7 b5VujO}Çy"ܱŘ%ÉS!4˝çCŹĎąÔ›ć˛ťŁi ·kxĘáż ż}9Q<†e»±Ę ő&–¤Ú‡:bÝd gvČ÷ˇă:ş»qX.t[ö1ČDKĺ–ĚT Ŕ{’ –†Łt•¸Í*"–»q™¸úPćO—Sle?tS§BOcńe÷ÂëÂEJ„Uąl† ŕëţëĄôhŐKQ:¨{¨¶w>poMUş/†Š±˙Q*;©+ďŇâ#E[…>–R¬:q\™óĆŚđęSÄ_^€0cs˙ţs÷`TŤAŃş˘f@NľĎ\F ţʆZ°H1Ó)2“)4TŐČĽ$O ňÖąôć0××*0ůKÉĎôuż5»żk|ö™űŞădőî3 X Ł:#{śF›ş8Ďş……ÝŻh딼źíąP˘ĄUkĺßŔm]§°dkŘI·ÄXć6Ŕ5­gż”˝rĘëöČ”50ČĂĺ/&°Ę„‘*ű’I4˙¦ű”Ă­ďő}nJĆf»Ë»-¶Í0v8¬daň—”‰$iâ7őĐjôš­Ć|ďÇń8‰°gçĄČö7úĘá’Ú׍—Ď:ô1ń]&—a?”G˝'WĚťúąě)â°Ô4ó¨Rä*""’8^Ş©Žć= ű…X/˛ęéúfS„âc{§î_–±´e ŻŁ!0…fÂłní¦ŮćѬ÷­~ťĐ4Ü—–!.ś"ĹÉ'/W@Y10ЍÖžř8ÖĘ/ćľhňëJXj'kÎýĺĺŐ&Lá ÜW  :Á™ĽűPŽÖ•‰ć÷5źéî&–Eá é:‘şďĹ' č°Ą ÖO šĐř#CCnĚĺ¸d¨Îd…z $ŻLZ9˝ĹSK ÓÄLxŘ·ó5xů),®ä]#žZKçéęvhÚééFnPÖŁQcÓ§č_¨Ó˙'ë`,ŹÇ©Şŕ=uyÝ-Ít˛ůPÍfĽČr^"űe÷îX6Fd|ę¨G~A=¤—ŐÖŘ„´Ž¬2Ă_đm,R’eąŕęhČ׸˘$$±Ö­‡%+}K“ß„!Ü>äó𳂾E¶ „„9¬%Áł îąaŁŕmXHSŕdąp]ć`TŐÚô.Öňć‡/¸zBZyAC!dAë(Ç~zIKł„ŕ@Tě…ç?CŻ0ŐáIőńň'őŁĐ˙f•ŕk ÝŕI@·ýqîŢ8˛ꬌ ˙zóŢť6µoóČÇ<ůęOzü>=IN»ab÷zÚR“˛¦`łTĂE¦,?íęĘŕ­6Ř‹e§!JčÓ%żMŐ˝Ż!ěÁ%dšK¬»›sLI¤÷Q˘ęĹ( đ„ĂłéŤć(µßü5+-‘đ2Ă GoľĆćđ‰ĐNA•;YJC;Č_ĐÜ%˛ź–‡@én¸IQ\á}G>Ů6ëé§Ć»µ„ł6<ŮÄ>ń‚Ĺë3™[1“B¸ K â8ŠĘ®Ľî˝˛Ä‡Ý”>±Ŕ¨ŚU˘v\oѸ7 kűş°CSe2$ďÂĚŞíń3H•<“ŔN»ëŢrl<— ›ŚĆL5_ͧg›=6ą)?Ëoqň[”ĎJŇí""7°ď(FvSC´ÉĎŽKy¸@›ŔčjBĎŘ,Ź*j°dYüPËS-4Źd,őˇsĄ¬ršhĂ ŤFÖ#kŇŮłŤ"¬,GŰŞ^† 漵H经r‚ořĐH¬ŽŽËĘŐ‹Cvőj€<0ą’ă%ç«J4đ őîlźrP ţ<śĎ6V ˝:ăýy|ł«Jě§~“ă§›A‰â/cOЉ1yő›ŰşŰŹ ö„s!ďMŰ<˝úĆŘáĚ|«ËĚl-Ö¤UgvÖc7P)Íâ;ÝTŕÜZ—VI1lâc®ú¶)z!DŰ?Ńů@ĚîNm‡(<ĺ=l¨* $‘ńő âm{*Ó[ëÔŠX˛‘‘ö‰ŻM€ jQä“oU y—t µHSÓËŤ.Q VaÔÍĽŻőąýjő]\ŃľĚÂ8©bK„Śĺj%<Őߎ\˙'/¸˛č·¤Mö»ľ`íݢ:‹  °ä4F>śŐ’š‹<(ľPÜČđyNůč5áŕu~KŔ3€dXťd>·j‰ÓnĐźKśÎ¦Ąă X˛;ćZpÁŇÚşňvé(DdTečö7A…eh™HiěÔ7×8A›q ÷ b_Ďe›‘ˇó“đ|y¨)ŮĆÚ~”7Ęü¬… ćĐb„&p»ş‰ĚÇVőĺĄá[Ň B& J«L/KÄŮmžŘ·ôâ*ě°©'ܸüśCú.X–zoÔ¨nă—ż‡ôÝťÄÍą¶“ ¬ě&ľŁ˛Ťo EŇYp$ˬ˛ž­qÉ:®y6t“2Í“ŁóŞÝż‘z,ľ4.SÖŹŚó°«f’č\tí˘]U„ĹĆ ßđ‹ęMH® >Ő˘ąW4í˘a&ľÜí¸ B %±?óŞQ¬ŹŔĄ­Ëőźf`ĄZĘô‡ Ë)úI¦ ‘çu…Y3+l%ŢÇN~îúŃŔíúAy±”ZŰją ˙˙űl€+†ŞĘäHAGá-XčÚdČAJfňěŮAŕ e®ľťt!)Ĺ$_x±™óâ,ŁIŇr'­Xź‡Î^SĽĄy’YQ(Ż ˙@4/ů˛©oƵÖaáíÝ:22妵¦µ¦·ž(T…˛őW8슋ˇşËu–ô8hPđ\kMkMkMeĄ1›iŻĹ”Ůü•Ęc3ć[§u–ë4Š9%:HË–šÖšÖšŔ­#?„ ‚0č× úű×.n®˛ÝeóT8ˇîx´Ö´Ö´Ödǵ·ë|Qçţ˙AđŻX®™_°Ľ`/4x€H…DP!}qaE€Ň¶Űě)nWšćZ~/ő;&÷ †ćŤWˇgŠĄjb.ŕ•Á=˝«Ż˛S%2S%2Z”C„}ÂN k,"S×6ë˝§ŘuSHZőć2H@ŁZ`8EU p}"0Á$ůŹ–i+,vŚn˛Ýeş©żořęDk˝IhI‰Ł>&äŹxqó^+[ »Ź]ÇŢóň˛&ăÝeşŔňâFÝíŰ>źÖ{•î #ÍďĄŕFˇŔł‘ŮÉ—Íl‘e31ľ0şËuEa™đĆüöG;T©31“¬Gl_i—b+Z:ŔYÔŢ gU$#aŢUÝ3±•Ö[¬·W đ Ż/X—ÁdöČUéRoŁŰĽRóź€YőB~±Ó@Ň`Ë‘I—áÓJş;ť=ži‡.®N÷'eTo@ńçĐ|$jx9f¦ `-Ú¦ .ď±âćyeŕ]Ž•kěk=iwm¨Â®Íu;ďU±ęOU¤˛µľ#r oz3uX2řmČąú!ŃöÓ­ĽĆ*Ϥլ:ͤÇŤNW9w=$˙PÚš5q˝˙i¬c¸ÚłĎ9Ńďlĺź2kN‚SHů—©]KŘQłdRáĘ0ôi4i[ńz—‹2Őb%_¸ŞĺŽÔŘż;Š‘Ťföłg8¬ĂëN$DČNĘ&8ż€E4Ähž)ęh‡DĄ5:LfĚŇIo3«°\)śO ó®ŕşËWůa¤]š.y¦fBH’Ó¨4ßUű«DŔ}řÝç»mťşŁ ‰2‚-ďheĹ‘šÚYéÉv&8ö­¶ZůZ"1Ś•ąˇ–Cf˘„=28¸‚o޸újč~¤ŰĽQˇöŠ'…}/(w`J‚^Ń-Î^#–e0séM´äźŤź®î·^2n+˝"ŢâfŢł+—˘Ş榽;ţ<Ş×+bŢ"ăN:ĘťX~”dáÜ[ű©·ş¸Ž(@„Q9#"ržVu˛Žqj7}0)+Á˙ 5˙ĎÉlŮmµÔ´ęąĘ\Ă‹÷îS&ôHýRC7¬}iđńé€ćt÷—Čú‰Du© ÁdÂ5˘â¸$DR'ŠţB‘¶j÷ž+\2]~ĘÓç›%YřY3ܧl-/ęt`/÷ĆŤ«Âž†ł¸§ţ"CŔ©Ž ŕ)ŤS]dŘš1 `ŃzŹŚ 0 )ĆŇ>ÇttG•@‘R?c&RŽźÇ$ÝŹ6Äiç4Ŕh˛Ś‰.Ú†ü˘ÚľPďg’ŘŰD\CÜv,JńVK¸1RM‰BQó_¨ăáHM\¦vşŠ_«i·Ú3Ov4ůíŮ—ĽŮď޵oWP+^˛”˝2jz±Ř2Ü©Őő%˝Ë0†ÉŚLš4/_“#ËâŹ`QËđü%·fżxđřÔ¶÷}łcńU;ÝŞ=Sowq ţĽ_7vüÇšaŚăN Ë&ˇ/ÂśžĄĺ§­ąž8ľť’CÁîĽmSú BÁčö‰@Oűíş ˝eÚ–ž,ţÉ'nΞN%đ6ŚÝWYô:'ÍłÓ2ÂŮS—cB-”ąžs»Ţśi ka±¨B·ŐOH÷˙'sŹ>b˛ą°î†Oż1ĺuľő< Hi+Ąx»őř2‚v =9ÍvľšÖu\ŁŔů<˝óÎüíÓ‘-[_ŚTÍB0ë¤K€„}nÝBs×=ź-{úBóW1Ń—˝ů¬©!ęú;6–"gŐv­Ă·˙|¤¬Î&¨X_äąź~b`P0/‡ťĎ#Ho0ŢaĽĂy†ó ćĚ7o0Ţa'™>P»ĎâÓZÓZÓZÓZÓZÓZÓZÓZÓZÓZÓZÓZÓZÓWµ¦“ 5ÁHÖšÖšÖšÖšÖšÖšÖšÖšÖšÖšÖšÖšÖšĘŔĹ‚zrĎĎ5âÓZÓZÓZÓZÓZÓZÓZÓZÓZÓZÓZÓZÓZö~˘řńäöÍëMkMkMkMkMkMkMkMkMkMkMkMkMkMt9Żdǵ·ű2HBďâ’Áů­“Í@HŻ+ŹńKńg˝açŮó/«7J’•-°?Âyw÷\¶\đ)Ü%xgņ2 ň6QZ,„{ż˙tv+ó™ ĹĽ[ĹĽ[Ĺ»1ş»ěe` 8Gá`ŹB4yKĘ^MMífp[_ň—”ĽĄĺ/)y …{;·|“#>X đ÷[Ě\Ąĺ/'ŁŻ]âî+UIĘ^Rň—”ĽĄTŻŮW®”Ŕ»)Ă˙HöUAs)y;×'U›ˇÇF)yKĘ^Rň;Ęí$z.]ŽF+oŤ_uŚfRyKÉÝk°9dz|xÖ2—”ĽĄĺ/#Çđďßĺ"řrľź´K±őäŘ9›ż]ÚV/iż^ů;”ćß‚Đ4wČ sç•ă´Śż‰)°ąü4’půƬQGŃU8¦”^łW¸ˇ6mfoŠÄ ž’˘9ŻńÝą%Őω3`ĂW˘źwJÝhs× µŢáxjÉŇ?ó¤Pí«Ź{ĎşńÔ›ĚÉV2›Wôé}ĂşĄ#~Üω;y©ř… "5?˝GÇęAô˘R%eok3 u(ęŐ?”HAg`”±Ź±  hIÄiŞŽ8)?8_°xH'\×Ű ÓÄ…<íč“JG-ĹOÉ­—~ŮFĺ‹ŇÜpŐĚx|kťé—µ8ő9řbďHťçěfD,ôW»w AˇgKŕşqłýázľÜ!č˘č´Ďăđł®n×űßú«ČN`SŠp˝¨´i5ëĎ ZŹ•˝â .4×úWřqwnÚ[J}ŰČ;j…ďÍÓSWÖîŢ% Fziô]‘¤×! ă(] jĘí7VőWŻ3÷ç &şHĚSF˛ë3 ’Ř˝Ź…‡X=č¤˙\é=ŸÚ…S ZŐ“:0 ľ˛ ÖJ ZtźiđŠâÖü˙fÝiS5L]ÓÚťµ_Š÷»#^hÍŹ=Đ´ęÚU‹ęZç¨BV†ˇh4’OĐü]ÓźŻřěś'‡É“äk~—†ď˙c·o@ÝS ×ryîCčý+ŕĄ@ŢCbĘŃŹR=a}p™_óąM7*Źő«„ik)lÇ)ëAiBRşXŽÍű٦ńżźÝ9‚‡eX‰OßLPl·Č螥Ö[¬·Yq!ž;ÎíKŃ<¨Päpk§Ťr{&79őňQJß]eŚ™Ş8ĎkPŁY7&ÖGýŞňwW؇şbă]e˝Ä˛ě´PWŤC 6+TžĹĂTËďĄoŚ.«…ŢokMöµž Ěle_.ř5k°ÉZ´x#ç’+ďčśŕžĎp6źsűż˘ä)Úk†-ŇÓqŢ ŃÜ/FűvA?ç|pvÝĽ×9ĄÍH>§VI¶Űm¶Űm¶Űm¶ÜO—˝[~exÍÂňI$’I$’I$’I$’Ë]¶5“UfŞI$’I$’I$’I$’ËsčĂü_›zM¶Űm¶Űm¶Űm¶ă>[Іü˛|+Öĺ«ü3 Ąč-® iűŐ9pŹnÄ‹XG{ě°™wš;ď˘Í^ Ńf˛r mŮ’lhůâň žš~Ś_˙qCâ• °ReĐ‘d~ś´h?W‰č‹yϲ*ŁA¸k–<ĹX4eßĂĽ h_űR‹°ŚôÝ2á,ÁwŔ…řť•Üyę[taň*°Šdć!°ÄţżËh<…­o‡Ýí̧„Ť`Äţhą•ŘBsĆ"|E99ľÉ5$€{µŔ1çńˇg”ɇ“3a éUr›ŁFĎîŤ 9pE%5PĎľQ°ÇPÄ8LË5­Ę÷Y–Ęĺ˝DŕÄG[ú?Łs@*f sş’[ĚNÎYZ—¸Ů“f|U?–KŮ×µUü:ßíÎ^ä¦ÁŹ@Ô”OC[Ť64Icĺ·>Ŕ˘ö0±Zr@–«ś-×fÎŔµ±uAS|¬˙/ Ňs=¸Ţ;Öa Ëj‰‰_Ť±ŚÝäĺČ3÷po%eBuë6Ú¶ćýů?ZŁsN{$Űm¶Űm¶Űm¶±Ěďśp[cŠKŘZŻ)ZŤ“b‰ËÓg|›j7PŃĹgâĹΔ«‡‰ËĚ3ö &€Xk ’ÜźQÁřGçy O$)«i,# B$Ż ČňÓčđrÁĐŞš&owř>MZ4ŮPw[ò×ëQZBŮ{ éŃŐlĚÖ=|\łŰŞEą^Ŕ‹ň0­ŰŹHÇNUNT’m¶Űm¶Űm¶Ű;{¦ˇR$ЇžkŇřaxG:îŔÚU{YÉŤB»Qui8¨pź …ýGʏötţ ňBł^Źňٵ˘ĎUrM‘ظʼnŇK1ÓÎgŽŽŔÎzjčáy'«ď0*vBy•%TLKjלÚĆÖŕĚóvł¤5\óí$•eˇ|-ÓÔäTHş`AWjćË•·I¶Űm¶Űm¶Űm´%ĺUwí¶«Ř_lžn¦‘M¸{ FÇ5Xł#Îȡ'7g2PzoýáDOż(Ëb8Ťqe˙íOsQCő˘=¸>S,ąż¶7s2b°eż¬`×âţL‹¦[g’dÎťO#h‡săŚAÄňôUŻżżššéşbÓaű?µ{ň“m¶Űm¶Űm¶€ń*t¤ÚLw§čŻŢ&đJűJš\X· ˘_üuię]’TEh{Xťn[Ú9ZúA+öbI¦ěµ2ćz°ü±>> r‰«|,¨AP)$˛Ót˙ZÚ^`ĚmŁŚu}Z]ű­§˙}Ă9ˇŤ$Vw~&3ÁzpĽk|QŇx GŐ+˝€ h“<ŁćeF<ĆT´DMŘ€Ůc`\â;ÎVă_[K®±1n@Ľ™'gY˙nk¦T7Ź“čĆ„ü†D@+éăjJ‰8Ü^zkĚ1ŐŘ "‰u  \`“jZ qű˙NĄmW¶‘čöŰm¶Űm¶Űm¶Űm¶Űm¶Ű€qáw‚ !FßVŰ;Ć:Ý[Äĺőśpáůl“ł˝ľ÷'ú<ĽżvŔx‘0ăFßY©<—LqiĚňŠ Ë• K&唡’I$’I$’I$’I$’I$’J˝ĺťľ+ęľÍšÓiçć'ä˙v_ť…"˙4ňjđQµ>kéŤhX¬e ;ŠrgĄfőŇK­Ad€ m¶Űm¶Űm¶Űm¶Űm¶Űm¨d@íámx*Ö^ĘůB â¨6·“Ž&ŮóÍlŕWö ]pÔů˝D† UJŐ›©ë„źŮeç|ŢÜIęm¶Űm¶Űm¶Űm¶Űm¶Űlô—řéß“Ďú oá’!“1i˛‚Ë›€scMĚÉVŰÖ¸ě ĺaLv]H;×n˘Ž,Z°Ý…Gšţаf|S’gýMŢň’7yYPŰÂG¬™ŻÂ÷[ůŚ벋˝,g~ -˘î­$T±ÚjĐţZąóͧ[tÝ sŞŤÉ}i&(Ŕ—ôť±f—b‹Ľ±„ŞäŰ5r‡ćY8ű·ťkt®ÖJ ˇŽÍ6|ďđ9”DÖB§"1óÜqMĘŰ|@H¬h©E´Í,ł +‹=ŔsŐŠ¤ß‰7Ý÷ÄaµľkôĚłaĽf¤"xµšP]‡µ+•sËÓ‘sÇgg®¤ĺSj]üpFí°‹óóÍ”!îB±‡‘Ć[4›#z%ŹéŁĐüçj]±|„B$ŃŰf‘őó™`Kžůńď[UÖîôÚą´ÜWëýŤřb§^bP)ȤŐ~iy†ŰşZG·ßóŇ<-¨Ik˝y41‚PĄË"ěó7cD'ÔtžµŢčhč9ź{*ŤyKIˇKíUŰÇ•°š%3 ¸zAçËůř Č CŤęŚăË@bÓJr- ’ŰD8 ŕĘ@ŞS›Sűő‰ćZîµ<:Ô…¦z­ő¦\ÖŇNcÖçŻóĂx÷xGő ˛«u&‰Ź|}/LRnS$G©Źn‹"ípKśSáY®]­Ó97tÍ]€#ČőÁµ„ŇčÝŮ·ŔrG ´ŰČA¦P5ÚÉn“I-ő† Áîşż&Ě €č i„Uwë»ĹĐě-[lĽŘŃo,5Łćq]„ÔSůßdâQ4{Uśé˙ng¤!jĎłiŹgŐŐ°žĚĹ•úú@ąĚś'íăůł¸T›déR!`/Ë7édv±Ź%öánS°iń>h•€Á$˘F%%i8Rí¶Kď6í–ńm§ľŤ^iB~áŽëąŕu¦˝É#¤RżwާB¤îĆŽGůŐóŃŚRoÍŰ˝ľ[yż6©Í‡HŢ2‚ó Pżś-WŽżĄĹšĄ5čŢt’$|9É8WťŃ"»˘ÄST!ŽÁ÷»Łň˘š›âďşPV.śmµiź5®Ř=ś|¶ďH-·._Çż‰ôµ<’ h ĺŠY;.MfD …±—_ŕ±v%ä2Vĺşö/"ó€ 7…ŹóË€c zsx~c“p‚7ŕ¨LÝ®;ă^íŮ­T8’_ZpŐ.!ŻĐéÄ5\ 5 Ž1=A|ÇO8%~y¨Řʶ-¦ŠÖďz±'§Ü݉4 z˙€=dWu3o°ÝwÚĂd«RĎ÷FË{ú‰GUł4E×r¬ lDý…s4Ąü'±öMęGy?$?hŽ"Q†ă4ZRńÎ}Ţk\'ŘZ/Ë›¶Q› ťg×rúS˘N뮯³Ůě}‘ŠP9Đjj:_@×ÓOŐ€Ŕ"˝@·|{ë®üg$ŔXtÜ»Ń<ĚPK?€ÔźŹřH&™`ö;M84o˘;M\ŻJ#ľ ,–Á“NĂiâĽNu“Ęu*áśTëk*|w|ç˙ …˘YfĘÔ[ŰŁ®$;JĽxqEÂyFëO€ĽX)zĐrŕĚaůŢöOiG^ "öřÂÚŻ„­Ž 0űô˘2řCŃ*`ynË 9ýü§žnZş>8ź±VS «[âa3Ő-}şhĺ2Xäîéśaš “^aż˛¤4gŤ‰ĚÜIđM`wöhG˙#e†%ů[żi=SÓś·ďÖ Í:GĄ’ŕˇ*Ř{…ížám’«ëŤőÉŢIŔ©=ČśFô{NÎ´»(–A“Ő0˘«pd9±&÷.Ôŕëĺü’ ‡aIHVcx 3"¶"d; "5Ú•aŘK@8¤Mj“€WŰoö‹Š=ü•ýţ#§~vR€ ůňü3Ń ţIqĄą-UŇÂţeaŔ«kęuŠĄ[ŔsîiřdýřDĚĽ—˘eĂń$Ű-Ýć×6dRńżćeťLc/éż?P¦)Đđ;Q¬A0˝&˛oŠěîLűűQ°ĘŐč;H‰Ä“QhĎń.q1ąEdc‚éŁR6ŕ¶‘–3Ľą^Uă*vs ÚDďżś“lOSTnřĆb´ćÉőK5$Ô˛®špşîd-dçîţ´‹  J4Ľ~ß(s`¤V×%4Ý] ŽëDú–ž¬ q,»$SšÄ™cÂPđ“#řä6lŹľ>Żşk˛§ľę˛u+óĄŢ'±Őçs[$ŃMLz䜆uÍ™űaůŽS†m›¶ĺ\f…iÁdމć°fH´L9ś‚zÖĹÖ§^’'&”}¤ŚC(9†i ł± $ŞNs ×NŹą1#_C1ˇŃ©TżęyDj¶$ľžQ‘ÔĹËčš+ŹujФB ŚŰ:ž¤~ZÇ8™ď m™†ť%­ć+%öh‹G®’ľY©N»*kB%€Śˇ}-Ä •ľ I&tÓ ‡Ą;«ŮÉÇš[µěRĽĂ·źÄm[¶u¦ 9‰ąb`mÝí* ď¸ő?äv‡éż©j r ąR…V°E±•ük4O:NľĽ¶G‰ţÝÝŻźµĽkNö¬”ŠëŘßǬę u†{Čď’eŇÂ-˝Fą›Kg‡Ű“©ÁŕĐÇóסcé~…–#-Ő“ŚĺZť‡őKéźÇ%ź)źo –@U9˘/Kgxö0úćrg7ĹQ–F ˝‹.Á<řx™ÝüµnXŔáčÍ®K­Ř »»3ŻíŚíôa{ř„¬PcuCe‚A ńśqĚjH#«dŘjÝúT¤*ˇLĆö–”Č 껊šÉÖ¶Đę4ô˘cF0FZA?ĚŤd+ż‘y.Ă·łľn/KÉöăđÔ΂¤†ŽQĂŮ7˝ţ GA h`©V^ă¶“Ýş:ţŮĄ’#90ÉđĆČÉ5E€AÖ>!v€ÝĎ` Î…®¤‡^ łś˝U°†ÁM´J‰ľűü›tu’ťĂtůđb¸Ő¶äô/wJÖ›Űó wV|šD=Ňn]šÚ=BĆSBěďř2 Zµ¦i¨(Ů68´/6Öʢ!“Ů%´eAŞ{XSpŮŚ”ŹJł|NŹůQ„ÉƤyĂ"ćß9š±đyÖýAĘÄ›Ś˙–` ™ĎˇąÚŃń‡0ńh®Ó†É`•®ŹÉUBa“°rkĹIkżÜ˙ Ń{őJ>Ddʬ—¶Ć9Égę‹ĘZŻ‹ç~da˙`0YץreAŐöĆ8ĽÍ%$šHőß™$íŘw~żc¶*VŃóÇEá;| M =I;‰¤Čz“2yvőmoĄÔâ2ń\7YLSEč5Ä8±Ö°9®p„žFořŰ€#‚  „1C Š•QăÎŚ‹ě“ßBD DlÇ8jçµy?ńŚ!ŢU"đ»Ł.ŰŢĽśÍŇq!ÓqŤu€´Â^”Ç«´ž^YíĆ'ťň“ł6łKÄ‚V¸ŁĘM[‰†®Ô厕šsI}Th±ZVvĺçĐg3-|* żŔ`B,YúÁş@7Ľ§-ísç†oE…+0é±KSÚrRŕO67ăY’“®’«v ”Á B\ćŁň¬˘ )šźHěwéYÇđU™8Ăř°ČŹ>‘_šC%î`Ťžuß•jňD ¸\ŠÍ¤°fŕf!†ĺ†YŇTăČ®ÜçqĆş ˛¬˛Ă~†ÎOgĆÜ”-Dď—żăňçMPoG*ëöx‹…đ=áŮť?¦U> ´·ŹuŤýŘ…ĄQZźjŞrL{šÉĄ7[—  )M±VˇW3Ě-µ®ĹŐ±ËW‚Swí´¶XâןLŘl| kiĺ"¨ˇÄŃśwů@šű Lޡ’ÝYn0ufpŮ+^F@¤ţd™^ź÷żă>UX^­¨,$°O?_%×-ŠÖ¶g)j. ăýť[ÄGÇ|ŁëNĆO ű[á­Ů? ?Ťóôđgľ4SÜ·¸Z† ń±Ľ;®tî2 …*ަMZĚ‚xźď“fčŮkŮęŘ6˝1Ť˙úGZi©40€E,ńSˇmj鏬é!^Ô&4…Đ©ů9¬ŢÚ›xMJ0óś­¤4VąÁ!ť&ŁVĘ·ÁŻ˙8Kż˝&¸ąpÎ,3†\eKóKŮ|ŤK˙8+ Řó~´ :x—`ń%Źaóçë¦Ó›¦~  Đ}~ÍQl.ďEŰÁ(­Č˙<ş ľĐ_‹-5?mEKt^ô öąµPŠXz.Z|cšBh´Żśýgžé´kÇŇd“<^€đ%»Gá“?eOďId›–EoL1ľQŕ¨˙Ëc’âľ÷btúEd™({ů^™ć¶­`Ţ"˘Ć>–lJhQĹ݇t[ħű}ťĎIŁiâׂ,·3Ę:q99ů}'îĚ=‘ c„ô ĄÔ0ęp2h°đvú2…ćE Ăę©c’Ⓨ8üźŰU¨köüń*Ü´š0ˇ&‚mŽMmř_ŢÖ÷ÍqĺôŐ)QÝhť¦-!نţpŁpçg`çŻĐ@­ěÔXeŰ@ÉŢ—Q,˘»%A0&‰JR~µ#™~»ÄŤ]YOţ)}Ťd&¤)€ęmFŐŔĽvd­Ű—(*Şý;2zfbŮióŻ\-ĄÁnR((„j)Sń|·ýÎęD='ŹO®ßŘfE* ži2‡ŞJ÷ŹŹS¦Ń0eýŽ^ë(˛éÉ‘źŽ%¨,Óü•9kÇłÁĽj}‚găé™B˛·đe+ăłq=㬠l%LČţp¸:u· ™ó ¨ß‚j,§ó#ťI÷§ÜŰţnB´—ČJ€›fcĂŤSč… Ł‰ätJSť JĐ)ť'É’¦mČE ľĹÔ„1Užŕ•0îgc,ąrĎNL» ‘-`t…”0¦»Ë0±.ŔqÉ™3•0űUŁÖ§ťŚ÷:EKůOß*şí{ÍRA®¦Ż©xQfýĽ˛kž…&0Ź­©oÝç;'É—zFnŇTÝ1€‘¸ ń»Ůq~ĄqÚ†„,ĺnőŔÉuěC.ç_ŚNoWĘMŔTLÇ~ɬę_Ă:I‡ÜlD‹â޸b1:b—ů[ź˙b)ßÎt…÷°Ş˘ä–˙ řÔ.MĎŮëiL@>e{ˇ¤—o;˝ĚІÚ$Ü1˙-ťlA1šJuuéU_E0Şˇ Ę ®CţuíŞ2*J•ŮpÎ4˙=ńĎňč%V”ĐŮčţÝ8˙6€GČÓöôý}Z”ăä{űt“ýş„~Ő‰óŮZ/Őrçç«!×oź¸żź íŇ~«‹ľ«„ýN¶hn…oÔş‡ĂO}]ţ­ hřkżV÷Ő}©sö¸€ę%č~"ęcč;Ę<\Ő3JlGG~^‹Bm B5ŰPuŚľ]˛q‚^Mć›É[ZKüáé +;Ą|´ÝÁ}ui9nż\á QůxŚ/Ť»m͸lÔéxnČT‘+geÝ}÷–yx3vŰ#¸€’—4Ţ»±ďŰ!4‚Ţy† !‚͵ŹB÷şÓ GĄ*”ŔĎod]´=¤čTľş'K^Ý"Aţ.”‹“}k>Ȱ'Ą¨‹ýÉ4şäNx™(böĽán łá©ˇD§‡woŰ#ŘfŔŃb?íó^ŃX+dG‰7ŽO9˙Ú_'}’ʶŹ Ąř:Ě·]™l§É¶)ë™’nőĆ6§v‹ŐîĂż˝őutř!űßžI˛jŁř bťćC# ďXMdi‹ őůz ¸…t˙بj<Ă˝ÓZˇ‰uÁ䇷,ż_˛_ŚÖ䚊IĘ©N♡@͸öŞav[FęEÉe€:şŕĐ­d(ç3§ó4żěÔoËzęĄŘVmßż™Í ®ünb¤m5†ž \*Đ'f˘#ň×óÝ©ĚĺEě3~K=<äđ×صÉ46b_’Z­»óđ2Ę?´Łs˙C6žĎňYí,árČâ´Âkj„#e­őIńH1‹v@Žź•ÜDźa‰[Báô_O /‚ Á@ű .˝•8 SÁ?ŽK×ţçŞCŢZÓJĎU¨řX*dŚŢ‘WiĎÇ ʉÇá42nRŐymĄ4´7Z ?*ňĄYŢ«vSA 4«¸QávĎ:˙F¤Ă=ć·9g|ěĺu>`÷ŁŚ|˘/cv™LŃ9 uO÷öŘ“żqív*ŠłąÔü–ř˛ ŢxnČLůřŢgZs—D€˘gXśÖĐâ®a~r®bµä˛ÁW(~C*ćšP¨©Ň_Î/Ńh*J^Ĺv1 ĐŘ×L¨Â¸CvŤ^ě"Ă€Żćß)n9 čB˝3Ť Íë%(Ý’](W 5G,x _ç_¸ Ń05šł‹¬{%?‡#áďŢígħx¶ĺO°’=¨>H €ĹiĎxĐNđ‰ż &Ö15 N6ë$š\ÔôTYgľ·4L ÂÖć'‚K”ÇâŔCmôćň"ui• śčí8eÂşµÄp/÷~EpXŤńńśrř@EĐfm#đ@zpü0~ŞÜ\~ú&\76ýe¬Ş·%%yÁc!ąë$üÝČÂđi;śIŹ]mÎË żvă&˝3ńĂ—’©Ő­"Xí%’‘´Ďl˛J9łOţz˝´ż9ÂlUź˝c@Ă$€ýł-ÓúźrÝQEvŤE±çYôîŁ;ő€Ů­‰Nš|k~°x/B‚ô§ŹIaP÷hëÓI¨ş%'ËG.ÄkE·A[‡‘áe}ęibQ¶ëECĚ5Lą¶‚kY1ÚxÓ"?â6Uî‚Öm7·3Í7k°61ĹŐúöóęâöéóÉí…­?JH@L´±g®ŘĚuŇźçHçM…;PçHE.ťüqJRhO©Ż¬17•’‡¨˘^;Ż­šXżŤç—ʍîŁmkRô!c ¬oŇÔ*ˇü߉šúyŚe^oµB¦ÇĎN*¶)˘ DlßŔ ąQZľEűżŢîCrň @­.Ľđć”Ňź÷^3Í4…ćĘ5ÜřşeE;‡°ßE®tŘîń˛wüż°Íćóf|ýÚ—Q}x KĚ1q!zNˇßXb3¤4±‚ŽżHăGÝíleę×ůŽű>X T™=Á„čOS&¦Ăč 0`˘üá“Mußä†×qJ@Ć÷°Łe…ţčµ'6g+.ćŘ}ÁĹűEd Ôáň@ŮšSjú5jYŹ?„čÄŚÚMă/Ľ\TR<±-·Ěîb¸ü§g™číł);sWL2uĂKŁ2)żUQ? Hú|G‰"ŕüŁç4Ä4ŔÝoí ö“XĆ”Ŕ f MY´MOâʇpí w/*1'$%Ń~újV×§µ˛˘aâNěĆPD8y«}[Ż!ä7™2śMĄË•Rw%”3Ë€ĎS‹UĚĺůľŞo˛ î劅Lâ=řĄŤ Mű(CŤ“µl5©O©‰{ ü”,m;?:ßxprQu™`MŤuŢŰ“ŚI†)•­7é8<Ä™b»Íý }ąŻËxZí=čFć0łńЉ,'ŐlÜ6*?ÓĹ©‹5trpŻë)NÖZ/ą BQ‰:­äŻ|„!t#Ľv5>޵ś°zştWxČ1Źmů„č…đĹ9öâiŢŻ#őéš÷@Ľ ďiÎ|AĆŘ}xąh‚dµČľĹg ŐĂrąFż”ŔZĽÖµŽŔł× YžAő-©¶ŚS{PĐ€‚b^ŘŤÉ»±|8ń†ŕérj7z˝őÜł6şm čŠŰ’‘QfÓö s‡Łť6ł×“™®=Ő@Ó Ćťű˙_—ýŠyŃ?Ř1Ç‘m >KŕßâÁÜ5ęăLPÚOřOoŮő›¶­Ä›·j‚Ëż±‹—Š ­ ‚ž{Đs݉(@-lÄu–Î~®Ď#žŘî š‚„>ĽŇĚ‹˝?rž-Ëă7ř˙thˬCý ±YĄÁËý˝rzO~§EÚîlČÄŇ×CÓĄ¸» ÍČ"đc#ö€J™đu.oW–őűoĆcKů$ŐçgßĆ9ĘÔ¬±Ă‹¬0â „f (ü°÷ŮeőË y2SËŕć ÇĘë9Ü@Âżň»ĐD”í–^lŔóŤS„»y F\ŹBęĐÖ4°@Ú*†Ţ|ĂLÖ,˘ąŁ$˙SŃ\iÂ~há›xíIÇurŢcľ’I#;Zn7\=@9ÖŰäęt€Ö ™ăçz¶’˘=ěîĚ1¸CŽ˘‘‰ěşäfćéĎ5W†+úPäÂćaw©FĘéřÍâ ąŽ ă}ńť¨:®tŰ([ş‰"ĽeBt(cŢnnK|ƵĄPĎĂAC;뻦•éďjŕ„†xz3 đbOĆŞ{ŔVă®^b?!ŽĽUż’”rŹ“ż´)'’á¤l‘Á¶účϢ­x»r´Šx U˝ŤÄŻŃéÝe#ÍĚLDë¸WŮ.Y±)L ›6đâLĆQů4¸‰íě–ę¶~#2G¤ĽŇÚ‡~6ŻË ąE-Ş"K*ňíáB•+`R°zklµ5—SI‡aż©AŞ(ÉŹŰňł*:.—»KZű·@ŃzČ(5®WÁĘ+ňĐUĄňĄväá6á2N#°Đ:ăâě6Ćőä[ÄXɉ‹ÍŤ»ź˘‚M];ńĺŁé<ÝÂ`·0±…©ôÄ^ҧo«D!js.A®ńÁ.Ţ™6ňŁĘ)!iűÚôXcŠśsšTť«úa^ ÓŐü^äéב"Ľúĺ´Ń‰pă‘Őfh\‚°ţ/ąęb‰,söńí….•$LĂĐłŁnżĆ]eŕY8rŘ$E¬S2–?ť›aĆ´€-Á ;Ť1oË”gżđzřĹ—ÁĽhÝ- 4Á<Đ,– ţH+Ą®]pIĂ/äĐěÝ˙9±;’­DuJ˝tŃŠAŕ€ö»}iÉżšOgˇ áS[4‡ %,*w"ż‚Á‡í“ţ܇’ą4 oć$(ÎKż›Ě¦q@#«ô (PXpýŇýý{í8`äŕçëĂňâsxřaĘĺf$ű–Ţ™ŹŇ®E»§ Ô™úŚÚ¦-l)ËĎp$ůÍްËDăWáudßC-–CĂĎrA„9ßBŁU‰4Ĺo©0ů| ţ$“ ŕŕ˛ń€aZëM§Ó:íĺé`¤†Z#jĎö§V\ýŻúRĎ!eý-.´Q5ÚťmťĆ§ŇzŐe}çÁ*"č”6çíÍ“Ç4ŽX$ŘgË–X[›°˝^čúŮĎ‹3źGĹś! E87‡ŚPëÇÜ ?ňO}XřŤć™švčőAĹʦ śž>ä´©Q¦§n>»@2Ҷ,¨Ĺ~Vńş‚ľ…LOČÔcÍ–’7y˛€Ť6A ŐťdíĐ)˘Nş‰ĺ™łăąÍE—YgŻĺşWź4A2däĂúŞđÖÄŚTĺř’äś™Vt´ĽŘ®\OtSŁĘĂdPůń€Ä+fJ˙+˛ł´˘ĹŃäcĹş«śNŰŚ'!P§” µm©˝©”Vłi)[I†E€đ5@vF(ŽSŃš$rż‰ěôSŮîüFŔş’9h"3˙2˛…•„«ŕ;‰•ţµ’-ÚŐuS€\Ú&%Ä’© Cf?G=ÁŤ2K§Ś2m* +8;2%¤xEz}®[;ç¸Ýšý%@J«Ű»mď ?lbč"v6ŃčlŃ×Ĺ˝’Đőň3Ň—Z ¸0)ď‘ă žL ľ†«Mć´źĆţBvę ˇF’ۇčKä+q“o`g”ú<ť«´´ńB™zoy2ÝĚfٶŤžEĆ2Šţ śŞń”Ĺ1íVđݰś|g–MßÉ Sçä„s¨’¸žšĽ»:Íó¸ň 3ł§žŁ^–®“ĺšąÓZ üqňîRŹő d…Ń(˘mŐȨѽaţěÄÎwµgáĉ,ç|" ˝ŮČŞól®4mŔ˘ ……>…Ůđ/>HC\ ŻÁ#~'ÄvŞuMo°$·&5ép§#ŞÇĹÄ»KË·Jk íó÷ Y˘ň$ZíRĂkéOcz<ĺJĆp _!h4îXŘv^yżÜ‚şűPxąŠéÉÄÝ$âš9&Ç»ÍÜSV“Ę4¶ůLˇłť‹ý(uâĺž—¨,5S$,ŔŁű–Ů4“5ó0®Ň+Ż83Y¤1ył4•]Ƭô?%:ňµ-rG ëÔ -Ŕ0y†5ŚŻÁ4hˇß}ę J¸ń–#ţ‚ ­]ŹŤ‘-ń¶§…‹ö^‹–ŰŘzéw­a»÷ú“V3I˙vÚË[ěEBN«Ë˝î Î`}Ť“ŃŕÖÍăS†bKä„괜ȊZ…ň¶ô($ňđ3ŘbđbÓ®uYtčŘ•Ĺ9‚ęśö  ś—%´5Š:,˝ŢĐ ‹žÚí0öŢiç–ż!˛b yŇ0D Ń> 匀¬óÂ÷âj‡.ć°ŔÔK;}Ř»ŻÚ™ űŮNK™ÝyřŁý߉&xR˝WĘ9KmÓxi397Ĩ&ŤŻÉGáM©“„TŁóÇU0Ń Ű& äŮ‘lěĎ]ÓÂůbşj»˘gčÔľPŞŕÍ»ĚÓ±˙lîşK(6żx†Ą7ĉĘČă‘jŢNy¦›Ç‰Ť‘ůîüIESO—+¦E˝7~ű˝¶ďc!Ţn|{+AKři˛7Ň6©»üŇÇ/w¬ŽT×ßĘ’7Í }'ŤIÝĚ€˙8ęÂoÚ&=](¬3çÇź%>;žM5şS„ťc59ţŢůŁ·Čç«B0ąÍ9ŚgŔ{çŢĆgC=,/ŤúbˇolJłľ¸îHç&źIó2-őě¬!îzw ĹY_YY1J‰4ŁÂť†’n„X8dE2J×ë‹•OËÖĚëRůP˝P˛śSű6*M˘ŕiűŕ¦n“ö/Ű,L~×”€ü˘` ?QpPÔPK‰.t ^®áżPÚöąđĂ~í˙'Ť'ç~Ě‹:śl®–¸Đü♢ŚËj›ńC¸«©ZóśĺG`÷`lp’âxÎťV~jw¸d˝xÇŘ3·Ž†ŕz|ŐźŹ÷S;FÖOşŠř_łźĽ ú !•ÁÍ˝čH&˘2HÜýn\nY”d™čŇĘĂIłĺE˘RjÍ&WG—`Wš˘-DšUŇŮRÚ榭:é6˛.:ůâPý•Yŕ·ŮŃôT:óĘ};4ĚI·µŮ'‹† CâůŞčž…0g2ęz$2™&F9Ś|LŁŮŠ<×ŁíŻ–@Ü›x¬\Ţ ńŽ«4z_’N‡ɡŻ.‘vúk%ńOąäýłT$0éG!Ź›ţł“¬*%ˇ¬ť;¨ ŮNßď¶\yrߥ°Łâmćú±˝š­ÔtxQ3q$4v[OšV|˝Ą2A<74†@˘ł}[4Đ #îMk©#ťřžßăUe©%ěť©jeE|űqq®–-¦¦ň‘/ŢôëRdLV›Ě¸ťeř€¬QS>ÓłlÎáQ{ÎCŻĹĽUęÔJ9bDę“Ý>Ś,č¨t´q?Ř^©fQ|Öî(ž€Ý&d<Ů_őm;0„|´oÖ/mb5Dčuď˝3–GžÂ*}pB-äČ+µ•ýo¨î-uÁá=ďůŠŰ¬1Ą€ż"ÉU'ŽYFöć>~ôŔM Łp§Ż†Vr„PXľ°ĂIÖ'ž żcť\¦)ťµ^ ŕ°°ĘM– CxçÄď¦`TűZ!wźIKt9†¤UąRĚéţ˙`ľŐKííűuĺżk'íŘŤűuĚý´źíĄ/ŐŞűőPËç¤~®°VŁąóŐ?n­˙[¤çí¬?mRĐţÝY˙6_ROíëö껾Ą/ŰŞíÓ#öŃ_¶–áC¬ <06HicrÇý›š©;ŻprÉ ~čăýEĂŢ)ăhł¸C6X ˛Łh‹Ëŕ±r‚‰üÓBdB°‚&«:‹ęńYB¸¦¤-ašVˇ3môű÷rţ-;çÝlĆĹůń+TI‘CšÂrünOr0qiŇ[/‰X˛]źŃŔ;Đ;ńdOUĽŘ{Ç ˛ÝĚŕpýč‘…8ýéć´\˘2…d€ŚˇŃ–Ę8ךř3}Xő瀚ř$XŘ ·úĐĽZ“iőÇv ó–Šţ¤¦s[.L—LĎ Ţ®kh)ez8f“ýäăO2˙ťkca\0&˛ ‰F‡ «–^qŮjź1„Cí­4¤ą0‚‘őg˘Őˉ’öŢ«+íNĘW!§aÎ0îÝ„¶µoáĽsYWż¶†TqĎŰŠ?qĐň/±kőýoš5—,żFQ Rđ÷Ţ–´˝Kż©‚{pCŚ„şáÓ^—-T\ňŠîlsYH2 dhĎl š9vU/ąádH”­ó@í, ‚&b1Í‹Ş].ďăP‡Ý*_|Ş<+®ó í¤±‰xVú"ŢQüÁpŃĎ‹­( ů·ÄȮВ^cwsä Ň"hw "Äý]ä}¶«ŕşUUeS‹ĚÓĐÄÓË·Ňq  ¨*ĎŃ'Š3’Ź­˛6Oůď™®ÁŘ=°¦°řî›ÂÝ‚X®Ł…đŇćLÖ+,„6'.•ey–KşQ|ňCí­˛vEHLu߀WČţň‡ҤC® ˙%ÄoóGU›¦Ř6ˇ%_Ŕ¶Vťădô¬)Ó5ßm$gh«˛a‰l°‘@š/ŚsC$-Ež5H\qRÔ÷Ľ˙+ˇŚ9ďR:s GŢ7´ 8*ói#ć¤ęN7×÷±Q«Ĺa$Ξŕ`ŇuÚĽ~_™†~gôąâWGtr?üŠE=ć!~ ąO;4›Ňź¦ĺ\ĐmJő“!Đą‹üęŔ'5oq˘yÔwÇíödSÖ77ÍÍ*¨ÚĎÚ)"Ĺ&°@—_Ú0MŁ(áő ąQ<šhM[koz´Ç‰đĎ}W‰a+–Ž^đ%ä îl©ÎţP ­ŁňI•“ż›.Ć~ßZáUłä ë´‹ Çöň’ln*ŚÍĚbh­Ţć׸%±Ăclaâmvçs幥ŰÖAÍűëř46"_K§p—-Ě)©' ŕŘrąÎČ+Ňď~ ´'y¶—ăP»Ę˘óÇ ÚŠ çµ>:ß}›:=c×3ś^7 S“ ßZb7Ęß $Ź@#¸Pm"McđÂ(ŚKĚO>WI·‹Şz¦Ş­yŽÂ”¤›'~sX·Űi9Jś›Î‘.űĐ•Őč Ř[Jý˛¨ –h´;ä â鋢)”$ă6Ó~¨†·zŰ_ÁHáüí«:ËĄ ×…ůł­çDśĹg/;ş¬hëáp7 Ow}OxOqQQFdřő7Ó6‡cßq\ŐŢ:ůÓĚOżÄşŮÝl*§6}×ä80ŤŠZŰŹßé+tB€:řáŚŔ®”ô­FPÂu¡ýpÍt笣¦”†ß˙^inđ|tŽć]Ďôcsy ňÉÂ*xN.ýe(IÓÖpö„Ś'Ó>g*&áo"CbxvK"ű$7{‘Ä–1Řšc—­7ĽüľRŠU).FűŃb"ŁŁzńsĂtő…(gěĚĐŃŘ3ëm‘¦ŇŁq+6©C­¤`Y§Ě—9¸úĂ­·űňłKR`~Z“hząńŔÍŢĘ©{_Žń9ĘŮ7`!ąMü!}i-żľ+‡ŘůÍA»é8N<ŇĂ]z'ôčÖ HÁĘC íÖ™#ň6Iš{ÇôO•.cI–•»éř÷˛SŢ–ÚŘúä%OUdČ«ÂÝm÷‚8°˛oß~$P®XËn¸;‡!i Ëg´p`|¨îĽŘXźwL ýĎÔ¦ý÷‡")Vk÷“ű yJ+l-/jε*DVd÷ą9'­pÂ!ąVqőFhTĎż/–žS=2HA}übĺˇ3T ’ő¤čś)Á/´¨xm»5­pĚš¦Ź!ĺ,‰ŤEµEÄt_˙E¤ŤŘG\§B°fr…śéę–ĎiTé˙w›95ý<Şý(ö±"÷`nŽÍa#i§óű 7ĹM4ĽŘ¤EC–$·Ďňl˘ %ÁŁ*2ŞĂô°Ú)˘UĆÓ˝łŔ©Ů‹b÷Wd€®$pFćúÚM¶)‘ö–Mzíkj Ľ?Uşäétžš†9eŞź))ďicM®őÚ“N,%ŤřŽŞ±«ž:Ą˝ ˙o%y)J“ šÝĄCµON¦ř]xŮ7fkDĆt‚s˙$sĂÂ7–¬ă= k¶ŘvÚĶlH}@ă5úĂą•ú€S"[ó'»Â“Rv®ľ"nUqŮM×,¶KGłŤ”‰śLźżŹ‡7lřÍч¸őŔńE¬IßlĹÖ€vţłTUý„MwŔ( ÜKXÇckŐůǸµvJti_üđ‹^żePňÝ.Hű\ÍŹľa™î×sśXm» <Óšď(ýw˘FŚz˘Řr¬8ćnGběc‰íÍýş rtŰ{Źnť#ľ~ ŤéŻŰ1ĺťdăČko˛nT e°¨Ś~HPezJÉE›/ xĂ("‰¶Y˝ô3`Ô˘uTQťkD¨çôN_çŢ€ťŐ~ą™RŠ9|—1~.B§K©ď\ę)ŹQú˘@y.r܇®1Ú-`nJÇvŇCqŔ;ôűý…!ZGďľÍV‡Ť ÎŤĐŮ:09rqěëXuŮëSnNń©—\vĹv0?ni€°î&Př›Ţë Bó,ěÄcşŐ¨Vęę(Ô„y[­ĚF(˝םż9Fâř1ó1ů»N=“Ř’ óëŕé;yz1/ÉR…’tî f‹`¦bň‡sýĆ˝P ť¸>p‹‚sCL驜aixűTh¤+Ó)b˛˝é7ĽŔ:A|ŻMnŃ+3‹¬`˙áů8."tp2íg8÷'gnáÁHšĚí.g5‰óĐcNľá AůĎő;č&w„”+,–µôT›’fłG”ëýLĹę7,ŕ~rK=Oügö7edŇV¦ŕWÖÜłéřu˝'ô÷‚+Ú†÷l'1y~¤€<«ŞĄ'Ł„‡Văú)Éą#Nz€±/ăEgúČ–żµ¤Kš±\"…"bĎçm)܉ý<1‚rÖ§–NşŚ:Ń›6źŁłđ¸ˇ*Wf††xĎ_×M'',Ť(•DłŐ˝tŮÜđTŹL—kG ­Ô¶Ë Fcžłßô¦ÇÜŽôăń ˝1ÚµĐK€ś 1•ŹI_šźŘŞâ“ňő™ôÔ©1N«Ď}—çl2ťľ Vvß,íÁçrđŐôĚ”ć?šĆéQŞ­înÎ>M¤SďnsµPbiĆe[°ňł@,î<„>ą)×Ń3ú(ÉĐąçű¦Şd‹ź a‰‡Ł•l»Í¨ 4‡Ž*¬‹ÇňľçAšáH{Ë’ôU˛w‚jí¬*­ňúnĆ™¦ ±mîH ÖH˙mŃŇ:6¨Q)l§ÜŢÎÓn0&»ĺľ‚Ą:4ę6ęů† w9lĆ„óŇÍýňmZŇó<đéNĆ>ŚňśČą(:á}z/Dkű'ÉHSbÜ}’ă´Ćź-(r‡ŔJG޶¬`"oH–LŔŹpuPv¤€ÝśUn<'_—™Î+íđ $öUĘt˙TĐYýţóڰ(˘KDůĚ„řđ V=€“_P¬fŤčt^OOź3€Ý‘yŔ€‰UŁJŐ/ş\ĎÝşö†8>ޱŽĂßĎ1™«čĹ/\}`^Ůíş ĘúÉPV-ČJb#źO“ AXôN?%1)pż›žłŮLĽ×<ĺ ‹g­ Ó¶7ä\ÓšÖxůýeÚĐ`gŽ@Őń§Űá͸Ýě(ňŰŔwJĆś–öO82Ş9|ţ/Đ4Ţ;ý¦ ôďŻď…ua…Ń3’ýóL?Ö aEm)…ÂĚzqUÄî™vë/ćť;ľ_ZK©"çźă‘w,>yk¸¦g2<ô>H˛ŢrŹóďؤ2Břb>—ô-w×"9˝şúŻqCüx*ÜsQŇ×Sç@–ÂĄF&fĺ–2B·–ô+Ä5¨M‘:ĺŮťAŐ:p„bÓ¦Zęv3UZ %bdá'Qň¬­‹Aů8‡",3NÚĂÎXp±¨›Â[źżu¶äZ ÉŢOĺîuK:‹ÓXqŐ­Dě(yGe^JmČ„…LW„m‰Č©Cö29É \j}Bäę&8öťÔBI¬ĺŁ„ĐÜŚćąž@ÂÄÄr‰ńŻşDiˇ_4;ţPŘ˙ńţZŚ‹§Ş]?K±ťŞá0(rěÎîgţ4/äĺę¦E ăyE·ĐÖAç$őJgűU»޸7äL´Ń±Ďá-„Ké’V„´î5i H@'ü(ÂKre »Św˙ »7ŠXhľÉąÝ{5Ňń<Řú_˘‚!Říă›·/¦Ţ'ŽÄ§gĐţ$QK^5ÝĐ?Ô_đtňŚ9=NY1]ßŢâAŁbŇoźč@U€ż@™Ôz'€!‰ę˛ä‚Ů«g5HrÍ]“†Ůţ(‘ŻiGŰťňéł ń íŻóČĐŠ'Ú§$ÇĽAŠ!>ż.{âĹ]=äĂö#+őěn÷ŰgElWŠ(éV‹¬€´;UxJŞŻŚ9e¤ÂČŃ)żŇEúźů[ü١Áˇ« ľ÷Ö´.č{«ćÉî0vN*ćTfłę-Đ…ŢUŚŚ­µ¬zúBÖş5o3eŐ§'ŚDÓ$UŔ_CĚ<—s…)Äď±˝ý‚^ŹÓEĽ&‚ďi{ň§îć­Ňrüeˇ– ÄŐűłÄŚŘH÷ŐRoÇŕOF  "lĆ0FĽWl^+ ÖđXÓ….‡˛DLqńýĚ*aýZ'Zȵ ‰"ĄŞ1%R!úÉŐř`SúůµŚ"J4ă`‚ăKň…ń=Čü§mŞü)˝ĄÄďŁÍťŞ ç ˘Ő–@ČďM‡vöŤÄ’máH¦ .<•”ŮUúw1hZF  €yçQfÇ0ťˇß>ÍŰůTĽkřąŻYo¨\ü›8SăÜHcŘ!űIŔ«gÓ™R@ˇ&? %€Ăî픢÷=ŕ÷îţYD1!UęZޡXqźcs ş6NčĂŇ›±AĆŚ«v°Ý‰}íÍ(lć7$ůĂÔŔ&&ŤĚÓÄa‘Ą°Ç mĚâ¬+ç ŻÉŢm%ş1ó:-ÉWBXÜÜ•{·ĐUíÎ#˘ä’˙/°ó*f[üűÔr* M\µ´˙yF¸ŕh×0-Ię~.JÝ$úé¶3ź3o©Ć»ŮbŤl ŃČ÷uŁ’Pz«źÚĺ cć—ĺĺÜá°W,łf°GKÇćięUĄŁÂ7§ÎŁ˙X—dńĎKp‘dÔ^…Lˇ P0ú?Ѣ@“Ćĺ#“Řáöҡň†âąXÇ©ú0ä™yůů ýůFQ±ż6wČvµôG«#ň•ąę9ôKěýńE†E€Ý—cłę˘Ë‚‡:·”dž[°Ă^Ű·`ĂÜW—‡wíś˝đË "®,âŇŠ»†t$µH[űňqRJ2c%  ÖÁö4Ű$°nńO@ó8!L«‰(Tý)ĺ‰=‰`¦˛ÖÚś«pę†,9ݡ“6sçůađÄ´őÇČĄÚ™—‹‡mÔüňcôg(ŹW­Ň`Qz2§76+´>~N˝9ăłXwŃW‰Í@K4źË$¬˘¬BÎQĐÍ ÎđsE"ăűG‹#{“ČĘË.•GěU®˝Fë˘đ'Wľ'™›ŘwuÄok›xxç—s/I{Pî˝w0FT÷ß„,­ŕ®ďŃ?MÝ·, ˘ —ö “Ś«}Żs‘Š­ŕ\VV^P‡—XýŻXˇí§ä55ř,ą &Čą› §fSJ/ç0ápśšÚ˛O «¸ë×T-›4ĂĎÂ.÷eÚ…@xś,.w;»~›O–¬D›t”?Ö¬ŃUrÁtzëä3D3öF”ţf[ŢßőßÝÝ_Čą‰¬g—hş¦><^Ä®.'ç}„]«Ś-ćGË{Ţíäyĺ~p^ŔM釔ţaV93ß ßńĐŁSˇ“ŹĄroU+Íř¶O±Ž-›”’) 9LŹg˛µuÜOC1ΊÉîJ“öqöĚ5ôČŕ‘Öi=qŢń+OŞ×w6ęĂ WÉşăŐâŕś ?x€@W‘YqÁŔuz€JŮłÍI!n ÓújéşĐm/+lÁúŐ¦† Qo¤\§SGˇŤ­đŹláĐđ#‚˙|¨_[Ú^Y›sŕ׉¶™€˘ń+FS‹·0łĘşÔ(Ďiř±š[Ôúżq,ĺăN^2x:íCáŹ*WžOă´m&šý‡ôeÔ` ´©ÉęÝA¤óżČJŇ\QŰČŚkĚŕßݸbŞ<#¦0BśŽŻ’» |+|¨ć¨o›;bbÄÝx+Q`ÝK’GđΦˇĺłŽHFLˇŇ+®¦Ĺ‚,|ąh69ě™®‰Ü:uɢ»5e˘qëJPq{ărş{JˇV*E¦Lľ˛ž»‚ŃüaÖo©Iî*Ó1¦?9Ż é,ŘE‰˛dń”AÉ<ů?S‡wßŇił’"ŕçÖÓM(ĎŤ*gqťĹ^×u˙Vó)z˘QŽ@ČČ’í€=ŞĹIŃâ‚@!;CX`.ŻöüDĎF‚„8s­u›t´ߡ4!d Ó@Ç ď ńőCʉĆDß«˘4íď‘ö±F w/]NĂNvÖ—l Ą×Ҹ˘d™”!/Ô„2D9áMŢëťDEĂÁďßűçeď™qűzŕĺBíńŕT[M:é;_°i˙ď*ŃTďž˝gŽ^ͦ1şqVďĺĺĐ%áâ7™Ě­ ýwOĽŰý¤ú¬Ę‚ISŤKrM¶ŕT67ű7­%ôFI‘đ_5="ąE®_ŕăSXV¸L$ÔlŘߡ¶M( ͆zWA‘y«z™}` Đę6ěs{QĄÜ?°#9kţAí\č Ş/Ď·ię^ĆC}8­ën¤îÁ"h`Ń_€ř»Ë‹•`óqöôń;«ÍMÎĄŮ5ĹC[T)µýâ©×e&"‡9t*QĺýÝ€ě™Ô,»šÓ8IŰQőˇĂ©6_‘ÎQ3ččwVŕŘ[–ŮOÎ˙[ű‚Qňšf((bÓRŻ<•›Hbyâ’GÖă×3z¦ß¬čP?KÝÔ4¤1µŘGCů$ܧjäNČĘĂzp>ŹĺÚޢ•¶ćVŘN|ĘGy‚Ć,ßHt“čv{uÖDÓăâvłN “ż5ͬ€y—€yÂˉ(Řś˝D˛Ď JKÂ<ˇ¨Î5HÓN kł4#Cq]&cÓđIŻ%‡ű Û̄Ha‰†Źq‰*­ŢŹá¨8číŇŹ‚±%‚瀂+¨żágĂ^kŞäÖáNe#ôĂô{z?Üěô6řřrü¨< ŤëČĐŰgý·?ľ¦B7˘ß…Ű…I? …âČ™ć2]¶űŠ ‚~jxŃLy–\COśŇΓo¦›Ń+ Ńş'ť$ń`9Ł ä7™cş ÚÍĚB‰ŇľĄwKi˝:?d¶ě龥OL’gř±Gč'z˘łrÉy–$áh@ĚWĄóĂřđ伅;űŹ5ÖĹ?ŐmÍIŕ'­QŐ ’úÚó¬ÁßGýĹʰ–äU÷aŰ ŇBɆ{rË žFD:9ráóá|ŻR´.}9«X&čÇň_>aY úc$[ÂUý58ˇJ)xgŻř\ŕ©J3€MTŇ‹˛OzůšVy]ď÷łZđzKnOoçH>Ô2°3'Y­ş¨îˇť7rn¨0räâó}¤ßĂOáMßi-ö„Ŕ._ŕ~Ňsřňý§·P˙p­o´â¤>@˛·đ˙Gđ>áý¤_¶ő˙5ďö‘Oś!}@’i!ý¤`řb¤ďgŤűĂX9—Kćô9Ś]ŻŢôSGőä‚ŮDäÂJő5ĹŰ10Ą·ŐQ¬Ąťü˙KëľKŤkŐék”YQÜÚďG+Ť›Őů%˛j(§ _ę¶Zěŕĺvâs,`›Ćś1fäîă`#w0Ď€Ř$&ÁŹ e ŤŇč^Ştř(ѵЦ7ÁpKj e®7s–¬˝GçoUrż!8ƤŹÁC#%¤żă+”}ě._Đž7b‹ ż JĐř’‰_żřć G!‹ %QŮ‹ĺĚh !šc3łŢł1ó@Ž#…ŮÂČ ŤđEâüĄ“z5đč(FŻËă4aŻň·~Ęö¶MŤlÓn1Ę­˛˙Oθ˙u]Xšť”ŘĽîéQÚlH69ĘMŔ%«_'o='âť«Ćęˇ*vťŘ)ťFg “/÷®ÓX©Âţ—[‘ÂN>ukßµ„M›5“ń~vá<«:Ş3דwWw°„š`Ěłü˘ÝYżęí˙ŕ‡cV:±”"Ű~ĚQN®*XVżiĆ)…ĄĂVďkżĚ˙@16śá1ëQ<ţ9&ÜöB‰uFń}ÉNBeĚt|JfˇLZŃÉ©?ő˛ľŤfö«Úń˙B¸Ô#íZćű >=ÓßĺňTdîŰíSЍťz“[řGËĘŞńôç6sa˛ćlˇÓđ!yç0–äUşHš+dO&~úÎ*N™óż˙gž€VšH1Ď˙*z[÷/jł*„Ę3˘2‘ď rOŐiť7“Ňi©{.uŤGzZÁ>…ääs°×šH1Ď÷/‚śăľę–°PŔ!óş—z»Ęř9ŻH|˘ďŻ<Ľ¤·ńeNż,ÇfŹń§÷±z¤CŻőŤĆ`ř×6©Ő1ř“hYɰ˙)ěnř«Ů˙ˇé,1ŕń¸v^/ Eüós BÓS=~'Rú„M’‰]R÷‘ęnę©hR~ x2¤ĺűqn02ůg’1P\"•pţ)2ŰqĆđŰ&¸(ňBâěíęEqŞ‘Ě…Ţ˝$ůţŐˇŔ›ů!SÖ®Pw<=nLäăO.Ëh/Ę“p)ŢO°5jŚ„&ŔY ڒĵ@Faäť­c z–#Śyš’c#iŰ—EŻĚŐŚ^DQÓ«^óŢv|zÚ‡ŢHrípS_Ż”÷sąą!.-gŮ"!˙}ümú—Ϋ? hĂUÚ?uéŠÝëwRXúF‹ońb}¦ňw„MG|É.Š[W´€'–ľ˘w,ĄąţŠ•Ď¦Ńłű<Űű™ ±­k§ęăPě+đĆhŔ$ŘĎ@*ŮŇ" těőÚ‹5ťšî‰ź‹Ř©ęŠčMŐđß22ŘăÜŔčPţ_Ň' a "÷Č×=y†u®Ő…ř7ŰX#ŽżSŢ7’aŤr´8éfu3ĘH0V'ÂduĹš^ř˝¦Ťa·öÝ{ď=EťuëŰl 0Ruľ"MŞ“Ňň1âk+÷°pÎr㤺ÚçĚ «żí0ǸëĄă!.Ur’(<<ľ˙ŮicnV ż€xscreensaver-5.15/OSX/SaverRunner.m000066400000000000000000000373051164314150500172650ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 2006-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* This program serves two purposes: First, It is a test harness for screen savers. When it launches, it looks around for .saver bundles (in the current directory, and then in the standard directories) and puts up a pair of windows that allow you to select the saver to run. This is less clicking than running them through System Preferences. This is the "SaverTester.app" program. Second, it can be used to transform any screen saver into a standalone program. Just put one (and only one) .saver bundle into the app bundle's Contents/PlugIns/ directory, and it will load and run that saver at start-up (without the saver-selection menu or other chrome). This is how the "Phosphor.app" and "Apple2.app" programs work. */ #import "SaverRunner.h" #import "XScreenSaverGLView.h" @implementation SaverRunner - (ScreenSaverView *) makeSaverView: (NSString *) module { NSString *name = [module stringByAppendingPathExtension:@"saver"]; NSString *path = [saverDir stringByAppendingPathComponent:name]; saverBundle = [NSBundle bundleWithPath:path]; Class new_class = [saverBundle principalClass]; NSAssert1 (new_class, @"unable to load \"%@\"", path); NSRect rect; rect.origin.x = rect.origin.y = 0; rect.size.width = 320; rect.size.height = 240; id instance = [[new_class alloc] initWithFrame:rect isPreview:YES]; NSAssert1 (instance, @"unable to instantiate %@", new_class); /* KLUGE: Inform the underlying program that we're in "standalone" mode. This is kind of horrible but I haven't thought of a more sensible way to make this work. */ if ([saverNames count] == 1) { putenv (strdup ("XSCREENSAVER_STANDALONE=1")); } return (ScreenSaverView *) instance; } static ScreenSaverView * find_saverView_child (NSView *v) { NSArray *kids = [v subviews]; int nkids = [kids count]; int i; for (i = 0; i < nkids; i++) { NSObject *kid = [kids objectAtIndex:i]; if ([kid isKindOfClass:[ScreenSaverView class]]) { return (ScreenSaverView *) kid; } else { ScreenSaverView *sv = find_saverView_child ((NSView *) kid); if (sv) return sv; } } return 0; } static ScreenSaverView * find_saverView (NSView *v) { while (1) { NSView *p = [v superview]; if (p) v = p; else break; } return find_saverView_child (v); } static void relabel_menus (NSObject *v, NSString *old_str, NSString *new_str) { if ([v isKindOfClass:[NSMenu class]]) { NSMenu *m = (NSMenu *)v; [m setTitle: [[m title] stringByReplacingOccurrencesOfString:old_str withString:new_str]]; NSArray *kids = [m itemArray]; int nkids = [kids count]; int i; for (i = 0; i < nkids; i++) { relabel_menus ([kids objectAtIndex:i], old_str, new_str); } } else if ([v isKindOfClass:[NSMenuItem class]]) { NSMenuItem *mi = (NSMenuItem *)v; [mi setTitle: [[mi title] stringByReplacingOccurrencesOfString:old_str withString:new_str]]; NSMenu *m = [mi submenu]; if (m) relabel_menus (m, old_str, new_str); } } - (void) openPreferences: (id) sender { ScreenSaverView *sv; if ([sender isKindOfClass:[NSView class]]) { // Sent from button sv = find_saverView ((NSView *) sender); } else { int i; NSWindow *w = 0; for (i = [windows count]-1; i >= 0; i--) { // Sent from menubar w = [windows objectAtIndex:i]; if ([w isKeyWindow]) break; } sv = find_saverView ([w contentView]); } NSAssert (sv, @"no saver view"); NSWindow *prefs = [sv configureSheet]; [NSApp beginSheet:prefs modalForWindow:[sv window] modalDelegate:self didEndSelector:@selector(preferencesClosed:returnCode:contextInfo:) contextInfo:nil]; int code = [NSApp runModalForWindow:prefs]; /* Restart the animation if the "OK" button was hit, but not if "Cancel". We have to restart *both* animations, because the xlockmore-style ones will blow up if one re-inits but the other doesn't. */ if (code != NSCancelButton) { [sv stopAnimation]; [sv startAnimation]; } } - (void) preferencesClosed: (NSWindow *) sheet returnCode: (int) returnCode contextInfo: (void *) contextInfo { [NSApp stopModalWithCode:returnCode]; } - (void)loadSaver:(NSString *)name { int i; for (i = 0; i < [windows count]; i++) { NSWindow *window = [windows objectAtIndex:i]; NSView *cv = [window contentView]; ScreenSaverView *old_view = find_saverView (cv); NSView *sup = [old_view superview]; NSString *old_title = [window title]; if (!old_title) old_title = @"XScreenSaver"; [window setTitle: name]; relabel_menus (menubar, old_title, name); [old_view stopAnimation]; [old_view removeFromSuperview]; ScreenSaverView *new_view = [self makeSaverView:name]; [new_view setFrame: [old_view frame]]; [sup addSubview: new_view]; [window makeFirstResponder:new_view]; [new_view setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; [new_view startAnimation]; } NSUserDefaultsController *ctl = [NSUserDefaultsController sharedUserDefaultsController]; [ctl save:self]; } - (void)aboutPanel:(id)sender { NSDictionary *bd = [saverBundle infoDictionary]; NSMutableDictionary *d = [NSMutableDictionary dictionaryWithCapacity:20]; [d setValue:[bd objectForKey:@"CFBundleName"] forKey:@"ApplicationName"]; [d setValue:[bd objectForKey:@"CFBundleVersion"] forKey:@"Version"]; [d setValue:[bd objectForKey:@"CFBundleShortVersionString"] forKey:@"ApplicationVersion"]; [d setValue:[bd objectForKey:@"NSHumanReadableCopyright"] forKey:@"Copy"]; [d setValue:[[NSAttributedString alloc] initWithString: (NSString *) [bd objectForKey:@"CFBundleGetInfoString"]] forKey:@"Credits"]; [[NSApplication sharedApplication] orderFrontStandardAboutPanelWithOptions:d]; } - (void)selectedSaverDidChange:(NSDictionary *)change { NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; NSString *name = [prefs stringForKey:@"selectedSaverName"]; if (! [saverNames containsObject:name]) { NSLog (@"Saver \"%@\" does not exist", name); return; } if (name) [self loadSaver: name]; } - (NSArray *) listSaverBundleNamesInDir:(NSString *)dir { NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:nil]; if (! files) return 0; int n = [files count]; NSMutableArray *result = [NSMutableArray arrayWithCapacity: n+1]; int i; for (i = 0; i < n; i++) { NSString *p = [files objectAtIndex:i]; if ([[p pathExtension] caseInsensitiveCompare:@"saver"]) continue; [result addObject: [[p lastPathComponent] stringByDeletingPathExtension]]; } return result; } - (NSArray *) listSaverBundleNames { NSMutableArray *dirs = [NSMutableArray arrayWithCapacity: 10]; // First look in the bundle itself. [dirs addObject: [[NSBundle mainBundle] builtInPlugInsPath]]; // Then look in the same directory as the executable. [dirs addObject: [[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent]]; // Then look in standard screensaver directories. [dirs addObject: @"~/Library/Screen Savers"]; [dirs addObject: @"/Library/Screen Savers"]; [dirs addObject: @"/System/Library/Screen Savers"]; int i; for (i = 0; i < [dirs count]; i++) { NSString *dir = [dirs objectAtIndex:i]; NSArray *names = [self listSaverBundleNamesInDir:dir]; if (! names) continue; // Make sure this directory is on $PATH. const char *cdir = [dir cStringUsingEncoding:NSUTF8StringEncoding]; const char *opath = getenv ("PATH"); if (!opath) opath = "/bin"; // $PATH is unset when running under Shark! char *npath = (char *) malloc (strlen (opath) + strlen (cdir) + 30); strcpy (npath, "PATH="); strcat (npath, cdir); strcat (npath, ":"); strcat (npath, opath); if (putenv (npath)) { perror ("putenv"); abort(); } /* Don't free (npath) -- MacOS's putenv() does not copy it. */ saverDir = [dir retain]; saverNames = [names retain]; return names; } NSString *err = @"no .saver bundles found in: "; for (i = 0; i < [dirs count]; i++) { if (i) err = [err stringByAppendingString:@", "]; err = [err stringByAppendingString:[[dirs objectAtIndex:i] stringByAbbreviatingWithTildeInPath]]; err = [err stringByAppendingString:@"/"]; } NSLog (@"%@", err); exit (1); } - (NSPopUpButton *) makeMenu { NSRect rect; rect.origin.x = rect.origin.y = 0; rect.size.width = 10; rect.size.height = 10; NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:rect pullsDown:NO]; int i; float max_width = 0; for (i = 0; i < [saverNames count]; i++) { NSString *name = [saverNames objectAtIndex:i]; [popup addItemWithTitle:name]; [[popup itemWithTitle:name] setRepresentedObject:name]; [popup sizeToFit]; NSRect r = [popup frame]; if (r.size.width > max_width) max_width = r.size.width; } // Bind the menu to preferences, and trigger a callback when an item // is selected. // NSString *key = @"values.selectedSaverName"; NSUserDefaultsController *prefs = [NSUserDefaultsController sharedUserDefaultsController]; [prefs addObserver:self forKeyPath:key options:0 context:@selector(selectedSaverDidChange:)]; [popup bind:@"selectedObject" toObject:prefs withKeyPath:key options:nil]; [prefs setAppliesImmediately:YES]; NSRect r = [popup frame]; r.size.width = max_width; [popup setFrame:r]; return popup; } /* This is called when the "selectedSaverName" pref changes, e.g., when a menu selection is made. */ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { SEL dispatchSelector = (SEL)context; if (dispatchSelector != NULL) { [self performSelector:dispatchSelector withObject:change]; } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } - (NSWindow *) makeWindow { NSRect rect; static int count = 0; Bool simple_p = ([saverNames count] == 1); NSButton *pb = 0; NSPopUpButton *menu = 0; NSBox *gbox = 0; NSBox *pbox = 0; NSRect sv_rect; sv_rect.origin.x = sv_rect.origin.y = 0; sv_rect.size.width = 320; sv_rect.size.height = 240; ScreenSaverView *sv = [[ScreenSaverView alloc] // dummy placeholder initWithFrame:sv_rect isPreview:YES]; // make a "Preferences" button // if (! simple_p) { rect.origin.x = 0; rect.origin.y = 0; rect.size.width = rect.size.height = 10; pb = [[NSButton alloc] initWithFrame:rect]; [pb setTitle:@"Preferences"]; [pb setBezelStyle:NSRoundedBezelStyle]; [pb sizeToFit]; rect.origin.x = ([sv frame].size.width - [pb frame].size.width) / 2; [pb setFrameOrigin:rect.origin]; // grab the click // [pb setTarget:self]; [pb setAction:@selector(openPreferences:)]; // Make a saver selection menu // menu = [self makeMenu]; rect.origin.x = 2; rect.origin.y = 2; [menu setFrameOrigin:rect.origin]; // make a box to wrap the saverView // rect = [sv frame]; rect.origin.x = 0; rect.origin.y = [pb frame].origin.y + [pb frame].size.height; gbox = [[NSBox alloc] initWithFrame:rect]; rect.size.width = rect.size.height = 10; [gbox setContentViewMargins:rect.size]; [gbox setTitlePosition:NSNoTitle]; [gbox addSubview:sv]; [gbox sizeToFit]; // make a box to wrap the other two boxes // rect.origin.x = rect.origin.y = 0; rect.size.width = [gbox frame].size.width; rect.size.height = [gbox frame].size.height + [gbox frame].origin.y; pbox = [[NSBox alloc] initWithFrame:rect]; [pbox setTitlePosition:NSNoTitle]; [pbox setBorderType:NSNoBorder]; [pbox addSubview:gbox]; if (menu) [pbox addSubview:menu]; if (pb) [pbox addSubview:pb]; [pbox sizeToFit]; [pb setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin]; [menu setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin]; [gbox setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; [pbox setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; } [sv setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; // and make a window to hold that. // NSScreen *screen = [NSScreen mainScreen]; rect = pbox ? [pbox frame] : [sv frame]; rect.origin.x = ([screen frame].size.width - rect.size.width) / 2; rect.origin.y = ([screen frame].size.height - rect.size.height) / 2; rect.origin.x += rect.size.width * (count ? 0.55 : -0.55); NSWindow *window = [[NSWindow alloc] initWithContentRect:rect styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask) backing:NSBackingStoreBuffered defer:YES screen:screen]; [window setMinSize:[window frameRectForContentRect:rect].size]; [[window contentView] addSubview: (pbox ? (NSView *) pbox : (NSView *) sv)]; [window makeKeyAndOrderFront:window]; [sv startAnimation]; // this is the dummy saver count++; return window; } - (void)applicationDidFinishLaunching: (NSNotification *) notif { [self listSaverBundleNames]; int n = ([saverNames count] == 1 ? 1 : 2); NSMutableArray *a = [[NSMutableArray arrayWithCapacity: n+1] retain]; windows = a; int i; for (i = 0; i < n; i++) { NSWindow *window = [self makeWindow]; // Get the last-saved window position out of preferences. [window setFrameAutosaveName: [NSString stringWithFormat:@"XScreenSaverWindow%d", i]]; [window setFrameUsingName:[window frameAutosaveName]]; [a addObject: window]; } if (n == 1) { [self loadSaver:[saverNames objectAtIndex:0]]; } else { /* In the XCode project, each .saver scheme sets this env var when launching SaverTester.app so that it knows which one we are currently debugging. If this is set, it overrides the default selection in the popup menu. If unset, that menu persists to whatever it was last time. */ const char *forced = getenv ("SELECTED_SAVER"); if (forced && *forced) { NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; NSString *s = [NSString stringWithCString:(char *)forced encoding:NSUTF8StringEncoding]; NSLog (@"selecting saver %@", s); [prefs setObject:s forKey:@"selectedSaverName"]; } [self selectedSaverDidChange:nil]; } } /* When the window closes, exit (even if prefs still open.) */ - (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication *) n { return YES; } @end xscreensaver-5.15/OSX/SaverRunner.plist000066400000000000000000000017331164314150500201600ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile SaverRunner CFBundleIdentifier org.jwz.xscreensaver.${EXECUTABLE_NAME} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 5.15 CFBundleSignature ???? CFBundleVersion 5.15 LSMinimumSystemVersion 10.4 NSMainNibFile SaverRunner NSPrincipalClass NSApplication xscreensaver-5.15/OSX/XScreenSaver.icns000066400000000000000000003115731164314150500200650ustar00rootroot00000000000000icns“{ic09“g jP ‡ ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2c˙O˙Q2˙R ˙\ PXX`XX`XX`XXXPPX˙dKakadu-v5.2.1˙ ’z˙“ĎĂB€Őn§ř‘ž‘ôgL˙ zNÚv2^äťÄqMűÝ$ľ7°ťźĆŇ Ţ°¬kMn1lčĹiú¦ (ŐTUë_*…Şë–\)qÝ"›S‘0·Hxđ(‰ŠWë[ţ-ÔaX*/Őgč˙]4cuř‹RŁâĂ›ĹÇ”?~ĺŕ;iáÎŐŐ8wÇ0×V`Ă!"hŔNi\R·J>”$ć‘rkKŔ^šţüeˇ&á接ŁęŞfÜŘY.§ňť’‰0fłáK›™rÓ÷úA¨Q‘#±&ö.SáImľÉk¸ŰO`Ź«"¬ZŮű7× ĘÚt WF‹˙{Ş[8r%Tők–¶uúŐĂęM‚*Ž3=7‚~·ć+„«ßAąT€KÎN(67ĆşŔé$g2ĐŤŐäâ|ý–j°&D´â<’ (őÔćĄyË/¦Ů~#Ç™ow@ß›Ŕ„M!gŘî]Â{#Ó$„‰9˘‡ 0+~*+LJ/śá¶’ćÚ@H2L$ŔŔŤöˇEÁkűx¶¶"¶ľäî!čöć¶G® ˝§eoˇłPr<—pľß”GM¤ŽX#›˛ÄťýťÖ0ŰöîŞ\dŞÖA(–Ď½`ßZřý FQ/Ő0Ř!®MÖ"~*yŘÇ=‹áă×vĚ DpŮC¸ę˝T5Ţ‚XŕžµÍÚed \]ĂŠf5ωHEr 0ž~‡ —Â7ßN„ň±cŠ‹$:×\4‘¦; ’–ÜÖć­Ý6-űgÖW·\Uˇě&­‹p#N·5G'E:·hx©›n¦W”é~‘`vqbKř2ábőú8áˇ\YÇź‰±‚§WÜń“ŇJw©ÇáoOÎŏη×&¬± •rÄjÁüôśätňÎתÊ€h–"y»AÇďůŐöGÚNňö*é€;`D±ŽT`Ní*‹\ĺ˛YA›–ę”Đ÷öćęh” ăą?k`8vaĘFă D=’j©“Ú¸*j¤RĹŻmec Íßá‘˝ŇáŁÂ•Îj™şô™Öý´ŮÔý}üŤ+črmeŽŻd 8iťĐůąîąáXte ĺŇjâ•”­Ś­¶ß&±ů+ßglŮ*ß5ňŐŞ Wsß—ď!+u8ÄSz¨ÎĆ"ěŠY$J÷Q};{Ľ(4fţMuőC2ČžŻé4)MĘčpłµiĐ×20Wh4´űźÚO©Ő îqI LD:ă=! ˝ě¶ĄÖ©ă§¤OÂő.‰DÓK'_™ BĽ×;Ą6KKŢ< sK ó-ŔCC:/–Eř¬Q†0!ÓCŮ@*=„ĸsĂÓ¨ąě|5‘ćńgľW䇯˛`ĽHVĹÝŤŠú°™¸ŕ†ňŐ[Ź…˝ä`ŞĂ1ęů¨±čň/ějbEäP˘lÓä±ăËé«‘65šAtn9ägNU-šCóUq-hc´;—’ŘP»ëčµdm˛ô2ÖšźŔ×f7őĽĚ{mQE Ä(Ó71/H:n[s+ďCó'ůČož“ĹwGuęLÜpí/•.BęşŮ’¨/ôéĹăuIe¶4ă€Kőx;PŐ>ń‚\Áő‘Ě×ëOd“üé$ă¶Jdm7 WąĽźĆĐ2ĂíKóŃ űT@‚.vŔy‰óżUE”ŘŻaL˙Xţoí›''-Ľ»IĆň5UěŐÄöO4Hş„Ęu¸-(AťUO¸‚ŽĂŘW¶Ĺfî ]ĄXP‚dŻyx˝!ˇĎ‡aýš—Ń{Â[ĚĂçăiř Dlň'2"××bf (kô ĂŔťę#Żď#1nXaÂ6Ѣ_+‚\ ó˛ăa®ÁďçęŔüËo[łµ˛Ěyţ+oŹńe2ôžáĚ‘f!šň´1", ĘĚ#Š®í(N"·˘r§Ą¸DęďÝ" ˙•঳ĂJĎÍÂ?~@‚4™ÇąĘO2L˲‹đp1rřrI¨±Wdb/0F¤Â${šÂ3Đü§2F4­÷\ K@G~Ťo”ęňôďÁÂ^Ú­Ęč„Jдظ„ZU ÷ĽĂKYeVP:"űdäĂJWJ}AcĐŐ•V|éĐߥĚ÷QP ş,zb \vţ‰&ů?1ĐäĚ`˘¤Z˘yÂ+\BJG`*Ô˘6yÉÍž‰ Š›§¬Ľý ;Ţw6Ť€üTç'!W{ŽVҢÖŰ5˝Ŕ–tĐO檄‡Ş6}wÇß™-€ôÁ ř›Zť}Ä„.Ģ·ň·ÉsúŽN1.ř'Ő¶Fß©’m;G‰—ŻW(Ůíi(ďlĹ–Řq<ÍYńč‘©ł‰ŃTŮ(j´”ěű;řaqęM1:Đ_x Č0şAÇŕťä?¤ëθpřL({•čÁ=Ó*ß©’ĂU'–Á GĂ5ĘśAŚt7ţ§#/´”EbßWŻn Ľ5%čg›,±ĎI‹OwůîđV"c€­´«’.Fm8+˝++ýH€ ‘ňf?ś%Đf'°^Ö°ëCôČQë«ęĘśŞq•`A’꾪úň%JWŚ‹üę<‹­/a=ďÚşt…đúqYźcEú~ÁPĎĎ©)ůô…žő¶™›§şQíß&ZĐ%Ď]–|ôĆYó• KąZř6lá1Ču&˙Yeiĺ)€¶5 Í"8‡ŘnFŃŕ> ÁŃŽÄĽ™P“.žÁ&”ßÖ¨˘6#™—dů…Íűź0–śýJ+î>«ycď.’TŹ í¤ ÄĐ­ę}˝Śˇ‰péxHîÍ0ŇáY[Cľ<ŹLîÁ}*i¦˙żłë=˘ů"&EAĹÔ÷Łfäuâ:±3źq®sYr¸ĺősSCX‹ćZŚ™Đ¬¸ţDLIeî“(ş‚€‹Ůłf<Ö)°lď Ćľ,k.÷Đ)ΠŤf5ńď‘v‘€™Çq—Wě!¸r®ŮKş2•˛Ä^ŘĂšUry+ÂVÖŠýPG lWŢ%,á˝@c,ľŔh€%É;¸°§Ł(|ŕű§ě #â|ĚĆ:Ş>]!]µ`\B݇Â~”A’µĐŞzĺěůE‰ľYËTÚt'‘;Ą×ą6N›Ms·HA}ĘknU'±ú¦ĐZ;c«6z¡Ňb=fĺ ö ,č±ë9µ®mŕŻUk7E[ôHRU^MţSRuŻw”×< ś‘orŤd ĚŹ\O±~Ĺ_«”„Đ9ď,D’Â/uÖ`S=Ö»4…h?Ń·ĺĘ0Ňqµf§H˝… >ŤÉpŐ`ék¤-<śRĽKěë>E »(kk+O$ą_śŘ׊#ÖFýč>…" {ᕨ,áóbjÇh>¨Öe`H±ů-“V÷łAhýmŽ«X/ ĚZ`ŤµďřĺŇnž zŐ5ý&«'p.YčűŔQľ|A¸oÔsFSaą$˛D=Ůmx (VŇč™ĺżśłÄł±˘©ç˘B˘ó úńv3'Ń54Ű ©—âçZńä|BçŔřĽSĆ-~g¤l?h) ¶ż Í–×CA‰ŠOŐlŘ]›®q·zÍ$m´˙ĽŤMżSťđíÓňłÓaPŤ×Ľf3ŘŠé°.›V”–V ĽůçÜ˝˛‘_^ Č\sK«b"XoŻ‘EĽđúŃsOß "Rŕý“~Đčú.0kµÖ*w3űÓ“ŔÜlDhŐ×…[f>_^˘í…DżHşŠ‡Dşn¸‘®°çÝ7ůi+Ďy ť­\R_%rĘŠz[,T.Fwxc»řÎ×1 [_ů_„őOŠBÂÍ?—üÔ0ŹbhýW÷cLÜ÷¶"š“d®±ý,h™LžÁľlĆDÜQĹíĘ{O*J xşű>Q»Ó}UKŘ?ÁkŇđ Hţ; ˙%xîgTćđÓ¬ŇŘő÷j˙]¬Üh=GĄßUďťüLęaH)ZŞŕUJk 7¦Ë*‡Qh;kÙȽoĂ@h–ű-D‡™ŤB/Ą9™î9"HBźpoŽý1µn+üž(Tě“|T´żĎЇM§¶e‡/›ńó śktlÁVĚoCđ^6ßçŞ4Ó<Ľ3Ş2e™CÜFŔ˙zĽ,0>{•EúUĽGźR‡w“"ŐÍ3¨Ą{ouČíťÇł&@ě¬wrXчúRř×Ĺ&WeČŔ>•óĺ3ŘŞÖ_:.>Ŕv˛ĹăúňeKÂyďÖîô^ť€hÄĘ“‡ßŚ”UŇýÇ7 ĺήäs‡›öęnF äsĚ™ )ÁÔăť8j۸Ľ„äąő»Ť®BżÂ8âZgz®ôńřMľ}ć]Č!žq,®ÁíÄ_‚hi«2ďr<n› ú%—±ăx«±âd“c^Hí]5=®ý‚8ňuNgçž ’]ĘA6ą7€]iLĽDw…Ődz‘)ŕL7·(¨t­éGęh9Őűńöd·Ç˘ůśŃ¨KÍ™{68^×V)€ů]×HĆ^ÜĄďŐ˛Ş.:Ç"uö@©Ô3\l,Žý‹m Ŕ^H1ő…O?‰ü™t·-§4i]á áëUă¬Ků˝^Rqę\ßŰ}}^ńµ[G„ú‰Hz P÷ZŢÁČtëâĘ1ß 9(rŮą+xŰ iű—D—l,ŻĺžďŮ2ělćŻĚXŃ#׏żdUÚĂKj˝N•ś».€ÍűĽŠ‘—ęÚď:·U“y¶YZ‰P^zŔ Ą.íŠŢ@äżE•Ô°Ż(öäo©ŹÔ¬Mädm•ľ}tş:×ďé Ůô¸˛AăÔŘŮí /bř¤ĎQ+˛>Ę^'*“Ě7Ů˝p®É"¬SĂĂóuŠCČLŹ˙©cň¤ŘÎÁÇ%‡-¸ŢŇäđť(3? <ˇű5‹Pě ĐÔŃĐBhÇé{&€•qý†¸ź>™en‘ˇxf&ű:»`©Ţ·Âé‚úËľżÄÎ ÄB1xĐΠĚ'ëŐ(_Ž«/ C‰@sOŽ«Ş Küds†5{N×GKŚÓP椗űĺ.G˛¸(Oü|“C5h-ČA—1Wňˇ×çy‰$_?äĐ,EüX*wĂSĘ:›5×›tÉ>Yú‰×µţ »$ČÉ/ߢşSŤlĄ­Âň~qülæă.k˝“,„x‹ëϰ şi+6XjéDäĆ  ĎĎTŁđŇx~vĆűťá˛w]ľDĹŔÉ—"2-őťÜ°™[úkl €ĄµK$đˇąH˘@ô¦k¨ĺ±÷ ď˱ńŇ ŢŚHŚşpu|÷źZĄqÝ>äîŃăËr“aŤ©©«JT¶^© b"cI¨dŃKĄŘY «¸±3Qk+ ô»µfhŞŃ-®·ľV7·´ĺ¶ŻN`µóU&€ÍŮA˙Ç%GZoüé‡zé3¶fĄ¦Ľ§ĺ¶,^‘€Ź4†ů쥚čjdhĎĹŠqŮďďbÁ¬‰Łđ đĐâ1|š%5d8Á|Ř1%tđŞ’iĘ=ëŚŔ¸9c°×lŞ«r›Ţ7Ńţĺ9ud‹p~’е/«¦;v¶®V{"Š}Ż˝ÁűˇÎÂGsܱŘigCÂşŠ­÷–PäjÜĹÖ!Zm%ş˛đlĚť«‹Öô—´¦fęheq ŔŘ×ţ{č~Dz×Ţp ’üżŰôe¨eÝbLç°‰e_¸”µ'Ş‘cä,˛!^Pq!H=S?cń˛2\6פ´:rźŰË÷ý/'4†OhWfađĆÂźÁôőVNv%ä˘áXŹýý~’)|ZĽý€Y´íĂť ^-„űäŔµĎq—VočňÁ§&*.JZżAěý—Ž‰Â ŮUwq|ěň|«™Eć‚–ä E˙0vo řCź7‡i{뿪K€pBčf6ůŔ*ćŘĆ„5rČc)őµˇŕkń7b‚ĘňŃRR> ¶Q94ařÖŻ1ßł&i]ť$e?ąĂš&Y_XŤĎĽ.}ú…řŐLWľs%›XM*wbăn‰w‡S§łđç©~+ťˇ(Č䪚c±ů’÷źÚmŕ ó*…·›ÜtšúçÔůą4ăŹ×ôk!qbwËwT›%ŰĄe[+ů­`Ó#J;r˛Ď…´»W±ę…|µţSÉ  D ·Ä"MĘ,řťYň¤Ť8G*ĐIÍ»ě;1,Óy¬ –ÄË«bčI®¶¦ËóľśŰ żř¦ęÁŃĘE¬­©=¶rĎ8de.ú¤ÁKÂŇ­č[z…sźÜ Ń™Qô$ÁHîwČčů)ěDf_±Ę8$Xe …}"€†{!‚h t[đ ‰…¨•Ü˙`7ý"‘uŘÁ.opĎ'ź¬š@O†x`[ڏľ ?÷"r±l®ÓŚ3Ú\-˛ë¶‹®égŁŞŕŮ[d+v.­H÷OâŰKZŚ(|C—Ń—®ŇĚصČó*ÖňŻjĺŃ.*ÍĹÇŕőD?p¶űqŔůÁé…wî /vZ’Ţ{ź&>@ń7)WTaú„‡5€ü+W KŔŻś“Gúáp’Üůŕ.üÄň›X“Ł··µ0Ŕ)@ýň€-x1Ĺ)}¶ ÷]dUi†“‹Śy" 7±oŔö¦†ăř4´K‘]\'[ëiáÄs˛ ÄWhOřßT˙"ě±-=ŠWúf·CýâëńŮł¶[Ľ¦ç±Đů¶QĎN'Řq‚0ăUxŕâ*H`)9·,´ ÂCfż^ʿߡđQ޸9[02Čű—ClijO°Ž‚†Ô»ă &ŞĽw`´očQ  |ż,oȵY‹Ö &-™Ż­·/߉¸a¨`Şä*Hs†˙ChsV4¦ö]ţ%Ň„ĎĎÚň?Zhüý0@âîś”*d€EÍ äRLgőm›`©WH«2y[}š$~xţŚ™ŞřdŤ/RÁqüa·tN>áoţ|ÇZpČ@š€ ü$4ÁhKl˙SCg%ń?]ń-€ŐˇEF‘Đ4f[ł(éĄ3ŻCĺďBđo´˛_Ô]AŇ:Üo±ôĚĚ,N’Qć§˝[Zp]cĘ ËQ‚Óżŕs ‚@éĎ•]JĄ„ j·” ׉óš˛Á bqś‰=Ţ9Ú"6ažz-o('©bjčc‹© ĎŔđ€-5PŚ 9M,$ç}ńË/¤0ńóL×…Ĺ“˙BrŔ:RźpÎă%Ă#˙tq}‚žĺ›oçDăDÎt”ş’°?ÓOőŃë’|LD_…ă~…óW„X?Ařş ľµWŢĽ9ž®Fä>Ď‹§–UťľŽćŁzuoź6Ŕčh|*?Ěľnr8FL$˘Vćŕp:4—ŔŰ=!oUľŽ'ű‰AqNžú7+‚ë¬:r’Đ 26u•˝3ŇŚy6kžUdý”ČČK`Ĺ-†=‚ćŐłk<”ÝRl ň{Č|NÖBK˛„µť®@J¬ř%ŠOÂÍşiţđ/âࣿŰ#«Ě$R§ĄÖŁaôT8­BłSl۰páWš[Oó±7M@Ň—Z3ň!YSăżµW;˙ EäĺýŇkеôX¤R·#aŐüҦ¸ćÂwЕ׆Ö-^‹¬»ş„â0,¦}ťÉĹ`uc.ĎsŠh/g ţ‰ ˝ŇűĐÖĺlZŕŤÉÉ;emoź/G_kDa˘Á˙<`ę…Ő]­NčÇ<˘ßÄŁ“śg–-üË·ĂĎĆY5XĂ@ę”qwđ3ďÜ-LW–k )U ůŮťźWŠÓ2;2i?Č)l¬i±çu”†‘—;^ÂpO‘M·]űľ¦Í#j}ľČO¶qôĆ:Ŕ¤]Ą˛đÉŚ–… ž˘&ý‚µuü”ă¦éס(îě–»ÓŢśô—˙#ô´QĘŤŻ0şIŃŢÉ ś“l[¸áŢő…¨›ŠľÄ%*ËÓŞ oĘJ*á""=3™„Ö®‹Â¬Éű˙T5úoNş[ćÎkŤwĄGÚ—x¬ ĺ`Ź˝i( ă•dđ_˘kµ¦HGÚC:ĘČXOR ćěľÉa”ekfŃÉj€°C®°ű^mDčă/X4Ľ "¸€őü(—‘°Č9Č[JFڦŽ;ô3zXăşÇę9äăbůÔfĹ?«•Q X2ŕ+Xs·Ş!łD]ßIű¨~4ťţĉ#‰ŞŢŕ?K©çŢ8Ő©gn;ˇßď4üŻhŃ—yNă‹iîB”ꑳ˙dG2úž_Joł‚ÎJ7ŔfM‚[€ÄžAR«Ň“•óţ˘;„â€(xűkLT›gˇ†^˝Sh>DáYÖCő„ŃĘĂ–Űľ;ˇd˙JjŽZż×°„ĎíDčńŹ·'T šŰ*íígđó¬g&LÍČrüf 'şkĺň‹]b%¶*đ0ןxó9ń ZŰ+ĎAF٨¸Î }Úq HEçěč9r𼕀ůť1$oş@ř÷“ýďŤ9á*üęĆ´‚ä…ć0lřDÍsř1fSľuârňź˝ŐORžřÇŢ+´ tJÔǵţj~’´™ł©şř CëÍKŐ¦3’·ŹdÖuoŚu¬É5YČ®Á¸j§ŽK:K9«g¸qGU?¨“…V·Řě¸=J_X0ZĐ?PĄfM p'^ďŐ÷¦Ž„ /2Şbí݉>ř¦5Ý­m06®Ě7Ş’ö|BÜżc aąü©iÓHOÖpsţńi]j„ĄżÄ­»D8Ď×1FMĺĄ2v‹L‚f2¨Ť6#ô­6Öí č—lĺ!`[đ ,gĚ”v0¦ ĄB°đđń“‘‹‹s ɶB»Óâ­NtG?›VÂjnU˛ °€)‰íáočÄëĎqÂé۵HÍ©0ĎËŮ•'‡wXŇÍĄzÖ›{ôÚ`CĽ m Ćĺ"ţiáúýKĹ÷ĘĹx˙'NDNÚNýµE~<ňĄ®2kb„ţýŚŢHgŻúVfb˝+Kw4ʵżŞ/7Sv)ń÷M.ÖşWţŃ?f٬™§ńUv‰˝©˙0ϦĹŮ属´OÜ˝VÁđ>fçě»\2íýúá@ŁáčĎ”°X_Lt.:Ŕ‰áJ"ÎE™–Ë€Ž^únf ©/ŚbÔĹčŔŤ/˝+5G»§©pŹ”ĘžK H 1룍µż“<ł&ŞŞÇf„ÍŃű0šNeˇ Řx›3OJÝZ“ÄO‚†d5. މţPĘßŘ|Ő ŕFG!‚jř÷¨yŔ€‹6•.Ěj̦Ú$ş+ş˘:ü9ŞŽ7 ¶Ź§žČ¶µ•ZCy|Đ~|xa,€‚ľť}Ş · ‰˝ď˝ 9Ć1·źlF¤Í«ośÖňMĽFŮ2”9ŃX?Ž=¦Ř¦>,$n[#L˘Âä~ş 4Bî1 "¸ü îbô™‘fÂ^‡)1Zđ<ÓFY˛d%­Ýń—˛qk6JîŻŰ×Ah°®` C¶kšďĹ“Ć"î(ݦăÎşŢpŮ$Wéűç죝 ÎňéH\:¨_‘TfćŤőĆ +ČŁÔëŻ č·y{É;ĘąĆŕy-`|®ü@§=,—Ű]ůŻ#u˙†őă}ô ÖěEßFmCűdř)ŘL˛ĎăĘhˇĹg”””R3ľĂâaâýĎâjő¸A  ôjd:®hů˛˛#§_¶hi3Ě•ëŇjJ‘@št#VE›–Işşř‘lÁçĹRť–¨X×ď! RCä\»ŞŹ±şţcß! ű‡Y˝7tňü ä0ÂL˙k,\˙2|Y?1ţ\–éţ¸pŰ?ů!ů¨ą Ä“ű›T#ŃÝŢlrm 2Łăq»ňQ«ZpJx©Ű2 ŽD#·8d¨:GO{ř‡ů¨Ś)9«­¦YAŔÓ/ČĎnëôŃíú`Äđš$boGż}ĂňOަ¸–€éPců§Şye“ŰőSS~I_R4Ř.–×Ć žŰąq¦đ•`®}%Ęóő8Ćß˙}6dR6Đß)MšQ%6]±O¸˛3’—}+2ţu)ör‰iş†®ľ2W1í"áĂS\P>ĆĚ;ń‘Ň١ł”.!Q˙wđ¤ď+7p*“LéÉíO•Ö®Fů(Ý{pdJË#łQtbikńd‹ˇuŹ|üńtq|v`Őáaí)ŻřئL\G„WsCÁĄOP\ HK®«[|··ä/z˛xŐw§[YgÄőC´[ýMźG‘µHůmyL˛,ąIÍľŠŽh®˛ ʡţ˘šŠŻěŚ2T >]ză¦:8?»˝ăuܰ ,‡‘o™™OŞÓ†„äĘ}ŁŇ#>€ŇZŰ~6Ĺ©'9pľFeŤ¶´f ŔťtüVrinŤß4O„T¦Ą¨42Đăfjn­Ł†ŮJeśeÔŔée]řŻ|ćNKň“˝čžtS0¶˘ř!§‘+ýmťw0ţ2F͚暼‚˘“ÁůŞ–®‡Bp…Ł1'Ş8ůOĹ|zÂ>·<±;u+4Ďľßôo©-g·8—eĐÚ·Léň %~}XyCꔢłCšć &Uz±2gcˇ4Q1wPčCÁU5ć]2UÝ& »lcI^$_Ş,yFsóşK¨~újG ç%&¸YkŢ3łiŔÇJ Z\úÓ  ąś8×›Ücu:’$Ą%1ö|X©Ëá–*„˝ünFjíáHT`;¬ŤÔÔL5ZIküt‘ťęOĎ/i3rW-ň+gKWF2Ő5a`8Ő©PçsEgł*7"نTJĚ:ĚďÜšÎŕĐKěaśßj?4·<~áZ3ë¦roč4§ž‰p‚ë”ĐM<© ·˙_żŠ{‹$ä÷L1ŕ‰`Ä!ß_0­Ň±Z§ŮWĎţ6QÇż ˇşűx-ő­Â{F‘éz…áˇ6.ŰQ`˝Đn°Žg&ŕ+áV伝×ů»·Vü'~pŮY…¶«±6'×˝’¬Ě×רéä?oĹc´ÁĄkNL:úňű§%đ8ńŚdĐ=›;úéaşj'ńhŕlýđŻŔA­ždÁÂfę×ÁDŰŔÝT) Č:łEUţĺľWôˇ™p[ĂÚ_–î„ÔŮeńŚÄ ź\·” Ëđ¨?]ޡú/˝ŤĚŻŕ!X"ë„§N÷dEÂ.űWőÝËĆ®YqavSE!č™`<ĘŘ·írÜd ŮtĚ[ůč˛K$şĐ ,Ý&Héqp v‰0P˝uR+®™z*\– Ü,ü43Ő »/Uw[gŮ ¦Čüđ¨vĹŕ˝k΂µdîź„~é'ů6Żőťľw|+ł:Úx°¸e4ěŘ+÷%ő˛ŽR/EđŠňĄĹ ůCNĺ‹™=É8z%Íʨ'ý.rĺžżŹ‰ň`Ě;,ő¶ć6»”§1ň–ßĚÝazjC-‰µŮ÷‡›¤=†Fa*D)ôźüĺ}Dô d¬ŇÂw:Š’}ěĘÂě<č,ořHB)))Ä©Ţř]X„Ţlbő[*_ŹÄAsĎĆ~l6.Ç ||%tjÎ8©0ć6ďE>u¨Źđq[î}0Ë– Á§ÎŁ%ŃÎÖŃöI€ňJĄéśź?Ů\1~óé€ő±}KcŽŰK‹SňŐŽyŤ›‰ơUivg…í3%˙jTpjk.;’V#?ŁëtôF/Č'â4#ž=°<ĄvBínŻ^“űŤZĆő“6ú̡OU-šň”Y˘§žšîR¤óVÓKVs“:hŹPÄ©GĘÚŮÍ !ŽÍLIťńzV"˛üąő›QĐ G"ůĺIČ™ĺű€EÄIÓŁx'Ů­UFÖ;żm‡Ŕ@G†đ)äŐ10đ=~RÁm)EVČŐ¨ŁOľŐÜŮç,­â¨•ÉZőZ*Q-lĺ[-ńFżţÇżBŚBx0°H Ö2|~Ú˙Aů 9ř2Wg™kn¦tź¦ĂT] ;Yp•ţ¶«Ź7Y—nŚneG>ěÜ⣡ÁńJ0×Ć]ÜŢÄ´c‘ͤ¦9~–“9ÝXŁéęK:›ůŐ‰FĂô{°ăÓv¬V^Ď”Š®>îüb$‡č_†řţh‰ćË’Dý« Ź‘‘ ×„zľqzˇ•okFâÍ^ÄĘ,ů3qÖu`ťČżđΙ}czmÖ% Ű;G»)´rşfŃ­ŕÄTé˙vn&KiŁ ŞĂ`żň—Áľ®"Z Cß]ľË§îĐ6*¨űT*®śôňô€JŤę…5O3… ˙tyŮ+–Ć`§ŮŠó±Ň#†ćĉ/CÚ˙&J€ˇ¨ŠđpĚ8ŕé}ꨏç YűEʲ"ŕÇšëU;'Ť©‰ “Í4˙'Ý˙1YLɇ!1ŠŹő$h…ZšzPÝËYSęe>Źm7z´1ŻÚJ ş-‰;ľµë©ěĎÄD§@â.™ľ$ąM%±äMéąôŐŇô†‡ÇáÖÜ>Ţť!řu¶éEl2‡ŹčÉ6ö)C—­ůuz˙mYĽEXÓÖćbR‚™.Qа%;Hđ§ŃřiéK!úý02A(HN§¸ô!‰_N‚‰ĉAăÚ#¸á‹űV}>Ëë‰5cłă/3>)S9îIN ›˘@Z ­ÄIřé·ŻóŻ" –FŁ}bcŤ˙sĘňŚŠůľZ*Ax{˙,$yĺŢJŔͬźá`_:őî«2ď˛ú–îÝů¸ä^/Í/€\{ŽŮ«]ńĂ,…Ű Ď}»´l „(ýť—{ e•„”brU tvřWŔ>ŠžŇpŃÚłLëu‚Do­ó_?]JÂçí+PLZ’E|â…qő)ůÜgÂ! \fá -2tšäë0©Ă°”«.)71ĐVv…ě¦Ý‹«ŹDÇ«»*H9ÖŘPiÇ»ş$ x6Ş˛PČg§72dź#ąË{őJč´+µCxk¶[ă¶„Ďk‰MŻstSŞű5¶—˙hPÇL˘‘H,6Ś^­¶V%© '¤ÎÄ*Č!ý[p †n7CwÁä5ok)ÝţtžnŚz¶Ę“aKKҶçĽ4sč×Jř oľZx¬lţ†|9i˙tݤď*¶Xě/„Qd%†yÄ@v~”Ť’bíy\Ň™;v­3¶ąŇm‰¬Wkő1é…”YüDďHĐ(6ŇI»üśŤgaQSŽo¨?YĽ~ťrd;^XÖěôV1ćËÎËőź(ŹCüý#ń1j‰e -$IÔ``ys?™šŢŕ.Ę[ ˝ô=šEěIg6B°űţO^‹2>3śĄĽ~ć%Ř>Ý%ź‘řąŤR˝őđĽŕšE± ‘aĄ“Ň\}q“Řš! ÁxŔP`}ž˝Ęq.8ŁÝÄŞćř·´ŠlőüáÉŚ%e~Đäá5 V@$©ô żtRá ĹÂ4"î<‡Ţʧ`V‚xż/k]ÖD‚«ČëůĚ•TT0@ÜľeöŚÉş»mK"ŁŰĄńśçKĺşá·r˘BBŕź(ż ‹ZĄ†¬_pAřöVţO,ë:G+\ËA3ŕ}ĆĚťi` 3$ôvô¦Đë~ŰÔÓś^AŰóđe»Ăö¨0CfL7…aŁI EŁpň_弮^’Ĺs˝™Ó¬®×ôc° šr:Â^C M÷Ćyć+¶ă•y´/d7©‘¬Wdđ'Č`°u+摮쟥ĺPoú3öMv™Ţsd‹r5ŻXţ—‡čś 4ą8_¸ÖqĄ Ě©é Cw.ęţI3r‰ěšn‚ţÄË$^u:›xś1lş~Đ€·ĆŐ:Öq )äMťK (Ň8bڼޤr·ŐĘđ6QşĹý˙ΰŮI #ŠôÔ4ˇę DŠNĚÔůQ‹< łÓ°ű˛üž,xĺ:XzŰQ=2Ľ˘((Wüw#Ł-ŢŢĄzP5G„í(§…Ő5ć_”žń3d]î]µ:$n—ľöĚŕRŞ %—ÇΔš‘ÁĽ¨¸×3ŰîŽKô­dřŔđ«“ţ`U˛Z\ę‚x YŻđúÖ´ŚôĂr…ÓÝšłh3ű›F&–ęgěĺ06o1˝S±P8 ĎS"™Ý—EYŮsjś°Ü ¨ סúÂŁ(®lÄ ÜPvĹ‚¤Ë-S¦ąí¬H$Ž9˙/µŔŮ9´Ť|ÔL¬ÔYWŐŤÄĆn„”#¨Ä“‘ěrŽó¤’ž1¸Ä9ć»w,źh@<3פĘčlőšő?ĺáLđßî91RÂMâ˘b,S‹ť©$­Î Ó[’0Őö:Ü”“s‹=Ý0Ůń8Éô81ĎüĄ·Ě ĺv7×óÜîdMi^Ú©UQ]ż§)WŃ^ç‰ŐŁ!1ôÄů?¨bÎ2Ę#Gľ@1G ľH¸¤C- ÷-GÁ‰S"¤q#łÇë"ţRĽBíW 'Ć^!„ˇEFËrźµ˘/0Y†NÇ-aQz\Ýő÷Ö,L^Ű5ćü1Č2ó/ĂK<>ĄwŰwp\—&ŇŐuW#ŃŘm4YZ Ĺ–âµçűĂ5^9qáę<Lż]MdTI°ÂÝ̡¨âđZş2ř¤Î{UĚč“)•1xžă,¶‡˛•‡X`UrU6m¬čp ÷nÔ8vX“đ1a¦Ć·Ň@íTî]{b?h^i©ŔŽü$`)—|`´›q©Ľ17ČëHŃ“«-¶÷ŔăÉÉgş»żďÜ"Ů%Ą: ‰I]%ýß±›ĄÄ€36N­“˙Öy=ß m( ěHTCěwĎ©’îĘüäŔVHpŰiqÓ4€y§Blf`:¶–*łhľ¸±ë´7âK)ÁĐ<ç `x‡<*ź Î—ÇŚb®q±=ŮžĂWgŔ%¨áP˙uj;YţúFĂAŮ©{:7anˇnR_ |cj\Oč •ţg?Ž'ˇň  czŃqôWN‹^őHbťĺäÍĆ[‡?ŁúJťIrŃŢ‚/ÎÝs©ĘŚ˘U¬¶úNWh’3ĎůeşôvbôAëČ@˙3Š/”™°W{ŘÝAÚ^?‡Çß]_’T·— Ű2 Ĺß)ÉÁ|íČ:ń?ßžĽyA—<śČ¨Ď´@`ý¸ľ„ĄOŽpÁťůM[)ôĽhśk˛î¬ÜL!˘ç…ŚÜĎĎľü;lŹĎ» ÷O«AV6Ź˝ ˝&/:ěžÖ Éšßé[|đrÎóĚ+Eh–tî0Źܾž‰úNMĚńŮäD©vPS:J±ŢűnÎ=ż´\˘Ąř˘ě¤Y>ą .¸úeŁ‘KĹMľ1ŘĂľP´Jű®ÇČýopőt׫L]sź˝×<‘°űĺss3x“bh*1p`tŃŇĄr0v Y¶őµÇî ŤuŤWÉŕ şËĂ8Éř:őK&şőCé'˝ #Dw<v†a9UÜĄ0~ö4ß]LŞ×čh[1ßźťůľA3 <*¬¨dčë‰*Ť7MµG‚ExŐŤcV°Ľ+Š'¤,táľŇ6á˙p˝ŁáVŢ2<ÜßÄ O]b§S†|ą[ҸcÎąä%ËG%„#$3J„%žFĽv]NŰwP\©˙‚v*pÂă–ă1;®´‚VŐ”ŰsÇ÷úĄšS%›Łlůä`EŚŕܰ*9¬Ń&ř¤ZŃś?¤ľ6d OhŻÚńäƨS+G2ÓŁŞęSă…ě9»MµŃmÉ^Žü„Ń÷Ž€"+¶NîYN÷ ,yšTÎ EŐ€´”Ń.‚R^ž6g "«żŁäT n¶®ú¤v€k×?N¦ 8ŕ‚´5ŹlAľ‚j&ÁýÔ˙1é ¶Azôőoćâ¶ŔpŐÉBý8Î_ůaˇ Ť3MÔ?ńĎęô?«Ě_zş2RÂŇbŻŰ‚ęô߇¨ÔQΩ¨=”łC?0Zŕ@D”禫xa‚ąl›IV˝ë#š|n/` ńNPÂŹí\ž3hŤ"‚:ny^˙[—Ľů¤BŤIş;T5«ĐýSf{ş‚·l%O.ěĹXŢĚŽg@î0T›$}®ˇ•7Úi›E —M‚gq÷häCâň=5jąî—8‚gĐyčä%?¦€x2Aµ\Xńw‰¨+7—‡ŢńN ć=Eţb*§¶®°×»˛,®ľóUäyöąű/KŐSDÉ#.!’`¨7‚=?˙,ŞjšQĺ;OOÝë“qvV±GEĚJ ž•DÍŠĘ&U+¦ŁqX© ˇŢ±s›nŞ%NĎÝě÷ŞL~A)Ń`ËtĂmüŞ30;űëłTţZÇzS–ŐŽŹ\H &ôuĚ!¨&UßbÉ4 TKőĆ7˘ ±üµ­’6§Âôv©Km3,=/·­µŐVíILŮ´˘F˛\Má5Ć?R'PťľX{캥CĘjQf Đ7ąkś!˛€@ őÁP‡”cáD.ŐşDôQŃ2ŮvÔl ŕ„ě$…óř ?]XŃÔŃĄzýĄ7«Rt»ç°šou%oŔ> «Ť˘ ¤ÓĽěĐ–˛.ęëŽÄRwEłâ^ś­-XËżYď2$łjŽ“sş9Ź ç'Ŕ)ÍôŞľý;Ř.Ü‹:LYż˛ÂöoE%ÝFÉČYJîJíŮ”çĽ|ő}8šË‹ĺě|X}Váđuń"$+apá ď Ę4şz2č>–.6I2Ş5cÉ4Ř!lşé† @&r%|řµVÍâcC侸č/âPŤ$·†02&ÁÇ äĘŔ)ąLKFÔRL*Q’äź‘:Ôgk±j«č+>¶Iˇ®Ň?H:żžŹŮ‡(cÜŢźb!,‡”Ý‘ő¸+=űsŢÄcĘâܬN¸řaҡÚ÷¤^ü«¤(×,v–zr;ăNZŰěňÁĆ”,Î_¸EŻşÂ×dG÷1ü[iâ2;‡×ĘEM€’¬CĎQş{>w–Ba_I Ľ’ŰČG¶6’–A *yřŮó~Ţ×Ů~ĐčOšPĆ#´DŮő‹Ý­^Ś=RÉ7)S“č {L¸ 7w©É>ŔĆŠüô€(ť®~bNÜâtP(Śd퓡Hh 1 őëłéŻ9ÂŰ}đĄYaíó `—:©»—[3°eqĐ9ßQbĚËŐˇ1’<,C)Pµ‡xżćş˙Fë3ýě»óŠ<Żz}·^Ôň5%‡LsŹ)ő*ÔÔ–ë®úą°×ĘĹč8ŕ*©;_~äŕź…ŹMfNTµ1ůOŁÖţ0é>Ńë%ąôÔťňĆĹ´đNłq_ůÓ* źŰ_Ţ}ý»ź63ÔL¸dčÇîť6`(ßů2aá=.˘ý@W‡ď‡×wca#yZ=Ć ŇJŢČV$.qŕţ0ŠŤfĐfpŐ1śyvšXˇä—©Sw1ČŽ›ĐMű%pęmédő9[˝Ł­±ßÍŻńţ®Â™uKµs1śSQ˝ŃĽÖáuý`ýşŕfKŽ?niLşÄ*Ňm‡›MĽľÓ×ů´)éAâN¨·d°FĹŘ6FÄ7\ĆŞ!ŘAX­YvÓV ŐvX7Ą¦˝˝cňÍĄ!ő(~¬ĹĹAŐŇ”ě·fü7öľűxtáZZ4‚ڬ%ůÍ/Raţ›ťs’uxÝđ ČşÔ˙ö­\ĎŁĚ*8¦.áćĄ]żŚM[é2"Y˝ SůĽp|Ý §NőŮýL¦[ßćăůŰzóΨzšV)Ďĺ1[:±ÖmCG5â. tż^"FgÚşĚ"¦‡OX™Ú†Žť^ ¦:˝Xp–ZÂn H«*8 S“ţg…Ďy–_N'č1ËDÉĐÓBIÇíÁŕŇ ń!Ľ2Zž’b±G÷gIEˇ_eCT$®Jąëś°¶Ţćń[ŚŰÝj%kÖkŮžuʧNČ|qz…˝—*úP Ă)·Dl_łĹ_ĺ¸3fjL¬H'Ŕ«¨Ë‡—ĺ3©3ݞ•DÄYůćoTŽ®Î—ˇˇąurĹ~ú9.6XËĂ[äo˝ýşĆ*T·žČ%˙‰)7Í tŕrÚkűĹFP(ޱŰ!]§…ŔSĐSßH"šĎöi–ćđş§ŮąQ­°!‘é• J¸Ýs"C37g1+g) ëÜ)Yj8u;šš–p{†bÎ3e?S°š˝ôL5<ć‚ňěßu‰F˝ç ĆT?%3´ÔyEúě[D¶–ý˛¬Ý=g?ý"łŰ“ÖyGG’tÓ|5¦ż”L¦őÔ))3´ň«gŞ^zvŕëíGy¶”łĆť~Ů~t2¨§6W¨Uá ńĘš‘6q Ębř˛Ýe_Ť_Aô;Ľ éÓ«ŢS„3@u´ËŠ˙Yö›<Ś!0ű żô^Ú®ä-čś ŹM§ŕFyEZŻć1˝ŘŻÔńcë{ÓŽ˘_§ŢŐź–Áß”±˙LĎp^DŇéűXOŚ™8věAC˙KŢSĄYËÎą(çbF D`łpUQĘ<•Á9žŠĎHTˇČ÷őÄř@pď!‘‘UşJß§+HB‘Ł) ßK(ú«?ľN9EÇ2lJ r¬XKŤ6K‘!A éY09ň҇c"^©?1‹׆Vś,ÝĆ9ú=N1CŁlĆô[ Ě–P#+W,`<¸ÔX®° oŁýcgBăx»Đ~ S”k¤ů‚ţäýŹý±Oˇ v¦D ˙yŽ,Ĺ’5n“ŘVNY÷ŐhSôä±V/:¶y'Ye¤rę3´>cŐ˙b$Ix}ëöĐţ]śXNŢGÚűľRś‚ĘĘ{T1ę턌UňK„4ęż0y©Ă=´ĆaG§Őę˝đ# pâ´¶°5ň_áěB´Š€)r2_XőĘ9ÂK€VpÉP:”A}Žőž…Ť^oţ^Ć-Ě Ö4ŃßHëHÖTůxŁRŢdO’líů‘=/kYi+Ő:EśB'ŢCü˸núUrăA™„’«°ô0bĺ8a[¸ ßQ1[ ‡ÁţLZăřv1ü= ˙CŇ»řzWcřtSü;ąţď˙wyü÷ľü;;żĎoñŕqÓ±Łá­3Cś49Łô@ńöŕ©}–›&ý8_<ňeß?ŚäZ“u­¶(ÔWjŐx`VVŤó˙NŻ Čėޢá”<•Ů‹˛ä×kâí[ß޲r@î°ŻđQg ü.ťŁb/Ö¤–{^°Îé@tl¬\â…Aȇ©‹3dý˛Z§őśňoY+_YęE¤ŮxŚ]úA®¶RcSvMm©ÖCĚQĂ¨Š†ÂÂr"Ô˝$XXęj‡nĽjÇR9áXPk¦˛˘đŢ˝%!XžĺC‹ĺîă{”ŢwÄmü;Ó§tHl}płşŇşşËĘ‹Q;"ěf/v FňŕD KrjđF–\Ý'[ŔŻĎz)\Ś·„ľâŃťk˛çmzzíŘĐݵ(,ąX2˛`'ÍfŁ őçQ‹·˘şË—[ŔЧćúó'0ÔUúR˙w7ĎŤŰ’ôÍśŽ3 Z 9çr\ůw÷µ/qČ“ă⋉TIGÔé:ß[H÷d úm= ű í<¶Lýo÷k\˝Żî\‹„LÇPISA-Ľ]¤ĚW ĆRÖnŕ(Gőŕş|î«e&ĺŇŢ …&|ÜŽ`$t[yµµH®Áp˘żŇBÜ­}—˝üđdžD éů(D3JĆ–ć^™?«D JßúĆGG8Z°ŇZŻ á%µ­a*‡ĽFK-[˘Óóřľö5Ő($SĘĆBřZYűĹ»…pŚŐĺN¶ŮŠq.©ž}Ö°sÁxËĘ⤿ƞ`^NsĹ ×–×^0¦¤B†0iVýąH3÷“⺏nKí"†ívC ľĺݵN¬­‰"*2 *¤])ÍSW°A¨_s}0>ĐY ±\Ů '­JŐqvŚCÎL§7žkg­i4 L‹X‹ťy+Ij ŇĚáîěšGúȸzěŁâ˛őTąËŞ ŕ‚Z˘T=F Ű>ŮfŁíŁ…vś.N7ŞJÍĺ@Ď!hjĘJé÷ÁÖO`AÂĄbßAóĚk6ÎUěćÜnóš¨Öjhm€Ŕr7ĆćÎJcłá–LĽXĘçĆĆçřŤxťµb’7N‚×+j!4Ďa-ŠŐą¦žťĘĆ0SJRQ–¸Íë+á[  ‹…ĆŚ¦Ż6e¸ďŹv3ZŁýžsń[a!v­µ7±ViDŽ7qŹ«T<’g˛ifvćG˝ČŚ$Ý>Ľřt2śŐĆď€ţY4—Ţô :gyú$ËrŽĄ—…9l5Ä‘đˇ^ĽŕLŇő­öĘcćwót›±ŢJbëľďÔ“o‹Ł2>űďěâe¶Ů…Ž>¸® &˛‘±˘a ßR±3i ßQ·\§0uµł­t.uş„HVĹ8`[ß‹â€+Ü]ťQ€›zB!Ř–/${Če‘q µ­ ńZß;ţrŤ~đ‚QĄŁ¤öĄ}™¸¸ľĂŚv gN§§eA9‘ŠH Ďî†:_ÚĽű3ćÉ…ąăĘ1ŁdŤM3±Aca~…J¨ÄĐ«‡Cđ» ŹS¬QGçÜdEŇ3®ÖoÉ5Xh ˇśČéĹ©ç©J]Ę™šÚ&ŰŚŽŃ+Ć!FŽßÓě­· ˉâ6üéjŻxĄ%śYÜ^‡-©ć*Ë,ávł—ú#«ˇ.Żl”HíZ€EŽby_ţ—(ź˝đYŽ2ÚSVŮU[‰÷Ă I߼§©|ń¶äćľ©JÝ‘a•÷I|ĐŠ~hg\tą s4Ě14“Đú€g‘” Ú‰Ýý>e=eşs˛`‘ý·Ç6 o@…H—8« N]"ŠHDP–%Ń”iŔâcÄó•¦.[Ţyq9ć¬|§¸Ri8Ŕţç;Î;KÇČĽ˛Çŕı&?™%_˘¦šÎŤ]U…Iź ˝„śîÍDNVA/í¶Ľ&›Ŕ%¸*üíÁve m´r\űP]¬?ž"íđ]Î.Śuäuoó päwęĆT™żR!|Ě˙'™Ź<fd¬ţ!ucCˇČSU€®.ěúá–€ÖÁ`= ˙˙] ’nŽpé>Ďçs×ŘybˇëJŕÁ‹‡ł‚CŮ”fŮ!}?Pn<_†âËýQđ⿤‹őµiů äŐ?oîóÚ™PŚŁ™Ę“z믎čŹ@Wdʬ¬ő V¬ĂHűXÖ8Ž®ęY˘?¦;ŃP[›Ň¤š3kxý¸t7«'…¶öŁ3 ř~ÉĆĎźBFÜ=Ľý˙y•<¨0‘˘âíc±04>˙dĆž÷ÖR9ę3ąŰW5Xź´sÜş*XÉE˙5j[Ţŕtp~ó3äśşÁ߆ï™=Orą+ÂĽö'žĹŰFňZnˇn[ń?ňĚ5Šdλ Áňťjąć„50yĹÖ˛bą„ă÷A*sźâ,¤;±§Ąřo¬ }›ý'8]1g‚r!k´ĄÁ Â˝˘Ü á®.2ߦmńŹűé*z xĽ”ó"4á2Ń ‚Ŕ)Lg˙kŤÍ{ŤŻłĆ¸u őFĆx6x;˙ :wĘQÖH"řľ;'“´eŠ7ß/ßźsˇ‡–;ó\MéyşGś,‚ý˘ő!ŞŕbI¶ČgÉôéŽčzo¶D<Ă·rÎr|‚lăä.VRĹĂíŚČ"}(đt……H˘•;śóRé›ÝÖ9ϱϳ4٨4-č zę\ťG°LŹÍŐ*3/ĺ€$ůă řŕ…ďÎK~Ń´‹ÉÂć"đZgé]Bię‰]Á­čP€DĂęŁ78FŞ… VľY„ö‘Ĺ€\uÍ.ńą}öšäs‡ů6Ö*¸qâŃQ¸ŠĄ%(#_¡=lećÂĐ[kb űŢű“8Z—ăČČé ±Ä’•çž.L(WĘŻ˛Ěx]‚‰]·Č±ŽúŤjIG‹›-ÝÉ벳fTë2 ®4•¬úěTĄ×}j=°Ň2í>ň«x¶±q”O“ŰÎg´ů—e"őť>oTgo@ăG{mĘ1¶úCř:ZŞ˙+@Îdwx}˘ĺWoTá&Leµ‰ŔS F* ö5îY\KřŞžßęl#U&ÎQŻl†gńµń"TÖŘL…*h€TRżő·eW^ŕřôF¶›\%¶{ľ¤Xşk]Á±cĹ–źţň!.úQ9—Ş(©Ş~¦[#ďéIâKŃoÔ8\›FŽ®›š˙pgť—š¦őBw:Ná¶côMRÝ56C‰>đb”’¬AÄ­˙kEőŤIşâ5¨zŠO|"K>K;`ęPâ}Ż,>q…ĺbţMjŰ‚ Xů«"ąĂN"ž˛WŞ:–ÁĂžŁŇ­Ęŕ٦íëú+Éż]ĎS<ÚË%Eâňüü Q^ľř%ădŢ čą‹ŮŚ˙{XěŠ !őő-JÄAW¦äwAě*µ/ĘEc…·ÂcŻ™2“čmç6Ń—¨iĹëh˛ż-ňN=“mĬ5Y?ˇ˛LËÁŮDü Yg€şĄ’vCo{*v2Ú"˛±ůŇÍĄ‡«ęď¦0:{’P8sş0Ńl¨ć1Ń9…Ľu{ěš«†ë3GKŹ»‘ý3AÔĂö ¦v>Ce‚Í,’öĐˤô9}›ůů“l“(äeŞőÔjŞ9đ\‡wkF¸p5šuŃňĐćU‚˝P§µ¶ ‰M8ťO?{Lh„µW©:ůbŮĘC86ŔlW-Đ9F®Řl§§¤\—÷DB0'ŘÍđlÄ’V6‰ć9đ˘ź&á–°4fí„2Ţr·SRF…ç#ň„üĺ)쎾«LtWëăp%ϲËŔşj7|^ĤŹ$ś —Ô1NfŢ4F°żľ°RÜߦäÜ܉«˘l!Ć'äú<•ÄŔ‡Ćf 7şˇćîď0˝đA˛?ÂűËgâ>µl“¶˝×0{×Ň«2Cş`ĘŞ[GÜJÖ–ťb3.ę0ýhp˛¤—Ś!;OĄía€fĺĘ.ŮB÷V8q´)ÔHŇR!3[óÍśrÚˇkąÇ‚×\궢y5pĄ/•’hž^U>˛űmŕ'ÔŤ¸TňđżtP˙,Ül˛  ŃÁ wLťĺó ň¤¨˝E?ˇ98ęÁađĚ+P7Îë«hËö‚A¦†‚·ÎˇŮDíĐĚFjuJ k4w.…ľp/˛ŮŠ›/đg‚€ŚyéOç"Ľŕł ­$ën¸>M.Ąp§¤#SČFŁ!żTCnüÉ­zpVqMXríXš4ą“˝=©KÓ{扩ißÎĘX P>Šđ|pxâ$Š7lŽoâP+×Q ‹ŠŻŹĐ®l\%ƤF˙QQťéĚŚtă˙[ŰčŁŢŃ[q”ÄwËvő{ńÉ}Nö]ŮŰQú¶Ó¤„=Ł­őą _SĂU˙Ii tŞ Źi(Ĺ?~dű7®¬ĺăg©:üMŐŢBBśy˙KŐXŰŐz5hMuTÇÂú1AÔĚbŃĚ˝S\ÂË‘ćn[‹7¦Ô-¨żN>G°{fYëln8u}¤Ň±Zú$ľš[tžpi;Aę‰÷.É…VIŐ•ÝŤąy"+c|‰oďŐzýwULkđëčí\áďYŤÍ&~:O7T hĂ1O9Ř”‚őż3ĎM™ďlŰ!f|¶ę]śŕqîłĐH^çcľ±8Ôß˙J’Ű`‘YKéĚlŞ—6óÔźdwµrč°1ŃôŽ*ŞŚ«*îňů<Ţ2K…Y~Ř0ŤŚ[ą=zŻUˇ˘Bb©gJú–»}ĄXQŮőád‘T< Á÷ďť¶c„áäÝ–‹—ąz'řbÓpa–*’˘¨áL…SśúC7iőü¦Ët!tY's„ýŇÜž0ôí¤´é´ŽNľ12¸Ëw•-ÖPÍ$žŰĂuěG˛ëUŃrŞxSôüÝŁř%ÓĐţ+ýL€Ľź7\ŹřcŢ`W XńY8ďđv -u \oŔ~ů]’¶h4(¶Č¤°ŚČ» Ů™4íXí÷łĚŤAń·T¬Ć“`°Ń5×-AkĺG캀Ć#Ś? ŰYćvľNKęŹ ^e-´\řCż”G#8Üß#.ö&j–úŃŇ2ŢR}Ú˙:Dvff=aőDŐ$-’Y•–dŔ0އäg~$Đ}çIEţűřĐě c4ővŁFU¶ţí]˛%%XFŇńRşýë3 m_˝v6«#aëK¦Y…W^%\˛ňÎÚőmäîů‰é“Ü– Ëŕí_˝ÉřŻH/mĹŽAQşŕÇ~‰ß™‡dRUŕŢXśh‰ÖÎEvŇŰĐžš‰ąĎý Čßvz ůjĂ:Ý!c@öłë6ptÝ´XPŕ*Ž˝.ČŽůůĘ#ÎĽÂ rÎálĆrxG]}%‘yMŃ2ó‹łîDˇ#oňnţ‡»K†DČË4ŁŔč+J#y~x S7nsďř”_JtJ 6TŇTFÁ­âTăé—°ő¶DbokŚËč~á^Îěío+Żöô}5ÁY}±Ző‘ă‡Đ„v†Ř ɱ˙^şĐMý˝ĺ/Íñ>»‰¬fSÖ]Ó”ÓcĺćrŽ›łÔ˙ÚUĆmFá}Ă;J~ÂÝ÷Üu'j—·»ßLľíÇşđ*i™*×úH$âÁÓ'bP’<†ŕ··2:ὺB[ľ°ä*Â9lĹ8_˛ˇŁ·AśÓąÖś+”§ˇ‰ăČśŢRFh)Óu˙@Ȳ<•Ć´~†ô˝"žĄÉ„"ŁeYś0W¤Qö€*ŁĄšu›âcř‰¶Ű˙—ĘN@†0tÄ®vpd$|9Sě>ŐĹ—šçaýŁgŞákOAĺ=_Gţd>fĹÖ|üČ˝˛Ť»Ž{đÖk őâb ńYŁ”ŇW›čÚëą_ýjďđH˛jÉÉč(Çç°ŁD–Řś…_Ł!Sî •¦ßű[É1˝;śÇO¬ŁŹHä2ć6°í'/4E ČžŁžÝ:¸W”GLÂÉďź6O±šuý Čg"„QQ%IQáXpű3ôč®d Čă39‘ďíŮZŁC#ł? /ÄĽĐwb#/Ç㼆Ď(Gz«Žú¤sľ Đ}¸ }ĚÖkĆľ>Ęěv­‹t?¸śM퍜uł×Âöă%5ÉcďóęV•ľś[»áx9’ aJ×ÜçĐ-ËŃÚ›Aü@ď¶ú(7_ú¸ĆööĽçöË*Ăü•j'âmyG‡OZ(‘÷â7ę«ČĐ‹®:‚…íý“¬h­c¦-›',ITÖ»†ŰáuTČô#Xwr9.fÔ‘×dżĆÔ]ťáÉÚľ‹u}dĽ\Ă©‡ŠĄ2®nUɰ,˙rÍżŹ˛Ąš¤ u°|ʦŞ'qâ=˙gŐŞ0+˛ůÓ¦éöi‚ł˝ EN¬ôż§‚ST´Şwt¬_+ńľŚ#Ą+PAî_gĚ,2Ý+ç˝ŐŢ•\–Ř|y1Ë N-Ę}2i޶?ĐävzŮT±©Ł7µw'Yń’»Y7rmŽZ°g#bśş ü[ŤćOŔǬpyĺýaă!ö łg{»Ý?Á:tÝnś·L,Á9I”HW´>´Ď°4x™Dż´"…ó{¦ňľ$¦1=z ą{ťb*ݱáDĽďł+K˙P“/Ă­€yŕQżPęÔWňŻ·~,í|zHwŔÔţŞS¤7 Ř <*|EĆöšeP)5đŤk„NC»0Ä'ěÂuUzŔđ¸xŰžIu}˛~?ÔŔ÷pź˙ K'ň>˘´ĽuŘž.†Bżšz“ôţ7kühő {XfÓ˛3Eˇz•đërŔsp„TĚmĄ$Uű:eË×WçhH>ŕň ÚäíŢ€%Q<Ó*YŞŹÝśÔ^ĹIÜ×÷•x×A'§ÚęM *âţúe^éŤm†MµRŰ{(íďŢ®,Ŕ–źŞ‰Ş ;ݬ(’đŔLkţ6eĎ`6XăfQ.jIm[Ż$ Đz0L“بšćđĄßČn‰Śß[¤aŠÁ“Gϻڂ?ńĚŃw«xÁöŠ6"ÂđÄ™U¨†É0JÁbWŕÂ~ycr`eOŮß·›(ď Éýk ŃÉ6D”˙ ?ďÚĽí†Q…Ă÷˙,­˙XřÔĐŔÇő`|4řď¶÷ ,­ýxЧŚě—ŕs‰ŻX§ŕŽF±’8 G)m3˝RŰú.° `—|»&B/d 衵ßÚ´v9µ—X˝Jţ«ľ(¶12z°‘‘VśŤ!=X~ďÝtýŘw˛iJµyđV]čÂoă*lous긎]ô3FÄ5Ő G§ú¶Ł‹U<3i–ů-ěôOażŁ}ŹiđˇźÚo Äj"ÔżEć©ĺw,(jŚ9Ň›Ź’Fť źÍ Đf­:tQNčkť/ł‡š÷ĘË•'M—ťHF+ĐÖ.«‡ŃpçÚůĚ{űjłW‘ŻĐ„ÄcIÚ÷ť•™goá‹Ńp ß§MdťTÝ菨‚íh<^Ţw}Ů”Pɡ˛É'0‹îCťŃřÄ;\weŹř­dßjîŘ‘őż\ăĆP(Íť úÇ{ެ%JÜ•{Ś8ĺ›U?RU˙ýˇîu;»ÎP÷ţeuášÇ”čšxZ¦´×]A#<‡vŞ!jŻ„-Žé¸ś°~hiV‡Ú1]›©—|Zmă;ëev˛»Y‹pŞúĽJă·~xs Fęä©KĹń1ąň(qÉ;¦˛Ç‹Żčĺő‰‰€ó/ßj°[Hĺ˘Tzhd–t÷ëFBś˛2-ŢF,^h<Őqى’*jěp¬ô|ˇŁ±vŚ•' ĄdĽFHąÇÓm‘Đ|Š÷Aż|JúIç‰6[Ĺ+¨¦Š‹ÜĺĂ%†ÚÉ=wl.Lô˝tmÂó.ÓurË˝Ż…Đ§ĹzŃŃ–9Ą˘?·p=î ¸I<.Jd•Üf»§«„ťőH—€˘$DÁ¤Ź;ţ0%ŕ÷Sެc54ń?P>3ś{#±ßŚwľ3p]üĎHbĐíĹ„P’ĆçlłëÎĆWýaˇť#źé$ž¤k6`ŕ|v«Ö6DçĐ”UEH>}ÝŇ}ąŘÉś`Ę˝7†CďU¬¸6X:"$x›Űö›wß#lŢÁ* m„a;>ĄJ{vŠqöU:k÷j®!l÷.ŽÂ čŤyeGÍʆ SsÓÜk»2"I¨Ź«Ł.”yT¶ä‡ţmĐWP%J,;Ť3 éuY/Uż ‡®ĎÖ]ăĚź%?úD»×á•GÚ#©\á@Čm]0K˘ßS(Ľ˛^ÜbE”J~vu“vu&řBs źŁ'o˘Í¸’M[Ű3‘–tµŕŃwm•#ąŠŞ»í9-·űŮ̬iHlˇ „Ň!úů"92Ăě­°ůĐÁ ˛ŤY´ŹÔ]™ÂGE´ëRl»9791kđČĹŁBěďgşˇ÷Ç0śôżĘ/OŚCµ®Ń-!ć~żSëÍçč‡ÝwéGNaiy{f‰ţQ»÷Ugw“ýőĂŘU†Ă‰ô#q•úLŻKĄݱ‰O"íÉä.¶RýPĚ÷ ŕäyü0Ńä8RžĆą„wdC¨´Ě 3ˇ‰ŕťŮ¶nďPŻYF7ăBR÷Ť’—¤ -řj–{5á(żJv÷ëí °üé/Uđ÷ꆜśO–ś‹ů yś—˘É¬uۨ÷­ońÔ/<ŻF}:µ›ÇkŘôĽĚ9ĎQÝĂ/ ;ľC µÎ€.¬ř¨Tśs`B1ĺ‰ÉÓz¶śFP2Ć]ZΩ1¦:·©›ŹJO É8dżŽÇýd€Ôޱ#>g쑨UWExä[]y,Č«˝Ňř¬s&´®BOX€¬ ď(Ł_1ŮťoŐýĆKPKp ë×DE?7> iţ–8ő“ŔĆŤiLá$ěŘfA˘­XpxřĎë’üŐ¶Đâ(8S}ÁY„:™ł$HÇ!€ë=(xmźŘĺK]#©®öá&‰ +äŃÓDśčńy¸4ů*»±YĆŐîLĄŘ^”"&>˛Ú‘E źçt ¤şo%ÓŮä†Tc€4qÚµřpSúBp”¶b,& ÝéÔĂÎŕ(yş9‘1ĐGE™ ™˝“FňĆÉOĽĄ_Â.?W oÉTÝůrôJÓľôVś–wëčŘĎü¨UÉpőšcĚđa;DOťâĐÜ?O+Ô˘r$‰—f*ŐXxŃ-@·ŔŻż5ĹóÎÍ—rýÍŇßע¦÷X%¦Č·–CýY •îňzp&vöZ 6ÓËđÖÍB%"QI ·Čł˝ mÂÝŐýµĘÔb‹š:†¦|NöÚ» őŞ©Ő˘ ©¶ŃWŽA —®dľŠ‹7$¨ `Ş–ť®D9/S‡©·P—ÓĆÄúb÷=ę ś)-Eď#†ŠóhŮîńxň‚»Usî(dßÔ÷DQÇiÂĄcÇŚ­>ý‚ň*€!;ýßV ^Ś*z$#iiǧK€Ďť~+K@VâÉŘkK< ęČň0Âgşp‘îÉIý˘5SH)Äőż 3\avIöb‹ű Ë«¶xë@ȬS A9ń6—±jVlmÁŚ2&4ĄŠe¸Ľ==’˝SĆáž`z–üµ Ŕ˙ ™ałć/r·»:xㄇ66Ū훼‡®”VîĘ«fľq É5|äN0\Ë0Pz„ ¦Óş—°ž cŻ}9č׉+öŤY] :´`jßB«Ú7÷ps® {ëóŽ%9«¬/§r¸SwŘVAwćYĆk`Ś‚+*×_Ąmc0ţu!·18|€Ôc/˙fmt؉Í2Ĺ—Ô2‹ŢŽq7 O—q‡uŽ2ČüëVB¸ĐíˇXϫǶI´Ô›Ö+¶ě·›‘:`{ZuŐTˇ™Byh±rfw´7Ó¸.X^C¶}‘ůž‡¨Â[ĘY“ęŞÄÇŠo$%*p ϶͌31$Ót€ęÍjąµĂŰŤxŽ °y˝Ü-‘­Éł?ŻvÎ/Í VÝlZGpÂirKů˛»#ßĘnßF„~űÚ˛ËĐ˙aÎř¨1kaྌŃFď㲭ëźA&3ΩÉ*)),( Sł…óCżfdÂ'O˝ÝÖ´[HŽ$|{ZÄÍŠeSť_źéŁáózÍU¬ö8ň yVLíŔzôfŽŁEť3Φnˇ¤$«§m ÂeW6ńł†µ2ě<µřą&M©ű4 FÄÖ*fť­~Q“ęoÓaԺЩm–őX7v?âÍËÚ$rÂcűüąâ}oů*ŕŢßTÉ‘‚©f$yÇ™ć{Áëî&ónÍgqűÜÁXË:Ž@2%có˙- Ś×äŕVS1_´gçH||ŠŕUtGš¶ k˙ ŻN {á$p¨µg÷l;öRô"2ăi‡ě®kJŰ•b‡eĂ²„ôH¨ľîŘá€cŤ$ćÁ ¬‡«ě 2P Ů6s˲Z!óśZýYé6óĺ ŤÎ·Ĺ{lB,ĎĆîWŁËĐÇ‹ç~łˇ\SÁŻţ)‹CC<±¶€íđ{GiÚIA;ú… {÷#­±ŕš”a«µű|„ů‚¬a,ȡÄş[†ş­¬“}dňřÁÄ5•99îOůSă}đţd pÔ×S:DĆýŇę/bź¤8˙g \wnŃÁ®‚Î"I˘2˙#7\l#íyŻyZ¬Łn…H|`x»ú±Q`–ËŤüďM]żWšÍp:śnöç…îöi>ć7«÷áŢ1ń9ĺÇÄŻůˇ‡5ÉX‰_DľÉVÄ> fdAŇŕ¦w;p€jÖF÷ÝT«‘ĂÔřĂŠŻlÚ¬0šŹtj‘ĚgČÍK—Ľ÷n"µť%qP˝p™žËńüć“ߣuDZ&٦°yu©|˛RÄ˙VĄůó8ą6i‡ř¨ěĘb<š%ńY€`Řb¶=3ŰřŻĐ)F‚‚đ$‚lč—í*ŁJe)*6”YŹ=[řy\˲öĎýłđ᪙mÖDP™Ćuy¶Ma€U ÚlłwŻ{’’ć¤Mâ´†_C ‚žMÖÎfiěŚT“}*ŐĎ (ÚŁ®9WĎ0C°‚ĺ•őq+“ŤŁ<ěřďú`m˛2]ôę;^˘É1l‹r§1}Ąž}Ď–gë·™âZŰRüĹâncú¤Ďş\śXçyÔ6÷Ä&÷9cĐ¤× :•ŤF­ ŻČ8*3ëěKHĄň‚ńcĐŰ{OŔh…™z^üâI‰7†»dŐ€•óC~çôńď=5 #ÖȢʱ!ň5ŮjĆë´^LŤ«X'–Yc÷±·ĹáŽĐçŤţ“ášě+ źś'W ţ/j0;QßHZµ2öť’úşÄt±A›xMŹGăžE <«×°e ®D…Hśžö™1ăřm?đ˝o·°Űíëó ęÔ›ę¶ď«¨ďŰÓ&/·f˙CBď·®ÉőufňBBúa¬ćň¨Iy3ÖUöďµH˙ B1s6‚_´Ň=ŰDűτ骞‚ţ~ Ť¤X „Ę'ظÖ.„ąVGšiÁ`Uł;•łA ÂDŕ‡öź_®›ÂmL~(ňóę[βŘMřBÁ› Äš*QßQKú'ź´$űnçĆť”Ó±¸v¬üe«ŃęPŰľëٶeµKUŻd8vÝܱ©?ćű—i®}VçäąÄť ođ§G÷ů?ĎŞ9űY{čő}ňnź+˘0oeÎÎ@°‹Ś`Ç+>…~{ě9¨ ”şĽž"ŢoLÝ„ÎrŰX“ˇú¨ňaáŽörćţ´žU>¸#˘Â$}SżbŃ0ÖTŐ!`´©ęálű°MúŃM1©ţ ü€ă«ł9¦Áżé¸šý:’sňI”Ň|#°"€űöe—çâôd€ąŚÉ\[×ÉĎ ˘]*TĹMúĂĽ«.Óű9B*;„čľÜ$ť é =o8Ă~ţ¦ľSĐŐ ­°ĘyéwÄę˛Äţ/Î,ůŔ=Á˘{zŰż˝0şŰłž§z WőÔőěˢSţ»(.‘vßaŚÎ3G˙ ÷Ż˘?B±Z1eŢE™¬ĐŔ_MÄeîü ¤Ç5–‹[ŘY}“ ŻÂŘcďă"tď7FÓH%t§ _¦2w/ßÂÍéý˙»ĎůíUľbŤU]˛IHbݶ~řUôďôXŞ´ŐöqdáŕţŠĺꬲۀäRjďťÔ±ňݬ,¬Łg&č‰a`¸g‡Ë<Ó§,JQ™h„•×Ű>e¬ßÂB™Ú5f”a·őŔńW\Á–QeýřX­<ٲҙxóÁ˙NX!Ł™´dôbFtÄĐD»Ţǰ%T®ťSÚ˘p<Ăd>›ÄĆš˙~¬qĆ łŰůä± Cëy/ácdôO"Ä&|żoßĚţ‰zľK†Ä—Šą(d˝íŠ›ś¶üU„ĐAőý«Ś‰ľ48íŚÁ =耍z‹sHÝNáULő-¬âĄfˇ‹7M`r%,ŚťIëDňqstÁ€1­Śâ)ÂÜfőcÎĽĘ–ćm\üžţßŢ ˙iśěWťç,/ńl­°żs†/š}zgýĺ2”Sň#S›—‚úßDC*ŚŐÝŢ͢Г­ÇN‹…× ÂŔüUřç™ĚĂŽçʤ…śöcctŮoŇč[Ú›±!Ŕ•ôMô“ ±€‡ůĘ©dý/®ď*z?L(‘Ĺkxi_š¤§hĐ~\h ßY07Ä0aęI)Čő&ĎnË>MťrÖmG ę-núsYüL{Ä鼡ŔĘî‹dÁęßďofV>sŔ"–¸cł9X•¦˙ľÇaĆ,b®yfW–4ą!4éľôîs¸ÜZ |íŘ|ÝĎn•Z{~˝č´˛{r¦TűĚč–}Şkřř/ô]ŽŐHrÁ¤˝ŚJ^tÔBšź‚čśĹŔ86–Ŕ¨¤"úiQ¦Â3 >XăîQO±ĺ0aX˙Q"G‰[Äß[YŞŕ©’7K·f53—Ú¸ns@€c’n]V3Cµś¨áD\ąÂás˛ř•Ó›ęܬ5ÎT~ ˙:÷ß%M&ťł˙,Xf"Q‹ß‘[sžˇ“zÍ‹D/Ëô™'Ža„XpVÜ2j;{ôĺcCG~€C·Ť“‰”ÉhĄŚKőçKÍÓőQ>Ćúî ‡Xí*MËźc\…H;â¤%?‰RAëó;KřMĹęW[wĹaŘ ďÚX3NES•@13a/âý§R÷…ŞpwZV¤˝? Á6|'ÜttloÎ5ÓUŮ E©łíůż€[.™?Ę˙’ď÷h«Ž˝/ꮇa­P™f]ĄňM3¦čÁt:ěöŮ9ľ.˛÷éNŽX>j™±;˝8şHeČŕLtü@zq)±ńé“qŘěˇ˙oxn¤ Ç—íRÁ0 BEUô©çý(=&Ǥ‰ţĄđž¤wŰř‘CîÎńJsaźżö _ň˛=Ś7ĆŞ9žiJ Ą©ŃAÜáNMŻJz…;09ĂqŞ×…őFé]N0ŚŕN›ďIaŰs{C¤†¦đąŰ˛d§™ŠÄSŁkyĹÉná7XQV”n\ůuäüöóN.ŮíI3ۂ־š+(V96ĐÄRqŰ„äo‡`Mf°˙8ß8ŞUĹ>ÓV v†­żŰY†¤RE0ÂĘ"ǰ1·QĎ“ý8'vW—cz ńQá’Š]ĚaZ[ bbK,­dĺj'•Žî[|‰®oËwSą‰żjŘȔà 铣?Ťćם®âśrĺ¶_`Öěb=´ý–脏n‘±ŞÔ·ŹĹřAĹyÄmŔGŔ'üŚ}¬ŤçM7)–ö&¶Äŕĺß3ťóqú‰&Ya ÍňćâLťş"–·÷GçQ$Í4„N‚éÇýF+b­cË3ş™×ĐÖÇČuß •-ÂÜ-팀;RŤ"Á\§E1ŞěÎA$ÜÔPo`­×@‹E>[ü/yÜSôÔ˛J‰¨tۂƮüÝ0ŻiC±ŮI@Ó¬xQN3ól<ŕŤ;ÁĎŚŇR’sJâs.TiášŰ”Ć= ˛ęŔ»xÖ˛”{á ˇ}‚ă"ťŹgÔHzĂŠz‹ńŻGš7árŔ“®ÖŘeËŽvś­<–ëřˇś\ČÝ\ů6Šřár „Ş0ćĎ>-l„˙§eućěđ~·$u+›`rš7®˝bL»:‡ýhźOŮ_"±6›_¶çĺI¦Aě“9hłŘ%¦Ăױɲâ`„łmŹç줍fV›"¤h72MKXŻUˇj¨äć'iW-~?ڇA˘ţTa tj.=äFöˇ˛ű>çŘž“*ŔXú«ţF<Đĺ&nĄ·ăć«ÇÇłÇő\ĽÂ]÷íůZvśg1NDQ!¸d·nĺńŘĐŠşŇ"ľ‡AT›ŕYEŤóýŻůÁ¤ľ»´4”Ůćđ±»ÝëůB[Ť†o„H8e€OËş†·1ń‘ł3Ł“Z9°+şőĂśgT‡˛–J¨ýzŰ#^Řo‰ňq-EFů kB´ÂŕŘ%ĄcżžÝ*ťM•~Y˙%ž†"\Ó‡’ĹSĂEÄżč´ô–µ*Á|qßŮÍöŃúř<ü€\żm”z«ęRśŇ|ť\r.UEšS[¤*V ĺ”ͤą™đ ¬¬śľŃ7őo¬iÎÁćźĎ۶•¨‰¬ÄVI’¦´ L¤ídgÎ0ďfŇiGł6:ű tî? ÖĎl*’I5›Ňéě<+ÜJúI˝tÜ^qi;˘6IĂ»B™Ł˙&QťG# E%©^÷ ‚(PşPŢS»AŚ 0f(°†Mł„Ô.Ý09^—wĽ’É­ !yŔŁ»‰ĐBÍtxŃÎ ~O÷pŘ`Á}Xf"ŽĄUăľĂărěU^ó°˝–ÇŢď@ řÝ}Ĺ„yďodŃŇŠNLjůŠ1[ łŰ Bö† ‚óŐV*píŔ—¸E«ÚűöÂ7ľŤAcřhŇ]ňíz‰ŔáiŃ–Xý–j˝,ÝCzZT€< 6§<{=Jcim ńéiŹ oŠ.4ąauŞa´•Ó'¬úÜOÂ\”Íç+’‡ižg$§BÄQ˛¤ ©=Č%Đűź‰r´ĺhdÚě3㼏áÔŞŢ3áă?,‘¤hŚ’=ß˝qŤR&µGW¨ )?ŻÔYńůŠÇÁŠź đ˛T(ČCůuŢ âŘ3ŹŁľĐ¤ÇŃĽEëGżŔgW)t?ĘĆW…ó`H•Ny%. ĽÂ’ôĆ·ü­^8I‚KŚÓ8Íů<ý}] ‘ú~Eş›u – ŹQîu}ÄÜÜŻ¦ZÄ~R|‡ÁŢŮ(čî/7+mŇ|ţĽIzŕ!?ŹŮđžó°AűŇdßD5˝Č‰ä€BŹ Ň ‰ŮéfěU˛§»¤Ĺ/Ŕç⵫®ÜÚF3€†2µ,= Y“{†Šä_XŘÄÍý@´átŢ2jX­É Ű:w_ÚůÚJÉÇJÉ€6‘>Ő«H8OFx•fď•€°)Ő*Ď($ZiŁ X]0üÄxÄĂÖ‘ÎE"İĂx¸R^ÚHŻ:hťVáÜtIćútąÁ´ą6ç2Lťďî2±u›)臢¸!J#Áś„B-ž¤‰y˝<Đ•¸źßÉĘÜÎN«€U\ˇ+y‹@OĺG2ĽĆÉa°ÚpdµÚćń8ú®nŻuĄŘŹ)řçŰ™@¦řo: íëó2ÜXvš1¤Ą™¦á3Km3ë|1÷?ąËq/çf?~ÁĹ.l>˙&ţűÝŹČß%¸{®‡đ—ukW™T°qüUâÖVA«&zŤćĘSö1mĐÖŢ…¦iš¬ŇČţ±Őb]9¸HślçĽ?*ć§+8zŞ+,O:V“ýý÷6¬Ëh "8Ř F…Ă3â2ę(5H1©„\UŻť0>]‰ i9ŢĽřQ˝am+äZľëŘ´ľ  h›ż-D¤¬ÂNŇ’¨CKMk]»Ę?Á8ç mÍĂJ* řä»ę´Żô“*Qďő$O!ß?üŮŕCk ä}'­‰&z§ÓşrąÖBĐ]$śô&ŕ^(z~Vß”3ŰÔČ{ëG~[`ť36¸HĚłZUˇ/Ą÷¤}ţ^X†…ŇôC…ŘůȢ'"§s3„W˝Üd l` ,ŃŘ2Ť<ŹŮý„˛wrBi›§Ľz' ´çű´‰ÉÍŞúb`ŹŞ$NYçľ4 z×îĹ8ęjńŹU§Éičç6>—$0ÁČ˙l˙T ^ÍŽYŕ!ĘO)Äk^9żúŽâˇś,2.S3ě:ZNl¦ŢD)Ůc)ËaaťGY‘]ÎŔgrü”ĐPy$©ÔÎͦwY lř1TűY®ö}Ýó˝“Ť•YÚm”  ŤdßކÁóŤ*ÜÚýŤ vÉÔNýˇŠ‚Ć´źś°,/.\“`/`Ŕ§ňJ«óaІÉßľDA~ض «S›qŁ’ &w~—ŻÓť¸Ö\đ)}îr[¬ôőńëÜŕ?"¦§€®nęČ<”Vc j77„Zµ/Ąć"~.®Š…}bhףť*ig˙5¤ú2RS—'[M€¶ëeMŢ2QóĺGSďTGÁtžPîcŘeCŐźäŞ)A ä“…0ĚjÍÚ"ß ÷ ±ß˝‘Dţĺea«•ńń@v!·5ŤZ)ÁHoč[(&ÎŻô Ě;–Bv{+;;ęŇDPŚR9\©™suÝ*ő'ĺµâ„¨-‰\!ćĘt-ysĆ–QAŮdĽA<8}qÓÔMÂ1›\î—c¶Âs 3©ŚâÄ2Ĺ Čś×MűWÓNŔĺµÔ‘/Ľ±—?É!ŠűبąuK°”řQ·.ýôX;QPT÷Á"mĘ>ŽŰĚGĐĆř©ciűűO€·čęôuWüIÜăh‰Šl˝ďËď¦9áUť ŇçĆ<{ĆDYľˇ†¶•g’ĐJßÝĆŘUčŹ:QUv-ŢJ”÷R1â˙ÚĺH¬źĽÍ‚ŮlU~ZI†ý;ÂÄčüpäeÇüÓŔ˛Ź™č‘3Ń|O±ŔF&i9Qh‚S B§3źHŃş,iĎY¸qq©â1ů€}:Ţ'd…j¨u"…•n˛•±ĂűÔLK*7÷ă [›B¶âˇK­qnĂúđÇ Ć9řŤË¦Ą¦Űg6@ţáBZĹR ^ÁçY3V^&43ĄHŕB܇_^Xž×NÄ;µ9>ÝĹ=žÍDą˙<šĄ\B“´r.,94âwÖöć%!čŘč3Gv”ş#ĄąŮ§˘ eÇÁ»żÔ4áóDáŁĎC ‘P*ă±ĐtdČę·~Şžwu.Ë:_Ň­ţwŠ@íĺń±w  mńŇ»1h2ßHŕdËčŹ%líĐI$@˙erg¬e‚=… ćrRŻ×ëž)™coMÂ-{¤ÜźBŤ$ÔÓ·7Š3%ś·“ááńGi\áÚV-ę-¶Ô •}€u¶{g4{9ÝÖç;ć# ĎŚ!mH~§äšt‹/‰ó G“Žm]•€oĐá!í(%GÝІ 3@ŁNŔţ­e°áM,›Ň=׼˙-B›¶řĹ„‘Uďśżá Ű,Ň*ˇJ¦Ľß%Ď\baĆ0 dCä,ů*\ifSëÁO{…2ѲKŐLÍ…¬ ś«GÔŘüż2@¬ýîđ€EčB–ڇFť9ź3µÎîťBécá|­¶tWa¸°gĽCCżÝYŁdV†R7żĘś×ë™i?FgŹ[µ“˝ËŢmóĂŐ0ěw2[SúÜ—yÉyÝĎ5’ŤŔ°?io<ÝŹ‰ě4¦i`s0¨z ďűD¬8nOc-yM€VŇs=™ú,™seţWëý kę…IGW,3cMb €- đȥ檯óĘIuEŁb˝®Ł`ÂNĂ@P·đQǦ»/±ÄČÔ˝/ٰ¨el ‡Ł”3uńX+'ÂŮĄ©ÓľěEY߯Ö9Ţzř´ćĐ,p˙rmô*B#ÁXaFĘ”ł(©‚Đń\ Ś(ď]ßrÎcťo,°7ş$98Ŕľv·Í?ËíYđˇ°Xh­ÓMZ„:äGDŮ÷ŢZLŹ>±Ę&ČČÖÄë’Eu?ĘľÔA‹»l_ㄜçžĘëËłď>mý[";ă‘ ďWý·éř±[š&ýE‹˙z  k¸)6-ŚŽ©’i÷™p2Í“űÖďµčcî᥂w ›vYNâŠr`éé G4ÂĐŁ°~va mnRőÝ|qŃf@ť‰Đ~ËĽˇnü,šVLJ©ucÓOĐ5*@ýÓ0®ń â†ůd™˝_0®´)(#v]îtĎTę[°m@‹ę ĚŮL¬Ćó+ábÉŹŹ/Đ#‡!Ě9" ’ĚƱłXş!zÍîáä2ó"ŃbśdS¶Š Ű»‘©!°oTpv¤WzhţĹF\ćËШşíC·ONÄAf"UźŢ?‰äŞ”pŃKE»Ę…şrpř=ä­Č*#¸‚xi©` KŚçrGŽS!˛Phdśű ĽÎWžş‚2€č…¶$Ťó–[}őşzÎř–˙4‡”ţúڶ)¦ÓăaMź‹‚ßTejHÎĄŚřʵCe1äčz.‚<ŕá“ÔfęÉ ą-¦™#ÂiD5É.Ž­Q$äŃŹFŔ¦,^wżňÔ^ĎG˛î*ˇ#ŤĘ• Ţ…)úĂŽĘLg€µKęś-]Ü«\s0äšdIIüôo̦|äMŐHg§GŹ/SÝ˝ }iZZŘ ‘6d9ńÂ/x‘ťëzžXýPŞÜâ9 &y›¤0ă }“ű™fZ°YˇěgßÄ˙nwîćA2ç2]łŔŮ΀ą4ۧŤľN ^g}÷D·y"8ŃAŠ1zĺ—Ü @ĘW´¦nSUr¨’_‘Bünňܡx¦öŤŃ~OXłb"D¬Xż‘6?¨±ś5IĹ0Ľ… Îfú2‚+đٗą3ľi«S˛˙)­đa˝IťŹĄˇ`;šFŃfŠĹjIÁςހ‹E¶Ś“9Š?°ă ÖŢĄž˘ >qQÎÎăÔ–GúŤTF»Őş§ZârěĹĎk‚€żŕđŻ!Č;ĹËoŔߍ±EMREÄsŤKŹ˝^!tĂg?čƶ ]üĂdŚZ.V˘*üyŰ `Ä­Ážźkĺ«Ű[Í2aô˝BřE¨€e±qMSŕ•YštŤŐCfŃ +áËŔŚśţĽĄ~ŇľčoNw>ľŤQf{­ ĆĺtžZł5 Ł ¤Ćb¨&1Â6ďfý&™ ĂűÚňKŮ%ś›,n™”gî3.ŇĄ•djíBË•Kóm}~ó1ö¸Ł§ś“˙7y…%f€÷UŞ‹DQ)Y?"éµČďĚŃ”ÜZ˘điNńť^§˘n?4ŻÔč4 çiď_'1n@˛ĚÓ6ä|8ŕ="~°’T`ăĽEÚó™(‹úLs)E˛pÔ|ÔdW&4»éű$‹h&÷„ĘY \“ŕÎGĹě„Ů4Âô2Đ8#ŢČžUę#ä)­äl'ĹÓrmÎŃ›)CygŔžP­‡ ¸˘dnŻ ç'¨ë”>ůÓ6łŃţULýŢVĽŞůńă>,MkçqPʇ÷0zĆxf‘Xřóý2»)üVE¶ž>Z/Żčm°ö‘ĄÔĎw"YŮŁJAadŮ=~ůźÉ\'.ň™™^6—‰AĺTĹ~}ýJąîD÷WÜŞ,v¤f=ÍŇ„[©Ô35“˝m_lW‚Ú=ň®rZťĹÜ\%FčMbsúhčîŤ3`Ş¬ÇĽ„ě éąĚĄĘ{^ţbó Ë«C¸Ú÷ŤŁ źń9×9uĹ;{ ŹMŕ‘ŽĎŇź‡Ń/ŰM»oţF{· {ňp¤˛ňB°‰r‚\˘˝ć4çŇLÎM¦{1 ö=^UKÂŮ5pA—ch†lťŻŢÄáŢLë4ńłÝm!î®]ťOÇ’0ŻR.ü~ZŁP>°ś±ÄĎy6l–X= ^§‰LŞËâ$TÝ(›…&ĐHĚwO´ŰĚ^Á4<ÔMDôŃ ¶“ć§Étq‡.»zV–J¶™Nři‹ĹG#"µ4Ţ|ó;®ć¦]Q*Ę‚t”|g,¤†§śÇäÎBă1ô ÁŰĸ&×<[–e ße&¤O`ĎźTĚuĺßé´_-$‡ŻM™—n `†şČ6×u»€ꊭnÜŞíď 2!Dođ‹Ůű)ú §‘0bµáůI”Pz™ă.ѧÖ#k/řßłÁ'­!3ŞV1?ÍwX2Ę·ţ+źmRĐwpdËLDÓH>mĚz¬gŚ–ëX0™ćI‰í8Z,ëş®.RŻ“<˛ŃjKĎęfLţ`~Ď9 ľťŃŤO$P L¨Uő‰N€ćčDrĐHÜrt‘Aßďâ‹—÷Íů±2S1~‰Bź.ˇO("÷ňŽHXoͦŐőcń>«ĐĎ3;­$kˇę4ň’ßîëÍ”Óy[QG°á5!:íŁzŐ7wő÷Ýdž n A.%Ţő÷qßqúÇÍŞ–@Ő'ô4‚“wţAęß[6":‚ĺĐST¶ ŤăĺC Ň|„Á[}Aę!ł Ä—Ď”=býIvˇDďĚ6]ŻQtó+Ł­[xżµaĄ˘ÜGF»:AßěÔüá`?őźţJmÔ9ŘÉŻ;©,ßzËŻ!gń/QĂŚ˛˘z~ÂVl©˘ę.š–Żă,,Qm8ĎÂvţsrKňˇżú€řŹĘâs=űŕáó”óţf–ĂĹ·c¬ü=uĺam÷ü%—' = y Htú4sŘTËóđ/róĺH\ܵW_ęĽQµÉ ŕ/Ľs z97ÄŞşKížśG"­ż¬VĎdɦ”Ýł‚AŤ°Ľ4„2€Č6ŕ™ęłąµ4âŇúčíůá\Lťy•´cµeOD˘h“Y\hĎţ xĐrˇY+™.ůĚÜďŠäUľ'úÚÍ~F‘8µ1CŇĽwĚj Ţ©ŘÓŐuő`ľŐ]6 ź×V‹uŰ‚¨ÓĄ:+wém‘ŞÄ(8Řz đËĐ!´m!b˙?řęćýźcęŻćśSKVčµ®Z–Ä–I_Ó䩢ś}ëV© Á7ĺô ·+ŘĎÓ›…? /q‹ÎqO!A_S"í}HW’x»äXÓýüá©ůŢ?ôŞ-U[—Č0mS«¦Z¬ŕÖůŻOtĄNR6 ·ľŘ°´Ć|_oť–fČŮ(4ű·űăš"jPU8Iă,^Lđd Ďp€ŕ9!˝#ná±5 mň¶ =¶/ę›ř"Ô,§^…[@t*€âCëŞ>IÝĄîÖóĘÍ[ zC.üĺA9¸Ź8UĄ!B5!…îzáŚA´2"©-=ók3ČňđLJý 9Ă®ˇG·'łDT°ó_Uv÷(+)Z‹ąYb8f‘t‚ŁQVK;ł»&šđ÷÷)™Óy)źć‘ŐV6<ťÉ)uĐç’ ě˛í$4–ĎJŢRďr•o‰e°ĐSS˙óß•qřś»Bö4‘é­ççVŮWLF÷d–˘őS«áîťĐ/˛X{ TË8ËďĹ"XÚâ‚}¶#€!´ K»—ĺ˝ŕő¤6,•_ššNżÖ“I0á»ĺkxŁżUÄšZňŠšFŕp‚gĎą¬4Ż<ÄłäWr&VE€P,?ćĘĐą™ŰŮ®;oĆ´;ÝgÔ p,l›mt†rS„:śÔ‰{–ěHH?çÁ éŕđÝ2ôý[(¤‰ajć›—ü±źcYśą[UĂN#µî!Ň,¸Ç ¤{Ă+”Hč§ĄëBîPQgšyĂ>Öâ~Ń|‹…^Í顣D?Wę"üó_Ď=Q®ßŔ·M·˘wŧěň]7Ž‘&őO€ČHŇSő;̢>‰_@ĎŔ°>´qš%rű'% ‡ENł;ńy1^ᾼϩX–ĚY§KsC5Ř™·ÍC…I*zÖýđ¸ţ$yHW"ávâűࢇdĄżâŹĺ29))Ťk¶0Ěí%cţ2µ)ďsŮB t·‹ˇĆí@k¶Š ĄŹsd/Uě%?tçVř·ŹÚjRaĺDşÁHĘ~f 2THmô5ś3n§V§+Z2M“†e’đWÚ ěž& «)qh©†3GŻÇŕ8(W6Ŕ„TŠzâŁÔ—‰u©÷#!©äÄćČN·üL ·Ć(ˇi€XúaYd¸ąÇňřŞ<ß/9ţÍ9†qŃş†”1_ě[ą ~ܤEć… úfŮĦń€Š•E«Őұ‡HUŕŮ—»OˇY®Ć~ť´úÁǧ[ćTR2†N5óH§źŘS!Ô!ą=}9€‹ ß;ÇÇ—ëWwÄ…hmwíË${ş4ę|8Ď›'É~8NďZz‰ŃaćˇÓ™L´$3–DZ µ5¤~ť"9čědm›€ŢwßÇ6\´’N«$´„®áĚŔ±ńź"ń¶‡źóA“ůjSěî#nÜš1}đU⇱˛˙Y®&łőiF(C8UXŽ›îŢgbKÓNÔĽ0ÖŹXݶšÇČł[űjňUx€ŢÓÓXŹ-J ů÷”ŮĘ` yě!U"i9Ž`—ďú€:öHX=ÜÓą9Z Ś$“7dn„zŕH:‘dßÍRgĚGśńŹôIŕŻDFq†…HQţä^émÂn"§ć1R Ö¬şUÇÍۉÂ~UüéŁ>ÉR€É·µÉóZšü|OĘJTHíg¬ŔeNË_Ŕ¶ľR’ët¶ľpZôIXM ©_*˝ýE™~rú%2ŕ“źŰá¸Ĺ9*śÂ&_ĹĐŞSńĎ,ÔdYSěĘ y"Ţď4S…,â6¶sa&AáرxI˛0jőJĂÝĆŻWŰÖ‚qă*Ă`*ˇhu/ż÷r ?Nřą˘wb ŘĎ ‹ âŻ$läňš§„WŞ©.m‰„ź‡ń: 2kľ[4°m…`‚Nź‡3u—ČŠ.䝀“¶ÖqC*eÔ-ľG0Ů+ aöíc~Gđłh“„l-.F‘ţ¸>ČLr—˘ĎÝ |ńŘż…uţĂ`z˙`싯.˙‚ ±Ťű¨ÎK¸†”,;ÜÓÜzŰę eeI•5‚˙}ŽI‡[S'űö ó>ÚT#GŃ,+[řRŤí Ť:qófŇ‘ßĹű3Ňi¬ ´ă=í˝Š“Ž—ŹPř@ ü+!@N=řŢjž9X):ćyNˇ!u{—߆l$7a1pMNçsŇ·ŕpyj©âD‚fő6|ąĽo«­#=Ď©c’ĆÖx4eëuJڎË™#˙Fř ŠŞć>pďyĹnłŽ…O)Dˇ´˝˙ÂTE&ŠłáŐ7j0Çx»ěŤ¨Řýľ<„&–˙ĽĽ´Â6t-ęiE Ľ­žZô…Yo®Z$ü Ňđo|Vo¨uâŚőZ)¶Z-ůéňVý!•Xýő/î=Y›ô:]ŮQĄ_?80ÉU}_-¬R¨@_˛©ßIס erndZćîVRP™ڦ–2´‚+›˙Z‘CďĘł3*ú”Y qW¨ t~×,R[W(ď÷ __|ł˙AŰ´ń1­‡3¨ ĽRF%Í‹?󆫍˝°×ľý ń.®Řń?W~ g˘  xUÓ’=AßÚąb7-7Uw?˛Đľ äu˝dżĐÔxIe/DX» žpsĺ[0‰Żä§6ďJk˛K©{»rÚd–ş×J—R‚ű"ŰwApö†!ďľ=:\<~Ô $ăŞ]‘ě˛]ęe± ťť¤:şż~áUx ĹéÎű$Bý¬4sÎŇjă(yJ\d¦nĐ}Á[8Tžjž¤¦¬u#˙Óç¦pŹl4OĄtä€ÎŻŰ(“ ÷(íXJšt9öć:Ë8zTŔ˘ľů`ů÷Ŕ\.;hWឦ9믶ԟŰpć~ěl5Ś|+ĎÓv¶Ěő-ëů’ŐĺŮ·,ôXÖ(=ś”ßU†Hź†sôzf&¬#ń¦pU—W75ň)PĚFO˘fŚÝZP»bT $`<-|¤ášŮ!éîţľ/ĂŠ[D—ËÎ UunGí'±É¬ż=›W_ą€wŻl%Z5AýÔc8Sň[ů “Ş8—xܻˋ™ ůC܆=ŁhAóĆ[KJDŃRź’Ň‚ßX°UŻ»b»młŔ/9$>ĚOíu^@;­$xÇâÜ3š˛4”Âě˝"&‹‡€wµłßUćé†÷¦ŕ~ćf4QlWßÔ_ŃMč›iąQ‰”~f;·gL9i•y'gÍäÔ p_8JQ]‰GˇóTCŇL1´$Ox/.®Â8ݧ˝{rnŔüÉnŰň˙gkvĎ %e˛@ř— ŢmíĎGx™±ât»ZnyřW°ЏĹOGá•Ěö^ ˇδó6‘˙TĚŰ-ČXłz?\đ5v ţm>xNňMŽvP\ŃB¦¦Ú|á}Áű\60 *ť™……¦jŔY8#ł€®Ů?KąťiozAĘEAO¸đâ?(ďóÚPą‰CĚF˙8‚~/ÜdXĎ«d‘§ w‹†ë[O6ŘáyÖđK­ň’Ńe*ěĺ÷Ë ŽŕÁĄlF)sďvg ĘĘ`·‹Ô‹wÔ‘1‰ <‘ď TjbZm–Ö/˸YŤËź˝b¶ńb“¤ëPÓĐLđEM&fץëžĹb3–ťŮŮ»jŐ*Č‚*§1ęîď"ß׋^][[Oe]×›lŢĆš{b„+hNO¬¤WË­ÔJß2)…Éť¸L÷Ί Ţ\QRĎ\ô‘€Ă«ť¦{â­Âýć$äÚúľô â ÍĽ΀z“[I˛ŐŹ÷¦ 'í_bżçą®WŮô'ďčDxýćT«Ărś d¶´z*ÄŰŰ®`ŕçuŮîŁDťť;ëčÇgíČyÄEÇfŮ˝Dé€LIJOňĐ7c+“έu;’“B{kY’d„)tb*Ö×\;ÓŘtkZQ%hŚ_ę‘aŐˇ9eŽęhŹÄbµt©čő PëĂ^ŮřŔÁhđ<•Ż/öݞŽúę4Ŕ9H.÷Ť“ë–QÁ»UMuź»9[3ˇNzł#l‰ŹC7逥ŐÚtý’.ťšT¬ÉGź&©.×ĎľĄ,! ý˛OxA ţ>e· ·liD}­+˘SßŐtRŃbťË@x¸ČËŹ{<ÓŇżËDez‡°vqňcj)!gË;^Ž.¬Úů ť[:i˛Z+”QÄXĺˇĐJu<`)ĉ`áý‘o›Ir['߇w–ŞÓá•'ú1ťĂę÷ĺzŔFÍ´y9kłQ«„{ÇksßżCĆ2Ô•şîŰ^ŢŐÄľl¨sĆsęE;ó} BDçúł×µS3Ľ¬đ‹–¤î‘Y&sHÎŤL˛ä*N÷ <×ާ6Ż#ČK$8+M{V=oĄE®¨_äđHYlĐ7°Űc·ücµu좒Ç#F3W)UÖú¸óĽ™ůCžvűü®P˛ÍĘ„g‰ůć}€ü€ŁŽeMë–Ą<‹Úű¨DŠŚz9čF3ń·˝›îsO>Ë$Ř´şůłźJA‡?ü;˝Z·ç č[0)÷[~ĹŇÍ“§* „·WUľ>űS ‰vr‚pçvÜ›ô őŢ nP͢´đü ëO\ögł5^ › =ż6÷ˇÁü%ţC Čˇ&ŘlCć#ş™›ßĐŘÚxIóc,%[źf˘Ü!sެ}¤ŮÔ(_z0CQÔŔCĐxŠi»ťńł±ŞŹ†F¸dđŤź^Ş—ő±4Ď8Č–%*c.°•dđđI‰’ăn6ćţźÇ =ŢŹ5—čHča[žş»ĺU<n±´š 7úßĂĆ⓸WíŽŘ6%ĹÂK>‘Ż×îGH-kóČĎee.$,dAη§vAE˝łŁ+uI¬§«Ąo¨ŇÚÚ>Ëť¦ĘĂ=Ç_sąBStĺľăOÁgÚĐšÔłP}€:k±š”—ýŤŻhcˇ )¬k'žZńÉ ăaĆŮé_ě‹‚ăăô¨ęéw‘ ű Ţ# źy§Ł´Ě2+ę‹(.+(ɢ °Ă77‹U§w$ŹŢs—ODµ°A“ü;Ňj•cÝńčjéŚzę=‰ě{ůŻÄůe4 Řó8:ŇËŁ^yjářGŇůž‘°łÔp„‹ Â\’kRÎ÷•HŮżŹ/â׵۸Qűüd3>9C¸‹GąŇÝ‘}»‘Őź(-«ţ­ĽaŠěóŔ~ăIʱ°çuwĎě6Rc:Âh".É&Ypż´˝żK6©»X0rp­¨:ážřů/ŕýđwQńö‚FŔ˛čՀܼu·š”UZ»w8Šsú ÉËÖWyË@9ţ~Kď?e°YĎ^.¨xç“‚MĆ*…ýc,yĂ·şÁJúäŢMëĆÉŘŃ#pôxżÝÜ;ÍŮÖ ő›úT§ËáŔaÔŐÇĽ7)ôJyŐlPA—OG~öŕwÔÂDý"řç{y«8ŕß´Ôĺoµ©Ü8§D“ĽŔ€ šČVră˝×ÖJÎs¤/k˛ŽŁ8ćbĂŚäQŰC0ĆFJä´¨F]z˝ÔŠîŻ>ŰÁ¸ěáŠĆ+U;*Ë«ěönŘZ\¶=‘YBt$)pś%ŇU•?ü ˘¸Óh7r°íĺ„ţ/€Ó"S3i“’ýkě'ęswAŻ#{={IrĄČXXčTÚßĂĄç ŰÖâśÝ˛čnţo˛ŰÝĆ?o'ÇV‘– °L'µř$>ˇ#gńáŰYR%ëMřü-”5¶ë6 ŰŠKđˇ ßłâ|BÉ~ÇŠ¸&Ľe7ňĐÂë@~GĹÎE9" Ѩd(zôż ˘Ŕ3nmt»Q»1$0Ť›—8ľ–őărďMż•‹0)Qj(˛@:Š{–á Ě/Ħ‚ŞSÍjô¸I |‰€°ŇY^CîxŰ;M„»´qÇB„OŰ,'™ă^ôýď7H5ŔŚS ˇ±M=ş©J-×Á±ł|,ŁK*~ŘŢŁ2µ t›˘Ç†Ç$WIy7ş®`Ć0ň™ @zŕô¤'Köö_ˇÇ÷¶CşMŰä-~É{¬}üXČĎgđ7ó'žgĽpÁJĐąXČTŘÔĐSÜ/ő'«fş}ý­D<á`uÓ,š¶#UkíŻź¬IÉ Ě¨ŕî=päĺUę<±íËÁ™ůúrł¨6lÁ{-Š9jşůżł‡Ű•"c¶ *{÷'{J¤đZ1í« đŰR‘íuý|Sę2ČśśźúźiúŚ•ŮŻś8Ŕ_ů«g•jľ3ńř:ćUî±ÜĆTËÝPYVÚŕY’bUÓqťŔÓëŮpkeŹÄçŃu˛ś˘DtCăcŘŽŘ& —t­ĄçŮŮU€ÜYN’)ŰëJ¤ŐŤŽĂŚ<ŠElš¤ký“´Ś×ł éŁZ^nj„!ÝŽŠ`×ÝÔ†ö:|Ţ4ţ÷ëĺňTZMĆŘ ŔäJMlwXÄKp›zń##Ĺ–»J¦ňú¸łP¬îŰż’[Işc·ŰÝŕř[l˛×“ŰŠ‡ůµ¦¤U˝#@zPĺó· %ʬžnř{TŕF&ÚIš3µŞŁW˙rPlí©–ôz­;˙dš¦1¶ŞR˙_.q·JáűPżműR?j–´ďöÓ˙ZoűNq> ďáJO¨B}B o¤Đ˙%úTwu4Ó†ş9@Ŕa!Ć+î:×Ŕ0;-`ü+nľo›ćůľo›ćůľo›¶CMĘXE˙şUËĽ~T (ńő•ŰAS|©f˙|ä.H¦:,i†[Đ?ExYg"z*Ň”“8AŐ‚Węí„×ô@\:čÉPµëč–·ú>łĎP‰$÷Ú˛ISĤ˛Ěj•a)¦ ŁYâ44mí®ŞĐOĹm×ÍójřL´y:X ŕ\1XdĚĚŮGNŁţ?ăţ>%^3×&ƸÉHjÄ$ÜŠ+ć0ý-ÍŽ¶€$IGś»©fńĐ‹˙“ż-ář:4»t·Kü7ťţ¤Ě ńĺĺăën]ł·üŻ[P«U}µÝMv0‚ťßě\g‚OđŹĐbÖëćůľo›ćůľo›´gI'V}_Lýç,ü“l`荗XÂÔĘ/Š™7±Ďůş°ĺ·i}87üHIh„/Ř÷˛łÖµşůľo›ćůľo›çbPX”>Ú‚úő.* « Ą=R€ü+nľo›ćůľo›ćůďEâŚrBżţ·uÜîÄęÍüH8W˛„H_®ŕÖĆB_b†}ĎŹ<ś{ŁŮd¨Ă xB«ââ ?X8iŤGBŤ9őúwşk0ŮążŕE%wňĹ›Ş+0ŞŔ ~G޶ÎjyöÍŔÍŢÖ©0§Ł{KŚ cIżeyBĐ„áşç.nkm}t”äAŽj˙'•"±Ë%ăţ?äÝ_F\ŐŢi…EDÝEŤ`˛•#Őţ~ţń –g/ĄY®ăţ?ăţ°Ş#Ű;D@#á|Caµ *Î×Č­2ĺ?°Ąá‹×ZQ_[áAĹŕÁ±Ę $–1x9oöč©ń»s€-/$M ĽÇ $f}Ďx ’âCÖ/dŔ/üPóŹo˝¤¤Ű€a {™ˇ ¸mâ“–‹oÜGw;"ŘľY¤ĐĄč›–×›6KÚżôdīȂŮÄ  ^Ł˝^Ď1íWÖ ††ĚKSĚip6Ź(ó‚„Ü×@`9¬°XPJKSEéŞŐ8•ß–j0äĽgHľw]ŢŔ©ýćjp?bőŠFLOÎĆKŇ[Ś_Ô-ÎĐDůSóí/Zţ\oHÝ0íOQş˝ľbcĐŚ¬žLbÂ:Űľ&śk+÷9ß ‡o}Ń8Á3Ó ­÷°ŐúÝÓöáx1R!Óž`†ŁxŹ˝ó}¨—ŰO˙a×gđÓkíĄ?‡ˇđíŰřj}Żá^űz‘ţ§đÝ«íôMţ—źĂgą~Ń˙m´żáÖöęíGý˝±_obÓę·yő˙C˙xtĂeü5řz˙˙a¬WÔ űi_řu_ü4;öˇ~ŢŘď·Ş˙p®´ůÝ~Ýâß·mađŃţÝ>ý˝Ŕ~Ţç?n‹6*§€đ–¦ZD©çaxDbÖëćůľo›ćůľo›ćÜE5‡+nľo›ćŐľŘË"1=`‹[Ż›ćúŰs‡îbŔ~Ą)ŇőQ¶ńŮô޶ňZ`A¨zĚ3c†™\ŠAŻşŐJJşÇżŁáe·ĆŇŤĘŐůXˇ…đÎŘń›ş6b'×`Ąô%Ř}ż™xäś6 X>ŰWîô©ńôµ:ؤCçaAŢ–Ař°ź¦ŹĐä…ĄťQ%çłĺąwÓ - t1Ë+ŁÂőŢäO×GŮ}µöÚ~®LŘęâĄOŽĄ‰őq¶ë™ătŽ®‹ő—5¨!â 95=ÍÄžśÁŕ‰|†»`ŤŘś÷Â>-´ŻĚéüűŔ‘¦aOť”™Ťz3•<< Ä3DHµFŻ ÂdpYŻăţ+WÜăV&ř×zÉŻ7Ý­JľÚpî!b*Ž ([ĺ–3†¶-˘´".ßSŤrSő@Ľ¤¤CÁý”:ĘąćÁ Ť,żfë@ ¬ż ©ˇz†J“íę<~ˇĺ&¨\”ÂÍâWÎiťŕ´x=_Ĺ M‚2Yz®xś´ŃhĘ-Żău/ĽWµ| ˝f+|!U-4›†ţ»O±6HÂDö•Ŕőn§¨óIą±ÂIz‡8řQ›†Ęś˘ăť"ł,ĆŞĄO„,Ľ»ţuD°ńŃÚY1ŞA”Đâý'ůČŻ?µ*!ĹĹéęý=Ź2Xe˛ZFGČć:e­nŇý”t[ŤHZE&ú=ˇ“BÍ„4ŞĹËM'¶Â)ŐŇŕLUĚŤűU®ü¸\oŹ# d‚CÔjŞó…]JÓł_HN¤Ö+L–óÍa)ź‚ĂŮŠc5í!ćăŁď&ľçC#ćzv dĎ”ŠźŃH†Xţ#”[}ŠöŹWUśđ­­O«#Z÷ÂA‰0á­/nĽźQô GV1ëÜYZáŐ‡Đ.Náüĺ\oŽ–@ĽŰ˘łČ&¸¬>x‘˛CF5em{6«zÚŕš[X©\1"ňɸŰ·ÝE6ţ?ű' »¶ÍÍžÜőfuA  b/ Ä %‚–ď\RŠTX1|ÎáčÖöRÔ¬Őč‘˙ je÷/QU¸.~ »˝áG…w+©w®ťzWę–ř{Úa¬IĽ–lź/ĺs·ŹGQOĽJâʧŃ1˘ú3ĽĹ‹°ş;xŇfçŇă>´LAËü°Đżđˇ5Â×€<–<^¬oÝÂŮŔĐę/tč|żały‡Ě°•ł“Y7pŠ;Ť«*V–"ע›?ĽŻ ąţ'[ľkG®ŞJÜŐé]‡X4ď‰˙A$W‡j·ç^Üq i5ĺ;Lxż­q47žŮÍE˝Üő…<+ô×euůŔ D0â@X%ZěWFŇu@µ =áSčÉpž ¨u!Z2Dě$ů¸¤#˙‰V,}±JJ0×lÉ#ń,`štńÎŕÇgiůĘĘ&›wˇ˛aĺ…± =÷đÜKKá–¬:XwXUăU·,˘Üo+Ś—f!jĆPν ŽhD ›Ćł ő<ş¸—ú¨Ŕ>*—–džď ‡šlS1Ö‹$Â7EΦM Ńú©…%(žz\P&n°K:i‹5p˙ô¸ĚhźššHíl›®"U‚—!”Ä€2Č` śUh$óŘě”L2בĐNlĂL¸Łó©eQeQeˇ—2\HóúTźŞ‹É®O ”ٲĂW •UĺâŚúöŠŕéLPüűŚ1Eg˙nŹÄú©(öµ‚#Ţ,ň€]›ěôE®¶űüĚĎG6îöÓĚŚnşLq(™lI'éń TÁ-\ŕv¤€5óQŮ^#ę ĆÔgŞ`¶­PŠ s­B ÓÁxÂhąßiěyEĂV 9bÚ°?4µQŕżţ•C~SńfY^PŮ÷„<Í?XŰľüŹvĎý(ÜQ°ůËž›"-‘€F#ĐOŕş®2Xs•ZéTšńDW•@í Đä(ü!ÖzŠ‹í!–Čč_ňf%’¸w·W &˛#[půrV>ş©ÖMÔ¬GͤoFŚü¸Çď(ümOÇČ€­;ÎN§’™Ö:ŔĄ™Š®冧žŮjqsNiŚąŮ¶Ú‘f,+˛})ZÁňRĹĘăý÷±ńł6Gü༶D$]Ľť0]Ů•äÔđŤíďz. ™Şw>bź hB€‚*$ŻűĽˇÚş .+=NJE^$1{”ËJGtÖdžŞwŘďŃăn¬/hW,)°i ,jˇç©/†(µuÖ *GľxřSâC #wBaÚĂß>©ĺ›N†r·2ä—îú–©ĺđC˝¦RHžśKËIóÖ¦°'!éž4W5â•(ţĐůQ"ĆnŚĽ YĎŇL¸¸@đÓ€^¶Tńąu¤ÜĐx$ăú©+NľÓúqî2^–BŰUŔp¤wíq?Rt9V‘Ôó~ă>(FZô÷ĘÂěčř¦¦e'즸žŘé»I˙NĘKë;yS˝ÝLmŔ~řHfý¨(Ů™¨WČ|ôA¶ŕP†&ÎA-„ó{‚r›-[ŻňĆŹĂŚě´Ătä5éDz¤ę'÷(™ÄEpą ËfKIl=IČôźbšO(¨tG~}›‰"ÄęG°¬á©¬Ý6TŚUđDŤŮ;^^kĎ«¬Š]ĐŠkŕĚĺU¶‘$äC%ÉÇ›Ó6}żď,ĵžXA¤Ýus®aöýYÖ±źuń [ť¸vé˝–ľ#$§B8méŐćŮ{5‡^Śçhđř§˝äNbw_ a;‰NŻş©Ę42Źä¤ŻFߥŚţ=:bc BĄú¬ Nć*Ś\úžmž§jęŰ'Ě.̶Áé ţÄň©X‘·śyŘďĚ\—[iĎĽ˙53ú×çpënFYÁÓF¨ňJŰ€\ű­řĎwµ´wcŽE6Ě%e›G]ť¤t]QŮá<ÍS[ŚôزÝ}[şÂťxÇEô$Y¶U˘zSŰ í"˛m"ăo…=łW‚ž4đĹ1<řäP¦“çň1É1x PŢq _B\/%dGdfÍDýÁm5˙qH@· íŻEKŐCěő)řnę€Ć+lęH-ś&ηH¸ŰkuŻŹ>mÔtŠ·G1E‹J|»ˇ´śAú‹h5z 0ő—pĎEq^xý©tOŻn¤Kë0FŇč”!ńܨ€r=ŻÎD©ÁŤ„@Ü@Í!xĂ'„ Źű†­°×ďŁNş+ČĎ‹ĂĹ ,H”’ű±‡{™şYůeâí. ©Ě§9Wýf*•÷0pµÇ+ĄłD|'MŞÄ4ý|› PË e2]w‰]:fŁ3S©nŰ„Yk¶ŃÍ‹ńËn2›ĂNó¨ŠO‡×pŠM©f(żc"öa˙nĎĆk' 眊Ü$ řÇĄFm8Ú$ÔyĚ5žŻoŚ˝Ś ‚śŹ&—jÚQřŐQŤaF«Ř.°IŐ?°ô"(¨;Ľ‚„dďrŕ0XIl˝ëń™, ű/ć)‹x©¦Ťk1P»ˇoąÚçÖć/ă3™ď]%鏒‘“mQDWőŁŚË?%·Ť„ĺJ2ݧŚ_sžj,¸tKŢą¤:ĺMÝúm†cůđřËÁIÂ{–»M¸DśXăP‡jexřwĆ‚’ňIŽżŤôĺ|e€IVőš+íQc8ń€aÔžMî:zÇ čkÖ´ XŹŢ¶lG‡(&!w`Śq±Ď˙(Ł(,d> ‰jÚd?ÍÎbĄOu6ĂH€)y~rŐŚŞ“´ě”lđČ~Śza›,t׋Mâ[óm!ăŞ$ ŰJ#¦ÎFh©"Íe®\TmŇMi°›«ium(öö5şůr˛ćëkćůľo›ćůľmqgłŢ]«Éîş9Ř_¨„,z]=ĹânĂ[ˇCĎ> Kň—”Ľ§kx»+–ئmţßÄC^ţÂz%÷řů€*Đ|ß7Íó|ß7Íö‘ůQ{őŮá ş–s žúć$„Ź˙4X’|,ó…¶ÉÂŐ¬©îď[Ý3wä`Úę×Ë5đĚOŁÔ#@%TźFDőcÝ­L"iŁÎýř7Řó˙i,«»fqčľ5˛µX9»l§ëčôä ňN%„Ţ»Igɨ€Xh +x]›§ď[2$„/ăc‹5p˙ý‰1»wGŕŚÁ¦Ćł—i–™i¬ĹÂZe«E# ˝ĹlL~™J,}wă’e Ůd/穢é?öľĘg‚dłˇžu¶Ř˙ł? Íď¶Ć4Ł˙Sť—1/ď4śŁŁßjM¨™ĐIޤŇuŔ÷!Á}ÓgFn×ѵű˘™\;WßXů—$šÚQ@í#Tfkň”Ž7+!”¸ ü.Ôţ/$ó¨H•nqßNéßAőĘzíĐSŃłż˙Ő÷»Ť~ĆF O§Š_ţĐńÎcDîéţ‚2ŘpmCýę•Pá׸,„Í.Ĺl {ă¤Ů•ükbýëňj}KÍ˝+i•V­šJ /n4Ň­¨Śě<ŘŢŘ,B4=ržrVu¢ e‚uéĽ]ĆĆŤëŕłY„Sýř©Ru¨ÎĆΕ‡jf3cĆzo:Ě;ÂB|˨ÚQýÖ3çßZ٤˙%bÁăô~)ĎCňŕa Ú@R“WŰŚMŔJX§¸!IóŐ˙XĂ)+ń†Ź;ĘY\Đéţ›GďńD­<ÜęíXR媂Úq‡µW®;ňj¨/śś5«â„ĚŹřnł™ÜOĽ—Ȣ Ń(śĆ6F®¬»ŤÖĽ1š+Ź-Âp™B4<>ďÎXˇg˘Î5Ţşi˝–Ig´0Š·=ëô‡zžÄ·ř+hřÖ&˝ů §DrČçŔ8QÖÁHŽâ±SH홆»u{úZ:‰B™ÓMőFĎ0Žt‹ĚäÝçbß® $Ô1!ý©»JL‰'+”*Ś'7»±(‰®ľ/Đ~ˇJ7‰kŞô‚ǬÇqă-WötÁNž JדüuŠ~G@ »©kŇŰR䬆ě6f`6iV“Ä5 X „Ůôľąh–P@ő+s&ÚÍPłJ·Ů‘×±Il5ßrę6c2 Lŕ ffă!č5• ©kĆH·Ëś'™"|´- ei°?+7 A˙—iëU6¤1+Ľ‰¸-YžßěஹĺąOŤE5Č’ t)ŘčCPWč{™;BŚŰ~dďŁ$·ZÖU 5΄]= î–2€—?űř¬™µ¸¸ÜůĐV%ĺëM-´ů^ó'`o ŃżÚ °Řl6!ý†™ŚđÓV"˘ŐńĘ©ŐÇUj™Ąz~„ézujßţW©č­u]ds¶|%śľë-Ôă2•ňB¬@Î6…צŃäw覭ě´_Ťw}ąRŃXi’Äkş·5ß•Bž-Qń±zMň»F=Xö& B”ÚKŻěT.uŮ{ÁDń DŇvrU×)ÉťcĄĄ.3ňb‚ˇä#:=9#a„sÖżĺČłpM}k*™Á>QQ­Ž­Ă˙ffZ±ywřĂŚŮźůĹÖ. ± <ßÎ'„«ÔX!ť“rśßôÉű­Ýşň´k­űÁŁÁüRë“ÎÖZiâÉfřJ˘·ľ'…ŽŹ‘Č=ŮţŐëFĐĄůsv«tĆ%°cnKĽż˙zŢČž»ĺ‡Ĺö.?)|:Íp‚JŔ˘/ /őł4Ľuž~Eď6´ôĐŁ¦Ä*Ŕ¸=ŞěG# âĽY7ţ.FPYżÚ@đfäĄčɬčŽ!ć2%“"G?”M)7‰"˝‰ţőí,Šă3ř]ĄDµ WÝŃÖé3»…˝Č7ßͧë\ŤĆ‘rî Bt$$–ô„Ű”‚ÉŮ<ĆZG«ŽŹr\gŚq±Ď˙(¸¬ß—eĎîˇx§ˇ =Lłß đʬuq›9I n[ČÖ“A˙|€‰¨wągUoXX••€Ř¦Šh¦‚¸-,»Ż›ćůľo›çŔŻŇůƵŮ?`Ś>PîůчĘ^NÍű®ő˘‚ěëćůľo›çŔŻŇůƵŮ?`Ś>PîůчĘ^NÍűĄPÔ¤Ś>Rőö¸Ó¦ٰ®­ôĆ”ľ¶—VŇęŰo™Kîŕo@ă(wćÚeőt›.şşŞ“¦cćIÚ®0ťĹĘŘďŮMŹşÔÓSć˙<«Ŕ ŁŚEÖ]~žb ĽQ0äŐ!i…$aňOq8ĽëM'řl{âµr캊`ŘX‚'µöS¦˘(­˛`–vÄ:"\.ʲ÷M1 ŻĆây@¶Aĺ3Ío¸+۸w Ú ‘¤ßÉbÜŹ“¨ĺ}K‚éMv$¸7•Ą‚[ <ŮîżhăĺH ],d÷}3crď#SŤ†T±‘ŘĂzBúúmD*EĽe’LKúJĎ3ľć ścűrá5XÚn°›éˇŹĐÔŃ+[«¸š˘uÖw¤ÉˇËóĚŁGç6Ť=Č á4ŠüĺŐŇGľt€¤'‹$N“ťŔňGZ1˙RŰAÁÍŔWjIŇw’ě=ĺ[Lđ HYIĆĎΞ¬.¨^o- ł2Đ]óö1–Ł Â2˙)]šR’Č)mGpű­Ľ=îÖ{íiOjäć}` éóôľůRÖţřrćÔĐŔ7 ’X4ĐîS#uÜÜÚmë…đL,Ň?MÇ›—c>3?`h÷!t%@¦·ů˘Á…†/ťî-ţ4ŐJżi‘řékx¦e;oÉYЏ´_¸Ľ?úńÝLH“¬/ş]ť«žRL#Â/°2 Ő7 .ΗŰ#CpnŠĄĚ!›Łň’äKč6u|®@ďCtćśÔ{Xj4ĺ„QdăDQCőÂmăř„™ ká®9–˝ł:xděj<[óŢM™­Rß”!ĹÄÜZ°éýąŤyV8€'Ą{0JŤňE¨ăŹ˝Tw5Q&ÜcHâTëN=“€Ňy6˙±{âuÚ˛ő”Ě|d?4DfúYďËX°´ĺŇ´PŤľł ™¤‚X&~ ‘hä´ŁDÎ@ö ł‹f™>xĎiuß 4ŃihpGěIyc ±rč]DNĽß.Tą—dăę#N.Ö»&ąhݸ¶ČŕŹJJÝşŻ7‹s¦Ť™~×8÷`§ ´ËťÇě:!We¬Ćʍ«®¸­\×XŐ,06 „© ‰Ś]Ó†;×±ŐŻ¤”ąŻçš#aé±kłzÚŠĂ!Ńí]&rĄˇPđ9J~×PÍąŢ˙:}Eaşîňő._l9}Z«ˇ¸J`QVµš•„É ¬Ż§5P,¤şăЧć±Ežd˘$žAŤč§}ů,BÖśEÉÉź­źŃn·YňAXÁäg^ć…1Äí űĹůĎ„6Ôă©ěĽHg|ÜyĚĺްÂCꜣÚ*a5`í5;ÍcšŰrµĆű ľ©í™ä›ô.“×Y­&DĎ.ĎĆćú HďÖx䥬МţůŻ^Ż ÷ýôŔ}e dž…|hH†ór¤>+qŮŠÓ)IA}@JĂa( 4j L¨ąÓ×YGĄI†nd¦‰ŞĄhČŞT»¬\řBIˇ'…»­+smś3}šxđeK ŢŹ&ŐŠŰčŮÉC_N˛dž¸Ă˘ąÁ3 …ŻITPźËâ,ăTßkvăĎbÍp»üóögQ˙k”·nľ+ŻŘuäĎ ÂůOŰVŽSé ‘Áh)łqqˇ„ş!5–FŮ×TE€_$Áq6'ďdą ĺŹŕśLÄQÂ}ČÁ»ßÔ‰ŕľF`RjÄŻ"„NůwZ`Čgş7űmbOeGÄv”$r¬˘¤Ęł]l÷ţCăG˙ô’1cŚH~ IAü KA4óEm6U@ŁCy=$/SĄvšÉF¬ÜWo€ďë{® ś™ĄAlZş˝x+/:)öş!ÔţńŚ;˘3^ü…]Š_ŔSDUxN:ď׋Ćz>‹X®ü(9·Ţ%nřŘč†gćWŮz૜KHO}`‰PZŚ ‰ňońŠÖl÷ó^Ăáŕô†S,ŁiÁKĹö#_"«qpŞNˇ#TCăQ˙:ôUŕ8Ö÷ áË ¸3+˛xH–¤6 ˛Ů«ď ť vŹ7&Ň':ß#€<˙˛Ş3GĹěˇgS^ćÓcúŘ W}Ťkqăp8+ŚÄ ÔőXáÄ7Ýâ*oĐ? t Ô1ŘIqŞ•ŕĄ‚9EÜ'ęVBűU¨§*S@‡Żş™ Ë–KČ*Şbyň'ioÓwŘ@IҸ|[|ŠĹHŞ!ö?[lĚ˙eĐ⼾UćÔáěÝ5šX=ˇúwŮ~ćôžf±S*T| â é×™ÁX8˝çřoł‡,—.0¸Äç<3Wâ €ĄŔöç•Ä÷¤Ú/x*ŰŢzáď düN­ŽnŐ?’`‹Z©ň“/Ížş’Éżě¨ţ•@C›„¤Â˙±»_öî}”E}ąN ŁĄ9k.r™HGŢűç‹Ó¨Îę/V“w^™Qó«×´¬.5ä"ýŮŽÄÔŞĽ\‚l>*Ż&™›Ďô ”mSs@5$ďž«–Ĺ2ů"© O'®léÜtî8ÂÉâ‰đ也Ε-J“’9Ŕ ©ë˙B‚A€\‘séçŽŢ]ą ó[básŐ±.ĺĆQ« í×űý¸\pŘÔ貇đ©á‚h}¤ZI"¶űýá8˙s)^¨şŃë1— ·˝Îclěw¬°-Ú’k  5Jń%óŽ7w‘qg´ÉmşpaÚ<çuv˛UCß3óěą%Aņ9ođÁćöŤ%†*†‹•Ť ”˝T„ň¤ě4¨‹&§NçJ˝‰[@?Ä‹ĆŐ9yĂŰżkT ·ëamĹĺµÄ˘[FPi˘·+&ĚçÄÁĂ—1†Ĺ´ĐIú_‡^c­!Đkŕ×®Řx•»QvňăËdë’ż¬ÉÄ v pq@7×±•Dë0µ=¨/5˘ĚâîmŘ#pWčśn•ţK1wm©9TĆrß,żŹgôO‹"Î,Ëíe}ĐĎƦ`¸˙'1‘ă\¨™©.)nŕćö[XűĘul˘\RqI—úŇýĚy˘˙=ÍŽ€ŐńŇ#® X\l„.ĐŢXÍ‚źü•Čś=ň‡·ëfVކ”çŔiä‰x5ý:WĹ)LKŽqTěOg¨DŞ,Íw—UýĎU!„T¨ –ćć5BQ“éAV §”>˘®ű˙bs ËL4!”á(ă%uĄj6ěGS×EĂ:óSë8TGCŢ‹˙qlŤÜRe:€Uk±«‚NćŰi‰<ą>8ŻîĆĐE§Fjf´€NSm‹ ç9HsÚÄO›ás’:×˘ĹˇŠŚ§¨ńÎJ۵8jűä64ů/˘‹9óë*˘¶ŕ‹çk‘?CĹ/Y‘D:Ń—$ öşWíő`·Î*/->î]řj_٤¶›‘1˛•*e?.âž×ý†g4oЎíXŁâP^×^ â$VđpŢíuŰŽ)¸Ľ gŽóc %PęüÁ˙PĄ]Żh×˙o†”»Ď®˘0Uޱíꡉŕs&ú<©ÜŚ+ĘÁ7N¶0ÝĘA»-G'É@Ľ)L§H‘VHżů§"“ÉQÖ`ôŃŔť™n>ş¬ŰHŁ-Ŕ·°‰¬ŻJ®ŕg{]Ě\Ĺ—iĆÚcŹş›X&îŻŐ!HGĽřŔřšöĹW{…‚×N|Ŕ€Ŕ3ŤŮľŐkĚ sbĺşsE.Á6ă;Ŕ Śoµ©ůç,“3eÂO: “ú°°Çř=ŕ .ĽMŮŮ!vűi‡Ó@?…síŞ˙es©l9şâs ÷„ů_Uc/Ó—vž7 :LŇőGJZ» tˇoýô…"ţxp0Ëđ#˝¨€Qô9˙đŢË­ä›ç[D}ŢCáýZŔýa9[2%ř¦É-‘®dŞŽ‰Ű Ŕ@UźĆ—č×lAzŐu2ćT®P(÷ÄŻw-EťĎYá•S—ŔđçXÍ#jÄŐČËř$˘ Ďş‘íôAŤjz¤ "g¤g#ĽŽ)ÖÔŐö‚ę }Ri»­°Bü`Lwô¤×nÓF]‹Ů¬9›:w˙p=‚`§ÁJ¸8ެŔ†I˝Ř<öVž5‡<„ŐWô©,MBşoajĄ˝Š/ăţ;¶ccŠ6÷˛ąv… eńXíwÁdŔ˝~†]›ŘC9ágQH˘bÄȨ ’5Ü‘ýcęäŞPP¨ňPYŃ›šU l ľÇĆ—Ý*Ł„:M_ţ´AŚ9;ZA¬.¬]x´Š2× űE¸LˇÂ3˝lžßÉĄµ,zÝhűÓ”ÝaˇC%ű ŕüW’ńŞE™•čgMňƤ¬˝ťćě®Ó`żĺ‰-âŐ Ń~°€Ú:Ą™ţ#5HfЍYć}…f[“d*V˙[“¶…«¨čKĎHôzĺż Tw®D1ň(€Ře˘Hçs?ě€Ć2A#>?iôż{śpK/~Gl©öř8µţeĚr¨ĺ?Ú:ĐŠk<ŃPě˙8YjqNÚucą%X’ŰGöĘŞîú–übEVˇmjĎýö=0‚Ź@&UTţŠUQ”2ĺĘČv1Ŕ¬ÄÜ˝»ě¦ťđ†–˝Ą<˝ŔtŰ#“`ó˙X±hl`rŚlWł ld#q‰©ł&ßőąMAˇůŹÝ?2ýÂŢ* aQµüţ'®Ly˘*@ý„ŞĎ]Üóňú21ô{Ç™Ą"O%'3ŐËNśP'|ť~ů-V—,•äúĂq§S  x›LĎ|éŠ(:F%łlö Šđ,v: [üĐuĐÝŔĺWLžÄ‹1|cś6Ą´ď2< 9űę1#1łáo&{¤•­ť">“FÔˇ?źÇĎŮÉjTˇ.t:" ¦Â;Ç84ç2F/BÚ÷â…§áü;×~4ŮĹTňbĎ8łT$N¬S$+—Ô.㮌Ź^ŁćĆ4¸żbc9EĄľzť¬ŐvAĽ*–uk­Őüň«Ń5~0çé*{BQÂ$™âAx{W@ŞÁĄëřČ]ťZ0•8#č]čZ <Ż|JÁîSđź*ąKi3ł0µĹη\öÖŚ&Ü b,:ó hňjÄÝśŽ‡Ŕ‘hż?ĚrŹŇI}×Ü—2Ň3ń%—d˘“ě|(¶Lťă’ęĎÝćȨ°P=€+ť˙&®ˇ aĄ Ż¦"Ôí‡T0WDž#‚YG*îŠR§Ó?ţ©;yPÚxí-˛i‹U¶+C~­ŤÄŰáţ<` ¶˝Q“?Sě«•Ó‡Éf QËŞÉsĚ[YĽÚ¦űËfńü'ţĚł`€IŠÂDŞ©łić=‹ç Í­ţá690čć‰XfIŤauů5‹%¶‰A+¸ŇP’Ś;ÖŰ–ZCXěţ žźľWXřşQČĘLăďĽm'µŃ®Pĺ[ý獥šşńS~´ś]a°ZXh°*uŕĂ/“ăŁçÖ Üëâ;oO J;vô›ÚŚ—ăÁ$´žĂÉÝpbůńÇç°™I§ß®Xý$[ĄM™ŕѢŕž_)iÚČc(~ÉXîĐG±¦Ť3`†š¶ľěŮF‡™ĽP§[µŘő şJń~ňt'!|źśyż çĘŘ‹Ň\Dh Dö­0á%Ą,2ŕL ×ňćmĆô}o6cű 0úk; ő<żď:Sývrń‹K„wĺĺą|AĹBŽ0«$č˙/FľUúăNr&?|úU6~´]•™ĘĂ·ĽV^‚‰®¦(3zLA°ą6q–‡Ůlŕ2“ËŹž:U­˘76)Íâç€ăg$"ź˛rrĽĺš> Ś«ŁM».IśŃÄ—† göfˇěNćÜ®ŢEcŐłOöŃ–z57Ů÷>Itçך¬ęZŠő¬ŤÝĆ:~٬R·ťžźŕĽ¦ÓäB«đ)ÖTÉ §bÇɲĂw)Ţ=hBaU{ł—?@Ěɢ[pCß4· Ţ®Áť•Łwa:lX—X›â.;˘€µkLŞ%Ĺ0ŇňM~.yˇ—\¸ą|6íË"H„ 9Ă€ uo0á^ ¨‚ěč8Ţ˙mî´”9ľ¶M´Oşó!Ë˝ľ)f4RtLľ° |ĎJE¤ÁţęÜ-·‹x>Ą#§ $ ŹŽb.íŕćtßt|'wC)Šl ř{{!2Ţ«Ĺ>ńĎ-°Í&Ş"·ŕ^Üo®Ë0ÄÇoé÷›9^č™ÍŻtd¬’ć“/dFî|Čăű 󌝯NJ~ ĆÎ(Âoâ?Q#nV‚ˇ<\YQĘ·íĆ-É<"hŤ2dŽŚ»SóyÓl ;Źlµ˙Úoţ¶.›H,lőGŻÝ‡¶ÓÜRXéÍ|ýĂŤ&ŕ+­Âś††l„`‰ ®]ޕΑ䏝ŮĹׯ¶ĹŮ_!=A6–;Ѣ<ŤrʸÁĐôśgŕ´Ż—uô1Éç:śĹ ݉MvŰ}ŰOv°ęî°vžŰ<°ó&{+áľ q.u=)ofIqµsNM«b“»„żŘ’ÖHw›˙„c(4P˙›@V™X0¸Áü?ßîćdńAu熹ôyË´×M·IXĚYJV[-J–7Bz—đWM˛e“¦jě÷ĺ’ř§Ŕ›Şú.{ÔĂ’‘}ÄqËţ‚“\o˛Ą- ęô=ęĐź<3ŮÚL$Ě”…Ńćęá ¬ (ę@ ^ťˇł¨h>Ł;Ř+i•¤›Đ´”1OťgN)챣ž­DÄéĚdëÎ eN˦|íSčhž˛ůż˙k€!•1†DbźSOÉ5±Ĺż÷Đö¦źćőHOÂLeŠäA¦đ`e 8A… ţĂđ5¨}ĹŰű:d˝śş€KäÚY!ü†/·^Uă`ÉţÄšüg9RękpȵXş i\p›ŚĐ‡ddŽŮWĚ2ůŤ€ĺ"đü"^‡LAc9ö%89&bxĘ$›šbJ( „•Bú´?Î'-ŞĐ§:cMŻĆ—}÷dŢŁ`‚¦X5ŕ ţˇeýmä\W)ĽÂ ÝŰD×í ĽhŃ-ł$vŔefn Äq6aĆnKÓŽ”lj°JşxČŘÚŞ–L]ÎÚy $0•cŻŹ\óżŤGúč˛4{B§H1¨9$ô>4¤–†™űVĚŐ”_ŃgÖâ›<­)lާ«pšĺŕ0[ç¶«›ź¬F!D ö;łJŠ)śgŁO88–ËjhFŃr`z›zW­€2= 7ËŚí€u™N XűˇiK(<QţścpÍ­Y™×Ú„;üT]ŁĂĐfÖŘA®_C ÔygW` ćC­µ‰/=ÝÎnrĆćŇ4vDÜPÔ%ÖÖ7ysvżÜÚ$ މ¸Ł+ţՌމBŘôg4$ŮR¦ŮĂäµk%ŕ_•Ůŕ% üśĽjVMżéĺ LR öř–4Ć]N˘ŐQŽ9´U‘đ#·ŠÓŹ’đSyâ”ä±Oď%3‚Úc¤Ń+˙!PĹŕŢÄé-RŢq–:˛*ý áƬεÝĆNšE,DŚ)B€%"¤«Áűg gŠ&ĽŽÉ°žÇÓŔXs)k™”ç—hLÍşˇţ$ů"[„!|^†É’c:.ţERž>Y#çfĆżŹy ¤R„Ž6’śĄm?Ś4Ćá%ű®’¨ĹrćT5ýłbăŘYRmŘĺ`ˇ…2€÷y'Dz+f“ Cď2¬ŮűHč˝tYSĚČíP(ô©$1ąÂXOh6rcK±PF¶Ú°ˇĂôKÜÍYć*>ÓHČ\vJ7Ż~µĹGR}ŕO AÇ}OdmęüzőL÷np [ ąµôB‡jßá©’‡(^*±#×§ÔĽľĘ3Íěŕ¶ÖĆh[ďLúÝ…O˘§@¤!˝=Ĺ­Ôę-7ű%ôÓ`  ëKoŮd79ŹVťpä8™ä‡@Kg˛©=«ŹĂÁ¦HĺîńĽčČůtwą/ë0SBŐ讀ŇÉ"{̵”LÚ„×ý©|ýĂ+÷q·ŻşŞst‡‰ÔJŇŁ“ŕˇ?‚˘7B(-Ż’IÂť"!›˘ äÄŠ’­RN' "a Gř™%˘¸˝ďćáč›H~&ȤY˙g3Ůł–cň2Čl±đH›xŻ50Ç$Ĺ»źG•,¨žYx)Ŕđ˘Yq˙<˛j“’Eúúí¤űźµîN7ţs•Ý Ó–':§ K`ŮŹăvčňřdË3äťî«é¦Ł:°+!{®Ep˛ÉśďhpŃPüIą“µă(<[<‹ ď—tújĺ\-”x”4˛\Gç»ň‚đę#Úy–[|иĐ×l[ŕ<4WĂ*ěđDVe–ÄL(yđYňšń_Öž şÉ`ł”ĹmiWČËk“ń0˛žCÓżÜřQuŁťT(‡$Ę9ݵn1wFŞŚ'žĹó2p–„µGń}3Ççß÷čîĄhLGE9?fqĘz‡qh»Ľe †ß8őąu±­´-đCĚ"/śřéC3˘QcWëR[†ĽeÉw…Ě=H-sQ‹´§LfŞ{ üćUŇ:óĎX©vŕ0Ž` #Z«*m ÄŹ»b©»Í"‰‡×{CóDů%Aí#SaFťç}đG:śCłźŤ.ŔÂĘďŢj…\D+Ý|ń¨˙WŇ`XÚň{ĹęTGXCÂČőśSxuýÖQ­+’őJĄ'űb$ą/×ç´żČo(ôŇ’ď€,ďq«S}4I ŕ;všS§ńđNʦbC-|0zň0źĐŇÔó€w±r.™ĹÂ8 M94·ő¶"çţřřpŕ™¶>Î`tŇóú)ć<ŕA9y }NZżĎ†Ëpô ô‚ŁĂÚQę¤'«49ŹőⲉgŁVĆŘ\¸i"Z#Ć›h6*Ď/ťŰz×ýkVćů¬Ť()‚~.ý €ÇŤş,ăřbĽ±}B7ŇÝU'ŽFlĽ÷GC^YÉb:–—™č¨)¬q>2Nç{óĎ»Úé8°BIĄ–řRëÁNs µ(O74ţq5Żůîpţ &Hď3ĺ^„óÎ÷0ŕrÖŘ 0Ý(<É őQ𩳶L‰ďˇʬ‡¸—łÝoCµ<šéżu3;ˇ„|Żâ´*ĄČ˘H´ńLGĚŮcŇşĽăruä0K­»%ĽcL_^đ5ËÂdĽĎĂöŢČ—Ź%\9ł÷ ąI˘)Â@ŁĹśŃ2ÔSüĂAĽ;‡~ßgq3|ó0|š0ýÜđhihý`¨3§Ż\ ±ŕ\ ~‡6ÎlSöT˙xH(c'4I;`6źŃÂ"Ög^­şYRLŔ°ář—}aE`µ ~U{®6 «ůÄ·[/ŰŤw—UŠűąšŢ·Fo&g˝`űvm V8řţ‰ďmˇDR“®î›n‡Ĺ¬Uż…É—˛ůĹÇHKţĆF˛Le·˛_|÷Hôľ2Í©čĘ”5A÷ ŕů1MŽ`BpłB¨BuÜJť\­Q•4ÔŠ„Qž›Í7˛Cĺ0A–ҳʥŐč@QTú§h;Dy';©˘çđ˛ŔHô Ş:"KőťŤíP“!´ÇŠŘ÷ jß»eĎQ=€ĆxFÇĘçú‚Ę/,~Śďž»2÷†Ő,çď¶JĐ 3ůž`M-Ô\đ0Š€„„Y–ţˇ÷lR¸% g řŘ{Ö ‡ÚŰqö?»›ź<çÉŤ )çđĺě,ޢT “µ’tn‚î;”_˘[‰/l”lëěčeĽ—C—«uŔ“•őű“-[Uř7b"‡|¬/áH)ŁěcPýŠ3?ń·E©M' HJ]÷Ď^bY‚Ő"髊/<ą15˝olS3ĄË#´AepěhÍ wŔ),6'ĹßďŽÚłŐ{ŘŹű Z°ÎŢť|!Ô/%ňJě0ç#ű1‡Ň»'Ók›óĘz˝»˝ű2Đ©Ůaśĺ–~Ąx2µěŁme«ÎdĘÄy…Ą®§ĺĘ4˝ XŐ ţr5}Jk80'©îŢˇÄ ‹,ŐľJĘ‚Ë%ŘJYîĆöŚW'ď_JćöîGż9úçß BŇ˙ +DŔ¬kí,Ě»gpNâ°µŽS®qy ŃőqçGŽ-ÔaŹ{M!˝×»±…_@ť‘’aŮ˝¤ąÇŸ*ľĄagťÁ­tFJÄ _uDöOж“ł`ţ@‡š'´áź÷Ó„<‘BwMôđ 16{Ç]/b«oŔńsi’çÖÁ55ň Lfä*1€Sç4ű"ť˙eŞ–÷c mgCh“~kźÎE†ů¤k…&V˙ERpă„„’ô†‡/śpąśtYÜ îŹ÷|üĘÎŻŢĚŠë9nÉQ˙C/ęt_.±EĹIĚ?îăKŐ*ŁvZ·ďe^Kq}ţ:=¶`r>CŠş]MŐČ’“9ú-ň—”ćĐ'¶†ú#zĹIčÓÖĐ̨]ß›RřŤĆ~™ ă“ć¦~>DW"7éňu'¤6ę-ŻÓ:Žż±îścěŽüŽlÍÍ””ęFüIťżżŔ…a—Ř[~ź~,T·řiÖ+Ěö1®śż¨~ F `¦rź¤h'ç™J‹7ĐłlI%Ň.e‡KśŤžB@RjFpčË”÷Í ŰŽzŕÍŞé’Ľ5Ä{Ô†ši¨„Š|sľ}í}ÉŃ#łČP2W˛ŕJSČ^-8Ŕ đřĆäiM€Utđ3jŐ0lű(»Ťśˇ™@0–Lp™ČŹ€¸‡ô Ş:"HŠFę4aňáâlĆĽőÓjůăx§#ţÄ‹; ŃŕĆśą“ŠńśKBĽ¨ÁnU„ö- „„Y‹ +ŹNm ěĎ´ˇôyŇDRÎ&Ý<]‰ßĄy5ąřFäžvď˛^ §3ŽIJ^~·0\sFdŕľúF#}ŘtžŤ­Y¤=řťÄ´[“Că0 Ő"˝ňđ ˙#ť›ć 0j?€a!$^06żL3Ň9«,©‡ Ž€#çŘz 9˘ď«˙~•pćI°17Ýâ1ŐóŞÖZ9Wݰ?ŠÇ˙a˙;˝ÎĐ:ţnúk#áKzň uF"ź ™ţ4í 5¤ż];lßOśş[ĂĘ–űbR}ž”㸜E§ ďŘüĽ=0n,nő™©Ş©ˇ€kŹ›»—ÓbĂĄŹ§ňąŔ0“kÁ1GÁŤlYłř‚·źŁ”r™¨ľ4ť¤7bR%h˛ć€‰``.Ě™Ú9ÚçζĽWđU˙˙š+¦\‘±ČŽmZʞ>ńűnŘ©ä upĆ^rÄČ É´×î|- ŃS8 ”ÔŹŢydOm@ęBtDřWL«©h®+KŰť]Ď/5‡ńo>@ŚrEőözâÓE9V»ąłDVl ć˝rNŕřŁ)*:1ŞçäôvµŃ’«×óůýČ Ď”z1•t8†Ö2@ľUčţ¸ Ćă Ëřw1˝…ą=§ „ fĚŞŁp„h› t_­'WC©TÖMŢH>·‰A“1 $$ÉŐON„ďÜ–;=˙‚Ář˙yŇ‚|PĹ +q4g^č"ÔÎś$Ş’Żé–´ÜŕčŠO_Áß ? i÷ _±žaî kL†V;Yś{“Âşľ‹ĺ‚d“m„npqĚ‘ë§_`oEŮď†ŰÁjţÔb»¤•ź!kńí Ď EĐ-2·ż&/ýrżůTěžB7†eÂBKâBT“ôvEćô ¨^źuŞ^iěŚ4V˙<č'ćťüÖŻËEő´ÖÍňź ěfzůn –ťÜ¸K ‚~4Śä7Pî[é”ĆS4°7ZSGRăćřJ!=;@„{§ ÝFA<ť ˝4«rä$.¸(úĆý‹A‘;­Š[gµlىޖw xT~Ű/* #x"3@ <ĺSěŽÖ|­J¤Tá„&ĚO'.|Ú||ř “Ö ®Ďłšr}a<¦˙0ŹÂ%ĐćV1Ü ĂŢmIS:0ł•5ĆŚ÷ŢI^H_WŔËÓöÝt­ ~»¶×1üFĽĎóDp€ÝU“ČžÎ3~#+a‚°CVśĂ­ąS~ň…Ą=oxˇÁĎp¤ĹăiIŇă@SVťˇ•†o‡‰=˙|>S ÇŰqFy’ŐśXA>wV{÷gv0®n {%-„~2Đ —‚¬/§‘ü ž¨őVŹę6ogŠO}ínăŇW¦H >‡¦Zžř˙Ş÷Aă);Ú\.}“jUŞCD¤Ó%î”ÝMâÄ2b\öö=ŘYBE hłńJeěžňÓ˝xp7Ϻò-ŠCůj'ó0ÔłqŢúݤ$Ľ[*űVŢYjŰÇÄ÷ŁÄzŘÎ.9ů!±1§sřÎÁxË­›tÍÄ-WVÉŇqôŘ˙>ŮÖô­Jő´>qÉH‘ʆ.úŻŃLŁ_ 쩤 ˙(°CKůxóÉżý‹ţIÓŞťtžJ!€„ŕ SWAHŰz¶yM‡”=trKŮ×u… ¨Jú%Łťőw;€tf„źT=–â›/óiůq§÷ Ĺ·ŽňĚMI&żO1DíUő±OBÖ ‡M`\ďqÇçśGń2îf¶>nŹ­w°­v’Č~¶Ó‰OÓ€"ÖtdO¨ÖÇ`çĺ0˝’\ź%żŻcűWčçvn.ÁU€˛ĚܬçIŹ~Á·ţŕő;ł$Y®wĎ硓L¤ń4 j)˘ đ>ĺü—ŞúÍë.WŠyů7Źŕ —ż7‰ŹćŔŁš+fÖ»Ŕp[ĺ)z7LČňÓŃ„¨j*…"/¬TD9#.{‰ľŘüL×¶pvŃęÜy›łˇ<<|Á´Akp—ÍţÝ>–©&öäŻ g5SęěţŽZőřu>¦É–Ýŕ€čÎI5CžçŰĆÎ_ą…5â‹Aäń}sůÄůzÎyŠĹh PЂ߇łŮYŃ.ČgÎĽ5ˇ'Ţ<€Bź1ý§ÎáÎ4á(büÉřYRĽ” ­ížń PjűTÉ?k—>éÍVÍ š|HuÍĂŤ UR=•+‚ž’v›´śŘ¨o$*•ĎěrcJăM¨óŻÇ‹ń)OÎW(§«v8š¨· ‚+罣B”m©Ř ¦ąG÷‰Č^ţš=‘·I˛© $,!§@ZIŽĂɡ§ć*čűÂq‹OBiúÇŇQ%c­¨šîE˘'5ăż®X#Y,ăě/rKYB!U =Ąyľ!óřöśČÉŞ‡u0úŢőbSę&]Ú°îÚłöľ łá•űIŹU˝8Śp4WË©Ű(ć&ۇ?´pńŐÖ“‡ˇŮˇéK®—1vYr´Ď{»ĽĐMźżŽn#’—Íĺ{®ŕ‹&.ŞKÉ4kyZ'Şđ";pĆ^ń§•đ]¶tĹ×)$sŃXťţ,AđcCI†·ô$ǰY-;8 Ĺ‚ţ.¬đ㏩[ÎY¶çUśô]š7~Éx•8»Ţ^îd<´çQZd€}şit™–’Q|‡WŤĺ­ŕÉ6¨Őŕ*$3{„­ ŁÜÍ%oÁ4©îz÷SýĐťA !ć]ľ¬lµČÔAćm0aűú4dŞÝ5CNżŽČ‚6°–(˛‘čÜ_áŃíéŚďŐ_ŐúÄfäś@}cqV¤ÁlWîxé„´WL* UQنđ€&§2ü!˘?i‰ČDQX‹wĆĄuUIÍsLóM?ĺ®gěęRÔ8ˇ“Íd^6źł6ÖŔ€ť qLˇ˘żJ0đÔGS–Şęôśč2ý}OąÝĹ5Žn÷Ôô00xő˘ ,ÍÂľŤ?ˇÎ˘óŐÓt•WJ”›”I±ˇ‹2g7+”©şĎ_ü*¬ff šÜÓfÔÄU"yl\gó­4 ‰łq„Ĺ+öLvi^ëN¸ŻCĄTHTü Ź_’ĄŐ¬šĎ^ Ć‹jBW©Ž#'Ć !Á 2ÝţĹJ4đĘ cËŹYoSÇ'ňxF$âÎÚ/䤇eSĄŁČ%Ú÷;< ‚akďďô«ŚTá˛dάxüdŠA¸Â{RŃmę;†Ć¨W€8v3ű=ď#R s˝§CíÂ/ÖĽ8dŇ{k'§{ötM#qD€ż¨éŞg‚6!‘ß۬ś űJ&azY­¶ÍĽUĺôŻŃ!G›đpzo ˙T (W–$żę ­“%‚‡L…OŰę¶±(č¬Z{é´6Ů[. :Sň9%Đ@Ř‚bVÚŮw@ŔTo‚(i•¤włSţ,ëµQrĎZ˛÷°‘˛ ů1*8íÔÁv¸Ç°_rĎuĎă: €ý:.ßÓWIĺ,=ľ˘G’[µ@ż4i‘úşqweJh'$ˇ5†pŤM‡»{T®ĺ.^‘Š,Ľ~ءˇńOˇŢFZĂ}§?ö©d%ô#;9L Q }ĺť4`×ŐăÓp¸Ó/·öh(t5‹ž×‚ ďľB\R˘Y\V2č3ťŤË—Kskߤ¨“€·šŕU;4e¸†™ą$¬š6čČ©1®Ô~fYR}č÷§řČ7”Ě('7čŠxNLřU‹Ĺ3˝ţ•K©ýXŰţ‰Ž9†FíHu &'˝Xi¸ łß7‰ţ}Ľ\°ÇIĘ띊X¶ťČ!Ăq­5PÔ‘UśĐ^ÖµĐ.]’ɶ®Zö:x8ÝPSň^k%5‹ď1!IżcIzřÄq&nT1‘–V%‹»Ű%Ž.Zž™– I÷RÄ_¸´cçKfrą778-M—‡Ž~F<Ć0řî ż„ŕőÂZU‰XąňĘ\ÝÇÖOöŢ)ďŢß5¬•mĂ7›#ŠK›ÉĹ…^Ů3Q®*Đ÷‚;żWÖIÔŕ˝xĆ>‰B\p¨l‡Můř­<˘%ň{Žp2”{G8»ůÚmXuëąły:튇v8Çp\ˇŤ"fá†ÍiĎÇą^r’Ž4ŁUK4Í»&rÍ2fŠ<ś'ëŮQć u±0Hö! 5Ű0ź–ĄCc)ЦUú\Ź|'/Ľöôŕ ‹Gt‰ú}ÝZĆ&|ôl‰Űí4ó-fVxo7!‡AÎ2Ý>)pŃXń Éţ4ŚGţ<Ö#»‚őnr ­÷ 8ÜAš|’„.aëzBqąý}ĺ*¸\ća…Ť5äŔűÚ}ĺŇY.~ÎX­k_âúĄ·ŤA‡¦Âu)¶Ćř&Ý;s â%Ťy.´<ęť«ěł`—OĽüL©#ˇ î(Ë,8Í.S?Ʊ*" ;騄ş ůř™ óĂuGgËů? l~ő"´yhĄě‹d?¤ …ŽzrĚŕw]I5ű¬ó‚ ţNH´[8-íśŔŔ¸“Cż*»öSŠĄßh>Mýô·â˘Ä^+CŔ–flţ©"Ŕoy6˛G ŽEaç·íe†´Ň>üž;Í4¸Qş;^â#RQݎPšAµ#­AxüpŠ‹Ä$TD­éžkţ„źˇř/Yűž…<{ ‚;±ÚŘ÷×ĺg‡ł‚eŘS(ť‰nň‡yšĚŐÇ SR…ľQvőĄ~µ7/+dâNMđP¨!~\[¬}W]pČßÓ7s~ÍÉvoCąvÂđŠďA¶ňťůLntŁf÷cŐ~ň±yÝáfŹĚjě‘ ţÇŔ¦8ăŕĽó9ĶH)Ŕę"5Ăú~Ů©vĆ€mîFő%ű9BA‰«— •+ävNŃx˘S¶a$˘ŕűK|YřéÁŤÎ'âÝŘv´L}ąŚĆ­˛ť„+÷"UM‹ŇθřYÚ§ó˙&%ŰÜ“Ľë6łőöž5ĽjO[8§(턊Zę|,ˇ F$§´.ĘŠfr ŕoRö÷̆0±Í+„GMőÔŰý+—¤\Š.ěL‡F=%śç4}†(ŻŇîĺ Śťß†u´Řé6ŕ}•˝‹iívOż§Ç ‚1tîpi°OW·řGÚ€,J‰Tź•ş©?M¸RĄÖ«Ă†öÂ#`ö"Ľ®ńTřČâUIDąďVöÔ©±VĂ•§ÂóÖňßŔËéG,vÂ~Ľˇh Řw+(nń.‡b/d˘Äîůˇě+ÝíF,4•Šdţ ŽjOSŃ?Lâş®‡QĽPä`IUK7Ľ‘/ŕŻ8¸)I‘˘—{PřQfĚ̦łjG¤s¶”Řä¨[(˘s)ĐÔ™zÄx:Ş§Ź»¨ś#çLćtŢÉ\ zľ(JćO«ö«v9jöBżőÓčLm7+†l7fĎĽuS <ł:ĺ‰ěűłO{¦U­Ü»~˙ČŁßYÍĹ™$ÚJ¤c0±śčŃ^­<»‡^mpŢsÚŚYćŔüŘ-$4ÔŹgĄŁgéhW3oYm¨ń ™o"c±ô÷Đ$©­ÚĖâ.’ U'¬,K×’&Š’aqcű«qżżÉł’™^łŰY±ż4^¤˝ĐńŐä| ]˘Ł#C떆Ƿ°onµĺ?Ăam%DŠĹB!¸Üť»)Oţ«äŤ?_ ËsDÚűÉcwUŃÔ!cťxXŔIÍf7á:q'ű—@¶TyűdQ·Ę>UŐ4pCi’čÍRč¨>ŻŃsJá?ŹňřC0“„@ ŕŃÎ,Âo’é$€ň®wWĎS?ń•¶ŹÎ‹nV†¦Ť@­‚ogÇŕnÝŤ!•­Ş1>¨Gб¦ ”UŠ-čđ 3‹K>s¨ÄwBáýájJ e]™ĹĐm"m•äż Št8F'Ĺ>[SôC:r¦rˇŞĆ#Ľ§lmŔHťoÔ'÷´sŮrëďÇ”PÜ/v\ć‹ţ:w|˙E@Ä*Ďă9U&†"Zš® qě©ŰËÎäh™–]¸ő©ĘĘ@–ىő\ţżˇä^bÚł‡őĂ^6éËŢŃn°n1ÇrZâţµ‰Š™}ýiŻŇ™wË…Ž¶wl×­<ÎiKźhybA.ő4A†Ńه« Žła9އE“ŃMćěŞőŽš´ ¤çÎ6é= –†$ĂÜr?ô‹¶CŹ,cZxµ·PyWYŘéiËŇŤ˙SŘ`šrçˇL± 3üĽjĄ˛¶‚ ?+}:ą±:"˛Iú8OWq i¤%ĄÓ S|Śy ĎlQüÍZ‰©ß7N6ňăÚ»hÎ}B¸"Yl´ş°?üďBŇ"Ä8ŢERŘ›ŢPŕÂőĎÇË•UPĘĐ:lSZěŐÍś˝QÖćvŞgX¸áŔůÜcÔW`­‡ú„ôµkfiůń_ŇËŕm«‡ů@Ťä~kY"XoÜΔ&wČYäa)C¨{Čcčň˘tF—oîÄoçkĽ?R¤’ŘÄŰ—l/\íXłGĚÄÖ™µoÝ€$ÝŹáÄźáQÝEá^?Ľ™ëa šóçőKŻ» µxóF Šhhď¨Ëč(Óíč¨]ߎj3ÝŕXu\FBČI­ ąE0n¸‡÷!gžĂłčÓ|"®Žý{/M&eÄŹROfţE›Ľ’ţ^2Ie˝±`ţȬ蝹שémŻ6ő“ÂÁ*/ÜŇ×dĺóŞÓž˛ľ.yŁŃL‹´hůUŘýĄşď@< BR,Ö×úËmŮ˝uÎ “Ó&ÉGĹÍ^µŤ«|V#˘Wć2¤ŔÁ4^b­ČőtH!1~]čq<±Öşq~‚­ň€¶*źt“z)÷“A ŔaZ·4¨ ŕ=śkŰ…ˇAşÄýX7»Çű»[ă#/PČĹ;·Ľ˝×Ţ"׏ĂxYrŔI\đĂŹ›"ů‚–{O&ÎKÂĚ4ŤÂ{ʦ$› Âů9­©­4)Ú,Í6PíâöŞ)’“Ž›ÇâS˛ćŢ™*®!Â4™°ˇ˘·ÍĄňĂlP n(˛aZp&Eŕvkża,UČ<°é—ĺO# ÷Pë oŇÇȢְN|íűd;Úę Qň' *•Ş˝«rrJB•Ĩw^GŚŇbŔ‡äŇř8’ś‡Š§´{F懖Ph¶­$ <­Ĺľ.c°H-ďąŢ4GâČá!oŹ`„ŁÉ\)óîą pŰ×s¦i†Ă Ußü3R7ŤÖĺçßvFz—ăT%eňÁR{»1rŚÖťďE™^Éé'Ăť:“‘ݲ×ř Ő«€Ů¶5±ÁĘŔťQP*&̵ÄšţoRgyű"Şj>¤ĘśĽŮţY*SK ôŕ?˝rc”wňě¤>Źv`µí“c˙Ebť%őîtע Ú~ŽĐřÓnj˙BxŞŘ‘Ý‹¬9'ŤĂ`r·§y1¦kXĘ:*ö‚#ŕ\6vCě"µ_š@,¸éŃO čn*1nŠF÷ ·úYËńŹÁ„çc>Ý$gU˘=L¦˘Ű8 AŔń%ĄŇŘ/ę§ŔÂĺĐËo„<»Žěżáת 4b7˙cŞÔ2÷1öýżŤ­˝&&ĺvśÉ}d{=>Ŕ%–?HÔ üDâCj‚˛ĺť†Ůh¶¶ŞĽWű»ţľ`}•cµśÇt 7ëÖ^ś3ä_DÉd…W2OÁ[L˙gGé5»öXof¸ÉwĄ5[yA”’Ľ4bcŚé¤żOŇOPćí©,(”â| EÎ}žň­p5§áŮ6x^g¸ÜjKz‘(ŞqÎáŮŃŃ÷q˛"ȰĐ,ţ˝ÝeŁő˙w˝ôěliÜNÔ|{bƶW!/°÷&FŰ>J†+BOĺ ®–¸ž±隼C¶ťxđŹ଩Ďč7‹ b@`ďD©QúaTvS+Ń#p©‡ĚBF*FĆ‚ .Ŕ/Qn§§ `µýÜv«J’«‡I§ľ¨ąçj“đ{řŰ0ó‰Űjé ŢC#™ĄA‰Gëv] ´ăř0L ŇÍNHŽŕ+kIP0Ąé©BŮŇ×čţ.­KŽRŇ3a60ťgsÉ Ř \'`ńÂ>¨Ő*ŹňJf&ôU˘ő„~ĚYţEôÁŇGŠô»ÇpW˙p»ŤőÚťt·~äů­őz”ĐA7Şöxćq[ÎNý(ŇSC1ô·#Ă{„4ňŁ’Vľé(Ń-ľTĽOV˝6®‹ý„;XŃL5…úW‚đ n™ĚXÉń•ʤ}BdzĽž?CĹ™ŘQů47Ęü/ŻÝwQˇ;ÜpuhufŐĘŞ±Ú9˘Ă%Đ=Ç$ú¤yăL‡J‘›\]°ůî!˘Ôńq&1K4\ű­!UY±Í=÷:C@zpś%ľdŞ˛GÄ–$–ĚDűďćŔZ\´‘… źŻˇ KéóŔ¸Úąm>ß„őśd1~|Aˇ|•”rt0Tę?¶x|ď«őŤ4ćţÂ[~•„=öĽ©Ň˝!2ǰE«ż‚ĐŰ·&˛l?Đ–S—łč‚Ií–M •š˛ÝÇčßRŹDĐżoˇ‡íëRĐpwNý˝…·«x;Y>~OźŰ¤ůýńOź z%ňP´Ávóóű ý]4CµĐ˙+­öőloź__?®)óúµź>ŘĐĎž—ˇŇwNý˝äţ®±ˇÚmë˙UŮţý]vö3äÜäőQ>O\łä×@řŚMľ Îu¸δţ~—ĐŔVĐ…R‡kz'Gxrđ“t'°5ŰŐ% ĐŢťxëOn4eś0˙aŐé8Eř4Ąě…¸g´V-a „){ÚHř[Úkă‚—»ô3r …ŠĹ Ź§T´LĆ ‹úTh~J*ŻşŻ$돜§¨î´Ĺ!0ęXĂ‚gî>Ěë*¬üTǢuyŰ{ó–˘W\ Ż¬Ł ÚÁŐD0R*Šâ±üMjÖ@Ží•—’‚wpü$¤– 2˝Ň~]ÄbŔ6yë±Á¤đó†t^v2O¬ŕ^ôV­Q(qµÉť,Ůš"‰ăę”O*Mś±çľý¬­ďă0Ń0kÎóZ±8Z1†ĘĺB˛ögöŻę>ěÉ®w-J–o”ŠatPż?sđ@ł‘śm `´Ő•0^§Ŕčő„ ¤_ đ×Z84íŻxź]§ ě»u®ď¬L¨@X$ †ďĎ·n Ś%Qâ(PG]M- \Ôy–^Qkld˝b%ďQŽ rţŞyő'O™·_GÉfěäŚ@ń7ĘS€Ś·!ŠęÚGń‚ä`ő‘ʤ:cOJźŔyňU†SÉ®>Ł—Oc'“żçuŻ[ç Áž1–ÂŢPd…{ť(|Ľ]ŽąxĐŢěâlű¦OlÁľ ‡!ÜáŔ1”0Rş^ú ż»«É|/tKP©(EżÔ\™±ŰŞńţč DŹ2ŹsKÖXrĘđ¸Şy9Zš‘4Ç%—·sDކ®xE|±<ą}»ŹAÁ=ĺ7}ŹLňŐźfäÍŠŮţˇ žžőˇ~–ďŠt1?–6OĺĹ“fđ.=·âűcěNIŔT%•Ş›\š•dk ŤÔ¶Óµř2«{_Ťę\đł#zĘaATqn˝U?Đű=üŠy)6n^ł”¦ś%–îToˇ2/·™Ľz"GífÚ…§Kę3őÝŽę¸A™é”˛aKo€Ze@ĐťEéc´Ôc°‹’L;;MpşĹĐ•P×qy–°Ů!ÇFćgĹJ’ý>ąOŞÔGŠz ܇ĘwÎxc˘yxn§ŕŇŻüÔZ‡ ×?żŇ\ŘŃř÷ĽOIGqň’ůĆ>X’Í Ź›ýXa\HďÓ´Äé}8,›7Q¤Ű˙lňZzŔfNlľŐ¶%Č@Ö‰Pg0Ët~˙LxEaX˝1fţ>)—ţĺc l§) Đîý¶ÁĄe·±zCŐ¦¤33¸çc/=͆ڨS±ěÄé”,f]^ŇÝ\Ďąĺ_ŢPĘď]A* (Ę@„wŔŚ}Ę˝żóĐBF!Th˘u|‰đ˛– Ě튬Ę)‚éđFҦNj ľęÁޢ\=üŁ(mˇŤN‹X˝8Ô8Č«Či†=•¶aJź5‚0uéLő—ú$°Yţk•Ml›ĹO+ kćű~ëő„s ÔOg ĽžśŮťŚÖ‡\«ď˛Ý p&pÍč&ű÷±ßW5Ść‹÷}y"ěËĄ@™"ţ4ŰBÜh‘EWĆďŰé¸çm¦‹K[Hq†}$>HÝî-n¸F‡žő›ć-TŐ˛¨µ„ď‚ţ~ČX{'ňXoM6Đ(Ąx´«čAîÎNĦ]rcLŁ=˙/ ®?KĂ#đż$Ô e˘UĘoS3Ăo&Ô¤€ËŐ˛ÂŮŕmřëă{z4zx*§lçayT”ŠÇęÜ %Gö f`C(%m°CR:iIűŢcĹŞăŃ>' ťô̧ĘdÇŽ~˘ŔŮq~R™2ÉIT?‰–…×…ý7A(·řÇ훟žTN‚ßF+&í0&b“QG¤Ö˛Ltł±FÍÓµ˛<ş™ę@Đ.Ř8€á~â7Ń‹¤¶Ë8«.´í*ŮĹ5Ľ)-l_ˇ©2lľ%ÖáĎň˘Â0áPĚ>‘Ý1‰Żbbh? RŞ’ćIt ÄĚRÇFšS]>…Ű[q1şKhj‰^ ȆW@8°ď}Ž0´•‹úĂ᲻Ër[ŢÍ6i|Üćuú„[}q˛y×E‰®ą?«!1göç;qW»ů´{ ĺé >Y€¬Čú@âíiÔ­Ý˝9űŘvŞ[Ŕ号ŔEř=A˝3Vý«# ŞÚ|Ę`Ő3U…ë $“ţTż˛ż‹#ů+ńŠZD9Í‘™Â^éű´¶ţŮĽ‰ŕ©SmÄńďţĄ9ľôÚĘŘC´łŘU|[4á•kŘuqłOí'$‰uäZÓăy”ĺĚďCsĺĚ%2ĎŢeó«ů]#HC–ÍĆx‘ł/vNgK qĘ·ęĹvůÄOŔ˛±s7Ń’dâ¤ë¬1ÚŞţâŻę*y÷ĘÖśkĺjµ–P™±Ł±Uꍍžł~…xm›[š ÷ó·ˇ\ł w&›Ů×`Ŕ<µÇ Şa28˛B¦8FLóŚÖ-c ÂĂu…‚¨|UťŃxV)DSłbžJëď̉÷Í“5ÔAň Š?ř0ŁC­,ů_΀ň·kź»ĽzäÂŞŤŞGv ý B®ňIăŚ:j:,·*´8Ęžhk·  0TÝ(23ä–Ą™RúNôRč®ë9@ľUÜđŇW5®˘Ńť/‚Ś8Žâˇ˙LŽH{~óC_T];¶‰ čYKjŤRÇΓ‡«îěšg_˛!“ú ´z×a:fç­—é ë^ÚăúBöśr«ˇ?;88ĽăĘNßŇRçQ}´řűĘÖE"(đ‹Č!- ŁŮ_×~ŕđ˘^+cąý™K<|Ї1?ôY$€_ĚĆÚ©űaÉy«ž«ü;» µĘXâń“Š«FÔC˘—ţ‹e~ôß*9ĎűnHÜą†Ă0ŕ0Eîl—Ňçş·&ZSkđÔ†óÓ¬Źěă°ÝC “„Áâ•+a׿ŘGŠî\ĘŁS® Xy9%ťŇűŠőbŹ•ńŻ,Ň-‰ŕ‘#Ý$:VŔ0´’*éž«ÚB-|fŔµ6<Ă äĺlćŞ1>óĺţďe+·5ÇѤôq1ŚśÂŤűŰMîâkp/ŕWĘ'öĚÂá‡Ňcä¤aâ;ü6‡ – ŤQŁąDś=őÓć#^#ľ'´ÉćŹű¤4Ą‡Ťč)ýájőqŔćC!s÷í śXOËłÓ®‚Sč8"PGS„—Í ďzăjĎIŤš·złf‡_ŕ€2ę|d}_qôÝký%2éĺäőFlŔ ä^.Üďť˙Z™ Vł”Ä'w†Wžŕ·í—,± `ÂjU®ÝJZëś-˝™6±ÝS¤€w1ßfŐź[píá~N…ĐÔ¶QüŠ ‹´ í’ŕÂv˙pLěČC÷ř´ˇcíňŞ:óFש¦Gť Uďě<n9FN+ —L)€dLěĐ·‹Zě«ó}đYĄV“-¶ŽCg>2{×’ŠűvŚ‹ócâ'ŻVť-$L*†.&y,Ő¨až óC*x9B®XÁÚŹiЎĚ¤ĽĹG—žN!T÷Eç^:éľdśÂűëĂVkV-.ĘU Ă˝ě˝yEÁłbXčŚqŞÂýţěß_›­“ÎÓ˝_ůrÓ*˝‘ ëËŕ˙%Ć™{]NńobĘ›”đ[C”ô ‡.w)<¬‡]Gݱ¦XNÂ&EŻ\Ч\€~±[Ţ®)¨’e-P<&źë’ç7ôęTÚVy® ĐMĽ˘PhĆ…vYDSŽtžáv ďŽR'>ü‹MůpŞ“ăAúťqă)v•Oo?á Ł­Ś#l¦f”ú;Ŕó)±dE98J»AŻÚĆŚ("[‹Ę‹0˙TÔĆ´Iě ŕ,i‡=Cžń,ľŤU<=y,¤í÷Ýë§W~Ȩ•A®Â#S 4‚>Mě6őŘ´˙`Íäö5(‰-ĎYî´NĄ–#[ÁTZĚh>Čk‚šţk&ĽxšžâęyF5 ţ÷+\& gbŮźj÷±˝ż ¶ą Uöďc?ˇ${ŞŻ ÔµŕóҡqĄvmßî˘k÷" łXŤ?,ů˛K‰¦¨Ť,m±‚ÓŘ«’°~ŘnŔR^.Ó{Y.Aµ`ť*Ń®źĎŔ1Ţ:üń™®˙6‹q.`Ĺ$]Úâ&Ő˛×uŞ;›ďÉ*U‰Q[ă®"Üĺ¬'ÖÄ+xö<çŻöŔĎóăµ—äńÂť»ôí6ä3ü””y‹:Ą3"ˇ :“ú(ý#Ó ŘfP› ŮŹO4€2 N´‘ >Uv¤ű86˙?żŞIőůŇĺ˙UňäRĚ/WKËH1ĘâÓ§TeFQŕďŻp«ÄÂY&˛Ş„nSl…©¦:·•†§Ő2ČelöČhĄżłž™ßQH„Âń0žd9ě^;W\¸0Ş ·çä|(ĘÂhě´×S× Í;AX bä$éŃR0=ţEG÷é3Zâ–sĐť¤ÍEF)ŮŠ©5ŐíćňUGĺúçꦙ8ŔcO‘]Ú"ůzN$“¤ŰĂHŰ›ý§ďŕP4úÓrźT5‘µç>hÉś›5tJ§éÖď;ę>ÓĂKx9ŞHŃËSvú^çuŃ1ĘQ!˛ł×#ŕÂeťfĚ&žrŮę(.ß§SG6)"kÎśQí§[,夶Ô3¤đ•GyÉĂżn~fď^Neq¶Űâ;wŰi—Č$Ąo·kŢŕm·CWT‡mÍŮŤŐ ;-á÷‹C#ĽÂß:»Ćî(Żd†4ňÍȵ0‚‰”Ŕ^\)ťEe´¤X,©ŐŔŕç yɵČ·@Yç# žíĆCęŐ1Ú"FáĎ G<9¶wr|ţáâQŮ]ťą!<žä•m[SRŮQĎşů-ď,vŽô2°ůŁź7 čnalť±ß>cU’‹y:óýźńŻńAŁ,2DVĘĂyHK&K‘3ŕcĺzˇ´ńků  »ô[ěěZBşç·Ă¤ĄŰAÔŘ9gÁ鍠\±@Żîţ¶mXG*Vz»ńc»yě“TĚĎÓ%śAqw©0ť!hCďwRřó´´e,«đĘ:ěWĺФÔŔ8M ¦Űu°IŻŃů «vşE¨{1ĄzEń¦ĚÍ ŞS”Ňp\îŹyĎdl{đ‚3AH¦{|Ţ7zě°n‡C‹;Ұ3rŻ]'ě+-e¶˙^Ş25ęäĐĚö]•$óę´?Yěµµ—Çx1Ďuző3ň`_ă®ëfß8†&`“aë%‚5wY5SxĽ ®Ń×í&§Ë;­¬ë^˘`äiVž FC‡Ö´0#Ý|Xŕ÷gG/Źq=‚#¨şâBzăĄdŰLĄ ¬SiŹsĆšĂEx3p˘j)–ď(@5ŹšIµęžc3č˛J*Żą7Öŕ‘yZß‚R{<ü3$7żQĽ)††îm2ÚÇ&IFąî7ŻŇúl9}¦K$ő•@Đnnśďaż*ď­ťđ×mÄ„>1<”çúroâS[¤cN•Ę^´vS–°Š-ްŔÍ-w3Â\ ˛•µ–|Ě˙RXű˝č*¤ű<ŕ ‰vö˝‘żň –ô»N1Ť[ÄĚsď͡öc Şł‹ŕ–Ŕ­<Ćja!3ëź-N˝‚°ßLH*ŃgO_°ŠBYzĂ©97‡Čh€‡ŁÎW!*Č,´t/‚g?XU`˙l‡žgrjWľ5źt»ś E#Ňę#Üše„Ł·ś˙&!…ľ˘ŠąwÄő,%Ź9­MË(d—bcłŚ&v™ćˇ"ÇĹGśĹ…6–3ťZ‘.ÔőŃd"˛ ŢĘÚ1Č3@8Šá#ÉŚeßĹ×ٵ÷ťąŤł™Üú9ÝÝ-k@nŰ‹P6Űc•ůŰń…±ŠZŘD¤RÁËČAréšÂm{¶·żîhpź•~pôîÚÜ/^R©6şÓ±'óÚM)ĺŽĘ¶le5«NډúšH%͆m+Î٧’D„Gó€j%¦É@ěGgŮÚďť«>ëŮ ŤŢ@˘w`7ą‰«\šH˶ş ný2EĺX`‘ůž- äť Ľ@řš´ńÄ~híbPŤŕé§ž˙ ěŔ‘ ]ś”ŇĹ w ĂqŤ{ťPš:ÍŃ!Ł™ä%&˘Á(OURŹA¸Dř9ä@á-sá?Ďą5Ę’ó§,·ą‡ĹŞH¨÷iY€…?¸ž…,F¨6QÎjŞçú˙őX©K˛ĺŐ”Ą÷ßľMŰSuKŤ‰bäÓČugɬݚŽt“¸HŮćőácÄßç'™ęáÝéś…´éŘĆ‘#6wRěpËŘUč8ĂŇźîÍ‹8ŠĚľźŠQŠó«‘اÚ#í®‹“…čAzĂ[tÎ\CJ0¸ú.ëRyBŕkť÷ő»ŕÔTS @ţ‚Č`Ť}1…˙-›ů}‹*oÚž( Âtmţ^‡Ó~8c°3Üłď^D˛ţ´Ó5UTč}ÄcöĺLmÉČíŞwÄŐ°ţt{}u,cŽ*níŕIůđ]â]FůÂÝ ÷·(eÄÝX™<žĚîÓĺL kLşFĆWÝĐ•Ą¸p˛LE?Whd—"Ť›2 EŘŚdgäS"Iýçôd!Cí٦|€ŕdü­&©ĎÝ}ôFµvăÍł}=GÁ'ˇ~‚Ě«¨K¬Ł4–¤řŚ˘ ‚‡3jQ…ŁŚWOAkŰ6r:Ć:%¦íĺ¤t‚7{*[ŽůVk~­Ś{‘b‡ĺ*G’JĹE2ÓLVĎsľř[íń]Ěç”ňFŘćĐ,zF–°ľŠ“U'u’Ő,ě 6—ňôňÜVQÝ_ňę©^^ĆIgm!,ŃđgŮQ€ŁgÜ ‘ä¬H‚1sŐSSqԅҦJ:Śn§&!¦G^“łrËč¡Jírŕ‡ĐžßýćŔ­[+%®čéJlě“!ŇŚ Ăd[9ę˝°^°±ú~źÔj˛§[· }’o˘]fb#SMÍMÁ78÷Š-żµĹ¤(ŻěíČ_)Î\‘bŇÔ^ĺ"żµfÎóÜł))!C‡{Ĺd[öfA„‘?`ÇňŻŢňk Ô°Z:?ÍčĄ/HŤ-UOcb«hśůĹ-6u*9Aä_<†śÝ˙=ű,š“×ŐLĄÁ~VŰť5gRŽ™y‹‡9Zşdw6ЬŐyâţŘcHQ±†°hŇ o‹EŹó–ç‘GÓÉÍx.ú)â2üď»ĎĆôĚż2Ľ˘ĘćE‚Î4zZůUÎ#Gý¬ÉĚÎ1UÉsüˇ4Ťçö&ęŽ_©……ÎÁ§3TUĺ4čŁ%Ł#l§¨5,‹ň.µyÔCĆŽśłŔÖ‘CĘ (‡j;ĄYă"Iöé jÓ¶ŻCi+“ĺ_F–ˇęË7K'Ôx¸ŤŰDÇl˙f,'Ř-Ą<üꏝ2›c@ýEp_Ą»î•Kv$‰đű®ŞąŚ K §ž[qä5Şü„r–?őäÜŤjąćř‚ş"ŕ$VňvSíHKŘaJ‹öaľÍ ře“9›x•Bf'1•ÇŢ’l7Yěúˇ!áaö˝nď‘ŃŤĄ6ŢŁźĽ?rĺęwŹâp Ń‹vłňńuńéĐW?»ŤĐŻŹŚ©q]ş@ěĽd܇YůŁ´Hł·ˇ&ťŠöÚý]«w4…ÔŇđĽ8^ż|ő–qYťżżPŢXUeÄeđyňW)*‡á§J_łÓÚ1ŐĆѦĆ0–‹,]xSó‹đhQľčHŞ4áçOőËËö?¶_Ś- ®F6EFDź’ŮL˝!&mÁĂZĎ"ݰç%(*!‰f‡Křˇ¨ .!¸UV­äO,áů!i˛ś›TŘeXb‹ôçžËeóxĘ?6¦Ls°Â·‰řUůűC™6x ďgŽV™ÍqăąŔŰ;‹Yúąmăýzž /‘^ó°îÜöł4ˇďl™#B¤ąĆăÝ_ľ đ°sQŞ©¦_D‚Í™SYXQçeÝDL‚ŢB‚ÓÁŘŻZ‚ýđ%ÍTťF$ Ţq#kőmć6LTfMÝżŁ¶ňŹĄßě·"4şU¨ßµTq:Äň%p"1 QőVÉ".r×AĄŹ.,nű{łź°[PőCłOC)®$ö«3%ťŻ9€ĹÍ‘Ś5}†T‚Ď`^IÖ3);čĚţH—š RÓ•Ő|aez˙!žą—96!±.ŐÝł‘”űw×a®kćcc ŁŮ-Rł¶Ýz¦<}ß´3€‰ßMx‘ýÎĹéŁTČL)YýůzĚss.IĄ?EK–\ŕßČŹť™éŐ{GN6őY˘M(ÔŤ$@"hľ˙[ý›YT€čq‰FîŢ #Ő&ß~4E!wÇ*3ź!¤/?úzŠő[‹‹/°đÇK&SŹDôŘ^ŘŘ^¸Âbúšž¤EMZ’Ějá‘0âćKg×ŰÝ*><÷¬ éâěYfJŹo!äÔ˝ÄGč«×»_“hÉ^äEëĐh;zŞÓř•q™ĹJś,´ 1ëŚNČę6VĂÔťăÉî;łţp^¸wŚÓ™©Ó"¨?ÝiÁˇ~ĽÁcťuňG˛a7Sq!$ˇ­®S‰@Ö ĘG۬#€Ů˘á4î÷dŽ'”č)#Z?(<˙PkJ˛ŮżÜ,%+d% FĆÇ;\©Ś·ć«}0žďŐ°Vúęő-đ«ÜŁňě° W2lÉěž]üÍdÄŁ}7˘‘Ŕ|ÄĆľ/…uٶúîf6óśA64^ć޸őĽęE#©G“­L€ÍE•čőv‚K*Ş"Ç˝Á죵.y eÝ}Ťß±Dćđă5XKaÎeNÖgţ˙2VeĆgbzŽ<Ôń°?Sóć!MÖ{…0××*Lş'74ł#˙G »_ ÷ŘÔB Ed50H‡ł Ž©`Ňq;xćq¸ëýéŽ_€XŞGđ‡ţŁ3ů8&uOčłb¤é÷e˘Wŕŕ]\žÓń1Ö2ČěĽř=ţdl©Ói†qňtSéEŞmőo…Ëě’v™gşň=)^ń%’.eu˝#źZLÚµuyµůżKóO ŹBzM8JŐmi)™‰IC>łD޵CÝ:@&FС…ňŮSGŰ„ęĐŔîµx='^ 5>ŞŤť díODůDŹBMśŤ’=˛ ÷pű”ňî$Xńg+|ËÂ1[â?0ă8'»˛“x©ĆHběÚý\ôřŮ~Ąpaţ˙˙a†:vo©Tń3S! űŐx·ŽcĽdö>ôo2[¤ű˘†jÂşoÚ&Ď…ˇ–ô˛hw“9ţç[9_ńfóg*±ăEăĹĹ ę­|cĐuhJЬ¤í.ŇBŰ ›Ă]ˇµŇ#Qu±»ý×(Ő¦gBŻUrp›*BFoŚ@Q3=‡d€÷§.7o†Őńł őĽ^ţ‚hĹç•čěöQĽ‰zA×a€¸ëź2^Ą÷ń2VšYĽ*«9lBťĂ×ďüWeV0Ňľ?ĹP'0ÖTřÎ %ÓᇾŰ$}ɉ+&yÚJ®L§[AI-Ţ@[‡;ÜČJ†2´€`rĘĽ€ŤGÔXÚ=p:»L~˙yÂ>Đw¶h%ÄélyVă6:µÖ*ĺ]#ĄŔ°X–łŚÔ“olk÷R,­B7`ŕ~âĺđ•—´ů®:F–_',Üâ‘eŕů٬N-wČÝoŁD4uX”îńMĽˇZ¦ć\GCľčËI'‚aD˝ĺ €¨c‰ËzŢGĺ*®ôł—‰i%‚şíx1§N0]TőĚĄyŽ„‘ŽurŢŃ·­Š_8˛oůEě`NÜQMŠC(xŻ~%ś~^P¦˘éÁŢ´ReC^¨^“1Xt>~IçILëÔĐŤĎC›9NÖúj#l¶&fŘaë8ä!·°'ű$A„D_˝6B`[Ř©ú_CpÇś+!?‘Í+éůŢUv†“†ŤiíýŞ$~S«ýy¨Ň†dwŃF-ěŻŇŔ{a凢J„!X€ b¨źö~…†ÝrQĂuĘ[»Ă%Ąrî¶ M’Ődíd” Â#•[@š@q Č`fcŢłšn§Ŕw8}.ą«°§S7 w2˙79Ďźµ–ím|SôďR¸y­óU{ÓŠÁćiSC«kĹ6ě\¬K[: *¬\ž6ţěU,7đQZ­ĹŢ­G'lâ“˙MnŐ"źžř.ߨ{“R »rÉçÂş!ˇ FÓ.Ä]Äđój4ŹGJýřЦ1ű¦Bő@n © ŽłŔ‘Őţ¶m.5 óÝ;P:˙h~íÖ—ˇ9Ç ˘ľÖ‡kDâŻČn-|ď$E8ŃjÓşĆtŚđ[ü0˝'uĄnŕ(§ ł’+đ™éçöĐ)]÷lŐaeĺRí! ;ŕ_⇜ˇçZ8ŮĘô? N‚RbÜ_\Ł>“) ¶:µ\ďźßoPżKj±¦žT,h+I†KŮĂƸΧz™}»—&G?`ŘĹ©~°Mutűă.˛ďëeŁűîc€dËŕł*ŮŚSŁ`Şg„NŚ27X+ÓÁłČ˛Ţ[Ę(Ĺž›ń@·0ȸż#Ĺjś¤Ą3¦&“1ä6~•˛ś=R" Ľ­Q˝€gچU©ŻPÖ‡Ž~)í5]ËjvqV·ăţ†ŇĚé>GiOőçÖP2ťÓ(re>ŻĽóô1ŽŘ›Ď{„Ĺe lwČÇř—öl0D©»Ťĺ Núr9>đäŢh–:ŃdF©M.%mřčëŤ8§¸#ă/öÄi1F;ž†ÍPƨó•ěՠṊW™š=ěá†'ŹâŇ6‘Ťj2xŤ/%ŃšDIŕÚŘĹĎóđőÉ>ý@2žµˇNä"Ĺ.ř[ŃŮGčYŤăŹsŃ5kżb˛řŹ©§O‡í5W¦+iŞp]€`µÚS0uŤÖßéy$Y^ĚńĄ˝˛»Ž;8ë”|{‚ˇŞĆÎ; ś/cĚj=Fôe¸'é‚ZôŞ­7b PŢ đ]1ý@±:qhyóÔäéUŔ6×ĺ'Č Ąő↎Ŕ'0b$<¸Öh#™Nľ:^-ÝĎî­—WC+=ëě– ‘%*THůů*ƢNťWÚŇzŹUPë­é·‘¶)ëŹL†€k’0g%ţ¬Ô°|]ç;™<ו t4ÍĽ‹} bäv iˇ ëé“A«ŮřŻYk!yQľŕ‡ĐS. ĺćX`sk>äiĚü,=d;Ó´e&lg_č_§ś‘‚ÖXˇo6ř=6eě=iú-Ŕ ČY<şŽG&ţěOô ˇňń9˝9žFŃN QhűPČ'÷N u ?>Ú„°v0ęäˇa r2€7â)Íitř/XVĂţÝL˛&‹ę~#YŤďK4[»‹=í¬¶4čâ_V•1őřˇĘ›řéµFš©w)2ş"ĽäśĐÔ-·U|PŽËlěímöéň•ŘNቼ5Ż·ó&…}{¬Ĺăřţ"ŔgC“rôÝeŕ/bŕws1Ěëŕĺ ®"§Ö¸!]«0^«Ôe/âŇźĎó­÷±âW–±¶Q@Ú´®aŚzŇ>bóĽźkLÓ$d¬“8ý‘c·[śAEZ‹`©YŤĆü4qŁ›¤çĘ3v´ăŞ»ă+şeJk(&‹¬sůfcűau“ęw›7Ć7_i¤]şęÄđ` !GIÇżf’Ů"±çă†(Ľo]śxˇ‘0źA`|{ŠÇLUĘëWç͉OđĎSŃJóġL7jµ/––ř¤Óř!`›„‡ 9x50°ş2żtÖ} ŮŹëŃ/öRËîz®ÁŔ«ňG÷u;µ͆"‰2«U¶tń4Ř ´?I)Í•Ś§ ŮźKĽE„÷Ť¸7šfd tŰ^M˝ĚśŹ3ďw+)»¤Ú qŐŕ›‚.ŚŹŚťÍ(ÔϸšţgvŇIá›ŐrCřŤŻëÇţst{hšÎĺGŮědë_÷,,YĂîľmőď)#Ţš8Y[rŇN›/–ťřÚ§Öź*SěÝo‰˙WiXHUthŔéŘyU éË#”@ŰXŕluĐF*Q.tň%O™[Ĺ´úoŁŐ\Â‹ß đOěăČŻĎ6ÜáB¦ îĺÜ1•˙?d'ÖTĎçj]čöşÁęń@نŢsŃă'dGyă[· úůߡŘă.€!ŕcŇXî©Őup‡ÓřJŹ(Ýásł~ńßVNYňŽzí‚“1'žG MtuJďâB$BČđ3"DQG쮍˙năb×~Ó‘ďCîş üꤸ"IşaŹîf­J©'±ÎĂTâ.>O?xşŐř$ČieÇě×)Ňům®p|îäËÚű¦‚ľ]¨µ„Â7ŇmֵѬo®•~ń÷¨eůµŁ§Ź»ËČ!r§ŁuJMą:XômDŤIÄV+…:,Ţ^#ďŰ­«^ĄHÁţ?(đm Öý˘d¨@N+§éĽçą…ÓgP==~°gÓŃĐťŔźnÖ›Şá®ňÖ×^9›0ˇőđ Żěµ‘<… ^‡Ěuâő™ĺ‘éŁEF@$«đ  Sgż\Ôřy NägQZ§ÉK߸+.´ţmíń× i €ńăŐÝůWŰ÷ľęJ?A%ݧjB/ÖLg &L#]‘á)ń›ÁŽĽćžĐ«śhnŘ&•IÚŮŻŮr…*]K]3¶Co’ż<`ł¬ŠQĚg G‡˘20dßů/©ˇHḶMšź1ź$ęÁÁW Ç˝ «ŇŘ…;1VGŻÇ(ÄĺżVëüqŕôbďUH) YÎ~b“„x«†Ţ˘b”;49`gU­cm>[x뎸äÂEŁ]°ŰhŔřlÔń|1)î-â%ÉĎĐGřéŁ$qŮhqÍW= Ĺg#‹@ą¬Ě«§Ďü¨»ľŘd‚˜ْÝÔ[ÚöÎE6‡”*,)™ő&µňvu‡N’¶eŁű÷'Ű ŮĽÜtŐq}O‘ó-Zz­+',!c%¤źŃAO“Ľ— ü¬? *â/Ăáť¶ŰÁŹ5Ľ¨´ ĺý슌=QÓuŽş<Ę’2Ě%<7{í«gÄ®= =R}RÁEBŠyaş۲ _¤˝‹úGîĄ2±»=)V§NĄ•`ßćŮŰámL˛WÓ‰áŞTyS¬nŔ‰ÝĘ4ŕŚ¶'Đq¬ăÔ9»śŠsÔ:S§‹NËrŤóW “HJ©2şµĎ,|ÜWüŹyär:äź HeíAW^Đ{P¸qC”ôôj`ßB±—ŔŚ™S}ú˝ŰŻQ¦ÝżťYäPÇ řĚ,TŮS_g&CtaŹLőb)»üHQä|5¤Ž:řچI·CoźŁ—Šo”d±2číJ‡µôLT™ ł”«<˘Íć%‡Ís°@ë"ćĘŽĎ´iVÓŽ Őđń[•Đ>nĹłÚE ˙!˛ýölRýPD±ŻsŹČâ”ůÝ©p”…"o`L·°~tnŔ”|Î6čŰŽźTká^fçÂÄ:4=Š)˛†ŢźÜáW˙ż8ŔgŚi·ÓĐÖQÔg^ůhČ~´Ýś+@»v,EÜ=§^1ßG/Bf’âň*ďĂ$.^c:?·]O2Óę{!6Ú–ŘĆ—F@2ÎŹ¦âšŕ`°`P ‘YĂމĆsĎ Sý‚­ź˙Rg°řěíą:ç6€ßS źĽ<—Ź řů™“éĄX*n®ĹXuôÝ5d~öĺĎuyđRGé 3 É—Ąß(ôDDZ豬yčjĺŔĄÄyÉ9ËĂ“}ŰđŞQ¨Üą­üŇfŢšť“”pŽčŞŁFÜ ë Ł$ŽË8ÜSFľ÷*ş [ę®.常8¨H_ăTë®ňVţÎórÎB°;¬_9Ť'^bů ÷¸FÇ}µj˝·@Gäo[S™3˛*‘ďşľ7”ň¤x®˙y‘Tż’1m‘ئ0—&ZFlżĆ(c)eśÇZ“Ś©I B‘’Z/#<.&Ä‚E—{i>‘AĽ  Ů±Nި•ÎŚŠ¸%ŕ$Â>5Ľ‚›˝&}z“żČ#.™Ú‹hލGÔZ¦é®aSÜ‘Ř}˙FßíŐpvbőóä#آďÝu"h‚;á`Ü÷7ÄşLłĄ˝.~aťj Fiφ”ůĺ«h¸$mFâk‰çĆ›±je|žĄŽ.ĽK Úď)ö ń‡§(pţŇš)±ŠâM]8HĄž{/­ĽüÂbhéRŽ›őł¨wM+?‘’ŻŤBĄĄ Ź/·sŔôÉ5ŤÔ.ÉFZ¬Mţ…dŠüÂxÝ~~}ś; żĺ«[jž—vč…~zÔ·ŤJđý®10Ą´f•ů§˘/ćě«ţ`€cLÝ‹,ĐĚlOŕŠŰ~čŘ­0ňŞOWÉx‹~ł‘xČkU~g /Ťt—ÔÓHˇćĹH‘:Že¤Śş´A?€QúĹúvţ ż–€Fl^<×F0 1D {¬®ă™Ö.GŃ:e3v†^ů:Uw2Š.q¸ĽÔw|Ł÷ÍÝÉśkp`= ¶Ś RÔŇ­´iŹ”?ŘäGs|ď°yβX\xĹÇf˘žT¸“Â^ť*… Ć®-$\fż6ˇź`HÎQŻÎ˛AâfňăHĆôÇ2a·Çôpďö–Ş “5Ĺ1WYSŰKáb~Z¨ęf.ĄžüĄ g3ÝK®ľČŐtе¦ÄłŰä}Uĺ™…pP/ž‡ôďŚ?™Ž©˙"„ld »p2Ąr‚4d-I| m‚Ź>˙ymSQö•;“°RA ”Í6`Ą €r ‰ Ü”ä(«Aís‡â‰žĄŮőČ™<‰o¦-‚‹žCK@_ň˘Z’~ŃO* Ĺ»¸5µöo›Ŕj'á¸cąđ)gÍŮN«f¬żB“IÍŚĹ´#3¤ó3+y˙p,)qĺ‰KŔĘÖçŠÇ×:I ŠňśÓßÎ1! WŽ%€Ż,{†ĚIĐ?kXvw0ĹŽĄŕť[a®•ál NYJoíÓUfő"7b4¨tRPµ‡@0c•ń«]…‰HZUć;µR…cňÔÂá˘Wcľ0†©ŻŘ@j•ĹhńąĘŁëŔĺ6»ś5Ąö ĚŹ‘IVý斮п3>ouN&D'M"t‡űNUBÂďŁ˙%Rń§0î@RéÜK‹¦Ś˛ŐĘäÉĎó™^GĄV…w@C±uŕŐw‘°ăźSäĂKa‰K ĄgŃ&l÷˱ἸeLe{X@ťůв&~ĎýIxu±¤r›đ;sPŠú:ĚvpuźçöUý ”M{2]°ź}34ßžŐěY4BŤ ’±'DÂ2Äf'Ü_ F˛Žż‹-ĐšňJ4ňĘŐo˛PôW)‹¨ěýi·äĐ ŐĂ˛Śšď2YW6»< LÁŁîbÔÔď+Ąńô]F$#rďçâJźV&I÷‹śŤĆ¶ )LT?Š"ŚÔć1ję›}Ѥ€ÁéçܦŻęy ű‡ZSťŽřm‡-o@ą› šŘO™µĺAoFţ§)m9·Ü–ćż˝î9í‘pďD±˘ý3a¨"46©§ĘٸVÄ×I–] ‰]™ÔK¦Ĺµ',vÉĽ?1UŮiQĘŕ ď‡$4čk;ÖëŹŔĚőĆ\Ň#NI”?´Ů6ezG|•Cť¦ eT%NâdçáB:ŘÝ´Ô™Dß˙oʶm˘¶¨ôzKâO‹őú‘¤ń–ŕěĽg>ľ<ż¸Cáą:ýM„M€¸_ęÔjú2ŽoŘźz™×şŢC1„ĆâM,&ÂćTvašpř€?ćĆiĘW‰6˝¸J»çľH,|MΑŇŢťŐÔ1ßOt`ëŚŘgĚť×*ÍŞGÜ-ńjŇw;‡9Çj‹J2l¶mţÎűW0Jú–}6ZH@í@F óµBç&ndŰÁ÷&@sç°dJŻs,ý»Źô‰}i#ĎţůgŞ©dęBIňç'¤Ý9ý6Ź),šJ„1!ŻM1‰Ç”Ôsp/'f0ć&ad 7¦9iÄ;+˲¶mŻ·ěÁR NÇ˙Dµ^rŰ\Í磍˝@r±$‰Yhhř«Ö…IhVJzţ¬––ZÚÄŕú¬uŕˇuFŃ®ëôůęxf·b9îýĽtî›QÉÓ·’EßĚyfqŢ ŰW…ł6tK{ŐůI+Ô®E´I1F8şzĐgž ˇX–YŰS~ÚĂ‚ł1 }˝ž~\`šő`ô;ö˝° á1z#gy}@˛ÄX/Ř´ľÎ[ľŔ¬z:ć®Ů˙EháĚܨ7ťäÇiÎĹź1T%Ů0ž­7  +§D[Ö¶Âś„î•XW©š4W0;ÉFůz›ł°Ď§8IéŻUh|Öh€*ű¨ę+D˘–d´c4hCőmţ$ XÇô¦‹AďNŔ;aĽ[µ3ľŢ\Žě¤VÖ„»Ŕ+xUrw…vu}•ó%‹ý=e‰Ąu††Ż|>–kZü¸ćá8*µŞÁ*ţ=1€( żżś˝2 Źo†:pŤLq[jű6ĺšV'’9Y(N ±kViß™zóŃőý4~?/ědDmʰ‰{Ţ÷®ôďâµI żV%b¬ą#źŹ˝±ěň/.Ť¨ouh퀦 U2s'°Í¦{3Śő6ű´Ř‡ß+kě[(§Úą=;üŃ.öą!Ť®Ő0 Av¨¤ß|ďĎ2+ë–\ÔŁá-ŮÇߨ5 iö+Ş}zĽ€rćç÷.ĚĘîmż›˛ŔŻďż ý;źčCä›5ŇI±Ě^đoü«ëŮěÖ˙í"p?ä˝^ÎŞ¨KÓ¶«®¨"´M=l^®NtáşoŃeĚëOX„Ö¦aý?Ď|Ř©ŁT_RAPµČ ˝đÁš†t_U¬“]]žţ´{‚"¸ ÄľBŞîÉ%~ŮZ‘Ş*Łçĺ^ČYjĚżDy‹¬^žCďqz’réA‘vůIú”g‘ Jypj®ÍYĐ*éç÷¬ß÷CÂÝŕ·ĺvŐgź| ˙?/sżđěhĺ }.WP#úl•ĄVŠň‰k8éľÖţG@í÷đôěţ„Ŕíoş-öö3˙Z;gü6óřz˛˙ŻŻđÚm+ęaĐ'Ľý˝ę~ަŕí?ş-öőé˙Sp;aü5_řze˙ĄOđÖ-írŕéP#şíôqý˝ÁÚNëź·´ßöő±lżn¶~ßFŰéťűtčö~˙{%hí` ©ęµXąăˇ <Đ. e­÷ßŢů±ęŹĂř૯‘aľE$=A/$ŽQOô$ő =:’Ë7"Ń%gMŮg±ĚCܶçŤ2ÓBJ,”ő?cm¤ÄgîpřDk2 ;€‰fÇA˝řX\Á1Tóeŕ¦pĘTąqĘĐj6őYR» °Ťî Äáo%¦á1EÍ]©ńz@˝ç’»ť»Öë\ż9ęĄĚmÍŔ×đÉé€č cQťtí˙şżń+ŃWÚťn˛ŚG§5Цwâľ<*ŇP)W㊉"ŽčěÇkţ”µ=Ś‚AZmŔČO’ĘłÇë…Vkîč%]{Îă!˘ďŞŘŐÝNŃ@#˘EĘGÓX»´®űĄ Ű?zŠň(׏/ˇé‘§MŃHŞFĹfąĐĎh{7HĚ4>i#O2ţLŻŇ­BK>ÁoÓpîŕľŰř…hVó  ·ë35âsTńĆh«MHřVşĄX$ çÚ š´Ě)¤–n„ţHŁ2;:ŠĐuĂą‘4Ç=fČĺÁlLÓIŘÉĂ—Ěpq_Âß;Ű“ÂyŘú łŹšnç{ü¨= _`4Óů´ÍŇÖ&4,śS©·-Â+ŚęLĺ„Ű‚Ď)-CĹ‹oN× ň\˙)Ú™9ż{/nÍVD#(]t §wtĂ˙E»ć Ȧ4úű+MŇÜŁ€ŔŁ8ˇ¦ľ™qW¤O °T…•8hç—Źw¶ëQ÷B•*uŕU hjš‚ětfÜzvb(óÁ?—Óě‰ĹOâ…Ť¤Ěn˝× FŰâ¸ëKľ–ĽuíÓÍśrńóÇŠąSúłs%Ă©QCđđşyq€Ń>Ą=ݸ).…5âř$©J—ÔzÎýŞC«IŃy»ă=ŢƱ7 ·\gëNŘÇ…'é"°?h<”ő$źz‹ ›ńŘť S"y§xTˇđ ÍÜp¬^ ÷…ŕűľěÄę‘qő .Ľ%÷ď>`˙zŇ!á=K9…?xÖq˛Đ®Y˘?¬§ýÓ·áć?Űv!j‚Óq^Π-ä-ľ/¨¸^ńüĄo‘i{ů·ś™"yŰ®.ÜE'Oł;1Qş}ß. ÷ą5aĽŘf2e&ˇ3%ý@ˇR¬ŰĽĄA†hÔ?äÄŕ­nÇ|±¦ݶ†E`Ĺečۇă:ŢŢ2©á]űF˘Řő.~µ–E9ź–¸ČŁEťżľän“Ď’¨!ŚĺľyŤÍţŔdvúű>ĚGx’"ű!¸Ů,ÝDÔ…ř(\ż˘_µĂŇĎę2°Őië?Á}3nrĂĐď0P†:Ń&ŔŇd&$¶›ű dUčfő4Đ;v_îĐGńzUcĺ˝{¤:6)/7"ŁPüđ¸S©ßM|‚ËV&E?&7‚ąÓ—˘¨` ´86îµGÉ„#‘ďĺx­â| G 5ě5Ď`čߤ¤0 ią˙JkĚR¶† "»I;‚ ľ=ôŚ1ˇ5ě°-·đöŤĺďń)ľ—Âgßă·ťcś*™Ń}ČNĂP>ÂÁTjüÉť.,‡@Ź÷<_^#ç{˘6@ĂÔŤiµev}íPľ3îő ěß§|ĺMf33<[; `^ríźmóT49śÁ -yĘĽoc˝¬ŻŮĆ3M\}[G÷Ň{o„kî@Í–ěń>"Î ›}}ŠrŇŃOŔžgVÉA.\j[ÎŹÍ)Ę€¦8Ř/ęqęÍ,‘ źŢb“µ­ý[*Ł23#tŘG5gřmŢ+ŕ3őCp`‡ßŰUˇĚ“ÓŘŚ«úh|ŠlűqUÇ EyÇ?8ý)@˛°˘ üçěÇVĹŕ±~žE¬w?—0]9ĘÚCYÂCfŕ¸Çë©čłĽulß_a¨°ÂoE;ZŃű}Tşŕ•qszş’!‰ŕt úg¦SqÜvr _tń‘Šođ3‹¬7šŔ€őő)u dřLwjnĹľ,YĚŃ.(ŕUáEĄ€ť Ím*'śäŢEtáɬxŚx–„ oí;;nßşöea:„ű¬Ë稼ĹhDí3łpńÝDR“:€n€ôT‡Ťn3z;[ÜŁ…óú¸+ť 2Âő{ŰÚőhŕ…F'·u:Ľ»ĂÍÚ9¨ ×C1µ_CěuPPxř Ž\*3‹Îńű†ţA˝­›–VS–ä€?čZžç}”0ŕőÖ{{÷|'>ădă"‡=v-/ŐÁÔćÍžc÷gó˛w“!‚Ţ mŮ*˝!Bě9Nľ…†ě y´+&×Ę×ę5˘Ĺ›Ň^:ńˇËőSqă·qĆ=ă¶I.•˛ĺ╉kşd¶ăŽ=•0hÁ’±Ru“w ŞB»0óÂôâÂŚujA(z¦ÖÄW-ö(ŚY3°ű“2]ŞŁV­¨—éßGßsjźľ–^úpcŹ`aĘLF\P¤~3B¤&/ßGV󾡉¤3#ţń\O#gÇŕĎekIeŤKÔ•YK ٶô‡%L˙‚U‚,#ś„ Č;w „Bbű Ĺ58ŃŢŐ¬×ĘĂÎMóo!ź÷>ˇS[Ű ˘Ů9“1Ď©>×ű´r¦±[Yą7r2¨,ačńoŔÜDç¸@ßM8-7:ÚÔ‘ÍŢčgĆW-ďęńäM¤Ü6ůXE`ť ŹÉşÉĽiüKŁEľ9±ťî”†D_e áóÂý±.żŔ ,î;@YĐË'°şBÚ‰ú=”ă«®íŻÄˇ‚OTsbľÓÔr|{…|‘ľŁ[’;]U&ů¶h%śł.·vĽ4§‡&G7qŐ÷ÁîC ďţB<`Úżž¸E`KÓeC6µŘAó ‚óß>öŔ™z*&ËŽ‚ń%lřŃÂ`ň´™N"Ö1Ůd:kŃĄ«ě©]gšF_.#żKfĐ,l”TĚ ¨iŕu<Ođg˝Fvw Şvđ‘{őd¬ÎŘvŚČq/´Ů”ôŘ™ Pńk6I]§łŹ!o“ ~ň*žSŮě1€-u°j Ł‰™€Â¶{K{śXóX¬´ăóˇ‘˙.§0]¸.†ľáa„ěr(j«8IŇâxQqϨ>Żt‰dRëgab®Ż¶G6«ďÚú…:¨ÜyÇčQX–NG.býŮ[q«ňŘt=Ľńté™ŰOb˛ź544î©gą€ţ&#m¨µ|§H6N áK‚HY˝ÎŠóLL˝Ż×űÄąSްÓO<Áąyź1­PLwŰaÝ^ĎBgŚUh¶˛{.čŽôG;@€`¦(ĎNCŇ–k˙äÍyćčŮKŮ@d´*ýú®©}ȬâČ5“@v-ş„¤ň˙S Ů_cĄOö \â t‹Ş±ŃZýÓ–”QŇB)ʍ­efcŹŃ“•HHX—OÁňE`ÝŽzQí…ŞKŐ~ơZ‡Z±&ĄŽl-šíŚŠ„&BšŮ%á%\şKĆ;¤‹ůlĆ4–Ř9©x6ťX5ń9#Úµ-ßbáŚ}°Ž—KŰkşiúTužëă­ßvťč=»hđeS{‡f~žŢQéĎŐčéę$o„¬śü{ĎvGšKË®ťÎ„‰1×Ú¸Qźßó°Cbä‘¶OĆ(ń y800bÉČÎGp¤®Šq­č#•)UµÚKŽ[Ů=Łż›pÍGă҉‰±‚ÔDŁ=Ö÷Ä5"ĺ’+řW‹g,¶YxVR\bt‡;†§ŻÄéůĺb–“bď"E¸]ř€sEFÇ _´#Őé«ű2¬í2ş(­î‚ĺź{++ÍTSX”=Âż@2hSŐŁ!xSVű)bé„.éĽIÔĆ,Toe_͆f©úG&¤Ń)Ş–Oq®)‡…ÜEŰ[WE–΂^R¨złmKšVb(ݲ<&źĺ\bľ™±„ä’°¸UÁúÁvvđ#Ebĺ/Ű3…µr‹`§5š^©çu…\K¨w…v‡µ2Eľ1cÎö HĚÚ‘~¸>9ÖnrDĚ ĽńsDÎż$5”¸Ť…ż˝(-đÖ’ŞmQY6%§|NščhÄĺŇujk¸CŻu˘}|ŮŹÓÝÇGFá”öQ`_E„©yôŻňŢĄ—ćOČYäěíQ~úŢř’dČc‘ ڶ Dɺܥ«!ÓŕCs ôąťîk,O–úL]žŠă\¤ąńHˇq ă¶d•— Ë’CDxS,44yÝžń +Ŕ°˝Úî~^]ß~5ćX€‘2—řĄ}‚Ňl˘?ëńMo”naŠ|ôňYä_U#x‡ůfdśÔZÇš"&Ş4ő@ľŃµqM=.Ă+JÜ 4Ělđ5§ç˝ŤŘúٰďŔkµ­bYÚ—}xwŘ㶲UF+±ů…¶XžŰéÉ@ĺFBĚÓKŮBC–Ł*™–Ű0—>»Cé˙p Nuđ™aŢ+hB“câ–[×oţŃâ§˙-AËşz¸v¤/ÎëĆĽXCŰWŃ .?ánń 6S9d“Äx‹ťhíźQU)$1á-ő)iĐeěeAÄSc†Ç’ŠZč q Żů°FĆD(ž Óř^{Éą’băG6®ç¬‡1Žd% ˛ 3@›,h>‹ĆIýÉ /˙(>6 ű(đ$żöu’- ˇ†9ŮÔR@, MśŔ©juDfPż™ą Ô0ňŠ*ŐN­ÉĺayÁÉrr ů’Mő(s¦‹«»61Ń ŽnMąM}›ńé{¤yŔCÇ?čŻŃfĄĺl"жł%"鍒Ä÷HO*ď¦cRŽĎ8Ś„\.4MŘʆὠĹçůxX8ă$_bwî@Ţ!^ ·ß˙]ŻÉŢ㬓Č\—ú–Ôeő0<2¸7ÖVäÚ—˘ÝÂSGăá˛ęMXŃĺ0VofHąÖ#Ů Íí‡RŁVž HŕÉK!•<tH¸XĄ­8QCĄh!®˙0şnJŞBp¶ yłü¶ÝkO‘HŤ—Z8µŁŮ°…ÓAÚŹ;`~Ď+Üyß VŢĆ!ěbĺ˘D‰0č°•LjrŮ‘kć]uÁ=.'Ĺ Ď>h•ÔÎ4_‹źE›!˘h+«:"â´#¦§&FZ…ˇć]f†0ȔԺ¶(‡' ɉ÷.űÉÝűX»Čéż®ž:R+É‚ťM–Š‹‰Í¨˛ďˇ"¸Św‚ ʨ…ůŤŽ•eîáúĐ´  ™fĎt­KLK,ľňĽ÷/đ9bŽ’®1”´Ť‘ô8iěîĽ## ř?\‰xIVÔÎę®Z™ąC [ȧëŕ„q’Ş”WY·Jai¬{Ú°,ü<*@¤”ĺ rż©Öř(MҸäť@  Ü…Şý1¬ĚănŞđQlö—ň ŔşÚĽíZŠh}Ö·«UÚúÍ{†+µMě38úőĺ0íÍN¬Ć5Đů` *ąźÎ&’ů$‹JWF ›g¸Ö×tbˇżKéa%©EŃË.7‚3#łĐuž“ħX¬TĘSëĄŕń ¶ţţšS= Ł/ŠBcţ&䦯źS×>0ę ű#ZwŤČKuŽ•Ůß; STć3đ‚‡$D‡.PQ ;4fy’Ş!Ř5I jŃUĆŽ€đ[Ü<Ô’ŚüěrpR­·IÎiť'ŰĎZ¨«čÔĄŮÎňˇgĄĐ‚ö •ĹŮY˝.`@¬?v—Jkň!ůzcŢj›>/Č`¦Y‚Ĺ|«6hxłIaQód^ä;suĽüDŚŔ×JŹJ¬ę?ťľK‘t ˛á7ęđVÍŃŔ¤äT«ąěSŠ×g  3Xř&ÔWň;Îü+ řóÔő‚ź]TďĘŕű8//7UąqÄóȉĎ1 đ ËóŇj ۬#˱űţ™{QĹÍŘ„żřČ™ĹVHČý[kn|8żI.¬Ä’řŻ5ÄWÓřRąîb¬Ą% Ôô• v‰á#%ě<0ö˘ţČŘĚ·ŠϸäüÝăç ńögŠ”ÇŔČܦZńDR/§T‹ôm’ľÂSÜ3Ů"›QŢ·.¬×+łŚŁ’řÚŐw"h#:Ć™$a2bAW;Ýß őÂKť°t…Dp›ٸŹ2Ôd–¬ě~0ZľŘü& 0@3ÚA©ެŚĽ±ăĆ’řöăjľĘ豹±<ű’ˇYłĎ‘=!‰ 2Il.4°ŔúrR!­mśÝ ďĎgp˙J.ü©ü‹v( đ±důö¸„:ąúśk ©)ÁżśC=ĎQAşVű¨Őh©őVęúԋྲDĎŘ4ü÷Şçř -žáýy°Qß !Jw«¨˛&R“#Ŕ@:ĘbSsČ)ĽśŘŔ@ÂŹf‹‹<5˝@Ô——A.ÜRŤżđÎ9ŢBL\ÔÓ0żËŚŞi#i¶_Šń× CĐńZD „¬‘µźµg±ĺđ˙& ¸Ó^qw­aĂ;Ŕ‰ňątÚ}ZtřJ4:ç)˛3ű÷•Ú€!ŃĄÝŞÖöóş Č}*ňěKQXOo»AŐířđµóëⱫ9üjž¶ÉĄ#žRş˘qq˘—-ě(J—BşSĚ-j5Ť`ßČÁÍęXtTăиŁĹ k˛Iµ«Ş ˛đ±;uŇjߥ1 ľXeúă‰é˝!G:îÇ[ÉgSŮí–Č{ůµóGŇâ|˘§^UÄ´ĚvgЎëĆ:ń„-¶ R¬"júČćX×RŰÓ„I±˘Hcc€gÚMDŐÄ[]|äP|đ¦‚Y űˇŘýn`ĄI Ť•©kÄ»ŚXŰoĂŽ9˛u‚óXÔy7ŢąŃÁÉÄťLń])ĹŔ/8hXTăŞHŮk? ŚÎęę\Ŕ:Z’˛ĄlČ„˛Á· 0بP7–žámVaWĽ­ĺě."0E¸{B99~Ő:cHŰęZ=x)G«ŽNąÚ#;á|tZ\…ÎŞ&š …Sěßă¦,ł…;Qůâ·}w_ ŹŞ:}t×ä!ç~x·+­Jx¸é|Źy) ¶&‰ÔbD߉óŰ$üµţÖ>YéśšGĆZ"ÝśšĄ–ÓĂ[ÂŔŘËiđ ·Ĺ㮡śôS•‡@ŃČ[-y™ĚM6+“;×rÄőÇÇř®Č޶­’“ý¤pßű Řőj9±Ź6úş€aŇľvÄęÎ{–óëurĺá–ëđQ›źW$©"4Áy ;ťuá;ĹŮĽţ\Â'¸ňĽŠhÓü`ٵ%îďwć,şuCˇ@EÍŇľĚĹýíđĆHŮzüĄĆ”kŕĎ2w—„VňPC(ëů ißś×äÁÚ?ý›oŁ-˙2ă¬ICň?zŚ7*']@=HŚjWLx«Ď…O›¸€>0‚xZö ±Ň]ťí݇#bhäź|Ľv›¬´…”ZČ9%Ř\Ź*wň|'Ľď#¦ţŐ{ŽÚŕÓF6Ř–…=`JĐw+G˙(ŇÇm¦ŃóŻŕ,âŃŞ(‹>Ô‚¶‰dţÁ ™(éˇč.ETÂĐ!°,]6îř+ ĘhÉ%¸çŽ2@U: Ç€HMq?Nźđ%bíČ%Çý…’÷˛ěµđtŤú#ÉNbu]îÝQ8†u>›˝‹Ć»i߯ů,]éZJYŽ3~ő?(ţ1íŻ(í—KJ ­ö(95Ö•ŞÉăDÍĹłŰÉsÄPx .ç\¦áŮ9n-YVűëܦ$KH~ě:c"8ý•¸·¸ßďajŻďřEnĚŞîgęJŇżXľT`÷íi_r3rú„•vĽ­¶‘DÉ pÇ8Gb=L…ŤCŹŤ?5`•đ*ÎRś3ŰsgÉ. 9‘2ާVPQÎŹknî˝Ĺ7ď0Í‹8‹)ôüÚKŞ! ’I,ĎçüţěQĐŤšöŔÄ~ĺć¦ţŃ2?t·¬üÇͱ{őFT~{ŠĂ¨Î“^újAěc‰îĆyŐq±»Ź ŃĂĂD§)Ú»iŽHÚ¶lNË ‡Í“G,±üóúŐĹwę=Ů2ÁiG0vg Ék»U×č†Çx–_ô3ÉÉ…·€µ±nM Ľm¸Sj§Ą,Iů/tPbph#čşp0$_=Íd­E÷ÍM ß‹YĺCŇĹÁ[x§$ řΗڝ‚÷QÎM`đŔčtŻâÝ–íKvv–[¸ŽĹtŁéÉU-/sI龫ዬóë\ťBµ•DU´•őDrS´ź»3›4/»<,ŢÎ&Şç"Ýáě¨2ŇC›Ŕćo+˘Dją”HŞŇ p€o€čHż!X%vđö‘µ°Řî*ö5 g˘B§Ç›.Ůôös|wŕ43}íĂîßávIsňĄż?˝äݧYlZYŰŚ}ťî‘Ćmś ;j/čW–EşÉŚm$HŢúS6UřXcĺZ¸(ń]Ěç”ňFĐ}vž‡?ó×˝ĹR#ć0!LŔs'­_DW俇I©„WXŮášŢ(BÚ0TűŹE»ęŚÔnZőŹ#Ěörݤ?¤Ą´ëŽ-E@0Ł9ŚĽáź]ŠśMëőĐ€Fń2ŠK›÷ì0˙¨ŁČÓNqqb,B}«Ëi7“űý5Á±4<…oVyő`łBűtsÚˇ *y•˙zË/ôŢ$Ŕ)jmŹ—mIWA•ę×â†ß& čA°MGÉÝăÝ3Ş2ZŘOTSý[/^aO€´D!L Ř—ůd\ ¬¤<Ú1ŕ}~ş'ńíÄR<´RHE3ž§ĘíBW \eńŘ‘G×L𦅵߄ŤO<‡"ę’)cÇY{|LHŽ÷–VŐ^âžăżMuÔϢap˝Ú`ߡą_Ö'®,Z)ÄĐ_ŚţUáÉGŇ,-Ý`cŔçżńąDHwěŃ_Ś•Ćqđ5HŐµĐIÇÄäŞÄWĎŁ¨ńŘ ŕŞQrV´Dńú&Ř29Í™>"T!Í—„®Dő|xÝ”*rh‚ô+áDeŹŹ'jťŞŕW¨›pˇţű¸î—sh’Fëמ ŕt5iďżš0[Ć%ř‡{»ţ~umwR ?ůĹ'˛Ą˘y‡ÝŮŰAľ\—ľpňÓxtʦősť}}B-ea?ŻĚö<í°ͦ©J©o[˘~e(s—Áu]«ŢSŞČôĆ Í’`Ť€˘±X ; p…L={Q+?ŔţNż”ÚÓŐ?,"ˇxŁŚU«VłµÓđp‡Éc°cV’4LĐĺ¨ ˘Ś7¨8ÜÁˇtqÖřĹS¨§Ď5yěÚ8ĆĎ[ÇaübeÎÔ=[:kĐ”djBs“ŞvŢ Ř÷Mvɧ¸ľĹ»×Ŕŕ<Ů´> ÎÝ0˝A'›s1y§Ţź&)_ůD¤Ůˇµü|zĆŽ5ÉôţĐ›Č_ŽäÎé/čű%±ÄuĘř,_} \^EP”Ü Ú]ĽD9XáEaZÁ˝]SI ’¸ë.ÖŃ 9hÂRp¶T~JO°~ —FŃ;ń1ŤśđŁ}ř„gřQ#Ć'z …•´Ę˝áĺů•%.çëŞÚł}oÇs!4ZŘ&•˛ŐBOsfGŞőź1XR´ë-X–5±Ž»Á“Řn`ܤíŐ¤śnHŔ(Gp ^ńŹńbš® -q‡˙bŻń˝h«FŤçóí×B}¶ĹaxŻ5±`ű!R ócnW‘îž–ĎíŔÖ3!†úődńW®¸•śž‹TŮl8}ëň^®LÝźş0´@&Îź.n"“&»$Ŭ“őř*bp÷Ś2řQUG±©dÚ<ČäJŻ2˙?śtRH̨µÉĘ‹Çńťxyvż|m!†—*+něĽw?LS¬—}Ť¤Ĺ…Ć7.Ę9AbyŽŽSŠŕXŮ•&“”ă$Ί_˘1÷IP”˙ ĹQźÔcmÇo’ĐrĆH^üÇ3ą‘a#ŁŤT+ďr´Óű<Ł‘A,ľ—˙+SŃ=Ni»s F¨°ťŮ7‘e2U_¨Ý#&°#ÇiÍŤŘë*Ŕś%Š8=ŰŐ<{r—Ĺ$ĚFÍT•äPŞK˘ű)„°l–U#_97¸!QLÖ˛^H%ĆĄĐVmôWX·ż˙ ŰŐĎl•¶bd,ůP3ădn¨€k¤Ň›€eĺÎđ^Č—ám Č J‚s<Ąćl;9hoB0»*PA áž”chúĽŞćóÁĚĺŰUÓ¶ĎL™µ8nçđw-śŚ¦ĽÚiZÚ49FY)=ÖžewÍŘ~NňŞ041­Ô»Ń JĐĎž›ÔťKŽU‰zů‡3µ"Żć˘Ć÷|hI„łr?€Ş0ˇµ‰8ď)őůşPrćěs.!&©`ń©Î Z.«ĺ,şç.é\†p&ÚÜ­a ŰCk*m:6iÉB©™µŰŃ‚ď '҇}ÂŰŠI:˘ĂͰaýÍ]vç~Ü®Td˙.H¤˙=ü÷X9–‹Ű"mdµĐđ9/ĘżˇÂ.Ę>ó>FżŚę˝Zç’hŔQˇ+ZŤołvŤZ·;‡úxź×öŠČý=8%ČŃgËÓŠŢ˝Aú­ &ˇ„˝Ââ8‹üŮŃbóŐąďŃ€ő¬‚Ž ůFčč Ě\9ŤJą™–öj`zú¸6x®ë”ôę-Q`ľ27Q4MąVS 0őđť_0[1Dpc~îÎÜ ňXřńDÝŮCK×ţY˝#Ŕ"MX@Şä&Ü”&y8_š˙?¦íDŢś´b¬7Š«ÇWe‹÷бU‰Ör׆çUQĹá®ëÓü—źÝAҰM¨÷řŞ?ćűŤ|p>íN×ĆâĽËSn˛ÓÚcżâ3âJ}4 ˝ŁŚĹcľSaB»úş®’żtů; ±×AÎËÝŘ™NŇé×~R€ “°RÚcŘ‚L¶b©ÄlzlŘGĚŠkÓĐŠˇ€pUM)›Ă™J®,*XďQŚ1)^šŻěQřváĐ, ,éŮ &ě¬6·Ť›×(Ź™‰#ÁłjygXŮ)pŐúp‰ékw´šÖśş‘ŇI™ć¤đĽ%KlL c{Ťý/uBO1HŤ¬#AcĎjžH•äĘ>ŃînD M¸WÄž3KńC–H:­­îŻ;ŻŚÎpR`5ĺ*;ó¬R|  “s3Ëűb}zI>íŃÍ;aµUźăĆĚҤ×"ó\gZşT‹‰p5x˙hTZ‚®¤^ĚQŢ›) ˙C™ßí«&?Ôi´´»7oŤ]Îuçio&řúŚš˙2«®>C™ŇVŻ€1<Ćő!ÂŮ}]›Nžl|ôR…ZąŇ­† qÇb3ęĹK XyU5Ł9Ů´ô$Ĺ1Ѥ@Ú.m†gÂ" ™V;á8ŕ~*h±­›{!QÖ»’ŤM/¶!ţ€Ů‚}žá´şď:ޢéM'ožŞu5OáßÝ0Ż 1 ÄÂFü'µú·óďóýďť„Öć(ů* Äa˝E~6dó§Ói[üIŁ-źÁăôă®7ÉĽ.~D¨ń,3mţĎBŁ% ą÷ńA—äŔy:µ#sâVÓ >‡úbíL•Ş8¸ľĂF‚g)+×nÓ“Ľý´Ŕü Ĺf!IXwLŽ‹F»WÂ&ýhKIV,x;ĎJľ‹ťí%ő•BĂ5ç±Î­Ę>x:ş†l§Hn|%¤÷áÔÍ<[ÄÚR\EĘĚhǬ꼵ÖÝ„ íë*'ťUć mľ(!˛Űν©*źY« Ňęßöř†ţ ŚüG5°r‡ěOő{Ä+>Ő&&ü_;ěĆ!qŞ˘~Đý §ë©î;Ş’‹Ť1ëˇ,ţ4Ě.ąN]5’Óúۨ .5ěVŁ ‡–c›µxŰÄŤÍźŁąŚŮŽ} M×Dlí …!ŢűăńZ“·Ö"×5Ü&3âfé]”śC»¨Żź*đcTÔ˝«C¸JŦPú´¤÷ómć<ľĹ©­ŽE|đE´%¨-%¬čĐ•Ôkšhxu–esaĚřţ"‹öž6^é?š=l-›~7c rYśŔw¸Z'fz0_*qÚ*ˇ¶RrLŕó^­˛mŹiÜcTľ _Đ?ęÓHöQ(DF'ě,•{r€ů:+5ü-łř7ÍĆÎz5“ş<ÓעˇY@Ŕřú_Fa/W˙ĽĎC¨˙%ݲôÎ/8ăÄďęČ-‰ć˙ ča–ŤqY^Ý`MÝłŕEĆ!TšSżaŻT5–݉VBo­Î˘÷Ľé3utťŤ\q]/p#ńŐB[Ë‹'IÖ˛U€_¬Ă$˛o—±lő4Ä*ťu’’ŢsÄůSVó Xß Kł°ŮzłPĹžŐŽxF•jj•|`½Y@ťR OĂ%z[¤Éčçˇ*ÔÔŇŕ&±l[ĹŃZ9jp“íôG0lĽ<îx;!}}€ +.]^IŮFŕĂŁfú•fĎĘÓňó1Ű x¬·K’žşń<ÔśŠŠDk®®hÂĎËÍ‚ö†}¨Ë­…ń†0G]ÎĚžgÇŐ\­™äPŮR.“žÄQŇ7Ôs'x}ĽoíćľSRžŽü×%i˘"cÎíÓÚě·}Kyće-Ć 5˘q*ÇU‰"&ö^bbőN{ńlŢÂ黩¬€ Išµ~'ńI{Ž­uż•™µŢ˝¦=cO±03žÚ›0[A/űŠţlÁIĆMë¶ǡh?˛¦’s ¬Y-µl@dę\ěmj)ô,Z´ąÍŠ©Ű|p(ĐϡĂúué˘`űáKţłn7˛íPy“F[™ö€ső*‰úľ9âm3rŰ BQ÷Ë˝®eCq.Z%G ő°ń}6=˛lÚr*¤Í+NŮÔŕ5ŚÓĽÝČňĹ”ô6T·ĚÚc*{)íYĹÁŽ2g$L»ZÂőŽţ =V„_—*¦¨±ţ‡9ł'öŮ7˙&1íúĹéQŕ/^ţdŇ’xmó×oP@;Á÷ßW—{·$„ đŰvÍ5D¤ümßá{żýŽNÁú<¸Čm gmOüoçOŚŚtŐŽj[qąŞÔň¤z×?ŁvX¬m?P0±`CnĽä 7˝^Kş›±Ž Ĺ#–ş˘.‡ş·ł$÷ę6ńˇF´ČśaeÓćٺʤ`âöĚ˙}ıW`|E‚;ôů¬D„,ßť°á\» “ľĽŐóčjKqž/&K[¶ÉĂÜü\WJeŹMÚçlg6<ĽşDOÔtQ¦e]'¦ţżŚ%==»­ťQŰŞ­dČćÄ2|§t=‡Ń7ö§¬s+Z} ’Í6¤âű"ôRR‚)@,ČÎß^t0Ńł“ź “í*1ůr9ĽuÍ»ů,›,”_Ěóč=”Ź=ľăV÷aň¤}5‰ăBŁC¨qwę5B®™§áS±ě+Ř~oÁ¦ťy˛†šPĺJştB*{ĺ?é ä¦źĂ&/ńť"ĐLKŘb1.<"‰·¦ ź<›(]nĚNnPOv[t,š ËxŐͶŰÇÓ Ć ľXŮ÷|nsŰąŠ(7± ß'čś“.Ă´lwJůžýWg@`Ü+z_ŇFÄÍ&<’WKÁRäúšJ"Dś}e7ŕßţaęţÚpD­ŰőA?ç)‘&1“¦ă1~ŁÓ"ج‚=z“™-Ě­ę¨Ĺ I%FLÖ*…ľŔkň€íĘ`h”Ů“Ü]żâŚDE™ú÷Ź‚XÁ¨E 0ń§ĽŢ5‰SÎčŮ–=‰Uc»”Ţf˝čMy±†ľˇŐ}ŠD¸¨ť˙tŤh ‡˙f%ecŔüÄĐ +wźúő!ĚŚŚđs(ČĹ­'€şˇSŚ ąéĽťżĽ‰” ŐU{‘Vs;”ÇrŐř«DPµ“şëÉ´Ű˝E6O[t]‚”üÓą©⊋Đ–´2´şű5és§`etnÁ˛©Ů¸`ľlç59ËX8Ú:±ë"ý†ŰŃkěş%C\gkŮŤJD˝>¨‹+Ű“‡Żüxňű)©c’ę˙[UaĆš'x8ńĘěĺs,±#cF‡™„0'fxŢWňW{:€aň×ţ`*V_ôN„ä AíŢ_šQV.uIśł<€ĹÖÁ—˝Ccł2”é¶UY˛ŃÇzű3ôšă(I)Ĺ…čooą±)KrKĽÜá@Y,łŕŰÎ]lÜެ űĹtťa˝_‰±†µ™9fżCŻ3q°…ůҶtpŻů×Ǧ˙+©¨ćJ…ĺôŞJÎeeÄBł×;*ÇĘ[9™Éët6֧ۤ1[٤FÉçťß»MR­4ďę†%wýfÇN•P‰j1`źň[>ĎńúG9Mőđ'Yľt!{Vޱ©!ůśSć–漨wŢŚ±’Źx%•ŽČFh}{Ô»ę`ZŻPpš“¸vl€÷é<¦›dk¸¶;ÁŮ\sî¬ů÷ż’žţj5néł]Ŕř4¬·Úč˘üä=‹­ńĂVť”רŤŃŕěk) blBµźąŔ[ÚĺEtĘŮŕ¤ţż}pťŰKć:TÜ8Ć$#ęć÷CŢ …_9pqĐLJ„‘"kĐs˙EŃÍ6 ĄcP´°ÔĎ·žÂ&÷:ŔµxÔµń$ôłŠPsŰĎXĹđâ6GńToÖ™yö=¬ĽŮmů•Éňď•Â0¬¤{Ń®©c °iUíˇWŇŠ‘ďŮÚ+Ż I˙Ui§;¨Ŕ‰­*g ŇTŕŽ\ :­xôŘç÷DŚ ¶ŐaQî4›DD?ţ¤ňéŐK9µWr'v˛-.ÉʇŮŃĺ,ŃŢB„ű3a’k5'äř^Ř®kłô~{ÂţVíwAŮb|ˇůŐ¸„ntř9p檌×8ß…íĹĘ׉ᛠéĹi9 ąă 5ŁYΞÁďk‚ó—OôF±ß…ÎkrŃŐęV&Á'÷˛U˛A-“R6UţÝA›±>'÷E¦n&˘ëâŽË%ă[Áß0éÂiś<űX_ OhBĺŐ ţŘd„(#—QŁŃńÚ+§Ŕ2őţžńXŐM©[]Ńč<ͦ$‡L¶đúčŘ3XjľŁ~şŔQÍééµaHDR Đ›`đ'´.ß4pâd•’ľ{z Ő˘ŃŇäĺóu85[Ť7]ŤI°(FąěŞyFJüJ¬cmąćł`}ÚäçĚ˙)17[ŘtËj'EŕŃMצJ[şV»Młá"˙7d%~ëĹ9ł2č.;…Y0EČNŽ8RÄ’B=,ű[÷kĚ!ˇ"ĹĆEŰ[3č ¸Ó ŔŢCśł0Äö%9Yą„ž7ľVő¬ Á±$N\ý6lňú5•a `.KBlŘI|Ç“Ý]@6|šäĂ.§ wY‰KIŻ‘‘.Öj˙\Äť ņy¤ ) GU‚Zčeň.@cÁe1Zâ’ű_ůyRř(’ o:·(+¦ŁěC5 qrĘ€mŠo·Şř¨Ö_>Ŕf5kg¨Őzş5«Î»Á "Đ? ¨Łď꧇úďů~ᥠ-§^ĎÇm¶Ďďí„gíÇ™.ł†v6ĎHÎŢ÷T[ZRl§ä‡É‹Y:H¸6„¦–ń˙X–X¦\ëU`Ř÷Ą%Őy¸ 1řh÷·¦ÇT\ÝNą¤%„)ő"©ÖźĐ]™Ňž)0ŽByh]ă•|/w'a8÷ąv5’í7Č‹Ş~*»­ŽÜ‘›* {ňž&÷é<˘çr%9oSÚÍ»ÁŃAاZľ1¤ÉO<ˇt©Ëc•˛ jávšá@®3(Ý1Ä˝xíwY2şú·Áä®r ä w±‘‹ čč-ł€7*§ÁŕÂt`ÇqA,j®űš†n{(gĹáŠę˙3€« ‚‘K/ •,^ĄD®\49 y$!{É#˘fóUË­Ińő#+Xŕ.ęöĐ}Ľ@Oţ죧Ő ¤ź·ě62ElĐ:“»4÷}Ç ‚KĚŢÇÜHůJ˙%EŤ·1ĺeÜ´ĺj1č$Źô¶ŰâÁKuböŔĐxB™Ě' < ]a1}{هvV“A`暨ůĘbżŤ¨uń‚ÓHVŽäm˝CŚŽÍĚšĚTÎOr(eLť;^2Wšť{MŰoíčĐü„wú˙/ńü$üÖy~- QĽ’ 36ňVřX‚.^Ě-B0ń4ąçy®™6‰7Ó“§Ř ďŐ ÓĆśťÄĺÜŞ>Ňe}`O‹ző\–ŁHBőLř}0ę•ŔŽŽ8ăÉtíÎ3…Ş?n«ÎPó•@^@Zˇ¦¶4•?ö5>ô 3źB`Ĺ7˛9™ÓđF6˝{¤3LŻoŐžrŃĽý§˙c N`¤g"«kYůŽUC B7#µťÖ˛ÍĆoßŮSf\ĘIÖá°¸OŐwVXŕ<–Ë‘ÜŢ lŰXˇ›FČD{ź„—€ńo;Ešd ý¬QIŚő>˙V¸›Óř±A&:í*2n§~s¶šĄ3˛ůÔŤÁ\Čę ňę)ô[ŕmʦÖLoŞ—˙vý¶˙*ÇqŢ[ýâsŕŰ Ŕ?ł†Ťă:R­ŤŹŽ‡ł/WŚMçKU'żÉ˘Öcű“Ú3¸ËkR4üp8¸@“¶ő/Ż©ź®żű70M0žź7Šlv»@ßş,Ú6úx(WqCIŠŽCţ¬ŰwxD0}yŁPĚ(‘ä$ü\ZbČH$RY´Â˙yńf$ÇqÝŮŠixLnŤĽ1¸8z27ác\ťÍé’3] ŕ^»Ň2¤ćiśĺŘOđşëFwe3<*N®ÓF…łb+KCNÚ˝ÓˇÄúmI+q!ôó;lˇŘç|g‡oáŘóYŐ ň, ÉAOlBŘŽß°ÜËäCâţźöQ«Ą¤€ŢďHmóüd=…ş°ŠÍÉş·éË)ă˛q3ÉĹ4ńĚ~:˙jëcCb*+Řą7¤mßŃł,#zŞĽB°ćhÓ#˝{e/FĹ:\äɢŢpržk@—N ¸5őą\ śKŹŇŢzĺX:ôŃąy©šr~—Am®(űš Ĺ3 .ůš˝k<…AOĆř|ß´¨™CuçTŽsĚĚé·>Ôő†EkäâěwÉÂń®%~—2âş”ŠhT–›áA±UŃXŠâD^ţő¦ĺ~`äFę96BZ8#ĺśőŢ+á?–[EÚ’F&Ju S;ţMÜč ĘA a9•óâu=l2t-ďEâ·ü?Ëđ×EŹ&ŰŰ€ÍÉ|P}ńDg÷gé \÷ůu1[ĐÚv¨ät„îĘkC¶•Ŕ—lX**`,¦& ;ťȶŐęÇ»—őĄ¤,Ľ.5µŃěő;Ź÷/SEăuĹHCŃa^c _kP–}„'««ůÝ÷őěM”,KŃ:NE–@ŃělEĆČńÖŞ§d?ÝBŠůٵ|ô˝e®­ÚÄj¬t{Z|ähUţÖw$H}”56ľŐ r›!ÔUÝ1"\Ă$ô)q7× –+ţ‘iAâhę`U[f?RÚ|É3âë•ČĚćošf#şáatUř LĘW~”%¤¦hěP¦;~ťµŁÜą/댊3±ßk ĚAęµs1ż ¨HR+ŔÍn °ąđW’ČDmšłůi¶ Ľxó!ikŻr‹×ĆĽ3Y$ jŰ}7™\]͡„Hćë?D ±› řńíŘN°pó8XÔ‰w8Ô9˘ż‘‰Ůčµ*-Â…&ŠÝ ŁŮµ˙E厪«N¶^ŃŻĎ“¬µß©:©˝ĹëŃĆY̙֙WědÜ0 mŠ„IŔěsĐk|ô&ŁWKK®]FÄ®ŹłÔě˙X. ^ŇFýËąuöÚ$ŤÍk*4Ş×-ö…˛”Ä\Ĺ›ßţy˝+ľx&ëwµŞ.ý$Ť:ŮŇQé‹S «şő4ĺ#Őhͬ¶ąŹZNbţfŮ\, ›âx,„Ҩń«<>Ĺ@ůyGU=W-ťđşt“ۡ6üEßh%‹q” ĽBôÔ“쓉†D®’ŕůŹĚ×DĐ9Gţfí˙7‘ua’Á’.ËŚř f;w, ?ÇS˝çźĺµ)áVýߏ” ʜhŔEć¤bđDI{CŻŇ>+ĺ拌®ŻD»žsM’°6nŤÖnPVŃ46‚ś‡^dĺQĐ Dbꣳ.Sľ#`Ď;/Hö€™ZÔDů™Ćv›dďo6ł k¸ĂW+÷4Ť_č­“ŽűÎĄGź¶Î˛C®Î'şć®gx5¨gř$•Č‹ţPűj;8ŽĐ,»ÝI†TŞŕ ŠSbQ&ä׎{ÖŔkÁWĽÍŁK ’ťsĽźÜű$SśÓ-!0—Őw ¶ÂQٸ¦Ż!Ô©®ÍÖpŔ1ÝÓö–VÇë˘ŘîpÖÔ"k—®–[ĆŔQWP IŇgIżâ©ŹßJBS!OÔŃżţs"µŢ1ű9Üčőőt˝Ë ł4Ëč´–˙9ěź6ř˙VĂhśµë»ZcRlĂŤî`QmÓ qŚËĆŕT¦Tąo{ü¸…Rş¦HĆâ(™E‚‹p¸^’ę‘»Çtá»E—ţ¸±žÚKZ†ÝŤ—Ć„Ž ×'hÄ6ůg€ç‡ŇKň“]űÔHÖŞ’ćy5VţiéíCT.ge×Yĺ±´Y4ŤÉ¸F%đ`ĺ)ěŻvŇB¦áżŮWf*ŐBĚfľoyMꏧ&Ăîqˇ¤Ő‚'K%%k›BÂé Ó´)â}­ŃoçÚţ‚ßßöIŤ}®ĚŻL Ž’ [Ź5FóYËźPMëĘ‘ŁQ@N+­ ŢÝ_Vhݦ6äĂÝĽ)óëLŕIĺĹmbô†ÂůËíp8|± 4˝=ă±x®î9~㔕H±ř5WtL‡Őo`>ą-:›$€Źl…WĆŚT°~2ł˙VĹřrKXĄ˝Yl ®Vßcă'@Y-™É:·’ÉčŢAh%‚Şd<ĹűĄŞŞ•2Čý[˛'úÚYđÇ+OŠíňE·D.Ěő뢕MĄŁVٳ͕‚ŮnWË–{ŻzŻ85{cɨÔ}j‡b! í–¦.8Ë͉»Fl!,ÜĹŔÓ‰–ą9Â:·UmĆ§Ź 'WĽcô( 'źbŞ ŽX ákŢ—-€IŠ/í” Ű ;¬Ô¶ŹŁýćÇűŕ],§Ľz·x¤Pă—Řʉ đX­ěĘ8„LxA`ĐţĹ—4#|‡cő‚)zŁľYňłW§¸Ź2jĂ-V¦#zďzâńH˙‡eAxâ­ű}7µźń^­Sz:‹—–řŁú*TŚGO~µL=@xWWR>xóü^x„7C›fZQZ§Ź{ŐŻč›L }€řzDôV>MĄ:ěŰľĺU1\ël€ű˙rq-{ÜF/8 aáA FUV ’’†\’ŁŰfŠ`Ö¨ĎC¬ůLg#˛ĐpýUëj$¬ä·Ń ŞÇ˘+‹Ä>Č–¤*™{QWe‹˝FˇĂúKn©ć„Aßţ’KľĄ,BÄ“vćżáGńxđqő]#îZČÜ  “`ŚďVOß ŠŰ§{ÁŞĽňq -?1 ĆđăĄÉ…®‰Ł˝żŽ„PeýËUbÔElĄ _¬g`ţ”ď”%^ú6Ł m?Ó»[oö®ô cY›0 R[2•*‰’P̰űňóţ#îŮ®"››(‰ť_Pç҉á«ÚĎĂŞî‚ćĺ…^qŁM†Q×®% 'ůźF§Îa€QNAt@9ŠBÁFÚ‰î{ő*ź‰Áy´űŕ©Tö}eŰô©Ě„©3î .‚zŐˇď¤ů%X‘ń šĺčAőź1¦& ÔGˇŹb"äÂ(ůŻR–‚ë¸Ď µ »WŐŠO`ŇAËşm/č_JÄQńµ©†ie—sÚ•„ĂÚŁíKÓ[̦ż§W5„Ę‹HüL$ …qný- Ş™ăxěőË\°€t3+.”b\‡Ške Jµ%ólb„pË"¬u Ë•ßĐÚĹ^ŃŮ;ZÇş>°][ˇÉv’—lVzżČFCŮ;g/á73×ŕ¨A].ŮSsŔ(<łˇ T_n”ö!éŚÄ|j©“!¦Y9î@Rj{ăa®ÔTř‘o/V‹tŻ9ëŠnű,3źŃäť4­ď*x)šť)ĺ•tâľ«‘Ú0KĹWŇ62ʢ~YTŮĽMä»*Y&˘ăq®-$gíÖŮT`ß¶oM‚Č® *†ü«!هžÎĂŢʢ`ˇľm-řP tęŰL$Á9Ů=ľ §tł${łŞPE´ HŠfŔ/ŰŘ€÷ äď?ŕŠ`ŁNZËaU›Ű™‘łĚb3ÎŞňćaţ×đőĽŘnß ń˛1q–劚_/$K÷T’Öbş!÷:&á$yO›«ŁĹč`ocÔ~Ź‚—ŇP>€ŞeRŕ\JcŕČiŕ]‰Pšp6ěL;`ĐśEtÁzŐ=>pź?‰‘Ű­>Ô"8Ň}z`«ˇĂ†CĹPßŐ®MT–´žőQ$iá>d,lfŕ~Ë$lĐŰÖ'-.ŐiCŞ))2)‰=FÝ—MďöđŢ'Ó3Î7[LA6e• W|ž1LlT3Áń#Éă$=Ż”‡Ł=*Ćěíľ×ńLp\p%şű<ş8uŕ´ę~ý¤dëҬv¤!·íŮ?éPĆ;“Uśk˘Ýd blCáĽŘ2W E‡A|nĎϤĘŁĚpĚu…şl¸5ÝŇÁĽžŽę±OGޱĂ×Bó€®’–+ŃđgŞmŇęn`Í­Í˔ӫ8śěäśgSŇŐęžőf¤/DÜŔ´`í†R¶Ę[ë=ľ4eú'Zú°7ť=Žäö+䟣”NťdËĺ­zępYsÎOľä-Ô K •w"1v ‰+wLö':˘„ ť‰ěäAěgÄ /Güţ.!ŮŚ çAR ńŰLEÖ)8$â—Ŕ{zŞIPµîŇᵞk^š áTŤŽř2›Ţ„ť…Ľ„ 0šU‰ F)Ćż ÷Byůޤ3CwöRN6żÔHŐH)kgęm ĐGJ¨Ěq—őçČ…AąVčLqB×ńň %Ű<áčQěµhE¶q˝âŇCPH‘`ĺĄÄőÚĚÜĂĚ+‰ó_”ń:Oěşôž+Ť0 ßý"ďőć}f¸ś/‘ç„<ŻgŰgŇ Zŕ–´Čę< ÷ę}b`¨Ţ3ĺľ }LS“SÖÖ´—„jżuźź8No{9Q8/)9;<·©ŔÓé>—łĎáSĆęJÇzÚŔFLgY×›Ű'^L”0%P”ęnYń0ĹĘžcËó~¤žłÂçjf9H˝CX߉Qö]—@¶zß—Ú_V°’µ-2’ÇÎ`áç95śĐŘħyĘĘÇV;9ÂMDX ŠŠt3=í{jăü@:c‚$ˇÚb“eT’XŇÄôóvÚjĘĚĹEZ%Ęb>đť‘©­~¸ąśšĂÚS‘H­ßŞŁEĽw5. ­ ńíÖS´®dŤŢÜ‹¸ Ë­qĄ]˝»FOăJ°ÍGĐď AE#ę‹Ő±1]wů&ÄśíXµ C»ťőéw®É0©mpŐH§áÉń”ĽŠu?˙>rż5ţŞ''ň°t6cdŁePą©Łč))Ő ąRŮTJ2ą!ńZgue:©ş“gë \ßagÍŘŃ>°°<TČôHqx:D›Ş6 ă 89«Ež8 ŻÍ‰˘Öľ^'­/ŞĽN„Ô%köŚ.Gö›”hEęŞKŔëé(­IIŘ2ŘV­·'SČu» !¬Ĺc żEčŤ5<~B5s’Ľ˛Á‰˙(ß=v\†÷Śş6ŚŠęĚëŔd(Ŕ۶É#zÚkSrŻ_<űŠűŐĺm˙[ĽŽzΉZMĘ^é5Ś":®Oű-lĆ’ę%ńw4}ÁŤđecúŇeAŔ\b ‚šÚG›f÷đŢ1¦/šŹ‘ULŁ%¤ťŐ3Żí•ɧć’Ęw®Ó.Ldg3‘:šŠ±Ô`^6H)=ÉJŇý”ŃŚ†|b9©˘%ÎÉVş4rĚč÷«ě˝/UČs Öl«’ŕ™÷fĚH}j޸Öúݎ]­ń+l^ŹW`žŢMü”蛲“`ÇęaMEÄ®S§Z]ů^Ś${ő†' Ůý ´˙~Ü~ż|“ŢK5ŐĹ0!uSW2OYČą8–IŠg–Ć5wČě­̱ȗ‚ˇr)Á9Ăw%Ź\|x†ŢˇłEn5ÇT˘ěťţ›I’řhEő˘…Ř×öu­Jn *Źh·Ş6n™ĺŐ§[…, ]ÜA+yÔ :~ÄD@5~ĺVďLÝň3—‰˙{IţÔ)îZńęŰş~áů„ m×›špĄn@k.»S,¶«őĐv1Hľ€*AáÉ«'EĆK‰˛óË0ń2 Şć!Í©pf±d€FY/O`źńóZ§řŁď\QN°•Ľ=iuđ9!ÁôĚc‡Ężţ:n0űălt¬jéжĐHrĘĄZÓ®°Ťi}(ČéŠ0 ¶ů- 5‡8lÄěč#şF]†m pćGĘgm[Šđj–§ŃüČĂj‚†ăň“ŇBů…&Ý9 ôˇ54ôčéDŽőAÄ•źüŠ3á%Ęgč=ÝßCŘ@”ah»9šű1őiíI—ńľÉIde–jĺ~ńť8•«2ä”yżżĄ–ĂőŰYV°ÖYS†ČŮ‹Â(ŕ¸UOĹ,ĹK·OU,˛)Ă#=ô#’ţ\üŰSAYRYôIő6ţHx˘Đb›úçĂÁ¨ómŔ“RˇO9|űöą§ČąĺîŃ«*ašU?;‘ănXw[ŃÜ# AŽŮôwCpĚééL“ZňdjÔŚ˙ çó ş¨ťř¦făźžÍ_GM Ý2%›Ť(3Ť’žô¶ÂÜv‘ÎS—Ö=TcH˘›ńjeÔý~KCö ¨Ánź[Z®W”éĽ6ڧąu5rÁa™M¬Ľ1h3Ż⡠2Ňq×4ui¬Ďh˙?î«ci•Ň’ uQNRs€špŇým;Ë;3Oú"W`îbł<˘ÝÖbL,Ż `żXŠíraí™ ĺďĚFK®j|¦/ó÷š)Ĺ4Ü%dŻaÇRbőGâřA]´:‹p·ebmP«†Sň{’• )hJ ď„ÇŹÎŹ}·˘pޑִ+Üy\6řýäp `IČöbą/—`‡-Ęv/s%µŘ°·ŽsÉNö«ö?‰fšŔĺ÷4DˇR˝ĂäĚ]í¨đë0˘¤Á&j-í5Ü;ď׌ŐÖě2ch’Ϋ#ʢěi&f ŕv=ŽF Ńa˙béŻ/Ł` đ,LO}ÄÖ'ö0"ť5š’“‚ë7'5µ±w[XS  ~ńÎuýň°˝ľŚ*˘řľâíßŠŁ¦Łŕ¨ = <Ăč9žěĹýCeÚ7Vđ^׫k4¤;Ŕ¬©)ęťŮ˛‡oŘ7=ÚéÂ:-P|6Ö>®ďW ř¶zäŔµů¸o 6iH¬B)Ň‚1ĘGds_Gäëlnňűý^¨Xö\?*Efâţ’vKµ}JöÓ¦ÓŚ“ŻNÎĹZ0Ą-¤µş‘"0ŤiÝBQD…€¬J2őĘŰĐŻ[Őî}9*v SJ{Qpw5 j)ąWľ1ű˘Ołĺ›ĐvÇŞ9őGXy•›»É®p17p<Ď_>77>µ}”g ńý§Ž|÷»ęGýŁ'íj˙4eiwíC?joÚaűE˙m®/©\_QtźÚyűG´wö pżPëý´ßía´ľKő—ę!Ĺt+`ť0@Ű~ PćÚđxŃŤĄë®ôM¤Dö=Ť‰ä6”)F±8”—3_î‹‘šjCą5’‰¨‡ ť AÂşs%ŚŻm’ŁŐlÜ95š/Z=Ĺ#8i˘;»•#¶?—•oŔTćÄx=ą•ňTú +vÎPż(L™«c2{ęu:ťNR™Ć@i˛íĄa±Ž ~ňL]I‰k™=“bJĺ8čď ěsFďő—đ ĄgDŰořca‰xĽˇëž.9a+’ěŢz)™żÖL @úfˇđťÉ#Dg¨Ko^°h‚x*źŞ—Çúźh>; žŠĵ/‘™1O˙,gpeý‘h„EęĹŠŮ®Ćyn‚•ĄPáşíţ/ě/´€É¨®ePć„sÖ ŔÝü‰©„WşÇŠrűTŕŠ$ÉŮŃXđç„ëÓłVŹ&ź|á€ń‚]}ŮČY†˙y”€4mTQĚŘl6 †Ăa°Řl5­° Dpę,‘™1O˙,gpeý‘h„EęŊٮƄţ1Ď˙.ő ´ÂBBL—ŠbHKčĘ 0˘„I†-nľo›ćůľo›ćůľo›‘™1OňăľĎ)aK±qé’$d!J’‘ĽđéZ˛LŃ& ł ‚űYřHK&3gBŰŽEÖUË=d4ŇIDíE€Îü‡„ţ1ĎňćRź· ĺÚŔ„’mnUXH:hŔ%-qâ˙9›-X ¦Đ [jű^ňĹJ‚ťL˘©'˝¬a`? ŰŻ›ćůľo›ć¨9N’ńŞEŚďť•€aĄţ6÷řý±qV{ŇcâެR;Ź˝+6zŁ—ôŞ.Ţ –Ő Ń~÷řŁßŔ $$—Ź"1ÄĘ©ÔÎ9X§0ľ®ŔťRý őŠ˝{˝‘Nš~ąx/`B×-A×|yšĂ˛€ÍP‘›˘}7pó˙{´ĚşcKţĹÝűDŞPýµ„Ŕ%˙sP˛Úfei­˛’aëƆ‘éŃäín‚ŕ|†ăĽwŽńŢ÷µyhn) Ři<ÁŮRz]~ýąĽ17ęćýoźŔů°ôqtL őz÷ĚS-ČlŘ#h˝ jčú ivén–én–Ú©^SŢřdźuŮh3C[š1ů÷>8Óc}„ieß2ž ŕrô†ĹWLí” Ľ]áeíH#B݆ŇÓĆ—S„{Ív§ĺŔ«ŹáĐj1ŤkPŘ­ŰK·K¸0»ŇGOv€¦~ '¤}÷ \5Ă5‡~ [ěalŚ+‘}' AP¤ĄŢ"FŞŘć{ŃóŰĐőM`©Ď1ŢÄęŻĺŹŠsáë;` €úBŮ©čl0›0?ęĆčeXţÍę‰2vݰÔEżÜ~Ç”ô Ş:#–.@ $%—HJ$±ŃŁĂŇô Ş:#–.@ $%—HJ$±ŃŁĂŇô ¨^źuĄžŐ˛H "±-Â<Ęťmٵs§ăg…H¨0—b¤ĺ+"aÜŃK5¬r:Ś`ąű=[¦Ž„§‘ü RYŐ$4JKĘ—1¶˛ŽôaOÓŚ5‚/ÔoÉËđĺQ •JĄRޤŁ<0˙Ó;U]˛2ÚaĚHÚĎČw|(2›|ćAŽČ4¶ň\ŰؤđŤÜUjş2‹ęč)†ąęr-"‰DĽvŤSŞXÂâ=¸ŮŽž ÖEÇ…c88 ^[Ćąg‘aŕ‡ÜѶć6^ă¶]`kůKSă«cťçŃ˙hdť6 I’hěříéŤÇ.Őřî7†aâ^ l¨*Ćšu#3,]§Żě UęË 1J+Ęţ‚JVőěÖľ ás»s4Tr5ăľ6É/jSůÂ[xČÉÇMažťĎÍúá8Ń}řÎ/dÓxI)JLĹoÍSYr Y@‰¨{~Źäą™!B&Édh·>Şí›¸Ot&őH"%şŇ·Ţ]­ď2/çS•”˘±Ń“Ź""n-HüLhwIBx»^Xaő%É|4ßýD pšfX¶ˇ$čęÂ$0´_úٲý™‚O˙ŮicnV ż€xscreensaver-5.15/OSX/XScreenSaver.plist000066400000000000000000000016651164314150500202620ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier org.jwz.xscreensaver.${EXECUTABLE_NAME} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType BNDL CFBundleShortVersionString 5.15 CFBundleSignature ???? CFBundleVersion 5.15 LSMinimumSystemVersion 10.4 NSMainNibFile SaverRunner NSPrincipalClass XScreenSaver${EXECUTABLE_NAME}View xscreensaver-5.15/OSX/XScreenSaverConfigSheet.h000066400000000000000000000031301164314150500214620ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 2006 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* XScreenSaver uses XML files to describe the user interface for configuring the various screen savers. These files live in .../hacks/config/ and say relatively high level things like: "there should be a checkbox labelled "Leave Trails", and when it is checked, add the option '-trails' to the command line when launching the program." This code reads that XML and constructs a Cocoa interface from it. The Cocoa controls are hooked up to NSUserDefaultsController to save those settings into the MacOS preferences system. The Cocoa preferences names are the same as the resource names specified in the screenhack's 'options' array (we use that array to map the command line switches specified in the XML to the resource names to use). */ #import #import "jwxyz.h" @interface XScreenSaverConfigSheet : NSWindow { NSUserDefaultsController *userDefaultsController; } - (id)initWithXMLFile: (NSString *) xml_file options: (const XrmOptionDescRec *) opts controller: (NSUserDefaultsController *) prefs; @end xscreensaver-5.15/OSX/XScreenSaverConfigSheet.m000066400000000000000000001646541164314150500215120ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 2006-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* XScreenSaver uses XML files to describe the user interface for configuring the various screen savers. These files live in .../hacks/config/ and say relatively high level things like: "there should be a checkbox labelled "Leave Trails", and when it is checked, add the option '-trails' to the command line when launching the program." This code reads that XML and constructs a Cocoa interface from it. The Cocoa controls are hooked up to NSUserDefaultsController to save those settings into the MacOS preferences system. The Cocoa preferences names are the same as the resource names specified in the screenhack's 'options' array (we use that array to map the command line switches specified in the XML to the resource names to use). */ #import "XScreenSaverConfigSheet.h" #import "jwxyz.h" #import "InvertedSlider.h" #import @implementation XScreenSaverConfigSheet #define LEFT_MARGIN 20 // left edge of window #define COLUMN_SPACING 10 // gap between e.g. labels and text fields #define LEFT_LABEL_WIDTH 70 // width of all left labels #define LINE_SPACING 10 // leading between each line // redefine these since they don't work when not inside an ObjC method #undef NSAssert #undef NSAssert1 #undef NSAssert2 #undef NSAssert3 #define NSAssert(CC,S) do { if (!(CC)) { NSLog(S); }} while(0) #define NSAssert1(CC,S,A) do { if (!(CC)) { NSLog(S,A); }} while(0) #define NSAssert2(CC,S,A,B) do { if (!(CC)) { NSLog(S,A,B); }} while(0) #define NSAssert3(CC,S,A,B,C) do { if (!(CC)) { NSLog(S,A,B,C); }} while(0) /* Given a command-line option, returns the corresponding resource name. Any arguments in the switch string are ignored (e.g., "-foo x"). */ static NSString * switch_to_resource (NSString *cmdline_switch, const XrmOptionDescRec *opts, NSString **val_ret) { char buf[255]; char *tail = 0; NSAssert(cmdline_switch, @"cmdline switch is null"); if (! [cmdline_switch getCString:buf maxLength:sizeof(buf) encoding:NSUTF8StringEncoding]) { NSAssert1(0, @"unable to convert %@", cmdline_switch); abort(); } char *s = strpbrk(buf, " \t\r\n"); if (s && *s) { *s = 0; tail = s+1; while (*tail && (*tail == ' ' || *tail == '\t')) tail++; } while (opts[0].option) { if (!strcmp (opts[0].option, buf)) { const char *ret = 0; if (opts[0].argKind == XrmoptionNoArg) { if (tail && *tail) NSAssert1 (0, @"expected no args to switch: \"%@\"", cmdline_switch); ret = opts[0].value; } else { if (!tail || !*tail) NSAssert1 (0, @"expected args to switch: \"%@\"", cmdline_switch); ret = tail; } if (val_ret) *val_ret = (ret ? [NSString stringWithCString:ret encoding:NSUTF8StringEncoding] : 0); const char *res = opts[0].specifier; while (*res && (*res == '.' || *res == '*')) res++; return [NSString stringWithCString:res encoding:NSUTF8StringEncoding]; } opts++; } NSAssert1 (0, @"\"%@\" not present in options", cmdline_switch); abort(); } /* Connects a control (checkbox, etc) to the corresponding preferences key. */ static void bind_resource_to_preferences (NSUserDefaultsController *prefs, NSObject *control, NSString *pref_key, const XrmOptionDescRec *opts) { NSString *bindto = ([control isKindOfClass:[NSPopUpButton class]] ? @"selectedObject" : ([control isKindOfClass:[NSMatrix class]] ? @"selectedIndex" : @"value")); [control bind:bindto toObject:prefs withKeyPath:[@"values." stringByAppendingString: pref_key] options:nil]; # if 0 // #### NSObject *def = [[prefs defaults] objectForKey:pref_key]; NSString *s = [NSString stringWithFormat:@"bind: \"%@\"", pref_key]; s = [s stringByPaddingToLength:18 withString:@" " startingAtIndex:0]; s = [NSString stringWithFormat:@"%@ = \"%@\"", s, def]; s = [s stringByPaddingToLength:28 withString:@" " startingAtIndex:0]; NSLog (@"%@ %@/%@", s, [def class], [control class]); # endif } static void bind_switch_to_preferences (NSUserDefaultsController *prefs, NSObject *control, NSString *cmdline_switch, const XrmOptionDescRec *opts) { NSString *pref_key = switch_to_resource (cmdline_switch, opts, 0); bind_resource_to_preferences (prefs, control, pref_key, opts); } /* Parse the attributes of an XML tag into a dictionary. For input, the dictionary should have as attributes the keys, each with @"" as their value. On output, the dictionary will set the keys to the values specified, and keys that were not specified will not be present in the dictionary. Warnings are printed if there are duplicate or unknown attributes. */ static void parse_attrs (NSMutableDictionary *dict, NSXMLNode *node) { NSArray *attrs = [(NSXMLElement *) node attributes]; int n = [attrs count]; int i; // For each key in the dictionary, fill in the dict with the corresponding // value. The value @"" is assumed to mean "un-set". Issue a warning if // an attribute is specified twice. // for (i = 0; i < n; i++) { NSXMLNode *attr = [attrs objectAtIndex:i]; NSString *key = [attr name]; NSString *val = [attr objectValue]; NSString *old = [dict objectForKey:key]; if (! old) { NSAssert2 (0, @"unknown attribute \"%@\" in \"%@\"", key, [node name]); } else if ([old length] != 0) { NSAssert3 (0, @"duplicate %@: \"%@\", \"%@\"", key, old, val); } else { [dict setValue:val forKey:key]; } } // Remove from the dictionary any keys whose value is still @"", // meaning there was no such attribute specified. // NSArray *keys = [dict allKeys]; n = [keys count]; for (i = 0; i < n; i++) { NSString *key = [keys objectAtIndex:i]; NSString *val = [dict objectForKey:key]; if ([val length] == 0) [dict removeObjectForKey:key]; } } /* Creates a label: an un-editable NSTextField displaying the given text. */ static NSTextField * make_label (NSString *text) { NSRect rect; rect.origin.x = rect.origin.y = 0; rect.size.width = rect.size.height = 10; NSTextField *lab = [[NSTextField alloc] initWithFrame:rect]; [lab setSelectable:NO]; [lab setEditable:NO]; [lab setBezeled:NO]; [lab setDrawsBackground:NO]; [lab setStringValue:text]; [lab sizeToFit]; return lab; } static NSView * last_child (NSView *parent) { NSArray *kids = [parent subviews]; int nkids = [kids count]; if (nkids == 0) return 0; else return [kids objectAtIndex:nkids-1]; } /* Add the child as a subview of the parent, positioning it immediately below or to the right of the previously-added child of that view. */ static void place_child (NSView *parent, NSView *child, BOOL right_p) { NSRect rect = [child frame]; NSView *last = last_child (parent); if (!last) { rect.origin.x = LEFT_MARGIN; rect.origin.y = [parent frame].size.height - rect.size.height - LINE_SPACING; } else if (right_p) { rect = [last frame]; rect.origin.x += rect.size.width + COLUMN_SPACING; } else { rect = [last frame]; rect.origin.x = LEFT_MARGIN; rect.origin.y -= [child frame].size.height + LINE_SPACING; } [child setFrameOrigin:rect.origin]; [parent addSubview:child]; } static void traverse_children (NSUserDefaultsController *, const XrmOptionDescRec *, NSView *, NSXMLNode *); /* Creates the checkbox (NSButton) described by the given XML node. */ static void make_checkbox (NSUserDefaultsController *prefs, const XrmOptionDescRec *opts, NSView *parent, NSXMLNode *node) { NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"", @"id", @"", @"_label", @"", @"arg-set", @"", @"arg-unset", nil]; parse_attrs (dict, node); NSString *label = [dict objectForKey:@"_label"]; NSString *arg_set = [dict objectForKey:@"arg-set"]; NSString *arg_unset = [dict objectForKey:@"arg-unset"]; if (!label) { NSAssert1 (0, @"no _label in %@", [node name]); return; } if (!arg_set && !arg_unset) { NSAssert1 (0, @"neither arg-set nor arg-unset provided in \"%@\"", label); } if (arg_set && arg_unset) { NSAssert1 (0, @"only one of arg-set and arg-unset may be used in \"%@\"", label); } // sanity-check the choice of argument names. // if (arg_set && ([arg_set hasPrefix:@"-no-"] || [arg_set hasPrefix:@"--no-"])) NSLog (@"arg-set should not be a \"no\" option in \"%@\": %@", label, arg_set); if (arg_unset && (![arg_unset hasPrefix:@"-no-"] && ![arg_unset hasPrefix:@"--no-"])) NSLog(@"arg-unset should be a \"no\" option in \"%@\": %@", label, arg_unset); NSRect rect; rect.origin.x = rect.origin.y = 0; rect.size.width = rect.size.height = 10; NSButton *button = [[NSButton alloc] initWithFrame:rect]; [button setButtonType:([[node name] isEqualToString:@"radio"] ? NSRadioButton : NSSwitchButton)]; [button setTitle:label]; [button sizeToFit]; place_child (parent, button, NO); bind_switch_to_preferences (prefs, button, (arg_set ? arg_set : arg_unset), opts); [button release]; } /* Creates the NSTextField described by the given XML node. */ static void make_text_field (NSUserDefaultsController *prefs, const XrmOptionDescRec *opts, NSView *parent, NSXMLNode *node, BOOL no_label_p) { NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"", @"id", @"", @"_label", @"", @"arg", nil]; parse_attrs (dict, node); NSString *label = [dict objectForKey:@"_label"]; NSString *arg = [dict objectForKey:@"arg"]; if (!label && !no_label_p) { NSAssert1 (0, @"no _label in %@", [node name]); return; } NSAssert1 (arg, @"no arg in %@", label); NSRect rect; rect.origin.x = rect.origin.y = 0; rect.size.width = rect.size.height = 10; NSTextField *txt = [[NSTextField alloc] initWithFrame:rect]; // make the default size be around 30 columns; a typical value for // these text fields is "xscreensaver-text --cols 40". // [txt setStringValue:@"123456789 123456789 123456789 "]; [txt sizeToFit]; [[txt cell] setWraps:NO]; [[txt cell] setScrollable:YES]; [txt setStringValue:@""]; if (label) { NSTextField *lab = make_label (label); place_child (parent, lab, NO); [lab release]; } place_child (parent, txt, (label ? YES : NO)); bind_switch_to_preferences (prefs, txt, arg, opts); [txt release]; } /* Creates the NSTextField described by the given XML node, and hooks it up to a Choose button and a file selector widget. */ static void make_file_selector (NSUserDefaultsController *prefs, const XrmOptionDescRec *opts, NSView *parent, NSXMLNode *node, BOOL dirs_only_p, BOOL no_label_p, BOOL editable_text_p) { NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"", @"id", @"", @"_label", @"", @"arg", nil]; parse_attrs (dict, node); NSString *label = [dict objectForKey:@"_label"]; NSString *arg = [dict objectForKey:@"arg"]; if (!label && !no_label_p) { NSAssert1 (0, @"no _label in %@", [node name]); return; } NSAssert1 (arg, @"no arg in %@", label); NSRect rect; rect.origin.x = rect.origin.y = 0; rect.size.width = rect.size.height = 10; NSTextField *txt = [[NSTextField alloc] initWithFrame:rect]; // make the default size be around 20 columns. // [txt setStringValue:@"123456789 123456789 "]; [txt sizeToFit]; [txt setSelectable:YES]; [txt setEditable:editable_text_p]; [txt setBezeled:editable_text_p]; [txt setDrawsBackground:editable_text_p]; [[txt cell] setWraps:NO]; [[txt cell] setScrollable:YES]; [[txt cell] setLineBreakMode:NSLineBreakByTruncatingHead]; [txt setStringValue:@""]; NSTextField *lab = 0; if (label) { lab = make_label (label); place_child (parent, lab, NO); [lab release]; } place_child (parent, txt, (label ? YES : NO)); bind_switch_to_preferences (prefs, txt, arg, opts); [txt release]; // Make the text field and label be the same height, whichever is taller. if (lab) { rect = [txt frame]; rect.size.height = ([lab frame].size.height > [txt frame].size.height ? [lab frame].size.height : [txt frame].size.height); [txt setFrame:rect]; } // Now put a "Choose" button next to it. // rect.origin.x = rect.origin.y = 0; rect.size.width = rect.size.height = 10; NSButton *choose = [[NSButton alloc] initWithFrame:rect]; [choose setTitle:@"Choose..."]; [choose setBezelStyle:NSRoundedBezelStyle]; [choose sizeToFit]; place_child (parent, choose, YES); // center the Choose button around the midpoint of the text field. rect = [choose frame]; rect.origin.y = ([txt frame].origin.y + (([txt frame].size.height - rect.size.height) / 2)); [choose setFrameOrigin:rect.origin]; [choose setTarget:[parent window]]; if (dirs_only_p) [choose setAction:@selector(chooseClickedDirs:)]; else [choose setAction:@selector(chooseClicked:)]; [choose release]; } /* Runs a modal file selector and sets the text field's value to the selected file or directory. */ static void do_file_selector (NSTextField *txt, BOOL dirs_p) { NSOpenPanel *panel = [NSOpenPanel openPanel]; [panel setAllowsMultipleSelection:NO]; [panel setCanChooseFiles:!dirs_p]; [panel setCanChooseDirectories:dirs_p]; NSString *file = [txt stringValue]; if ([file length] <= 0) { file = NSHomeDirectory(); if (dirs_p) file = [file stringByAppendingPathComponent:@"Pictures"]; } // NSString *dir = [file stringByDeletingLastPathComponent]; int result = [panel runModalForDirectory:file //dir file:nil //[file lastPathComponent] types:nil]; if (result == NSOKButton) { NSArray *files = [panel filenames]; file = ([files count] > 0 ? [files objectAtIndex:0] : @""); file = [file stringByAbbreviatingWithTildeInPath]; [txt setStringValue:file]; // Fuck me! Just setting the value of the NSTextField does not cause // that to end up in the preferences! // NSDictionary *dict = [txt infoForBinding:@"value"]; NSUserDefaultsController *prefs = [dict objectForKey:@"NSObservedObject"]; NSString *path = [dict objectForKey:@"NSObservedKeyPath"]; if ([path hasPrefix:@"values."]) // WTF. path = [path substringFromIndex:7]; [[prefs values] setValue:file forKey:path]; #if 0 // make sure the end of the string is visible. NSText *fe = [[txt window] fieldEditor:YES forObject:txt]; NSRange range; range.location = [file length]-3; range.length = 1; if (! [[txt window] makeFirstResponder:[txt window]]) [[txt window] endEditingFor:nil]; // [[txt window] makeFirstResponder:nil]; [fe setSelectedRange:range]; // [tv scrollRangeToVisible:range]; // [txt setNeedsDisplay:YES]; // [[txt cell] setNeedsDisplay:YES]; // [txt selectAll:txt]; #endif } } /* Returns the NSTextField that is to the left of or above the NSButton. */ static NSTextField * find_text_field_of_button (NSButton *button) { NSView *parent = [button superview]; NSArray *kids = [parent subviews]; int nkids = [kids count]; int i; NSTextField *f = 0; for (i = 0; i < nkids; i++) { NSObject *kid = [kids objectAtIndex:i]; if ([kid isKindOfClass:[NSTextField class]]) { f = (NSTextField *) kid; } else if (kid == button) { if (! f) abort(); return f; } } abort(); } - (void) chooseClicked:(NSObject *)arg { NSButton *choose = (NSButton *) arg; NSTextField *txt = find_text_field_of_button (choose); do_file_selector (txt, NO); } - (void) chooseClickedDirs:(NSObject *)arg { NSButton *choose = (NSButton *) arg; NSTextField *txt = find_text_field_of_button (choose); do_file_selector (txt, YES); } /* Creates the number selection control described by the given XML node. If "type=slider", it's an NSSlider. If "type=spinbutton", it's a text field with up/down arrows next to it. */ static void make_number_selector (NSUserDefaultsController *prefs, const XrmOptionDescRec *opts, NSView *parent, NSXMLNode *node) { NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"", @"id", @"", @"_label", @"", @"_low-label", @"", @"_high-label", @"", @"type", @"", @"arg", @"", @"low", @"", @"high", @"", @"default", @"", @"convert", nil]; parse_attrs (dict, node); NSString *label = [dict objectForKey:@"_label"]; NSString *low_label = [dict objectForKey:@"_low-label"]; NSString *high_label = [dict objectForKey:@"_high-label"]; NSString *type = [dict objectForKey:@"type"]; NSString *arg = [dict objectForKey:@"arg"]; NSString *low = [dict objectForKey:@"low"]; NSString *high = [dict objectForKey:@"high"]; NSString *def = [dict objectForKey:@"default"]; NSString *cvt = [dict objectForKey:@"convert"]; NSAssert1 (arg, @"no arg in %@", label); NSAssert1 (type, @"no type in %@", label); if (! low) { NSAssert1 (0, @"no low in %@", [node name]); return; } if (! high) { NSAssert1 (0, @"no high in %@", [node name]); return; } if (! def) { NSAssert1 (0, @"no default in %@", [node name]); return; } if (cvt && ![cvt isEqualToString:@"invert"]) { NSAssert1 (0, @"if provided, \"convert\" must be \"invert\" in %@", label); } // If either the min or max field contains a decimal point, then this // option may have a floating point value; otherwise, it is constrained // to be an integer. // NSCharacterSet *dot = [NSCharacterSet characterSetWithCharactersInString:@"."]; BOOL float_p = ([low rangeOfCharacterFromSet:dot].location != NSNotFound || [high rangeOfCharacterFromSet:dot].location != NSNotFound); if ([type isEqualToString:@"slider"]) { NSRect rect; rect.origin.x = rect.origin.y = 0; rect.size.width = 150; rect.size.height = 23; // apparent min height for slider with ticks... NSSlider *slider; if (cvt) slider = [[InvertedSlider alloc] initWithFrame:rect]; else slider = [[NSSlider alloc] initWithFrame:rect]; [slider setMaxValue:[high doubleValue]]; [slider setMinValue:[low doubleValue]]; int range = [slider maxValue] - [slider minValue] + 1; int range2 = range; int max_ticks = 21; while (range2 > max_ticks) range2 /= 10; // If we have elided ticks, leave it at the max number of ticks. if (range != range2 && range2 < max_ticks) range2 = max_ticks; // If it's a float, always display the max number of ticks. if (float_p && range2 < max_ticks) range2 = max_ticks; [slider setNumberOfTickMarks:range2]; [slider setAllowsTickMarkValuesOnly: (range == range2 && // we are showing the actual number of ticks !float_p)]; // and we want integer results // #### Note: when the slider's range is large enough that we aren't // showing all possible ticks, the slider's value is not constrained // to be an integer, even though it should be... // Maybe we need to use a value converter or something? if (label) { NSTextField *lab = make_label (label); place_child (parent, lab, NO); [lab release]; } if (low_label) { NSTextField *lab = make_label (low_label); [lab setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; [lab setAlignment:1]; // right aligned rect = [lab frame]; if (rect.size.width < LEFT_LABEL_WIDTH) rect.size.width = LEFT_LABEL_WIDTH; // make all left labels same size rect.size.height = [slider frame].size.height; [lab setFrame:rect]; place_child (parent, lab, NO); [lab release]; } place_child (parent, slider, (low_label ? YES : NO)); if (! low_label) { rect = [slider frame]; if (rect.origin.x < LEFT_LABEL_WIDTH) rect.origin.x = LEFT_LABEL_WIDTH; // make unlabelled sliders line up too [slider setFrame:rect]; } if (high_label) { NSTextField *lab = make_label (high_label); [lab setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; rect = [lab frame]; rect.size.height = [slider frame].size.height; [lab setFrame:rect]; place_child (parent, lab, YES); [lab release]; } bind_switch_to_preferences (prefs, slider, arg, opts); [slider release]; } else if ([type isEqualToString:@"spinbutton"]) { if (! label) { NSAssert1 (0, @"no _label in spinbutton %@", [node name]); return; } NSAssert1 (!low_label, @"low-label not allowed in spinbutton \"%@\"", [node name]); NSAssert1 (!high_label, @"high-label not allowed in spinbutton \"%@\"", [node name]); NSAssert1 (!cvt, @"convert not allowed in spinbutton \"%@\"", [node name]); NSRect rect; rect.origin.x = rect.origin.y = 0; rect.size.width = rect.size.height = 10; NSTextField *txt = [[NSTextField alloc] initWithFrame:rect]; [txt setStringValue:@"0000.0"]; [txt sizeToFit]; [txt setStringValue:@""]; if (label) { NSTextField *lab = make_label (label); //[lab setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; [lab setAlignment:1]; // right aligned rect = [lab frame]; if (rect.size.width < LEFT_LABEL_WIDTH) rect.size.width = LEFT_LABEL_WIDTH; // make all left labels same size rect.size.height = [txt frame].size.height; [lab setFrame:rect]; place_child (parent, lab, NO); [lab release]; } place_child (parent, txt, (label ? YES : NO)); if (! label) { rect = [txt frame]; if (rect.origin.x < LEFT_LABEL_WIDTH) rect.origin.x = LEFT_LABEL_WIDTH; // make unlabelled spinbtns line up [txt setFrame:rect]; } rect.size.width = rect.size.height = 10; NSStepper *step = [[NSStepper alloc] initWithFrame:rect]; [step sizeToFit]; place_child (parent, step, YES); rect = [step frame]; rect.origin.x -= COLUMN_SPACING; // this one goes close rect.origin.y += ([txt frame].size.height - rect.size.height) / 2; [step setFrame:rect]; [step setMinValue:[low doubleValue]]; [step setMaxValue:[high doubleValue]]; [step setAutorepeat:YES]; [step setValueWraps:NO]; double range = [high doubleValue] - [low doubleValue]; if (range < 1.0) [step setIncrement:range / 10.0]; else if (range >= 500) [step setIncrement:range / 100.0]; else [step setIncrement:1.0]; NSNumberFormatter *fmt = [[[NSNumberFormatter alloc] init] autorelease]; [fmt setFormatterBehavior:NSNumberFormatterBehavior10_4]; [fmt setNumberStyle:NSNumberFormatterDecimalStyle]; [fmt setMinimum:[NSNumber numberWithDouble:[low doubleValue]]]; [fmt setMaximum:[NSNumber numberWithDouble:[high doubleValue]]]; [fmt setMinimumFractionDigits: (float_p ? 1 : 0)]; [fmt setMaximumFractionDigits: (float_p ? 2 : 0)]; [fmt setGeneratesDecimalNumbers:float_p]; [[txt cell] setFormatter:fmt]; bind_switch_to_preferences (prefs, step, arg, opts); bind_switch_to_preferences (prefs, txt, arg, opts); [step release]; [txt release]; } else { NSAssert2 (0, @"unknown type \"%@\" in \"%@\"", type, label); } } static void set_menu_item_object (NSMenuItem *item, NSObject *obj) { /* If the object associated with this menu item looks like a boolean, store an NSNumber instead of an NSString, since that's what will be in the preferences (due to similar logic in PrefsReader). */ if ([obj isKindOfClass:[NSString class]]) { NSString *string = (NSString *) obj; if (NSOrderedSame == [string caseInsensitiveCompare:@"true"] || NSOrderedSame == [string caseInsensitiveCompare:@"yes"]) obj = [NSNumber numberWithBool:YES]; else if (NSOrderedSame == [string caseInsensitiveCompare:@"false"] || NSOrderedSame == [string caseInsensitiveCompare:@"no"]) obj = [NSNumber numberWithBool:NO]; else obj = string; } [item setRepresentedObject:obj]; //NSLog (@"menu item \"%@\" = \"%@\" %@", [item title], obj, [obj class]); } /* Creates the popup menu described by the given XML node (and its children). */ static void make_option_menu (NSUserDefaultsController *prefs, const XrmOptionDescRec *opts, NSView *parent, NSXMLNode *node) { NSArray *children = [node children]; int i, count = [children count]; if (count <= 0) { NSAssert1 (0, @"no menu items in \"%@\"", [node name]); return; } // get the "id" attribute off the \n"); } else if (p->type == DESCRIPTION) { if (p->string) fprintf (out, " %s\n", p->string); fprintf (out, "\n"); } } #endif /* 0 */ /* Like xmlGetProp() but parses a float out of the string. If the number was expressed as a float and not an integer (that is, the string contained a decimal point) then `floatp' is set to TRUE. Otherwise, it is unchanged. */ static float xml_get_float (xmlNodePtr node, const xmlChar *name, gboolean *floatpP) { const char *s = (char *) xmlGetProp (node, name); float f; char c; if (!s || 1 != sscanf (s, "%f %c", &f, &c)) return 0; else { if (strchr (s, '.')) *floatpP = TRUE; return f; } } static void sanity_check_parameter (const char *filename, const xmlChar *node_name, parameter *p); static void sanity_check_text_node (const char *filename, const xmlNodePtr node); static void sanity_check_menu_options (const char *filename, const xmlChar *node_name, parameter *p); /* Allocates and returns a new `parameter' object based on the properties in the given XML node. Returns 0 if there's nothing to create (comment, or unknown tag.) */ static parameter * make_parameter (const char *filename, xmlNodePtr node) { parameter *p; const char *name = (char *) node->name; const char *convert; gboolean floatp = FALSE; if (node->type == XML_COMMENT_NODE) return 0; p = calloc (1, sizeof(*p)); if (!name) abort(); else if (!strcmp (name, "command")) p->type = COMMAND; else if (!strcmp (name, "fullcommand")) p->type = COMMAND; else if (!strcmp (name, "_description")) p->type = DESCRIPTION; else if (!strcmp (name, "fakepreview")) p->type = FAKEPREVIEW; else if (!strcmp (name, "fake")) p->type = FAKE; else if (!strcmp (name, "boolean")) p->type = BOOLEAN; else if (!strcmp (name, "string")) p->type = STRING; else if (!strcmp (name, "file")) p->type = FILENAME; else if (!strcmp (name, "number")) p->type = SPINBUTTON; else if (!strcmp (name, "select")) p->type = SELECT; else if (!strcmp (name, "xscreensaver-text") || /* these are ignored in X11 */ !strcmp (name, "xscreensaver-image")) /* (they are used in Cocoa) */ { free (p); return 0; } else if (node->type == XML_TEXT_NODE) { sanity_check_text_node (filename, node); free (p); return 0; } else { if (debug_p) fprintf (stderr, "%s: WARNING: %s: unknown tag: \"%s\"\n", blurb(), filename, name); free (p); return 0; } if (p->type == SPINBUTTON) { const char *type = (char *) xmlGetProp (node, (xmlChar *) "type"); if (!type || !strcmp (type, "spinbutton")) p->type = SPINBUTTON; else if (!strcmp (type, "slider")) p->type = SLIDER; else { if (debug_p) fprintf (stderr, "%s: WARNING: %s: unknown %s type: \"%s\"\n", blurb(), filename, name, type); free (p); return 0; } } else if (p->type == DESCRIPTION) { if (node->xmlChildrenNode && node->xmlChildrenNode->type == XML_TEXT_NODE && !node->xmlChildrenNode->next) p->string = (xmlChar *) strdup ((char *) node->xmlChildrenNode->content); } p->id = xmlGetProp (node, (xmlChar *) "id"); p->label = xmlGetProp (node, (xmlChar *) "_label"); p->low_label = xmlGetProp (node, (xmlChar *) "_low-label"); p->high_label = xmlGetProp (node, (xmlChar *) "_high-label"); p->low = xml_get_float (node, (xmlChar *) "low", &floatp); p->high = xml_get_float (node, (xmlChar *) "high", &floatp); p->value = xml_get_float (node, (xmlChar *) "default", &floatp); p->integer_p = !floatp; convert = (char *) xmlGetProp (node, (xmlChar *) "convert"); p->invert_p = (convert && !strcmp (convert, "invert")); p->arg = xmlGetProp (node, (xmlChar *) "arg"); p->arg_set = xmlGetProp (node, (xmlChar *) "arg-set"); p->arg_unset = xmlGetProp (node, (xmlChar *) "arg-unset"); /* Check for missing decimal point */ if (debug_p && p->integer_p && (p->high != p->low) && (p->high - p->low) <= 1) fprintf (stderr, "%s: WARNING: %s: %s: range [%.1f, %.1f] shouldn't be integral!\n", blurb(), filename, p->id, p->low, p->high); if (p->type == SELECT) { xmlNodePtr kids; for (kids = node->xmlChildrenNode; kids; kids = kids->next) { parameter *s = make_select_option (filename, kids); if (s) p->options = g_list_append (p->options, s); } } sanity_check_parameter (filename, (const xmlChar *) name, p); return p; } /* Allocates and returns a new SELECT_OPTION `parameter' object based on the properties in the given XML node. Returns 0 if there's nothing to create (comment, or unknown tag.) */ static parameter * make_select_option (const char *filename, xmlNodePtr node) { if (node->type == XML_COMMENT_NODE) return 0; else if (node->type == XML_TEXT_NODE) { sanity_check_text_node (filename, node); return 0; } else if (node->type != XML_ELEMENT_NODE) { if (debug_p) fprintf (stderr, "%s: WARNING: %s: %s: unexpected child tag type %d\n", blurb(), filename, node->name, (int)node->type); return 0; } else if (strcmp ((char *) node->name, "option")) { if (debug_p) fprintf (stderr, "%s: WARNING: %s: %s: child not an option tag: \"%s\"\n", blurb(), filename, node->name, node->name); return 0; } else { parameter *s = calloc (1, sizeof(*s)); s->type = SELECT_OPTION; s->id = xmlGetProp (node, (xmlChar *) "id"); s->label = xmlGetProp (node, (xmlChar *) "_label"); s->arg_set = xmlGetProp (node, (xmlChar *) "arg-set"); s->arg_unset = xmlGetProp (node, (xmlChar *) "arg-unset"); sanity_check_parameter (filename, node->name, s); return s; } } /* Rudimentary check to make sure someone hasn't typed "arg-set=" when they should have typed "arg=", etc. */ static void sanity_check_parameter (const char *filename, const xmlChar *node_name, parameter *p) { struct { gboolean id; gboolean label; gboolean string; gboolean low_label; gboolean high_label; gboolean low; gboolean high; gboolean value; gboolean arg; gboolean invert_p; gboolean arg_set; gboolean arg_unset; } allowed, require; memset (&allowed, 0, sizeof (allowed)); memset (&require, 0, sizeof (require)); switch (p->type) { case COMMAND: allowed.arg = TRUE; require.arg = TRUE; break; case FAKE: break; case DESCRIPTION: allowed.string = TRUE; break; case FAKEPREVIEW: break; case STRING: allowed.id = TRUE; require.id = TRUE; allowed.label = TRUE; require.label = TRUE; allowed.arg = TRUE; require.arg = TRUE; break; case FILENAME: allowed.id = TRUE; require.id = TRUE; allowed.label = TRUE; allowed.arg = TRUE; require.arg = TRUE; break; case SLIDER: allowed.id = TRUE; require.id = TRUE; allowed.label = TRUE; allowed.low_label = TRUE; allowed.high_label = TRUE; allowed.arg = TRUE; require.arg = TRUE; allowed.low = TRUE; /* require.low = TRUE; -- may be 0 */ allowed.high = TRUE; /* require.high = TRUE; -- may be 0 */ allowed.value = TRUE; /* require.value = TRUE; -- may be 0 */ allowed.invert_p = TRUE; break; case SPINBUTTON: allowed.id = TRUE; require.id = TRUE; allowed.label = TRUE; allowed.arg = TRUE; require.arg = TRUE; allowed.low = TRUE; /* require.low = TRUE; -- may be 0 */ allowed.high = TRUE; /* require.high = TRUE; -- may be 0 */ allowed.value = TRUE; /* require.value = TRUE; -- may be 0 */ allowed.invert_p = TRUE; break; case BOOLEAN: allowed.id = TRUE; require.id = TRUE; allowed.label = TRUE; allowed.arg_set = TRUE; allowed.arg_unset = TRUE; break; case SELECT: allowed.id = TRUE; require.id = TRUE; break; case SELECT_OPTION: allowed.id = TRUE; allowed.label = TRUE; require.label = TRUE; allowed.arg_set = TRUE; break; default: abort(); break; } # define WARN(STR) \ fprintf (stderr, "%s: %s: " STR " in <%s%s id=\"%s\">\n", \ blurb(), filename, node_name, \ (!strcmp((char *) node_name, "number") \ ? (p->type == SPINBUTTON ? " type=spinbutton" : " type=slider")\ : ""), \ (p->id ? (char *) p->id : "")) # define CHECK(SLOT,NAME) \ if (p->SLOT && !allowed.SLOT) \ WARN ("\"" NAME "\" is not a valid option"); \ if (!p->SLOT && require.SLOT) \ WARN ("\"" NAME "\" is required") CHECK (id, "id"); CHECK (label, "_label"); CHECK (string, "(body text)"); CHECK (low_label, "_low-label"); CHECK (high_label, "_high-label"); CHECK (low, "low"); CHECK (high, "high"); CHECK (value, "default"); CHECK (arg, "arg"); CHECK (invert_p, "convert"); CHECK (arg_set, "arg-set"); CHECK (arg_unset, "arg-unset"); # undef CHECK # undef WARN if (p->type == SELECT) sanity_check_menu_options (filename, node_name, p); } static void sanity_check_menu_options (const char *filename, const xmlChar *node_name, parameter *p) { GList *opts; int noptions = 0; int nulls = 0; char *prefix = 0; /* fprintf (stderr, "\n## %s\n", p->id);*/ for (opts = p->options; opts; opts = opts->next) { parameter *s = (parameter *) opts->data; if (!s->arg_set) nulls++; noptions++; if (s->arg_set) { char *a = strdup ((char *) s->arg_set); char *spc = strchr (a, ' '); if (spc) *spc = 0; if (prefix) { if (strcmp (a, prefix)) fprintf (stderr, "%s: %s: both \"%s\" and \"%s\" used in \n", blurb(), filename, p->id); } /* "text" nodes show up for all the non-tag text in the file, including all the newlines between tags. Warn if there is text there that is not whitespace. */ static void sanity_check_text_node (const char *filename, const xmlNodePtr node) { const char *body = (const char *) node->content; if (node->type != XML_TEXT_NODE) abort(); while (isspace (*body)) body++; if (*body) fprintf (stderr, "%s: WARNING: %s: random text present: \"%s\"\n", blurb(), filename, body); } /* Returns a list of strings, every switch mentioned in the parameters. The strings must be freed. */ static GList * get_all_switches (const char *filename, GList *parms) { GList *switches = 0; GList *p; for (p = parms; p; p = p->next) { parameter *pp = (parameter *) p->data; if (pp->type == SELECT) { GList *list2 = get_all_switches (filename, pp->options); switches = g_list_concat (switches, list2); } if (pp->arg && *pp->arg) switches = g_list_append (switches, strdup ((char *) pp->arg)); if (pp->arg_set && *pp->arg_set) switches = g_list_append (switches, strdup ((char *) pp->arg_set)); if (pp->arg_unset && *pp->arg_unset) switches = g_list_append (switches, strdup ((char *) pp->arg_unset)); } return switches; } /* Ensures that no switch is mentioned more than once. */ static void sanity_check_parameters (const char *filename, GList *parms) { GList *list = get_all_switches (filename, parms); GList *p; for (p = list; p; p = p->next) { char *sw = (char *) p->data; GList *p2; if (*sw != '-' && *sw != '+') fprintf (stderr, "%s: %s: switch does not begin with hyphen \"%s\"\n", blurb(), filename, sw); for (p2 = p->next; p2; p2 = p2->next) { const char *sw2 = (const char *) p2->data; if (!strcmp (sw, sw2)) fprintf (stderr, "%s: %s: duplicate switch \"%s\"\n", blurb(), filename, sw); } free (sw); } g_list_free (list); } /* Helper for make_parameters() */ static GList * make_parameters_1 (const char *filename, xmlNodePtr node, GtkWidget *parent) { GList *list = 0; for (; node; node = node->next) { const char *name = (char *) node->name; if (!strcmp (name, "hgroup") || !strcmp (name, "vgroup")) { GtkWidget *box = (*name == 'h' ? gtk_hbox_new (FALSE, 0) : gtk_vbox_new (FALSE, 0)); GList *list2; gtk_widget_show (box); gtk_box_pack_start (GTK_BOX (parent), box, FALSE, FALSE, 0); list2 = make_parameters_1 (filename, node->xmlChildrenNode, box); if (list2) list = g_list_concat (list, list2); } else { parameter *p = make_parameter (filename, node); if (p) { list = g_list_append (list, p); make_parameter_widget (filename, p, parent); } } } return list; } /* Calls make_parameter() and make_parameter_widget() on each relevant tag in the XML tree. Also handles the "hgroup" and "vgroup" flags. Returns a GList of `parameter' objects. */ static GList * make_parameters (const char *filename, xmlNodePtr node, GtkWidget *parent) { for (; node; node = node->next) { if (node->type == XML_ELEMENT_NODE && !strcmp ((char *) node->name, "screensaver")) return make_parameters_1 (filename, node->xmlChildrenNode, parent); } return 0; } static gfloat invert_range (gfloat low, gfloat high, gfloat value) { gfloat range = high-low; gfloat off = value-low; return (low + (range - off)); } static GtkAdjustment * make_adjustment (const char *filename, parameter *p) { float range = (p->high - p->low); float value = (p->invert_p ? invert_range (p->low, p->high, p->value) : p->value); gfloat si = (p->high - p->low) / 100; gfloat pi = (p->high - p->low) / 10; gfloat page_size = ((p->type == SLIDER) ? 1 : 0); if (p->value < p->low || p->value > p->high) { if (debug_p && p->integer_p) fprintf (stderr, "%s: WARNING: %s: %d is not in range [%d, %d]\n", blurb(), filename, (int) p->value, (int) p->low, (int) p->high); else if (debug_p) fprintf (stderr, "%s: WARNING: %s: %.2f is not in range [%.2f, %.2f]\n", blurb(), filename, p->value, p->low, p->high); value = (value < p->low ? p->low : p->high); } #if 0 else if (debug_p && p->value < 1000 && p->high >= 10000) { if (p->integer_p) fprintf (stderr, "%s: WARNING: %s: %d is suspicious for range [%d, %d]\n", blurb(), filename, (int) p->value, (int) p->low, (int) p->high); else fprintf (stderr, "%s: WARNING: %s: %.2f is suspicious for range [%.2f, %.2f]\n", blurb(), filename, p->value, p->low, p->high); } #endif /* 0 */ si = (int) (si + 0.5); pi = (int) (pi + 0.5); if (si < 1) si = 1; if (pi < 1) pi = 1; if (range <= 500) si = 1; return GTK_ADJUSTMENT (gtk_adjustment_new (value, p->low, p->high + page_size, si, pi, page_size)); } static void set_widget_min_width (GtkWidget *w, int width) { GtkRequisition req; gtk_widget_size_request (GTK_WIDGET (w), &req); if (req.width < width) gtk_widget_set_size_request (GTK_WIDGET (w), width, -1); } /* If we're inside a vbox, we need to put an hbox in it, or labels appear on top instead of to the left, and things stretch to the full width of the window. */ static GtkWidget * insert_fake_hbox (GtkWidget *parent) { if (GTK_IS_VBOX (parent)) { GtkWidget *hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (parent), hbox, FALSE, FALSE, 4); gtk_widget_show (hbox); return hbox; } return parent; } static void link_atk_label_to_widget(GtkWidget *label, GtkWidget *widget) { AtkObject *atk_label = gtk_widget_get_accessible (label); AtkObject *atk_widget = gtk_widget_get_accessible (widget); atk_object_add_relationship (atk_label, ATK_RELATION_LABEL_FOR, atk_widget); atk_object_add_relationship (atk_widget, ATK_RELATION_LABELLED_BY, atk_label); } /* Given a `parameter' struct, allocates an appropriate GtkWidget for it, and stores it in `p->widget'. `parent' must be a GtkBox. */ static void make_parameter_widget (const char *filename, parameter *p, GtkWidget *parent) { const char *label = (char *) p->label; if (p->widget) return; switch (p->type) { case STRING: { GtkWidget *entry = gtk_entry_new (); parent = insert_fake_hbox (parent); if (label) { GtkWidget *w = gtk_label_new (_(label)); link_atk_label_to_widget (w, entry); gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); set_widget_min_width (GTK_WIDGET (w), MIN_LABEL_WIDTH); gtk_widget_show (w); gtk_box_pack_start (GTK_BOX (parent), w, FALSE, FALSE, 4); } p->widget = entry; if (p->string) gtk_entry_set_text (GTK_ENTRY (p->widget), (char *) p->string); gtk_box_pack_start (GTK_BOX (parent), p->widget, FALSE, FALSE, 4); break; } case FILENAME: { GtkWidget *L = gtk_label_new (label ? _(label) : ""); GtkWidget *entry = gtk_entry_new (); GtkWidget *button = gtk_button_new_with_label (_("Browse...")); link_atk_label_to_widget (L, entry); gtk_widget_show (entry); gtk_widget_show (button); p->widget = entry; gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (browse_button_cb), (gpointer) entry); gtk_label_set_justify (GTK_LABEL (L), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (L), 1.0, 0.5); set_widget_min_width (GTK_WIDGET (L), MIN_LABEL_WIDTH); gtk_widget_show (L); if (p->string) gtk_entry_set_text (GTK_ENTRY (entry), (char *) p->string); parent = insert_fake_hbox (parent); gtk_box_pack_start (GTK_BOX (parent), L, FALSE, FALSE, 4); gtk_box_pack_start (GTK_BOX (parent), entry, TRUE, TRUE, 4); gtk_box_pack_start (GTK_BOX (parent), button, FALSE, FALSE, 4); break; } case SLIDER: { GtkAdjustment *adj = make_adjustment (filename, p); GtkWidget *scale = gtk_hscale_new (adj); GtkWidget *labelw = 0; if (label) { labelw = gtk_label_new (_(label)); link_atk_label_to_widget (labelw, scale); gtk_label_set_justify (GTK_LABEL (labelw), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (labelw), 0.0, 0.5); set_widget_min_width (GTK_WIDGET (labelw), MIN_LABEL_WIDTH); gtk_widget_show (labelw); gtk_box_pack_start (GTK_BOX (parent), labelw, FALSE, FALSE, 2); } /* Do this after 'labelw' so that it appears above, not to left. */ parent = insert_fake_hbox (parent); if (p->low_label) { GtkWidget *w = gtk_label_new (_((char *) p->low_label)); link_atk_label_to_widget (w, scale); gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); set_widget_min_width (GTK_WIDGET (w), MIN_LABEL_WIDTH); gtk_widget_show (w); gtk_box_pack_start (GTK_BOX (parent), w, FALSE, FALSE, 4); } gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_BOTTOM); gtk_scale_set_draw_value (GTK_SCALE (scale), debug_p); gtk_scale_set_digits (GTK_SCALE (scale), (p->integer_p ? 0 : 2)); set_widget_min_width (GTK_WIDGET (scale), MIN_SLIDER_WIDTH); gtk_box_pack_start (GTK_BOX (parent), scale, FALSE, FALSE, 4); gtk_widget_show (scale); if (p->high_label) { GtkWidget *w = gtk_label_new (_((char *) p->high_label)); link_atk_label_to_widget (w, scale); gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5); set_widget_min_width (GTK_WIDGET (w), MIN_LABEL_WIDTH); gtk_widget_show (w); gtk_box_pack_start (GTK_BOX (parent), w, FALSE, FALSE, 4); } p->widget = scale; break; } case SPINBUTTON: { GtkAdjustment *adj = make_adjustment (filename, p); GtkWidget *spin = gtk_spin_button_new (adj, 15, 0); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (spin), TRUE); gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), GET_ADJ_VALUE(adj)); set_widget_min_width (GTK_WIDGET (spin), MIN_SPINBUTTON_WIDTH); if (label) { GtkWidget *w = gtk_label_new (_(label)); link_atk_label_to_widget (w, spin); gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); set_widget_min_width (GTK_WIDGET (w), MIN_LABEL_WIDTH); gtk_widget_show (w); parent = insert_fake_hbox (parent); gtk_box_pack_start (GTK_BOX (parent), w, FALSE, FALSE, 4); } gtk_widget_show (spin); gtk_box_pack_start (GTK_BOX (parent), spin, FALSE, FALSE, 4); p->widget = spin; break; } case BOOLEAN: { p->widget = gtk_check_button_new_with_label (_(label)); /* Let these stretch -- doesn't hurt. parent = insert_fake_hbox (parent); */ gtk_box_pack_start (GTK_BOX (parent), p->widget, FALSE, FALSE, 4); break; } case SELECT: { GtkWidget *opt = gtk_option_menu_new (); GtkWidget *menu = gtk_menu_new (); GList *opts; for (opts = p->options; opts; opts = opts->next) { parameter *s = (parameter *) opts->data; GtkWidget *i = gtk_menu_item_new_with_label (_((char *) s->label)); gtk_widget_show (i); gtk_menu_append (GTK_MENU (menu), i); } gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu); p->widget = opt; parent = insert_fake_hbox (parent); gtk_box_pack_start (GTK_BOX (parent), p->widget, FALSE, FALSE, 4); break; } case COMMAND: case FAKE: case DESCRIPTION: case FAKEPREVIEW: break; default: abort(); } if (p->widget) { gtk_widget_set_name (p->widget, (char *) p->id); gtk_widget_show (p->widget); } } /* File selection. Absurdly, there is no GTK file entry widget, only a GNOME one, so in order to avoid depending on GNOME in this code, we have to do it ourselves. */ /* cancel button on GtkFileSelection: user_data unused */ static void file_sel_cancel (GtkWidget *button, gpointer user_data) { GtkWidget *dialog = button; while (GET_PARENT (dialog)) dialog = GET_PARENT (dialog); gtk_widget_destroy (dialog); } /* ok button on GtkFileSelection: user_data is the corresponding GtkEntry */ static void file_sel_ok (GtkWidget *button, gpointer user_data) { GtkWidget *entry = GTK_WIDGET (user_data); GtkWidget *dialog = button; const char *path; while (GET_PARENT (dialog)) dialog = GET_PARENT (dialog); gtk_widget_hide (dialog); path = gtk_file_selection_get_filename (GTK_FILE_SELECTION (dialog)); /* apparently one doesn't free `path' */ gtk_entry_set_text (GTK_ENTRY (entry), path); gtk_entry_set_position (GTK_ENTRY (entry), strlen (path)); gtk_widget_destroy (dialog); } /* WM close on GtkFileSelection: user_data unused */ static void file_sel_close (GtkWidget *widget, GdkEvent *event, gpointer user_data) { file_sel_cancel (widget, user_data); } /* "Browse" button: user_data is the corresponding GtkEntry */ static void browse_button_cb (GtkButton *button, gpointer user_data) { GtkWidget *entry = GTK_WIDGET (user_data); const char *text = gtk_entry_get_text (GTK_ENTRY (entry)); GtkFileSelection *selector = GTK_FILE_SELECTION (gtk_file_selection_new (_("Select file."))); gtk_file_selection_set_filename (selector, text); gtk_signal_connect (GTK_OBJECT (selector->ok_button), "clicked", GTK_SIGNAL_FUNC (file_sel_ok), (gpointer) entry); gtk_signal_connect (GTK_OBJECT (selector->cancel_button), "clicked", GTK_SIGNAL_FUNC (file_sel_cancel), (gpointer) entry); gtk_signal_connect (GTK_OBJECT (selector), "delete_event", GTK_SIGNAL_FUNC (file_sel_close), (gpointer) entry); gtk_window_set_modal (GTK_WINDOW (selector), TRUE); gtk_widget_show (GTK_WIDGET (selector)); } /* Converting to and from command-lines */ /* Returns a copy of string that has been quoted according to shell rules: it may have been wrapped in "" and had some characters backslashed; or it may be unchanged. */ static char * shell_quotify (const char *string) { char *string2 = (char *) malloc ((strlen (string) * 2) + 10); const char *in; char *out; int need_quotes = 0; int in_length = 0; out = string2; *out++ = '"'; for (in = string; *in; in++) { in_length++; if (*in == '!' || *in == '"' || *in == '$') { need_quotes = 1; *out++ = '\\'; *out++ = *in; } else if (*in <= ' ' || *in >= 127 || *in == '\'' || *in == '#' || *in == '%' || *in == '&' || *in == '(' || *in == ')' || *in == '*') { need_quotes = 1; *out++ = *in; } else *out++ = *in; } *out++ = '"'; *out = 0; if (in_length == 0) need_quotes = 1; if (need_quotes) return (string2); free (string2); return strdup (string); } /* Modify the string in place to remove wrapping double-quotes and interior backslashes. */ static void de_stringify (char *s) { char q = s[0]; if (q != '\'' && q != '\"' && q != '`') abort(); memmove (s, s+1, strlen (s)+1); while (*s && *s != q) { if (*s == '\\') memmove (s, s+1, strlen (s)+1); s++; } if (*s != q) abort(); *s = 0; } /* Substitutes a shell-quotified version of `value' into `p->arg' at the place where the `%' character appeared. */ static char * format_switch (parameter *p, const char *value) { char *fmt = (char *) p->arg; char *v2; char *result, *s; if (!fmt || !value) return 0; v2 = shell_quotify (value); result = (char *) malloc (strlen (fmt) + strlen (v2) + 10); s = result; for (; *fmt; fmt++) if (*fmt != '%') *s++ = *fmt; else { strcpy (s, v2); s += strlen (s); } *s = 0; free (v2); return result; } /* Maps a `parameter' to a command-line switch. Returns 0 if it can't, or if the parameter has the default value. */ static char * parameter_to_switch (parameter *p) { switch (p->type) { case COMMAND: if (p->arg) return strdup ((char *) p->arg); else return 0; break; case STRING: case FILENAME: if (!p->widget) return 0; { const char *s = gtk_entry_get_text (GTK_ENTRY (p->widget)); char *v; if (!strcmp ((s ? s : ""), (p->string ? (char *) p->string : ""))) v = 0; /* same as default */ else v = format_switch (p, s); /* don't free `s' */ return v; } case SLIDER: case SPINBUTTON: if (!p->widget) return 0; { GtkAdjustment *adj = (p->type == SLIDER ? gtk_range_get_adjustment (GTK_RANGE (p->widget)) : gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (p->widget))); char buf[255]; char *s1; float value = (p->invert_p ? invert_range (GET_ADJ_LOWER(adj), GET_ADJ_UPPER(adj), GET_ADJ_VALUE(adj)) - 1 : GET_ADJ_VALUE(adj)); if (value == p->value) /* same as default */ return 0; if (p->integer_p) sprintf (buf, "%d", (int) (value + (value > 0 ? 0.5 : -0.5))); else sprintf (buf, "%.4f", value); s1 = strchr (buf, '.'); if (s1) { char *s2 = s1 + strlen(s1) - 1; while (s2 > s1 && *s2 == '0') /* lose trailing zeroes */ *s2-- = 0; if (s2 >= s1 && *s2 == '.') /* lose trailing decimal */ *s2-- = 0; } return format_switch (p, buf); } case BOOLEAN: if (!p->widget) return 0; { GtkToggleButton *b = GTK_TOGGLE_BUTTON (p->widget); const char *s = (gtk_toggle_button_get_active (b) ? (char *) p->arg_set : (char *) p->arg_unset); if (s) return strdup (s); else return 0; } case SELECT: if (!p->widget) return 0; { GtkOptionMenu *opt = GTK_OPTION_MENU (p->widget); GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt)); GtkWidget *selected = gtk_menu_get_active (menu); GList *kids = gtk_container_children (GTK_CONTAINER (menu)); int menu_elt = g_list_index (kids, (gpointer) selected); GList *ol = g_list_nth (p->options, menu_elt); parameter *o = (ol ? (parameter *) ol->data : 0); const char *s; if (!o) abort(); if (o->type != SELECT_OPTION) abort(); s = (char *) o->arg_set; if (s) return strdup (s); else return 0; } default: if (p->widget) abort(); else return 0; } } /* Maps a GList of `parameter' objects to a complete command-line string. All arguments will be properly quoted. */ static char * parameters_to_cmd_line (GList *parms, gboolean default_p) { int L = g_list_length (parms); int LL = 0; char **strs = (char **) calloc (sizeof (*parms), L); char *result; char *out; int i, j; for (i = 0, j = 0; parms; parms = parms->next, i++) { parameter *p = (parameter *) parms->data; if (!default_p || p->type == COMMAND) { char *s = parameter_to_switch (p); strs[j++] = s; LL += (s ? strlen(s) : 0) + 1; } } result = (char *) malloc (LL + 10); out = result; for (i = 0; i < j; i++) if (strs[i]) { strcpy (out, strs[i]); out += strlen (out); *out++ = ' '; free (strs[i]); } *out = 0; while (out > result && out[-1] == ' ') /* strip trailing spaces */ *(--out) = 0; free (strs); return result; } /* Returns a GList of the tokens the string, using shell syntax; Quoted strings are handled as a single token. */ static GList * tokenize_command_line (const char *cmd) { GList *result = 0; const char *s = cmd; while (*s) { const char *start; char *ss; for (; isspace(*s); s++); /* skip whitespace */ start = s; if (*s == '\'' || *s == '\"' || *s == '`') { char q = *s; s++; while (*s && *s != q) /* skip to matching quote */ { if (*s == '\\' && s[1]) /* allowing backslash quoting */ s++; s++; } s++; } else { while (*s && (! (isspace(*s) || *s == '\'' || *s == '\"' || *s == '`'))) s++; } if (s > start) { ss = (char *) malloc ((s - start) + 1); strncpy (ss, start, s-start); ss[s-start] = 0; if (*ss == '\'' || *ss == '\"' || *ss == '`') de_stringify (ss); result = g_list_append (result, ss); } } return result; } static void parameter_set_switch (parameter *, gpointer value); static gboolean parse_command_line_into_parameters_1 (const char *filename, GList *parms, const char *option, const char *value, parameter *parent); /* Parses the command line, and flushes those options down into the `parameter' structs in the list. */ static void parse_command_line_into_parameters (const char *filename, const char *cmd, GList *parms) { GList *tokens = tokenize_command_line (cmd); GList *rest; for (rest = tokens; rest; rest = rest->next) { char *option = rest->data; rest->data = 0; if (option[0] != '-' && option[0] != '+') { if (debug_p) fprintf (stderr, "%s: WARNING: %s: not a switch: \"%s\"\n", blurb(), filename, option); } else { char *value = 0; if (rest->next) /* pop off the arg to this option */ { char *s = (char *) rest->next->data; /* the next token is the next switch iff it matches "-[a-z]". (To avoid losing on "-x -3.1".) */ if (s && (s[0] != '-' || !isalpha(s[1]))) { value = s; rest->next->data = 0; rest = rest->next; } } parse_command_line_into_parameters_1 (filename, parms, option, value, 0); if (value) free (value); free (option); } } g_list_free (tokens); } static gboolean compare_opts (const char *option, const char *value, const char *template) { int ol = strlen (option); char *c; if (strncmp (option, template, ol)) return FALSE; if (template[ol] != (value ? ' ' : 0)) return FALSE; /* At this point, we have a match against "option". If template contains a %, we're done. Else, compare against "value" too. */ c = strchr (template, '%'); if (c) return TRUE; if (!value) return (template[ol] == 0); if (strcmp (template + ol + 1, value)) return FALSE; return TRUE; } static gboolean parse_command_line_into_parameters_1 (const char *filename, GList *parms, const char *option, const char *value, parameter *parent) { GList *p; parameter *match = 0; gint which = -1; gint index = 0; for (p = parms; p; p = p->next) { parameter *pp = (parameter *) p->data; which = -99; if (pp->type == SELECT) { if (parse_command_line_into_parameters_1 (filename, pp->options, option, value, pp)) { which = -2; match = pp; } } else if (pp->arg) { if (compare_opts (option, value, (char *) pp->arg)) { which = -1; match = pp; } } else if (pp->arg_set) { if (compare_opts (option, value, (char *) pp->arg_set)) { which = 1; match = pp; } } else if (pp->arg_unset) { if (compare_opts (option, value, (char *) pp->arg_unset)) { which = 0; match = pp; } } if (match) break; index++; } if (!match) { if (debug_p && !parent) fprintf (stderr, "%s: WARNING: %s: no match for %s %s\n", blurb(), filename, option, (value ? value : "")); return FALSE; } switch (match->type) { case STRING: case FILENAME: case SLIDER: case SPINBUTTON: if (which != -1) abort(); parameter_set_switch (match, (gpointer) value); break; case BOOLEAN: if (which != 0 && which != 1) abort(); parameter_set_switch (match, GINT_TO_POINTER(which)); break; case SELECT_OPTION: if (which != 1) abort(); parameter_set_switch (parent, GINT_TO_POINTER(index)); break; default: break; } return TRUE; } /* Set the parameter's value. For STRING, FILENAME, SLIDER, and SPINBUTTON, `value' is a char*. For BOOLEAN and SELECT, `value' is an int. */ static void parameter_set_switch (parameter *p, gpointer value) { if (p->type == SELECT_OPTION) abort(); if (!p->widget) return; switch (p->type) { case STRING: case FILENAME: { gtk_entry_set_text (GTK_ENTRY (p->widget), (char *) value); break; } case SLIDER: case SPINBUTTON: { GtkAdjustment *adj = (p->type == SLIDER ? gtk_range_get_adjustment (GTK_RANGE (p->widget)) : gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (p->widget))); float f; char c; if (1 == sscanf ((char *) value, "%f %c", &f, &c)) { if (p->invert_p) f = invert_range (GET_ADJ_LOWER(adj), GET_ADJ_UPPER(adj), f) - 1; gtk_adjustment_set_value (adj, f); } break; } case BOOLEAN: { GtkToggleButton *b = GTK_TOGGLE_BUTTON (p->widget); gtk_toggle_button_set_active (b, GPOINTER_TO_INT(value)); break; } case SELECT: { gtk_option_menu_set_history (GTK_OPTION_MENU (p->widget), GPOINTER_TO_INT(value)); break; } default: abort(); } } static void restore_defaults (const char *progname, GList *parms) { for (; parms; parms = parms->next) { parameter *p = (parameter *) parms->data; if (!p->widget) continue; switch (p->type) { case STRING: case FILENAME: { gtk_entry_set_text (GTK_ENTRY (p->widget), (p->string ? (char *) p->string : "")); break; } case SLIDER: case SPINBUTTON: { GtkAdjustment *adj = (p->type == SLIDER ? gtk_range_get_adjustment (GTK_RANGE (p->widget)) : gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (p->widget))); float value = (p->invert_p ? invert_range (p->low, p->high, p->value) : p->value); gtk_adjustment_set_value (adj, value); break; } case BOOLEAN: { /* A toggle button should be on by default if it inserts nothing into the command line when on. E.g., it should be on if `arg_set' is null. */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (p->widget), (!p->arg_set || !*p->arg_set)); break; } case SELECT: { GtkOptionMenu *opt = GTK_OPTION_MENU (p->widget); GList *opts; int selected = 0; int index; for (opts = p->options, index = 0; opts; opts = opts->next, index++) { parameter *s = (parameter *) opts->data; /* The default menu item is the first one with no `arg_set' field. */ if (!s->arg_set) { selected = index; break; } } gtk_option_menu_set_history (GTK_OPTION_MENU (opt), selected); break; } default: abort(); } } } /* Documentation strings */ static char * get_description (GList *parms, gboolean verbose_p) { parameter *doc = 0; for (; parms; parms = parms->next) { parameter *p = (parameter *) parms->data; if (p->type == DESCRIPTION) { doc = p; break; } } if (!doc || !doc->string) return 0; else { char *d = strdup ((char *) doc->string); char *s; char *p; for (s = d; *s; s++) if (s[0] == '\n') { if (s[1] == '\n') /* blank line: leave it */ s++; else if (s[1] == ' ' || s[1] == '\t') s++; /* next line is indented: leave newline */ else if (!strncmp(s+1, "http:", 5)) s++; /* next line begins a URL: leave newline */ else s[0] = ' '; /* delete newline to un-fold this line */ } /* strip off leading whitespace on first line only */ for (s = d; *s && (*s == ' ' || *s == '\t'); s++) ; while (*s == '\n') /* strip leading newlines */ s++; if (s != d) memmove (d, s, strlen(s)+1); /* strip off trailing whitespace and newlines */ { int L = strlen(d); while (L && isspace(d[L-1])) d[--L] = 0; } /* strip off duplicated whitespaces */ for (s = d; *s; s++) if (s[0] == ' ') { p = s+1; while (*s == ' ') s++; if (*p && (s != p)) memmove (p, s, strlen(s)+1); } #if 0 if (verbose_p) { fprintf (stderr, "%s: text read is \"%s\"\n", blurb(),doc->string); fprintf (stderr, "%s: description is \"%s\"\n", blurb(), d); fprintf (stderr, "%s: translation is \"%s\"\n", blurb(), _(d)); } #endif /* 0 */ return (d); } } /* External interface. */ static conf_data * load_configurator_1 (const char *program, const char *arguments, gboolean verbose_p) { const char *dir = hack_configuration_path; char *base_program; int L = strlen (dir); char *file; char *s; FILE *f; conf_data *data; if (L == 0) return 0; base_program = strrchr(program, '/'); if (base_program) base_program++; if (!base_program) base_program = (char *) program; file = (char *) malloc (L + strlen (base_program) + 10); data = (conf_data *) calloc (1, sizeof(*data)); strcpy (file, dir); if (file[L-1] != '/') file[L++] = '/'; strcpy (file+L, base_program); for (s = file+L; *s; s++) if (*s == '/' || *s == ' ') *s = '_'; else if (isupper (*s)) *s = tolower (*s); strcat (file+L, ".xml"); f = fopen (file, "r"); if (f) { int res, size = 1024; char chars[1024]; xmlParserCtxtPtr ctxt; xmlDocPtr doc = 0; GtkWidget *vbox0; GList *parms; if (verbose_p) fprintf (stderr, "%s: reading %s...\n", blurb(), file); res = fread (chars, 1, 4, f); if (res <= 0) { free (data); data = 0; goto DONE; } ctxt = xmlCreatePushParserCtxt(NULL, NULL, chars, res, file); while ((res = fread(chars, 1, size, f)) > 0) xmlParseChunk (ctxt, chars, res, 0); xmlParseChunk (ctxt, chars, 0, 1); doc = ctxt->myDoc; xmlFreeParserCtxt (ctxt); fclose (f); /* Parsed the XML file. Now make some widgets. */ vbox0 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox0); parms = make_parameters (file, doc->xmlRootNode, vbox0); sanity_check_parameters (file, parms); xmlFreeDoc (doc); restore_defaults (program, parms); if (arguments && *arguments) parse_command_line_into_parameters (program, arguments, parms); data->widget = vbox0; data->parameters = parms; data->description = get_description (parms, verbose_p); } else { parameter *p; if (verbose_p) fprintf (stderr, "%s: %s does not exist.\n", blurb(), file); p = calloc (1, sizeof(*p)); p->type = COMMAND; p->arg = (xmlChar *) strdup (arguments); data->parameters = g_list_append (0, (gpointer) p); } data->progname = strdup (program); DONE: free (file); return data; } static void split_command_line (const char *full_command_line, char **prog_ret, char **args_ret) { char *line = strdup (full_command_line); char *prog; char *args; char *s; prog = line; s = line; while (*s) { if (isspace (*s)) { *s = 0; s++; while (isspace (*s)) s++; break; } else if (*s == '=') /* if the leading word contains an "=", skip it. */ { while (*s && !isspace (*s)) s++; while (isspace (*s)) s++; prog = s; } s++; } args = s; *prog_ret = strdup (prog); *args_ret = strdup (args); free (line); } conf_data * load_configurator (const char *full_command_line, gboolean verbose_p) { char *prog; char *args; conf_data *cd; debug_p = verbose_p; split_command_line (full_command_line, &prog, &args); cd = load_configurator_1 (prog, args, verbose_p); free (prog); free (args); return cd; } char * get_configurator_command_line (conf_data *data, gboolean default_p) { char *args = parameters_to_cmd_line (data->parameters, default_p); char *result = (char *) malloc (strlen (data->progname) + strlen (args) + 2); strcpy (result, data->progname); strcat (result, " "); strcat (result, args); free (args); return result; } void set_configurator_command_line (conf_data *data, const char *full_command_line) { char *prog; char *args; split_command_line (full_command_line, &prog, &args); if (data->progname) free (data->progname); data->progname = prog; restore_defaults (prog, data->parameters); parse_command_line_into_parameters (prog, args, data->parameters); free (args); } void free_conf_data (conf_data *data) { if (data->parameters) { GList *rest; for (rest = data->parameters; rest; rest = rest->next) { free_parameter ((parameter *) rest->data); rest->data = 0; } g_list_free (data->parameters); data->parameters = 0; } if (data->widget) gtk_widget_destroy (data->widget); if (data->progname) free (data->progname); if (data->description) free (data->description); memset (data, ~0, sizeof(*data)); free (data); } #endif /* HAVE_GTK && HAVE_XML -- whole file */ xscreensaver-5.15/driver/demo-Gtk-conf.h000066400000000000000000000023341164314150500202140ustar00rootroot00000000000000/* demo-Gtk-conf.c --- implements the dynamic configuration dialogs. * xscreensaver, Copyright (c) 2001-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifndef _DEMO_GTK_CONF_H_ #define _DEMO_GTK_CONF_H_ typedef struct { GtkWidget *widget; /* the container widget with the sliders and stuff. */ GList *parameters; /* internal data -- hands off */ char *progname; char *progclass; char *description; } conf_data; extern conf_data *load_configurator (const char *cmd_line, gboolean verbose_p); extern char *get_configurator_command_line (conf_data *, gboolean default_p); extern void set_configurator_command_line (conf_data *, const char *cmd_line); extern void free_conf_data (conf_data *); extern const char *hack_configuration_path; #endif /* _DEMO_GTK_CONF_H_ */ xscreensaver-5.15/driver/demo-Gtk-stubs.h000066400000000000000000000062561164314150500204360ustar00rootroot00000000000000#include void activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data); void lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data); void kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data); void restart_menu_cb (GtkMenuItem *menuitem, gpointer user_data); void exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data); void about_menu_cb (GtkMenuItem *menuitem, gpointer user_data); void doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data); void switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page, gint page_num, gpointer user_data); void pref_changed_cb (GtkToggleButton *togglebutton, gpointer user_data); void run_this_cb (GtkButton *button, gpointer user_data); void settings_cb (GtkButton *button, gpointer user_data); void run_next_cb (GtkButton *button, gpointer user_data); void run_prev_cb (GtkButton *button, gpointer user_data); void browse_image_dir_cb (GtkButton *button, gpointer user_data); void settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page, gint page_num, gpointer user_data); void manual_cb (GtkButton *button, gpointer user_data); void settings_adv_cb (GtkButton *button, gpointer user_data); void settings_std_cb (GtkButton *button, gpointer user_data); void settings_reset_cb (GtkButton *button, gpointer user_data); void settings_ok_cb (GtkButton *button, gpointer user_data); void settings_cancel_cb (GtkButton *button, gpointer user_data); xscreensaver-5.15/driver/demo-Gtk-support.c000066400000000000000000000136001164314150500207740ustar00rootroot00000000000000/* * DO NOT EDIT THIS FILE - it is generated by Glade. WARNING: I did edit this file! Be careful! -jwz */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include "demo-Gtk-support.h" /* jwz -- dumbass Glade1 doesn't emit code that can read PNGs. this does. */ #ifdef HAVE_GDK_PIXBUF # ifdef HAVE_GTK2 # include # else /* !HAVE_GTK2 */ # include # endif /* !HAVE_GTK2 */ #endif /* HAVE_GDK_PIXBUF */ /* This is an internally used function to check if a pixmap file exists. */ static gchar* check_file_exists (const gchar *directory, const gchar *filename); /* This is an internally used function to create pixmaps. */ static GtkWidget* create_dummy_pixmap (GtkWidget *widget); GtkWidget* lookup_widget (GtkWidget *widget, const gchar *widget_name) { GtkWidget *parent, *found_widget; for (;;) { if (GTK_IS_MENU (widget)) parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); else parent = widget->parent; if (parent == NULL) break; widget = parent; } found_widget = (GtkWidget*) gtk_object_get_data (GTK_OBJECT (widget), widget_name); if (!found_widget) g_warning ("Widget not found: %s", widget_name); return found_widget; } /* This is a dummy pixmap we use when a pixmap can't be found. */ static char *dummy_pixmap_xpm[] = { /* columns rows colors chars-per-pixel */ "1 1 1 1", " c None", /* pixels */ " " }; /* This is an internally used function to create pixmaps. */ static GtkWidget* create_dummy_pixmap (GtkWidget *widget) { GdkColormap *colormap; GdkPixmap *gdkpixmap; GdkBitmap *mask; GtkWidget *pixmap; colormap = gtk_widget_get_colormap (widget); gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL, dummy_pixmap_xpm); if (gdkpixmap == NULL) g_error ("Couldn't create replacement pixmap."); pixmap = gtk_pixmap_new (gdkpixmap, mask); gdk_pixmap_unref (gdkpixmap); gdk_bitmap_unref (mask); return pixmap; } static GList *pixmaps_directories = NULL; /* Use this function to set the directory containing installed pixmaps. */ void add_pixmap_directory (const gchar *directory) { pixmaps_directories = g_list_prepend (pixmaps_directories, g_strdup (directory)); } /* This is an internally used function to create pixmaps. */ /* #### Warning: this version of this function hacked by jwz to support PNGs. Don't let Glade1 overwrite this! */ GtkWidget* create_pixmap (GtkWidget *widget, const gchar *filename) { gchar *found_filename = NULL; GdkColormap *colormap = 0; GdkPixmap *gdkpixmap = 0; GdkBitmap *mask = 0; GtkWidget *pixmap = 0; GList *elem = 0; if (!filename || !filename[0]) return create_dummy_pixmap (widget); /* We first try any pixmaps directories set by the application. */ elem = pixmaps_directories; while (elem) { found_filename = check_file_exists ((gchar*)elem->data, filename); if (found_filename) break; elem = elem->next; } /* If we haven't found the pixmap, try the source directory. */ if (!found_filename) { found_filename = check_file_exists ("../utils/images", filename); } if (!found_filename) { g_warning (_("Couldn't find pixmap file: %s"), filename); return create_dummy_pixmap (widget); } colormap = gtk_widget_get_colormap (widget); # ifndef HAVE_GDK_PIXBUF gdkpixmap = gdk_pixmap_colormap_create_from_xpm (NULL, colormap, &mask, NULL, found_filename); if (gdkpixmap == NULL) { g_warning (_("Error loading pixmap file: %s"), found_filename); g_free (found_filename); return create_dummy_pixmap (widget); } # else /* HAVE_GDK_PIXBUF */ /* jwz -- dumbass Glade1 doesn't emit code that can read PNGs. This code does... */ /* #### Danger: we aren't using `colormap'... */ { GdkPixbuf *pb; # ifdef HAVE_GTK2 GError *gerr = 0; # endif /* HAVE_GTK2 */ pb = gdk_pixbuf_new_from_file (found_filename # ifdef HAVE_GTK2 , &gerr # endif /* HAVE_GTK2 */ ); if (pb) { gdkpixmap = 0; mask = 0; gdk_pixbuf_render_pixmap_and_mask (pb, &gdkpixmap, &mask, 128); } else { g_warning (_("Error loading pixmap file: %s"), found_filename); # ifdef HAVE_GTK2 if (gerr && gerr->message && *gerr->message) g_warning (_("reason: %s\n"), gerr->message); # endif /* HAVE_GTK2 */ return create_dummy_pixmap (widget); } } # endif /* HAVE_GDK_PIXBUF */ g_free (found_filename); pixmap = gtk_pixmap_new (gdkpixmap, mask); gdk_pixmap_unref (gdkpixmap); gdk_bitmap_unref (mask); return pixmap; } /* This is an internally used function to check if a pixmap file exists. */ static gchar* check_file_exists (const gchar *directory, const gchar *filename) { gchar *full_filename; struct stat s; gint status; full_filename = (gchar*) g_malloc (strlen (directory) + 1 + strlen (filename) + 1); strcpy (full_filename, directory); strcat (full_filename, G_DIR_SEPARATOR_S); strcat (full_filename, filename); status = stat (full_filename, &s); if (status == 0 && S_ISREG (s.st_mode)) return full_filename; g_free (full_filename); return NULL; } xscreensaver-5.15/driver/demo-Gtk-support.h000066400000000000000000000030261164314150500210020ustar00rootroot00000000000000/* * DO NOT EDIT THIS FILE - it is generated by Glade. */ #ifdef HAVE_CONFIG_H # include #endif #include /* * Standard gettext macros. */ #ifdef ENABLE_NLS # include # undef _ # define _(String) dgettext (PACKAGE, String) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define textdomain(String) (String) # define gettext(String) (String) # define dgettext(Domain,Message) (Message) # define dcgettext(Domain,Message,Type) (Message) # define bindtextdomain(Domain,Directory) (Domain) # define _(String) (String) # define N_(String) (String) #endif /* * Public Functions. */ /* * This function returns a widget in a component created by Glade. * Call it with the toplevel widget in the component (i.e. a window/dialog), * or alternatively any widget in the component, and the name of the widget * you want returned. */ GtkWidget* lookup_widget (GtkWidget *widget, const gchar *widget_name); /* get_widget() is deprecated. Use lookup_widget instead. */ #define get_widget lookup_widget /* Use this function to set the directory containing installed pixmaps. */ void add_pixmap_directory (const gchar *directory); /* * Private Functions. */ /* This is used to create the pixmaps in the interface. */ GtkWidget* create_pixmap (GtkWidget *widget, const gchar *filename); xscreensaver-5.15/driver/demo-Gtk-widgets.c000066400000000000000000002612421164314150500207350ustar00rootroot00000000000000/* * DO NOT EDIT THIS FILE - it is generated by Glade. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include "demo-Gtk-stubs.h" #include "demo-Gtk-widgets.h" #include "demo-Gtk-support.h" # ifdef __GNUC__ # define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */ # else # define STFU /* */ # endif GtkWidget* create_xscreensaver_demo (void) { GtkWidget *xscreensaver_demo; GtkWidget *outer_vbox; GtkWidget *menubar; guint tmp_key; GtkWidget *file; GtkWidget *file_menu; GtkAccelGroup *file_menu_accels; GtkWidget *activate_menu; GtkWidget *lock_menu; GtkWidget *kill_menu; GtkWidget *restart; GtkWidget *separator1; GtkWidget *exit_menu; GtkWidget *help; GtkWidget *help_menu; GtkAccelGroup *help_menu_accels; GtkWidget *about_menu; GtkWidget *doc_menu; GtkWidget *notebook; GtkWidget *demos_table; GtkWidget *blanking_table; GtkWidget *cycle_label; GtkWidget *lock_button_eventbox; GtkWidget *lock_button; GtkWidget *timeout_label; GtkObject *timeout_spinbutton_adj; GtkWidget *timeout_spinbutton; GtkWidget *timeout_mlabel; GtkWidget *cycle_mlabel; GtkWidget *lock_mlabel; GtkObject *lock_spinbutton_adj; GtkWidget *lock_spinbutton; GtkObject *cycle_spinbutton_adj; GtkWidget *cycle_spinbutton; GtkWidget *demo_manual_hbbox; GtkWidget *demo; GtkWidget *settings; GtkWidget *list_vbox; GtkWidget *mode_hbox; GtkWidget *mode_label; GtkWidget *mode_menu; GtkWidget *mode_menu_menu; GtkWidget *glade_menuitem; GtkWidget *col_head_hbox; GtkWidget *use_col_frame; GtkWidget *use_label; GtkWidget *saver_col_frame; GtkWidget *saver_label; GtkWidget *scroller; GtkWidget *viewport; GtkWidget *list; GtkWidget *centering_hbox; GtkWidget *next_prev_hbox; GtkWidget *next; GtkWidget *prev; GtkWidget *preview_frame; GtkWidget *preview_aspectframe; GtkWidget *preview; GtkWidget *demo_tab; GtkWidget *options_table; GtkWidget *diag_frame; GtkWidget *diag_hbox; GtkWidget *diag_logo; GtkWidget *diag_vbox; GtkWidget *verbose_button_eventbox; GtkWidget *verbose_button; GtkWidget *capture_button_eventbox; GtkWidget *capture_button; GtkWidget *splash_button_eventbox; GtkWidget *splash_button; GtkWidget *cmap_frame; GtkWidget *cmap_hbox; GtkWidget *cmap_logo; GtkWidget *cmap_vbox; GtkWidget *install_button_eventbox; GtkWidget *install_button; GtkWidget *cmap_hr; GtkWidget *fade_button_eventbox; GtkWidget *fade_button; GtkWidget *unfade_button_eventbox; GtkWidget *unfade_button; GtkWidget *fade_hbox; GtkWidget *fade_dummy; GtkWidget *fade_label; GtkObject *fade_spinbutton_adj; GtkWidget *fade_spinbutton; GtkWidget *fade_sec_label; GtkWidget *dpms_frame; GtkWidget *dpms_hbox; GtkWidget *dpms_logo; GtkWidget *dpms_vbox; GtkWidget *dpms_button_eventbox; GtkWidget *dpms_button; GtkWidget *dpms_table; GtkObject *dpms_standby_spinbutton_adj; GtkWidget *dpms_standby_spinbutton; GtkWidget *dpms_standby_mlabel; GtkWidget *dpms_suspend_mlabel; GtkWidget *dpms_off_mlabel; GtkWidget *dpms_off_label; GtkWidget *dpms_suspend_label; GtkWidget *dpms_standby_label; GtkObject *dpms_suspend_spinbutton_adj; GtkWidget *dpms_suspend_spinbutton; GtkObject *dpms_off_spinbutton_adj; GtkWidget *dpms_off_spinbutton; GtkWidget *grab_frame; GtkWidget *grab_hbox; GtkWidget *img_logo; GtkWidget *grab_vbox; GtkWidget *grab_desk_eventbox; GtkWidget *grab_desk_button; GtkWidget *grab_video_eventbox; GtkWidget *grab_video_button; GtkWidget *grab_image_eventbox; GtkWidget *grab_image_button; GtkWidget *image_hbox; GtkWidget *grab_dummy; GtkWidget *image_text; GtkWidget *image_browse_button; GtkWidget *options_tab; GtkAccelGroup *accel_group; GtkTooltips *tooltips; tooltips = gtk_tooltips_new (); accel_group = gtk_accel_group_new (); xscreensaver_demo = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_set_name (xscreensaver_demo, "xscreensaver_demo"); gtk_object_set_data (GTK_OBJECT (xscreensaver_demo), "xscreensaver_demo", xscreensaver_demo); gtk_window_set_title (GTK_WINDOW (xscreensaver_demo), _("XScreenSaver")); gtk_window_set_wmclass (GTK_WINDOW (xscreensaver_demo), "xscreensaver", "XScreenSaver"); outer_vbox = gtk_vbox_new (FALSE, 5); gtk_widget_set_name (outer_vbox, "outer_vbox"); gtk_widget_ref (outer_vbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "outer_vbox", outer_vbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (outer_vbox); gtk_container_add (GTK_CONTAINER (xscreensaver_demo), outer_vbox); menubar = gtk_menu_bar_new (); gtk_widget_set_name (menubar, "menubar"); gtk_widget_ref (menubar); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "menubar", menubar, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (menubar); gtk_box_pack_start (GTK_BOX (outer_vbox), menubar, FALSE, FALSE, 0); file = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (file)->child), _("_File")); gtk_widget_add_accelerator (file, "activate_item", accel_group, tmp_key, GDK_MOD1_MASK, (GtkAccelFlags) 0); gtk_widget_set_name (file, "file"); gtk_widget_ref (file); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "file", file, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (file); gtk_container_add (GTK_CONTAINER (menubar), file); file_menu = gtk_menu_new (); gtk_widget_set_name (file_menu, "file_menu"); gtk_widget_ref (file_menu); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "file_menu", file_menu, (GtkDestroyNotify) gtk_widget_unref); gtk_menu_item_set_submenu (GTK_MENU_ITEM (file), file_menu); file_menu_accels = gtk_menu_ensure_uline_accel_group (GTK_MENU (file_menu)); activate_menu = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (activate_menu)->child), _("_Blank Screen Now")); gtk_widget_add_accelerator (activate_menu, "activate_item", file_menu_accels, tmp_key, 0, 0); gtk_widget_set_name (activate_menu, "activate_menu"); gtk_widget_ref (activate_menu); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "activate_menu", activate_menu, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (activate_menu); gtk_container_add (GTK_CONTAINER (file_menu), activate_menu); gtk_tooltips_set_tip (tooltips, activate_menu, _("Activate the XScreenSaver daemon now (locking the screen if so configured.)"), NULL); lock_menu = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (lock_menu)->child), _("_Lock Screen Now")); gtk_widget_add_accelerator (lock_menu, "activate_item", file_menu_accels, tmp_key, 0, 0); gtk_widget_set_name (lock_menu, "lock_menu"); gtk_widget_ref (lock_menu); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "lock_menu", lock_menu, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (lock_menu); gtk_container_add (GTK_CONTAINER (file_menu), lock_menu); gtk_tooltips_set_tip (tooltips, lock_menu, _("Lock the screen now (even if \"Lock Screen\" is unchecked.)"), NULL); kill_menu = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (kill_menu)->child), _("_Kill Daemon")); gtk_widget_add_accelerator (kill_menu, "activate_item", file_menu_accels, tmp_key, 0, 0); gtk_widget_set_name (kill_menu, "kill_menu"); gtk_widget_ref (kill_menu); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "kill_menu", kill_menu, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (kill_menu); gtk_container_add (GTK_CONTAINER (file_menu), kill_menu); gtk_tooltips_set_tip (tooltips, kill_menu, _("Tell the running XScreenSaver daemon to exit."), NULL); restart = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (restart)->child), _("_Restart Daemon")); gtk_widget_add_accelerator (restart, "activate_item", file_menu_accels, tmp_key, 0, 0); gtk_widget_set_name (restart, "restart"); gtk_widget_ref (restart); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "restart", restart, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (restart); gtk_container_add (GTK_CONTAINER (file_menu), restart); gtk_tooltips_set_tip (tooltips, restart, _("Kill and re-launch the XScreenSaver daemon."), NULL); separator1 = gtk_menu_item_new (); gtk_widget_set_name (separator1, "separator1"); gtk_widget_ref (separator1); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "separator1", separator1, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (separator1); gtk_container_add (GTK_CONTAINER (file_menu), separator1); gtk_widget_set_sensitive (separator1, FALSE); exit_menu = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (exit_menu)->child), _("_Exit")); gtk_widget_add_accelerator (exit_menu, "activate_item", file_menu_accels, tmp_key, 0, 0); gtk_widget_set_name (exit_menu, "exit_menu"); gtk_widget_ref (exit_menu); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "exit_menu", exit_menu, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (exit_menu); gtk_container_add (GTK_CONTAINER (file_menu), exit_menu); gtk_tooltips_set_tip (tooltips, exit_menu, _("Exit the xscreensaver-demo program (but leave the XScreenSaver daemon running in the background.)"), NULL); help = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (help)->child), _("_Help")); gtk_widget_add_accelerator (help, "activate_item", accel_group, tmp_key, GDK_MOD1_MASK, (GtkAccelFlags) 0); gtk_widget_set_name (help, "help"); gtk_widget_ref (help); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "help", help, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (help); gtk_container_add (GTK_CONTAINER (menubar), help); help_menu = gtk_menu_new (); gtk_widget_set_name (help_menu, "help_menu"); gtk_widget_ref (help_menu); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "help_menu", help_menu, (GtkDestroyNotify) gtk_widget_unref); gtk_menu_item_set_submenu (GTK_MENU_ITEM (help), help_menu); help_menu_accels = gtk_menu_ensure_uline_accel_group (GTK_MENU (help_menu)); about_menu = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (about_menu)->child), _("_About...")); gtk_widget_add_accelerator (about_menu, "activate_item", help_menu_accels, tmp_key, 0, 0); gtk_widget_set_name (about_menu, "about_menu"); gtk_widget_ref (about_menu); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "about_menu", about_menu, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (about_menu); gtk_container_add (GTK_CONTAINER (help_menu), about_menu); gtk_tooltips_set_tip (tooltips, about_menu, _("Display version information."), NULL); doc_menu = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (doc_menu)->child), _("_Documentation...")); gtk_widget_add_accelerator (doc_menu, "activate_item", help_menu_accels, tmp_key, 0, 0); gtk_widget_set_name (doc_menu, "doc_menu"); gtk_widget_ref (doc_menu); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "doc_menu", doc_menu, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (doc_menu); gtk_container_add (GTK_CONTAINER (help_menu), doc_menu); gtk_tooltips_set_tip (tooltips, doc_menu, _("Go to the documentation on the XScreenSaver web page."), NULL); notebook = gtk_notebook_new (); gtk_widget_set_name (notebook, "notebook"); gtk_widget_ref (notebook); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "notebook", notebook, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (notebook); gtk_box_pack_start (GTK_BOX (outer_vbox), notebook, TRUE, TRUE, 0); demos_table = gtk_table_new (2, 2, FALSE); gtk_widget_set_name (demos_table, "demos_table"); gtk_widget_ref (demos_table); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "demos_table", demos_table, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (demos_table); gtk_container_add (GTK_CONTAINER (notebook), demos_table); gtk_container_set_border_width (GTK_CONTAINER (demos_table), 10); blanking_table = gtk_table_new (3, 4, FALSE); gtk_widget_set_name (blanking_table, "blanking_table"); gtk_widget_ref (blanking_table); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "blanking_table", blanking_table, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (blanking_table); gtk_table_attach (GTK_TABLE (demos_table), blanking_table, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0); gtk_table_set_row_spacings (GTK_TABLE (blanking_table), 2); cycle_label = gtk_label_new (_("Cycle After")); gtk_widget_set_name (cycle_label, "cycle_label"); gtk_widget_ref (cycle_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "cycle_label", cycle_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cycle_label); gtk_table_attach (GTK_TABLE (blanking_table), cycle_label, 1, 2, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (cycle_label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (cycle_label), 1, 0.5); gtk_misc_set_padding (GTK_MISC (cycle_label), 8, 0); lock_button_eventbox = gtk_event_box_new (); gtk_widget_set_name (lock_button_eventbox, "lock_button_eventbox"); gtk_widget_ref (lock_button_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "lock_button_eventbox", lock_button_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (lock_button_eventbox); gtk_table_attach (GTK_TABLE (blanking_table), lock_button_eventbox, 0, 2, 2, 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_tooltips_set_tip (tooltips, lock_button_eventbox, _("Whether a password should be required to un-blank the screen."), NULL); lock_button = gtk_check_button_new_with_label (_("Lock Screen After")); gtk_widget_set_name (lock_button, "lock_button"); gtk_widget_ref (lock_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "lock_button", lock_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (lock_button); gtk_container_add (GTK_CONTAINER (lock_button_eventbox), lock_button); timeout_label = gtk_label_new (_("Blank After")); gtk_widget_set_name (timeout_label, "timeout_label"); gtk_widget_ref (timeout_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "timeout_label", timeout_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (timeout_label); gtk_table_attach (GTK_TABLE (blanking_table), timeout_label, 1, 2, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (timeout_label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (timeout_label), 1, 0.5); gtk_misc_set_padding (GTK_MISC (timeout_label), 8, 0); timeout_spinbutton_adj = gtk_adjustment_new (0, 1, 720, 1, 30, 30); timeout_spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (timeout_spinbutton_adj), 15, 0); gtk_widget_set_name (timeout_spinbutton, "timeout_spinbutton"); gtk_widget_ref (timeout_spinbutton); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "timeout_spinbutton", timeout_spinbutton, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (timeout_spinbutton); gtk_table_attach (GTK_TABLE (blanking_table), timeout_spinbutton, 2, 3, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_tooltips_set_tip (tooltips, timeout_spinbutton, _("How long before the monitor goes completely black."), NULL); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (timeout_spinbutton), TRUE); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (timeout_spinbutton), TRUE); timeout_mlabel = gtk_label_new (_("minutes")); gtk_widget_set_name (timeout_mlabel, "timeout_mlabel"); gtk_widget_ref (timeout_mlabel); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "timeout_mlabel", timeout_mlabel, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (timeout_mlabel); gtk_table_attach (GTK_TABLE (blanking_table), timeout_mlabel, 3, 4, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (timeout_mlabel), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (timeout_mlabel), 0, 0.5); cycle_mlabel = gtk_label_new (_("minutes")); gtk_widget_set_name (cycle_mlabel, "cycle_mlabel"); gtk_widget_ref (cycle_mlabel); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "cycle_mlabel", cycle_mlabel, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cycle_mlabel); gtk_table_attach (GTK_TABLE (blanking_table), cycle_mlabel, 3, 4, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (cycle_mlabel), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (cycle_mlabel), 0, 0.5); lock_mlabel = gtk_label_new (_("minutes")); gtk_widget_set_name (lock_mlabel, "lock_mlabel"); gtk_widget_ref (lock_mlabel); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "lock_mlabel", lock_mlabel, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (lock_mlabel); gtk_table_attach (GTK_TABLE (blanking_table), lock_mlabel, 3, 4, 2, 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (lock_mlabel), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (lock_mlabel), 0, 0.5); lock_spinbutton_adj = gtk_adjustment_new (0, 0, 720, 1, 30, 30); lock_spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (lock_spinbutton_adj), 15, 0); gtk_widget_set_name (lock_spinbutton, "lock_spinbutton"); gtk_widget_ref (lock_spinbutton); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "lock_spinbutton", lock_spinbutton, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (lock_spinbutton); gtk_table_attach (GTK_TABLE (blanking_table), lock_spinbutton, 2, 3, 2, 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 10); gtk_tooltips_set_tip (tooltips, lock_spinbutton, _("How long before the monitor goes completely black."), NULL); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (lock_spinbutton), TRUE); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (lock_spinbutton), TRUE); cycle_spinbutton_adj = gtk_adjustment_new (0, 1, 720, 1, 30, 30); cycle_spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (cycle_spinbutton_adj), 15, 0); gtk_widget_set_name (cycle_spinbutton, "cycle_spinbutton"); gtk_widget_ref (cycle_spinbutton); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "cycle_spinbutton", cycle_spinbutton, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cycle_spinbutton); gtk_table_attach (GTK_TABLE (blanking_table), cycle_spinbutton, 2, 3, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_tooltips_set_tip (tooltips, cycle_spinbutton, _("How long before the monitor goes completely black."), NULL); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (cycle_spinbutton), TRUE); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (cycle_spinbutton), TRUE); demo_manual_hbbox = gtk_hbutton_box_new (); gtk_widget_set_name (demo_manual_hbbox, "demo_manual_hbbox"); gtk_widget_ref (demo_manual_hbbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "demo_manual_hbbox", demo_manual_hbbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (demo_manual_hbbox); gtk_table_attach (GTK_TABLE (demos_table), demo_manual_hbbox, 1, 2, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0); gtk_button_box_set_layout (GTK_BUTTON_BOX (demo_manual_hbbox), GTK_BUTTONBOX_SPREAD); demo = gtk_button_new_with_label (_("Preview")); gtk_widget_set_name (demo, "demo"); gtk_widget_ref (demo); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "demo", demo, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (demo); gtk_container_add (GTK_CONTAINER (demo_manual_hbbox), demo); STFU GTK_WIDGET_SET_FLAGS (demo, GTK_CAN_DEFAULT); gtk_tooltips_set_tip (tooltips, demo, _("Demo the selected screen saver in full-screen mode (click the mouse to return.)"), NULL); settings = gtk_button_new_with_label (_("Settings...")); gtk_widget_set_name (settings, "settings"); gtk_widget_ref (settings); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "settings", settings, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (settings); gtk_container_add (GTK_CONTAINER (demo_manual_hbbox), settings); STFU GTK_WIDGET_SET_FLAGS (settings, GTK_CAN_DEFAULT); gtk_tooltips_set_tip (tooltips, settings, _("Customization and explanation of the selected screen saver."), NULL); list_vbox = gtk_vbox_new (FALSE, 0); gtk_widget_set_name (list_vbox, "list_vbox"); gtk_widget_ref (list_vbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "list_vbox", list_vbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (list_vbox); gtk_table_attach (GTK_TABLE (demos_table), list_vbox, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); gtk_container_set_border_width (GTK_CONTAINER (list_vbox), 10); mode_hbox = gtk_hbox_new (FALSE, 0); gtk_widget_set_name (mode_hbox, "mode_hbox"); gtk_widget_ref (mode_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "mode_hbox", mode_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (mode_hbox); gtk_box_pack_start (GTK_BOX (list_vbox), mode_hbox, FALSE, TRUE, 10); mode_label = gtk_label_new (_("Mode:")); gtk_widget_set_name (mode_label, "mode_label"); gtk_widget_ref (mode_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "mode_label", mode_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (mode_label); gtk_box_pack_start (GTK_BOX (mode_hbox), mode_label, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (mode_label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (mode_label), 0, 0.5); mode_menu = gtk_option_menu_new (); gtk_widget_set_name (mode_menu, "mode_menu"); gtk_widget_ref (mode_menu); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "mode_menu", mode_menu, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (mode_menu); gtk_box_pack_start (GTK_BOX (mode_hbox), mode_menu, FALSE, FALSE, 4); mode_menu_menu = gtk_menu_new (); glade_menuitem = gtk_menu_item_new_with_label (_("Disable Screen Saver")); gtk_widget_show (glade_menuitem); gtk_menu_append (GTK_MENU (mode_menu_menu), glade_menuitem); glade_menuitem = gtk_menu_item_new_with_label (_("Blank Screen Only")); gtk_widget_show (glade_menuitem); gtk_menu_append (GTK_MENU (mode_menu_menu), glade_menuitem); glade_menuitem = gtk_menu_item_new_with_label (_("Only One Screen Saver")); gtk_widget_show (glade_menuitem); gtk_menu_append (GTK_MENU (mode_menu_menu), glade_menuitem); glade_menuitem = gtk_menu_item_new_with_label (_("Random Screen Saver")); gtk_widget_show (glade_menuitem); gtk_menu_append (GTK_MENU (mode_menu_menu), glade_menuitem); gtk_option_menu_set_menu (GTK_OPTION_MENU (mode_menu), mode_menu_menu); gtk_option_menu_set_history (GTK_OPTION_MENU (mode_menu), 3); col_head_hbox = gtk_hbox_new (FALSE, 0); gtk_widget_set_name (col_head_hbox, "col_head_hbox"); gtk_widget_ref (col_head_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "col_head_hbox", col_head_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (col_head_hbox); gtk_box_pack_start (GTK_BOX (list_vbox), col_head_hbox, FALSE, TRUE, 0); use_col_frame = gtk_frame_new (NULL); gtk_widget_set_name (use_col_frame, "use_col_frame"); gtk_widget_ref (use_col_frame); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "use_col_frame", use_col_frame, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (use_col_frame); gtk_box_pack_start (GTK_BOX (col_head_hbox), use_col_frame, FALSE, FALSE, 0); gtk_frame_set_shadow_type (GTK_FRAME (use_col_frame), GTK_SHADOW_OUT); use_label = gtk_label_new (_("Use")); gtk_widget_set_name (use_label, "use_label"); gtk_widget_ref (use_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "use_label", use_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (use_label); gtk_container_add (GTK_CONTAINER (use_col_frame), use_label); gtk_label_set_justify (GTK_LABEL (use_label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (use_label), 0, 0.5); gtk_misc_set_padding (GTK_MISC (use_label), 3, 0); saver_col_frame = gtk_frame_new (NULL); gtk_widget_set_name (saver_col_frame, "saver_col_frame"); gtk_widget_ref (saver_col_frame); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "saver_col_frame", saver_col_frame, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (saver_col_frame); gtk_box_pack_start (GTK_BOX (col_head_hbox), saver_col_frame, TRUE, TRUE, 0); gtk_frame_set_shadow_type (GTK_FRAME (saver_col_frame), GTK_SHADOW_OUT); saver_label = gtk_label_new (_("Screen Saver")); gtk_widget_set_name (saver_label, "saver_label"); gtk_widget_ref (saver_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "saver_label", saver_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (saver_label); gtk_container_add (GTK_CONTAINER (saver_col_frame), saver_label); gtk_label_set_justify (GTK_LABEL (saver_label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (saver_label), 0, 0.5); gtk_misc_set_padding (GTK_MISC (saver_label), 6, 0); scroller = gtk_scrolled_window_new (NULL, NULL); gtk_widget_set_name (scroller, "scroller"); gtk_widget_ref (scroller); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "scroller", scroller, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (scroller); gtk_box_pack_start (GTK_BOX (list_vbox), scroller, TRUE, TRUE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); viewport = gtk_viewport_new (NULL, NULL); gtk_widget_set_name (viewport, "viewport"); gtk_widget_ref (viewport); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "viewport", viewport, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (viewport); gtk_container_add (GTK_CONTAINER (scroller), viewport); gtk_container_set_border_width (GTK_CONTAINER (viewport), 1); list = gtk_list_new (); gtk_widget_set_name (list, "list"); gtk_widget_ref (list); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "list", list, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (list); gtk_container_add (GTK_CONTAINER (viewport), list); centering_hbox = gtk_hbox_new (TRUE, 0); gtk_widget_set_name (centering_hbox, "centering_hbox"); gtk_widget_ref (centering_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "centering_hbox", centering_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (centering_hbox); gtk_box_pack_end (GTK_BOX (list_vbox), centering_hbox, FALSE, TRUE, 0); next_prev_hbox = gtk_hbox_new (FALSE, 0); gtk_widget_set_name (next_prev_hbox, "next_prev_hbox"); gtk_widget_ref (next_prev_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "next_prev_hbox", next_prev_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (next_prev_hbox); gtk_box_pack_start (GTK_BOX (centering_hbox), next_prev_hbox, FALSE, FALSE, 0); next = gtk_button_new_with_label (_("\\/")); gtk_widget_set_name (next, "next"); gtk_widget_ref (next); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "next", next, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (next); gtk_box_pack_start (GTK_BOX (next_prev_hbox), next, FALSE, FALSE, 0); STFU GTK_WIDGET_SET_FLAGS (next, GTK_CAN_DEFAULT); gtk_tooltips_set_tip (tooltips, next, _("Run the next screen saver in the list in full-screen mode (click the mouse to return.)"), NULL); prev = gtk_button_new_with_label (_("/\\")); gtk_widget_set_name (prev, "prev"); gtk_widget_ref (prev); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "prev", prev, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (prev); gtk_box_pack_start (GTK_BOX (next_prev_hbox), prev, FALSE, FALSE, 0); STFU GTK_WIDGET_SET_FLAGS (prev, GTK_CAN_DEFAULT); gtk_tooltips_set_tip (tooltips, prev, _("Run the previous screen saver in the list in full-screen mode (click the mouse to return.)"), NULL); preview_frame = gtk_frame_new (_("Description")); gtk_widget_set_name (preview_frame, "preview_frame"); gtk_widget_ref (preview_frame); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "preview_frame", preview_frame, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (preview_frame); gtk_table_attach (GTK_TABLE (demos_table), preview_frame, 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), 0, 6); preview_aspectframe = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.33, FALSE); gtk_widget_set_name (preview_aspectframe, "preview_aspectframe"); gtk_widget_ref (preview_aspectframe); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "preview_aspectframe", preview_aspectframe, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (preview_aspectframe); gtk_container_add (GTK_CONTAINER (preview_frame), preview_aspectframe); gtk_container_set_border_width (GTK_CONTAINER (preview_aspectframe), 8); preview = gtk_drawing_area_new (); gtk_widget_set_name (preview, "preview"); gtk_widget_ref (preview); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "preview", preview, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (preview); gtk_container_add (GTK_CONTAINER (preview_aspectframe), preview); demo_tab = gtk_label_new (_("Display Modes")); gtk_widget_set_name (demo_tab, "demo_tab"); gtk_widget_ref (demo_tab); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "demo_tab", demo_tab, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (demo_tab); gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 0), demo_tab); options_table = gtk_table_new (2, 2, TRUE); gtk_widget_set_name (options_table, "options_table"); gtk_widget_ref (options_table); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "options_table", options_table, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (options_table); gtk_container_add (GTK_CONTAINER (notebook), options_table); diag_frame = gtk_frame_new (_("Diagnostics")); gtk_widget_set_name (diag_frame, "diag_frame"); gtk_widget_ref (diag_frame); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "diag_frame", diag_frame, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (diag_frame); gtk_table_attach (GTK_TABLE (options_table), diag_frame, 0, 1, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); gtk_container_set_border_width (GTK_CONTAINER (diag_frame), 10); diag_hbox = gtk_hbox_new (FALSE, 8); gtk_widget_set_name (diag_hbox, "diag_hbox"); gtk_widget_ref (diag_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "diag_hbox", diag_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (diag_hbox); gtk_container_add (GTK_CONTAINER (diag_frame), diag_hbox); gtk_container_set_border_width (GTK_CONTAINER (diag_hbox), 8); diag_logo = create_pixmap (xscreensaver_demo, "screensaver-diagnostic.png"); gtk_widget_set_name (diag_logo, "diag_logo"); gtk_widget_ref (diag_logo); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "diag_logo", diag_logo, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (diag_logo); gtk_box_pack_start (GTK_BOX (diag_hbox), diag_logo, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (diag_logo), 0.5, 0); diag_vbox = gtk_vbox_new (FALSE, 0); gtk_widget_set_name (diag_vbox, "diag_vbox"); gtk_widget_ref (diag_vbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "diag_vbox", diag_vbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (diag_vbox); gtk_box_pack_start (GTK_BOX (diag_hbox), diag_vbox, TRUE, TRUE, 0); verbose_button_eventbox = gtk_event_box_new (); gtk_widget_set_name (verbose_button_eventbox, "verbose_button_eventbox"); gtk_widget_ref (verbose_button_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "verbose_button_eventbox", verbose_button_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (verbose_button_eventbox); gtk_box_pack_start (GTK_BOX (diag_vbox), verbose_button_eventbox, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, verbose_button_eventbox, _("Whether the daemon should print lots of debugging information."), NULL); verbose_button = gtk_check_button_new_with_label (_("Verbose Diagnostics")); gtk_widget_set_name (verbose_button, "verbose_button"); gtk_widget_ref (verbose_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "verbose_button", verbose_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (verbose_button); gtk_container_add (GTK_CONTAINER (verbose_button_eventbox), verbose_button); capture_button_eventbox = gtk_event_box_new (); gtk_widget_set_name (capture_button_eventbox, "capture_button_eventbox"); gtk_widget_ref (capture_button_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "capture_button_eventbox", capture_button_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (capture_button_eventbox); gtk_box_pack_start (GTK_BOX (diag_vbox), capture_button_eventbox, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, capture_button_eventbox, _("Whether any error output of the display modes should be redirected to the screen."), NULL); capture_button = gtk_check_button_new_with_label (_("Display Subprocess Errors")); gtk_widget_set_name (capture_button, "capture_button"); gtk_widget_ref (capture_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "capture_button", capture_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (capture_button); gtk_container_add (GTK_CONTAINER (capture_button_eventbox), capture_button); splash_button_eventbox = gtk_event_box_new (); gtk_widget_set_name (splash_button_eventbox, "splash_button_eventbox"); gtk_widget_ref (splash_button_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "splash_button_eventbox", splash_button_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (splash_button_eventbox); gtk_box_pack_start (GTK_BOX (diag_vbox), splash_button_eventbox, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, splash_button_eventbox, _("Whether the splash screen (with the version number and `Help' button) should be momentarily displayed when the daemon first starts up."), NULL); splash_button = gtk_check_button_new_with_label (_("Display Splash Screen at Startup")); gtk_widget_set_name (splash_button, "splash_button"); gtk_widget_ref (splash_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "splash_button", splash_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (splash_button); gtk_container_add (GTK_CONTAINER (splash_button_eventbox), splash_button); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (splash_button), TRUE); cmap_frame = gtk_frame_new (_("Colormaps")); gtk_widget_set_name (cmap_frame, "cmap_frame"); gtk_widget_ref (cmap_frame); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "cmap_frame", cmap_frame, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cmap_frame); gtk_table_attach (GTK_TABLE (options_table), cmap_frame, 1, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0); gtk_container_set_border_width (GTK_CONTAINER (cmap_frame), 10); cmap_hbox = gtk_hbox_new (FALSE, 8); gtk_widget_set_name (cmap_hbox, "cmap_hbox"); gtk_widget_ref (cmap_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "cmap_hbox", cmap_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cmap_hbox); gtk_container_add (GTK_CONTAINER (cmap_frame), cmap_hbox); gtk_container_set_border_width (GTK_CONTAINER (cmap_hbox), 8); cmap_logo = create_pixmap (xscreensaver_demo, "screensaver-colorselector.png"); gtk_widget_set_name (cmap_logo, "cmap_logo"); gtk_widget_ref (cmap_logo); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "cmap_logo", cmap_logo, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cmap_logo); gtk_box_pack_start (GTK_BOX (cmap_hbox), cmap_logo, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (cmap_logo), 0.5, 0); cmap_vbox = gtk_vbox_new (FALSE, 0); gtk_widget_set_name (cmap_vbox, "cmap_vbox"); gtk_widget_ref (cmap_vbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "cmap_vbox", cmap_vbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cmap_vbox); gtk_box_pack_start (GTK_BOX (cmap_hbox), cmap_vbox, TRUE, TRUE, 0); install_button_eventbox = gtk_event_box_new (); gtk_widget_set_name (install_button_eventbox, "install_button_eventbox"); gtk_widget_ref (install_button_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "install_button_eventbox", install_button_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (install_button_eventbox); gtk_box_pack_start (GTK_BOX (cmap_vbox), install_button_eventbox, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, install_button_eventbox, _("Whether to install a private colormap when running in 8-bit mode on the default Visual."), NULL); install_button = gtk_check_button_new_with_label (_("Install Colormap")); gtk_widget_set_name (install_button, "install_button"); gtk_widget_ref (install_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "install_button", install_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (install_button); gtk_container_add (GTK_CONTAINER (install_button_eventbox), install_button); cmap_hr = gtk_hseparator_new (); gtk_widget_set_name (cmap_hr, "cmap_hr"); gtk_widget_ref (cmap_hr); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "cmap_hr", cmap_hr, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cmap_hr); gtk_box_pack_start (GTK_BOX (cmap_vbox), cmap_hr, FALSE, FALSE, 4); fade_button_eventbox = gtk_event_box_new (); gtk_widget_set_name (fade_button_eventbox, "fade_button_eventbox"); gtk_widget_ref (fade_button_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "fade_button_eventbox", fade_button_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (fade_button_eventbox); gtk_box_pack_start (GTK_BOX (cmap_vbox), fade_button_eventbox, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, fade_button_eventbox, _("Whether the screen should slowly fade to black when the screen saver activates."), NULL); fade_button = gtk_check_button_new_with_label (_("Fade To Black When Blanking")); gtk_widget_set_name (fade_button, "fade_button"); gtk_widget_ref (fade_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "fade_button", fade_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (fade_button); gtk_container_add (GTK_CONTAINER (fade_button_eventbox), fade_button); unfade_button_eventbox = gtk_event_box_new (); gtk_widget_set_name (unfade_button_eventbox, "unfade_button_eventbox"); gtk_widget_ref (unfade_button_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "unfade_button_eventbox", unfade_button_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (unfade_button_eventbox); gtk_box_pack_start (GTK_BOX (cmap_vbox), unfade_button_eventbox, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, unfade_button_eventbox, _("Whether the screen should slowly fade in from black when the screen saver deactivates."), NULL); unfade_button = gtk_check_button_new_with_label (_("Fade From Black When Unblanking")); gtk_widget_set_name (unfade_button, "unfade_button"); gtk_widget_ref (unfade_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "unfade_button", unfade_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (unfade_button); gtk_container_add (GTK_CONTAINER (unfade_button_eventbox), unfade_button); fade_hbox = gtk_hbox_new (FALSE, 0); gtk_widget_set_name (fade_hbox, "fade_hbox"); gtk_widget_ref (fade_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "fade_hbox", fade_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (fade_hbox); gtk_box_pack_start (GTK_BOX (cmap_vbox), fade_hbox, FALSE, FALSE, 0); fade_dummy = gtk_label_new (""); gtk_widget_set_name (fade_dummy, "fade_dummy"); gtk_widget_ref (fade_dummy); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "fade_dummy", fade_dummy, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (fade_dummy); gtk_box_pack_start (GTK_BOX (fade_hbox), fade_dummy, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (fade_dummy), GTK_JUSTIFY_LEFT); gtk_misc_set_padding (GTK_MISC (fade_dummy), 3, 0); fade_label = gtk_label_new (_("Fade Duration")); gtk_widget_set_name (fade_label, "fade_label"); gtk_widget_ref (fade_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "fade_label", fade_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (fade_label); gtk_box_pack_start (GTK_BOX (fade_hbox), fade_label, FALSE, FALSE, 10); gtk_label_set_justify (GTK_LABEL (fade_label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (fade_label), 0, 0.5); fade_spinbutton_adj = gtk_adjustment_new (0, 0, 10, 1, 1, 1); fade_spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (fade_spinbutton_adj), 1, 0); gtk_widget_set_name (fade_spinbutton, "fade_spinbutton"); gtk_widget_ref (fade_spinbutton); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "fade_spinbutton", fade_spinbutton, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (fade_spinbutton); gtk_box_pack_start (GTK_BOX (fade_hbox), fade_spinbutton, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, fade_spinbutton, _("How long it should take for the screen to fade in and out."), NULL); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (fade_spinbutton), TRUE); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (fade_spinbutton), TRUE); fade_sec_label = gtk_label_new (_("seconds")); gtk_widget_set_name (fade_sec_label, "fade_sec_label"); gtk_widget_ref (fade_sec_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "fade_sec_label", fade_sec_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (fade_sec_label); gtk_box_pack_start (GTK_BOX (fade_hbox), fade_sec_label, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (fade_sec_label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (fade_sec_label), 0, 0.5); dpms_frame = gtk_frame_new (_("Display Power Management")); gtk_widget_set_name (dpms_frame, "dpms_frame"); gtk_widget_ref (dpms_frame); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_frame", dpms_frame, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_frame); gtk_table_attach (GTK_TABLE (options_table), dpms_frame, 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0); gtk_container_set_border_width (GTK_CONTAINER (dpms_frame), 10); dpms_hbox = gtk_hbox_new (FALSE, 8); gtk_widget_set_name (dpms_hbox, "dpms_hbox"); gtk_widget_ref (dpms_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_hbox", dpms_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_hbox); gtk_container_add (GTK_CONTAINER (dpms_frame), dpms_hbox); gtk_container_set_border_width (GTK_CONTAINER (dpms_hbox), 8); dpms_logo = create_pixmap (xscreensaver_demo, "screensaver-power.png"); gtk_widget_set_name (dpms_logo, "dpms_logo"); gtk_widget_ref (dpms_logo); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_logo", dpms_logo, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_logo); gtk_box_pack_start (GTK_BOX (dpms_hbox), dpms_logo, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (dpms_logo), 0.5, 0); dpms_vbox = gtk_vbox_new (FALSE, 0); gtk_widget_set_name (dpms_vbox, "dpms_vbox"); gtk_widget_ref (dpms_vbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_vbox", dpms_vbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_vbox); gtk_box_pack_start (GTK_BOX (dpms_hbox), dpms_vbox, FALSE, FALSE, 0); dpms_button_eventbox = gtk_event_box_new (); gtk_widget_set_name (dpms_button_eventbox, "dpms_button_eventbox"); gtk_widget_ref (dpms_button_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_button_eventbox", dpms_button_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_button_eventbox); gtk_box_pack_start (GTK_BOX (dpms_vbox), dpms_button_eventbox, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, dpms_button_eventbox, _("Whether the monitor should be powered down after a while."), NULL); dpms_button = gtk_check_button_new_with_label (_("Power Management Enabled")); gtk_widget_set_name (dpms_button, "dpms_button"); gtk_widget_ref (dpms_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_button", dpms_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_button); gtk_container_add (GTK_CONTAINER (dpms_button_eventbox), dpms_button); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dpms_button), TRUE); dpms_table = gtk_table_new (3, 3, FALSE); gtk_widget_set_name (dpms_table, "dpms_table"); gtk_widget_ref (dpms_table); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_table", dpms_table, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_table); gtk_box_pack_start (GTK_BOX (dpms_vbox), dpms_table, FALSE, FALSE, 0); gtk_table_set_row_spacings (GTK_TABLE (dpms_table), 2); dpms_standby_spinbutton_adj = gtk_adjustment_new (0, 0, 1440, 1, 30, 30); dpms_standby_spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (dpms_standby_spinbutton_adj), 15, 0); gtk_widget_set_name (dpms_standby_spinbutton, "dpms_standby_spinbutton"); gtk_widget_ref (dpms_standby_spinbutton); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_standby_spinbutton", dpms_standby_spinbutton, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_standby_spinbutton); gtk_table_attach (GTK_TABLE (dpms_table), dpms_standby_spinbutton, 1, 2, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_tooltips_set_tip (tooltips, dpms_standby_spinbutton, _("How long before the monitor goes completely black."), NULL); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dpms_standby_spinbutton), TRUE); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (dpms_standby_spinbutton), TRUE); dpms_standby_mlabel = gtk_label_new (_("minutes")); gtk_widget_set_name (dpms_standby_mlabel, "dpms_standby_mlabel"); gtk_widget_ref (dpms_standby_mlabel); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_standby_mlabel", dpms_standby_mlabel, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_standby_mlabel); gtk_table_attach (GTK_TABLE (dpms_table), dpms_standby_mlabel, 2, 3, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (dpms_standby_mlabel), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (dpms_standby_mlabel), 0, 0.5); dpms_suspend_mlabel = gtk_label_new (_("minutes")); gtk_widget_set_name (dpms_suspend_mlabel, "dpms_suspend_mlabel"); gtk_widget_ref (dpms_suspend_mlabel); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_suspend_mlabel", dpms_suspend_mlabel, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_suspend_mlabel); gtk_table_attach (GTK_TABLE (dpms_table), dpms_suspend_mlabel, 2, 3, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (dpms_suspend_mlabel), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (dpms_suspend_mlabel), 0, 0.5); dpms_off_mlabel = gtk_label_new (_("minutes")); gtk_widget_set_name (dpms_off_mlabel, "dpms_off_mlabel"); gtk_widget_ref (dpms_off_mlabel); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_off_mlabel", dpms_off_mlabel, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_off_mlabel); gtk_table_attach (GTK_TABLE (dpms_table), dpms_off_mlabel, 2, 3, 2, 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (dpms_off_mlabel), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (dpms_off_mlabel), 0, 0.5); dpms_off_label = gtk_label_new (_("Off After")); gtk_widget_set_name (dpms_off_label, "dpms_off_label"); gtk_widget_ref (dpms_off_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_off_label", dpms_off_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_off_label); gtk_table_attach (GTK_TABLE (dpms_table), dpms_off_label, 0, 1, 2, 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (dpms_off_label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (dpms_off_label), 1, 0.5); gtk_misc_set_padding (GTK_MISC (dpms_off_label), 10, 0); dpms_suspend_label = gtk_label_new (_("Suspend After")); gtk_widget_set_name (dpms_suspend_label, "dpms_suspend_label"); gtk_widget_ref (dpms_suspend_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_suspend_label", dpms_suspend_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_suspend_label); gtk_table_attach (GTK_TABLE (dpms_table), dpms_suspend_label, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (dpms_suspend_label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (dpms_suspend_label), 1, 0.5); gtk_misc_set_padding (GTK_MISC (dpms_suspend_label), 10, 0); dpms_standby_label = gtk_label_new (_("Standby After")); gtk_widget_set_name (dpms_standby_label, "dpms_standby_label"); gtk_widget_ref (dpms_standby_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_standby_label", dpms_standby_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_standby_label); gtk_table_attach (GTK_TABLE (dpms_table), dpms_standby_label, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (dpms_standby_label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (dpms_standby_label), 1, 0.5); gtk_misc_set_padding (GTK_MISC (dpms_standby_label), 10, 0); dpms_suspend_spinbutton_adj = gtk_adjustment_new (0, 0, 1440, 1, 30, 30); dpms_suspend_spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (dpms_suspend_spinbutton_adj), 15, 0); gtk_widget_set_name (dpms_suspend_spinbutton, "dpms_suspend_spinbutton"); gtk_widget_ref (dpms_suspend_spinbutton); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_suspend_spinbutton", dpms_suspend_spinbutton, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_suspend_spinbutton); gtk_table_attach (GTK_TABLE (dpms_table), dpms_suspend_spinbutton, 1, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_tooltips_set_tip (tooltips, dpms_suspend_spinbutton, _("How long until the monitor goes into power-saving mode."), NULL); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dpms_suspend_spinbutton), TRUE); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (dpms_suspend_spinbutton), TRUE); dpms_off_spinbutton_adj = gtk_adjustment_new (0, 0, 1440, 1, 30, 30); dpms_off_spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (dpms_off_spinbutton_adj), 15, 0); gtk_widget_set_name (dpms_off_spinbutton, "dpms_off_spinbutton"); gtk_widget_ref (dpms_off_spinbutton); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "dpms_off_spinbutton", dpms_off_spinbutton, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dpms_off_spinbutton); gtk_table_attach (GTK_TABLE (dpms_table), dpms_off_spinbutton, 1, 2, 2, 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_tooltips_set_tip (tooltips, dpms_off_spinbutton, _("How long until the monitor powers down."), NULL); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dpms_off_spinbutton), TRUE); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (dpms_off_spinbutton), TRUE); grab_frame = gtk_frame_new (_("Image Manipulation")); gtk_widget_set_name (grab_frame, "grab_frame"); gtk_widget_ref (grab_frame); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "grab_frame", grab_frame, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (grab_frame); gtk_table_attach (GTK_TABLE (options_table), grab_frame, 0, 1, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0); gtk_container_set_border_width (GTK_CONTAINER (grab_frame), 10); grab_hbox = gtk_hbox_new (FALSE, 8); gtk_widget_set_name (grab_hbox, "grab_hbox"); gtk_widget_ref (grab_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "grab_hbox", grab_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (grab_hbox); gtk_container_add (GTK_CONTAINER (grab_frame), grab_hbox); gtk_container_set_border_width (GTK_CONTAINER (grab_hbox), 8); img_logo = create_pixmap (xscreensaver_demo, "screensaver-snap.png"); gtk_widget_set_name (img_logo, "img_logo"); gtk_widget_ref (img_logo); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "img_logo", img_logo, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (img_logo); gtk_box_pack_start (GTK_BOX (grab_hbox), img_logo, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (img_logo), 0.5, 0); grab_vbox = gtk_vbox_new (FALSE, 0); gtk_widget_set_name (grab_vbox, "grab_vbox"); gtk_widget_ref (grab_vbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "grab_vbox", grab_vbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (grab_vbox); gtk_box_pack_start (GTK_BOX (grab_hbox), grab_vbox, TRUE, TRUE, 0); grab_desk_eventbox = gtk_event_box_new (); gtk_widget_set_name (grab_desk_eventbox, "grab_desk_eventbox"); gtk_widget_ref (grab_desk_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "grab_desk_eventbox", grab_desk_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (grab_desk_eventbox); gtk_box_pack_start (GTK_BOX (grab_vbox), grab_desk_eventbox, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, grab_desk_eventbox, _("Whether the image-manipulating modes should be allowed to operate on an image of your desktop."), NULL); grab_desk_button = gtk_check_button_new_with_label (_("Grab Desktop Images")); gtk_widget_set_name (grab_desk_button, "grab_desk_button"); gtk_widget_ref (grab_desk_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "grab_desk_button", grab_desk_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (grab_desk_button); gtk_container_add (GTK_CONTAINER (grab_desk_eventbox), grab_desk_button); grab_video_eventbox = gtk_event_box_new (); gtk_widget_set_name (grab_video_eventbox, "grab_video_eventbox"); gtk_widget_ref (grab_video_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "grab_video_eventbox", grab_video_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (grab_video_eventbox); gtk_box_pack_start (GTK_BOX (grab_vbox), grab_video_eventbox, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, grab_video_eventbox, _("Whether the image-manipulating modes should operate on images captured from the system's video input (if there is one)."), NULL); grab_video_button = gtk_check_button_new_with_label (_("Grab Video Frames")); gtk_widget_set_name (grab_video_button, "grab_video_button"); gtk_widget_ref (grab_video_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "grab_video_button", grab_video_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (grab_video_button); gtk_container_add (GTK_CONTAINER (grab_video_eventbox), grab_video_button); grab_image_eventbox = gtk_event_box_new (); gtk_widget_set_name (grab_image_eventbox, "grab_image_eventbox"); gtk_widget_ref (grab_image_eventbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "grab_image_eventbox", grab_image_eventbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (grab_image_eventbox); gtk_box_pack_start (GTK_BOX (grab_vbox), grab_image_eventbox, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, grab_image_eventbox, _("Whether the image-manipulating modes should load image files."), NULL); grab_image_button = gtk_check_button_new_with_label (_("Choose Random Image:")); gtk_widget_set_name (grab_image_button, "grab_image_button"); gtk_widget_ref (grab_image_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "grab_image_button", grab_image_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (grab_image_button); gtk_container_add (GTK_CONTAINER (grab_image_eventbox), grab_image_button); image_hbox = gtk_hbox_new (FALSE, 0); gtk_widget_set_name (image_hbox, "image_hbox"); gtk_widget_ref (image_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "image_hbox", image_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (image_hbox); gtk_box_pack_start (GTK_BOX (grab_vbox), image_hbox, FALSE, FALSE, 0); grab_dummy = gtk_label_new (""); gtk_widget_set_name (grab_dummy, "grab_dummy"); gtk_widget_ref (grab_dummy); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "grab_dummy", grab_dummy, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (grab_dummy); gtk_box_pack_start (GTK_BOX (image_hbox), grab_dummy, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (grab_dummy), GTK_JUSTIFY_LEFT); gtk_misc_set_padding (GTK_MISC (grab_dummy), 8, 0); image_text = gtk_entry_new (); gtk_widget_set_name (image_text, "image_text"); gtk_widget_ref (image_text); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "image_text", image_text, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (image_text); gtk_box_pack_start (GTK_BOX (image_hbox), image_text, TRUE, TRUE, 0); gtk_tooltips_set_tip (tooltips, image_text, _("The local directory, RSS feed or Atom feed from which images will be randomly chosen."), NULL); image_browse_button = gtk_button_new_with_label (_("Browse")); gtk_widget_set_name (image_browse_button, "image_browse_button"); gtk_widget_ref (image_browse_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "image_browse_button", image_browse_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (image_browse_button); gtk_box_pack_start (GTK_BOX (image_hbox), image_browse_button, FALSE, FALSE, 4); options_tab = gtk_label_new (_("Advanced")); gtk_widget_set_name (options_tab, "options_tab"); gtk_widget_ref (options_tab); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_demo), "options_tab", options_tab, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (options_tab); gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 1), options_tab); gtk_signal_connect (GTK_OBJECT (activate_menu), "activate", GTK_SIGNAL_FUNC (activate_menu_cb), NULL); gtk_signal_connect (GTK_OBJECT (lock_menu), "activate", GTK_SIGNAL_FUNC (lock_menu_cb), NULL); gtk_signal_connect (GTK_OBJECT (kill_menu), "activate", GTK_SIGNAL_FUNC (kill_menu_cb), NULL); gtk_signal_connect (GTK_OBJECT (restart), "activate", GTK_SIGNAL_FUNC (restart_menu_cb), NULL); gtk_signal_connect (GTK_OBJECT (exit_menu), "activate", GTK_SIGNAL_FUNC (exit_menu_cb), NULL); gtk_signal_connect (GTK_OBJECT (about_menu), "activate", GTK_SIGNAL_FUNC (about_menu_cb), NULL); gtk_signal_connect (GTK_OBJECT (doc_menu), "activate", GTK_SIGNAL_FUNC (doc_menu_cb), NULL); gtk_signal_connect (GTK_OBJECT (notebook), "switch_page", GTK_SIGNAL_FUNC (switch_page_cb), NULL); gtk_signal_connect (GTK_OBJECT (lock_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (timeout_spinbutton), "activate", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (timeout_spinbutton), "focus_out_event", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (timeout_spinbutton), "changed", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (lock_spinbutton), "activate", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (lock_spinbutton), "focus_out_event", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (lock_spinbutton), "changed", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (cycle_spinbutton), "activate", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (cycle_spinbutton), "focus_out_event", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (cycle_spinbutton), "changed", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (demo), "clicked", GTK_SIGNAL_FUNC (run_this_cb), NULL); gtk_signal_connect (GTK_OBJECT (settings), "clicked", GTK_SIGNAL_FUNC (settings_cb), NULL); gtk_signal_connect (GTK_OBJECT (next), "clicked", GTK_SIGNAL_FUNC (run_next_cb), NULL); gtk_signal_connect (GTK_OBJECT (prev), "clicked", GTK_SIGNAL_FUNC (run_prev_cb), NULL); gtk_signal_connect (GTK_OBJECT (verbose_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (capture_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (splash_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (install_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (fade_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (unfade_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (fade_spinbutton), "activate", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (fade_spinbutton), "focus_out_event", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (fade_spinbutton), "changed", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (dpms_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (dpms_standby_spinbutton), "activate", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (dpms_standby_spinbutton), "focus_out_event", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (dpms_standby_spinbutton), "changed", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (dpms_suspend_spinbutton), "activate", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (dpms_suspend_spinbutton), "focus_out_event", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (dpms_suspend_spinbutton), "changed", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (dpms_off_spinbutton), "activate", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (dpms_off_spinbutton), "focus_out_event", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (dpms_off_spinbutton), "changed", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (grab_desk_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (grab_video_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (grab_image_button), "toggled", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (image_text), "activate", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (image_text), "focus_out_event", GTK_SIGNAL_FUNC (pref_changed_cb), NULL); gtk_signal_connect (GTK_OBJECT (image_browse_button), "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cb), NULL); gtk_widget_grab_default (next); gtk_object_set_data (GTK_OBJECT (xscreensaver_demo), "tooltips", tooltips); gtk_window_add_accel_group (GTK_WINDOW (xscreensaver_demo), accel_group); return xscreensaver_demo; } GtkWidget* create_xscreensaver_settings_dialog (void) { GtkWidget *xscreensaver_settings_dialog; GtkWidget *dialog_vbox; GtkWidget *dialog_top_table; GtkWidget *opt_frame; GtkWidget *opt_notebook; GtkWidget *settings_vbox; GtkWidget *std_label; GtkWidget *opt_table; GtkWidget *cmd_logo; GtkWidget *visual_hbox; GtkWidget *visual; GtkWidget *visual_combo; GList *visual_combo_items = NULL; GtkWidget *combo_entry1; GtkWidget *cmd_label; GtkWidget *cmd_text; GtkWidget *adv_label; GtkWidget *doc_frame; GtkWidget *doc_vbox; GtkWidget *doc; GtkWidget *doc_hbuttonbox; GtkWidget *manual; GtkWidget *dialog_action_area; GtkWidget *actionarea_hbox; GtkWidget *dialog_hbuttonbox; GtkWidget *adv_button; GtkWidget *std_button; GtkWidget *reset_button; GtkWidget *ok_cancel_hbuttonbox; GtkWidget *ok_button; GtkWidget *cancel_button; GtkTooltips *tooltips; tooltips = gtk_tooltips_new (); xscreensaver_settings_dialog = gtk_dialog_new (); gtk_widget_set_name (xscreensaver_settings_dialog, "xscreensaver_settings_dialog"); gtk_object_set_data (GTK_OBJECT (xscreensaver_settings_dialog), "xscreensaver_settings_dialog", xscreensaver_settings_dialog); gtk_window_set_title (GTK_WINDOW (xscreensaver_settings_dialog), _("XScreenSaver: Mode-Specific Settings")); GTK_WINDOW (xscreensaver_settings_dialog)->type = GTK_WINDOW_DIALOG; gtk_window_set_modal (GTK_WINDOW (xscreensaver_settings_dialog), TRUE); gtk_window_set_policy (GTK_WINDOW (xscreensaver_settings_dialog), TRUE, TRUE, FALSE); gtk_window_set_wmclass (GTK_WINDOW (xscreensaver_settings_dialog), "settings", "XScreenSaver"); dialog_vbox = GTK_DIALOG (xscreensaver_settings_dialog)->vbox; gtk_widget_set_name (dialog_vbox, "dialog_vbox"); gtk_object_set_data (GTK_OBJECT (xscreensaver_settings_dialog), "dialog_vbox", dialog_vbox); gtk_widget_show (dialog_vbox); dialog_top_table = gtk_table_new (1, 2, FALSE); gtk_widget_set_name (dialog_top_table, "dialog_top_table"); gtk_widget_ref (dialog_top_table); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "dialog_top_table", dialog_top_table, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dialog_top_table); gtk_box_pack_start (GTK_BOX (dialog_vbox), dialog_top_table, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (dialog_top_table), 8); gtk_table_set_row_spacings (GTK_TABLE (dialog_top_table), 8); gtk_table_set_col_spacings (GTK_TABLE (dialog_top_table), 8); opt_frame = gtk_frame_new (_("Settings")); gtk_widget_set_name (opt_frame, "opt_frame"); gtk_widget_ref (opt_frame); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "opt_frame", opt_frame, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (opt_frame); gtk_table_attach (GTK_TABLE (dialog_top_table), opt_frame, 0, 1, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 4, 8); opt_notebook = gtk_notebook_new (); gtk_widget_set_name (opt_notebook, "opt_notebook"); gtk_widget_ref (opt_notebook); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "opt_notebook", opt_notebook, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (opt_notebook); gtk_container_add (GTK_CONTAINER (opt_frame), opt_notebook); gtk_container_set_border_width (GTK_CONTAINER (opt_notebook), 12); gtk_notebook_set_show_border (GTK_NOTEBOOK (opt_notebook), FALSE); gtk_notebook_set_tab_pos (GTK_NOTEBOOK (opt_notebook), GTK_POS_BOTTOM); settings_vbox = gtk_vbox_new (FALSE, 0); gtk_widget_set_name (settings_vbox, "settings_vbox"); gtk_widget_ref (settings_vbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "settings_vbox", settings_vbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (settings_vbox); gtk_container_add (GTK_CONTAINER (opt_notebook), settings_vbox); std_label = gtk_label_new (_("Standard")); gtk_widget_set_name (std_label, "std_label"); gtk_widget_ref (std_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "std_label", std_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (std_label); gtk_notebook_set_tab_label (GTK_NOTEBOOK (opt_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (opt_notebook), 0), std_label); opt_table = gtk_table_new (4, 2, FALSE); gtk_widget_set_name (opt_table, "opt_table"); gtk_widget_ref (opt_table); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "opt_table", opt_table, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (opt_table); gtk_container_add (GTK_CONTAINER (opt_notebook), opt_table); cmd_logo = create_pixmap (xscreensaver_settings_dialog, "screensaver-cmndln.png"); gtk_widget_set_name (cmd_logo, "cmd_logo"); gtk_widget_ref (cmd_logo); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "cmd_logo", cmd_logo, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cmd_logo); gtk_table_attach (GTK_TABLE (opt_table), cmd_logo, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0); gtk_misc_set_padding (GTK_MISC (cmd_logo), 4, 8); visual_hbox = gtk_hbox_new (FALSE, 0); gtk_widget_set_name (visual_hbox, "visual_hbox"); gtk_widget_ref (visual_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "visual_hbox", visual_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (visual_hbox); gtk_table_attach (GTK_TABLE (opt_table), visual_hbox, 1, 2, 3, 4, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0); visual = gtk_label_new (_("Visual:")); gtk_widget_set_name (visual, "visual"); gtk_widget_ref (visual); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "visual", visual, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (visual); gtk_box_pack_start (GTK_BOX (visual_hbox), visual, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (visual), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (visual), 1, 0.5); gtk_misc_set_padding (GTK_MISC (visual), 4, 0); visual_combo = gtk_combo_new (); gtk_widget_set_name (visual_combo, "visual_combo"); gtk_widget_ref (visual_combo); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "visual_combo", visual_combo, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (visual_combo); gtk_box_pack_start (GTK_BOX (visual_hbox), visual_combo, FALSE, FALSE, 0); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("Any")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("Best")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("Default")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("Default-N")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("GL")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("TrueColor")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("PseudoColor")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("StaticGray")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("GrayScale")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("DirectColor")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("Color")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("Gray")); visual_combo_items = g_list_append (visual_combo_items, (gpointer) _("Mono")); gtk_combo_set_popdown_strings (GTK_COMBO (visual_combo), visual_combo_items); g_list_free (visual_combo_items); combo_entry1 = GTK_COMBO (visual_combo)->entry; gtk_widget_set_name (combo_entry1, "combo_entry1"); gtk_widget_ref (combo_entry1); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "combo_entry1", combo_entry1, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (combo_entry1); gtk_tooltips_set_tip (tooltips, combo_entry1, _("The X visual type that this demo will require. If that visual is available it will be used, otherwise, this demo will not be run."), NULL); gtk_entry_set_text (GTK_ENTRY (combo_entry1), _("Any")); cmd_label = gtk_label_new (_("Command Line:")); gtk_widget_set_name (cmd_label, "cmd_label"); gtk_widget_ref (cmd_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "cmd_label", cmd_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cmd_label); gtk_table_attach (GTK_TABLE (opt_table), cmd_label, 1, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (cmd_label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (cmd_label), 0, 1); gtk_misc_set_padding (GTK_MISC (cmd_label), 0, 2); cmd_text = gtk_entry_new (); gtk_widget_set_name (cmd_text, "cmd_text"); gtk_widget_ref (cmd_text); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "cmd_text", cmd_text, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cmd_text); gtk_table_attach (GTK_TABLE (opt_table), cmd_text, 1, 2, 2, 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_widget_set_usize (cmd_text, 80, -2); adv_label = gtk_label_new (_("Advanced")); gtk_widget_set_name (adv_label, "adv_label"); gtk_widget_ref (adv_label); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "adv_label", adv_label, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (adv_label); gtk_notebook_set_tab_label (GTK_NOTEBOOK (opt_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (opt_notebook), 1), adv_label); doc_frame = gtk_frame_new (_("Description")); gtk_widget_set_name (doc_frame, "doc_frame"); gtk_widget_ref (doc_frame); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "doc_frame", doc_frame, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (doc_frame); gtk_table_attach (GTK_TABLE (dialog_top_table), doc_frame, 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), 4, 8); doc_vbox = gtk_vbox_new (FALSE, 0); gtk_widget_set_name (doc_vbox, "doc_vbox"); gtk_widget_ref (doc_vbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "doc_vbox", doc_vbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (doc_vbox); gtk_container_add (GTK_CONTAINER (doc_frame), doc_vbox); doc = gtk_label_new (""); gtk_widget_set_name (doc, "doc"); gtk_widget_ref (doc); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "doc", doc, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (doc); gtk_box_pack_start (GTK_BOX (doc_vbox), doc, TRUE, TRUE, 0); gtk_label_set_justify (GTK_LABEL (doc), GTK_JUSTIFY_LEFT); gtk_label_set_line_wrap (GTK_LABEL (doc), TRUE); gtk_misc_set_alignment (GTK_MISC (doc), 0, 0); gtk_misc_set_padding (GTK_MISC (doc), 10, 10); doc_hbuttonbox = gtk_hbutton_box_new (); gtk_widget_set_name (doc_hbuttonbox, "doc_hbuttonbox"); gtk_widget_ref (doc_hbuttonbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "doc_hbuttonbox", doc_hbuttonbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (doc_hbuttonbox); gtk_box_pack_end (GTK_BOX (doc_vbox), doc_hbuttonbox, FALSE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (doc_hbuttonbox), 4); gtk_button_box_set_layout (GTK_BUTTON_BOX (doc_hbuttonbox), GTK_BUTTONBOX_END); manual = gtk_button_new_with_label (_("Documentation...")); gtk_widget_set_name (manual, "manual"); gtk_widget_ref (manual); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "manual", manual, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (manual); gtk_container_add (GTK_CONTAINER (doc_hbuttonbox), manual); STFU GTK_WIDGET_SET_FLAGS (manual, GTK_CAN_DEFAULT); gtk_tooltips_set_tip (tooltips, manual, _("Click here to read the manual for this display mode, if it has one."), NULL); dialog_action_area = GTK_DIALOG (xscreensaver_settings_dialog)->action_area; gtk_widget_set_name (dialog_action_area, "dialog_action_area"); gtk_object_set_data (GTK_OBJECT (xscreensaver_settings_dialog), "dialog_action_area", dialog_action_area); gtk_widget_show (dialog_action_area); gtk_container_set_border_width (GTK_CONTAINER (dialog_action_area), 10); actionarea_hbox = gtk_hbox_new (FALSE, 0); gtk_widget_set_name (actionarea_hbox, "actionarea_hbox"); gtk_widget_ref (actionarea_hbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "actionarea_hbox", actionarea_hbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (actionarea_hbox); gtk_box_pack_start (GTK_BOX (dialog_action_area), actionarea_hbox, TRUE, TRUE, 0); dialog_hbuttonbox = gtk_hbutton_box_new (); gtk_widget_set_name (dialog_hbuttonbox, "dialog_hbuttonbox"); gtk_widget_ref (dialog_hbuttonbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "dialog_hbuttonbox", dialog_hbuttonbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (dialog_hbuttonbox); gtk_box_pack_start (GTK_BOX (actionarea_hbox), dialog_hbuttonbox, TRUE, TRUE, 0); gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_hbuttonbox), GTK_BUTTONBOX_SPREAD); adv_button = gtk_button_new_with_label (_("Advanced >>")); gtk_widget_set_name (adv_button, "adv_button"); gtk_widget_ref (adv_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "adv_button", adv_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (adv_button); gtk_container_add (GTK_CONTAINER (dialog_hbuttonbox), adv_button); STFU GTK_WIDGET_SET_FLAGS (adv_button, GTK_CAN_DEFAULT); gtk_tooltips_set_tip (tooltips, adv_button, _("Edit the command line directly."), NULL); std_button = gtk_button_new_with_label (_("Standard <<")); gtk_widget_set_name (std_button, "std_button"); gtk_widget_ref (std_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "std_button", std_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (std_button); gtk_container_add (GTK_CONTAINER (dialog_hbuttonbox), std_button); STFU GTK_WIDGET_SET_FLAGS (std_button, GTK_CAN_DEFAULT); gtk_tooltips_set_tip (tooltips, std_button, _("Back to the graphical configuration options."), NULL); reset_button = gtk_button_new_with_label (_("Reset to Defaults")); gtk_widget_set_name (reset_button, "reset_button"); gtk_widget_ref (reset_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "reset_button", reset_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (reset_button); gtk_container_add (GTK_CONTAINER (dialog_hbuttonbox), reset_button); STFU GTK_WIDGET_SET_FLAGS (reset_button, GTK_CAN_DEFAULT); gtk_tooltips_set_tip (tooltips, reset_button, _("Reset this screen saver to the default settings."), NULL); ok_cancel_hbuttonbox = gtk_hbutton_box_new (); gtk_widget_set_name (ok_cancel_hbuttonbox, "ok_cancel_hbuttonbox"); gtk_widget_ref (ok_cancel_hbuttonbox); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "ok_cancel_hbuttonbox", ok_cancel_hbuttonbox, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (ok_cancel_hbuttonbox); gtk_box_pack_start (GTK_BOX (actionarea_hbox), ok_cancel_hbuttonbox, TRUE, TRUE, 0); gtk_button_box_set_layout (GTK_BUTTON_BOX (ok_cancel_hbuttonbox), GTK_BUTTONBOX_END); ok_button = gtk_button_new_with_label (_("OK")); gtk_widget_set_name (ok_button, "ok_button"); gtk_widget_ref (ok_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "ok_button", ok_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (ok_button); gtk_container_add (GTK_CONTAINER (ok_cancel_hbuttonbox), ok_button); STFU GTK_WIDGET_SET_FLAGS (ok_button, GTK_CAN_DEFAULT); cancel_button = gtk_button_new_with_label (_("Cancel")); gtk_widget_set_name (cancel_button, "cancel_button"); gtk_widget_ref (cancel_button); gtk_object_set_data_full (GTK_OBJECT (xscreensaver_settings_dialog), "cancel_button", cancel_button, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (cancel_button); gtk_container_add (GTK_CONTAINER (ok_cancel_hbuttonbox), cancel_button); STFU GTK_WIDGET_SET_FLAGS (cancel_button, GTK_CAN_DEFAULT); gtk_signal_connect (GTK_OBJECT (opt_notebook), "switch_page", GTK_SIGNAL_FUNC (settings_switch_page_cb), NULL); gtk_signal_connect (GTK_OBJECT (manual), "clicked", GTK_SIGNAL_FUNC (manual_cb), NULL); gtk_signal_connect (GTK_OBJECT (adv_button), "clicked", GTK_SIGNAL_FUNC (settings_adv_cb), NULL); gtk_signal_connect (GTK_OBJECT (std_button), "clicked", GTK_SIGNAL_FUNC (settings_std_cb), NULL); gtk_signal_connect (GTK_OBJECT (reset_button), "clicked", GTK_SIGNAL_FUNC (settings_reset_cb), NULL); gtk_signal_connect (GTK_OBJECT (ok_button), "clicked", GTK_SIGNAL_FUNC (settings_ok_cb), NULL); gtk_signal_connect (GTK_OBJECT (cancel_button), "clicked", GTK_SIGNAL_FUNC (settings_cancel_cb), NULL); gtk_object_set_data (GTK_OBJECT (xscreensaver_settings_dialog), "tooltips", tooltips); return xscreensaver_settings_dialog; } xscreensaver-5.15/driver/demo-Gtk-widgets.h000066400000000000000000000002401164314150500207270ustar00rootroot00000000000000/* * DO NOT EDIT THIS FILE - it is generated by Glade. */ GtkWidget* create_xscreensaver_demo (void); GtkWidget* create_xscreensaver_settings_dialog (void); xscreensaver-5.15/driver/demo-Gtk.c000066400000000000000000004565131164314150500173000ustar00rootroot00000000000000/* demo-Gtk.c --- implements the interactive demo-mode and options dialogs. * xscreensaver, Copyright (c) 1993-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_GTK /* whole file */ #include #include #ifdef HAVE_UNISTD_H # include #endif # ifdef __GNUC__ # define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */ # else # define STFU /* */ # endif #ifdef ENABLE_NLS # include #endif /* ENABLE_NLS */ #ifndef VMS # include /* for getpwuid() */ #else /* VMS */ # include "vms-pwd.h" #endif /* VMS */ #ifdef HAVE_UNAME # include /* for uname() */ #endif /* HAVE_UNAME */ #include #include #include #include #include #include #ifdef HAVE_SYS_WAIT_H # include /* for waitpid() and associated macros */ #endif #include /* for CARD32 */ #include /* for XA_INTEGER */ #include #include /* We don't actually use any widget internals, but these are included so that gdb will have debug info for the widgets... */ #include #include #ifdef HAVE_XMU # ifndef VMS # include # else /* VMS */ # include # endif #else # include "xmu.h" #endif #ifdef HAVE_XINERAMA # include #endif /* HAVE_XINERAMA */ #include #ifdef HAVE_CRAPPLET # include # include #endif #include #ifdef HAVE_GTK2 # include # include #else /* !HAVE_GTK2 */ # define G_MODULE_EXPORT /**/ #endif /* !HAVE_GTK2 */ #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR) # define GLADE_DIR DEFAULT_ICONDIR #endif #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR) # define DEFAULT_ICONDIR GLADE_DIR #endif #ifndef HAVE_XML /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML. It is unused otherwise, so in that case, stub it out. */ static const char *hack_configuration_path = 0; #endif #include "version.h" #include "prefs.h" #include "resources.h" /* for parse_time() */ #include "visual.h" /* for has_writable_cells() */ #include "remote.h" /* for xscreensaver_command() */ #include "usleep.h" #include "logo-50.xpm" #include "logo-180.xpm" #undef dgettext /* else these are defined twice... */ #undef dcgettext #include "demo-Gtk-widgets.h" #include "demo-Gtk-support.h" #include "demo-Gtk-conf.h" #include #include #include #ifdef HAVE_GTK2 enum { COL_ENABLED, COL_NAME, COL_LAST }; #endif /* HAVE_GTK2 */ /* Deal with deprecation of direct access to struct fields on the way to GTK3 See http://live.gnome.org/GnomeGoals/UseGseal */ #if GTK_CHECK_VERSION(2,14,0) # define GET_PARENT(w) gtk_widget_get_parent (w) # define GET_WINDOW(w) gtk_widget_get_window (w) # define GET_ACTION_AREA(d) gtk_dialog_get_action_area (d) # define GET_CONTENT_AREA(d) gtk_dialog_get_content_area (d) # define GET_ADJ_VALUE(a) gtk_adjustment_get_value (a) # define SET_ADJ_VALUE(a,v) gtk_adjustment_set_value (a, v) # define SET_ADJ_UPPER(a,v) gtk_adjustment_set_upper (a, v) #else # define GET_PARENT(w) ((w)->parent) # define GET_WINDOW(w) ((w)->window) # define GET_ACTION_AREA(d) ((d)->action_area) # define GET_CONTENT_AREA(d) ((d)->vbox) # define GET_ADJ_VALUE(a) ((a)->value) # define SET_ADJ_VALUE(a,v) (a)->value = v # define SET_ADJ_UPPER(a,v) (a)->upper = v #endif #if GTK_CHECK_VERSION(2,18,0) # define SET_CAN_DEFAULT(w) gtk_widget_set_can_default ((w), TRUE) # define GET_SENSITIVE(w) gtk_widget_get_sensitive (w) #else # define SET_CAN_DEFAULT(w) GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT) # define GET_SENSITIVE(w) GTK_WIDGET_IS_SENSITIVE (w) #endif #if GTK_CHECK_VERSION(2,20,0) # define GET_REALIZED(w) gtk_widget_get_realized (w) #else # define GET_REALIZED(w) GTK_WIDGET_REALIZED (w) #endif /* from exec.c */ extern void exec_command (const char *shell, const char *command, int nice); extern int on_path_p (const char *program); static void hack_subproc_environment (Window preview_window_id, Bool debug_p); #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) /* You might think that to read an array of 32-bit quantities out of a server-side property, you would pass an array of 32-bit data quantities into XGetWindowProperty(). You would be wrong. You have to use an array of longs, even if long is 64 bits (using 32 of each 64.) */ typedef long PROP32; char *progname = 0; char *progclass = "XScreenSaver"; XrmDatabase db; /* The order of the items in the mode menu. */ static int mode_menu_order[] = { DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME }; typedef struct { char *short_version; /* version number of this xscreensaver build */ GtkWidget *toplevel_widget; /* the main window */ GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */ GtkWidget *popup_widget; /* the "Settings" dialog */ conf_data *cdata; /* private data for per-hack configuration */ #ifdef HAVE_GTK2 GladeXML *glade_ui; /* Glade UI file */ #endif /* HAVE_GTK2 */ Bool debug_p; /* whether to print diagnostics */ Bool initializing_p; /* flag for breaking recursion loops */ Bool saving_p; /* flag for breaking recursion loops */ char *desired_preview_cmd; /* subprocess we intend to run */ char *running_preview_cmd; /* subprocess we are currently running */ pid_t running_preview_pid; /* pid of forked subproc (might be dead) */ Bool running_preview_error_p; /* whether the pid died abnormally */ Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */ int subproc_timer_id; /* timer to delay subproc launch */ int subproc_check_timer_id; /* timer to check whether it started up */ int subproc_check_countdown; /* how many more checks left */ int *list_elt_to_hack_number; /* table for sorting the hack list */ int *hack_number_to_list_elt; /* the inverse table */ Bool *hacks_available_p; /* whether hacks are on $PATH */ int total_available; /* how many are on $PATH */ int list_count; /* how many items are in the list: this may be less than p->screenhacks_count, if some are suppressed. */ int _selected_list_element; /* don't use this: call selected_list_element() instead */ int nscreens; /* How many X or Xinerama screens there are */ saver_preferences prefs; } state; /* Total fucking evilness due to the fact that it's rocket science to get a closure object of our own down into the various widget callbacks. */ static state *global_state_kludge; Atom XA_VROOT; Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION; Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO; Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT; static void populate_demo_window (state *, int list_elt); static void populate_prefs_page (state *); static void populate_popup_window (state *); static Bool flush_dialog_changes_and_save (state *); static Bool flush_popup_changes_and_save (state *); static int maybe_reload_init_file (state *); static void await_xscreensaver (state *); static Bool xscreensaver_running_p (state *); static void sensitize_menu_items (state *s, Bool force_p); static void force_dialog_repaint (state *s); static void schedule_preview (state *, const char *cmd); static void kill_preview_subproc (state *, Bool reset_p); static void schedule_preview_check (state *); /* Prototypes of functions used by the Glade-generated code, to avoid warnings. */ void exit_menu_cb (GtkMenuItem *, gpointer user_data); void about_menu_cb (GtkMenuItem *, gpointer user_data); void doc_menu_cb (GtkMenuItem *, gpointer user_data); void file_menu_cb (GtkMenuItem *, gpointer user_data); void activate_menu_cb (GtkMenuItem *, gpointer user_data); void lock_menu_cb (GtkMenuItem *, gpointer user_data); void kill_menu_cb (GtkMenuItem *, gpointer user_data); void restart_menu_cb (GtkWidget *, gpointer user_data); void run_this_cb (GtkButton *, gpointer user_data); void manual_cb (GtkButton *, gpointer user_data); void run_next_cb (GtkButton *, gpointer user_data); void run_prev_cb (GtkButton *, gpointer user_data); void pref_changed_cb (GtkWidget *, gpointer user_data); gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data); void mode_menu_item_cb (GtkWidget *, gpointer user_data); void switch_page_cb (GtkNotebook *, GtkNotebookPage *, gint page_num, gpointer user_data); void browse_image_dir_cb (GtkButton *, gpointer user_data); void browse_text_file_cb (GtkButton *, gpointer user_data); void browse_text_program_cb (GtkButton *, gpointer user_data); void settings_cb (GtkButton *, gpointer user_data); void settings_adv_cb (GtkButton *, gpointer user_data); void settings_std_cb (GtkButton *, gpointer user_data); void settings_reset_cb (GtkButton *, gpointer user_data); void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *, gint page_num, gpointer user_data); void settings_cancel_cb (GtkButton *, gpointer user_data); void settings_ok_cb (GtkButton *, gpointer user_data); static void kill_gnome_screensaver (void); static void kill_kde_screensaver (void); /* Some random utility functions */ const char *blurb (void); const char * blurb (void) { time_t now = time ((time_t *) 0); char *ct = (char *) ctime (&now); static char buf[255]; int n = strlen(progname); if (n > 100) n = 99; strncpy(buf, progname, n); buf[n++] = ':'; buf[n++] = ' '; strncpy(buf+n, ct+11, 8); strcpy(buf+n+9, ": "); return buf; } static GtkWidget * name_to_widget (state *s, const char *name) { GtkWidget *w; if (!s) abort(); if (!name) abort(); if (!*name) abort(); #ifdef HAVE_GTK2 if (!s->glade_ui) { /* First try to load the Glade file from the current directory; if there isn't one there, check the installed directory. */ # define GLADE_FILE_NAME "xscreensaver-demo.glade2" const char * const files[] = { GLADE_FILE_NAME, GLADE_DIR "/" GLADE_FILE_NAME }; int i; for (i = 0; i < countof (files); i++) { struct stat st; if (!stat (files[i], &st)) { s->glade_ui = glade_xml_new (files[i], NULL, NULL); break; } } if (!s->glade_ui) { fprintf (stderr, "%s: could not load \"" GLADE_FILE_NAME "\"\n" "\tfrom " GLADE_DIR "/ or current directory.\n", blurb()); exit (-1); } # undef GLADE_FILE_NAME glade_xml_signal_autoconnect (s->glade_ui); } w = glade_xml_get_widget (s->glade_ui, name); #else /* !HAVE_GTK2 */ w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget), name); if (w) return w; w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget), name); #endif /* HAVE_GTK2 */ if (w) return w; fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n", blurb(), name); abort(); } /* Why this behavior isn't automatic in *either* toolkit, I'll never know. Takes a scroller, viewport, or list as an argument. */ static void ensure_selected_item_visible (GtkWidget *widget) { #ifdef HAVE_GTK2 GtkTreePath *path; GtkTreeSelection *selection; GtkTreeIter iter; GtkTreeModel *model; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) path = gtk_tree_path_new_first (); else path = gtk_tree_model_get_path (model, &iter); gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE); gtk_tree_path_free (path); #else /* !HAVE_GTK2 */ GtkScrolledWindow *scroller = 0; GtkViewport *vp = 0; GtkList *list_widget = 0; GList *slist; GList *kids; int nkids = 0; GtkWidget *selected = 0; int list_elt = -1; GtkAdjustment *adj; gint parent_h, child_y, child_h, children_h, ignore; double ratio_t, ratio_b; if (GTK_IS_SCROLLED_WINDOW (widget)) { scroller = GTK_SCROLLED_WINDOW (widget); vp = GTK_VIEWPORT (GTK_BIN (scroller)->child); list_widget = GTK_LIST (GTK_BIN(vp)->child); } else if (GTK_IS_VIEWPORT (widget)) { vp = GTK_VIEWPORT (widget); scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent); list_widget = GTK_LIST (GTK_BIN(vp)->child); } else if (GTK_IS_LIST (widget)) { list_widget = GTK_LIST (widget); vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent); scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent); } else abort(); slist = list_widget->selection; selected = (slist ? GTK_WIDGET (slist->data) : 0); if (!selected) return; list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected)); for (kids = gtk_container_children (GTK_CONTAINER (list_widget)); kids; kids = kids->next) nkids++; adj = gtk_scrolled_window_get_vadjustment (scroller); gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (vp)), &ignore, &ignore, &ignore, &parent_h, &ignore); gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (selected)), &ignore, &child_y, &ignore, &child_h, &ignore); children_h = nkids * child_h; ratio_t = ((double) child_y) / ((double) children_h); ratio_b = ((double) child_y + child_h) / ((double) children_h); if (adj->upper == 0.0) /* no items in list */ return; if (ratio_t < (adj->value / adj->upper) || ratio_b > ((adj->value + adj->page_size) / adj->upper)) { double target; int slop = parent_h * 0.75; /* how much to overshoot by */ if (ratio_t < (adj->value / adj->upper)) { double ratio_w = ((double) parent_h) / ((double) children_h); double ratio_l = (ratio_b - ratio_t); target = ((ratio_t - ratio_w + ratio_l) * adj->upper); target += slop; } else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/ { target = ratio_t * adj->upper; target -= slop; } if (target > adj->upper - adj->page_size) target = adj->upper - adj->page_size; if (target < 0) target = 0; gtk_adjustment_set_value (adj, target); } #endif /* !HAVE_GTK2 */ } static void warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data) { GtkWidget *shell = GTK_WIDGET (user_data); while (GET_PARENT (shell)) shell = GET_PARENT (shell); gtk_widget_destroy (GTK_WIDGET (shell)); } void restart_menu_cb (GtkWidget *widget, gpointer user_data); static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data) { restart_menu_cb (widget, user_data); warning_dialog_dismiss_cb (widget, user_data); } static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data) { kill_gnome_screensaver (); warning_dialog_dismiss_cb (widget, user_data); } static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data) { kill_kde_screensaver (); warning_dialog_dismiss_cb (widget, user_data); } typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button; static Bool warning_dialog (GtkWidget *parent, const char *message, dialog_button button_type, int center) { char *msg = strdup (message); char *head; GtkWidget *dialog = gtk_dialog_new (); GtkWidget *label = 0; GtkWidget *ok = 0; GtkWidget *cancel = 0; int i = 0; while (parent && !GET_WINDOW (parent)) parent = GET_PARENT (parent); if (!parent || !GET_WINDOW (parent)) /* too early to pop up transient dialogs */ { fprintf (stderr, "%s: too early for dialog?\n", progname); return False; } head = msg; while (head) { char name[20]; char *s = strchr (head, '\n'); if (s) *s = 0; sprintf (name, "label%d", i++); { label = gtk_label_new (head); #ifdef HAVE_GTK2 gtk_label_set_selectable (GTK_LABEL (label), TRUE); #endif /* HAVE_GTK2 */ #ifndef HAVE_GTK2 if (i == 1) { GTK_WIDGET (label)->style = gtk_style_copy (GTK_WIDGET (label)->style); GTK_WIDGET (label)->style->font = gdk_font_load (get_string_resource("warning_dialog.headingFont", "Dialog.Font")); gtk_widget_set_style (GTK_WIDGET (label), GTK_WIDGET (label)->style); } #endif /* !HAVE_GTK2 */ if (center <= 0) gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))), label, TRUE, TRUE, 0); gtk_widget_show (label); } if (s) head = s+1; else head = 0; center--; } label = gtk_label_new (""); gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))), label, TRUE, TRUE, 0); gtk_widget_show (label); label = gtk_hbutton_box_new (); gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))), label, TRUE, TRUE, 0); #ifdef HAVE_GTK2 if (button_type != D_NONE) { cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL); gtk_container_add (GTK_CONTAINER (label), cancel); } ok = gtk_button_new_from_stock (GTK_STOCK_OK); gtk_container_add (GTK_CONTAINER (label), ok); #else /* !HAVE_GTK2 */ ok = gtk_button_new_with_label ("OK"); gtk_container_add (GTK_CONTAINER (label), ok); if (button_type != D_NONE) { cancel = gtk_button_new_with_label ("Cancel"); gtk_container_add (GTK_CONTAINER (label), cancel); } #endif /* !HAVE_GTK2 */ gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_container_set_border_width (GTK_CONTAINER (dialog), 10); gtk_window_set_title (GTK_WINDOW (dialog), progclass); SET_CAN_DEFAULT (ok); gtk_widget_show (ok); gtk_widget_grab_focus (ok); if (cancel) { SET_CAN_DEFAULT (cancel); gtk_widget_show (cancel); } gtk_widget_show (label); gtk_widget_show (dialog); if (button_type != D_NONE) { GtkSignalFunc fn; switch (button_type) { case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break; case D_GNOME: fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb); break; case D_KDE: fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb); break; default: abort(); break; } gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn, (gpointer) dialog); gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked", GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb), (gpointer) dialog); } else { gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb), (gpointer) dialog); } gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)), GET_WINDOW (GTK_WIDGET (parent))); #ifdef HAVE_GTK2 gtk_window_present (GTK_WINDOW (dialog)); #else /* !HAVE_GTK2 */ gdk_window_show (GTK_WIDGET (dialog)->window); gdk_window_raise (GTK_WIDGET (dialog)->window); #endif /* !HAVE_GTK2 */ free (msg); return True; } static void run_cmd (state *s, Atom command, int arg) { char *err = 0; int status; flush_dialog_changes_and_save (s); status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err); /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */ if (status < 0 && err && strstr (err, "unexpectedly deleted")) status = 0; if (status < 0) { char buf [255]; if (err) sprintf (buf, "Error:\n\n%s", err); else strcpy (buf, "Unknown error!"); warning_dialog (s->toplevel_widget, buf, D_NONE, 100); } if (err) free (err); sensitize_menu_items (s, True); force_dialog_repaint (s); } static void run_hack (state *s, int list_elt, Bool report_errors_p) { int hack_number; char *err = 0; int status; if (list_elt < 0) return; hack_number = s->list_elt_to_hack_number[list_elt]; flush_dialog_changes_and_save (s); schedule_preview (s, 0); status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1, False, &err); if (status < 0 && report_errors_p) { if (xscreensaver_running_p (s)) { /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */ if (err && strstr (err, "unexpectedly deleted")) status = 0; if (status < 0) { char buf [255]; if (err) sprintf (buf, "Error:\n\n%s", err); else strcpy (buf, "Unknown error!"); warning_dialog (s->toplevel_widget, buf, D_NONE, 100); } } else { /* The error is that the daemon isn't running; offer to restart it. */ const char *d = DisplayString (GDK_DISPLAY()); char msg [1024]; sprintf (msg, _("Warning:\n\n" "The XScreenSaver daemon doesn't seem to be running\n" "on display \"%s\". Launch it now?"), d); warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1); } } if (err) free (err); sensitize_menu_items (s, False); } /* Button callbacks According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make libglade work on Cygwin; apparently all Glade callbacks need this magic extra declaration. I do not pretend to understand. */ G_MODULE_EXPORT void exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ flush_dialog_changes_and_save (s); kill_preview_subproc (s, False); gtk_main_quit (); } static gboolean wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data) { state *s = (state *) data; flush_dialog_changes_and_save (s); gtk_main_quit (); return TRUE; } G_MODULE_EXPORT void about_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { char msg [2048]; char *vers = strdup (screensaver_id + 4); char *s; char copy[1024]; char *desc = _("For updates, check http://www.jwz.org/xscreensaver/"); s = strchr (vers, ','); *s = 0; s += 2; /* Ole Laursen says "don't use _() here because non-ASCII characters aren't allowed in localizable string keys." (I don't want to just use (c) instead of © because that doesn't look as good in the plain-old default Latin1 "C" locale.) */ #ifdef HAVE_GTK2 sprintf(copy, ("Copyright \xC2\xA9 1991-2008 %s"), s); #else /* !HAVE_GTK2 */ sprintf(copy, ("Copyright \251 1991-2008 %s"), s); #endif /* !HAVE_GTK2 */ sprintf (msg, "%s\n\n%s", copy, desc); /* I can't make gnome_about_new() work here -- it starts dying in gdk_imlib_get_visual() under gnome_about_new(). If this worked, then this might be the thing to do: #ifdef HAVE_CRAPPLET { const gchar *auth[] = { 0 }; GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc, "xscreensaver.xpm"); gtk_widget_show (about); } #else / * GTK but not GNOME * / ... */ { GdkColormap *colormap; GdkPixmap *gdkpixmap; GdkBitmap *mask; GtkWidget *dialog = gtk_dialog_new (); GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok; GtkWidget *parent = GTK_WIDGET (menuitem); while (GET_PARENT (parent)) parent = GET_PARENT (parent); hbox = gtk_hbox_new (FALSE, 20); gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))), hbox, TRUE, TRUE, 0); colormap = gtk_widget_get_colormap (parent); gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL, (gchar **) logo_180_xpm); icon = gtk_pixmap_new (gdkpixmap, mask); gtk_misc_set_padding (GTK_MISC (icon), 10, 10); gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0); vbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); label1 = gtk_label_new (vers); gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0); gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75); #ifndef HAVE_GTK2 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style); GTK_WIDGET (label1)->style->font = gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font")); gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style); #endif /* HAVE_GTK2 */ label2 = gtk_label_new (msg); gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0); gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25); #ifndef HAVE_GTK2 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style); GTK_WIDGET (label2)->style->font = gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font")); gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style); #endif /* HAVE_GTK2 */ hb = gtk_hbutton_box_new (); gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))), hb, TRUE, TRUE, 0); #ifdef HAVE_GTK2 ok = gtk_button_new_from_stock (GTK_STOCK_OK); #else /* !HAVE_GTK2 */ ok = gtk_button_new_with_label (_("OK")); #endif /* !HAVE_GTK2 */ gtk_container_add (GTK_CONTAINER (hb), ok); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_container_set_border_width (GTK_CONTAINER (dialog), 10); gtk_window_set_title (GTK_WINDOW (dialog), progclass); gtk_widget_show (hbox); gtk_widget_show (icon); gtk_widget_show (vbox); gtk_widget_show (label1); gtk_widget_show (label2); gtk_widget_show (hb); gtk_widget_show (ok); gtk_widget_show (dialog); gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb), (gpointer) dialog); gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)), GET_WINDOW (GTK_WIDGET (parent))); gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog))); gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog))); } } G_MODULE_EXPORT void doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ saver_preferences *p = &s->prefs; char *help_command; if (!p->help_url || !*p->help_url) { warning_dialog (s->toplevel_widget, _("Error:\n\n" "No Help URL has been specified.\n"), D_NONE, 100); return; } help_command = (char *) malloc (strlen (p->load_url_command) + (strlen (p->help_url) * 4) + 20); strcpy (help_command, "( "); sprintf (help_command + strlen(help_command), p->load_url_command, p->help_url, p->help_url, p->help_url, p->help_url); strcat (help_command, " ) &"); if (system (help_command) < 0) fprintf (stderr, "%s: fork error\n", blurb()); free (help_command); } G_MODULE_EXPORT void file_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ sensitize_menu_items (s, False); } G_MODULE_EXPORT void activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ run_cmd (s, XA_ACTIVATE, 0); } G_MODULE_EXPORT void lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ run_cmd (s, XA_LOCK, 0); } G_MODULE_EXPORT void kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ run_cmd (s, XA_EXIT, 0); } G_MODULE_EXPORT void restart_menu_cb (GtkWidget *widget, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ flush_dialog_changes_and_save (s); xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL); sleep (1); if (system ("xscreensaver -nosplash &") < 0) fprintf (stderr, "%s: fork error\n", blurb()); await_xscreensaver (s); } static Bool xscreensaver_running_p (state *s) { Display *dpy = GDK_DISPLAY(); char *rversion = 0; server_xscreensaver_version (dpy, &rversion, 0, 0); if (!rversion) return False; free (rversion); return True; } static void await_xscreensaver (state *s) { int countdown = 5; Bool ok = False; while (!ok && (--countdown > 0)) if (xscreensaver_running_p (s)) ok = True; else sleep (1); /* If it's not there yet, wait a second... */ sensitize_menu_items (s, True); if (! ok) { /* Timed out, no screensaver running. */ char buf [1024]; Bool root_p = (geteuid () == 0); strcpy (buf, _("Error:\n\n" "The xscreensaver daemon did not start up properly.\n" "\n")); if (root_p) # ifdef __GNUC__ __extension__ /* don't warn about "string length is greater than the length ISO C89 compilers are required to support" in the following expression... */ # endif strcat (buf, STFU _("You are running as root. This usually means that xscreensaver\n" "was unable to contact your X server because access control is\n" "turned on. Try running this command:\n" "\n" " xhost +localhost\n" "\n" "and then selecting `File / Restart Daemon'.\n" "\n" "Note that turning off access control will allow anyone logged\n" "on to this machine to access your screen, which might be\n" "considered a security problem. Please read the xscreensaver\n" "manual and FAQ for more information.\n" "\n" "You shouldn't run X as root. Instead, you should log in as a\n" "normal user, and `su' as necessary.")); else strcat (buf, _("Please check your $PATH and permissions.")); warning_dialog (s->toplevel_widget, buf, D_NONE, 1); } force_dialog_repaint (s); } static int selected_list_element (state *s) { return s->_selected_list_element; } static int demo_write_init_file (state *s, saver_preferences *p) { Display *dpy = GDK_DISPLAY(); #if 0 /* #### try to figure out why shit keeps getting reordered... */ if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow")) abort(); #endif if (!write_init_file (dpy, p, s->short_version, False)) { if (s->debug_p) fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name()); return 0; } else { const char *f = init_file_name(); if (!f || !*f) warning_dialog (s->toplevel_widget, _("Error:\n\nCouldn't determine init file name!\n"), D_NONE, 100); else { char *b = (char *) malloc (strlen(f) + 1024); sprintf (b, _("Error:\n\nCouldn't write %s\n"), f); warning_dialog (s->toplevel_widget, b, D_NONE, 100); free (b); } return -1; } } G_MODULE_EXPORT void run_this_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ int list_elt = selected_list_element (s); if (list_elt < 0) return; if (!flush_dialog_changes_and_save (s)) run_hack (s, list_elt, True); } G_MODULE_EXPORT void manual_cb (GtkButton *button, gpointer user_data) { Display *dpy = GDK_DISPLAY(); state *s = global_state_kludge; /* I hate C so much... */ saver_preferences *p = &s->prefs; GtkWidget *list_widget = name_to_widget (s, "list"); int list_elt = selected_list_element (s); int hack_number; char *name, *name2, *cmd, *str; char *oname = 0; if (list_elt < 0) return; hack_number = s->list_elt_to_hack_number[list_elt]; flush_dialog_changes_and_save (s); ensure_selected_item_visible (list_widget); name = strdup (p->screenhacks[hack_number]->command); name2 = name; oname = name; while (isspace (*name2)) name2++; str = name2; while (*str && !isspace (*str)) str++; *str = 0; str = strrchr (name2, '/'); if (str) name2 = str+1; cmd = get_string_resource (dpy, "manualCommand", "ManualCommand"); if (cmd) { char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100); strcpy (cmd2, "( "); sprintf (cmd2 + strlen (cmd2), cmd, name2, name2, name2, name2); strcat (cmd2, " ) &"); if (system (cmd2) < 0) fprintf (stderr, "%s: fork error\n", blurb()); free (cmd2); } else { warning_dialog (GTK_WIDGET (button), _("Error:\n\nno `manualCommand' resource set."), D_NONE, 100); } free (oname); } static void force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p) { GtkWidget *parent = name_to_widget (s, "scroller"); gboolean was = GET_SENSITIVE (parent); #ifdef HAVE_GTK2 GtkTreeIter iter; GtkTreeModel *model; GtkTreeSelection *selection; #endif /* HAVE_GTK2 */ if (!was) gtk_widget_set_sensitive (parent, True); #ifdef HAVE_GTK2 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list)); g_assert (model); if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt)) { selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); gtk_tree_selection_select_iter (selection, &iter); } #else /* !HAVE_GTK2 */ gtk_list_select_item (GTK_LIST (list), list_elt); #endif /* !HAVE_GTK2 */ if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list)); if (!was) gtk_widget_set_sensitive (parent, False); } G_MODULE_EXPORT void run_next_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ /* saver_preferences *p = &s->prefs; */ Bool ops = s->preview_suppressed_p; GtkWidget *list_widget = name_to_widget (s, "list"); int list_elt = selected_list_element (s); if (list_elt < 0) list_elt = 0; else list_elt++; if (list_elt >= s->list_count) list_elt = 0; s->preview_suppressed_p = True; flush_dialog_changes_and_save (s); force_list_select_item (s, list_widget, list_elt, True); populate_demo_window (s, list_elt); run_hack (s, list_elt, False); s->preview_suppressed_p = ops; } G_MODULE_EXPORT void run_prev_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ /* saver_preferences *p = &s->prefs; */ Bool ops = s->preview_suppressed_p; GtkWidget *list_widget = name_to_widget (s, "list"); int list_elt = selected_list_element (s); if (list_elt < 0) list_elt = s->list_count - 1; else list_elt--; if (list_elt < 0) list_elt = s->list_count - 1; s->preview_suppressed_p = True; flush_dialog_changes_and_save (s); force_list_select_item (s, list_widget, list_elt, True); populate_demo_window (s, list_elt); run_hack (s, list_elt, False); s->preview_suppressed_p = ops; } /* Writes the given settings into prefs. Returns true if there was a change, False otherwise. command and/or visual may be 0, or enabled_p may be -1, meaning "no change". */ static Bool flush_changes (state *s, int list_elt, int enabled_p, const char *command, const char *visual) { saver_preferences *p = &s->prefs; Bool changed = False; screenhack *hack; int hack_number; if (list_elt < 0 || list_elt >= s->list_count) abort(); hack_number = s->list_elt_to_hack_number[list_elt]; hack = p->screenhacks[hack_number]; if (enabled_p != -1 && enabled_p != hack->enabled_p) { hack->enabled_p = enabled_p; changed = True; if (s->debug_p) fprintf (stderr, "%s: \"%s\": enabled => %d\n", blurb(), hack->name, enabled_p); } if (command) { if (!hack->command || !!strcmp (command, hack->command)) { if (hack->command) free (hack->command); hack->command = strdup (command); changed = True; if (s->debug_p) fprintf (stderr, "%s: \"%s\": command => \"%s\"\n", blurb(), hack->name, command); } } if (visual) { const char *ov = hack->visual; if (!ov || !*ov) ov = "any"; if (!*visual) visual = "any"; if (!!strcasecmp (visual, ov)) { if (hack->visual) free (hack->visual); hack->visual = strdup (visual); changed = True; if (s->debug_p) fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n", blurb(), hack->name, visual); } } return changed; } /* Helper for the text fields that contain time specifications: this parses the text, and does error checking. */ static void hack_time_text (state *s, const char *line, Time *store, Bool sec_p) { if (*line) { int value; if (!sec_p || strchr (line, ':')) value = parse_time ((char *) line, sec_p, True); else { char c; if (sscanf (line, "%d%c", &value, &c) != 1) value = -1; if (!sec_p) value *= 60; } value *= 1000; /* Time measures in microseconds */ if (value < 0) { char b[255]; sprintf (b, _("Error:\n\n" "Unparsable time format: \"%s\"\n"), line); warning_dialog (s->toplevel_widget, b, D_NONE, 100); } else *store = value; } } static Bool directory_p (const char *path) { struct stat st; if (!path || !*path) return False; else if (stat (path, &st)) return False; else if (!S_ISDIR (st.st_mode)) return False; else return True; } static Bool file_p (const char *path) { struct stat st; if (!path || !*path) return False; else if (stat (path, &st)) return False; else if (S_ISDIR (st.st_mode)) return False; else return True; } static char * normalize_directory (const char *path) { int L; char *p2, *s; if (!path || !*path) return 0; L = strlen (path); p2 = (char *) malloc (L + 2); strcpy (p2, path); if (p2[L-1] == '/') /* remove trailing slash */ p2[--L] = 0; for (s = p2; s && *s; s++) { if (*s == '/' && (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */ !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */ { char *s0 = s; while (s0 > p2 && s0[-1] != '/') s0--; if (s0 > p2) { s0--; s += 3; strcpy (s0, s); s = s0-1; } } else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */ strcpy (s, s+2), s--; else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */ *s = 0, s--; } for (s = p2; s && *s; s++) /* normalize consecutive slashes */ while (s[0] == '/' && s[1] == '/') strcpy (s, s+1); /* and strip trailing whitespace for good measure. */ L = strlen(p2); while (isspace(p2[L-1])) p2[--L] = 0; return p2; } #ifdef HAVE_GTK2 typedef struct { state *s; int i; Bool *changed; } FlushForeachClosure; static gboolean flush_checkbox (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { FlushForeachClosure *closure = data; gboolean checked; gtk_tree_model_get (model, iter, COL_ENABLED, &checked, -1); if (flush_changes (closure->s, closure->i, checked, 0, 0)) *closure->changed = True; closure->i++; /* don't remove row */ return FALSE; } #endif /* HAVE_GTK2 */ /* Flush out any changes made in the main dialog window (where changes take place immediately: clicking on a checkbox causes the init file to be written right away.) */ static Bool flush_dialog_changes_and_save (state *s) { saver_preferences *p = &s->prefs; saver_preferences P2, *p2 = &P2; #ifdef HAVE_GTK2 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list")); GtkTreeModel *model = gtk_tree_view_get_model (list_widget); FlushForeachClosure closure; #else /* !HAVE_GTK2 */ GtkList *list_widget = GTK_LIST (name_to_widget (s, "list")); GList *kids = gtk_container_children (GTK_CONTAINER (list_widget)); int i; #endif /* !HAVE_GTK2 */ static Bool already_warned_about_missing_image_directory = False; /* very long name... */ Bool changed = False; GtkWidget *w; if (s->saving_p) return False; s->saving_p = True; *p2 = *p; /* Flush any checkbox changes in the list down into the prefs struct. */ #ifdef HAVE_GTK2 closure.s = s; closure.changed = &changed; closure.i = 0; gtk_tree_model_foreach (model, flush_checkbox, &closure); #else /* !HAVE_GTK2 */ for (i = 0; kids; kids = kids->next, i++) { GtkWidget *line = GTK_WIDGET (kids->data); GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child); GtkWidget *line_check = GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data); Bool checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check)); if (flush_changes (s, i, (checked ? 1 : 0), 0, 0)) changed = True; } #endif /* ~HAVE_GTK2 */ /* Flush the non-hack-specific settings down into the prefs struct. */ # define SECONDS(FIELD,NAME) \ w = name_to_widget (s, (NAME)); \ hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True) # define MINUTES(FIELD,NAME) \ w = name_to_widget (s, (NAME)); \ hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False) # define CHECKBOX(FIELD,NAME) \ w = name_to_widget (s, (NAME)); \ (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)) # define PATHNAME(FIELD,NAME) \ w = name_to_widget (s, (NAME)); \ (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w))) # define TEXT(FIELD,NAME) \ w = name_to_widget (s, (NAME)); \ (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w)) MINUTES (&p2->timeout, "timeout_spinbutton"); MINUTES (&p2->cycle, "cycle_spinbutton"); CHECKBOX (p2->lock_p, "lock_button"); MINUTES (&p2->lock_timeout, "lock_spinbutton"); CHECKBOX (p2->dpms_enabled_p, "dpms_button"); CHECKBOX (p2->dpms_quickoff_p, "dpms_quickoff_button"); MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton"); MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton"); MINUTES (&p2->dpms_off, "dpms_off_spinbutton"); CHECKBOX (p2->grab_desktop_p, "grab_desk_button"); CHECKBOX (p2->grab_video_p, "grab_video_button"); CHECKBOX (p2->random_image_p, "grab_image_button"); PATHNAME (p2->image_directory, "image_text"); #if 0 CHECKBOX (p2->verbose_p, "verbose_button"); CHECKBOX (p2->capture_stderr_p, "capture_button"); CHECKBOX (p2->splash_p, "splash_button"); #endif { Bool v = False; CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE; CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL; CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE; CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM; CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL; TEXT (p2->text_literal, "text_entry"); PATHNAME (p2->text_file, "text_file_entry"); PATHNAME (p2->text_program, "text_program_entry"); PATHNAME (p2->text_program, "text_program_entry"); TEXT (p2->text_url, "text_url_entry"); } CHECKBOX (p2->install_cmap_p, "install_button"); CHECKBOX (p2->fade_p, "fade_button"); CHECKBOX (p2->unfade_p, "unfade_button"); SECONDS (&p2->fade_seconds, "fade_spinbutton"); # undef SECONDS # undef MINUTES # undef CHECKBOX # undef PATHNAME # undef TEXT /* Warn if the image directory doesn't exist, when: - not being warned before - image directory is changed and the directory doesn't exist */ if (p2->image_directory && *p2->image_directory && !directory_p (p2->image_directory) && ( !already_warned_about_missing_image_directory || ( p->image_directory && *p->image_directory && strcmp(p->image_directory, p2->image_directory) ) ) ) { char b[255]; sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n", p2->image_directory); if (warning_dialog (s->toplevel_widget, b, D_NONE, 100)) already_warned_about_missing_image_directory = True; } /* Map the mode menu to `saver_mode' enum values. */ { GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu")); GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt)); GtkWidget *selected = gtk_menu_get_active (menu); GList *kids = gtk_container_children (GTK_CONTAINER (menu)); int menu_elt = g_list_index (kids, (gpointer) selected); if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort(); p2->mode = mode_menu_order[menu_elt]; } if (p2->mode == ONE_HACK) { int list_elt = selected_list_element (s); p2->selected_hack = (list_elt >= 0 ? s->list_elt_to_hack_number[list_elt] : -1); } # define COPY(field, name) \ if (p->field != p2->field) { \ changed = True; \ if (s->debug_p) \ fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \ } \ p->field = p2->field COPY(mode, "mode"); COPY(selected_hack, "selected_hack"); COPY(timeout, "timeout"); COPY(cycle, "cycle"); COPY(lock_p, "lock_p"); COPY(lock_timeout, "lock_timeout"); COPY(dpms_enabled_p, "dpms_enabled_p"); COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p"); COPY(dpms_standby, "dpms_standby"); COPY(dpms_suspend, "dpms_suspend"); COPY(dpms_off, "dpms_off"); #if 0 COPY(verbose_p, "verbose_p"); COPY(capture_stderr_p, "capture_stderr_p"); COPY(splash_p, "splash_p"); #endif COPY(tmode, "tmode"); COPY(install_cmap_p, "install_cmap_p"); COPY(fade_p, "fade_p"); COPY(unfade_p, "unfade_p"); COPY(fade_seconds, "fade_seconds"); COPY(grab_desktop_p, "grab_desktop_p"); COPY(grab_video_p, "grab_video_p"); COPY(random_image_p, "random_image_p"); # undef COPY # define COPYSTR(FIELD,NAME) \ if (!p->FIELD || \ !p2->FIELD || \ strcmp(p->FIELD, p2->FIELD)) \ { \ changed = True; \ if (s->debug_p) \ fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \ } \ if (p->FIELD && p->FIELD != p2->FIELD) \ free (p->FIELD); \ p->FIELD = p2->FIELD; \ p2->FIELD = 0 COPYSTR(image_directory, "image_directory"); COPYSTR(text_literal, "text_literal"); COPYSTR(text_file, "text_file"); COPYSTR(text_program, "text_program"); COPYSTR(text_url, "text_url"); # undef COPYSTR populate_prefs_page (s); if (changed) { Display *dpy = GDK_DISPLAY(); Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK); sync_server_dpms_settings (dpy, enabled_p, p->dpms_standby / 1000, p->dpms_suspend / 1000, p->dpms_off / 1000, False); changed = demo_write_init_file (s, p); } s->saving_p = False; return changed; } /* Flush out any changes made in the popup dialog box (where changes take place only when the OK button is clicked.) */ static Bool flush_popup_changes_and_save (state *s) { Bool changed = False; saver_preferences *p = &s->prefs; int list_elt = selected_list_element (s); GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text")); GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo")); const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry)); const char *command = gtk_entry_get_text (cmd); char c; unsigned long id; if (s->saving_p) return False; s->saving_p = True; if (list_elt < 0) goto DONE; if (maybe_reload_init_file (s) != 0) { changed = True; goto DONE; } /* Sanity-check and canonicalize whatever the user typed into the combo box. */ if (!strcasecmp (visual, "")) visual = ""; else if (!strcasecmp (visual, "any")) visual = ""; else if (!strcasecmp (visual, "default")) visual = "Default"; else if (!strcasecmp (visual, "default-n")) visual = "Default-N"; else if (!strcasecmp (visual, "default-i")) visual = "Default-I"; else if (!strcasecmp (visual, "best")) visual = "Best"; else if (!strcasecmp (visual, "mono")) visual = "Mono"; else if (!strcasecmp (visual, "monochrome")) visual = "Mono"; else if (!strcasecmp (visual, "gray")) visual = "Gray"; else if (!strcasecmp (visual, "grey")) visual = "Gray"; else if (!strcasecmp (visual, "color")) visual = "Color"; else if (!strcasecmp (visual, "gl")) visual = "GL"; else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray"; else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor"; else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor"; else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale"; else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale"; else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor"; else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor"; else if (1 == sscanf (visual, " %lu %c", &id, &c)) ; else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ; else { gdk_beep (); /* unparsable */ visual = ""; gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any")); } changed = flush_changes (s, list_elt, -1, command, visual); if (changed) { changed = demo_write_init_file (s, p); /* Do this to re-launch the hack if (and only if) the command line has changed. */ populate_demo_window (s, selected_list_element (s)); } DONE: s->saving_p = False; return changed; } G_MODULE_EXPORT void pref_changed_cb (GtkWidget *widget, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ if (! s->initializing_p) { s->initializing_p = True; flush_dialog_changes_and_save (s); s->initializing_p = False; } } G_MODULE_EXPORT gboolean pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) { pref_changed_cb (widget, user_data); return FALSE; } /* Callback on menu items in the "mode" options menu. */ G_MODULE_EXPORT void mode_menu_item_cb (GtkWidget *widget, gpointer user_data) { state *s = (state *) user_data; saver_preferences *p = &s->prefs; GtkWidget *list = name_to_widget (s, "list"); int list_elt; GList *menu_items = gtk_container_children (GTK_CONTAINER (GET_PARENT (widget))); int menu_index = 0; saver_mode new_mode; while (menu_items) { if (menu_items->data == widget) break; menu_index++; menu_items = menu_items->next; } if (!menu_items) abort(); new_mode = mode_menu_order[menu_index]; /* Keep the same list element displayed as before; except if we're switching *to* "one screensaver" mode from any other mode, set "the one" to be that which is currently selected. */ list_elt = selected_list_element (s); if (new_mode == ONE_HACK) p->selected_hack = s->list_elt_to_hack_number[list_elt]; { saver_mode old_mode = p->mode; p->mode = new_mode; populate_demo_window (s, list_elt); force_list_select_item (s, list, list_elt, True); p->mode = old_mode; /* put it back, so the init file gets written */ } pref_changed_cb (widget, user_data); } G_MODULE_EXPORT void switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page, gint page_num, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ pref_changed_cb (GTK_WIDGET (notebook), user_data); /* If we're switching to page 0, schedule the current hack to be run. Otherwise, schedule it to stop. */ if (page_num == 0) populate_demo_window (s, selected_list_element (s)); else schedule_preview (s, 0); } #ifdef HAVE_GTK2 static void list_activated_cb (GtkTreeView *list, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { state *s = data; char *str; int list_elt; g_return_if_fail (!gdk_pointer_is_grabbed ()); str = gtk_tree_path_to_string (path); list_elt = strtol (str, NULL, 10); g_free (str); if (list_elt >= 0) run_hack (s, list_elt, True); } static void list_select_changed_cb (GtkTreeSelection *selection, gpointer data) { state *s = (state *)data; GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *path; char *str; int list_elt; if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; path = gtk_tree_model_get_path (model, &iter); str = gtk_tree_path_to_string (path); list_elt = strtol (str, NULL, 10); gtk_tree_path_free (path); g_free (str); populate_demo_window (s, list_elt); flush_dialog_changes_and_save (s); /* Re-populate the Settings window any time a new item is selected in the list, in case both windows are currently visible. */ populate_popup_window (s); } #else /* !HAVE_GTK2 */ static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the list_select_cb that comes in *after* we've double-clicked. */ static gint list_doubleclick_cb (GtkWidget *button, GdkEventButton *event, gpointer data) { state *s = (state *) data; if (event->type == GDK_2BUTTON_PRESS) { GtkList *list = GTK_LIST (name_to_widget (s, "list")); int list_elt = gtk_list_child_position (list, GTK_WIDGET (button)); last_doubleclick_time = time ((time_t *) 0); if (list_elt >= 0) run_hack (s, list_elt, True); } return FALSE; } static void list_select_cb (GtkList *list, GtkWidget *child, gpointer data) { state *s = (state *) data; time_t now = time ((time_t *) 0); if (now >= last_doubleclick_time + 2) { int list_elt = gtk_list_child_position (list, GTK_WIDGET (child)); populate_demo_window (s, list_elt); flush_dialog_changes_and_save (s); } } static void list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data) { state *s = (state *) data; populate_demo_window (s, -1); flush_dialog_changes_and_save (s); } #endif /* !HAVE_GTK2 */ /* Called when the checkboxes that are in the left column of the scrolling list are clicked. This both populates the right pane (just as clicking on the label (really, listitem) does) and also syncs this checkbox with the right pane Enabled checkbox. */ static void list_checkbox_cb ( #ifdef HAVE_GTK2 GtkCellRendererToggle *toggle, gchar *path_string, #else /* !HAVE_GTK2 */ GtkWidget *cb, #endif /* !HAVE_GTK2 */ gpointer data) { state *s = (state *) data; #ifdef HAVE_GTK2 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller")); GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list")); GtkTreeModel *model = gtk_tree_view_get_model (list); GtkTreePath *path = gtk_tree_path_new_from_string (path_string); GtkTreeIter iter; gboolean active; #else /* !HAVE_GTK2 */ GtkWidget *line_hbox = GTK_WIDGET (cb)->parent; GtkWidget *line = GTK_WIDGET (line_hbox)->parent; GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent); GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent); GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent); #endif /* !HAVE_GTK2 */ GtkAdjustment *adj; double scroll_top; int list_elt; #ifdef HAVE_GTK2 if (!gtk_tree_model_get_iter (model, &iter, path)) { g_warning ("bad path: %s", path_string); return; } gtk_tree_path_free (path); gtk_tree_model_get (model, &iter, COL_ENABLED, &active, -1); gtk_list_store_set (GTK_LIST_STORE (model), &iter, COL_ENABLED, !active, -1); list_elt = strtol (path_string, NULL, 10); #else /* !HAVE_GTK2 */ list_elt = gtk_list_child_position (list, line); #endif /* !HAVE_GTK2 */ /* remember previous scroll position of the top of the list */ adj = gtk_scrolled_window_get_vadjustment (scroller); scroll_top = GET_ADJ_VALUE (adj); flush_dialog_changes_and_save (s); force_list_select_item (s, GTK_WIDGET (list), list_elt, False); populate_demo_window (s, list_elt); /* restore the previous scroll position of the top of the list. this is weak, but I don't really know why it's moving... */ gtk_adjustment_set_value (adj, scroll_top); } typedef struct { state *state; GtkFileSelection *widget; } file_selection_data; static void store_image_directory (GtkWidget *button, gpointer user_data) { file_selection_data *fsd = (file_selection_data *) user_data; state *s = fsd->state; GtkFileSelection *selector = fsd->widget; GtkWidget *top = s->toplevel_widget; saver_preferences *p = &s->prefs; const char *path = gtk_file_selection_get_filename (selector); if (p->image_directory && !strcmp(p->image_directory, path)) return; /* no change */ if (!directory_p (path)) { char b[255]; sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path); warning_dialog (GTK_WIDGET (top), b, D_NONE, 100); return; } if (p->image_directory) free (p->image_directory); p->image_directory = normalize_directory (path); gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")), (p->image_directory ? p->image_directory : "")); demo_write_init_file (s, p); } static void store_text_file (GtkWidget *button, gpointer user_data) { file_selection_data *fsd = (file_selection_data *) user_data; state *s = fsd->state; GtkFileSelection *selector = fsd->widget; GtkWidget *top = s->toplevel_widget; saver_preferences *p = &s->prefs; const char *path = gtk_file_selection_get_filename (selector); if (p->text_file && !strcmp(p->text_file, path)) return; /* no change */ if (!file_p (path)) { char b[255]; sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path); warning_dialog (GTK_WIDGET (top), b, D_NONE, 100); return; } if (p->text_file) free (p->text_file); p->text_file = normalize_directory (path); gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")), (p->text_file ? p->text_file : "")); demo_write_init_file (s, p); } static void store_text_program (GtkWidget *button, gpointer user_data) { file_selection_data *fsd = (file_selection_data *) user_data; state *s = fsd->state; GtkFileSelection *selector = fsd->widget; /*GtkWidget *top = s->toplevel_widget;*/ saver_preferences *p = &s->prefs; const char *path = gtk_file_selection_get_filename (selector); if (p->text_program && !strcmp(p->text_program, path)) return; /* no change */ # if 0 if (!file_p (path)) { char b[255]; sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path); warning_dialog (GTK_WIDGET (top), b, D_NONE, 100); return; } # endif if (p->text_program) free (p->text_program); p->text_program = normalize_directory (path); gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")), (p->text_program ? p->text_program : "")); demo_write_init_file (s, p); } static void browse_image_dir_cancel (GtkWidget *button, gpointer user_data) { file_selection_data *fsd = (file_selection_data *) user_data; gtk_widget_hide (GTK_WIDGET (fsd->widget)); } static void browse_image_dir_ok (GtkWidget *button, gpointer user_data) { browse_image_dir_cancel (button, user_data); store_image_directory (button, user_data); } static void browse_text_file_ok (GtkWidget *button, gpointer user_data) { browse_image_dir_cancel (button, user_data); store_text_file (button, user_data); } static void browse_text_program_ok (GtkWidget *button, gpointer user_data) { browse_image_dir_cancel (button, user_data); store_text_program (button, user_data); } static void browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data) { browse_image_dir_cancel (widget, user_data); } G_MODULE_EXPORT void browse_image_dir_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ saver_preferences *p = &s->prefs; static file_selection_data *fsd = 0; GtkFileSelection *selector = GTK_FILE_SELECTION( gtk_file_selection_new ("Please select the image directory.")); if (!fsd) fsd = (file_selection_data *) malloc (sizeof (*fsd)); fsd->widget = selector; fsd->state = s; if (p->image_directory && *p->image_directory) gtk_file_selection_set_filename (selector, p->image_directory); gtk_signal_connect (GTK_OBJECT (selector->ok_button), "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok), (gpointer *) fsd); gtk_signal_connect (GTK_OBJECT (selector->cancel_button), "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel), (gpointer *) fsd); gtk_signal_connect (GTK_OBJECT (selector), "delete_event", GTK_SIGNAL_FUNC (browse_image_dir_close), (gpointer *) fsd); gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False); gtk_window_set_modal (GTK_WINDOW (selector), True); gtk_widget_show (GTK_WIDGET (selector)); } G_MODULE_EXPORT void browse_text_file_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ saver_preferences *p = &s->prefs; static file_selection_data *fsd = 0; GtkFileSelection *selector = GTK_FILE_SELECTION( gtk_file_selection_new ("Please select a text file.")); if (!fsd) fsd = (file_selection_data *) malloc (sizeof (*fsd)); fsd->widget = selector; fsd->state = s; if (p->text_file && *p->text_file) gtk_file_selection_set_filename (selector, p->text_file); gtk_signal_connect (GTK_OBJECT (selector->ok_button), "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok), (gpointer *) fsd); gtk_signal_connect (GTK_OBJECT (selector->cancel_button), "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel), (gpointer *) fsd); gtk_signal_connect (GTK_OBJECT (selector), "delete_event", GTK_SIGNAL_FUNC (browse_image_dir_close), (gpointer *) fsd); gtk_window_set_modal (GTK_WINDOW (selector), True); gtk_widget_show (GTK_WIDGET (selector)); } G_MODULE_EXPORT void browse_text_program_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ saver_preferences *p = &s->prefs; static file_selection_data *fsd = 0; GtkFileSelection *selector = GTK_FILE_SELECTION( gtk_file_selection_new ("Please select a text-generating program.")); if (!fsd) fsd = (file_selection_data *) malloc (sizeof (*fsd)); fsd->widget = selector; fsd->state = s; if (p->text_program && *p->text_program) gtk_file_selection_set_filename (selector, p->text_program); gtk_signal_connect (GTK_OBJECT (selector->ok_button), "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok), (gpointer *) fsd); gtk_signal_connect (GTK_OBJECT (selector->cancel_button), "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel), (gpointer *) fsd); gtk_signal_connect (GTK_OBJECT (selector), "delete_event", GTK_SIGNAL_FUNC (browse_image_dir_close), (gpointer *) fsd); gtk_window_set_modal (GTK_WINDOW (selector), True); gtk_widget_show (GTK_WIDGET (selector)); } G_MODULE_EXPORT void settings_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ int list_elt = selected_list_element (s); populate_demo_window (s, list_elt); /* reset the widget */ populate_popup_window (s); /* create UI on popup window */ gtk_widget_show (s->popup_widget); } static void settings_sync_cmd_text (state *s) { # ifdef HAVE_XML GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text")); char *cmd_line = get_configurator_command_line (s->cdata, False); gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line); gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line)); free (cmd_line); # endif /* HAVE_XML */ } G_MODULE_EXPORT void settings_adv_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook")); settings_sync_cmd_text (s); gtk_notebook_set_page (notebook, 1); } G_MODULE_EXPORT void settings_std_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook")); /* Re-create UI to reflect the in-progress command-line settings. */ populate_popup_window (s); gtk_notebook_set_page (notebook, 0); } G_MODULE_EXPORT void settings_reset_cb (GtkButton *button, gpointer user_data) { # ifdef HAVE_XML state *s = global_state_kludge; /* I hate C so much... */ GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text")); char *cmd_line = get_configurator_command_line (s->cdata, True); gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line); gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line)); free (cmd_line); populate_popup_window (s); # endif /* HAVE_XML */ } G_MODULE_EXPORT void settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page, gint page_num, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ GtkWidget *adv = name_to_widget (s, "adv_button"); GtkWidget *std = name_to_widget (s, "std_button"); if (page_num == 0) { gtk_widget_show (adv); gtk_widget_hide (std); } else if (page_num == 1) { gtk_widget_hide (adv); gtk_widget_show (std); } else abort(); } G_MODULE_EXPORT void settings_cancel_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ gtk_widget_hide (s->popup_widget); } G_MODULE_EXPORT void settings_ok_cb (GtkButton *button, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook")); int page = gtk_notebook_get_current_page (notebook); if (page == 0) /* Regenerate the command-line from the widget contents before saving. But don't do this if we're looking at the command-line page already, or we will blow away what they typed... */ settings_sync_cmd_text (s); flush_popup_changes_and_save (s); gtk_widget_hide (s->popup_widget); } static gboolean wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data) { state *s = (state *) data; settings_cancel_cb (0, (gpointer) s); return TRUE; } /* Populating the various widgets */ /* Returns the number of the last hack run by the server. */ static int server_current_hack (void) { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *dataP = 0; Display *dpy = GDK_DISPLAY(); int hack_number = -1; if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */ XA_SCREENSAVER_STATUS, 0, 3, False, XA_INTEGER, &type, &format, &nitems, &bytesafter, &dataP) == Success && type == XA_INTEGER && nitems >= 3 && dataP) { PROP32 *data = (PROP32 *) dataP; hack_number = (int) data[2] - 1; } if (dataP) XFree (dataP); return hack_number; } /* Finds the number of the last hack that was run, and makes that item be selected by default. */ static void scroll_to_current_hack (state *s) { saver_preferences *p = &s->prefs; int hack_number = -1; if (p->mode == ONE_HACK) /* in "one" mode, use the one */ hack_number = p->selected_hack; if (hack_number < 0) /* otherwise, use the last-run */ hack_number = server_current_hack (); if (hack_number < 0) /* failing that, last "one mode" */ hack_number = p->selected_hack; if (hack_number < 0) /* failing that, newest hack. */ { /* We should only get here if the user does not have a .xscreensaver file, and the screen has not been blanked with a hack since X started up: in other words, this is probably a fresh install. Instead of just defaulting to hack #0 (in either "programs" or "alphabetical" order) let's try to default to the last runnable hack in the "programs" list: this is probably the hack that was most recently added to the xscreensaver distribution (and so it's probably the currently-coolest one!) */ hack_number = p->screenhacks_count-1; while (hack_number > 0 && ! (s->hacks_available_p[hack_number] && p->screenhacks[hack_number]->enabled_p)) hack_number--; } if (hack_number >= 0 && hack_number < p->screenhacks_count) { int list_elt = s->hack_number_to_list_elt[hack_number]; GtkWidget *list = name_to_widget (s, "list"); force_list_select_item (s, list, list_elt, True); populate_demo_window (s, list_elt); } } static void populate_hack_list (state *s) { Display *dpy = GDK_DISPLAY(); #ifdef HAVE_GTK2 saver_preferences *p = &s->prefs; GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list")); GtkListStore *model; GtkTreeSelection *selection; GtkCellRenderer *ren; GtkTreeIter iter; int i; g_object_get (G_OBJECT (list), "model", &model, NULL); if (!model) { model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING); g_object_set (G_OBJECT (list), "model", model, NULL); g_object_unref (model); ren = gtk_cell_renderer_toggle_new (); gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED, _("Use"), ren, "active", COL_ENABLED, NULL); g_signal_connect (ren, "toggled", G_CALLBACK (list_checkbox_cb), s); ren = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (list, COL_NAME, _("Screen Saver"), ren, "markup", COL_NAME, NULL); g_signal_connect_after (list, "row_activated", G_CALLBACK (list_activated_cb), s); selection = gtk_tree_view_get_selection (list); g_signal_connect (selection, "changed", G_CALLBACK (list_select_changed_cb), s); } for (i = 0; i < s->list_count; i++) { int hack_number = s->list_elt_to_hack_number[i]; screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]); char *pretty_name; Bool available_p = (hack && s->hacks_available_p [hack_number]); if (!hack) continue; /* If we're to suppress uninstalled hacks, check $PATH now. */ if (p->ignore_uninstalled_p && !available_p) continue; pretty_name = (hack->name ? strdup (hack->name) : make_hack_name (dpy, hack->command)); if (!available_p) { /* Make the text foreground be the color of insensitive widgets (but don't actually make it be insensitive, since we still want to be able to click on it.) */ GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list)); GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE]; /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */ char *buf = (char *) malloc (strlen (pretty_name) + 100); sprintf (buf, "%s", fg->red >> 8, fg->green >> 8, fg->blue >> 8, /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */ pretty_name); free (pretty_name); pretty_name = buf; } gtk_list_store_append (model, &iter); gtk_list_store_set (model, &iter, COL_ENABLED, hack->enabled_p, COL_NAME, pretty_name, -1); free (pretty_name); } #else /* !HAVE_GTK2 */ saver_preferences *p = &s->prefs; GtkList *list = GTK_LIST (name_to_widget (s, "list")); int i; for (i = 0; i < s->list_count; i++) { int hack_number = s->list_elt_to_hack_number[i]; screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]); /* A GtkList must contain only GtkListItems, but those can contain an arbitrary widget. We add an Hbox, and inside that, a Checkbox and a Label. We handle single and double click events on the line itself, for clicking on the text, but the interior checkbox also handles its own events. */ GtkWidget *line; GtkWidget *line_hbox; GtkWidget *line_check; GtkWidget *line_label; char *pretty_name; Bool available_p = (hack && s->hacks_available_p [hack_number]); if (!hack) continue; /* If we're to suppress uninstalled hacks, check $PATH now. */ if (p->ignore_uninstalled_p && !available_p) continue; pretty_name = (hack->name ? strdup (hack->name) : make_hack_name (hack->command)); line = gtk_list_item_new (); line_hbox = gtk_hbox_new (FALSE, 0); line_check = gtk_check_button_new (); line_label = gtk_label_new (pretty_name); gtk_container_add (GTK_CONTAINER (line), line_hbox); gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check), hack->enabled_p); gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT); gtk_widget_show (line_check); gtk_widget_show (line_label); gtk_widget_show (line_hbox); gtk_widget_show (line); free (pretty_name); gtk_container_add (GTK_CONTAINER (list), line); gtk_signal_connect (GTK_OBJECT (line), "button_press_event", GTK_SIGNAL_FUNC (list_doubleclick_cb), (gpointer) s); gtk_signal_connect (GTK_OBJECT (line_check), "toggled", GTK_SIGNAL_FUNC (list_checkbox_cb), (gpointer) s); gtk_widget_show (line); if (!available_p) { /* Make the widget be colored like insensitive widgets (but don't actually make it be insensitive, since we still want to be able to click on it.) */ GtkRcStyle *rc_style; GdkColor fg, bg; gtk_widget_realize (GTK_WIDGET (line_label)); fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE]; bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE]; rc_style = gtk_rc_style_new (); rc_style->fg[GTK_STATE_NORMAL] = fg; rc_style->bg[GTK_STATE_NORMAL] = bg; rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG; gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style); gtk_rc_style_unref (rc_style); } } gtk_signal_connect (GTK_OBJECT (list), "select_child", GTK_SIGNAL_FUNC (list_select_cb), (gpointer) s); gtk_signal_connect (GTK_OBJECT (list), "unselect_child", GTK_SIGNAL_FUNC (list_unselect_cb), (gpointer) s); #endif /* !HAVE_GTK2 */ } static void update_list_sensitivity (state *s) { saver_preferences *p = &s->prefs; Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == RANDOM_HACKS_SAME || p->mode == ONE_HACK); Bool checkable = (p->mode == RANDOM_HACKS || p->mode == RANDOM_HACKS_SAME); Bool blankable = (p->mode != DONT_BLANK); #ifndef HAVE_GTK2 GtkWidget *head = name_to_widget (s, "col_head_hbox"); GtkWidget *use = name_to_widget (s, "use_col_frame"); #endif /* HAVE_GTK2 */ GtkWidget *scroller = name_to_widget (s, "scroller"); GtkWidget *buttons = name_to_widget (s, "next_prev_hbox"); GtkWidget *blanker = name_to_widget (s, "blanking_table"); #ifdef HAVE_GTK2 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list")); GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED); #else /* !HAVE_GTK2 */ GtkList *list = GTK_LIST (name_to_widget (s, "list")); GList *kids = gtk_container_children (GTK_CONTAINER (list)); gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive); #endif /* !HAVE_GTK2 */ gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive); gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive); gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable); #ifdef HAVE_GTK2 gtk_tree_view_column_set_visible (use, checkable); #else /* !HAVE_GTK2 */ if (checkable) gtk_widget_show (use); /* the "Use" column header */ else gtk_widget_hide (use); while (kids) { GtkBin *line = GTK_BIN (kids->data); GtkContainer *line_hbox = GTK_CONTAINER (line->child); GtkWidget *line_check = GTK_WIDGET (gtk_container_children (line_hbox)->data); if (checkable) gtk_widget_show (line_check); else gtk_widget_hide (line_check); kids = kids->next; } #endif /* !HAVE_GTK2 */ } static void populate_prefs_page (state *s) { saver_preferences *p = &s->prefs; Bool can_lock_p = True; /* Disable all the "lock" controls if locking support was not provided at compile-time, or if running on MacOS. */ # if defined(NO_LOCKING) || defined(__APPLE__) can_lock_p = False; # endif /* If there is only one screen, the mode menu contains "random" but not "random-same". */ if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME) p->mode = RANDOM_HACKS; /* The file supports timeouts of less than a minute, but the GUI does not, so throttle the values to be at least one minute (since "0" is a bad rounding choice...) */ # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000 THROTTLE (timeout); THROTTLE (cycle); /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */ # undef THROTTLE # define FMT_MINUTES(NAME,N) \ gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000)) # define FMT_SECONDS(NAME,N) \ gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000)) FMT_MINUTES ("timeout_spinbutton", p->timeout); FMT_MINUTES ("cycle_spinbutton", p->cycle); FMT_MINUTES ("lock_spinbutton", p->lock_timeout); FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby); FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend); FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off); FMT_SECONDS ("fade_spinbutton", p->fade_seconds); # undef FMT_MINUTES # undef FMT_SECONDS # define TOGGLE_ACTIVE(NAME,ACTIVEP) \ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\ (ACTIVEP)) TOGGLE_ACTIVE ("lock_button", p->lock_p); #if 0 TOGGLE_ACTIVE ("verbose_button", p->verbose_p); TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p); TOGGLE_ACTIVE ("splash_button", p->splash_p); #endif TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p); TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p); TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p); TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p); TOGGLE_ACTIVE ("grab_image_button", p->random_image_p); TOGGLE_ACTIVE ("install_button", p->install_cmap_p); TOGGLE_ACTIVE ("fade_button", p->fade_p); TOGGLE_ACTIVE ("unfade_button", p->unfade_p); switch (p->tmode) { case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break; case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break; case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break; case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break; default: TOGGLE_ACTIVE ("text_host_radio", True); break; } # undef TOGGLE_ACTIVE gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")), (p->image_directory ? p->image_directory : "")); gtk_widget_set_sensitive (name_to_widget (s, "image_text"), p->random_image_p); gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"), p->random_image_p); gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")), (p->text_literal ? p->text_literal : "")); gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")), (p->text_file ? p->text_file : "")); gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")), (p->text_program ? p->text_program : "")); gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")), (p->text_url ? p->text_url : "")); gtk_widget_set_sensitive (name_to_widget (s, "text_entry"), p->tmode == TEXT_LITERAL); gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"), p->tmode == TEXT_FILE); gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"), p->tmode == TEXT_FILE); gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"), p->tmode == TEXT_PROGRAM); gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"), p->tmode == TEXT_PROGRAM); gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"), p->tmode == TEXT_URL); /* Map the `saver_mode' enum to mode menu to values. */ { GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu")); int i; for (i = 0; i < countof(mode_menu_order); i++) if (mode_menu_order[i] == p->mode) break; gtk_option_menu_set_history (opt, i); update_list_sensitivity (s); } { Bool found_any_writable_cells = False; Bool fading_possible = False; Bool dpms_supported = False; Display *dpy = GDK_DISPLAY(); int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */ int i; for (i = 0; i < nscreens; i++) { Screen *s = ScreenOfDisplay (dpy, i); if (has_writable_cells (s, DefaultVisualOfScreen (s))) { found_any_writable_cells = True; break; } } fading_possible = found_any_writable_cells; #ifdef HAVE_XF86VMODE_GAMMA fading_possible = True; #endif #ifdef HAVE_DPMS_EXTENSION { int op = 0, event = 0, error = 0; if (XQueryExtension (dpy, "DPMS", &op, &event, &error)) dpms_supported = True; } #endif /* HAVE_DPMS_EXTENSION */ # define SENSITIZE(NAME,SENSITIVEP) \ gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP)) /* Blanking and Locking */ SENSITIZE ("lock_button", can_lock_p); SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p); SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p); /* DPMS */ dpms_supported=1; SENSITIZE ("dpms_frame", dpms_supported); SENSITIZE ("dpms_button", dpms_supported); SENSITIZE ("dpms_quickoff_button", dpms_supported); SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p); /* Colormaps */ SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible); SENSITIZE ("install_button", found_any_writable_cells); SENSITIZE ("fade_button", fading_possible); SENSITIZE ("unfade_button", fading_possible); SENSITIZE ("fade_label", (fading_possible && (p->fade_p || p->unfade_p))); SENSITIZE ("fade_spinbutton", (fading_possible && (p->fade_p || p->unfade_p))); # undef SENSITIZE } } static void populate_popup_window (state *s) { GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc")); char *doc_string = 0; /* #### not in Gtk 1.2 gtk_label_set_selectable (doc); */ # ifdef HAVE_XML if (s->cdata) { free_conf_data (s->cdata); s->cdata = 0; } { saver_preferences *p = &s->prefs; int list_elt = selected_list_element (s); int hack_number = (list_elt >= 0 && list_elt < s->list_count ? s->list_elt_to_hack_number[list_elt] : -1); screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0); if (hack) { GtkWidget *parent = name_to_widget (s, "settings_vbox"); GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text")); const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd)); s->cdata = load_configurator (cmd_line, s->debug_p); if (s->cdata && s->cdata->widget) gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget, TRUE, TRUE, 0); } } doc_string = (s->cdata ? s->cdata->description : 0); # else /* !HAVE_XML */ doc_string = _("Descriptions not available: no XML support compiled in."); # endif /* !HAVE_XML */ gtk_label_set_text (doc, (doc_string ? _(doc_string) : _("No description available."))); } static void sensitize_demo_widgets (state *s, Bool sensitive_p) { const char *names[] = { "demo", "settings", "cmd_label", "cmd_text", "manual", "visual", "visual_combo" }; int i; for (i = 0; i < countof(names); i++) { GtkWidget *w = name_to_widget (s, names[i]); gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p); } } static void sensitize_menu_items (state *s, Bool force_p) { static Bool running_p = False; static time_t last_checked = 0; time_t now = time ((time_t *) 0); const char *names[] = { "activate_menu", "lock_menu", "kill_menu", /* "demo" */ }; int i; if (force_p || now > last_checked + 10) /* check every 10 seconds */ { running_p = xscreensaver_running_p (s); last_checked = time ((time_t *) 0); } for (i = 0; i < countof(names); i++) { GtkWidget *w = name_to_widget (s, names[i]); gtk_widget_set_sensitive (GTK_WIDGET(w), running_p); } } /* When the File menu is de-posted after a "Restart Daemon" command, the window underneath doesn't repaint for some reason. I guess this is a bug in exposure handling in GTK or GDK. This works around it. */ static void force_dialog_repaint (state *s) { #if 1 /* Tell GDK to invalidate and repaint the whole window. */ GdkWindow *w = GET_WINDOW (s->toplevel_widget); GdkRegion *region = gdk_region_new (); GdkRectangle rect; rect.x = rect.y = 0; rect.width = rect.height = 32767; gdk_region_union_with_rect (region, &rect); gdk_window_invalidate_region (w, region, True); gdk_region_destroy (region); gdk_window_process_updates (w, True); #else /* Force the server to send an exposure event by creating and then destroying a window as a child of the top level shell. */ Display *dpy = GDK_DISPLAY(); Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window); Window w; XWindowAttributes xgwa; XGetWindowAttributes (dpy, parent, &xgwa); w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0); XMapRaised (dpy, w); XDestroyWindow (dpy, w); XSync (dpy, False); #endif } /* Even though we've given these text fields a maximum number of characters, their default size is still about 30 characters wide -- so measure out a string in their font, and resize them to just fit that. */ static void fix_text_entry_sizes (state *s) { GtkWidget *w; # if 0 /* appears no longer necessary with Gtk 1.2.10 */ const char * const spinbuttons[] = { "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton", "dpms_standby_spinbutton", "dpms_suspend_spinbutton", "dpms_off_spinbutton", "-fade_spinbutton" }; int i; int width = 0; for (i = 0; i < countof(spinbuttons); i++) { const char *n = spinbuttons[i]; int cols = 4; while (*n == '-') n++, cols--; w = GTK_WIDGET (name_to_widget (s, n)); width = gdk_text_width (w->style->font, "MMMMMMMM", cols); gtk_widget_set_usize (w, width, -2); } /* Now fix the width of the combo box. */ w = GTK_WIDGET (name_to_widget (s, "visual_combo")); w = GTK_COMBO (w)->entry; width = gdk_string_width (w->style->font, "PseudoColor___"); gtk_widget_set_usize (w, width, -2); /* Now fix the width of the file entry text. */ w = GTK_WIDGET (name_to_widget (s, "image_text")); width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm"); gtk_widget_set_usize (w, width, -2); /* Now fix the width of the command line text. */ w = GTK_WIDGET (name_to_widget (s, "cmd_text")); width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm"); gtk_widget_set_usize (w, width, -2); # endif /* 0 */ /* Now fix the height of the list widget: make it default to being around 10 text-lines high instead of 4. */ w = GTK_WIDGET (name_to_widget (s, "list")); { int lines = 10; int height; int leading = 3; /* approximate is ok... */ int border = 2; #ifdef HAVE_GTK2 PangoFontMetrics *pain = pango_context_get_metrics (gtk_widget_get_pango_context (w), gtk_widget_get_style (w)->font_desc, gtk_get_default_language ()); height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) + pango_font_metrics_get_descent (pain)); #else /* !HAVE_GTK2 */ height = w->style->font->ascent + w->style->font->descent; #endif /* !HAVE_GTK2 */ height += leading; height *= lines; height += border * 2; w = GTK_WIDGET (name_to_widget (s, "scroller")); gtk_widget_set_usize (w, -2, height); } } #ifndef HAVE_GTK2 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...) */ static char *up_arrow_xpm[] = { "15 15 4 1", " c None", "- c #FFFFFF", "+ c #D6D6D6", "@ c #000000", " @ ", " @ ", " -+@ ", " -+@ ", " -+++@ ", " -+++@ ", " -+++++@ ", " -+++++@ ", " -+++++++@ ", " -+++++++@ ", " -+++++++++@ ", " -+++++++++@ ", " -+++++++++++@ ", " @@@@@@@@@@@@@ ", " ", /* Need these here because gdk_pixmap_create_from_xpm_d() walks off the end of the array (Gtk 1.2.5.) */ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" }; static char *down_arrow_xpm[] = { "15 15 4 1", " c None", "- c #FFFFFF", "+ c #D6D6D6", "@ c #000000", " ", " ------------- ", " -+++++++++++@ ", " -+++++++++@ ", " -+++++++++@ ", " -+++++++@ ", " -+++++++@ ", " -+++++@ ", " -+++++@ ", " -+++@ ", " -+++@ ", " -+@ ", " -+@ ", " @ ", " @ ", /* Need these here because gdk_pixmap_create_from_xpm_d() walks off the end of the array (Gtk 1.2.5.) */ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" }; static void pixmapify_button (state *s, int down_p) { GdkPixmap *pixmap; GdkBitmap *mask; GtkWidget *pixmapwid; GtkStyle *style; GtkWidget *w; w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev"))); style = gtk_widget_get_style (w); mask = 0; pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask, &style->bg[GTK_STATE_NORMAL], (down_p ? (gchar **) down_arrow_xpm : (gchar **) up_arrow_xpm)); pixmapwid = gtk_pixmap_new (pixmap, mask); gtk_widget_show (pixmapwid); gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child); gtk_container_add (GTK_CONTAINER (w), pixmapwid); } static void map_next_button_cb (GtkWidget *w, gpointer user_data) { state *s = (state *) user_data; pixmapify_button (s, 1); } static void map_prev_button_cb (GtkWidget *w, gpointer user_data) { state *s = (state *) user_data; pixmapify_button (s, 0); } #endif /* !HAVE_GTK2 */ #ifndef HAVE_GTK2 /* Work around a Gtk bug that causes label widgets to wrap text too early. */ static void you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label, GtkAllocation *allocation, void *foo) { GtkRequisition req; GtkWidgetAuxInfo *aux_info; aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info"); aux_info->width = allocation->width; aux_info->height = -2; aux_info->x = -1; aux_info->y = -1; gtk_widget_size_request (label, &req); } /* Feel the love. Thanks to Nat Friedman for finding this workaround. */ static void eschew_gtk_lossage (GtkLabel *label) { GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1); aux_info->width = GTK_WIDGET (label)->allocation.width; aux_info->height = -2; aux_info->x = -1; aux_info->y = -1; gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info); gtk_signal_connect (GTK_OBJECT (label), "size_allocate", GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake), 0); gtk_widget_set_usize (GTK_WIDGET (label), -2, -2); gtk_widget_queue_resize (GTK_WIDGET (label)); } #endif /* !HAVE_GTK2 */ static void populate_demo_window (state *s, int list_elt) { Display *dpy = GDK_DISPLAY(); saver_preferences *p = &s->prefs; screenhack *hack; char *pretty_name; GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame")); GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame")); GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text")); GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo")); GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list")); if (p->mode == BLANK_ONLY) { hack = 0; pretty_name = strdup (_("Blank Screen")); schedule_preview (s, 0); } else if (p->mode == DONT_BLANK) { hack = 0; pretty_name = strdup (_("Screen Saver Disabled")); schedule_preview (s, 0); } else { int hack_number = (list_elt >= 0 && list_elt < s->list_count ? s->list_elt_to_hack_number[list_elt] : -1); hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0); pretty_name = (hack ? (hack->name ? strdup (hack->name) : make_hack_name (dpy, hack->command)) : 0); if (hack) schedule_preview (s, hack->command); else schedule_preview (s, 0); } if (!pretty_name) pretty_name = strdup (_("Preview")); gtk_frame_set_label (frame1, _(pretty_name)); gtk_frame_set_label (frame2, _(pretty_name)); gtk_entry_set_text (cmd, (hack ? hack->command : "")); gtk_entry_set_position (cmd, 0); { char title[255]; sprintf (title, _("%s: %.100s Settings"), progclass, (pretty_name ? pretty_name : "???")); gtk_window_set_title (GTK_WINDOW (s->popup_widget), title); } gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), (hack ? (hack->visual && *hack->visual ? hack->visual : _("Any")) : "")); sensitize_demo_widgets (s, (hack ? True : False)); if (pretty_name) free (pretty_name); ensure_selected_item_visible (list); s->_selected_list_element = list_elt; } static void widget_deleter (GtkWidget *widget, gpointer data) { /* #### Well, I want to destroy these widgets, but if I do that, they get referenced again, and eventually I get a SEGV. So instead of destroying them, I'll just hide them, and leak a bunch of memory every time the disk file changes. Go go go Gtk! #### Ok, that's a lie, I get a crash even if I just hide the widget and don't ever delete it. Fuck! */ #if 0 gtk_widget_destroy (widget); #else gtk_widget_hide (widget); #endif } static char **sort_hack_cmp_names_kludge; static int sort_hack_cmp (const void *a, const void *b) { if (a == b) return 0; else { int aa = *(int *) a; int bb = *(int *) b; const char last[] = "\377\377\377\377\377\377\377\377\377\377\377"; return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]), (bb < 0 ? last : sort_hack_cmp_names_kludge[bb])); } } static void initialize_sort_map (state *s) { Display *dpy = GDK_DISPLAY(); saver_preferences *p = &s->prefs; int i, j; if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number); if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt); if (s->hacks_available_p) free (s->hacks_available_p); s->list_elt_to_hack_number = (int *) calloc (sizeof(int), p->screenhacks_count + 1); s->hack_number_to_list_elt = (int *) calloc (sizeof(int), p->screenhacks_count + 1); s->hacks_available_p = (Bool *) calloc (sizeof(Bool), p->screenhacks_count + 1); s->total_available = 0; /* Check which hacks actually exist on $PATH */ for (i = 0; i < p->screenhacks_count; i++) { screenhack *hack = p->screenhacks[i]; int on = on_path_p (hack->command) ? 1 : 0; s->hacks_available_p[i] = on; s->total_available += on; } /* Initialize list->hack table to unsorted mapping, omitting nonexistent hacks, if desired. */ j = 0; for (i = 0; i < p->screenhacks_count; i++) { if (!p->ignore_uninstalled_p || s->hacks_available_p[i]) s->list_elt_to_hack_number[j++] = i; } s->list_count = j; for (; j < p->screenhacks_count; j++) s->list_elt_to_hack_number[j] = -1; /* Generate list of sortable names (once) */ sort_hack_cmp_names_kludge = (char **) calloc (sizeof(char *), p->screenhacks_count); for (i = 0; i < p->screenhacks_count; i++) { screenhack *hack = p->screenhacks[i]; char *name = (hack->name && *hack->name ? strdup (hack->name) : make_hack_name (dpy, hack->command)); char *str; for (str = name; *str; str++) *str = tolower(*str); sort_hack_cmp_names_kludge[i] = name; } /* Sort list->hack map alphabetically */ qsort (s->list_elt_to_hack_number, p->screenhacks_count, sizeof(*s->list_elt_to_hack_number), sort_hack_cmp); /* Free names */ for (i = 0; i < p->screenhacks_count; i++) free (sort_hack_cmp_names_kludge[i]); free (sort_hack_cmp_names_kludge); sort_hack_cmp_names_kludge = 0; /* Build inverse table */ for (i = 0; i < p->screenhacks_count; i++) { int n = s->list_elt_to_hack_number[i]; if (n != -1) s->hack_number_to_list_elt[n] = i; } } static int maybe_reload_init_file (state *s) { Display *dpy = GDK_DISPLAY(); saver_preferences *p = &s->prefs; int status = 0; static Bool reentrant_lock = False; if (reentrant_lock) return 0; reentrant_lock = True; if (init_file_changed_p (p)) { const char *f = init_file_name(); char *b; int list_elt; GtkWidget *list; if (!f || !*f) return 0; b = (char *) malloc (strlen(f) + 1024); sprintf (b, _("Warning:\n\n" "file \"%s\" has changed, reloading.\n"), f); warning_dialog (s->toplevel_widget, b, D_NONE, 100); free (b); load_init_file (dpy, p); initialize_sort_map (s); list_elt = selected_list_element (s); list = name_to_widget (s, "list"); gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL); populate_hack_list (s); force_list_select_item (s, list, list_elt, True); populate_prefs_page (s); populate_demo_window (s, list_elt); ensure_selected_item_visible (list); status = 1; } reentrant_lock = False; return status; } /* Making the preview window have the right X visual (so that GL works.) */ static Visual *get_best_gl_visual (state *); static GdkVisual * x_visual_to_gdk_visual (Visual *xv) { GList *gvs = gdk_list_visuals(); if (!xv) return gdk_visual_get_system(); for (; gvs; gvs = gvs->next) { GdkVisual *gv = (GdkVisual *) gvs->data; if (xv == GDK_VISUAL_XVISUAL (gv)) return gv; } fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n", blurb(), (unsigned long) xv->visualid); abort(); } static void clear_preview_window (state *s) { GtkWidget *p; GdkWindow *window; GtkStyle *style; if (!s->toplevel_widget) return; /* very early */ p = name_to_widget (s, "preview"); window = GET_WINDOW (p); if (!window) return; /* Flush the widget background down into the window, in case a subproc has changed it. */ style = gtk_widget_get_style (p); gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]); gdk_window_clear (window); { int list_elt = selected_list_element (s); int hack_number = (list_elt >= 0 ? s->list_elt_to_hack_number[list_elt] : -1); Bool available_p = (hack_number >= 0 ? s->hacks_available_p [hack_number] : True); Bool nothing_p = (s->total_available < 5); #ifdef HAVE_GTK2 GtkWidget *notebook = name_to_widget (s, "preview_notebook"); gtk_notebook_set_page (GTK_NOTEBOOK (notebook), (s->running_preview_error_p ? (available_p ? 1 : nothing_p ? 3 : 2) : 0)); #else /* !HAVE_GTK2 */ if (s->running_preview_error_p) { const char * const lines1[] = { N_("No Preview"), N_("Available") }; const char * const lines2[] = { N_("Not"), N_("Installed") }; int nlines = countof(lines1); int lh = p->style->font->ascent + p->style->font->descent; int y, i; gint w, h; const char * const *lines = (available_p ? lines1 : lines2); gdk_window_get_size (window, &w, &h); y = (h - (lh * nlines)) / 2; y += p->style->font->ascent; for (i = 0; i < nlines; i++) { int sw = gdk_string_width (p->style->font, _(lines[i])); int x = (w - sw) / 2; gdk_draw_string (window, p->style->font, p->style->fg_gc[GTK_STATE_NORMAL], x, y, _(lines[i])); y += lh; } } #endif /* !HAVE_GTK2 */ } gdk_flush (); } static void reset_preview_window (state *s) { /* On some systems (most recently, MacOS X) OpenGL programs get confused when you kill one and re-start another on the same window. So maybe it's best to just always destroy and recreate the preview window when changing hacks, instead of always trying to reuse the same one? */ GtkWidget *pr = name_to_widget (s, "preview"); if (GET_REALIZED (pr)) { GdkWindow *window = GET_WINDOW (pr); Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0); Window id; gtk_widget_hide (pr); gtk_widget_unrealize (pr); gtk_widget_realize (pr); gtk_widget_show (pr); id = (window ? GDK_WINDOW_XWINDOW (window) : 0); if (s->debug_p) fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(), (unsigned int) oid, (unsigned int) id); } } static void fix_preview_visual (state *s) { GtkWidget *widget = name_to_widget (s, "preview"); Visual *xvisual = get_best_gl_visual (s); GdkVisual *visual = x_visual_to_gdk_visual (xvisual); GdkVisual *dvisual = gdk_visual_get_system(); GdkColormap *cmap = (visual == dvisual ? gdk_colormap_get_system () : gdk_colormap_new (visual, False)); if (s->debug_p) fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(), (visual == dvisual ? "default" : "non-default"), (xvisual ? (unsigned long) xvisual->visualid : 0L)); if (!GET_REALIZED (widget) || gtk_widget_get_visual (widget) != visual) { gtk_widget_unrealize (widget); gtk_widget_set_visual (widget, visual); gtk_widget_set_colormap (widget, cmap); gtk_widget_realize (widget); } /* Set the Widget colors to be white-on-black. */ { GdkWindow *window = GET_WINDOW (widget); GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget)); GdkColormap *cmap = gtk_widget_get_colormap (widget); GdkColor *fg = &style->fg[GTK_STATE_NORMAL]; GdkColor *bg = &style->bg[GTK_STATE_NORMAL]; GdkGC *fgc = gdk_gc_new(window); GdkGC *bgc = gdk_gc_new(window); if (!gdk_color_white (cmap, fg)) abort(); if (!gdk_color_black (cmap, bg)) abort(); gdk_gc_set_foreground (fgc, fg); gdk_gc_set_background (fgc, bg); gdk_gc_set_foreground (bgc, bg); gdk_gc_set_background (bgc, fg); style->fg_gc[GTK_STATE_NORMAL] = fgc; style->bg_gc[GTK_STATE_NORMAL] = fgc; gtk_widget_set_style (widget, style); /* For debugging purposes, put a title on the window (so that it can be easily found in the output of "xwininfo -tree".) */ gdk_window_set_title (window, "Preview"); } gtk_widget_show (widget); } /* Subprocesses */ static char * subproc_pretty_name (state *s) { if (s->running_preview_cmd) { char *ps = strdup (s->running_preview_cmd); char *ss = strchr (ps, ' '); if (ss) *ss = 0; ss = strrchr (ps, '/'); if (!ss) ss = ps; else { ss = strdup (ss+1); free (ps); } return ss; } else return strdup ("???"); } static void reap_zombies (state *s) { int wait_status = 0; pid_t pid; while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0) { if (s->debug_p) { if (pid == s->running_preview_pid) { char *ss = subproc_pretty_name (s); fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(), (unsigned long) pid, ss); free (ss); } else fprintf (stderr, "%s: pid %lu died\n", blurb(), (unsigned long) pid); } } } /* Mostly lifted from driver/subprocs.c */ static Visual * get_best_gl_visual (state *s) { Display *dpy = GDK_DISPLAY(); pid_t forked; int fds [2]; int in, out; char buf[1024]; char *av[10]; int ac = 0; av[ac++] = "xscreensaver-gl-helper"; av[ac] = 0; if (pipe (fds)) { perror ("error creating pipe:"); return 0; } in = fds [0]; out = fds [1]; switch ((int) (forked = fork ())) { case -1: { sprintf (buf, "%s: couldn't fork", blurb()); perror (buf); exit (1); } case 0: { int stdout_fd = 1; close (in); /* don't need this one */ close (ConnectionNumber (dpy)); /* close display fd */ if (dup2 (out, stdout_fd) < 0) /* pipe stdout */ { perror ("could not dup() a new stdout:"); return 0; } execvp (av[0], av); /* shouldn't return. */ if (errno != ENOENT) { /* Ignore "no such file or directory" errors, unless verbose. Issue all other exec errors, though. */ sprintf (buf, "%s: running %s", blurb(), av[0]); perror (buf); } /* Note that one must use _exit() instead of exit() in procs forked off of Gtk programs -- Gtk installs an atexit handler that has a copy of the X connection (which we've already closed, for safety.) If one uses exit() instead of _exit(), then one sometimes gets a spurious "Gdk-ERROR: Fatal IO error on X server" error message. */ _exit (1); /* exits fork */ break; } default: { int result = 0; int wait_status = 0; FILE *f = fdopen (in, "r"); unsigned int v = 0; char c; close (out); /* don't need this one */ *buf = 0; if (!fgets (buf, sizeof(buf)-1, f)) *buf = 0; fclose (f); /* Wait for the child to die. */ waitpid (-1, &wait_status, 0); if (1 == sscanf (buf, "0x%x %c", &v, &c)) result = (int) v; if (result == 0) { if (s->debug_p) fprintf (stderr, "%s: %s did not report a GL visual!\n", blurb(), av[0]); return 0; } else { Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result); if (s->debug_p) fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n", blurb(), av[0], result); if (!v) abort(); return v; } } } abort(); } static void kill_preview_subproc (state *s, Bool reset_p) { s->running_preview_error_p = False; reap_zombies (s); clear_preview_window (s); if (s->subproc_check_timer_id) { gtk_timeout_remove (s->subproc_check_timer_id); s->subproc_check_timer_id = 0; s->subproc_check_countdown = 0; } if (s->running_preview_pid) { int status = kill (s->running_preview_pid, SIGTERM); char *ss = subproc_pretty_name (s); if (status < 0) { if (errno == ESRCH) { if (s->debug_p) fprintf (stderr, "%s: pid %lu (%s) was already dead.\n", blurb(), (unsigned long) s->running_preview_pid, ss); } else { char buf [1024]; sprintf (buf, "%s: couldn't kill pid %lu (%s)", blurb(), (unsigned long) s->running_preview_pid, ss); perror (buf); } } else { int endstatus; waitpid(s->running_preview_pid, &endstatus, 0); if (s->debug_p) fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(), (unsigned long) s->running_preview_pid, ss); } free (ss); s->running_preview_pid = 0; if (s->running_preview_cmd) free (s->running_preview_cmd); s->running_preview_cmd = 0; } reap_zombies (s); if (reset_p) { reset_preview_window (s); clear_preview_window (s); } } /* Immediately and unconditionally launches the given process, after appending the -window-id option; sets running_preview_pid. */ static void launch_preview_subproc (state *s) { saver_preferences *p = &s->prefs; Window id; char *new_cmd = 0; pid_t forked; const char *cmd = s->desired_preview_cmd; GtkWidget *pr = name_to_widget (s, "preview"); GdkWindow *window; reset_preview_window (s); window = GET_WINDOW (pr); s->running_preview_error_p = False; if (s->preview_suppressed_p) { kill_preview_subproc (s, False); goto DONE; } new_cmd = malloc (strlen (cmd) + 40); id = (window ? GDK_WINDOW_XWINDOW (window) : 0); if (id == 0) { /* No window id? No command to run. */ free (new_cmd); new_cmd = 0; } else { strcpy (new_cmd, cmd); sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X", (unsigned int) id); } kill_preview_subproc (s, False); if (! new_cmd) { s->running_preview_error_p = True; clear_preview_window (s); goto DONE; } switch ((int) (forked = fork ())) { case -1: { char buf[255]; sprintf (buf, "%s: couldn't fork", blurb()); perror (buf); s->running_preview_error_p = True; goto DONE; break; } case 0: { close (ConnectionNumber (GDK_DISPLAY())); hack_subproc_environment (id, s->debug_p); usleep (250000); /* pause for 1/4th second before launching, to give the previous program time to die and flush its X buffer, so we don't get leftover turds on the window. */ exec_command (p->shell, new_cmd, p->nice_inferior); /* Don't bother printing an error message when we are unable to exec subprocesses; we handle that by polling the pid later. Note that one must use _exit() instead of exit() in procs forked off of Gtk programs -- Gtk installs an atexit handler that has a copy of the X connection (which we've already closed, for safety.) If one uses exit() instead of _exit(), then one sometimes gets a spurious "Gdk-ERROR: Fatal IO error on X server" error message. */ _exit (1); /* exits child fork */ break; default: if (s->running_preview_cmd) free (s->running_preview_cmd); s->running_preview_cmd = strdup (s->desired_preview_cmd); s->running_preview_pid = forked; if (s->debug_p) { char *ss = subproc_pretty_name (s); fprintf (stderr, "%s: forked %lu (%s)\n", blurb(), (unsigned long) forked, ss); free (ss); } break; } } schedule_preview_check (s); DONE: if (new_cmd) free (new_cmd); new_cmd = 0; } /* Modify $DISPLAY and $PATH for the benefit of subprocesses. */ static void hack_environment (state *s) { static const char *def_path = # ifdef DEFAULT_PATH_PREFIX DEFAULT_PATH_PREFIX; # else ""; # endif Display *dpy = GDK_DISPLAY(); const char *odpy = DisplayString (dpy); char *ndpy = (char *) malloc(strlen(odpy) + 20); strcpy (ndpy, "DISPLAY="); strcat (ndpy, odpy); if (putenv (ndpy)) abort (); if (s->debug_p) fprintf (stderr, "%s: %s\n", blurb(), ndpy); /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not. So we must leak it (and/or the previous setting). Yay. */ if (def_path && *def_path) { const char *opath = getenv("PATH"); char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20); strcpy (npath, "PATH="); strcat (npath, def_path); strcat (npath, ":"); strcat (npath, opath); if (putenv (npath)) abort (); /* do not free(npath) -- see above */ if (s->debug_p) fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path); } } static void hack_subproc_environment (Window preview_window_id, Bool debug_p) { /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly necessary yet, but it will make programs work if we had invoked them with "-root" and not with "-window-id" -- which, of course, doesn't happen. */ char *nssw = (char *) malloc (40); sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id); /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems any more, right? It's not Posix, but everyone seems to have it. */ if (putenv (nssw)) abort (); if (debug_p) fprintf (stderr, "%s: %s\n", blurb(), nssw); /* do not free(nssw) -- see above */ } /* Called from a timer: Launches the currently-chosen subprocess, if it's not already running. If there's a different process running, kills it. */ static int update_subproc_timer (gpointer data) { state *s = (state *) data; if (! s->desired_preview_cmd) kill_preview_subproc (s, True); else if (!s->running_preview_cmd || !!strcmp (s->desired_preview_cmd, s->running_preview_cmd)) launch_preview_subproc (s); s->subproc_timer_id = 0; return FALSE; /* do not re-execute timer */ } static int settings_timer (gpointer data) { settings_cb (0, 0); return FALSE; } /* Call this when you think you might want a preview process running. It will set a timer that will actually launch that program a second from now, if you haven't changed your mind (to avoid double-click spazzing, etc.) `cmd' may be null meaning "no process". */ static void schedule_preview (state *s, const char *cmd) { int delay = 1000 * 0.5; /* 1/2 second hysteresis */ if (s->debug_p) { if (cmd) fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd); else fprintf (stderr, "%s: scheduling preview death\n", blurb()); } if (s->desired_preview_cmd) free (s->desired_preview_cmd); s->desired_preview_cmd = (cmd ? strdup (cmd) : 0); if (s->subproc_timer_id) gtk_timeout_remove (s->subproc_timer_id); s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s); } /* Called from a timer: Checks to see if the subproc that should be running, actually is. */ static int check_subproc_timer (gpointer data) { state *s = (state *) data; Bool again_p = True; if (s->running_preview_error_p || /* already dead */ s->running_preview_pid <= 0) { again_p = False; } else { int status; reap_zombies (s); status = kill (s->running_preview_pid, 0); if (status < 0 && errno == ESRCH) s->running_preview_error_p = True; if (s->debug_p) { char *ss = subproc_pretty_name (s); fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(), (unsigned long) s->running_preview_pid, ss, (s->running_preview_error_p ? "dead" : "alive")); free (ss); } if (s->running_preview_error_p) { clear_preview_window (s); again_p = False; } } /* Otherwise, it's currently alive. We might be checking again, or we might be satisfied. */ if (--s->subproc_check_countdown <= 0) again_p = False; if (again_p) return TRUE; /* re-execute timer */ else { s->subproc_check_timer_id = 0; s->subproc_check_countdown = 0; return FALSE; /* do not re-execute timer */ } } /* Call this just after launching a subprocess. This sets a timer that will, five times a second for two seconds, check whether the program is still running. The assumption here is that if the process didn't stay up for more than a couple of seconds, then either the program doesn't exist, or it doesn't take a -window-id argument. */ static void schedule_preview_check (state *s) { int seconds = 2; int ticks = 5; if (s->debug_p) fprintf (stderr, "%s: scheduling check\n", blurb()); if (s->subproc_check_timer_id) gtk_timeout_remove (s->subproc_check_timer_id); s->subproc_check_timer_id = gtk_timeout_add (1000 / ticks, check_subproc_timer, (gpointer) s); s->subproc_check_countdown = ticks * seconds; } static Bool screen_blanked_p (void) { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *dataP = 0; Display *dpy = GDK_DISPLAY(); Bool blanked_p = False; if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */ XA_SCREENSAVER_STATUS, 0, 3, False, XA_INTEGER, &type, &format, &nitems, &bytesafter, &dataP) == Success && type == XA_INTEGER && nitems >= 3 && dataP) { Atom *data = (Atom *) dataP; blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK); } if (dataP) XFree (dataP); return blanked_p; } /* Wake up every now and then and see if the screen is blanked. If it is, kill off the small-window demo -- no point in wasting cycles by running two screensavers at once... */ static int check_blanked_timer (gpointer data) { state *s = (state *) data; Bool blanked_p = screen_blanked_p (); if (blanked_p && s->running_preview_pid) { if (s->debug_p) fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb()); kill_preview_subproc (s, True); } return True; /* re-execute timer */ } /* How many screens are there (including Xinerama.) */ static int screen_count (Display *dpy) { int nscreens = ScreenCount(dpy); # ifdef HAVE_XINERAMA if (nscreens <= 1) { int event_number, error_number; if (XineramaQueryExtension (dpy, &event_number, &error_number) && XineramaIsActive (dpy)) { XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens); if (xsi) XFree (xsi); } } # endif /* HAVE_XINERAMA */ return nscreens; } /* Setting window manager icon */ static void init_icon (GdkWindow *window) { GdkBitmap *mask = 0; GdkColor transp; GdkPixmap *pixmap = gdk_pixmap_create_from_xpm_d (window, &mask, &transp, (gchar **) logo_50_xpm); if (pixmap) gdk_window_set_icon (window, 0, pixmap, mask); } /* The main demo-mode command loop. */ #if 0 static Bool mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks, XrmRepresentation *type, XrmValue *value, XPointer closure) { int i; for (i = 0; quarks[i]; i++) { if (bindings[i] == XrmBindTightly) fprintf (stderr, (i == 0 ? "" : ".")); else if (bindings[i] == XrmBindLoosely) fprintf (stderr, "*"); else fprintf (stderr, " ??? "); fprintf(stderr, "%s", XrmQuarkToString (quarks[i])); } fprintf (stderr, ": %s\n", (char *) value->addr); return False; } #endif static Window gnome_screensaver_window (Screen *screen) { Display *dpy = DisplayOfScreen (screen); Window root = RootWindowOfScreen (screen); Window parent, *kids; unsigned int nkids; Window gnome_window = 0; int i; if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids)) abort (); for (i = 0; i < nkids; i++) { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *name; if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128, False, XA_STRING, &type, &format, &nitems, &bytesafter, &name) == Success && type != None && !strcmp ((char *) name, "gnome-screensaver")) { gnome_window = kids[i]; break; } } if (kids) XFree ((char *) kids); return gnome_window; } static Bool gnome_screensaver_active_p (void) { Display *dpy = GDK_DISPLAY(); Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy)); return (w ? True : False); } static void kill_gnome_screensaver (void) { Display *dpy = GDK_DISPLAY(); Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy)); if (w) XKillClient (dpy, (XID) w); } static Bool kde_screensaver_active_p (void) { FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null", "r"); char buf[255]; fgets (buf, sizeof(buf)-1, p); pclose (p); if (!strcmp (buf, "true\n")) return True; else return False; } static void kill_kde_screensaver (void) { system ("dcop kdesktop KScreensaverIface enable false"); } static void the_network_is_not_the_computer (state *s) { Display *dpy = GDK_DISPLAY(); char *rversion = 0, *ruser = 0, *rhost = 0; char *luser, *lhost; char *msg = 0; struct passwd *p = getpwuid (getuid ()); const char *d = DisplayString (dpy); # if defined(HAVE_UNAME) struct utsname uts; if (uname (&uts) < 0) lhost = ""; else lhost = uts.nodename; # elif defined(VMS) strcpy (lhost, getenv("SYS$NODE")); # else /* !HAVE_UNAME && !VMS */ strcat (lhost, ""); # endif /* !HAVE_UNAME && !VMS */ if (p && p->pw_name) luser = p->pw_name; else luser = "???"; server_xscreensaver_version (dpy, &rversion, &ruser, &rhost); /* Make a buffer that's big enough for a number of copies of all the strings, plus some. */ msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) + (ruser ? strlen(ruser) : 0) + (rhost ? strlen(rhost) : 0) + strlen(lhost) + strlen(luser) + strlen(d) + 1024)); *msg = 0; if (!rversion || !*rversion) { sprintf (msg, _("Warning:\n\n" "The XScreenSaver daemon doesn't seem to be running\n" "on display \"%s\". Launch it now?"), d); } else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name)) { /* Warn that the two processes are running as different users. */ sprintf(msg, _("Warning:\n\n" "%s is running as user \"%s\" on host \"%s\".\n" "But the xscreensaver managing display \"%s\"\n" "is running as user \"%s\" on host \"%s\".\n" "\n" "Since they are different users, they won't be reading/writing\n" "the same ~/.xscreensaver file, so %s isn't\n" "going to work right.\n" "\n" "You should either re-run %s as \"%s\", or re-run\n" "xscreensaver as \"%s\".\n" "\n" "Restart the xscreensaver daemon now?\n"), progname, luser, lhost, d, (ruser ? ruser : "???"), (rhost ? rhost : "???"), progname, progname, (ruser ? ruser : "???"), luser); } else if (rhost && *rhost && !!strcmp (rhost, lhost)) { /* Warn that the two processes are running on different hosts. */ sprintf (msg, _("Warning:\n\n" "%s is running as user \"%s\" on host \"%s\".\n" "But the xscreensaver managing display \"%s\"\n" "is running as user \"%s\" on host \"%s\".\n" "\n" "If those two machines don't share a file system (that is,\n" "if they don't see the same ~%s/.xscreensaver file) then\n" "%s won't work right.\n" "\n" "Restart the daemon on \"%s\" as \"%s\" now?\n"), progname, luser, lhost, d, (ruser ? ruser : "???"), (rhost ? rhost : "???"), luser, progname, lhost, luser); } else if (!!strcmp (rversion, s->short_version)) { /* Warn that the version numbers don't match. */ sprintf (msg, _("Warning:\n\n" "This is %s version %s.\n" "But the xscreensaver managing display \"%s\"\n" "is version %s. This could cause problems.\n" "\n" "Restart the xscreensaver daemon now?\n"), progname, s->short_version, d, rversion); } if (*msg) warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1); if (rversion) free (rversion); if (ruser) free (ruser); if (rhost) free (rhost); free (msg); msg = 0; /* Note: since these dialogs are not modal, they will stack up. So we do this check *after* popping up the "xscreensaver is not running" dialog so that these are on top. Good enough. */ if (gnome_screensaver_active_p ()) warning_dialog (s->toplevel_widget, _("Warning:\n\n" "The GNOME screensaver daemon appears to be running.\n" "It must be stopped for XScreenSaver to work properly.\n" "\n" "Stop the GNOME screen saver daemon now?\n"), D_GNOME, 1); if (kde_screensaver_active_p ()) warning_dialog (s->toplevel_widget, _("Warning:\n\n" "The KDE screen saver daemon appears to be running.\n" "It must be stopped for XScreenSaver to work properly.\n" "\n" "Stop the KDE screen saver daemon now?\n"), D_KDE, 1); } /* We use this error handler so that X errors are preceeded by the name of the program that generated them. */ static int demo_ehandler (Display *dpy, XErrorEvent *error) { state *s = global_state_kludge; /* I hate C so much... */ fprintf (stderr, "\nX error in %s:\n", blurb()); XmuPrintDefaultErrorMessage (dpy, error, stderr); kill_preview_subproc (s, False); exit (-1); return 0; } /* We use this error handler so that Gtk/Gdk errors are preceeded by the name of the program that generated them; and also that we can ignore one particular bogus error message that Gdk madly spews. */ static void g_log_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { /* Ignore the message "Got event for unknown window: 0x...". Apparently some events are coming in for the xscreensaver window (presumably reply events related to the ClientMessage) and Gdk feels the need to complain about them. So, just suppress any messages that look like that one. */ if (strstr (message, "unknown window")) return; fprintf (stderr, "%s: %s-%s: %s%s", blurb(), (log_domain ? log_domain : progclass), (log_level == G_LOG_LEVEL_ERROR ? "error" : log_level == G_LOG_LEVEL_CRITICAL ? "critical" : log_level == G_LOG_LEVEL_WARNING ? "warning" : log_level == G_LOG_LEVEL_MESSAGE ? "message" : log_level == G_LOG_LEVEL_INFO ? "info" : log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"), message, ((!*message || message[strlen(message)-1] != '\n') ? "\n" : "")); } #ifdef __GNUC__ __extension__ /* shut up about "string length is greater than the length ISO C89 compilers are required to support" when including the .ad file... */ #endif STFU static char *defaults[] = { #include "XScreenSaver_ad.h" 0 }; #if 0 #ifdef HAVE_CRAPPLET static struct poptOption crapplet_options[] = { {NULL, '\0', 0, NULL, 0} }; #endif /* HAVE_CRAPPLET */ #endif /* 0 */ const char *usage = "[--display dpy] [--prefs | --settings]" # ifdef HAVE_CRAPPLET " [--crapplet]" # endif "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]"; static void map_popup_window_cb (GtkWidget *w, gpointer user_data) { state *s = (state *) user_data; Boolean oi = s->initializing_p; #ifndef HAVE_GTK2 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc")); #endif s->initializing_p = True; #ifndef HAVE_GTK2 eschew_gtk_lossage (label); #endif s->initializing_p = oi; } #if 0 static void print_widget_tree (GtkWidget *w, int depth) { int i; for (i = 0; i < depth; i++) fprintf (stderr, " "); fprintf (stderr, "%s\n", gtk_widget_get_name (w)); if (GTK_IS_LIST (w)) { for (i = 0; i < depth+1; i++) fprintf (stderr, " "); fprintf (stderr, "...list kids...\n"); } else if (GTK_IS_CONTAINER (w)) { GList *kids = gtk_container_children (GTK_CONTAINER (w)); while (kids) { print_widget_tree (GTK_WIDGET (kids->data), depth+1); kids = kids->next; } } } #endif /* 0 */ static int delayed_scroll_kludge (gpointer data) { state *s = (state *) data; GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list")); ensure_selected_item_visible (w); /* Oh, this is just fucking lovely, too. */ w = GTK_WIDGET (name_to_widget (s, "preview")); gtk_widget_hide (w); gtk_widget_show (w); return FALSE; /* do not re-execute timer */ } #ifdef HAVE_GTK2 GtkWidget * create_xscreensaver_demo (void) { GtkWidget *nb; nb = name_to_widget (global_state_kludge, "preview_notebook"); gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE); return name_to_widget (global_state_kludge, "xscreensaver_demo"); } GtkWidget * create_xscreensaver_settings_dialog (void) { GtkWidget *w, *box; box = name_to_widget (global_state_kludge, "dialog_action_area"); w = name_to_widget (global_state_kludge, "adv_button"); gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE); w = name_to_widget (global_state_kludge, "std_button"); gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE); return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog"); } #endif /* HAVE_GTK2 */ int main (int argc, char **argv) { XtAppContext app; state S, *s; saver_preferences *p; Bool prefs_p = False; Bool settings_p = False; int i; Display *dpy; Widget toplevel_shell; char *real_progname = argv[0]; char *window_title; char *geom = 0; Bool crapplet_p = False; char *str; #ifdef ENABLE_NLS bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); textdomain (GETTEXT_PACKAGE); # ifdef HAVE_GTK2 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); # else /* !HAVE_GTK2 */ if (!setlocale (LC_ALL, "")) fprintf (stderr, "%s: locale not supported by C library\n", real_progname); # endif /* !HAVE_GTK2 */ #endif /* ENABLE_NLS */ str = strrchr (real_progname, '/'); if (str) real_progname = str+1; s = &S; memset (s, 0, sizeof(*s)); s->initializing_p = True; p = &s->prefs; global_state_kludge = s; /* I hate C so much... */ progname = real_progname; s->short_version = (char *) malloc (5); memcpy (s->short_version, screensaver_id + 17, 4); s->short_version [4] = 0; /* Register our error message logger for every ``log domain'' known. There's no way to do this globally, so I grepped the Gtk/Gdk sources for all of the domains that seem to be in use. */ { const char * const domains[] = { 0, "Gtk", "Gdk", "GLib", "GModule", "GThread", "Gnome", "GnomeUI" }; for (i = 0; i < countof(domains); i++) g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0); } #ifdef DEFAULT_ICONDIR /* from -D on compile line */ # ifndef HAVE_GTK2 { const char *dir = DEFAULT_ICONDIR; if (*dir) add_pixmap_directory (dir); } # endif /* !HAVE_GTK2 */ #endif /* DEFAULT_ICONDIR */ /* This is gross, but Gtk understands --display and not -display... */ for (i = 1; i < argc; i++) if (argv[i][0] && argv[i][1] && !strncmp(argv[i], "-display", strlen(argv[i]))) argv[i] = "--display"; /* We need to parse this arg really early... Sigh. */ for (i = 1; i < argc; i++) { if (argv[i] && (!strcmp(argv[i], "--crapplet") || !strcmp(argv[i], "--capplet"))) { # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2) int j; crapplet_p = True; for (j = i; j < argc; j++) /* remove it from the list */ argv[j] = argv[j+1]; argc--; # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */ fprintf (stderr, "%s: not compiled with --crapplet support\n", real_progname); fprintf (stderr, "%s: %s\n", real_progname, usage); exit (1); # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */ } else if (argv[i] && (!strcmp(argv[i], "--debug") || !strcmp(argv[i], "-debug") || !strcmp(argv[i], "-d"))) { int j; s->debug_p = True; for (j = i; j < argc; j++) /* remove it from the list */ argv[j] = argv[j+1]; argc--; i--; } else if (argv[i] && argc > i+1 && *argv[i+1] && (!strcmp(argv[i], "-geometry") || !strcmp(argv[i], "-geom") || !strcmp(argv[i], "-geo") || !strcmp(argv[i], "-g"))) { int j; geom = argv[i+1]; for (j = i; j < argc; j++) /* remove them from the list */ argv[j] = argv[j+2]; argc -= 2; i -= 2; } else if (argv[i] && argc > i+1 && *argv[i+1] && (!strcmp(argv[i], "--configdir"))) { int j; struct stat st; hack_configuration_path = argv[i+1]; for (j = i; j < argc; j++) /* remove them from the list */ argv[j] = argv[j+2]; argc -= 2; i -= 2; if (0 != stat (hack_configuration_path, &st)) { char buf[255]; sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path); perror (buf); exit (1); } else if (!S_ISDIR (st.st_mode)) { fprintf (stderr, "%s: not a directory: %s\n", blurb(), hack_configuration_path); exit (1); } } } if (s->debug_p) fprintf (stderr, "%s: using config directory \"%s\"\n", progname, hack_configuration_path); /* Let Gtk open the X connection, then initialize Xt to use that same connection. Doctor Frankenstein would be proud. */ # ifdef HAVE_CRAPPLET if (crapplet_p) { GnomeClient *client; GnomeClientFlags flags = 0; int init_results = gnome_capplet_init ("screensaver-properties", s->short_version, argc, argv, NULL, 0, NULL); /* init_results is: 0 upon successful initialization; 1 if --init-session-settings was passed on the cmdline; 2 if --ignore was passed on the cmdline; -1 on error. So the 1 signifies just to init the settings, and quit, basically. (Meaning launch the xscreensaver daemon.) */ if (init_results < 0) { # if 0 g_error ("An initialization error occurred while " "starting xscreensaver-capplet.\n"); # else /* !0 */ fprintf (stderr, "%s: gnome_capplet_init failed: %d\n", real_progname, init_results); exit (1); # endif /* !0 */ } client = gnome_master_client (); if (client) flags = gnome_client_get_flags (client); if (flags & GNOME_CLIENT_IS_CONNECTED) { int token = gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES", gnome_client_get_id (client)); if (token) { char *session_args[20]; int i = 0; session_args[i++] = real_progname; session_args[i++] = "--capplet"; session_args[i++] = "--init-session-settings"; session_args[i] = 0; gnome_client_set_priority (client, 20); gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY); gnome_client_set_restart_command (client, i, session_args); } else { gnome_client_set_restart_style (client, GNOME_RESTART_NEVER); } gnome_client_flush (client); } if (init_results == 1) { system ("xscreensaver -nosplash &"); return 0; } } else # endif /* HAVE_CRAPPLET */ { gtk_init (&argc, &argv); } /* We must read exactly the same resources as xscreensaver. That means we must have both the same progclass *and* progname, at least as far as the resource database is concerned. So, put "xscreensaver" in argv[0] while initializing Xt. */ argv[0] = "xscreensaver"; progname = argv[0]; /* Teach Xt to use the Display that Gtk/Gdk have already opened. */ XtToolkitInitialize (); app = XtCreateApplicationContext (); dpy = GDK_DISPLAY(); XtAppSetFallbackResources (app, defaults); XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv); toplevel_shell = XtAppCreateShell (progname, progclass, applicationShellWidgetClass, dpy, 0, 0); dpy = XtDisplay (toplevel_shell); db = XtDatabase (dpy); XtGetApplicationNameAndClass (dpy, &progname, &progclass); XSetErrorHandler (demo_ehandler); /* Let's just ignore these. They seem to confuse Irix Gtk... */ signal (SIGPIPE, SIG_IGN); /* After doing Xt-style command-line processing, complain about any unrecognized command-line arguments. */ for (i = 1; i < argc; i++) { char *str = argv[i]; if (str[0] == '-' && str[1] == '-') str++; if (!strcmp (str, "-prefs")) prefs_p = True; else if (!strcmp (str, "-settings")) settings_p = True; else if (crapplet_p) /* There are lots of random args that we don't care about when we're started as a crapplet, so just ignore unknown args in that case. */ ; else { fprintf (stderr, _("%s: unknown option: %s\n"), real_progname, argv[i]); fprintf (stderr, "%s: %s\n", real_progname, usage); exit (1); } } /* Load the init file, which may end up consulting the X resource database and the site-wide app-defaults file. Note that at this point, it's important that `progname' be "xscreensaver", rather than whatever was in argv[0]. */ p->db = db; s->nscreens = screen_count (dpy); hack_environment (s); /* must be before initialize_sort_map() */ load_init_file (dpy, p); initialize_sort_map (s); /* Now that Xt has been initialized, and the resources have been read, we can set our `progname' variable to something more in line with reality. */ progname = real_progname; #if 0 /* Print out all the resources we read. */ { XrmName name = { 0 }; XrmClass class = { 0 }; int count = 0; XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper, (POINTER) &count); } #endif /* Intern the atoms that xscreensaver_command() needs. */ XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False); XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False); XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False); XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False); XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False); XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False); XA_SELECT = XInternAtom (dpy, "SELECT", False); XA_DEMO = XInternAtom (dpy, "DEMO", False); XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False); XA_BLANK = XInternAtom (dpy, "BLANK", False); XA_LOCK = XInternAtom (dpy, "LOCK", False); XA_EXIT = XInternAtom (dpy, "EXIT", False); XA_RESTART = XInternAtom (dpy, "RESTART", False); /* Create the window and all its widgets. */ s->base_widget = create_xscreensaver_demo (); s->popup_widget = create_xscreensaver_settings_dialog (); s->toplevel_widget = s->base_widget; /* Set the main window's title. */ { char *base_title = _("Screensaver Preferences"); char *v = (char *) strdup(strchr(screensaver_id, ' ')); char *s1, *s2, *s3, *s4; s1 = (char *) strchr(v, ' '); s1++; s2 = (char *) strchr(s1, ' '); s3 = (char *) strchr(v, '('); s3++; s4 = (char *) strchr(s3, ')'); *s2 = 0; *s4 = 0; window_title = (char *) malloc (strlen (base_title) + strlen (progclass) + strlen (s1) + strlen (s3) + 100); sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3); gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title); gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title); free (v); } /* Adjust the (invisible) notebooks on the popup dialog... */ { GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook")); GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button")); int page = 0; # ifdef HAVE_XML gtk_widget_hide (std); # else /* !HAVE_XML */ /* Make the advanced page be the only one available. */ gtk_widget_set_sensitive (std, False); std = GTK_WIDGET (name_to_widget (s, "adv_button")); gtk_widget_hide (std); std = GTK_WIDGET (name_to_widget (s, "reset_button")); gtk_widget_hide (std); page = 1; # endif /* !HAVE_XML */ gtk_notebook_set_page (notebook, page); gtk_notebook_set_show_tabs (notebook, False); } /* Various other widget initializations... */ gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event", GTK_SIGNAL_FUNC (wm_toplevel_close_cb), (gpointer) s); gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event", GTK_SIGNAL_FUNC (wm_popup_close_cb), (gpointer) s); populate_hack_list (s); populate_prefs_page (s); sensitize_demo_widgets (s, False); fix_text_entry_sizes (s); scroll_to_current_hack (s); gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")), "map", GTK_SIGNAL_FUNC(map_popup_window_cb), (gpointer) s); #ifndef HAVE_GTK2 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")), "map", GTK_SIGNAL_FUNC(map_prev_button_cb), (gpointer) s); gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")), "map", GTK_SIGNAL_FUNC(map_next_button_cb), (gpointer) s); #endif /* !HAVE_GTK2 */ /* Hook up callbacks to the items on the mode menu. */ { GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu")); GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt)); GList *kids = gtk_container_children (GTK_CONTAINER (menu)); int i; for (i = 0; kids; kids = kids->next, i++) { gtk_signal_connect (GTK_OBJECT (kids->data), "activate", GTK_SIGNAL_FUNC (mode_menu_item_cb), (gpointer) s); /* The "random-same" mode menu item does not appear unless there are multple screens. */ if (s->nscreens <= 1 && mode_menu_order[i] == RANDOM_HACKS_SAME) gtk_widget_hide (GTK_WIDGET (kids->data)); } if (s->nscreens <= 1) /* recompute option-menu size */ { gtk_widget_unrealize (GTK_WIDGET (menu)); gtk_widget_realize (GTK_WIDGET (menu)); } } /* Handle the -prefs command-line argument. */ if (prefs_p) { GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "notebook")); gtk_notebook_set_page (notebook, 1); } # ifdef HAVE_CRAPPLET if (crapplet_p) { GtkWidget *capplet; GtkWidget *outer_vbox; gtk_widget_hide (s->toplevel_widget); capplet = capplet_widget_new (); /* Make there be a "Close" button instead of "OK" and "Cancel" */ # ifdef HAVE_CRAPPLET_IMMEDIATE capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet)); # endif /* HAVE_CRAPPLET_IMMEDIATE */ /* In crapplet-mode, take off the menubar. */ gtk_widget_hide (name_to_widget (s, "menubar")); /* Reparent our top-level container to be a child of the capplet window. */ outer_vbox = GTK_BIN (s->toplevel_widget)->child; gtk_widget_ref (outer_vbox); gtk_container_remove (GTK_CONTAINER (s->toplevel_widget), outer_vbox); STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING); gtk_container_add (GTK_CONTAINER (capplet), outer_vbox); /* Find the window above us, and set the title and close handler. */ { GtkWidget *window = capplet; while (window && !GTK_IS_WINDOW (window)) window = GET_PARENT (window); if (window) { gtk_window_set_title (GTK_WINDOW (window), window_title); gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (wm_toplevel_close_cb), (gpointer) s); } } s->toplevel_widget = capplet; } # endif /* HAVE_CRAPPLET */ /* The Gnome folks hate the menubar. I think it's important to have access to the commands on the File menu (Restart Daemon, etc.) and to the About and Documentation commands on the Help menu. */ #if 0 #ifdef HAVE_GTK2 gtk_widget_hide (name_to_widget (s, "menubar")); #endif #endif free (window_title); window_title = 0; #ifdef HAVE_GTK2 /* After picking the default size, allow -geometry to override it. */ if (geom) gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom); #endif gtk_widget_show (s->toplevel_widget); init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */ fix_preview_visual (s); /* Realize page zero, so that we can diddle the scrollbar when the user tabs back to it -- otherwise, the current hack isn't scrolled to the first time they tab back there, when started with "-prefs". (Though it is if they then tab away, and back again.) #### Bah! This doesn't work. Gtk eats my ass! Someone who #### understands this crap, explain to me how to make this work. */ gtk_widget_realize (name_to_widget (s, "demos_table")); gtk_timeout_add (60 * 1000, check_blanked_timer, s); /* Handle the --settings command-line argument. */ if (settings_p) gtk_timeout_add (500, settings_timer, 0); /* Issue any warnings about the running xscreensaver daemon. */ if (! s->debug_p) the_network_is_not_the_computer (s); /* Run the Gtk event loop, and not the Xt event loop. This means that if there were Xt timers or fds registered, they would never get serviced, and if there were any Xt widgets, they would never have events delivered. Fortunately, we're using Gtk for all of the UI, and only initialized Xt so that we could process the command line and use the X resource manager. */ s->initializing_p = False; /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds after we start up. Otherwise, it always appears scrolled to the top when in crapplet-mode. */ gtk_timeout_add (500, delayed_scroll_kludge, s); #if 1 /* Load every configurator in turn, to scan them for errors all at once. */ if (s->debug_p) { int i; for (i = 0; i < p->screenhacks_count; i++) { screenhack *hack = p->screenhacks[i]; conf_data *d = load_configurator (hack->command, s->debug_p); if (d) free_conf_data (d); } } #endif # ifdef HAVE_CRAPPLET if (crapplet_p) capplet_gtk_main (); else # endif /* HAVE_CRAPPLET */ gtk_main (); kill_preview_subproc (s, False); exit (0); } #endif /* HAVE_GTK -- whole file */ xscreensaver-5.15/driver/demo-Xm-widgets.c000066400000000000000000000737421164314150500206020ustar00rootroot00000000000000/* demo-Xm.c --- implements the interactive demo-mode and options dialogs. * xscreensaver, Copyright (c) 1999, 2003 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include /* just for debug info */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_XMCOMBOBOX /* a Motif 2.0 widget */ # include # ifndef XmNtextField /* Lesstif 0.89.4 bug */ # undef HAVE_XMCOMBOBOX # endif #endif /* HAVE_XMCOMBOBOX */ #include #include const char *visual_menu[] = { "Any", "Best", "Default", "Default-N", "GL", "TrueColor", "PseudoColor", "StaticGray", "GrayScale", "DirectColor", "Color", "Gray", "Mono", 0 }; static Widget create_demos_page (Widget parent); static Widget create_options_page (Widget parent); static void tab_cb (Widget button, XtPointer client_data, XtPointer ignored) { Widget parent = XtParent(button); Widget tabber = XtNameToWidget (parent, "*folder"); Widget this_tab = (Widget) client_data; Widget *kids = 0; Cardinal nkids = 0; if (!tabber) abort(); XtVaGetValues (tabber, XmNnumChildren, &nkids, XmNchildren, &kids, NULL); if (!kids) abort(); if (nkids > 0) XtUnmanageChildren (kids, nkids); XtManageChild (this_tab); } Widget create_xscreensaver_demo (Widget parent) { /* MainWindow Form Menubar DemoTab OptionsTab HR Tabber (demo page) (options page) */ Widget mainw, form, menubar; Widget demo_tab, options_tab, hr, tabber, demos, options; Arg av[100]; int ac = 0; mainw = XmCreateMainWindow (parent, "demoForm", av, ac); form = XmCreateForm (mainw, "form", av, ac); menubar = XmCreateSimpleMenuBar (form, "menubar", av, ac); XtVaSetValues (menubar, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); { Widget menu = 0, item = 0; char *menus[] = { "*file", "blank", "lock", "kill", "restart", "-", "exit", "*edit", "cut", "copy", "paste", "*help", "about", "docMenu" }; int i; for (i = 0; i < sizeof(menus)/sizeof(*menus); i++) { ac = 0; if (menus[i][0] == '-') item = XmCreateSeparatorGadget (menu, "separator", av, ac); else if (menus[i][0] != '*') item = XmCreatePushButtonGadget (menu, menus[i], av, ac); else { menu = XmCreatePulldownMenu (parent, menus[i]+1, av, ac); XtSetArg (av [ac], XmNsubMenuId, menu); ac++; item = XmCreateCascadeButtonGadget (menubar, menus[i]+1, av, ac); if (!strcmp (menus[i]+1, "help")) XtVaSetValues(menubar, XmNmenuHelpWidget, item, NULL); } XtManageChild (item); } ac = 0; } demo_tab = XmCreatePushButtonGadget (form, "demoTab", av, ac); XtVaSetValues (demo_tab, XmNleftAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menubar, NULL); options_tab = XmCreatePushButtonGadget (form, "optionsTab", av, ac); XtVaSetValues (options_tab, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, demo_tab, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, demo_tab, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, demo_tab, NULL); hr = XmCreateSeparatorGadget (form, "hr", av, ac); XtVaSetValues (hr, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, demo_tab, NULL); tabber = XmCreateForm (form, "folder", av, ac); XtVaSetValues (tabber, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, hr, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); demos = create_demos_page (tabber); options = create_options_page (tabber); XtAddCallback (demo_tab, XmNactivateCallback, tab_cb, demos); XtAddCallback (options_tab, XmNactivateCallback, tab_cb, options); XtManageChild (demos); XtManageChild (options); XtManageChild (demo_tab); XtManageChild (options_tab); XtManageChild (hr); XtManageChild (menubar); XtManageChild (tabber); XtManageChild (form); #if 1 XtUnmanageChild (options); XtManageChild (demos); #endif return mainw; } static Widget create_demos_page (Widget parent) { /* Form1 (horizontal) Form2 (vertical) Scroller List ButtonBox1 (vertical) Button ("Down") Button ("Up") Form3 (vertical) Frame Label TextArea (doc) Label Text ("Command Line") Form4 (horizontal) Checkbox ("Enabled") Label ("Visual") ComboBox HR ButtonBox2 (vertical) Button ("Demo") Button ("Documentation") */ Widget form1, form2, form3, form4; Widget scroller, list, buttonbox1, down, up; Widget frame, frame_label, doc, cmd_label, cmd_text, enabled, vis_label; Widget combo; Widget hr, buttonbox2, demo, man; Arg av[100]; int ac = 0; int i; form1 = XmCreateForm (parent, "form1", av, ac); form2 = XmCreateForm (form1, "form2", av, ac); XtVaSetValues (form2, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); scroller = XmCreateScrolledWindow (form2, "scroller", av, ac); XtVaSetValues (scroller, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_WIDGET, NULL); list = XmCreateList (scroller, "list", av, ac); buttonbox1 = XmCreateForm (form2, "buttonbox1", av, ac); XtVaSetValues (buttonbox1, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtVaSetValues (scroller, XmNbottomWidget, buttonbox1, NULL); down = XmCreatePushButton (buttonbox1, "down", av, ac); XtVaSetValues (down, XmNleftAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); up = XmCreatePushButton (buttonbox1, "up", av, ac); XtVaSetValues (up, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, down, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); form3 = XmCreateForm (form1, "form3", av, ac); XtVaSetValues (form3, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, form2, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); frame = XmCreateFrame (form3, "frame", av, ac); ac = 0; XtSetArg (av [ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; frame_label = XmCreateLabelGadget (frame, "frameLabel", av, ac); ac = 0; XtVaSetValues (frame, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_WIDGET, NULL); ac = 0; XtSetArg (av [ac], XmNchildType, XmFRAME_WORKAREA_CHILD); ac++; doc = XmCreateText (frame, "doc", av, ac); ac = 0; XtVaSetValues (doc, XmNeditable, FALSE, XmNcursorPositionVisible, FALSE, XmNwordWrap, TRUE, XmNeditMode, XmMULTI_LINE_EDIT, XmNshadowThickness, 0, NULL); cmd_label = XmCreateLabelGadget (form3, "cmdLabel", av, ac); XtVaSetValues (cmd_label, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_WIDGET, NULL); XtVaSetValues (frame, XmNbottomWidget, cmd_label, NULL); cmd_text = XmCreateTextField (form3, "cmdText", av, ac); XtVaSetValues (cmd_text, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_WIDGET, NULL); XtVaSetValues (cmd_label, XmNbottomWidget, cmd_text, NULL); form4 = XmCreateForm (form3, "form4", av, ac); XtVaSetValues (form4, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_WIDGET, NULL); XtVaSetValues (cmd_text, XmNbottomWidget, form4, NULL); enabled = XmCreateToggleButtonGadget (form4, "enabled", av, ac); XtVaSetValues (enabled, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); vis_label = XmCreateLabelGadget (form4, "visLabel", av, ac); XtVaSetValues (vis_label, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, enabled, XmNbottomAttachment, XmATTACH_FORM, NULL); #ifdef HAVE_XMCOMBOBOX { Widget list; ac = 0; XtSetArg (av [ac], XmNcomboBoxType, XmDROP_DOWN_COMBO_BOX); ac++; combo = XmCreateComboBox (form4, "combo", av, ac); for (i = 0; visual_menu[i]; i++) { XmString xs = XmStringCreate ((char *) visual_menu[i], XmSTRING_DEFAULT_CHARSET); XmComboBoxAddItem (combo, xs, 0, False); XmStringFree (xs); } XtVaGetValues (combo, XmNlist, &list, NULL); XtVaSetValues (list, XmNvisibleItemCount, i, NULL); } #else /* !HAVE_XMCOMBOBOX */ { Widget popup_menu = XmCreatePulldownMenu (parent, "menu", av, ac); Widget kids[100]; for (i = 0; visual_menu[i]; i++) { XmString xs = XmStringCreate ((char *) visual_menu[i], XmSTRING_DEFAULT_CHARSET); ac = 0; XtSetArg (av [ac], XmNlabelString, xs); ac++; kids[i] = XmCreatePushButtonGadget (popup_menu, "button", av, ac); /* XtAddCallback (combo, XmNactivateCallback, visual_popup_cb, combo); */ XmStringFree (xs); } XtManageChildren (kids, i); ac = 0; XtSetArg (av [ac], XmNsubMenuId, popup_menu); ac++; combo = XmCreateOptionMenu (form4, "combo", av, ac); ac = 0; } #endif /* !HAVE_XMCOMBOBOX */ XtVaSetValues (combo, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, vis_label, XmNbottomAttachment, XmATTACH_FORM, NULL); hr = XmCreateSeparatorGadget (form3, "hr", av, ac); XtVaSetValues (hr, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_WIDGET, NULL); XtVaSetValues (form4, XmNbottomWidget, hr, NULL); buttonbox2 = XmCreateForm (form3, "buttonbox2", av, ac); XtVaSetValues (buttonbox2, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtVaSetValues (hr, XmNbottomWidget, buttonbox2, NULL); demo = XmCreatePushButtonGadget (buttonbox2, "demo", av, ac); XtVaSetValues (demo, XmNleftAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); man = XmCreatePushButtonGadget (buttonbox2, "man", av, ac); XtVaSetValues (man, XmNrightAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtManageChild (demo); XtManageChild (man); XtManageChild (buttonbox2); XtManageChild (hr); XtManageChild (combo); XtManageChild (vis_label); XtManageChild (enabled); XtManageChild (form4); XtManageChild (cmd_text); XtManageChild (cmd_label); XtManageChild (doc); XtManageChild (frame_label); XtManageChild (frame); XtManageChild (form3); XtManageChild (up); XtManageChild (down); XtManageChild (buttonbox1); XtManageChild (list); XtManageChild (scroller); XtManageChild (form2); XtManageChild (form1); XtVaSetValues (form1, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); return form1; } static Widget create_options_page (Widget parent) { /* This is what the layout is today: Form (horizontal) Label ("Saver Timeout") Label ("Cycle Timeout") Label ("Fade Duration") Label ("Fade Ticks") Label ("Lock Timeout") Label ("Password Timeout") Text (timeout) Text (cycle) Text (fade seconds) Text (fade ticks) Text (lock) Text (passwd) Toggle ("Verbose") Toggle ("Install Colormap") Toggle ("Fade Colormap") Toggle ("Unfade Colormap") Toggle ("Require Password") HR Button ("OK") Button ("Cancel") */ /* This is what it should be: Form (horizontal) Form (vertical) ("column1") Frame Label ("Blanking and Locking") Form Label ("Blank After") Label ("Cycle After") Text ("Blank After") Text ("Cycle After") HR Checkbox ("Require Password") Label ("Lock After") Text ("Lock After") Frame Label ("Image Manipulation") Form Checkbox ("Grab Desktop Images") Checkbox ("Grab Video Frames") Checkbox ("Choose Random Image") Text (pathname) Button ("Browse") Frame Label ("Diagnostics") Form Checkbox ("Verbose Diagnostics") Checkbox ("Display Subprocess Errors") Checkbox ("Display Splash Screen at Startup") Form (vertical) ("column2") Frame Label ("Display Power Management") Form Checkbox ("Power Management Enabled") Label ("Standby After") Label ("Suspend After") Label ("Off After") Text ("Standby After") Text ("Suspend After") Text ("Off After") Frame Label ("Colormaps") Form Checkbox ("Install Colormap") HR Checkbox ("Fade To Black When Blanking") Checkbox ("Fade From Black When Unblanking") Label ("Fade Duration") Text ("Fade Duration") timeoutLabel cycleLabel fadeSecondsLabel fadeTicksLabel lockLabel passwdLabel timeoutText cycleText fadeSecondsText fadeTicksText lockText passwdText verboseToggle cmapToggle fadeToggle unfadeToggle lockToggle separator OK Cancel */ Arg av[64]; int ac = 0; Widget children[100]; Widget timeout_label, cycle_label, fade_seconds_label, fade_ticks_label; Widget lock_label, passwd_label, hr; Widget preferences_form; Widget timeout_text, cycle_text, fade_text, fade_ticks_text; Widget lock_timeout_text, passwd_timeout_text, verbose_toggle; Widget install_cmap_toggle, fade_toggle, unfade_toggle; Widget lock_toggle, prefs_done, prefs_cancel; ac = 0; XtSetArg (av [ac], XmNdialogType, XmDIALOG_PROMPT); ac++; ac = 0; XtSetArg (av [ac], XmNtopAttachment, XmATTACH_FORM); ac++; XtSetArg (av [ac], XmNbottomAttachment, XmATTACH_FORM); ac++; XtSetArg (av [ac], XmNleftAttachment, XmATTACH_FORM); ac++; XtSetArg (av [ac], XmNrightAttachment, XmATTACH_FORM); ac++; preferences_form = XmCreateForm (parent, "preferencesForm", av, ac); XtManageChild (preferences_form); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; timeout_label = XmCreateLabelGadget (preferences_form, "timeoutLabel", av, ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; cycle_label = XmCreateLabelGadget (preferences_form, "cycleLabel", av, ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; fade_seconds_label = XmCreateLabelGadget (preferences_form, "fadeSecondsLabel", av, ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; fade_ticks_label = XmCreateLabelGadget (preferences_form, "fadeTicksLabel", av, ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; lock_label = XmCreateLabelGadget (preferences_form, "lockLabel", av, ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; passwd_label = XmCreateLabelGadget (preferences_form, "passwdLabel", av, ac); ac = 0; timeout_text = XmCreateTextField (preferences_form, "timeoutText", av, ac); cycle_text = XmCreateTextField (preferences_form, "cycleText", av, ac); fade_text = XmCreateTextField (preferences_form, "fadeSecondsText", av, ac); fade_ticks_text = XmCreateTextField (preferences_form, "fadeTicksText", av, ac); lock_timeout_text = XmCreateTextField (preferences_form, "lockText", av, ac); passwd_timeout_text = XmCreateTextField (preferences_form, "passwdText", av, ac); XtSetArg(av[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; verbose_toggle = XmCreateToggleButtonGadget (preferences_form, "verboseToggle", av, ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; install_cmap_toggle = XmCreateToggleButtonGadget (preferences_form, "cmapToggle", av, ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; fade_toggle = XmCreateToggleButtonGadget (preferences_form, "fadeToggle", av, ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; unfade_toggle = XmCreateToggleButtonGadget (preferences_form, "unfadeToggle", av,ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; lock_toggle = XmCreateToggleButtonGadget (preferences_form, "lockToggle", av, ac); ac = 0; hr = XmCreateSeparatorGadget (preferences_form, "separator", av, ac); prefs_done = XmCreatePushButtonGadget (preferences_form, "OK", av, ac); prefs_cancel = XmCreatePushButtonGadget (preferences_form, "Cancel", av, ac); XtVaSetValues (timeout_label, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 4, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, timeout_text, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 20, XmNrightAttachment, XmATTACH_WIDGET, XmNrightOffset, 4, XmNrightWidget, timeout_text, NULL); XtVaSetValues (cycle_label, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, XmNtopWidget, cycle_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, XmNbottomWidget, cycle_text, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 20, XmNrightAttachment, XmATTACH_WIDGET, XmNrightOffset, 4, XmNrightWidget, cycle_text, NULL); XtVaSetValues (fade_seconds_label, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, XmNtopWidget, fade_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, XmNbottomWidget, fade_text, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 20, XmNrightAttachment, XmATTACH_WIDGET, XmNrightOffset, 4, XmNrightWidget, fade_text, NULL); XtVaSetValues (fade_ticks_label, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, XmNtopWidget, fade_ticks_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, XmNbottomWidget, fade_ticks_text, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 20, XmNrightAttachment, XmATTACH_WIDGET, XmNrightOffset, 4, XmNrightWidget, fade_ticks_text, NULL); XtVaSetValues (lock_label, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, XmNtopWidget, lock_timeout_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, XmNbottomWidget, lock_timeout_text, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 19, XmNrightAttachment, XmATTACH_WIDGET, XmNrightOffset, 4, XmNrightWidget, lock_timeout_text, NULL); XtVaSetValues (passwd_label, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, XmNtopWidget, passwd_timeout_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, XmNbottomWidget, passwd_timeout_text, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 14, XmNrightAttachment, XmATTACH_WIDGET, XmNrightOffset, 4, XmNrightWidget, passwd_timeout_text, NULL); XtVaSetValues (timeout_text, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 4, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 141, NULL); XtVaSetValues (cycle_text, XmNtopAttachment, XmATTACH_WIDGET, XmNtopOffset, 2, XmNtopWidget, timeout_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, timeout_text, NULL); XtVaSetValues (fade_text, XmNtopAttachment, XmATTACH_WIDGET, XmNtopOffset, 2, XmNtopWidget, cycle_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, cycle_text, NULL); XtVaSetValues (fade_ticks_text, XmNtopAttachment, XmATTACH_WIDGET, XmNtopOffset, 2, XmNtopWidget, fade_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, fade_text, NULL); XtVaSetValues (lock_timeout_text, XmNtopAttachment, XmATTACH_WIDGET, XmNtopOffset, 2, XmNtopWidget, fade_ticks_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, fade_ticks_text, NULL); XtVaSetValues (passwd_timeout_text, XmNtopAttachment, XmATTACH_WIDGET, XmNtopOffset, 4, XmNtopWidget, lock_timeout_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, lock_timeout_text, NULL); XtVaSetValues (verbose_toggle, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 4, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, XmNbottomWidget, timeout_text, XmNleftAttachment, XmATTACH_WIDGET, XmNleftOffset, 20, XmNleftWidget, timeout_text, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 20, NULL); XtVaSetValues (install_cmap_toggle, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, XmNtopWidget, cycle_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, XmNbottomWidget, cycle_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, verbose_toggle, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 20, NULL); XtVaSetValues (fade_toggle, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, XmNtopWidget, fade_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, XmNbottomWidget, fade_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, install_cmap_toggle, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 20, NULL); XtVaSetValues (unfade_toggle, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, XmNtopWidget, fade_ticks_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, XmNbottomWidget, fade_ticks_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, fade_toggle, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 20, NULL); XtVaSetValues (lock_toggle, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, XmNtopWidget, lock_timeout_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, XmNbottomWidget, lock_timeout_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, unfade_toggle, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 20, NULL); XtVaSetValues (hr, XmNtopWidget, passwd_timeout_text, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 4, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); XtVaSetValues (prefs_done, XmNleftAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtVaSetValues (prefs_cancel, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtVaSetValues (hr, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, prefs_done, NULL); ac = 0; children[ac++] = timeout_label; children[ac++] = cycle_label; children[ac++] = fade_seconds_label; children[ac++] = fade_ticks_label; children[ac++] = lock_label; children[ac++] = passwd_label; children[ac++] = timeout_text; children[ac++] = cycle_text; children[ac++] = fade_text; children[ac++] = fade_ticks_text; children[ac++] = lock_timeout_text; children[ac++] = passwd_timeout_text; children[ac++] = verbose_toggle; children[ac++] = install_cmap_toggle; children[ac++] = fade_toggle; children[ac++] = unfade_toggle; children[ac++] = lock_toggle; children[ac++] = hr; XtManageChildren(children, ac); ac = 0; XtManageChild (prefs_done); XtManageChild (prefs_cancel); XtManageChild (preferences_form); XtVaSetValues (preferences_form, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); return preferences_form; } xscreensaver-5.15/driver/demo-Xm.c000066400000000000000000001426561164314150500171370ustar00rootroot00000000000000/* demo-Xm.c --- implements the interactive demo-mode and options dialogs. * xscreensaver, Copyright (c) 1993-2003, 2005 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_MOTIF /* whole file */ #include #ifdef HAVE_UNISTD_H # include #endif #ifndef VMS # include /* for getpwuid() */ #else /* VMS */ # include "vms-pwd.h" #endif /* VMS */ #ifdef HAVE_UNAME # include /* for uname() */ #endif /* HAVE_UNAME */ #include #include /* for CARD32 */ #include /* for XA_INTEGER */ #include #include /* We don't actually use any widget internals, but these are included so that gdb will have debug info for the widgets... */ #include #include #ifdef HAVE_XPM # include #endif /* HAVE_XPM */ #ifdef HAVE_XMU # ifndef VMS # include # else /* VMS */ # include # endif #else # include "xmu.h" #endif #include #include #include #include #include #include #ifdef HAVE_XMCOMBOBOX /* a Motif 2.0 widget */ # include # ifndef XmNtextField /* Lesstif 0.89.4 bug */ # undef HAVE_XMCOMBOBOX # endif # if (XmVersion < 2001) /* Lesstif has two personalities these days */ # undef HAVE_XMCOMBOBOX # endif #endif /* HAVE_XMCOMBOBOX */ #include "version.h" #include "prefs.h" #include "resources.h" /* for parse_time() */ #include "visual.h" /* for has_writable_cells() */ #include "remote.h" /* for xscreensaver_command() */ #include "usleep.h" #include #include #include #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) char *progname = 0; char *progclass = "XScreenSaver"; XrmDatabase db; typedef struct { saver_preferences *a, *b; } prefs_pair; static void *global_prefs_pair; /* I hate C so much... */ char *blurb (void) { return progname; } extern Widget create_xscreensaver_demo (Widget parent); extern const char *visual_menu[]; static char *short_version = 0; Atom XA_VROOT; Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION; Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO; Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT; static void populate_demo_window (Widget toplevel, int which, prefs_pair *pair); static void populate_prefs_page (Widget top, prefs_pair *pair); static int apply_changes_and_save (Widget widget); static int maybe_reload_init_file (Widget widget, prefs_pair *pair); static void await_xscreensaver (Widget widget); /* Some random utility functions */ static Widget name_to_widget (Widget widget, const char *name) { Widget parent; char name2[255]; name2[0] = '*'; strcpy (name2+1, name); while ((parent = XtParent (widget))) widget = parent; return XtNameToWidget (widget, name2); } /* Why this behavior isn't automatic in *either* toolkit, I'll never know. Takes a scroller, viewport, or list as an argument. */ static void ensure_selected_item_visible (Widget list) { int *pos_list = 0; int pos_count = 0; if (XmListGetSelectedPos (list, &pos_list, &pos_count) && pos_count > 0) { int top = -2; int visible = 0; XtVaGetValues (list, XmNtopItemPosition, &top, XmNvisibleItemCount, &visible, NULL); if (pos_list[0] >= top + visible) { int pos = pos_list[0] - visible + 1; if (pos < 0) pos = 0; XmListSetPos (list, pos); } else if (pos_list[0] < top) { XmListSetPos (list, pos_list[0]); } } if (pos_list) XtFree ((char *) pos_list); } static void warning_dialog_dismiss_cb (Widget button, XtPointer client_data, XtPointer user_data) { Widget shell = (Widget) client_data; XtDestroyWidget (shell); } static void warning_dialog (Widget parent, const char *message, int center) { char *msg = strdup (message); char *head; Widget dialog = 0; Widget label = 0; Widget ok = 0; int i = 0; Widget w; Widget container; XmString xmstr; Arg av[10]; int ac = 0; ac = 0; dialog = XmCreateWarningDialog (parent, "warning", av, ac); w = XmMessageBoxGetChild (dialog, XmDIALOG_MESSAGE_LABEL); if (w) XtUnmanageChild (w); w = XmMessageBoxGetChild (dialog, XmDIALOG_CANCEL_BUTTON); if (w) XtUnmanageChild (w); w = XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON); if (w) XtUnmanageChild (w); ok = XmMessageBoxGetChild (dialog, XmDIALOG_OK_BUTTON); ac = 0; XtSetArg (av[ac], XmNnumColumns, 1); ac++; XtSetArg (av[ac], XmNorientation, XmVERTICAL); ac++; XtSetArg (av[ac], XmNpacking, XmPACK_COLUMN); ac++; XtSetArg (av[ac], XmNrowColumnType, XmWORK_AREA); ac++; XtSetArg (av[ac], XmNspacing, 0); ac++; container = XmCreateRowColumn (dialog, "container", av, ac); head = msg; while (head) { char name[20]; char *s = strchr (head, '\n'); if (s) *s = 0; sprintf (name, "label%d", i++); xmstr = XmStringCreate (head, XmSTRING_DEFAULT_CHARSET); ac = 0; XtSetArg (av[ac], XmNlabelString, xmstr); ac++; XtSetArg (av[ac], XmNmarginHeight, 0); ac++; label = XmCreateLabelGadget (container, name, av, ac); XtManageChild (label); XmStringFree (xmstr); if (s) head = s+1; else head = 0; center--; } XtManageChild (container); XtRealizeWidget (dialog); XtManageChild (dialog); XtAddCallback (ok, XmNactivateCallback, warning_dialog_dismiss_cb, dialog); free (msg); } static void run_cmd (Widget widget, Atom command, int arg) { char *err = 0; int status; apply_changes_and_save (widget); status = xscreensaver_command (XtDisplay (widget), command, arg, False, &err); if (status < 0) { char buf [255]; if (err) sprintf (buf, "Error:\n\n%s", err); else strcpy (buf, "Unknown error!"); warning_dialog (widget, buf, 100); } if (err) free (err); } static void run_hack (Widget widget, int which, Bool report_errors_p) { if (which < 0) return; apply_changes_and_save (widget); if (report_errors_p) run_cmd (widget, XA_DEMO, which + 1); else { char *s = 0; xscreensaver_command (XtDisplay (widget), XA_DEMO, which + 1, False, &s); if (s) free (s); } } /* Button callbacks */ void exit_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { apply_changes_and_save (XtParent (button)); exit (0); } #if 0 static void wm_close_cb (Widget widget, GdkEvent *event, XtPointer data) { apply_changes_and_save (XtParent (button)); exit (0); } #endif void cut_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { /* #### */ warning_dialog (XtParent (button), "Error:\n\n" "cut unimplemented\n", 1); } void copy_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { /* #### */ warning_dialog (XtParent (button), "Error:\n\n" "copy unimplemented\n", 1); } void paste_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { /* #### */ warning_dialog (XtParent (button), "Error:\n\n" "paste unimplemented\n", 1); } void about_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { char buf [2048]; char *s = strdup (screensaver_id + 4); char *s2; s2 = strchr (s, ','); *s2 = 0; s2 += 2; sprintf (buf, "%s\n%s\n" "\n" "This is the Motif version of \"xscreensaver-demo\". The Motif\n" "version is no longer maintained. Please use the GTK version\n" "instead, which has many more features.\n" "\n" "For xscreensaver updates, check http://www.jwz.org/xscreensaver/", s, s2); free (s); warning_dialog (XtParent (button), buf, 100); } void doc_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; saver_preferences *p = pair->a; char *help_command; if (!p->help_url || !*p->help_url) { warning_dialog (XtParent (button), "Error:\n\n" "No Help URL has been specified.\n", 100); return; } help_command = (char *) malloc (strlen (p->load_url_command) + (strlen (p->help_url) * 4) + 20); strcpy (help_command, "( "); sprintf (help_command + strlen(help_command), p->load_url_command, p->help_url, p->help_url, p->help_url, p->help_url); strcat (help_command, " ) &"); system (help_command); free (help_command); } void activate_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { run_cmd (XtParent (button), XA_ACTIVATE, 0); } void lock_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { run_cmd (XtParent (button), XA_LOCK, 0); } void kill_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { run_cmd (XtParent (button), XA_EXIT, 0); } void restart_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { #if 0 run_cmd (XtParent (button), XA_RESTART, 0); #else button = XtParent (button); apply_changes_and_save (button); xscreensaver_command (XtDisplay (button), XA_EXIT, 0, False, NULL); sleep (1); system ("xscreensaver -nosplash &"); #endif await_xscreensaver (button); } static void await_xscreensaver (Widget widget) { int countdown = 5; Display *dpy = XtDisplay (widget); char *rversion = 0; while (!rversion && (--countdown > 0)) { /* Check for the version of the running xscreensaver... */ server_xscreensaver_version (dpy, &rversion, 0, 0); /* If it's not there yet, wait a second... */ sleep (1); } if (rversion) { /* Got it. */ free (rversion); } else { /* Timed out, no screensaver running. */ char buf [1024]; Bool root_p = (geteuid () == 0); strcpy (buf, "Error:\n\n" "The xscreensaver daemon did not start up properly.\n" "\n"); if (root_p) # ifdef __GNUC__ __extension__ /* don't warn about "string length is greater than the length ISO C89 compilers are required to support" in the following expression... */ # endif strcat (buf, "You are running as root. This usually means that xscreensaver\n" "was unable to contact your X server because access control is\n" "turned on. Try running this command:\n" "\n" " xhost +localhost\n" "\n" "and then selecting `File / Restart Daemon'.\n" "\n" "Note that turning off access control will allow anyone logged\n" "on to this machine to access your screen, which might be\n" "considered a security problem. Please read the xscreensaver\n" "manual and FAQ for more information.\n" "\n" "You shouldn't run X as root. Instead, you should log in as a\n" "normal user, and `su' as necessary."); else strcat (buf, "Please check your $PATH and permissions."); warning_dialog (XtParent (widget), buf, 1); } } static int _selected_hack_number = -1; static int selected_hack_number (Widget toplevel) { return _selected_hack_number; } static int demo_write_init_file (Widget widget, saver_preferences *p) { if (!write_init_file (XtDisplay (widget), p, short_version, False)) return 0; else { const char *f = init_file_name(); if (!f || !*f) warning_dialog (widget, "Error:\n\nCouldn't determine init file name!\n", 100); else { char *b = (char *) malloc (strlen(f) + 1024); sprintf (b, "Error:\n\nCouldn't write %s\n", f); warning_dialog (widget, b, 100); free (b); } return -1; } } static int apply_changes_and_save (Widget widget) { prefs_pair *pair = global_prefs_pair; saver_preferences *p = pair->a; Widget list_widget = name_to_widget (widget, "list"); int which = selected_hack_number (widget); Widget cmd = name_to_widget (widget, "cmdText"); Widget enabled = name_to_widget (widget, "enabled"); Widget vis = name_to_widget (widget, "combo"); # ifdef HAVE_XMCOMBOBOX Widget text = 0; # else /* !HAVE_XMCOMBOBOX */ Widget menu = 0, *kids = 0, selected_item = 0; Cardinal nkids = 0; int i = 0; # endif /* !HAVE_XMCOMBOBOX */ Bool enabled_p = False; const char *visual = 0; const char *command = 0; char c; unsigned long id; if (which < 0) return -1; # ifdef HAVE_XMCOMBOBOX XtVaGetValues (vis, XmNtextField, &text, NULL); if (!text) /* If we can't get at the text field of this combo box, we're screwed. */ abort(); XtVaGetValues (text, XmNvalue, &visual, NULL); # else /* !HAVE_XMCOMBOBOX */ XtVaGetValues (vis, XmNsubMenuId, &menu, NULL); XtVaGetValues (menu, XmNnumChildren, &nkids, XmNchildren, &kids, NULL); XtVaGetValues (menu, XmNmenuHistory, &selected_item, NULL); if (selected_item) for (i = 0; i < nkids; i++) if (kids[i] == selected_item) break; visual = visual_menu[i]; # endif /* !HAVE_XMCOMBOBOX */ XtVaGetValues (enabled, XmNset, &enabled_p, NULL); XtVaGetValues (cmd, XtNvalue, &command, NULL); if (maybe_reload_init_file (widget, pair) != 0) return 1; /* Sanity-check and canonicalize whatever the user typed into the combo box. */ if (!strcasecmp (visual, "")) visual = ""; else if (!strcasecmp (visual, "any")) visual = ""; else if (!strcasecmp (visual, "default")) visual = "Default"; else if (!strcasecmp (visual, "default-n")) visual = "Default-N"; else if (!strcasecmp (visual, "default-i")) visual = "Default-I"; else if (!strcasecmp (visual, "best")) visual = "Best"; else if (!strcasecmp (visual, "mono")) visual = "Mono"; else if (!strcasecmp (visual, "monochrome")) visual = "Mono"; else if (!strcasecmp (visual, "gray")) visual = "Gray"; else if (!strcasecmp (visual, "grey")) visual = "Gray"; else if (!strcasecmp (visual, "color")) visual = "Color"; else if (!strcasecmp (visual, "gl")) visual = "GL"; else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray"; else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor"; else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor"; else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale"; else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale"; else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor"; else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor"; else if (1 == sscanf (visual, " %lu %c", &id, &c)) ; else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ; else { XBell (XtDisplay (widget), 0); /* unparsable */ visual = ""; /* #### gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");*/ } ensure_selected_item_visible (list_widget); if (!p->screenhacks[which]->visual) p->screenhacks[which]->visual = strdup (""); if (!p->screenhacks[which]->command) p->screenhacks[which]->command = strdup (""); if (p->screenhacks[which]->enabled_p != enabled_p || !!strcasecmp (p->screenhacks[which]->visual, visual) || !!strcasecmp (p->screenhacks[which]->command, command)) { /* Something was changed -- store results into the struct, and write the file. */ free (p->screenhacks[which]->visual); free (p->screenhacks[which]->command); p->screenhacks[which]->visual = strdup (visual); p->screenhacks[which]->command = strdup (command); p->screenhacks[which]->enabled_p = enabled_p; return demo_write_init_file (widget, p); } /* No changes made */ return 0; } void run_this_cb (Widget button, XtPointer client_data, XtPointer ignored) { int which = selected_hack_number (XtParent (button)); if (which < 0) return; if (0 == apply_changes_and_save (XtParent (button))) run_hack (XtParent (button), which, True); } void manual_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; saver_preferences *p = pair->a; Widget list_widget = name_to_widget (button, "list"); int which = selected_hack_number (button); char *name, *name2, *cmd, *s; if (which < 0) return; apply_changes_and_save (button); ensure_selected_item_visible (list_widget); name = strdup (p->screenhacks[which]->command); name2 = name; while (isspace (*name2)) name2++; s = name2; while (*s && !isspace (*s)) s++; *s = 0; s = strrchr (name2, '/'); if (s) name = s+1; cmd = get_string_resource (XtDisplay (button), "manualCommand", "ManualCommand"); if (cmd) { char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100); strcpy (cmd2, "( "); sprintf (cmd2 + strlen (cmd2), cmd, name2, name2, name2, name2); strcat (cmd2, " ) &"); system (cmd2); free (cmd2); } else { warning_dialog (XtParent (button), "Error:\n\nno `manualCommand' resource set.", 100); } free (name); } void run_next_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; saver_preferences *p = pair->a; Widget list_widget = name_to_widget (button, "list"); int which = selected_hack_number (button); button = XtParent (button); if (which < 0) which = 0; else which++; if (which >= p->screenhacks_count) which = 0; apply_changes_and_save (button); XmListDeselectAllItems (list_widget); /* LessTif lossage */ XmListSelectPos (list_widget, which+1, True); ensure_selected_item_visible (list_widget); populate_demo_window (button, which, pair); run_hack (button, which, False); } void run_prev_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; saver_preferences *p = pair->a; Widget list_widget = name_to_widget (button, "list"); int which = selected_hack_number (button); button = XtParent (button); if (which < 0) which = p->screenhacks_count - 1; else which--; if (which < 0) which = p->screenhacks_count - 1; apply_changes_and_save (button); XmListDeselectAllItems (list_widget); /* LessTif lossage */ XmListSelectPos (list_widget, which+1, True); ensure_selected_item_visible (list_widget); populate_demo_window (button, which, pair); run_hack (button, which, False); } /* Helper for the text fields that contain time specifications: this parses the text, and does error checking. */ static void hack_time_text (Widget button, const char *line, Time *store, Bool sec_p) { if (*line) { int value; value = parse_time ((char *) line, sec_p, True); value *= 1000; /* Time measures in microseconds */ if (value < 0) { char b[255]; sprintf (b, "Error:\n\n" "Unparsable time format: \"%s\"\n", line); warning_dialog (XtParent (button), b, 100); } else *store = value; } } void prefs_ok_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; saver_preferences *p = pair->a; saver_preferences *p2 = pair->b; Bool changed = False; char *v = 0; button = XtParent (button); # define SECONDS(field, name) \ v = 0; \ XtVaGetValues (name_to_widget (button, (name)), XtNvalue, &v, NULL); \ hack_time_text (button, v, (field), True) # define MINUTES(field, name) \ v = 0; \ XtVaGetValues (name_to_widget (button, (name)), XtNvalue, &v, NULL); \ hack_time_text (button, v, (field), False) # define INTEGER(field, name) do { \ unsigned int value; \ char c; \ XtVaGetValues (name_to_widget (button, (name)), XtNvalue, &v, NULL); \ if (! *v) \ ; \ else if (sscanf (v, "%u%c", &value, &c) != 1) \ { \ char b[255]; \ sprintf (b, "Error:\n\n" "Not an integer: \"%s\"\n", v); \ warning_dialog (XtParent (button), b, 100); \ } \ else \ *(field) = value; \ } while(0) # define CHECKBOX(field, name) \ XtVaGetValues (name_to_widget (button, (name)), XmNset, &field, NULL) MINUTES (&p2->timeout, "timeoutText"); MINUTES (&p2->cycle, "cycleText"); SECONDS (&p2->fade_seconds, "fadeSecondsText"); INTEGER (&p2->fade_ticks, "fadeTicksText"); MINUTES (&p2->lock_timeout, "lockText"); SECONDS (&p2->passwd_timeout, "passwdText"); CHECKBOX (p2->verbose_p, "verboseToggle"); CHECKBOX (p2->install_cmap_p, "cmapToggle"); CHECKBOX (p2->fade_p, "fadeToggle"); CHECKBOX (p2->unfade_p, "unfadeToggle"); CHECKBOX (p2->lock_p, "lockToggle"); # undef SECONDS # undef MINUTES # undef INTEGER # undef CHECKBOX # define COPY(field) \ if (p->field != p2->field) changed = True; \ p->field = p2->field COPY(timeout); COPY(cycle); COPY(lock_timeout); COPY(passwd_timeout); COPY(fade_seconds); COPY(fade_ticks); COPY(verbose_p); COPY(install_cmap_p); COPY(fade_p); COPY(unfade_p); COPY(lock_p); # undef COPY populate_prefs_page (button, pair); if (changed) demo_write_init_file (button, p); } void prefs_cancel_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; *pair->b = *pair->a; populate_prefs_page (XtParent (button), pair); } static void list_select_cb (Widget list, XtPointer client_data, XtPointer call_data) { prefs_pair *pair = (prefs_pair *) client_data; XmListCallbackStruct *lcb = (XmListCallbackStruct *) call_data; int which = lcb->item_position - 1; apply_changes_and_save (list); populate_demo_window (list, which, pair); if (lcb->reason == XmCR_DEFAULT_ACTION && which >= 0) run_hack (list, which, True); } /* Populating the various widgets */ /* Formats a `Time' into "H:MM:SS". (Time is microseconds.) */ static void format_time (char *buf, Time time) { int s = time / 1000; unsigned int h = 0, m = 0; if (s >= 60) { m += (s / 60); s %= 60; } if (m >= 60) { h += (m / 60); m %= 60; } sprintf (buf, "%u:%02u:%02u", h, m, s); } /* Finds the number of the last hack to run, and makes that item be selected by default. */ static void scroll_to_current_hack (Widget toplevel, prefs_pair *pair) { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *data = 0; Display *dpy = XtDisplay (toplevel); int which = 0; Widget list; if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */ XA_SCREENSAVER_STATUS, 0, 3, False, XA_INTEGER, &type, &format, &nitems, &bytesafter, &data) == Success && type == XA_INTEGER && nitems >= 3 && data) which = (int) data[2] - 1; if (data) free (data); if (which < 0) return; list = name_to_widget (toplevel, "list"); apply_changes_and_save (toplevel); XmListDeselectAllItems (list); /* LessTif lossage */ XmListSelectPos (list, which+1, True); ensure_selected_item_visible (list); populate_demo_window (toplevel, which, pair); } static void populate_hack_list (Widget toplevel, prefs_pair *pair) { saver_preferences *p = pair->a; Widget list = name_to_widget (toplevel, "list"); screenhack **hacks = p->screenhacks; screenhack **h; for (h = hacks; *h; h++) { char *pretty_name = (h[0]->name ? strdup (h[0]->name) : make_hack_name (XtDisplay (toplevel), h[0]->command)); XmString xmstr = XmStringCreate (pretty_name, XmSTRING_DEFAULT_CHARSET); XmListAddItem (list, xmstr, 0); XmStringFree (xmstr); } XtAddCallback (list, XmNbrowseSelectionCallback, list_select_cb, pair); XtAddCallback (list, XmNdefaultActionCallback, list_select_cb, pair); } static void populate_prefs_page (Widget top, prefs_pair *pair) { saver_preferences *p = pair->a; char s[100]; format_time (s, p->timeout); XtVaSetValues (name_to_widget (top, "timeoutText"), XmNvalue, s, NULL); format_time (s, p->cycle); XtVaSetValues (name_to_widget (top, "cycleText"), XmNvalue, s, NULL); format_time (s, p->lock_timeout); XtVaSetValues (name_to_widget (top, "lockText"), XmNvalue, s, NULL); format_time (s, p->passwd_timeout); XtVaSetValues (name_to_widget (top, "passwdText"), XmNvalue, s, NULL); format_time (s, p->fade_seconds); XtVaSetValues (name_to_widget (top, "fadeSecondsText"), XmNvalue, s, NULL); sprintf (s, "%u", p->fade_ticks); XtVaSetValues (name_to_widget (top, "fadeTicksText"), XmNvalue, s, NULL); XtVaSetValues (name_to_widget (top, "verboseToggle"), XmNset, p->verbose_p, NULL); XtVaSetValues (name_to_widget (top, "cmapToggle"), XmNset, p->install_cmap_p, NULL); XtVaSetValues (name_to_widget (top, "fadeToggle"), XmNset, p->fade_p, NULL); XtVaSetValues (name_to_widget (top, "unfadeToggle"), XmNset, p->unfade_p, NULL); XtVaSetValues (name_to_widget (top, "lockToggle"), XmNset, p->lock_p, NULL); { Bool found_any_writable_cells = False; Display *dpy = XtDisplay (top); int nscreens = ScreenCount(dpy); int i; for (i = 0; i < nscreens; i++) { Screen *s = ScreenOfDisplay (dpy, i); if (has_writable_cells (s, DefaultVisualOfScreen (s))) { found_any_writable_cells = True; break; } } #ifdef HAVE_XF86VMODE_GAMMA found_any_writable_cells = True; /* if we can gamma fade, go for it */ #endif XtVaSetValues (name_to_widget (top, "fadeSecondsLabel"), XtNsensitive, found_any_writable_cells, NULL); XtVaSetValues (name_to_widget (top, "fadeTicksLabel"), XtNsensitive, found_any_writable_cells, NULL); XtVaSetValues (name_to_widget (top, "fadeSecondsText"), XtNsensitive, found_any_writable_cells, NULL); XtVaSetValues (name_to_widget (top, "fadeTicksText"), XtNsensitive, found_any_writable_cells, NULL); XtVaSetValues (name_to_widget (top, "cmapToggle"), XtNsensitive, found_any_writable_cells, NULL); XtVaSetValues (name_to_widget (top, "fadeToggle"), XtNsensitive, found_any_writable_cells, NULL); XtVaSetValues (name_to_widget (top, "unfadeToggle"), XtNsensitive, found_any_writable_cells, NULL); } } static void sensitize_demo_widgets (Widget toplevel, Bool sensitive_p) { const char *names[] = { "cmdLabel", "cmdText", "enabled", "visLabel", "combo", "demo", "man" }; int i; for (i = 0; i < sizeof(names)/countof(*names); i++) { Widget w = name_to_widget (toplevel, names[i]); XtVaSetValues (w, XtNsensitive, sensitive_p, NULL); } /* I don't know how to handle these yet... */ { const char *names2[] = { "cut", "copy", "paste" }; for (i = 0; i < sizeof(names2)/countof(*names2); i++) { Widget w = name_to_widget (toplevel, names2[i]); XtVaSetValues (w, XtNsensitive, FALSE, NULL); } } } /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...) */ #ifdef HAVE_XPM static char *up_arrow_xpm[] = { "15 15 4 1", " c None s background", "- c #FFFFFF", "+ c #D6D6D6", "@ c #000000", " @ ", " @ ", " -+@ ", " -+@ ", " -+++@ ", " -+++@ ", " -+++++@ ", " -+++++@ ", " -+++++++@ ", " -+++++++@ ", " -+++++++++@ ", " -+++++++++@ ", " -+++++++++++@ ", " @@@@@@@@@@@@@ ", " " }; static char *down_arrow_xpm[] = { "15 15 4 1", " c None s background", "- c #FFFFFF", "+ c #D6D6D6", "@ c #000000", " ", " ------------- ", " -+++++++++++@ ", " -+++++++++@ ", " -+++++++++@ ", " -+++++++@ ", " -+++++++@ ", " -+++++@ ", " -+++++@ ", " -+++@ ", " -+++@ ", " -+@ ", " -+@ ", " @ ", " @ " }; #endif /* HAVE_XPM */ static void pixmapify_buttons (Widget toplevel) { #ifdef HAVE_XPM Display *dpy = XtDisplay (toplevel); Window window = XtWindow (toplevel); XWindowAttributes xgwa; XpmAttributes xpmattrs; Pixmap up_pixmap = 0, down_pixmap = 0; int result; Widget up = name_to_widget (toplevel, "up"); Widget dn = name_to_widget (toplevel, "down"); # ifdef XpmColorSymbols XColor xc; XpmColorSymbol symbols[2]; char color[20]; # endif XGetWindowAttributes (dpy, window, &xgwa); xpmattrs.valuemask = 0; # ifdef XpmColorSymbols symbols[0].name = "background"; symbols[0].pixel = 0; symbols[1].name = 0; XtVaGetValues (up, XmNbackground, &xc, NULL); XQueryColor (dpy, xgwa.colormap, &xc); sprintf (color, "#%04X%04X%04X", xc.red, xc.green, xc.blue); symbols[0].value = color; symbols[0].pixel = xc.pixel; xpmattrs.valuemask |= XpmColorSymbols; xpmattrs.colorsymbols = symbols; xpmattrs.numsymbols = 1; # endif # ifdef XpmCloseness xpmattrs.valuemask |= XpmCloseness; xpmattrs.closeness = 40000; # endif # ifdef XpmVisual xpmattrs.valuemask |= XpmVisual; xpmattrs.visual = xgwa.visual; # endif # ifdef XpmDepth xpmattrs.valuemask |= XpmDepth; xpmattrs.depth = xgwa.depth; # endif # ifdef XpmColormap xpmattrs.valuemask |= XpmColormap; xpmattrs.colormap = xgwa.colormap; # endif result = XpmCreatePixmapFromData(dpy, window, up_arrow_xpm, &up_pixmap, 0 /* mask */, &xpmattrs); if (!up_pixmap || (result != XpmSuccess && result != XpmColorError)) { fprintf (stderr, "%s: Can't load pixmaps\n", progname); return; } result = XpmCreatePixmapFromData(dpy, window, down_arrow_xpm, &down_pixmap, 0 /* mask */, &xpmattrs); if (!down_pixmap || (result != XpmSuccess && result != XpmColorError)) { fprintf (stderr, "%s: Can't load pixmaps\n", progname); return; } XtVaSetValues (up, XmNlabelType, XmPIXMAP, XmNlabelPixmap, up_pixmap, NULL); XtVaSetValues (dn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, down_pixmap,NULL); #endif /* HAVE_XPM */ } char * get_hack_blurb (Display *dpy, screenhack *hack) { char *doc_string; char *prog_name = strdup (hack->command); char *pretty_name = (hack->name ? strdup (hack->name) : make_hack_name (dpy, hack->command)); char doc_name[255], doc_class[255]; char *s, *s2; for (s = prog_name; *s && !isspace(*s); s++) ; *s = 0; s = strrchr (prog_name, '/'); if (s) strcpy (prog_name, s+1); sprintf (doc_name, "hacks.%s.documentation", pretty_name); sprintf (doc_class, "hacks.%s.documentation", prog_name); free (prog_name); free (pretty_name); doc_string = get_string_resource (dpy, doc_name, doc_class); if (doc_string) { for (s = doc_string; *s; s++) { if (*s == '\n') { /* skip over whitespace at beginning of line */ s++; while (*s && (*s == ' ' || *s == '\t')) s++; } else if (*s == ' ' || *s == '\t') { /* compress all other horizontal whitespace. */ *s = ' '; s++; for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++) ; if (s2 > s) strcpy (s, s2); s--; } } while (*s && isspace (*s)) /* Strip trailing whitespace */ *(--s) = 0; /* Delete whitespace at end of each line. */ for (; s > doc_string; s--) if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t')) { for (s2 = s-1; s2 > doc_string && (*s2 == ' ' || *s2 == '\t'); s2--) ; s2++; if (s2 < s) strcpy (s2, s); s = s2; } /* Delete leading blank lines. */ for (s = doc_string; *s == '\n'; s++) ; if (s > doc_string) strcpy (doc_string, s); } else { # if 0 static int doc_installed = 0; if (doc_installed == 0) { if (get_boolean_resource ("hacks.documentation.isInstalled", "hacks.documentation.isInstalled")) doc_installed = 1; else doc_installed = -1; } if (doc_installed < 0) doc_string = strdup ("Error:\n\n" "The documentation strings do not appear to be " "installed. This is probably because there is " "an \"XScreenSaver\" app-defaults file installed " "that is from an older version of the program. " "To fix this problem, delete that file, or " "install a current version (either will work.)"); else # endif /* 0 */ doc_string = strdup ( "\n" "This is the Motif version of \"xscreensaver-demo\". The Motif " "version is no longer maintained. Please use the GTK version " "instead, which has many more features." "\n\n" "If you were running the GTK version, there would be a preview " "of this screen saver mode displayed here, along with graphical " "configuration options."); } return doc_string; } static void populate_demo_window (Widget toplevel, int which, prefs_pair *pair) { saver_preferences *p = pair->a; screenhack *hack = (which >= 0 ? p->screenhacks[which] : 0); Widget frameL = name_to_widget (toplevel, "frameLabel"); Widget doc = name_to_widget (toplevel, "doc"); Widget cmd = name_to_widget (toplevel, "cmdText"); Widget enabled = name_to_widget (toplevel, "enabled"); Widget vis = name_to_widget (toplevel, "combo"); int i = 0; char *pretty_name = (hack ? (hack->name ? strdup (hack->name) : make_hack_name (XtDisplay (toplevel), hack->command)) : 0); char *doc_string = hack ? get_hack_blurb (XtDisplay (toplevel), hack) : 0; XmString xmstr; xmstr = XmStringCreate (pretty_name, XmSTRING_DEFAULT_CHARSET); XtVaSetValues (frameL, XmNlabelString, xmstr, NULL); XmStringFree (xmstr); XtVaSetValues (doc, XmNvalue, doc_string, NULL); XtVaSetValues (cmd, XmNvalue, (hack ? hack->command : ""), NULL); XtVaSetValues (enabled, XmNset, (hack ? hack->enabled_p : False), NULL); i = 0; if (hack && hack->visual && *hack->visual) for (i = 0; visual_menu[i]; i++) if (!strcasecmp (hack->visual, visual_menu[i])) break; if (!visual_menu[i]) i = -1; { # ifdef HAVE_XMCOMBOBOX Widget text = 0; XtVaGetValues (vis, XmNtextField, &text, NULL); XtVaSetValues (vis, XmNselectedPosition, i, NULL); if (i < 0) XtVaSetValues (text, XmNvalue, hack->visual, NULL); # else /* !HAVE_XMCOMBOBOX */ Cardinal nkids; Widget *kids; Widget menu; XtVaGetValues (vis, XmNsubMenuId, &menu, NULL); if (!menu) abort (); XtVaGetValues (menu, XmNnumChildren, &nkids, XmNchildren, &kids, NULL); if (!kids) abort(); if (i < nkids) XtVaSetValues (vis, XmNmenuHistory, kids[i], NULL); # endif /* !HAVE_XMCOMBOBOX */ } sensitize_demo_widgets (toplevel, (hack ? True : False)); if (pretty_name) free (pretty_name); if (doc_string) free (doc_string); _selected_hack_number = which; } static int maybe_reload_init_file (Widget widget, prefs_pair *pair) { int status = 0; saver_preferences *p = pair->a; static Bool reentrant_lock = False; if (reentrant_lock) return 0; reentrant_lock = True; if (init_file_changed_p (p)) { const char *f = init_file_name(); char *b; int which; Widget list; if (!f || !*f) return 0; b = (char *) malloc (strlen(f) + 1024); sprintf (b, "Warning:\n\n" "file \"%s\" has changed, reloading.\n", f); warning_dialog (widget, b, 100); free (b); load_init_file (XtDisplay (widget), p); which = selected_hack_number (widget); list = name_to_widget (widget, "list"); XtVaSetValues (list, XmNitemCount, 0, NULL); populate_hack_list (widget, pair); XmListDeselectAllItems (list); /* LessTif lossage */ XmListSelectPos (list, which+1, True); populate_prefs_page (widget, pair); populate_demo_window (widget, which, pair); ensure_selected_item_visible (list); status = 1; } reentrant_lock = False; return status; } /* Attach all callback functions to widgets */ static void add_callbacks (Widget toplevel, prefs_pair *pair) { Widget w; # define CB(NAME,FN) \ w = name_to_widget (toplevel, (NAME)); \ XtAddCallback (w, XmNactivateCallback, (FN), pair) CB ("blank", activate_menu_cb); CB ("lock", lock_menu_cb); CB ("kill", kill_menu_cb); CB ("restart", restart_menu_cb); CB ("exit", exit_menu_cb); CB ("cut", cut_menu_cb); CB ("copy", copy_menu_cb); CB ("paste", paste_menu_cb); CB ("about", about_menu_cb); CB ("docMenu", doc_menu_cb); CB ("down", run_next_cb); CB ("up", run_prev_cb); CB ("demo", run_this_cb); CB ("man", manual_cb); CB ("preferencesForm.Cancel", prefs_cancel_cb); CB ("preferencesForm.OK", prefs_ok_cb); # undef CB } static void sanity_check_resources (Widget toplevel) { const char *names[] = { "demoTab", "optionsTab", "cmdLabel", "visLabel", "enabled", "demo", "man", "timeoutLabel", "cycleLabel", "fadeSecondsLabel", "fadeTicksLabel", "lockLabel", "passwdLabel" }; int i; for (i = 0; i < sizeof(names)/countof(*names); i++) { Widget w = name_to_widget (toplevel, names[i]); const char *name = XtName(w); XmString xm = 0; char *label = 0; XtVaGetValues (w, XmNlabelString, &xm, NULL); if (xm) XmStringGetLtoR (xm, XmSTRING_DEFAULT_CHARSET, &label); if (w && (!label || !strcmp (name, label))) { xm = XmStringCreate ("ERROR", XmSTRING_DEFAULT_CHARSET); XtVaSetValues (w, XmNlabelString, xm, NULL); } } } /* Set certain buttons to be the same size (the max of the set.) */ static void hack_button_sizes (Widget toplevel) { Widget demo = name_to_widget (toplevel, "demo"); Widget man = name_to_widget (toplevel, "man"); Widget ok = name_to_widget (toplevel, "OK"); Widget can = name_to_widget (toplevel, "Cancel"); Widget up = name_to_widget (toplevel, "up"); Widget down = name_to_widget (toplevel, "down"); Dimension w1, w2; XtVaGetValues (demo, XmNwidth, &w1, NULL); XtVaGetValues (man, XmNwidth, &w2, NULL); XtVaSetValues ((w1 > w2 ? man : demo), XmNwidth, (w1 > w2 ? w1 : w2), NULL); XtVaGetValues (ok, XmNwidth, &w1, NULL); XtVaGetValues (can, XmNwidth, &w2, NULL); XtVaSetValues ((w1 > w2 ? can : ok), XmNwidth, (w1 > w2 ? w1 : w2), NULL); XtVaGetValues (up, XmNwidth, &w1, NULL); XtVaGetValues (down, XmNwidth, &w2, NULL); XtVaSetValues ((w1 > w2 ? down : up), XmNwidth, (w1 > w2 ? w1 : w2), NULL); } /* The main demo-mode command loop. */ #if 0 static Bool mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks, XrmRepresentation *type, XrmValue *value, XPointer closure) { int i; for (i = 0; quarks[i]; i++) { if (bindings[i] == XrmBindTightly) fprintf (stderr, (i == 0 ? "" : ".")); else if (bindings[i] == XrmBindLoosely) fprintf (stderr, "*"); else fprintf (stderr, " ??? "); fprintf(stderr, "%s", XrmQuarkToString (quarks[i])); } fprintf (stderr, ": %s\n", (char *) value->addr); return False; } #endif static void the_network_is_not_the_computer (Widget parent) { Display *dpy = XtDisplay (parent); char *rversion, *ruser, *rhost; char *luser, *lhost; char *msg = 0; struct passwd *p = getpwuid (getuid ()); const char *d = DisplayString (dpy); # if defined(HAVE_UNAME) struct utsname uts; if (uname (&uts) < 0) lhost = ""; else lhost = uts.nodename; # elif defined(VMS) strcpy (lhost, getenv("SYS$NODE")); # else /* !HAVE_UNAME && !VMS */ strcat (lhost, ""); # endif /* !HAVE_UNAME && !VMS */ if (p && p->pw_name) luser = p->pw_name; else luser = "???"; server_xscreensaver_version (dpy, &rversion, &ruser, &rhost); /* Make a buffer that's big enough for a number of copies of all the strings, plus some. */ msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) + (ruser ? strlen(ruser) : 0) + (rhost ? strlen(rhost) : 0) + strlen(lhost) + strlen(luser) + strlen(d) + 1024)); *msg = 0; if (!rversion || !*rversion) { sprintf (msg, "Warning:\n\n" "The XScreenSaver daemon doesn't seem to be running\n" "on display \"%s\". You can launch it by selecting\n" "`Restart Daemon' from the File menu, or by typing\n" "\"xscreensaver &\" in a shell.", d); } else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name)) { /* Warn that the two processes are running as different users. */ sprintf(msg, "Warning:\n\n" "%s is running as user \"%s\" on host \"%s\".\n" "But the xscreensaver managing display \"%s\"\n" "is running as user \"%s\" on host \"%s\".\n" "\n" "Since they are different users, they won't be reading/writing\n" "the same ~/.xscreensaver file, so %s isn't\n" "going to work right.\n" "\n" "Either re-run %s as \"%s\", or re-run\n" "xscreensaver as \"%s\" (which you can do by\n" "selecting `Restart Daemon' from the File menu.)\n", progname, luser, lhost, d, (ruser ? ruser : "???"), (rhost ? rhost : "???"), progname, progname, (ruser ? ruser : "???"), luser); } else if (rhost && *rhost && !!strcmp (rhost, lhost)) { /* Warn that the two processes are running on different hosts. */ sprintf (msg, "Warning:\n\n" "%s is running as user \"%s\" on host \"%s\".\n" "But the xscreensaver managing display \"%s\"\n" "is running as user \"%s\" on host \"%s\".\n" "\n" "If those two machines don't share a file system (that is,\n" "if they don't see the same ~%s/.xscreensaver file) then\n" "%s won't work right.\n" "\n" "You can restart the daemon on \"%s\" as \"%s\" by\n" "selecting `Restart Daemon' from the File menu.)", progname, luser, lhost, d, (ruser ? ruser : "???"), (rhost ? rhost : "???"), luser, progname, lhost, luser); } else if (!!strcmp (rversion, short_version)) { /* Warn that the version numbers don't match. */ sprintf (msg, "Warning:\n\n" "This is %s version %s.\n" "But the xscreensaver managing display \"%s\"\n" "is version %s. This could cause problems.", progname, short_version, d, rversion); } if (*msg) warning_dialog (parent, msg, 1); free (msg); } /* We use this error handler so that X errors are preceeded by the name of the program that generated them. */ static int demo_ehandler (Display *dpy, XErrorEvent *error) { fprintf (stderr, "\nX error in %s:\n", progname); XmuPrintDefaultErrorMessage (dpy, error, stderr); exit (-1); return 0; } #ifdef __GNUC__ __extension__ /* shut up about "string length is greater than the length ISO C89 compilers are required to support" when including the .ad file... */ #endif static char *defaults[] = { #include "XScreenSaver_ad.h" #include "XScreenSaver_Xm_ad.h" 0 }; int main (int argc, char **argv) { XtAppContext app; prefs_pair Pair, *pair; saver_preferences P, P2, *p, *p2; Bool prefs = False; int i; Display *dpy; Widget toplevel_shell, dialog; char *real_progname = argv[0]; char *s; s = strrchr (real_progname, '/'); if (s) real_progname = s+1; p = &P; p2 = &P2; pair = &Pair; pair->a = p; pair->b = p2; memset (p, 0, sizeof (*p)); memset (p2, 0, sizeof (*p2)); global_prefs_pair = pair; progname = real_progname; /* We must read exactly the same resources as xscreensaver. That means we must have both the same progclass *and* progname, at least as far as the resource database is concerned. So, put "xscreensaver" in argv[0] while initializing Xt. */ argv[0] = "xscreensaver"; progname = argv[0]; toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, defaults, 0, 0); dpy = XtDisplay (toplevel_shell); db = XtDatabase (dpy); XtGetApplicationNameAndClass (dpy, &progname, &progclass); XSetErrorHandler (demo_ehandler); /* Complain about unrecognized command-line arguments. */ for (i = 1; i < argc; i++) { char *s = argv[i]; if (s[0] == '-' && s[1] == '-') s++; if (!strcmp (s, "-prefs")) prefs = True; else { fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", real_progname); exit (1); } } short_version = (char *) malloc (5); memcpy (short_version, screensaver_id + 17, 4); short_version [4] = 0; /* Load the init file, which may end up consulting the X resource database and the site-wide app-defaults file. Note that at this point, it's important that `progname' be "xscreensaver", rather than whatever was in argv[0]. */ p->db = db; load_init_file (dpy, p); *p2 = *p; /* Now that Xt has been initialized, and the resources have been read, we can set our `progname' variable to something more in line with reality. */ progname = real_progname; #if 0 { XrmName name = { 0 }; XrmClass class = { 0 }; int count = 0; XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper, (POINTER) &count); } #endif /* Intern the atoms that xscreensaver_command() needs. */ XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False); XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False); XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False); XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False); XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False); XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False); XA_SELECT = XInternAtom (dpy, "SELECT", False); XA_DEMO = XInternAtom (dpy, "DEMO", False); XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False); XA_BLANK = XInternAtom (dpy, "BLANK", False); XA_LOCK = XInternAtom (dpy, "LOCK", False); XA_EXIT = XInternAtom (dpy, "EXIT", False); XA_RESTART = XInternAtom (dpy, "RESTART", False); /* Create the window and all its widgets. */ dialog = create_xscreensaver_demo (toplevel_shell); /* Set the window's title. */ { char title[255]; char *v = (char *) strdup(strchr(screensaver_id, ' ')); char *s1, *s2, *s3, *s4; s1 = (char *) strchr(v, ' '); s1++; s2 = (char *) strchr(s1, ' '); s3 = (char *) strchr(v, '('); s3++; s4 = (char *) strchr(s3, ')'); *s2 = 0; *s4 = 0; sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3); XtVaSetValues (toplevel_shell, XtNtitle, title, NULL); free (v); } sanity_check_resources (toplevel_shell); add_callbacks (toplevel_shell, pair); populate_hack_list (toplevel_shell, pair); populate_prefs_page (toplevel_shell, pair); sensitize_demo_widgets (toplevel_shell, False); scroll_to_current_hack (toplevel_shell, pair); XtManageChild (dialog); XtRealizeWidget(toplevel_shell); /* The next few calls must come after XtRealizeWidget(). */ pixmapify_buttons (toplevel_shell); hack_button_sizes (toplevel_shell); ensure_selected_item_visible (name_to_widget (toplevel_shell, "list")); XSync (dpy, False); XtVaSetValues (toplevel_shell, XmNallowShellResize, False, NULL); /* Handle the -prefs command-line argument. */ if (prefs) { Widget tabber = name_to_widget (toplevel_shell, "folder"); Widget this_tab = name_to_widget (toplevel_shell, "optionsTab"); Widget this_page = name_to_widget (toplevel_shell, "preferencesForm"); Widget *kids = 0; Cardinal nkids = 0; if (!tabber) abort(); XtVaGetValues (tabber, XmNnumChildren, &nkids, XmNchildren, &kids, NULL); if (!kids) abort(); if (nkids > 0) XtUnmanageChildren (kids, nkids); XtManageChild (this_page); XmProcessTraversal (this_tab, XmTRAVERSE_CURRENT); } /* Issue any warnings about the running xscreensaver daemon. */ the_network_is_not_the_computer (toplevel_shell); XtAppMainLoop (app); exit (0); } #endif /* HAVE_MOTIF -- whole file */ xscreensaver-5.15/driver/dpms.c000066400000000000000000000224421164314150500165620ustar00rootroot00000000000000/* dpms.c --- syncing the X Display Power Management values * xscreensaver, Copyright (c) 2001-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* Display Power Management System (DPMS.) On XFree86 systems, "man xset" reports: -dpms The -dpms option disables DPMS (Energy Star) features. +dpms The +dpms option enables DPMS (Energy Star) features. dpms flags... The dpms option allows the DPMS (Energy Star) parameters to be set. The option can take up to three numerical values, or the `force' flag followed by a DPMS state. The `force' flags forces the server to immediately switch to the DPMS state specified. The DPMS state can be one of `standby', `suspend', or `off'. When numerical values are given, they set the inactivity period before the three modes are activated. The first value given is for the `standby' mode, the second is for the `suspend' mode, and the third is for the `off' mode. Setting these values implicitly enables the DPMS features. A value of zero disables a particular mode. However, note that the implementation is more than a little bogus, in that there is code in /usr/X11R6/lib/libXdpms.a to implement all the usual server-extension-querying utilities -- but there are no prototypes in any header file! Thus, the prototypes here. (The stuff in X11/extensions/dpms.h and X11/extensions/dpmsstr.h define the raw X protcol, they don't define the API to libXdpms.a.) Some documentation: Library: ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMSLib.ms Protocol: ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMS.ms */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #ifdef HAVE_DPMS_EXTENSION /* almost the whole file */ # include # include /*# include */ /* Why this crap is not in a header file somewhere, I have no idea. Losers! */ extern Bool DPMSQueryExtension (Display *, int *event_ret, int *err_ret); extern Status DPMSGetVersion (Display *, int *major_ret, int *minor_ret); extern Bool DPMSCapable (Display *); extern Status DPMSInfo (Display *, CARD16 *power_level, BOOL *state); extern Status DPMSEnable (Display *dpy); extern Status DPMSDisable (Display *dpy); extern Status DPMSForceLevel (Display *, CARD16 level); extern Status DPMSSetTimeouts (Display *, CARD16 standby, CARD16 suspend, CARD16 off); extern Bool DPMSGetTimeouts (Display *, CARD16 *standby, CARD16 *suspend, CARD16 *off); #endif /* HAVE_DPMS_EXTENSION */ /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer #define XtAppContext void* #define XrmDatabase void* #define XtIntervalId void* #define XtPointer void* #define Widget void* #include "xscreensaver.h" #ifdef HAVE_DPMS_EXTENSION static Bool error_handler_hit_p = False; static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) { error_handler_hit_p = True; return 0; } void sync_server_dpms_settings (Display *dpy, Bool enabled_p, int standby_secs, int suspend_secs, int off_secs, Bool verbose_p) { int event = 0, error = 0; BOOL o_enabled = False; CARD16 o_power = 0; CARD16 o_standby = 0, o_suspend = 0, o_off = 0; Bool bogus_p = False; if (standby_secs == 0 && suspend_secs == 0 && off_secs == 0) /* all zero implies "DPMS disabled" */ enabled_p = False; else if ((standby_secs != 0 && standby_secs < 10) || (suspend_secs != 0 && suspend_secs < 10) || (off_secs != 0 && off_secs < 10)) /* any negative, or any positive-and-less-than-10-seconds, is crazy. */ bogus_p = True; if (bogus_p) enabled_p = False; /* X protocol sends these values in a CARD16, so truncate them to 16 bits. This means that the maximum timeout is 18:12:15. */ if (standby_secs > 0xFFFF) standby_secs = 0xFFFF; if (suspend_secs > 0xFFFF) suspend_secs = 0xFFFF; if (off_secs > 0xFFFF) off_secs = 0xFFFF; if (! DPMSQueryExtension (dpy, &event, &error)) { if (verbose_p) fprintf (stderr, "%s: XDPMS extension not supported.\n", blurb()); return; } if (! DPMSCapable (dpy)) { if (verbose_p) fprintf (stderr, "%s: DPMS not supported.\n", blurb()); return; } if (! DPMSInfo (dpy, &o_power, &o_enabled)) { if (verbose_p) fprintf (stderr, "%s: unable to get DPMS state.\n", blurb()); return; } if (o_enabled != enabled_p) { if (! (enabled_p ? DPMSEnable (dpy) : DPMSDisable (dpy))) { if (verbose_p) fprintf (stderr, "%s: unable to set DPMS state.\n", blurb()); return; } else if (verbose_p) fprintf (stderr, "%s: turned DPMS %s.\n", blurb(), enabled_p ? "on" : "off"); } if (bogus_p) { if (verbose_p) fprintf (stderr, "%s: not setting bogus DPMS timeouts: %d %d %d.\n", blurb(), standby_secs, suspend_secs, off_secs); return; } if (!DPMSGetTimeouts (dpy, &o_standby, &o_suspend, &o_off)) { if (verbose_p) fprintf (stderr, "%s: unable to get DPMS timeouts.\n", blurb()); return; } if (o_standby != standby_secs || o_suspend != suspend_secs || o_off != off_secs) { if (!DPMSSetTimeouts (dpy, standby_secs, suspend_secs, off_secs)) { if (verbose_p) fprintf (stderr, "%s: unable to set DPMS timeouts.\n", blurb()); return; } else if (verbose_p) fprintf (stderr, "%s: set DPMS timeouts: %d %d %d.\n", blurb(), standby_secs, suspend_secs, off_secs); } } Bool monitor_powered_on_p (saver_info *si) { Bool result; int event_number, error_number; BOOL onoff = False; CARD16 state; if (!DPMSQueryExtension(si->dpy, &event_number, &error_number)) /* Server doesn't know -- assume the monitor is on. */ result = True; else if (!DPMSCapable(si->dpy)) /* Server says the monitor doesn't do power management -- so it's on. */ result = True; else { DPMSInfo(si->dpy, &state, &onoff); if (!onoff) /* Server says DPMS is disabled -- so the monitor is on. */ result = True; else switch (state) { case DPMSModeOn: result = True; break; /* really on */ case DPMSModeStandby: result = False; break; /* kinda off */ case DPMSModeSuspend: result = False; break; /* pretty off */ case DPMSModeOff: result = False; break; /* really off */ default: result = True; break; /* protocol error? */ } } return result; } void monitor_power_on (saver_info *si, Bool on_p) { if ((!!on_p) != monitor_powered_on_p (si)) { XErrorHandler old_handler; int event_number, error_number; if (!DPMSQueryExtension(si->dpy, &event_number, &error_number) || !DPMSCapable(si->dpy)) { if (si->prefs.verbose_p) fprintf (stderr, "%s: unable to power %s monitor: no DPMS extension.\n", blurb(), (on_p ? "on" : "off")); return; } /* The manual for DPMSForceLevel() says that it throws BadMatch if "DPMS is disabled on the specified display." The manual for DPMSCapable() says that it "returns True if the X server is capable of DPMS." Apparently they consider "capable of DPMS" and "DPMS is enabled" to be different things, and so even if DPMSCapable() returns True, DPMSForceLevel() *might* throw an X Error. Isn't that just fucking special. */ XSync (si->dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); XSync (si->dpy, False); DPMSForceLevel(si->dpy, (on_p ? DPMSModeOn : DPMSModeOff)); XSync (si->dpy, False); /* Ignore error_handler_hit_p, just probe monitor instead */ if ((!!on_p) != monitor_powered_on_p (si)) /* double-check */ fprintf (stderr, "%s: DPMSForceLevel(dpy, %s) did not change monitor power state.\n", blurb(), (on_p ? "DPMSModeOn" : "DPMSModeOff")); } } #else /* !HAVE_DPMS_EXTENSION */ void sync_server_dpms_settings (Display *dpy, Bool enabled_p, int standby_secs, int suspend_secs, int off_secs, Bool verbose_p) { if (verbose_p) fprintf (stderr, "%s: DPMS support not compiled in.\n", blurb()); } Bool monitor_powered_on_p (saver_info *si) { return True; } void monitor_power_on (saver_info *si, Bool on_p) { return; } #endif /* !HAVE_DPMS_EXTENSION */ xscreensaver-5.15/driver/exec.c000066400000000000000000000167721164314150500165540ustar00rootroot00000000000000/* exec.c --- executes a program in *this* pid, without an intervening process. * xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* I don't believe what a sorry excuse for an operating system UNIX is! - I want to spawn a process. - I want to know it's pid so that I can kill it. - I would like to receive a message when it dies of natural causes. - I want the spawned process to have user-specified arguments. If shell metacharacters are present (wildcards, backquotes, etc), the only way to parse those arguments is to run a shell to do the parsing for you. And the only way to know the pid of the process is to fork() and exec() it in the spawned side of the fork. But if you're running a shell to parse your arguments, this gives you the pid of the *shell*, not the pid of the *process* that you're actually interested in, which is an *inferior* of the shell. This also means that the SIGCHLD you get applies to the shell, not its inferior. (Why isn't that sufficient? I don't remember any more, but it turns out that it isn't.) So, the only solution, when metacharacters are present, is to force the shell to exec() its inferior. What a fucking hack! We prepend "exec " to the command string, and hope it doesn't contain unquoted semicolons or ampersands (we don't search for them, because we don't want to prohibit their use in quoted strings (messages, for example) and parsing out the various quote characters is too much of a pain.) (Actually, Clint Wong points out that process groups might be used to take care of this problem; this may be worth considering some day, except that, 1: this code works now, so why fix it, and 2: from what I've seen in Emacs, dealing with process groups isn't especially portable.) */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #ifndef ESRCH # include #endif #if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS) # include /* for setpriority() and PRIO_PROCESS */ /* and also setrlimit() and RLIMIT_AS */ #endif #ifdef VMS # include # include /* for close */ # include /* for getpid */ # define pid_t int # define fork vfork #endif /* VMS */ #include "exec.h" extern const char *blurb (void); static void nice_process (int nice_level); #ifndef VMS static void exec_simple_command (const char *command) { char *av[1024]; int ac = 0; char *token = strtok (strdup(command), " \t"); while (token) { av[ac++] = token; token = strtok(0, " \t"); } av[ac] = 0; execvp (av[0], av); /* shouldn't return. */ } static void exec_complex_command (const char *shell, const char *command) { char *av[5]; int ac = 0; char *command2 = (char *) malloc (strlen (command) + 10); const char *s; int got_eq = 0; const char *after_vars; /* Skip leading whitespace. */ while (*command == ' ' || *command == '\t') command++; /* If the string has a series of tokens with "=" in them at them, set `after_vars' to point into the string after those tokens and any trailing whitespace. Otherwise, after_vars == command. */ after_vars = command; for (s = command; *s; s++) { if (*s == '=') got_eq = 1; else if (*s == ' ') { if (got_eq) { while (*s == ' ' || *s == '\t') s++; after_vars = s; got_eq = 0; } else break; } } *command2 = 0; strncat (command2, command, after_vars - command); strcat (command2, "exec "); strcat (command2, after_vars); /* We have now done these transformations: "foo -x -y" ==> "exec foo -x -y" "BLAT=foop foo -x" ==> "BLAT=foop exec foo -x" "BLAT=foop A=b foo -x" ==> "BLAT=foop A=b exec foo -x" */ /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */ av [ac++] = (char *) shell; av [ac++] = "-c"; av [ac++] = command2; av [ac] = 0; execvp (av[0], av); /* shouldn't return. */ } #else /* VMS */ static void exec_vms_command (const char *command) { system (command); fflush (stderr); fflush (stdout); exit (1); /* Note that this only exits a child fork. */ } #endif /* !VMS */ void exec_command (const char *shell, const char *command, int nice_level) { int hairy_p; #ifndef VMS nice_process (nice_level); hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"="); /* note: = is in the above because of the sh syntax "FOO=bar cmd". */ if (getuid() == (uid_t) 0 || geteuid() == (uid_t) 0) { /* If you're thinking of commenting this out, think again. If you do so, you will open a security hole. Mail jwz so that he may enlighten you as to the error of your ways. */ fprintf (stderr, "%s: we're still running as root! Disaster!\n", blurb()); exit (-1); } if (hairy_p) /* If it contains any shell metacharacters, do it the hard way, and fork a shell to parse the arguments for us. */ exec_complex_command (shell, command); else /* Otherwise, we can just exec the program directly. */ exec_simple_command (command); #else /* VMS */ exec_vms_command (command); #endif /* VMS */ } /* Setting process priority */ static void nice_process (int nice_level) { if (nice_level == 0) return; #if defined(HAVE_NICE) { int old_nice = nice (0); int n = nice_level - old_nice; errno = 0; if (nice (n) == -1 && errno != 0) { char buf [512]; sprintf (buf, "%s: nice(%d) failed", blurb(), n); perror (buf); } } #elif defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS) if (setpriority (PRIO_PROCESS, getpid(), nice_level) != 0) { char buf [512]; sprintf (buf, "%s: setpriority(PRIO_PROCESS, %lu, %d) failed", blurb(), (unsigned long) getpid(), nice_level); perror (buf); } #else fprintf (stderr, "%s: don't know how to change process priority on this system.\n", blurb()); #endif } /* Whether the given command exists on $PATH. (Anything before the first space is considered to be the program name.) */ int on_path_p (const char *program) { int result = 0; struct stat st; char *cmd = strdup (program); char *token = strchr (cmd, ' '); char *path = 0; int L; if (token) *token = 0; token = 0; if (strchr (cmd, '/')) { result = (0 == stat (cmd, &st)); goto DONE; } path = getenv("PATH"); if (!path || !*path) goto DONE; L = strlen (cmd); path = strdup (path); token = strtok (path, ":"); while (token) { char *p2 = (char *) malloc (strlen (token) + L + 3); strcpy (p2, token); strcat (p2, "/"); strcat (p2, cmd); result = (0 == stat (p2, &st)); if (result) goto DONE; token = strtok (0, ":"); } DONE: free (cmd); if (path) free (path); return result; } xscreensaver-5.15/driver/exec.h000066400000000000000000000015611164314150500165470ustar00rootroot00000000000000/* exec.c --- executes a program in *this* pid, without an intervening process. * xscreensaver, Copyright (c) 1991-2006 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifndef __XSCREENSAVER_EXEC_H__ #define __XSCREENSAVER_EXEC_H__ extern void exec_command (const char *shell, const char *command, int nice_level); extern int on_path_p (const char *program); #endif /* __XSCREENSAVER_EXEC_H__ */ xscreensaver-5.15/driver/link_axp.com000066400000000000000000000012641164314150500177570ustar00rootroot00000000000000$! We fisrt test the version of DECW/Motif ; if 1.2 we need to link with new $! X11R5 libraries $@sys$update:decw$get_image_version sys$share:decw$xlibshr.exe decw$version $ if f$extract(4,3,decw$version).eqs."1.2" $ then $! DECW/Motif 1.2 : link with X11R5 libraries $ link xscreensaver-command,vms_axp_12.opt/opt $ link xscreensaver,demo,dialogs-xm,lock,passwd,stderr,subprocs,timers, - windows,xset,vms-getpwnam,vms-hpwd,vms-validate,vms_axp_12.opt/opt $ else $! Else, link with X11R4 libraries $ link xscreensaver-command,vms_axp.opt/opt $ link xscreensaver,demo,dialogs-xm,lock,passwd,stderr,subprocs,timers, - windows,xset,vms-getpwnam,vms-hpwd,vms-validate,vms_axp.opt/opt $ endif xscreensaver-5.15/driver/link_decc.com000066400000000000000000000012701164314150500200620ustar00rootroot00000000000000$! We fisrt test the version of DECW/Motif ; if 1.2 we need to link with new $! X11R5 libraries $@sys$update:decw$get_image_version sys$share:decw$xlibshr.exe decw$version $ if f$extract(4,3,decw$version).eqs."1.2" $ then $! DECW/Motif 1.2 : link with X11R5 libraries $ link xscreensaver-command,vms_decc_12.opt/opt $ link xscreensaver,demo,dialogs-xm,lock,passwd,stderr,subprocs,timers, - windows,xset,vms-getpwnam,vms-hpwd,vms-validate,vms_decc_12.opt/opt $ else $! Else, link with X11R4 libraries $ link xscreensaver-command,vms_decc.opt/opt $ link xscreensaver,demo,dialogs-xm,lock,passwd,stderr,subprocs,timers, - windows,xset,vms-getpwnam,vms-hpwd,vms-validate,vms_decc.opt/opt $ endif xscreensaver-5.15/driver/lock.c000066400000000000000000002045341164314150500165530ustar00rootroot00000000000000/* lock.c --- handling the password dialog for locking-mode. * xscreensaver, Copyright (c) 1993-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* Athena locking code contributed by Jon A. Christopher */ /* Copyright 1997, with the same permissions as above. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include /* for time() */ #include #include #include "xscreensaver.h" #include "resources.h" #include "mlstring.h" #include "auth.h" #ifndef NO_LOCKING /* (mostly) whole file */ #ifdef HAVE_XHPDISABLERESET # include static void hp_lock_reset (saver_info *si, Bool lock_p); #endif /* HAVE_XHPDISABLERESET */ #ifdef HAVE_XF86VMODE # include static void xfree_lock_mode_switch (saver_info *si, Bool lock_p); #endif /* HAVE_XF86VMODE */ #ifdef HAVE_XF86MISCSETGRABKEYSSTATE # include static void xfree_lock_grab_smasher (saver_info *si, Bool lock_p); #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */ #ifdef HAVE_RANDR # include #endif /* HAVE_RANDR */ #ifdef _VROOT_H_ ERROR! You must not include vroot.h in this file. #endif #ifdef HAVE_UNAME # include /* for hostname info */ #endif /* HAVE_UNAME */ #include #ifndef VMS # include #else /* VMS */ extern char *getenv(const char *name); extern int validate_user(char *name, char *password); static Bool vms_passwd_valid_p(char *pw, Bool verbose_p) { return (validate_user (getenv("USER"), typed_passwd) == 1); } # undef passwd_valid_p # define passwd_valid_p vms_passwd_valid_p #endif /* VMS */ #define SAMPLE_INPUT "MMMMMMMMMMMM" #undef MAX #define MAX(a,b) ((a)>(b)?(a):(b)) typedef struct info_dialog_data info_dialog_data; #define MAX_BYTES_PER_CHAR 8 /* UTF-8 uses no more than 3, I think */ #define MAX_PASSWD_CHARS 128 /* Longest possible passphrase */ struct passwd_dialog_data { saver_screen_info *prompt_screen; int previous_mouse_x, previous_mouse_y; /* "Characters" in the password may be a variable number of bytes long. typed_passwd contains the raw bytes. typed_passwd_char_size indicates the size in bytes of each character, so that we can make backspace work. */ char typed_passwd [MAX_PASSWD_CHARS * MAX_BYTES_PER_CHAR]; char typed_passwd_char_size [MAX_PASSWD_CHARS]; XtIntervalId timer; int i_beam; float ratio; Position x, y; Dimension width; Dimension height; Dimension border_width; Bool echo_input; Bool show_stars_p; /* "I regret that I have but one asterisk for my country." -- Nathan Hale, 1776. */ char *heading_label; char *body_label; char *user_label; mlstring *info_label; /* The entry field shall only be displayed if prompt_label is not NULL */ mlstring *prompt_label; char *date_label; char *passwd_string; Bool passwd_changed_p; /* Whether the user entry field needs redrawing */ Bool caps_p; /* Whether we saw a keypress with caps-lock on */ char *unlock_label; char *login_label; char *uname_label; Bool show_uname_p; XFontStruct *heading_font; XFontStruct *body_font; XFontStruct *label_font; XFontStruct *passwd_font; XFontStruct *date_font; XFontStruct *button_font; XFontStruct *uname_font; Pixel foreground; Pixel background; Pixel border; Pixel passwd_foreground; Pixel passwd_background; Pixel thermo_foreground; Pixel thermo_background; Pixel shadow_top; Pixel shadow_bottom; Pixel button_foreground; Pixel button_background; Dimension preferred_logo_width, logo_width; Dimension preferred_logo_height, logo_height; Dimension thermo_width; Dimension internal_border; Dimension shadow_width; Dimension passwd_field_x, passwd_field_y; Dimension passwd_field_width, passwd_field_height; Dimension unlock_button_x, unlock_button_y; Dimension unlock_button_width, unlock_button_height; Dimension login_button_x, login_button_y; Dimension login_button_width, login_button_height; Dimension thermo_field_x, thermo_field_y; Dimension thermo_field_height; Pixmap logo_pixmap; Pixmap logo_clipmask; int logo_npixels; unsigned long *logo_pixels; Cursor passwd_cursor; Bool unlock_button_down_p; Bool login_button_down_p; Bool login_button_p; Bool login_button_enabled_p; Bool button_state_changed_p; /* Refers to both buttons */ Pixmap save_under; Pixmap user_entry_pixmap; }; static void draw_passwd_window (saver_info *si); static void update_passwd_window (saver_info *si, const char *printed_passwd, float ratio); static void destroy_passwd_window (saver_info *si); static void undo_vp_motion (saver_info *si); static void finished_typing_passwd (saver_info *si, passwd_dialog_data *pw); static void cleanup_passwd_window (saver_info *si); static void restore_background (saver_info *si); extern void xss_authenticate(saver_info *si, Bool verbose_p); static int new_passwd_window (saver_info *si) { passwd_dialog_data *pw; Screen *screen; Colormap cmap; char *f; saver_screen_info *ssi = &si->screens [mouse_screen (si)]; pw = (passwd_dialog_data *) calloc (1, sizeof(*pw)); if (!pw) return -1; /* Display the button only if the "newLoginCommand" pref is non-null. */ pw->login_button_p = (si->prefs.new_login_command && *si->prefs.new_login_command); pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow); pw->prompt_screen = ssi; screen = pw->prompt_screen->screen; cmap = DefaultColormapOfScreen (screen); pw->show_stars_p = get_boolean_resource(si->dpy, "passwd.asterisks", "Boolean"); pw->heading_label = get_string_resource (si->dpy, "passwd.heading.label", "Dialog.Label.Label"); pw->body_label = get_string_resource (si->dpy, "passwd.body.label", "Dialog.Label.Label"); pw->user_label = get_string_resource (si->dpy, "passwd.user.label", "Dialog.Label.Label"); pw->unlock_label = get_string_resource (si->dpy, "passwd.unlock.label", "Dialog.Button.Label"); pw->login_label = get_string_resource (si->dpy, "passwd.login.label", "Dialog.Button.Label"); pw->date_label = get_string_resource (si->dpy, "dateFormat", "DateFormat"); if (!pw->heading_label) pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY"); if (!pw->body_label) pw->body_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY"); if (!pw->user_label) pw->user_label = strdup("ERROR"); if (!pw->date_label) pw->date_label = strdup("ERROR"); if (!pw->unlock_label) pw->unlock_label = strdup("ERROR (UNLOCK)"); if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ; /* Put the version number in the label. */ { char *s = (char *) malloc (strlen(pw->heading_label) + 20); sprintf(s, pw->heading_label, si->version); free (pw->heading_label); pw->heading_label = s; } /* Get hostname info */ pw->uname_label = strdup(""); /* Initialy, write nothing */ # ifdef HAVE_UNAME { struct utsname uts; if (uname (&uts) == 0) { #if 0 /* Get the full hostname */ { char *s; if ((s = strchr(uts.nodename, '.'))) *s = 0; } #endif char *s = strdup (uts.nodename); free (pw->uname_label); pw->uname_label = s; } } # endif pw->passwd_string = strdup(""); f = get_string_resource (si->dpy, "passwd.headingFont", "Dialog.Font"); pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); f = get_string_resource (si->dpy, "passwd.buttonFont", "Dialog.Font"); pw->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->button_font) pw->button_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); f = get_string_resource(si->dpy, "passwd.bodyFont", "Dialog.Font"); pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); f = get_string_resource(si->dpy, "passwd.labelFont", "Dialog.Font"); pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); f = get_string_resource(si->dpy, "passwd.passwdFont", "Dialog.Font"); pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); f = get_string_resource(si->dpy, "passwd.dateFont", "Dialog.Font"); pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); f = get_string_resource(si->dpy, "passwd.unameFont", "Dialog.Font"); pw->uname_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->uname_font) pw->uname_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); pw->show_uname_p = get_boolean_resource(si->dpy, "passwd.uname", "Boolean"); pw->foreground = get_pixel_resource (si->dpy, cmap, "passwd.foreground", "Dialog.Foreground" ); pw->background = get_pixel_resource (si->dpy, cmap, "passwd.background", "Dialog.Background" ); pw->border = get_pixel_resource (si->dpy, cmap, "passwd.borderColor", "Dialog.borderColor"); if (pw->foreground == pw->background) { /* Make sure the error messages show up. */ pw->foreground = BlackPixelOfScreen (screen); pw->background = WhitePixelOfScreen (screen); } pw->passwd_foreground = get_pixel_resource (si->dpy, cmap, "passwd.text.foreground", "Dialog.Text.Foreground" ); pw->passwd_background = get_pixel_resource (si->dpy, cmap, "passwd.text.background", "Dialog.Text.Background" ); pw->button_foreground = get_pixel_resource (si->dpy, cmap, "splash.Button.foreground", "Dialog.Button.Foreground" ); pw->button_background = get_pixel_resource (si->dpy, cmap, "splash.Button.background", "Dialog.Button.Background" ); pw->thermo_foreground = get_pixel_resource (si->dpy, cmap, "passwd.thermometer.foreground", "Dialog.Thermometer.Foreground"); pw->thermo_background = get_pixel_resource ( si->dpy, cmap, "passwd.thermometer.background", "Dialog.Thermometer.Background"); pw->shadow_top = get_pixel_resource ( si->dpy, cmap, "passwd.topShadowColor", "Dialog.Foreground" ); pw->shadow_bottom = get_pixel_resource (si->dpy, cmap, "passwd.bottomShadowColor", "Dialog.Background" ); pw->preferred_logo_width = get_integer_resource (si->dpy, "passwd.logo.width", "Dialog.Logo.Width"); pw->preferred_logo_height = get_integer_resource (si->dpy, "passwd.logo.height", "Dialog.Logo.Height"); pw->thermo_width = get_integer_resource (si->dpy, "passwd.thermometer.width", "Dialog.Thermometer.Width"); pw->internal_border = get_integer_resource (si->dpy, "passwd.internalBorderWidth", "Dialog.InternalBorderWidth"); pw->shadow_width = get_integer_resource (si->dpy, "passwd.shadowThickness", "Dialog.ShadowThickness"); if (pw->preferred_logo_width == 0) pw->preferred_logo_width = 150; if (pw->preferred_logo_height == 0) pw->preferred_logo_height = 150; if (pw->internal_border == 0) pw->internal_border = 15; if (pw->shadow_width == 0) pw->shadow_width = 4; if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width; /* We need to remember the mouse position and restore it afterward, or sometimes (perhaps only with Xinerama?) the mouse gets warped to inside the bounds of the lock dialog window. */ { Window pointer_root, pointer_child; int root_x, root_y, win_x, win_y; unsigned int mask; pw->previous_mouse_x = 0; pw->previous_mouse_y = 0; if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen), &pointer_root, &pointer_child, &root_x, &root_y, &win_x, &win_y, &mask)) { pw->previous_mouse_x = root_x; pw->previous_mouse_y = root_y; if (si->prefs.verbose_p) fprintf (stderr, "%s: %d: mouse is at %d,%d.\n", blurb(), pw->prompt_screen->number, pw->previous_mouse_x, pw->previous_mouse_y); } else if (si->prefs.verbose_p) fprintf (stderr, "%s: %d: unable to determine mouse position?\n", blurb(), pw->prompt_screen->number); } /* Before mapping the window, save a pixmap of the current screen. When we lower the window, we restore these bits. This works, because the running screenhack has already been sent SIGSTOP, so we know nothing else is drawing right now! */ { XGCValues gcv; GC gc; pw->save_under = XCreatePixmap (si->dpy, pw->prompt_screen->screensaver_window, pw->prompt_screen->width, pw->prompt_screen->height, pw->prompt_screen->current_depth); gcv.function = GXcopy; gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv); XCopyArea (si->dpy, pw->prompt_screen->screensaver_window, pw->save_under, gc, 0, 0, pw->prompt_screen->width, pw->prompt_screen->height, 0, 0); XFreeGC (si->dpy, gc); } si->pw_data = pw; return 0; } Bool debug_passwd_window_p = False; /* used only by test-passwd.c */ /** * info_msg and prompt may be NULL. */ static int make_passwd_window (saver_info *si, const char *info_msg, const char *prompt, Bool echo) { XSetWindowAttributes attrs; unsigned long attrmask = 0; passwd_dialog_data *pw; Screen *screen; Colormap cmap; Dimension max_string_width_px; saver_screen_info *ssi = &si->screens [mouse_screen (si)]; cleanup_passwd_window (si); if (! ssi) /* WTF? Trying to prompt while no screens connected? */ return -1; if (!si->pw_data) if (new_passwd_window (si) < 0) return -1; if (!(pw = si->pw_data)) return -1; pw->ratio = 1.0; pw->prompt_screen = ssi; if (si->prefs.verbose_p) fprintf (stderr, "%s: %d: creating password dialog (\"%s\")\n", blurb(), pw->prompt_screen->number, info_msg ? info_msg : ""); screen = pw->prompt_screen->screen; cmap = DefaultColormapOfScreen (screen); pw->echo_input = echo; max_string_width_px = ssi->width - pw->shadow_width * 4 - pw->border_width * 2 - pw->thermo_width - pw->preferred_logo_width - pw->internal_border * 2; /* As the string wraps it makes the window taller which makes the logo wider * which leaves less room for the text which makes the string wrap. Uh-oh, a * loop. By wrapping at a bit less than the available width, there's some * room for the dialog to grow without going off the edge of the screen. */ max_string_width_px *= 0.75; pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label, pw->label_font, max_string_width_px); { int direction, ascent, descent; XCharStruct overall; pw->width = 0; pw->height = 0; /* Measure the heading_label. */ XTextExtents (pw->heading_font, pw->heading_label, strlen(pw->heading_label), &direction, &ascent, &descent, &overall); if (overall.width > pw->width) pw->width = overall.width; pw->height += ascent + descent; /* Measure the uname_label. */ if ((strlen(pw->uname_label)) && pw->show_uname_p) { XTextExtents (pw->uname_font, pw->uname_label, strlen(pw->uname_label), &direction, &ascent, &descent, &overall); if (overall.width > pw->width) pw->width = overall.width; pw->height += ascent + descent; } { Dimension w2 = 0, w3 = 0, button_w = 0; Dimension h2 = 0, h3 = 0, button_h = 0; const char *passwd_string = SAMPLE_INPUT; /* Measure the user_label. */ XTextExtents (pw->label_font, pw->user_label, strlen(pw->user_label), &direction, &ascent, &descent, &overall); if (overall.width > w2) w2 = overall.width; h2 += ascent + descent; /* Measure the info_label. */ if (pw->info_label->overall_width > pw->width) pw->width = pw->info_label->overall_width; h2 += pw->info_label->overall_height; /* Measure the user string. */ XTextExtents (pw->passwd_font, si->user, strlen(si->user), &direction, &ascent, &descent, &overall); overall.width += (pw->shadow_width * 4); ascent += (pw->shadow_width * 4); if (overall.width > w3) w3 = overall.width; h3 += ascent + descent; /* Measure the (dummy) passwd_string. */ if (prompt) { XTextExtents (pw->passwd_font, passwd_string, strlen(passwd_string), &direction, &ascent, &descent, &overall); overall.width += (pw->shadow_width * 4); ascent += (pw->shadow_width * 4); if (overall.width > w3) w3 = overall.width; h3 += ascent + descent; /* Measure the prompt_label. */ max_string_width_px -= w3; pw->prompt_label = mlstring_new (prompt, pw->label_font, max_string_width_px); if (pw->prompt_label->overall_width > w2) w2 = pw->prompt_label->overall_width; h2 += pw->prompt_label->overall_height; w2 = w2 + w3 + (pw->shadow_width * 2); h2 = MAX (h2, h3); } /* The "Unlock" button. */ XTextExtents (pw->label_font, pw->unlock_label, strlen(pw->unlock_label), &direction, &ascent, &descent, &overall); button_w = overall.width; button_h = ascent + descent; /* Add some horizontal padding inside the button. */ button_w += ascent; button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2); button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2); pw->unlock_button_width = button_w; pw->unlock_button_height = button_h; w2 = MAX (w2, button_w); h2 += button_h * 1.5; /* The "New Login" button */ pw->login_button_width = 0; pw->login_button_height = 0; if (pw->login_button_p) { pw->login_button_enabled_p = True; /* Measure the "New Login" button */ XTextExtents (pw->button_font, pw->login_label, strlen (pw->login_label), &direction, &ascent, &descent, &overall); button_w = overall.width; button_h = ascent + descent; /* Add some horizontal padding inside the buttons. */ button_w += ascent; button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2); button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2); pw->login_button_width = button_w; pw->login_button_height = button_h; if (button_h > pw->unlock_button_height) h2 += (button_h * 1.5 - pw->unlock_button_height * 1.5); /* Use (2 * shadow_width) spacing between the buttons. Another (2 * shadow_width) is required to account for button shadows. */ w2 = MAX (w2, button_w + pw->unlock_button_width + (pw->shadow_width * 4)); } if (w2 > pw->width) pw->width = w2; pw->height += h2; } pw->width += (pw->internal_border * 2); pw->height += (pw->internal_border * 4); pw->width += pw->thermo_width + (pw->shadow_width * 3); if (pw->preferred_logo_height > pw->height) pw->height = pw->logo_height = pw->preferred_logo_height; else if (pw->height > pw->preferred_logo_height) pw->logo_height = pw->height; pw->logo_width = pw->logo_height; pw->width += pw->logo_width; } attrmask |= CWOverrideRedirect; attrs.override_redirect = True; if (debug_passwd_window_p) attrs.override_redirect = False; /* kludge for test-passwd.c */ attrmask |= CWEventMask; attrs.event_mask = (ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask); /* Figure out where on the desktop to place the window so that it will actually be visible; this takes into account virtual viewports as well as Xinerama. */ { saver_screen_info *ssi = &si->screens [mouse_screen (si)]; int x = ssi->x; int y = ssi->y; int w = ssi->width; int h = ssi->height; if (si->prefs.debug_p) w /= 2; pw->x = x + ((w + pw->width) / 2) - pw->width; pw->y = y + ((h + pw->height) / 2) - pw->height; if (pw->x < x) pw->x = x; if (pw->y < y) pw->y = y; } pw->border_width = get_integer_resource (si->dpy, "passwd.borderWidth", "Dialog.BorderWidth"); /* Only create the window the first time around */ if (!si->passwd_dialog) { si->passwd_dialog = XCreateWindow (si->dpy, RootWindowOfScreen(screen), pw->x, pw->y, pw->width, pw->height, pw->border_width, DefaultDepthOfScreen (screen), InputOutput, DefaultVisualOfScreen(screen), attrmask, &attrs); XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background); XSetWindowBorder (si->dpy, si->passwd_dialog, pw->border); /* We use the default visual, not ssi->visual, so that the logo pixmap's visual matches that of the si->passwd_dialog window. */ pw->logo_pixmap = xscreensaver_logo (ssi->screen, /* ssi->current_visual, */ DefaultVisualOfScreen(screen), si->passwd_dialog, cmap, pw->background, &pw->logo_pixels, &pw->logo_npixels, &pw->logo_clipmask, True); } else /* On successive prompts, just resize the window */ { XWindowChanges wc; unsigned int mask = CWX | CWY | CWWidth | CWHeight; wc.x = pw->x; wc.y = pw->y; wc.width = pw->width; wc.height = pw->height; XConfigureWindow (si->dpy, si->passwd_dialog, mask, &wc); } restore_background(si); XMapRaised (si->dpy, si->passwd_dialog); XSync (si->dpy, False); move_mouse_grab (si, si->passwd_dialog, pw->passwd_cursor, pw->prompt_screen->number); undo_vp_motion (si); si->pw_data = pw; if (cmap) XInstallColormap (si->dpy, cmap); draw_passwd_window (si); return 0; } static void draw_passwd_window (saver_info *si) { passwd_dialog_data *pw = si->pw_data; XGCValues gcv; GC gc1, gc2; int spacing, height; int x1, x2, x3, y1, y2; int sw; int tb_height; /* Force redraw */ pw->passwd_changed_p = True; pw->button_state_changed_p = True; /* This height is the height of all the elements, not to be confused with * the overall window height which is pw->height. It is used to compute * the amount of spacing (padding) between elements. */ height = (pw->heading_font->ascent + pw->heading_font->descent + pw->info_label->overall_height + MAX (((pw->label_font->ascent + pw->label_font->descent) + (pw->prompt_label ? pw->prompt_label->overall_height : 0)), ((pw->passwd_font->ascent + pw->passwd_font->descent) + (pw->shadow_width * 2)) * (pw->prompt_label ? 2 : 1)) + pw->date_font->ascent + pw->date_font->descent); if ((strlen(pw->uname_label)) && pw->show_uname_p) height += (pw->uname_font->ascent + pw->uname_font->descent); height += ((pw->button_font->ascent + pw->button_font->descent) * 2 + 2 * pw->shadow_width); spacing = ((pw->height - 2 * pw->shadow_width - pw->internal_border - height) / 10); if (spacing < 0) spacing = 0; gcv.foreground = pw->foreground; gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv); gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv); x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3); x3 = pw->width - (pw->shadow_width * 2); y1 = (pw->shadow_width * 2) + spacing + spacing; /* top heading */ XSetFont (si->dpy, gc1, pw->heading_font->fid); sw = string_width (pw->heading_font, pw->heading_label); x2 = (x1 + ((x3 - x1 - sw) / 2)); y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent; XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, pw->heading_label, strlen(pw->heading_label)); /* uname below top heading */ if ((strlen(pw->uname_label)) && pw->show_uname_p) { XSetFont (si->dpy, gc1, pw->uname_font->fid); y1 += spacing + pw->uname_font->ascent + pw->uname_font->descent; sw = string_width (pw->uname_font, pw->uname_label); x2 = (x1 + ((x3 - x1 - sw) / 2)); XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, pw->uname_label, strlen(pw->uname_label)); } /* the info_label (below uname) */ x2 = (x1 + ((x3 - x1 - pw->info_label->overall_width) / 2)); y1 += spacing + pw->info_label->font_height / 2; mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->info_label, x2, y1); y1 += pw->info_label->overall_height; tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent + (pw->shadow_width * 4)); /* the "User:" prompt */ y2 = y1; XSetForeground (si->dpy, gc1, pw->foreground); XSetFont (si->dpy, gc1, pw->label_font->fid); y1 += (spacing + tb_height + pw->shadow_width); x2 = (x1 + pw->internal_border + MAX(string_width (pw->label_font, pw->user_label), pw->prompt_label ? pw->prompt_label->overall_width : 0)); XDrawString (si->dpy, si->passwd_dialog, gc1, x2 - string_width (pw->label_font, pw->user_label), y1 - pw->passwd_font->descent, pw->user_label, strlen(pw->user_label)); /* the prompt_label prompt */ if (pw->prompt_label) { y1 += tb_height - pw->label_font->ascent + pw->shadow_width; mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->prompt_label, x2 - pw->prompt_label->overall_width, y1); } /* the "user name" text field */ y1 = y2; XSetForeground (si->dpy, gc1, pw->passwd_foreground); XSetForeground (si->dpy, gc2, pw->passwd_background); XSetFont (si->dpy, gc1, pw->passwd_font->fid); y1 += (spacing + tb_height); x2 += (pw->shadow_width * 4); pw->passwd_field_width = x3 - x2 - pw->internal_border; pw->passwd_field_height = (pw->passwd_font->ascent + pw->passwd_font->descent + pw->shadow_width); XFillRectangle (si->dpy, si->passwd_dialog, gc2, x2 - pw->shadow_width, y1 - (pw->passwd_font->ascent + pw->passwd_font->descent), pw->passwd_field_width, pw->passwd_field_height); XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1 - pw->passwd_font->descent, si->user, strlen(si->user)); /* the password/prompt text field */ if (pw->prompt_label) { y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width*2); pw->passwd_field_x = x2 - pw->shadow_width; pw->passwd_field_y = y1 - (pw->passwd_font->ascent + pw->passwd_font->descent); } /* The shadow around the text fields */ y1 = y2; y1 += (spacing + (pw->shadow_width * 3)); x1 = x2 - (pw->shadow_width * 2); x2 = pw->passwd_field_width + (pw->shadow_width * 2); y2 = pw->passwd_field_height + (pw->shadow_width * 2); draw_shaded_rectangle (si->dpy, si->passwd_dialog, x1, y1, x2, y2, pw->shadow_width, pw->shadow_bottom, pw->shadow_top); if (pw->prompt_label) { y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width*2); draw_shaded_rectangle (si->dpy, si->passwd_dialog, x1, y1, x2, y2, pw->shadow_width, pw->shadow_bottom, pw->shadow_top); } /* The date, below the text fields */ { char buf[100]; time_t now = time ((time_t *) 0); struct tm *tm = localtime (&now); memset (buf, 0, sizeof(buf)); strftime (buf, sizeof(buf)-1, pw->date_label, tm); XSetFont (si->dpy, gc1, pw->date_font->fid); y1 += pw->shadow_width; y1 += (spacing + tb_height); y1 += spacing/2; sw = string_width (pw->date_font, buf); x2 = x1 + x2 - sw; XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf)); } /* Set up the GCs for the "New Login" and "Unlock" buttons. */ XSetForeground(si->dpy, gc1, pw->button_foreground); XSetForeground(si->dpy, gc2, pw->button_background); XSetFont(si->dpy, gc1, pw->button_font->fid); /* The "Unlock" button */ x2 = pw->width - pw->internal_border - (pw->shadow_width * 2); /* right aligned button */ x1 = x2 - pw->unlock_button_width; /* Add half the difference between y1 and the internal edge. * It actually looks better if the internal border is ignored. */ y1 += ((pw->height - MAX (pw->unlock_button_height, pw->login_button_height) - spacing - y1) / 2); pw->unlock_button_x = x1; pw->unlock_button_y = y1; /* The "New Login" button */ if (pw->login_button_p) { /* Using the same GC as for the Unlock button */ sw = string_width (pw->button_font, pw->login_label); /* left aligned button */ x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) + pw->internal_border); pw->login_button_x = x1; pw->login_button_y = y1; } /* The logo */ x1 = pw->shadow_width * 6; y1 = pw->shadow_width * 6; x2 = pw->logo_width - (pw->shadow_width * 12); y2 = pw->logo_height - (pw->shadow_width * 12); if (pw->logo_pixmap) { Window root; int x, y; unsigned int w, h, bw, d; XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d); XSetForeground (si->dpy, gc1, pw->foreground); XSetBackground (si->dpy, gc1, pw->background); XSetClipMask (si->dpy, gc1, pw->logo_clipmask); XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2)); if (d == 1) XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1, 0, 0, w, h, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2), 1); else XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1, 0, 0, w, h, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2)); } /* The thermometer */ XSetForeground (si->dpy, gc1, pw->thermo_foreground); XSetForeground (si->dpy, gc2, pw->thermo_background); pw->thermo_field_x = pw->logo_width + pw->shadow_width; pw->thermo_field_y = pw->shadow_width * 5; pw->thermo_field_height = pw->height - (pw->shadow_width * 10); #if 0 /* Solid border inside the logo box. */ XSetForeground (si->dpy, gc1, pw->foreground); XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1); #endif /* The shadow around the logo */ draw_shaded_rectangle (si->dpy, si->passwd_dialog, pw->shadow_width * 4, pw->shadow_width * 4, pw->logo_width - (pw->shadow_width * 8), pw->logo_height - (pw->shadow_width * 8), pw->shadow_width, pw->shadow_bottom, pw->shadow_top); /* The shadow around the thermometer */ draw_shaded_rectangle (si->dpy, si->passwd_dialog, pw->logo_width, pw->shadow_width * 4, pw->thermo_width + (pw->shadow_width * 2), pw->height - (pw->shadow_width * 8), pw->shadow_width, pw->shadow_bottom, pw->shadow_top); #if 1 /* Solid border inside the thermometer. */ XSetForeground (si->dpy, gc1, pw->foreground); XDrawRectangle (si->dpy, si->passwd_dialog, gc1, pw->thermo_field_x, pw->thermo_field_y, pw->thermo_width - 1, pw->thermo_field_height - 1); #endif /* The shadow around the whole window */ draw_shaded_rectangle (si->dpy, si->passwd_dialog, 0, 0, pw->width, pw->height, pw->shadow_width, pw->shadow_top, pw->shadow_bottom); XFreeGC (si->dpy, gc1); XFreeGC (si->dpy, gc2); update_passwd_window (si, pw->passwd_string, pw->ratio); } static void draw_button(Display *dpy, Drawable dialog, XFontStruct *font, unsigned long foreground, unsigned long background, char *label, int x, int y, int width, int height, int shadow_width, Pixel shadow_light, Pixel shadow_dark, Bool button_down) { XGCValues gcv; GC gc1, gc2; int sw; int label_x, label_y; gcv.foreground = foreground; gcv.font = font->fid; gc1 = XCreateGC(dpy, dialog, GCForeground|GCFont, &gcv); gcv.foreground = background; gc2 = XCreateGC(dpy, dialog, GCForeground, &gcv); XFillRectangle(dpy, dialog, gc2, x, y, width, height); sw = string_width(font, label); label_x = x + ((width - sw) / 2); label_y = (y + (height - (font->ascent + font->descent)) / 2 + font->ascent); if (button_down) { label_x += 2; label_y += 2; } XDrawString(dpy, dialog, gc1, label_x, label_y, label, strlen(label)); XFreeGC(dpy, gc1); XFreeGC(dpy, gc2); draw_shaded_rectangle(dpy, dialog, x, y, width, height, shadow_width, shadow_light, shadow_dark); } static void update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) { passwd_dialog_data *pw = si->pw_data; XGCValues gcv; GC gc1, gc2; int x, y; XRectangle rects[1]; pw->ratio = ratio; gcv.foreground = pw->passwd_foreground; gcv.font = pw->passwd_font->fid; gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv); gcv.foreground = pw->passwd_background; gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv); if (printed_passwd) { char *s = strdup (printed_passwd); if (pw->passwd_string) free (pw->passwd_string); pw->passwd_string = s; } if (pw->prompt_label) { /* the "password" text field */ rects[0].x = pw->passwd_field_x; rects[0].y = pw->passwd_field_y; rects[0].width = pw->passwd_field_width; rects[0].height = pw->passwd_field_height; /* The user entry (password) field is double buffered. * This avoids flickering, particularly in synchronous mode. */ if (pw->passwd_changed_p) { pw->passwd_changed_p = False; if (pw->user_entry_pixmap) { XFreePixmap(si->dpy, pw->user_entry_pixmap); pw->user_entry_pixmap = 0; } pw->user_entry_pixmap = XCreatePixmap (si->dpy, si->passwd_dialog, rects[0].width, rects[0].height, DefaultDepthOfScreen (pw->prompt_screen->screen)); XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2, 0, 0, rects[0].width, rects[0].height); XDrawString (si->dpy, pw->user_entry_pixmap, gc1, pw->shadow_width, pw->passwd_font->ascent, pw->passwd_string, strlen(pw->passwd_string)); /* Ensure the new pixmap gets copied to the window */ pw->i_beam = 0; } /* The I-beam */ if (pw->i_beam == 0) { /* Make the I-beam disappear */ XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2, 0, 0, rects[0].width, rects[0].height, rects[0].x, rects[0].y); } else if (pw->i_beam == 1) { /* Make the I-beam appear */ x = (rects[0].x + pw->shadow_width + string_width (pw->passwd_font, pw->passwd_string)); y = rects[0].y + pw->shadow_width; if (x > rects[0].x + rects[0].width - 1) x = rects[0].x + rects[0].width - 1; XDrawLine (si->dpy, si->passwd_dialog, gc1, x, y, x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1); } pw->i_beam = (pw->i_beam + 1) % 4; } /* the thermometer */ y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio); if (y > 0) { XFillRectangle (si->dpy, si->passwd_dialog, gc2, pw->thermo_field_x + 1, pw->thermo_field_y + 1, pw->thermo_width-2, y); XSetForeground (si->dpy, gc1, pw->thermo_foreground); XFillRectangle (si->dpy, si->passwd_dialog, gc1, pw->thermo_field_x + 1, pw->thermo_field_y + 1 + y, pw->thermo_width-2, MAX (0, pw->thermo_field_height - y - 2)); } if (pw->button_state_changed_p) { pw->button_state_changed_p = False; /* The "Unlock" button */ draw_button(si->dpy, si->passwd_dialog, pw->button_font, pw->button_foreground, pw->button_background, pw->unlock_label, pw->unlock_button_x, pw->unlock_button_y, pw->unlock_button_width, pw->unlock_button_height, pw->shadow_width, (pw->unlock_button_down_p ? pw->shadow_bottom : pw->shadow_top), (pw->unlock_button_down_p ? pw->shadow_top : pw->shadow_bottom), pw->unlock_button_down_p); /* The "New Login" button */ if (pw->login_button_p) { draw_button(si->dpy, si->passwd_dialog, pw->button_font, (pw->login_button_enabled_p ? pw->passwd_foreground : pw->shadow_bottom), pw->button_background, pw->login_label, pw->login_button_x, pw->login_button_y, pw->login_button_width, pw->login_button_height, pw->shadow_width, (pw->login_button_down_p ? pw->shadow_bottom : pw->shadow_top), (pw->login_button_down_p ? pw->shadow_top : pw->shadow_bottom), pw->login_button_down_p); } } XFreeGC (si->dpy, gc1); XFreeGC (si->dpy, gc2); XSync (si->dpy, False); } void restore_background (saver_info *si) { passwd_dialog_data *pw = si->pw_data; saver_screen_info *ssi = pw->prompt_screen; XGCValues gcv; GC gc; gcv.function = GXcopy; gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv); XCopyArea (si->dpy, pw->save_under, ssi->screensaver_window, gc, 0, 0, ssi->width, ssi->height, 0, 0); XFreeGC (si->dpy, gc); } /* Frees anything created by make_passwd_window */ static void cleanup_passwd_window (saver_info *si) { passwd_dialog_data *pw; if (!(pw = si->pw_data)) return; if (pw->info_label) { mlstring_free(pw->info_label); pw->info_label = 0; } if (pw->prompt_label) { mlstring_free(pw->prompt_label); pw->prompt_label = 0; } memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd)); memset (pw->typed_passwd_char_size, 0, sizeof(pw->typed_passwd_char_size)); memset (pw->passwd_string, 0, strlen(pw->passwd_string)); if (pw->timer) { XtRemoveTimeOut (pw->timer); pw->timer = 0; } if (pw->user_entry_pixmap) { XFreePixmap(si->dpy, pw->user_entry_pixmap); pw->user_entry_pixmap = 0; } } static void destroy_passwd_window (saver_info *si) { saver_preferences *p = &si->prefs; passwd_dialog_data *pw = si->pw_data; saver_screen_info *ssi = pw->prompt_screen; Colormap cmap = DefaultColormapOfScreen (ssi->screen); Pixel black = BlackPixelOfScreen (ssi->screen); Pixel white = WhitePixelOfScreen (ssi->screen); XEvent event; cleanup_passwd_window (si); if (si->cached_passwd) { char *wipe = si->cached_passwd; while (*wipe) *wipe++ = '\0'; free(si->cached_passwd); si->cached_passwd = NULL; } move_mouse_grab (si, RootWindowOfScreen (ssi->screen), ssi->cursor, ssi->number); if (pw->passwd_cursor) XFreeCursor (si->dpy, pw->passwd_cursor); if (p->verbose_p) fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n", blurb(), ssi->number, pw->previous_mouse_x, pw->previous_mouse_y); XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen), 0, 0, 0, 0, pw->previous_mouse_x, pw->previous_mouse_y); XSync (si->dpy, False); while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event)) if (p->verbose_p) fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb()); #ifdef HAVE_XINPUT if (si->using_xinput_extension && si->xinput_DeviceMotionNotify) while (XCheckTypedEvent (si->dpy, si->xinput_DeviceMotionNotify, &event)) if (p->verbose_p) fprintf (stderr, "%s: discarding DeviceMotionNotify event.\n", blurb()); #endif if (si->passwd_dialog) { if (si->prefs.verbose_p) fprintf (stderr, "%s: %d: destroying password dialog.\n", blurb(), pw->prompt_screen->number); XDestroyWindow (si->dpy, si->passwd_dialog); si->passwd_dialog = 0; } if (pw->save_under) { restore_background(si); XFreePixmap (si->dpy, pw->save_under); pw->save_under = 0; } if (pw->heading_label) free (pw->heading_label); if (pw->body_label) free (pw->body_label); if (pw->user_label) free (pw->user_label); if (pw->date_label) free (pw->date_label); if (pw->login_label) free (pw->login_label); if (pw->unlock_label) free (pw->unlock_label); if (pw->passwd_string) free (pw->passwd_string); if (pw->uname_label) free (pw->uname_label); if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font); if (pw->body_font) XFreeFont (si->dpy, pw->body_font); if (pw->label_font) XFreeFont (si->dpy, pw->label_font); if (pw->passwd_font) XFreeFont (si->dpy, pw->passwd_font); if (pw->date_font) XFreeFont (si->dpy, pw->date_font); if (pw->button_font) XFreeFont (si->dpy, pw->button_font); if (pw->uname_font) XFreeFont (si->dpy, pw->uname_font); if (pw->foreground != black && pw->foreground != white) XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L); if (pw->background != black && pw->background != white) XFreeColors (si->dpy, cmap, &pw->background, 1, 0L); if (!(pw->button_foreground == black || pw->button_foreground == white)) XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L); if (!(pw->button_background == black || pw->button_background == white)) XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L); if (pw->passwd_foreground != black && pw->passwd_foreground != white) XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L); if (pw->passwd_background != black && pw->passwd_background != white) XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L); if (pw->thermo_foreground != black && pw->thermo_foreground != white) XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L); if (pw->thermo_background != black && pw->thermo_background != white) XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L); if (pw->shadow_top != black && pw->shadow_top != white) XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L); if (pw->shadow_bottom != black && pw->shadow_bottom != white) XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L); if (pw->logo_pixmap) XFreePixmap (si->dpy, pw->logo_pixmap); if (pw-> logo_clipmask) XFreePixmap (si->dpy, pw->logo_clipmask); if (pw->logo_pixels) { if (pw->logo_npixels) XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L); free (pw->logo_pixels); pw->logo_pixels = 0; pw->logo_npixels = 0; } if (pw->save_under) XFreePixmap (si->dpy, pw->save_under); if (cmap) XInstallColormap (si->dpy, cmap); memset (pw, 0, sizeof(*pw)); free (pw); si->pw_data = 0; } static Bool error_handler_hit_p = False; static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) { error_handler_hit_p = True; return 0; } #ifdef HAVE_XHPDISABLERESET /* This function enables and disables the C-Sh-Reset hot-key, which normally resets the X server (logging out the logged-in user.) We don't want random people to be able to do that while the screen is locked. */ static void hp_lock_reset (saver_info *si, Bool lock_p) { static Bool hp_locked_p = False; /* Calls to XHPDisableReset and XHPEnableReset must be balanced, or BadAccess errors occur. (It's ok for this to be global, since it affects the whole machine, not just the current screen.) */ if (hp_locked_p == lock_p) return; if (lock_p) XHPDisableReset (si->dpy); else XHPEnableReset (si->dpy); hp_locked_p = lock_p; } #endif /* HAVE_XHPDISABLERESET */ #ifdef HAVE_XF86MISCSETGRABKEYSSTATE /* This function enables and disables the Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any grabs and/or kill the grabbing client. That would effectively unlock the screen, so we don't like that. The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on in XF86Config. I believe they are disabled by default. This does not affect any other keys (specifically Ctrl-Alt-BS or Ctrl-Alt-F1) but I wish it did. Maybe it will someday. */ static void xfree_lock_grab_smasher (saver_info *si, Bool lock_p) { saver_preferences *p = &si->prefs; int status; int event, error; XErrorHandler old_handler; if (!XF86MiscQueryExtension(si->dpy, &event, &error)) return; XSync (si->dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); XSync (si->dpy, False); status = XF86MiscSetGrabKeysState (si->dpy, !lock_p); XSync (si->dpy, False); if (error_handler_hit_p) status = 666; if (!lock_p && status == MiscExtGrabStateAlready) status = MiscExtGrabStateSuccess; /* shut up, consider this success */ if (p->verbose_p && status != MiscExtGrabStateSuccess) fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n", blurb(), !lock_p, (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" : status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" : status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" : status == 666 ? "an X error" : "unknown value")); XSync (si->dpy, False); XSetErrorHandler (old_handler); XSync (si->dpy, False); } #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */ /* This function enables and disables the C-Alt-Plus and C-Alt-Minus hot-keys, which normally change the resolution of the X server. We don't want people to be able to switch the server resolution while the screen is locked, because if they switch to a higher resolution, it could cause part of the underlying desktop to become exposed. */ #ifdef HAVE_XF86VMODE static void xfree_lock_mode_switch (saver_info *si, Bool lock_p) { static Bool any_mode_locked_p = False; saver_preferences *p = &si->prefs; int screen; int real_nscreens = ScreenCount (si->dpy); int event, error; Bool status; XErrorHandler old_handler; if (any_mode_locked_p == lock_p) return; if (!XF86VidModeQueryExtension (si->dpy, &event, &error)) return; for (screen = 0; screen < real_nscreens; screen++) { XSync (si->dpy, False); old_handler = XSetErrorHandler (ignore_all_errors_ehandler); error_handler_hit_p = False; status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p); XSync (si->dpy, False); XSetErrorHandler (old_handler); if (error_handler_hit_p) status = False; if (status) any_mode_locked_p = lock_p; if (!status && (p->verbose_p || !lock_p)) /* Only print this when verbose, or when we locked but can't unlock. I tried printing this message whenever it comes up, but mode-locking always fails if DontZoom is set in XF86Config. */ fprintf (stderr, "%s: %d: unable to %s mode switching!\n", blurb(), screen, (lock_p ? "lock" : "unlock")); else if (p->verbose_p) fprintf (stderr, "%s: %d: %s mode switching.\n", blurb(), screen, (lock_p ? "locked" : "unlocked")); } } #endif /* HAVE_XF86VMODE */ /* If the viewport has been scrolled since the screen was blanked, then scroll it back to where it belongs. This function only exists to patch over a very brief race condition. */ static void undo_vp_motion (saver_info *si) { #ifdef HAVE_XF86VMODE saver_preferences *p = &si->prefs; int screen; int real_nscreens = ScreenCount (si->dpy); int event, error; if (!XF86VidModeQueryExtension (si->dpy, &event, &error)) return; for (screen = 0; screen < real_nscreens; screen++) { saver_screen_info *ssi = &si->screens[screen]; int x, y; Bool status; if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1) break; if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y)) return; if (ssi->blank_vp_x == x && ssi->blank_vp_y == y) return; /* We're going to move the viewport. The mouse has just been grabbed on (and constrained to, thus warped to) the password window, so it is no longer near the edge of the screen. However, wait a bit anyway, just to make sure the server drains its last motion event, so that the screen doesn't continue to scroll after we've reset the viewport. */ XSync (si->dpy, False); usleep (250000); /* 1/4 second */ XSync (si->dpy, False); status = XF86VidModeSetViewPort (si->dpy, screen, ssi->blank_vp_x, ssi->blank_vp_y); if (!status) fprintf (stderr, "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n", blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y); else if (p->verbose_p) fprintf (stderr, "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n", blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y); } #endif /* HAVE_XF86VMODE */ } /* Interactions */ static void passwd_animate_timer (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; int tick = 166; passwd_dialog_data *pw = si->pw_data; if (!pw) return; pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick)); if (pw->ratio < 0) { pw->ratio = 0; if (si->unlock_state == ul_read) si->unlock_state = ul_time; } update_passwd_window (si, 0, pw->ratio); if (si->unlock_state == ul_read) pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer, (XtPointer) si); else pw->timer = 0; idle_timer ((XtPointer) si, 0); } static XComposeStatus *compose_status; static void handle_login_button (saver_info *si, XEvent *event) { saver_preferences *p = &si->prefs; Bool mouse_in_box = False; Bool hit_p = False; passwd_dialog_data *pw = si->pw_data; saver_screen_info *ssi = pw->prompt_screen; if (! pw->login_button_enabled_p) return; mouse_in_box = (event->xbutton.x >= pw->login_button_x && event->xbutton.x <= pw->login_button_x + pw->login_button_width && event->xbutton.y >= pw->login_button_y && event->xbutton.y <= pw->login_button_y + pw->login_button_height); if (ButtonRelease == event->xany.type && pw->login_button_down_p && mouse_in_box) { /* Only allow them to press the button once: don't want to accidentally launch a dozen gdm choosers if the machine is being slow. */ hit_p = True; pw->login_button_enabled_p = False; } pw->login_button_down_p = (mouse_in_box && ButtonRelease != event->xany.type); update_passwd_window (si, 0, pw->ratio); if (hit_p) fork_and_exec (ssi, p->new_login_command); } static void handle_unlock_button (saver_info *si, XEvent *event) { Bool mouse_in_box = False; passwd_dialog_data *pw = si->pw_data; mouse_in_box = (event->xbutton.x >= pw->unlock_button_x && event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width && event->xbutton.y >= pw->unlock_button_y && event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height); if (ButtonRelease == event->xany.type && pw->unlock_button_down_p && mouse_in_box) finished_typing_passwd (si, pw); pw->unlock_button_down_p = (mouse_in_box && ButtonRelease != event->xany.type); } static void finished_typing_passwd (saver_info *si, passwd_dialog_data *pw) { if (si->unlock_state == ul_read) { update_passwd_window (si, "Checking...", pw->ratio); XSync (si->dpy, False); si->unlock_state = ul_finished; update_passwd_window (si, "", pw->ratio); } } static void handle_passwd_key (saver_info *si, XKeyEvent *event) { passwd_dialog_data *pw = si->pw_data; unsigned char decoded [MAX_BYTES_PER_CHAR * 10]; /* leave some slack */ KeySym keysym = 0; /* XLookupString may return more than one character via XRebindKeysym; and on some systems it returns multi-byte UTF-8 characters (contrary to its documentation, which says it returns only Latin1.) It seems to only do so, however, if setlocale() has been called. See the code inside ENABLE_NLS in xscreensaver.c. */ int decoded_size = XLookupString (event, (char *)decoded, sizeof(decoded), &keysym, compose_status); #if 0 { const char *ks = XKeysymToString (keysym); int i; fprintf(stderr, "## %-12s\t=> %d\t", (ks ? ks : "(null)"), decoded_size); for (i = 0; i < decoded_size; i++) fprintf(stderr, "%c", decoded[i]); fprintf(stderr, "\t"); for (i = 0; i < decoded_size; i++) fprintf(stderr, "\\%03o", ((unsigned char *)decoded)[i]); fprintf(stderr, "\n"); } #endif if (decoded_size > MAX_BYTES_PER_CHAR) { /* The multi-byte character returned is too large. */ XBell (si->dpy, 0); return; } decoded[decoded_size] = 0; pw->passwd_changed_p = True; /* Add 10% to the time remaining every time a key is pressed. */ pw->ratio += 0.1; if (pw->ratio > 1) pw->ratio = 1; if (decoded_size == 1) /* Handle single-char commands */ { switch (*decoded) { case '\010': case '\177': /* Backspace */ { /* kludgey way to get the number of "logical" characters. */ int nchars = strlen (pw->typed_passwd_char_size); int nbytes = strlen (pw->typed_passwd); if (nbytes <= 0) XBell (si->dpy, 0); else { int i; for (i = pw->typed_passwd_char_size[nchars-1]; i >= 0; i--) { if (nbytes < 0) abort(); pw->typed_passwd[nbytes--] = 0; } pw->typed_passwd_char_size[nchars-1] = 0; } } break; case '\012': case '\015': /* Enter */ finished_typing_passwd (si, pw); break; case '\033': /* Escape */ si->unlock_state = ul_cancel; break; case '\025': case '\030': /* Erase line */ memset (pw->typed_passwd, 0, sizeof (pw->typed_passwd)); memset (pw->typed_passwd_char_size, 0, sizeof (pw->typed_passwd_char_size)); break; default: if (*decoded < ' ' && *decoded != '\t') /* Other ctrl char */ XBell (si->dpy, 0); else goto SELF_INSERT; break; } } else { int nbytes, nchars; SELF_INSERT: nbytes = strlen (pw->typed_passwd); nchars = strlen (pw->typed_passwd_char_size); if (nchars + 1 >= sizeof (pw->typed_passwd_char_size)-1 || nbytes + decoded_size >= sizeof (pw->typed_passwd)-1) /* overflow */ XBell (si->dpy, 0); else { pw->typed_passwd_char_size[nchars] = decoded_size; pw->typed_passwd_char_size[nchars+1] = 0; memcpy (pw->typed_passwd + nbytes, decoded, decoded_size); pw->typed_passwd[nbytes + decoded_size] = 0; } } if (pw->echo_input) { /* If the input is wider than the text box, only show the last portion, to simulate a horizontally-scrolling text field. */ int chars_in_pwfield = (pw->passwd_field_width / pw->passwd_font->max_bounds.width); const char *output = pw->typed_passwd; if (strlen(output) > chars_in_pwfield) output += (strlen(output) - chars_in_pwfield); update_passwd_window (si, output, pw->ratio); } else if (pw->show_stars_p) { int nchars = strlen (pw->typed_passwd_char_size); char *stars = 0; stars = (char *) malloc(nchars + 1); memset (stars, '*', nchars); stars[nchars] = 0; update_passwd_window (si, stars, pw->ratio); free (stars); } else { update_passwd_window (si, "", pw->ratio); } } static void passwd_event_loop (saver_info *si) { saver_preferences *p = &si->prefs; char *msg = 0; /* We have to go through this union bullshit because gcc-4.4.0 has stricter struct-aliasing rules. Without this, the optimizer can fuck things up. */ union { XEvent x_event; # ifdef HAVE_RANDR XRRScreenChangeNotifyEvent xrr_event; # endif /* HAVE_RANDR */ } event; passwd_animate_timer ((XtPointer) si, 0); while (si->unlock_state == ul_read) { XtAppNextEvent (si->app, &event.x_event); #ifdef HAVE_RANDR if (si->using_randr_extension && (event.x_event.type == (si->randr_event_number + RRScreenChangeNotify))) { /* The Resize and Rotate extension sends an event when the size, rotation, or refresh rate of any screen has changed. */ if (p->verbose_p) { /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */ int screen = XRRRootToScreen(si->dpy, event.xrr_event.window); fprintf (stderr, "%s: %d: screen change event received\n", blurb(), screen); } #ifdef RRScreenChangeNotifyMask /* Inform Xlib that it's ok to update its data structures. */ XRRUpdateConfiguration(&event.x_event); /* Xrandr.h 1.9, 2002/09/29*/ #endif /* RRScreenChangeNotifyMask */ /* Resize the existing xscreensaver windows and cached ssi data. */ if (update_screen_layout (si)) { if (p->verbose_p) { fprintf (stderr, "%s: new layout:\n", blurb()); describe_monitor_layout (si); } resize_screensaver_window (si); } } else #endif /* HAVE_RANDR */ if (event.x_event.xany.window == si->passwd_dialog && event.x_event.xany.type == Expose) draw_passwd_window (si); else if (event.x_event.xany.type == KeyPress) { handle_passwd_key (si, &event.x_event.xkey); si->pw_data->caps_p = (event.x_event.xkey.state & LockMask); } else if (event.x_event.xany.type == ButtonPress || event.x_event.xany.type == ButtonRelease) { si->pw_data->button_state_changed_p = True; handle_unlock_button (si, &event.x_event); if (si->pw_data->login_button_p) handle_login_button (si, &event.x_event); } else XtDispatchEvent (&event.x_event); } switch (si->unlock_state) { case ul_cancel: msg = ""; break; case ul_time: msg = "Timed out!"; break; case ul_finished: msg = "Checking..."; break; default: msg = 0; break; } if (p->verbose_p) switch (si->unlock_state) { case ul_cancel: fprintf (stderr, "%s: input cancelled.\n", blurb()); break; case ul_time: fprintf (stderr, "%s: input timed out.\n", blurb()); break; case ul_finished: fprintf (stderr, "%s: input finished.\n", blurb()); break; default: break; } if (msg) { si->pw_data->i_beam = 0; update_passwd_window (si, msg, 0.0); XSync (si->dpy, False); /* Swallow all pending KeyPress/KeyRelease events. */ { XEvent e; while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e)) ; } } } static void handle_typeahead (saver_info *si) { passwd_dialog_data *pw = si->pw_data; int i; if (!si->unlock_typeahead) return; pw->passwd_changed_p = True; i = strlen (si->unlock_typeahead); if (i >= sizeof(pw->typed_passwd) - 1) i = sizeof(pw->typed_passwd) - 1; memcpy (pw->typed_passwd, si->unlock_typeahead, i); pw->typed_passwd [i] = 0; { int j; char *c = pw->typed_passwd_char_size; for (j = 0; j < i; j++) *c++ = 1; *c = 0; } memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead)); si->unlock_typeahead[i] = 0; update_passwd_window (si, si->unlock_typeahead, pw->ratio); free (si->unlock_typeahead); si->unlock_typeahead = 0; } /** * Returns a copy of the input string with trailing whitespace removed. * Whitespace is anything considered so by isspace(). * It is safe to call this with NULL, in which case NULL will be returned. * The returned string (if not NULL) should be freed by the caller with free(). */ static char * remove_trailing_whitespace(const char *str) { size_t len; char *newstr, *chr; if (!str) return NULL; len = strlen(str); newstr = malloc(len + 1); if (!newstr) return NULL; (void) strcpy(newstr, str); chr = newstr + len; while (isspace(*--chr) && chr >= newstr) *chr = '\0'; return newstr; } /* * The authentication conversation function. * Like a PAM conversation function, this accepts multiple messages in a single * round. It then splits them into individual messages for display on the * passwd dialog. A message sequence of info or error followed by a prompt will * be reduced into a single dialog window. * * Returns 0 on success or -1 if some problem occurred (cancelled, OOM, etc.) */ int gui_auth_conv(int num_msg, const struct auth_message auth_msgs[], struct auth_response **resp, saver_info *si) { int i; const char *info_msg, *prompt; struct auth_response *responses; if (si->unlock_state == ul_cancel || si->unlock_state == ul_time) /* If we've already cancelled or timed out in this PAM conversation, don't prompt again even if PAM asks us to! */ return -1; if (!(responses = calloc(num_msg, sizeof(struct auth_response)))) goto fail; for (i = 0; i < num_msg; ++i) { info_msg = prompt = NULL; /* See if there is a following message that can be shown at the same * time */ if (auth_msgs[i].type == AUTH_MSGTYPE_INFO && i+1 < num_msg && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO) ) { info_msg = auth_msgs[i].msg; prompt = auth_msgs[++i].msg; } else { if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO || auth_msgs[i].type == AUTH_MSGTYPE_ERROR) info_msg = auth_msgs[i].msg; else prompt = auth_msgs[i].msg; } { char *info_msg_trimmed, *prompt_trimmed; /* Trailing whitespace looks bad in a GUI */ info_msg_trimmed = remove_trailing_whitespace(info_msg); prompt_trimmed = remove_trailing_whitespace(prompt); if (make_passwd_window(si, info_msg_trimmed, prompt_trimmed, auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO ? True : False) < 0) goto fail; if (info_msg_trimmed) free(info_msg_trimmed); if (prompt_trimmed) free(prompt_trimmed); } compose_status = calloc (1, sizeof (*compose_status)); if (!compose_status) goto fail; si->unlock_state = ul_read; handle_typeahead (si); passwd_event_loop (si); if (si->unlock_state == ul_cancel) goto fail; responses[i].response = strdup(si->pw_data->typed_passwd); /* Cache the first response to a PROMPT_NOECHO to save prompting for * each auth mechanism. */ if (si->cached_passwd == NULL && auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO) si->cached_passwd = strdup(responses[i].response); free (compose_status); compose_status = 0; } *resp = responses; return (si->unlock_state == ul_finished) ? 0 : -1; fail: if (compose_status) free (compose_status); compose_status = 0; if (responses) { for (i = 0; i < num_msg; ++i) if (responses[i].response) free (responses[i].response); free (responses); } return -1; } void auth_finished_cb (saver_info *si) { char buf[1024]; const char *s; /* If we have something to say, put the dialog back up for a few seconds to display it. Otherwise, don't bother. */ if (si->unlock_state == ul_fail && /* failed with caps lock on */ si->pw_data && si->pw_data->caps_p) s = "Authentication failed (Caps Lock?)"; else if (si->unlock_state == ul_fail) /* failed without caps lock */ s = "Authentication failed!"; else if (si->unlock_state == ul_success && /* good, but report failures */ si->unlock_failures > 0) { if (si->unlock_failures == 1) s = "There has been\n1 failed login attempt."; else { sprintf (buf, "There have been\n%d failed login attempts.", si->unlock_failures); s = buf; } si->unlock_failures = 0; } else /* good, with no failures, */ goto END; /* or timeout, or cancel. */ make_passwd_window (si, s, NULL, True); XSync (si->dpy, False); { int secs = 4; time_t start = time ((time_t *) 0); XEvent event; while (time ((time_t *) 0) < start + secs) if (XPending (si->dpy)) { XNextEvent (si->dpy, &event); if (event.xany.window == si->passwd_dialog && event.xany.type == Expose) draw_passwd_window (si); else if (event.xany.type == ButtonPress || event.xany.type == KeyPress) break; XSync (si->dpy, False); } else usleep (250000); /* 1/4 second */ } END: if (si->pw_data) destroy_passwd_window (si); } Bool unlock_p (saver_info *si) { saver_preferences *p = &si->prefs; if (!si->unlock_cb) { fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb()); return False; } raise_window (si, True, True, True); xss_authenticate(si, p->verbose_p); return (si->unlock_state == ul_success); } void set_locked_p (saver_info *si, Bool locked_p) { si->locked_p = locked_p; #ifdef HAVE_XHPDISABLERESET hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */ #endif #ifdef HAVE_XF86VMODE xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */ #endif #ifdef HAVE_XF86MISCSETGRABKEYSSTATE xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */ #endif store_saver_status (si); /* store locked-p */ } #else /* NO_LOCKING -- whole file */ void set_locked_p (saver_info *si, Bool locked_p) { if (locked_p) abort(); } #endif /* !NO_LOCKING */ xscreensaver-5.15/driver/mlstring.c000066400000000000000000000120601164314150500174510ustar00rootroot00000000000000/* * (c) 2007, Quest Software, Inc. All rights reserved. * * This file is part of XScreenSaver, * Copyright (c) 1993-2009 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #include #include #include #include "mlstring.h" #define LINE_SPACING 1.2 static mlstring * mlstring_allocate(const char *msg); static void mlstring_calculate(mlstring *str, XFontStruct *font); mlstring* mlstring_new(const char *msg, XFontStruct *font, Dimension wrap_width) { mlstring *newstr; if (!(newstr = mlstring_allocate(msg))) return NULL; newstr->font_id = font->fid; mlstring_wrap(newstr, font, wrap_width); return newstr; } mlstring * mlstring_allocate(const char *msg) { const char *s; mlstring *ml; struct mlstr_line *cur, *prev = NULL; size_t linelength; int the_end = 0; if (!msg) return NULL; ml = calloc(1, sizeof(mlstring)); if (!ml) return NULL; for (s = msg; !the_end; msg = ++s) { /* New string struct */ cur = calloc(1, sizeof(struct mlstr_line)); if (!cur) goto fail; if (!ml->lines) ml->lines = cur; /* Find the \n or end of string */ while (*s != '\n') { if (*s == '\0') { the_end = 1; break; } ++s; } linelength = s - msg; /* Duplicate the string */ cur->line = malloc(linelength + 1); if (!cur->line) goto fail; strncpy(cur->line, msg, linelength); cur->line[linelength] = '\0'; if (prev) prev->next_line = cur; prev = cur; } return ml; fail: if (ml) mlstring_free(ml); return NULL; } /* * Frees an mlstring. * This function does not have any unit tests. */ void mlstring_free(mlstring *str) { struct mlstr_line *cur, *next; for (cur = str->lines; cur; cur = next) { next = cur->next_line; free(cur->line); free(cur); } free(str); } void mlstring_wrap(mlstring *mstring, XFontStruct *font, Dimension width) { short char_width = font->max_bounds.width; int line_length, wrap_at; struct mlstr_line *mstr, *newml; /* An alternative implementation of this function would be to keep trying * XTextWidth() on space-delimited substrings until the longest one less * than 'width' is found, however there shouldn't be much difference * between that, and this implementation. */ for (mstr = mstring->lines; mstr; mstr = mstr->next_line) { if (XTextWidth(font, mstr->line, strlen(mstr->line)) > width) { /* Wrap it */ line_length = width / char_width; if (line_length == 0) line_length = 1; /* First try to soft wrap by finding a space */ for (wrap_at = line_length; wrap_at >= 0 && !isspace(mstr->line[wrap_at]); --wrap_at); if (wrap_at == -1) /* No space found, hard wrap */ wrap_at = line_length; else wrap_at++; /* Leave the space at the end of the line. */ newml = calloc(1, sizeof(*newml)); if (!newml) /* OOM, don't bother trying to wrap */ break; if (NULL == (newml->line = strdup(mstr->line + wrap_at))) { /* OOM, jump ship */ free(newml); break; } /* Terminate the existing string at its end */ mstr->line[wrap_at] = '\0'; newml->next_line = mstr->next_line; mstr->next_line = newml; } } mlstring_calculate(mstring, font); } #undef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) /* * Calculates the overall extents (width + height of the multi-line string). * This function is called as part of mlstring_new(). * It does not have any unit testing. */ void mlstring_calculate(mlstring *str, XFontStruct *font) { struct mlstr_line *line; str->font_height = font->ascent + font->descent; str->overall_height = 0; str->overall_width = 0; /* XXX: Should there be some baseline calculations to help XDrawString later on? */ str->font_ascent = font->ascent; for (line = str->lines; line; line = line->next_line) { line->line_width = XTextWidth(font, line->line, strlen(line->line)); str->overall_width = MAX(str->overall_width, line->line_width); /* Don't add line spacing for the first line */ str->overall_height += (font->ascent + font->descent) * (line == str->lines ? 1 : LINE_SPACING); } } void mlstring_draw(Display *dpy, Drawable dialog, GC gc, mlstring *string, int x, int y) { struct mlstr_line *line; if (!string) return; y += string->font_ascent; XSetFont(dpy, gc, string->font_id); for (line = string->lines; line; line = line->next_line) { XDrawString(dpy, dialog, gc, x, y, line->line, strlen(line->line)); y += string->font_height * LINE_SPACING; } } /* vim:ts=8:sw=2:noet */ xscreensaver-5.15/driver/mlstring.h000066400000000000000000000030141164314150500174550ustar00rootroot00000000000000/* mlstring.h --- Multi-line strings for use with Xlib * * (c) 2007, Quest Software, Inc. All rights reserved. * * This file is part of XScreenSaver, * Copyright (c) 1993-2004 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifndef MLSTRING_H #define MLSTRING_H #include /* mlstring means multi-line string */ struct mlstr_line; typedef struct mlstring mlstring; struct mlstring { struct mlstr_line *lines; /* linked list */ Dimension overall_height; Dimension overall_width; /* XXX: Perhaps it is simpler to keep a reference to the XFontStruct */ int font_ascent; int font_height; Font font_id; }; struct mlstr_line { char *line; Dimension line_width; struct mlstr_line *next_line; }; mlstring * mlstring_new(const char *str, XFontStruct *font, Dimension wrap_width); /* Does not have to be called manually */ void mlstring_wrap(mlstring *mstr, XFontStruct *font, Dimension width); void mlstring_free(mlstring *str); void mlstring_draw(Display *dpy, Drawable dialog, GC gc, mlstring *string, int x, int y); #endif /* vim:ts=8:sw=2:noet */ xscreensaver-5.15/driver/passwd-helper.c000066400000000000000000000102741164314150500203750ustar00rootroot00000000000000/* passwd-helper.c --- verifying typed passwords with external helper program * written by Olaf Kirch * xscreensaver, Copyright (c) 1993-2005 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* The idea here is to be able to run xscreensaver without any setuid bits. * Password verification happens through an external program that you feed * your password to on stdin. The external command is invoked with a user * name argument. * * The external helper does whatever authentication is necessary. Currently, * SuSE uses "unix2_chkpwd", which is a variation of "unix_chkpwd" from the * PAM distribution. * * Normally, the password helper should just authenticate the calling user * (i.e. based on the caller's real uid). This is in order to prevent * brute-forcing passwords in a shadow environment. A less restrictive * approach would be to allow verifying other passwords as well, but always * with a 2 second delay or so. (Not sure what SuSE's "unix2_chkpwd" * currently does.) * -- Olaf Kirch , 16-Dec-2003 */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifndef NO_LOCKING /* whole file */ #include /* not used for much... */ /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer #define XtAppContext void* #define XrmDatabase void* #define XtIntervalId void* #define XtPointer void* #define Widget void* #include "xscreensaver.h" #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include static int ext_run (const char *user, const char *typed_passwd, int verbose_p) { int pfd[2], status; pid_t pid; if (pipe(pfd) < 0) return 0; if (verbose_p) fprintf (stderr, "%s: ext_run (%s, %s)\n", blurb(), PASSWD_HELPER_PROGRAM, user); block_sigchld(); if ((pid = fork()) < 0) { close(pfd[0]); close(pfd[1]); return 0; } if (pid == 0) { close(pfd[1]); if (pfd[0] != 0) dup2(pfd[0], 0); /* Helper is invoked as helper service-name [user] */ execlp(PASSWD_HELPER_PROGRAM, PASSWD_HELPER_PROGRAM, "xscreensaver", user, NULL); if (verbose_p) fprintf(stderr, "%s: %s\n", PASSWD_HELPER_PROGRAM, strerror(errno)); exit(1); } close(pfd[0]); /* Write out password to helper process */ if (!typed_passwd) typed_passwd = ""; write(pfd[1], typed_passwd, strlen(typed_passwd)); close(pfd[1]); while (waitpid(pid, &status, 0) < 0) { if (errno == EINTR) continue; if (verbose_p) fprintf(stderr, "%s: ext_run: waitpid failed: %s\n", blurb(), strerror(errno)); unblock_sigchld(); return 0; } unblock_sigchld(); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return 0; return 1; } /* This can be called at any time, and says whether the typed password belongs to either the logged in user (real uid, not effective); or to root. */ int ext_passwd_valid_p (const char *typed_passwd, int verbose_p) { struct passwd *pw; int res = 0; if ((pw = getpwuid(getuid())) != NULL) res = ext_run (pw->pw_name, typed_passwd, verbose_p); endpwent(); #ifdef ALLOW_ROOT_PASSWD if (!res) res = ext_run ("root", typed_passwd, verbose_p); #endif /* ALLOW_ROOT_PASSWD */ return res; } int ext_priv_init (int argc, char **argv, int verbose_p) { /* Make sure the passwd helper exists */ if (access(PASSWD_HELPER_PROGRAM, X_OK) < 0) { fprintf(stderr, "%s: warning: %s does not exist.\n" "%s: password authentication via " "external helper will not work.\n", blurb(), PASSWD_HELPER_PROGRAM, blurb()); return 0; } return 1; } #endif /* NO_LOCKING -- whole file */ xscreensaver-5.15/driver/passwd-kerberos.c000066400000000000000000000164411164314150500207340ustar00rootroot00000000000000/* kpasswd.c --- verify kerberos passwords. * written by Nat Lanza (magus@cs.cmu.edu) for * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifndef NO_LOCKING /* whole file */ #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include /* I'm not sure if this is exactly the right test... Might __APPLE__ be defined if this is apple hardware, but not an Apple OS? Thanks to Alexei Kosut for the MacOS X code. */ #ifdef __APPLE__ # define HAVE_DARWIN #endif #if defined(HAVE_DARWIN) # include #elif defined(HAVE_KERBEROS5) # include # include #else /* !HAVE_KERBEROS5 (meaning Kerberos 4) */ # include # include #endif /* !HAVE_KERBEROS5 */ #if !defined(VMS) && !defined(HAVE_ADJUNCT_PASSWD) # include #endif #ifdef __bsdi__ # include # if _BSDI_VERSION >= 199608 # define BSD_AUTH # endif #endif /* __bsdi__ */ /* blargh */ #undef Bool #undef True #undef False #define Bool int #define True 1 #define False 0 /* The user information we need to store */ #ifdef HAVE_DARWIN static KLPrincipal princ; #else /* !HAVE_DARWIN */ static char realm[REALM_SZ]; static char name[ANAME_SZ]; static char inst[INST_SZ]; static const char *tk_file; #endif /* !HAVE_DARWIN */ /* warning suppression: duplicated in passwd.c */ extern Bool kerberos_lock_init (int argc, char **argv, Bool verbose_p); extern Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p); /* Called at startup to grab user, instance, and realm information from the user's ticketfile (remember, name.inst@realm). Since we're using tf_get_pname(), this should work even if your kerberos username isn't the same as your local username. We grab the ticket at startup time so that even if your ticketfile dies while the screen's locked we'll still have the information to unlock it. Problems: the password dialog currently displays local username, so if you have some non-standard name/instance when you run xscreensaver, you'll need to remember what it was when unlocking, or else you lose. Also, we use des_string_to_key(), so if you have an AFS password (encrypted with ka_StringToKey()), you'll lose. Get a kerberos password; it isn't that hard. Like the original lock_init, we return false if something went wrong. We don't use the arguments we're given, though. */ Bool kerberos_lock_init (int argc, char **argv, Bool verbose_p) { # ifdef HAVE_DARWIN KLBoolean found; return ((klNoErr == (KLCacheHasValidTickets (NULL, kerberosVersion_Any, &found, &princ, NULL))) && found); # else /* !HAVE_DARWIN */ /* Perhaps we should be doing it the Mac way (above) all the time? The following code assumes Unix-style file-based Kerberos credentials cache, which Mac OS X doesn't use. But is there any real reason to do it this way at all, even on other Unixen? */ int k_errno; memset(name, 0, sizeof(name)); memset(inst, 0, sizeof(inst)); /* find out where the user's keeping his tickets. squirrel it away for later use. */ tk_file = tkt_string(); /* open ticket file or die trying. */ if ((k_errno = tf_init(tk_file, R_TKT_FIL))) { return False; } /* same with principal and instance names */ if ((k_errno = tf_get_pname(name)) || (k_errno = tf_get_pinst(inst))) { return False; } /* close the ticketfile to release the lock on it. */ tf_close(); /* figure out what realm we're authenticated to. this ought to be the local realm, but it pays to be sure. */ if ((k_errno = krb_get_tf_realm(tk_file, realm))) { return False; } /* last-minute sanity check on what we got. */ if ((strlen(name)+strlen(inst)+strlen(realm)+3) > (REALM_SZ + ANAME_SZ + INST_SZ + 3)) { return False; } /* success */ return True; # endif /* !HAVE_DARWIN */ } /* des_string_to_key() wants this. If C didn't suck, we could have an anonymous function do this. Even a local one. But it does, so here we are. Calling it ive_got_your_local_function_right_here_buddy() would have been rude. */ #ifndef HAVE_DARWIN static int key_to_key(char *user, char *instance, char *realm, char *passwd, C_Block key) { memcpy(key, passwd, sizeof(des_cblock)); return (0); } #endif /* !HAVE_DARWIN */ /* Called to see if the user's typed password is valid. We do this by asking the kerberos server for a ticket and checking to see if it gave us one. We need to move the ticketfile first, or otherwise we end up updating the user's tkfile with new tickets. This would break services like zephyr that like to stay authenticated, and it would screw with AFS authentication at some sites. So, we do a quick, painful hack with a tmpfile. */ Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p) { # ifdef HAVE_DARWIN return (klNoErr == KLAcquireNewInitialTicketsWithPassword (princ, NULL, typed_passwd, NULL)); # else /* !HAVE_DARWIN */ /* See comments in kerberos_lock_init -- should we do it the Mac Way on all systems? */ C_Block mitkey; Bool success; char *newtkfile; int fh = -1; /* temporarily switch to a new ticketfile. I'm not using tmpnam() because it isn't entirely portable. this could probably be fixed with autoconf. */ newtkfile = malloc(80 * sizeof(char)); memset(newtkfile, 0, sizeof(newtkfile)); sprintf(newtkfile, "/tmp/xscrn-%i.XXXXXX", getpid()); if( (fh = mkstemp(newtkfile)) < 0) { free(newtkfile); return(False); } if( fchmod(fh, 0600) < 0) { free(newtkfile); return(False); } krb_set_tkt_string(newtkfile); /* encrypt the typed password. if you have an AFS password instead of a kerberos one, you lose *right here*. If you want to use AFS passwords, you can use ka_StringToKey() instead. As always, ymmv. */ des_string_to_key(typed_passwd, mitkey); if (krb_get_in_tkt(name, inst, realm, "krbtgt", realm, DEFAULT_TKT_LIFE, key_to_key, NULL, (char *) mitkey) != 0) { success = False; } else { success = True; } /* quickly block out the tempfile and password to prevent snooping, then restore the old ticketfile and cleean up a bit. */ dest_tkt(); krb_set_tkt_string(tk_file); free(newtkfile); memset(mitkey, 0, sizeof(mitkey)); close(fh); /* #### tom: should the file be removed? */ /* Did we verify successfully? */ return success; # endif /* !HAVE_DARWIN */ } #endif /* NO_LOCKING -- whole file */ xscreensaver-5.15/driver/passwd-pam.c000066400000000000000000000406031164314150500176720ustar00rootroot00000000000000/* passwd-pam.c --- verifying typed passwords with PAM * (Pluggable Authentication Modules.) * written by Bill Nottingham (and jwz) for * xscreensaver, Copyright (c) 1993-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * Some PAM resources: * * PAM home page: * http://www.us.kernel.org/pub/linux/libs/pam/ * * PAM FAQ: * http://www.us.kernel.org/pub/linux/libs/pam/FAQ * * PAM Application Developers' Guide: * http://www.us.kernel.org/pub/linux/libs/pam/Linux-PAM-html/Linux-PAM_ADG.html * * PAM Mailing list archives: * http://www.linuxhq.com/lnxlists/linux-pam/ * * Compatibility notes, especially between Linux and Solaris: * http://www.contrib.andrew.cmu.edu/u/shadow/pam.html * * The Open Group's PAM API documentation: * http://www.opengroup.org/onlinepubs/8329799/pam_start.htm */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifndef NO_LOCKING /* whole file */ #include #ifdef HAVE_UNISTD_H # include #endif extern char *blurb(void); #include #include #include #include #include #include #include #include #include #include #include "auth.h" extern sigset_t block_sigchld (void); extern void unblock_sigchld (void); /* blargh */ #undef Bool #undef True #undef False #define Bool int #define True 1 #define False 0 #undef countof #define countof(x) (sizeof((x))/sizeof(*(x))) /* Some time between Red Hat 4.2 and 7.0, the words were transposed in the various PAM_x_CRED macro names. Yay! */ #ifndef PAM_REFRESH_CRED # define PAM_REFRESH_CRED PAM_CRED_REFRESH #endif static int pam_conversation (int nmsgs, const struct pam_message **msg, struct pam_response **resp, void *closure); void pam_try_unlock(saver_info *si, Bool verbose_p, Bool (*valid_p)(const char *typed_passwd, Bool verbose_p)); Bool pam_priv_init (int argc, char **argv, Bool verbose_p); #ifdef HAVE_PAM_FAIL_DELAY /* We handle delays ourself.*/ /* Don't set this to 0 (Linux bug workaround.) */ # define PAM_NO_DELAY(pamh) pam_fail_delay ((pamh), 1) #else /* !HAVE_PAM_FAIL_DELAY */ # define PAM_NO_DELAY(pamh) /* */ #endif /* !HAVE_PAM_FAIL_DELAY */ /* On SunOS 5.6, and on Linux with PAM 0.64, pam_strerror() takes two args. On some other Linux systems with some other version of PAM (e.g., whichever Debian release comes with a 2.2.5 kernel) it takes one arg. I can't tell which is more "recent" or "correct" behavior, so configure figures out which is in use for us. Shoot me! */ #ifdef PAM_STRERROR_TWO_ARGS # define PAM_STRERROR(pamh, status) pam_strerror((pamh), (status)) #else /* !PAM_STRERROR_TWO_ARGS */ # define PAM_STRERROR(pamh, status) pam_strerror((status)) #endif /* !PAM_STRERROR_TWO_ARGS */ /* PAM sucks in that there is no way to tell whether a particular service is configured at all. That is, there is no way to tell the difference between "authentication of the FOO service is not allowed" and "the user typed the wrong password." On RedHat 5.1 systems, if a service name is not known, it defaults to being not allowed (because the fallback service, /etc/pam.d/other, is set to `pam_deny'.) On Solaris 2.6 systems, unknown services default to authenticating normally. So, we could simply require that the person who installs xscreensaver set up an "xscreensaver" PAM service. However, if we went that route, it would have a really awful failure mode: the failure mode would be that xscreensaver was willing to *lock* the screen, but would be unwilling to *unlock* the screen. (With the non-PAM password code, the analagous situation -- security not being configured properly, for example do to the executable not being installed as setuid root -- the failure mode is much more palettable, in that xscreensaver will refuse to *lock* the screen, because it can know up front that there is no password that will work.) Another route would be to have the service name to consult be computed at compile-time (perhaps with a configure option.) However, that doesn't really solve the problem, because it means that the same executable might work fine on one machine, but refuse to unlock when run on another machine. Another alternative would be to look in /etc/pam.conf or /etc/pam.d/ at runtime to see what services actually exist. But I think that's no good, because who is to say that the PAM info is actually specified in those files? Opening and reading those files is not a part of the PAM client API, so it's not guarenteed to work on any given system. An alternative I tried was to specify a list of services to try, and to try them all in turn ("xscreensaver", "xlock", "xdm", and "login"). This worked, but it was slow (and I also had to do some contortions to work around bugs in Linux PAM 0.64-3.) So what we do today is, try PAM once, and if that fails, try the usual getpwent() method. So if PAM doesn't work, it will at least make an attempt at looking up passwords in /etc/passwd or /etc/shadow instead. This all kind of blows. I'm not sure what else to do. */ /* On SunOS 5.6, the `pam_conv.appdata_ptr' slot seems to be ignored, and the `closure' argument to pc.conv always comes in as random garbage. So we get around this by using a global variable instead. Shoot me! (I've been told this is bug 4092227, and is fixed in Solaris 7.) (I've also been told that it's fixed in Solaris 2.6 by patch 106257-05.) */ static void *suns_pam_implementation_blows = 0; /** * This function is the PAM conversation driver. It conducts a full * authentication round by invoking the GUI with various prompts. */ void pam_try_unlock(saver_info *si, Bool verbose_p, Bool (*valid_p)(const char *typed_passwd, Bool verbose_p)) { const char *service = PAM_SERVICE_NAME; pam_handle_t *pamh = 0; int status = -1; struct pam_conv pc; sigset_t set; struct timespec timeout; pc.conv = &pam_conversation; pc.appdata_ptr = (void *) si; /* On SunOS 5.6, the `appdata_ptr' slot seems to be ignored, and the `closure' argument to pc.conv always comes in as random garbage. */ suns_pam_implementation_blows = (void *) si; /* Initialize PAM. */ status = pam_start (service, si->user, &pc, &pamh); if (verbose_p) fprintf (stderr, "%s: pam_start (\"%s\", \"%s\", ...) ==> %d (%s)\n", blurb(), service, si->user, status, PAM_STRERROR (pamh, status)); if (status != PAM_SUCCESS) goto DONE; /* #### We should set PAM_TTY to the display we're using, but we don't have that handy from here. So set it to :0.0, which is a good guess (and has the bonus of counting as a "secure tty" as far as PAM is concerned...) */ { char *tty = strdup (":0.0"); status = pam_set_item (pamh, PAM_TTY, tty); if (verbose_p) fprintf (stderr, "%s: pam_set_item (p, PAM_TTY, \"%s\") ==> %d (%s)\n", blurb(), tty, status, PAM_STRERROR(pamh, status)); free (tty); } /* Try to authenticate as the current user. We must turn off our SIGCHLD handler for the duration of the call to pam_authenticate(), because in some cases, the underlying PAM code will do this: 1: fork a setuid subprocess to do some dirty work; 2: read a response from that subprocess; 3: waitpid(pid, ...) on that subprocess. If we (the ignorant parent process) have a SIGCHLD handler, then there's a race condition between steps 2 and 3: if the subprocess exits before waitpid() was called, then our SIGCHLD handler fires, and gets notified of the subprocess death; then PAM's call to waitpid() fails, because the process has already been reaped. I consider this a bug in PAM, since the caller should be able to have whatever signal handlers it wants -- the PAM documentation doesn't say "oh by the way, if you use PAM, you can't use SIGCHLD." */ PAM_NO_DELAY(pamh); if (verbose_p) fprintf (stderr, "%s: pam_authenticate (...) ...\n", blurb()); timeout.tv_sec = 0; timeout.tv_nsec = 1; set = block_sigchld(); status = pam_authenticate (pamh, 0); # ifdef HAVE_SIGTIMEDWAIT sigtimedwait (&set, NULL, &timeout); /* #### What is the portable thing to do if we don't have it? */ # endif /* HAVE_SIGTIMEDWAIT */ unblock_sigchld(); if (verbose_p) fprintf (stderr, "%s: pam_authenticate (...) ==> %d (%s)\n", blurb(), status, PAM_STRERROR(pamh, status)); if (status == PAM_SUCCESS) /* Win! */ { int status2; /* We don't actually care if the account modules fail or succeed, * but we need to run them anyway because certain pam modules * depend on side effects of the account modules getting run. */ status2 = pam_acct_mgmt (pamh, 0); if (verbose_p) fprintf (stderr, "%s: pam_acct_mgmt (...) ==> %d (%s)\n", blurb(), status2, PAM_STRERROR(pamh, status2)); /* HPUX for some reason likes to make PAM defines different from * everyone else's. */ #ifdef PAM_AUTHTOKEN_REQD if (status2 == PAM_AUTHTOKEN_REQD) #else if (status2 == PAM_NEW_AUTHTOK_REQD) #endif { status2 = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK); if (verbose_p) fprintf (stderr, "%s: pam_chauthtok (...) ==> %d (%s)\n", blurb(), status2, PAM_STRERROR(pamh, status2)); } /* Each time we successfully authenticate, refresh credentials, for Kerberos/AFS/DCE/etc. If this fails, just ignore that failure and blunder along; it shouldn't matter. Note: this used to be PAM_REFRESH_CRED instead of PAM_REINITIALIZE_CRED, but Jason Heiss says that the Linux PAM library ignores that one, and only refreshes credentials when using PAM_REINITIALIZE_CRED. */ status2 = pam_setcred (pamh, PAM_REINITIALIZE_CRED); if (verbose_p) fprintf (stderr, "%s: pam_setcred (...) ==> %d (%s)\n", blurb(), status2, PAM_STRERROR(pamh, status2)); } DONE: if (pamh) { int status2 = pam_end (pamh, status); pamh = 0; if (verbose_p) fprintf (stderr, "%s: pam_end (...) ==> %d (%s)\n", blurb(), status2, (status2 == PAM_SUCCESS ? "Success" : "Failure")); } if (status == PAM_SUCCESS) si->unlock_state = ul_success; /* yay */ else if (si->unlock_state == ul_cancel || si->unlock_state == ul_time) ; /* more specific failures ok */ else si->unlock_state = ul_fail; /* generic failure */ } Bool pam_priv_init (int argc, char **argv, Bool verbose_p) { /* We have nothing to do at init-time. However, we might as well do some error checking. If "/etc/pam.d" exists and is a directory, but "/etc/pam.d/xlock" does not exist, warn that PAM probably isn't going to work. This is a priv-init instead of a non-priv init in case the directory is unreadable or something (don't know if that actually happens.) */ const char dir[] = "/etc/pam.d"; const char file[] = "/etc/pam.d/" PAM_SERVICE_NAME; const char file2[] = "/etc/pam.conf"; struct stat st; # ifndef S_ISDIR # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) # endif if (stat (dir, &st) == 0 && S_ISDIR(st.st_mode)) { if (stat (file, &st) != 0) fprintf (stderr, "%s: warning: %s does not exist.\n" "%s: password authentication via PAM is unlikely to work.\n", blurb(), file, blurb()); } else if (stat (file2, &st) == 0) { FILE *f = fopen (file2, "r"); if (f) { Bool ok = False; char buf[255]; while (fgets (buf, sizeof(buf), f)) if (strstr (buf, PAM_SERVICE_NAME)) { ok = True; break; } fclose (f); if (!ok) { fprintf (stderr, "%s: warning: %s does not list the `%s' service.\n" "%s: password authentication via PAM is unlikely to work.\n", blurb(), file2, PAM_SERVICE_NAME, blurb()); } } /* else warn about file2 existing but being unreadable? */ } else { fprintf (stderr, "%s: warning: neither %s nor %s exist.\n" "%s: password authentication via PAM is unlikely to work.\n", blurb(), file2, file, blurb()); } /* Return true anyway, just in case. */ return True; } static int pam_conversation (int nmsgs, const struct pam_message **msg, struct pam_response **resp, void *vsaver_info) { int i, ret = -1; struct auth_message *messages = 0; struct auth_response *authresp = 0; struct pam_response *pam_responses; saver_info *si = (saver_info *) vsaver_info; Bool verbose_p; /* On SunOS 5.6, the `closure' argument always comes in as random garbage. */ si = (saver_info *) suns_pam_implementation_blows; verbose_p = si->prefs.verbose_p; /* Converting the PAM prompts into the XScreenSaver native format. * It was a design goal to collapse (INFO,PROMPT) pairs from PAM * into a single call to the unlock_cb function. The unlock_cb function * does that, but only if it is passed several prompts at a time. Most PAM * modules only send a single prompt at a time, but because there is no way * of telling whether there will be more prompts to follow, we can only ever * pass along whatever was passed in here. */ messages = calloc(nmsgs, sizeof(struct auth_message)); pam_responses = calloc(nmsgs, sizeof(*pam_responses)); if (!pam_responses || !messages) goto end; if (verbose_p) fprintf (stderr, "%s: pam_conversation (", blurb()); for (i = 0; i < nmsgs; ++i) { if (verbose_p && i > 0) fprintf (stderr, ", "); messages[i].msg = msg[i]->msg; switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: messages[i].type = AUTH_MSGTYPE_PROMPT_NOECHO; if (verbose_p) fprintf (stderr, "ECHO_OFF"); break; case PAM_PROMPT_ECHO_ON: messages[i].type = AUTH_MSGTYPE_PROMPT_ECHO; if (verbose_p) fprintf (stderr, "ECHO_ON"); break; case PAM_ERROR_MSG: messages[i].type = AUTH_MSGTYPE_ERROR; if (verbose_p) fprintf (stderr, "ERROR_MSG"); break; case PAM_TEXT_INFO: messages[i].type = AUTH_MSGTYPE_INFO; if (verbose_p) fprintf (stderr, "TEXT_INFO"); break; default: messages[i].type = AUTH_MSGTYPE_PROMPT_ECHO; if (verbose_p) fprintf (stderr, "PROMPT_ECHO"); break; } if (verbose_p) fprintf (stderr, "=\"%s\"", msg[i]->msg ? msg[i]->msg : "(null)"); } if (verbose_p) fprintf (stderr, ") ...\n"); ret = si->unlock_cb(nmsgs, messages, &authresp, si); /* #### If the user times out, or hits ESC or Cancel, we return PAM_CONV_ERR, and PAM logs this as an authentication failure. It would be nice if there was some way to indicate that this was a "cancel" rather than a "fail", so that it wouldn't show up in syslog, but I think the only options are PAM_SUCCESS and PAM_CONV_ERR. (I think that PAM_ABORT means "internal error", not "cancel".) Bleh. */ if (ret == 0) { for (i = 0; i < nmsgs; ++i) pam_responses[i].resp = authresp[i].response; } end: if (messages) free(messages); if (authresp) free(authresp); if (verbose_p) fprintf (stderr, "%s: pam_conversation (...) ==> %s\n", blurb(), (ret == 0 ? "PAM_SUCCESS" : "PAM_CONV_ERR")); if (ret == 0) { *resp = pam_responses; return PAM_SUCCESS; } /* Failure only */ if (pam_responses) free(pam_responses); return PAM_CONV_ERR; } #endif /* NO_LOCKING -- whole file */ xscreensaver-5.15/driver/passwd-pwent.c000066400000000000000000000164261164314150500202600ustar00rootroot00000000000000/* passwd-pwent.c --- verifying typed passwords with the OS. * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifndef NO_LOCKING /* whole file */ #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_CRYPT_H # include #endif #include #include #include #ifndef VMS # include # include #else /* VMS */ # include "vms-pwd.h" #endif /* VMS */ #ifdef __bsdi__ # include # if _BSDI_VERSION >= 199608 # define BSD_AUTH # endif #endif /* __bsdi__ */ #if defined(HAVE_SHADOW_PASSWD) /* passwds live in /etc/shadow */ # include # define PWTYPE struct spwd * # define PWPSLOT sp_pwdp # define GETPW getspnam #elif defined(HAVE_ENHANCED_PASSWD) /* passwds live in /tcb/files/auth/ */ /* M.Matsumoto */ # include # include # define PWTYPE struct pr_passwd * # define PWPSLOT ufld.fd_encrypt # define GETPW getprpwnam #elif defined(HAVE_ADJUNCT_PASSWD) # include # include # include # define PWTYPE struct passwd_adjunct * # define PWPSLOT pwa_passwd # define GETPW getpwanam #elif defined(HAVE_HPUX_PASSWD) # include # include # define PWTYPE struct s_passwd * # define PWPSLOT pw_passwd # define GETPW getspwnam # define HAVE_BIGCRYPT #endif /* blargh */ #undef Bool #undef True #undef False #define Bool int #define True 1 #define False 0 extern const char *blurb(void); static char *encrypted_root_passwd = 0; static char *encrypted_user_passwd = 0; #ifdef VMS # define ROOT "SYSTEM" #else # define ROOT "root" #endif #ifndef VMS Bool pwent_priv_init (int argc, char **argv, Bool verbose_p); Bool pwent_lock_init (int argc, char **argv, Bool verbose_p); Bool pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p); #endif #ifndef VMS static char * user_name (void) { /* I think that just checking $USER here is not the best idea. */ const char *u = 0; /* It has been reported that getlogin() returns the wrong user id on some very old SGI systems... And I've seen it return the string "rlogin" sometimes! Screw it, using getpwuid() should be enough... */ /* u = (char *) getlogin (); */ /* getlogin() fails if not attached to a terminal; in that case, use getpwuid(). (Note that in this case, we're not doing shadow stuff, since all we're interested in is the name, not the password. So that should still work. Right?) */ if (!u || !*u) { struct passwd *p = getpwuid (getuid ()); u = (p ? p->pw_name : 0); } return (u ? strdup(u) : 0); } #else /* VMS */ static char * user_name (void) { char *u = getenv("USER"); return (u ? strdup(u) : 0); } #endif /* VMS */ static Bool passwd_known_p (const char *pw) { return (pw && pw[0] != '*' && /* This would be sensible... */ strlen(pw) > 4); /* ...but this is what Solaris does. */ } static char * get_encrypted_passwd(const char *user) { char *result = 0; #ifdef PWTYPE if (user && *user && !result) { /* First check the shadow passwords. */ PWTYPE p = GETPW((char *) user); if (p && passwd_known_p (p->PWPSLOT)) result = strdup(p->PWPSLOT); } #endif /* PWTYPE */ if (user && *user && !result) { /* Check non-shadow passwords too. */ struct passwd *p = getpwnam(user); if (p && passwd_known_p (p->pw_passwd)) result = strdup(p->pw_passwd); } /* The manual for passwd(4) on HPUX 10.10 says: Password aging is put in effect for a particular user if his encrypted password in the password file is followed by a comma and a nonnull string of characters from the above alphabet. This string defines the "age" needed to implement password aging. So this means that passwd->pw_passwd isn't simply a string of cyphertext, it might have trailing junk. So, if there is a comma in the string, and that comma is beyond position 13, terminate the string before the comma. */ if (result && strlen(result) > 13) { char *s = strchr (result+13, ','); if (s) *s = 0; } #ifndef HAVE_PAM /* We only issue this warning if not compiled with support for PAM. If we're using PAM, it's not unheard of that normal pwent passwords would be unavailable. */ if (!result) fprintf (stderr, "%s: couldn't get password of \"%s\"\n", blurb(), (user ? user : "(null)")); #endif /* !HAVE_PAM */ return result; } /* This has to be called before we've changed our effective user ID, because it might need privileges to get at the encrypted passwords. Returns false if we weren't able to get any passwords, and therefore, locking isn't possible. (It will also have written to stderr.) */ #ifndef VMS Bool pwent_priv_init (int argc, char **argv, Bool verbose_p) { char *u; #ifdef HAVE_ENHANCED_PASSWD set_auth_parameters(argc, argv); check_auth_parameters(); #endif /* HAVE_DEC_ENHANCED */ u = user_name(); encrypted_user_passwd = get_encrypted_passwd(u); encrypted_root_passwd = get_encrypted_passwd(ROOT); if (u) free (u); if (encrypted_user_passwd) return True; else return False; } Bool pwent_lock_init (int argc, char **argv, Bool verbose_p) { if (encrypted_user_passwd) return True; else return False; } static Bool passwds_match_p (const char *cleartext, const char *ciphertext) { char *s = 0; /* note that on some systems, crypt() may return null */ s = (char *) crypt (cleartext, ciphertext); if (s && !strcmp (s, ciphertext)) return True; #ifdef HAVE_BIGCRYPT /* There seems to be no way to tell at runtime if an HP machine is in "trusted" mode, and thereby, which of crypt() or bigcrypt() we should be calling to compare passwords. So call them both, and see which one works. */ s = (char *) bigcrypt (cleartext, ciphertext); if (s && !strcmp (s, ciphertext)) return True; #endif /* HAVE_BIGCRYPT */ return False; } /* This can be called at any time, and says whether the typed password belongs to either the logged in user (real uid, not effective); or to root. */ Bool pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p) { if (encrypted_user_passwd && passwds_match_p (typed_passwd, encrypted_user_passwd)) return True; #ifdef ALLOW_ROOT_PASSWD /* do not allow root to have a null password. */ else if (typed_passwd[0] && encrypted_root_passwd && passwds_match_p (typed_passwd, encrypted_root_passwd)) return True; #endif /* ALLOW_ROOT_PASSWD */ else return False; } #else /* VMS */ Bool pwent_lock_init (int argc, char **argv, Bool verbose_p) { return True; } #endif /* VMS */ #endif /* NO_LOCKING -- whole file */ xscreensaver-5.15/driver/passwd.c000066400000000000000000000230461164314150500171210ustar00rootroot00000000000000/* passwd.c --- verifying typed passwords with the OS. * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifndef NO_LOCKING /* whole file */ #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifndef VMS # include /* for getpwuid() */ #else /* VMS */ # include "vms-pwd.h" #endif /* VMS */ #ifdef HAVE_SYSLOG # include #endif /* HAVE_SYSLOG */ #include #include "xscreensaver.h" #include "auth.h" extern const char *blurb(void); extern void check_for_leaks (const char *where); /* blargh */ #undef Bool #undef True #undef False #define Bool int #define True 1 #define False 0 #undef countof #define countof(x) (sizeof((x))/sizeof(*(x))) struct auth_methods { const char *name; Bool (*init) (int argc, char **argv, Bool verbose_p); Bool (*priv_init) (int argc, char **argv, Bool verbose_p); Bool (*valid_p) (const char *typed_passwd, Bool verbose_p); void (*try_unlock) (saver_info *si, Bool verbose_p, Bool (*valid_p)(const char *typed_passwd, Bool verbose_p)); Bool initted_p; Bool priv_initted_p; }; #ifdef HAVE_KERBEROS extern Bool kerberos_lock_init (int argc, char **argv, Bool verbose_p); extern Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p); #endif #ifdef HAVE_PAM extern Bool pam_priv_init (int argc, char **argv, Bool verbose_p); extern void pam_try_unlock (saver_info *si, Bool verbose_p, Bool (*valid_p)(const char *typed_passwd, Bool verbose_p)); #endif #ifdef PASSWD_HELPER_PROGRAM extern Bool ext_priv_init (int argc, char **argv, Bool verbose_p); extern Bool ext_passwd_valid_p (const char *typed_passwd, Bool verbose_p); #endif extern Bool pwent_lock_init (int argc, char **argv, Bool verbose_p); extern Bool pwent_priv_init (int argc, char **argv, Bool verbose_p); extern Bool pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p); Bool lock_priv_init (int argc, char **argv, Bool verbose_p); Bool lock_init (int argc, char **argv, Bool verbose_p); Bool passwd_valid_p (const char *typed_passwd, Bool verbose_p); /* The authorization methods to try, in order. Note that the last one (the pwent version) is actually two auth methods, since that code tries shadow passwords, and then non-shadow passwords. (It's all in the same file since the APIs are randomly nearly-identical.) */ struct auth_methods methods[] = { # ifdef HAVE_PAM { "PAM", 0, pam_priv_init, 0, pam_try_unlock, False, False }, # endif # ifdef HAVE_KERBEROS { "Kerberos", kerberos_lock_init, 0, kerberos_passwd_valid_p, 0, False, False }, # endif # ifdef PASSWD_HELPER_PROGRAM { "external", 0, ext_priv_init, ext_passwd_valid_p, 0, False, False }, # endif { "normal", pwent_lock_init, pwent_priv_init, pwent_passwd_valid_p, 0, False, False } }; Bool lock_priv_init (int argc, char **argv, Bool verbose_p) { int i; Bool any_ok = False; for (i = 0; i < countof(methods); i++) { if (!methods[i].priv_init) methods[i].priv_initted_p = True; else methods[i].priv_initted_p = methods[i].priv_init (argc, argv, verbose_p); if (methods[i].priv_initted_p) any_ok = True; else if (verbose_p) fprintf (stderr, "%s: initialization of %s passwords failed.\n", blurb(), methods[i].name); } return any_ok; } Bool lock_init (int argc, char **argv, Bool verbose_p) { int i; Bool any_ok = False; for (i = 0; i < countof(methods); i++) { if (!methods[i].priv_initted_p) /* Bail if lock_priv_init failed. */ continue; if (!methods[i].init) methods[i].initted_p = True; else methods[i].initted_p = methods[i].init (argc, argv, verbose_p); if (methods[i].initted_p) any_ok = True; else if (verbose_p) fprintf (stderr, "%s: initialization of %s passwords failed.\n", blurb(), methods[i].name); } return any_ok; } /* A basic auth driver that simply prompts for a password then runs it through * valid_p to determine whether the password is correct. */ static void try_unlock_password(saver_info *si, Bool verbose_p, Bool (*valid_p)(const char *typed_passwd, Bool verbose_p)) { struct auth_message message; struct auth_response *response = NULL; memset(&message, 0, sizeof(message)); if (verbose_p) fprintf(stderr, "%s: non-PAM password auth.\n", blurb()); /* Call the auth_conv function with "Password:", then feed * the result into valid_p() */ message.type = AUTH_MSGTYPE_PROMPT_NOECHO; message.msg = "Password:"; si->unlock_cb(1, &message, &response, si); if (!response) return; if (valid_p (response->response, verbose_p)) si->unlock_state = ul_success; /* yay */ else if (si->unlock_state == ul_cancel || si->unlock_state == ul_time) ; /* more specific failures ok */ else si->unlock_state = ul_fail; /* generic failure */ if (response->response) free(response->response); free(response); } /* Write a password failure to the system log. */ static void do_syslog (saver_info *si, Bool verbose_p) { # ifdef HAVE_SYSLOG struct passwd *pw = getpwuid (getuid ()); char *d = DisplayString (si->dpy); char *u = (pw && pw->pw_name ? pw->pw_name : "???"); int opt = 0; int fac = 0; # ifdef LOG_PID opt = LOG_PID; # endif # if defined(LOG_AUTHPRIV) fac = LOG_AUTHPRIV; # elif defined(LOG_AUTH) fac = LOG_AUTH; # else fac = LOG_DAEMON; # endif if (!d) d = ""; # undef FMT # define FMT "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"" if (verbose_p) fprintf (stderr, "%s: syslog: " FMT "\n", blurb(), si->unlock_failures, d, u); openlog (progname, opt, fac); syslog (LOG_NOTICE, FMT, si->unlock_failures, d, u); closelog (); # endif /* HAVE_SYSLOG */ } /** * Runs through each authentication driver calling its try_unlock function. * Called xss_authenticate() because AIX beat us to the name authenticate(). */ void xss_authenticate(saver_info *si, Bool verbose_p) { int i, j; si->unlock_state = ul_read; for (i = 0; i < countof(methods); i++) { if (!methods[i].initted_p) continue; if (si->cached_passwd != NULL && methods[i].valid_p) si->unlock_state = (methods[i].valid_p(si->cached_passwd, verbose_p) == True) ? ul_success : ul_fail; else if (methods[i].try_unlock != NULL) methods[i].try_unlock(si, verbose_p, methods[i].valid_p); else if (methods[i].valid_p) try_unlock_password(si, verbose_p, methods[i].valid_p); else /* Ze goggles, zey do nozing! */ fprintf(stderr, "%s: authentication method %s does nothing.\n", blurb(), methods[i].name); check_for_leaks (methods[i].name); /* If password authentication failed, but the password was NULL (meaning the user just hit RET) then treat that as "cancel". This means that if the password is literally NULL, it will work; but if not, then NULL passwords are treated as cancel. */ if (si->unlock_state == ul_fail && si->cached_passwd && !*si->cached_passwd) { fprintf (stderr, "%s: assuming null password means cancel.\n", blurb()); si->unlock_state = ul_cancel; } if (si->unlock_state == ul_success) { /* If we successfully authenticated by method N, but attempting to authenticate by method N-1 failed, mention that (since if an earlier authentication method fails and a later one succeeds, something screwy is probably going on.) */ if (verbose_p && i > 0) { for (j = 0; j < i; j++) if (methods[j].initted_p) fprintf (stderr, "%s: authentication via %s failed.\n", blurb(), methods[j].name); fprintf (stderr, "%s: authentication via %s succeeded.\n", blurb(), methods[i].name); } goto DONE; /* Successfully authenticated! */ } else if (si->unlock_state == ul_cancel || si->unlock_state == ul_time) { /* If any auth method gets a cancel or timeout, don't try the next auth method! We're done! */ fprintf (stderr, "%s: authentication via %s %s.\n", blurb(), methods[i].name, (si->unlock_state == ul_cancel ? "cancelled" : "timed out")); goto DONE; } } if (verbose_p) fprintf(stderr, "%s: All authentication mechanisms failed.\n", blurb()); if (si->unlock_state == ul_fail) { si->unlock_failures++; do_syslog (si, verbose_p); } DONE: if (si->auth_finished_cb) si->auth_finished_cb (si); } #endif /* NO_LOCKING -- whole file */ xscreensaver-5.15/driver/pdf2jpeg.m000066400000000000000000000111111164314150500173210ustar00rootroot00000000000000/* pdf2jpeg -- converts a PDF file to a JPEG file, using Cocoa * * Copyright (c) 2003, 2008 by Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * Inspired by clues provided by Jan Kujawa and Jonathan Hendry. */ #import #include #include int main (int argc, char** argv) { const char *progname = argv[0]; const char *infile = 0, *outfile = 0; double compression = 0.85; double scale = 1.0; int verbose = 0; int i; for (i = 1; i < argc; i++) { char c; if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++; if (!strcmp (argv[i], "-q") || !strcmp (argv[i], "-qual") || !strcmp (argv[i], "-quality")) { int q; if (1 != sscanf (argv[++i], " %d %c", &q, &c) || q < 5 || q > 100) { fprintf (stderr, "%s: quality must be 5 - 100 (%d)\n", progname, q); goto USAGE; } compression = q / 100.0; } else if (!strcmp (argv[i], "-scale")) { float s; if (1 != sscanf (argv[++i], " %f %c", &s, &c) || s <= 0 || s > 50) { fprintf (stderr, "%s: scale must be 0.0 - 50.0 (%f)\n", progname, s); goto USAGE; } scale = s; } else if (!strcmp (argv[i], "-verbose")) verbose++; else if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-vv") || !strcmp (argv[i], "-vvv")) verbose += strlen(argv[i])-1; else if (argv[i][0] == '-') { fprintf (stderr, "%s: unknown option %s\n", progname, argv[i]); goto USAGE; } else if (!infile) infile = argv[i]; else if (!outfile) outfile = argv[i]; else { USAGE: fprintf (stderr, "usage: %s [-verbose] [-scale N] [-quality NN] " "infile.pdf outfile.jpg\n", progname); exit (1); } } if (!infile || !outfile) goto USAGE; // Much of Cocoa needs one of these to be available. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //Need an NSApp instance to make [NSImage TIFFRepresentation] work NSApp = [NSApplication sharedApplication]; [NSApp autorelease]; if (verbose) fprintf (stderr, "%s: reading %s...\n", progname, infile); // Load the PDF file into an NSData object: NSData *pdf_data = [NSData dataWithContentsOfFile: [NSString stringWithCString:infile encoding:NSUTF8StringEncoding]]; // Create an NSPDFImageRep from the data: NSPDFImageRep *pdf_rep = [NSPDFImageRep imageRepWithData:pdf_data]; // Create an NSImage instance NSRect rect; rect.size = [pdf_rep size]; rect.size.width *= scale; rect.size.height *= scale; rect.origin.x = rect.origin.y = 0; NSImage *image = [[NSImage alloc] initWithSize:rect.size]; // Draw the PDFImageRep in the NSImage [image lockFocus]; [pdf_rep drawInRect:rect]; [image unlockFocus]; // Load the NSImage's contents into an NSBitmapImageRep: NSBitmapImageRep *bit_rep = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]]; // Write the bitmapImageRep to a JPEG file: if (bit_rep == nil) { fprintf (stderr, "%s: error converting image?\n", argv[0]); exit (1); } if (verbose) fprintf (stderr, "%s: writing %s (%d%% quality)...\n", progname, outfile, (int) (compression * 100)); NSDictionary *props = [NSDictionary dictionaryWithObject: [NSNumber numberWithFloat:compression] forKey:NSImageCompressionFactor]; NSData *jpeg_data = [bit_rep representationUsingType:NSJPEGFileType properties:props]; [jpeg_data writeToFile: [NSString stringWithCString:outfile encoding:NSUTF8StringEncoding] atomically:YES]; [image release]; [pool release]; exit (0); } xscreensaver-5.15/driver/pdf2jpeg.man000066400000000000000000000026761164314150500176600ustar00rootroot00000000000000.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11" .SH NAME pdf2jpeg - converts a PDF file to a JPEG file using Cocoa .SH SYNOPSIS .B pdf2jpeg [\--verbose] [\--quality \fINN\fP] infile.pdf outfile.jpg .SH DESCRIPTION This reads a PDF file (for example, as written by the .BR screencapture (1) program) and writes a JPEG file. .SH OPTIONS .I pdf2jpeg accepts the following options: .TP 4 .B --verbose Print diagnostics. .TP 4 .B --quality \fINN\fP JPEG compression factor. Default 85%. .SH BUGS The input and output files must be files: pipes don't work. This program is Cocoa-specific, so it won't work on non-MacOS systems. This shouldn't need to be a part of the XScreenSaver distribution at all, but Apple is COMPLETELY INSANE and made .BR screencapture (1) only write PDFs, with no simple way to convert that to something less crazy. .SH SEE ALSO .BR screencapture (1), .BR xscreensaver\-getimage\-desktop (1) .SH COPYRIGHT Copyright \(co 2003 by Jamie Zawinski. Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. No representations are made about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. .SH AUTHOR Jamie Zawinski , 20-Oct-03. xscreensaver-5.15/driver/prefs.c000066400000000000000000001267041164314150500167440ustar00rootroot00000000000000/* dotfile.c --- management of the ~/.xscreensaver file. * xscreensaver, Copyright (c) 1998-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #include /* for PATH_MAX */ #include #include #ifndef VMS # include #else /* VMS */ # include "vms-pwd.h" #endif /* VMS */ /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer #define XtAppContext void* #define XtIntervalId void* #define XtPointer void* #define Widget void* /* Just in case there's something pathological about stat.h... */ #ifndef S_IRUSR # define S_IRUSR 00400 #endif #ifndef S_IWUSR # define S_IWUSR 00200 #endif #ifndef S_IXUSR # define S_IXUSR 00100 #endif #ifndef S_IXGRP # define S_IXGRP 00010 #endif #ifndef S_IXOTH # define S_IXOTH 00001 #endif #include "prefs.h" #include "resources.h" /* don't use realpath() on fedora system */ #ifdef _FORTIFY_SOURCE #undef HAVE_REALPATH #endif extern char *progname; extern char *progclass; extern const char *blurb (void); static void get_screenhacks (Display *, saver_preferences *); static char *format_command (const char *cmd, Bool wrap_p); static void merge_system_screenhacks (Display *, saver_preferences *, screenhack **system_list, int count); static void stop_the_insanity (saver_preferences *p); static char * chase_symlinks (const char *file) { # ifdef HAVE_REALPATH if (file) { # ifndef PATH_MAX # ifdef MAXPATHLEN # define PATH_MAX MAXPATHLEN # else # define PATH_MAX 2048 # endif # endif char buf[PATH_MAX]; if (realpath (file, buf)) return strdup (buf); /* sprintf (buf, "%.100s: realpath %.200s", blurb(), file); perror(buf);*/ } # endif /* HAVE_REALPATH */ return 0; } static Bool i_am_a_nobody (uid_t uid) { struct passwd *p; p = getpwnam ("nobody"); if (! p) p = getpwnam ("noaccess"); if (! p) p = getpwnam ("daemon"); if (! p) /* There is no nobody? */ return False; return (uid == p->pw_uid); } const char * init_file_name (void) { static char *file = 0; if (!file) { uid_t uid = getuid (); struct passwd *p = getpwuid (uid); if (i_am_a_nobody (uid)) /* If we're running as nobody, then use root's .xscreensaver file (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely to be different -- if we didn't do this, then xscreensaver-demo would appear to have no effect when the luser is running as root.) */ uid = 0; p = getpwuid (uid); if (!p || !p->pw_name || !*p->pw_name) { fprintf (stderr, "%s: couldn't get user info of uid %d\n", blurb(), getuid ()); file = ""; } else if (!p->pw_dir || !*p->pw_dir) { fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n", blurb(), (p->pw_name ? p->pw_name : "???")); file = ""; } else { const char *home = p->pw_dir; const char *name = ".xscreensaver"; file = (char *) malloc(strlen(home) + strlen(name) + 2); strcpy(file, home); if (!*home || home[strlen(home)-1] != '/') strcat(file, "/"); strcat(file, name); } } if (file && *file) return file; else return 0; } static const char * init_file_tmp_name (void) { static char *file = 0; if (!file) { const char *name = init_file_name(); const char *suffix = ".tmp"; char *n2 = chase_symlinks (name); if (n2) name = n2; if (!name || !*name) file = ""; else { file = (char *) malloc(strlen(name) + strlen(suffix) + 2); strcpy(file, name); strcat(file, suffix); } if (n2) free (n2); } if (file && *file) return file; else return 0; } static int get_byte_resource (Display *dpy, char *name, char *class) { char *s = get_string_resource (dpy, name, class); char *s2 = s; int n = 0; if (!s) return 0; while (isspace(*s2)) s2++; while (*s2 >= '0' && *s2 <= '9') { n = (n * 10) + (*s2 - '0'); s2++; } while (isspace(*s2)) s2++; if (*s2 == 'k' || *s2 == 'K') n <<= 10; else if (*s2 == 'm' || *s2 == 'M') n <<= 20; else if (*s2 == 'g' || *s2 == 'G') n <<= 30; else if (*s2) { LOSE: fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n", progname, name, s); free (s); return 0; } s2++; if (*s2 == 'b' || *s2 == 'B') s2++; while (isspace(*s2)) s2++; if (*s2) goto LOSE; free (s); return n; } static const char * const prefs[] = { "timeout", "cycle", "lock", "lockVTs", /* not saved */ "lockTimeout", "passwdTimeout", "visualID", "installColormap", "verbose", "timestamp", "splash", "splashDuration", "quad", "demoCommand", "prefsCommand", "newLoginCommand", "helpURL", /* not saved */ "loadURL", /* not saved */ "newLoginCommand", /* not saved */ "nice", "memoryLimit", "fade", "unfade", "fadeSeconds", "fadeTicks", "captureStderr", "captureStdout", /* not saved -- obsolete */ "logFile", /* not saved */ "ignoreUninstalledPrograms", "font", "dpmsEnabled", "dpmsQuickOff", "dpmsStandby", "dpmsSuspend", "dpmsOff", "grabDesktopImages", "grabVideoFrames", "chooseRandomImages", "imageDirectory", "mode", "selected", "textMode", "textLiteral", "textFile", "textProgram", "textURL", "", "programs", "", "pointerPollTime", "pointerHysteresis", "windowCreationTimeout", "initialDelay", "sgiSaverExtension", /* not saved -- obsolete */ "mitSaverExtension", /* not saved -- obsolete */ "xidleExtension", /* not saved -- obsolete */ "GetViewPortIsFullOfLies", "procInterrupts", "xinputExtensionDev", "overlayStderr", "overlayTextBackground", /* not saved -- X resources only */ "overlayTextForeground", /* not saved -- X resources only */ "bourneShell", /* not saved -- X resources only */ 0 }; static char * strip (char *s) { char *s2; while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n') s++; for (s2 = s; *s2; s2++) ; for (s2--; s2 >= s; s2--) if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n') *s2 = 0; else break; return s; } /* Reading */ static int handle_entry (XrmDatabase *db, const char *key, const char *value, const char *filename, int line) { int i; for (i = 0; prefs[i]; i++) if (*prefs[i] && !strcasecmp(key, prefs[i])) { char *val = strdup(value); char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10); strcpy(spec, progclass); strcat(spec, "."); strcat(spec, prefs[i]); XrmPutStringResource (db, spec, val); free(spec); free(val); return 0; } fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n", blurb(), filename, line, key); return 1; } static int parse_init_file (saver_preferences *p) { time_t write_date = 0; const char *name = init_file_name(); int line = 0; struct stat st; FILE *in; int buf_size = 1024; char *buf; if (!name) return 0; if (stat(name, &st) != 0) { p->init_file_date = 0; return 0; } in = fopen(name, "r"); if (!in) { char *buf = (char *) malloc(1024 + strlen(name)); sprintf(buf, "%s: error reading \"%s\"", blurb(), name); perror(buf); free(buf); return -1; } if (fstat (fileno(in), &st) == 0) { write_date = st.st_mtime; } else { char *buf = (char *) malloc(1024 + strlen(name)); sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name); perror(buf); free(buf); return -1; } buf = (char *) malloc(buf_size); while (fgets (buf, buf_size-1, in)) { char *key, *value; int L = strlen(buf); line++; while (L > 2 && (buf[L-1] != '\n' || /* whole line didn't fit in buffer */ buf[L-2] == '\\')) /* or line ended with backslash */ { if (buf[L-2] == '\\') /* backslash-newline gets swallowed */ { buf[L-2] = 0; L -= 2; } buf_size += 1024; buf = (char *) realloc(buf, buf_size); if (!buf) exit(1); line++; if (!fgets (buf + L, buf_size-L-1, in)) break; L = strlen(buf); } /* Now handle other backslash escapes. */ { int i, j; for (i = 0; buf[i]; i++) if (buf[i] == '\\') { switch (buf[i+1]) { case 'n': buf[i] = '\n'; break; case 'r': buf[i] = '\r'; break; case 't': buf[i] = '\t'; break; default: buf[i] = buf[i+1]; break; } for (j = i+2; buf[j]; j++) buf[j-1] = buf[j]; buf[j-1] = 0; } } key = strip(buf); if (*key == '#' || *key == '!' || *key == ';' || *key == '\n' || *key == 0) continue; value = strchr (key, ':'); if (!value) { fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(), name, line, key); continue; } else { *value++ = 0; value = strip(value); } if (!p->db) abort(); handle_entry (&p->db, key, value, name, line); } fclose (in); free(buf); p->init_file_date = write_date; return 0; } Bool init_file_changed_p (saver_preferences *p) { const char *name = init_file_name(); struct stat st; if (!name) return False; if (stat(name, &st) != 0) return False; if (p->init_file_date == st.st_mtime) return False; return True; } /* Writing */ static int tab_to (FILE *out, int from, int to) { int tab_width = 8; int to_mod = (to / tab_width) * tab_width; while (from < to_mod) { fprintf(out, "\t"); from = (((from / tab_width) + 1) * tab_width); } while (from < to) { fprintf(out, " "); from++; } return from; } static char * stab_to (char *out, int from, int to) { int tab_width = 8; int to_mod = (to / tab_width) * tab_width; while (from < to_mod) { *out++ = '\t'; from = (((from / tab_width) + 1) * tab_width); } while (from < to) { *out++ = ' '; from++; } return out; } static int string_columns (const char *string, int length, int start) { int tab_width = 8; int col = start; const char *end = string + length; while (string < end) { if (*string == '\n') col = 0; else if (*string == '\t') col = (((col / tab_width) + 1) * tab_width); else col++; string++; } return col; } static void write_entry (FILE *out, const char *key, const char *value) { char *v = strdup(value ? value : ""); char *v2 = v; char *nl = 0; int col; Bool programs_p = (!strcmp(key, "programs")); int tab = (programs_p ? 32 : 16); Bool first = True; fprintf(out, "%s:", key); col = strlen(key) + 1; if (strlen(key) > 14) col = tab_to (out, col, 20); while (1) { if (!programs_p) v2 = strip(v2); nl = strchr(v2, '\n'); if (nl) *nl = 0; if (first && programs_p) { col = tab_to (out, col, 77); fprintf (out, " \\\n"); col = 0; } if (first) first = False; else { col = tab_to (out, col, 75); fprintf (out, " \\n\\\n"); col = 0; } if (!programs_p) col = tab_to (out, col, tab); if (programs_p && string_columns(v2, strlen (v2), col) + col > 75) { int L = strlen (v2); int start = 0; int end = start; while (start < L) { while (v2[end] == ' ' || v2[end] == '\t') end++; while (v2[end] != ' ' && v2[end] != '\t' && v2[end] != '\n' && v2[end] != 0) end++; if (string_columns (v2 + start, (end - start), col) >= 74) { col = tab_to (out, col, 75); fprintf(out, " \\\n"); col = tab_to (out, 0, tab + 2); while (v2[start] == ' ' || v2[start] == '\t') start++; } col = string_columns (v2 + start, (end - start), col); while (start < end) fputc(v2[start++], out); } } else { fprintf (out, "%s", v2); col += string_columns(v2, strlen (v2), col); } if (nl) v2 = nl + 1; else break; } fprintf(out, "\n"); free(v); } int write_init_file (Display *dpy, saver_preferences *p, const char *version_string, Bool verbose_p) { int status = -1; const char *name = init_file_name(); const char *tmp_name = init_file_tmp_name(); char *n2 = chase_symlinks (name); struct stat st; int i, j; /* Kludge, since these aren't in the saver_preferences struct as strings... */ char *visual_name; char *programs; Bool overlay_stderr_p; char *stderr_font; FILE *out; if (!name) goto END; if (n2) name = n2; /* Throttle the various timeouts to reasonable values before writing the file to disk. */ stop_the_insanity (p); if (verbose_p) fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name); unlink (tmp_name); out = fopen(tmp_name, "w"); if (!out) { char *buf = (char *) malloc(1024 + strlen(name)); sprintf(buf, "%s: error writing \"%s\"", blurb(), name); perror(buf); free(buf); goto END; } /* Give the new .xscreensaver file the same permissions as the old one; except ensure that it is readable and writable by owner, and not executable. Extra hack: if we're running as root, make the file be world-readable (so that the daemon, running as "nobody", will still be able to read it.) */ if (stat(name, &st) == 0) { mode_t mode = st.st_mode; mode |= S_IRUSR | S_IWUSR; /* read/write by user */ mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */ if (getuid() == (uid_t) 0) /* read by group/other */ mode |= S_IRGRP | S_IROTH; if (fchmod (fileno(out), mode) != 0) { char *buf = (char *) malloc(1024 + strlen(name)); sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(), tmp_name, (unsigned int) mode); perror(buf); free(buf); goto END; } } /* Kludge, since these aren't in the saver_preferences struct... */ visual_name = get_string_resource (dpy, "visualID", "VisualID"); programs = 0; overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean"); stderr_font = get_string_resource (dpy, "font", "Font"); i = 0; { char *ss; char **hack_strings = (char **) calloc (p->screenhacks_count, sizeof(char *)); for (j = 0; j < p->screenhacks_count; j++) { hack_strings[j] = format_hack (dpy, p->screenhacks[j], True); i += strlen (hack_strings[j]); i += 2; } ss = programs = (char *) malloc(i + 10); *ss = 0; for (j = 0; j < p->screenhacks_count; j++) { strcat (ss, hack_strings[j]); free (hack_strings[j]); ss += strlen(ss); *ss++ = '\n'; *ss = 0; } free (hack_strings); } { struct passwd *pw = getpwuid (getuid ()); char *whoami = (pw && pw->pw_name && *pw->pw_name ? pw->pw_name : ""); time_t now = time ((time_t *) 0); char *timestr = (char *) ctime (&now); char *nl = (char *) strchr (timestr, '\n'); if (nl) *nl = 0; fprintf (out, "# %s Preferences File\n" "# Written by %s %s for %s on %s.\n" "# http://www.jwz.org/xscreensaver/\n" "\n", progclass, progname, version_string, whoami, timestr); } for (j = 0; prefs[j]; j++) { char buf[255]; const char *pr = prefs[j]; enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time } type = pref_str; const char *s = 0; int i = 0; Bool b = False; Time t = 0; if (pr && !*pr) { fprintf(out, "\n"); continue; } # undef CHECK # define CHECK(X) else if (!strcmp(pr, X)) if (!pr || !*pr) ; CHECK("timeout") type = pref_time, t = p->timeout; CHECK("cycle") type = pref_time, t = p->cycle; CHECK("lock") type = pref_bool, b = p->lock_p; CHECK("lockVTs") continue; /* don't save, unused */ CHECK("lockTimeout") type = pref_time, t = p->lock_timeout; CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout; CHECK("visualID") type = pref_str, s = visual_name; CHECK("installColormap") type = pref_bool, b = p->install_cmap_p; CHECK("verbose") type = pref_bool, b = p->verbose_p; CHECK("timestamp") type = pref_bool, b = p->timestamp_p; CHECK("splash") type = pref_bool, b = p->splash_p; CHECK("splashDuration") type = pref_time, t = p->splash_duration; # ifdef QUAD_MODE CHECK("quad") type = pref_bool, b = p->quad_p; # else /* !QUAD_MODE */ CHECK("quad") continue; /* don't save */ # endif /* !QUAD_MODE */ CHECK("demoCommand") type = pref_str, s = p->demo_command; CHECK("prefsCommand") type = pref_str, s = p->prefs_command; /* CHECK("helpURL") type = pref_str, s = p->help_url; */ CHECK("helpURL") continue; /* don't save */ /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */ CHECK("loadURL") continue; /* don't save */ /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */ CHECK("newLoginCommand") continue; /* don't save */ CHECK("nice") type = pref_int, i = p->nice_inferior; CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit; CHECK("fade") type = pref_bool, b = p->fade_p; CHECK("unfade") type = pref_bool, b = p->unfade_p; CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds; CHECK("fadeTicks") type = pref_int, i = p->fade_ticks; CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p; CHECK("captureStdout") continue; /* don't save */ CHECK("logFile") continue; /* don't save */ CHECK("ignoreUninstalledPrograms") type = pref_bool, b = p->ignore_uninstalled_p; CHECK("font") type = pref_str, s = stderr_font; CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p; CHECK("dpmsQuickOff") type = pref_bool, b = p->dpms_quickoff_p; CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby; CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend; CHECK("dpmsOff") type = pref_time, t = p->dpms_off; CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p; CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p; CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p; CHECK("imageDirectory") type =pref_str, s = p->image_directory; CHECK("mode") type = pref_str, s = (p->mode == ONE_HACK ? "one" : p->mode == BLANK_ONLY ? "blank" : p->mode == DONT_BLANK ? "off" : p->mode == RANDOM_HACKS_SAME ? "random-same" : "random"); CHECK("selected") type = pref_int, i = p->selected_hack; CHECK("textMode") type = pref_str, s = (p->tmode == TEXT_URL ? "url" : p->tmode == TEXT_LITERAL ? "literal" : p->tmode == TEXT_FILE ? "file" : p->tmode == TEXT_PROGRAM ? "program" : "date"); CHECK("textLiteral") type = pref_str, s = p->text_literal; CHECK("textFile") type = pref_str, s = p->text_file; CHECK("textProgram") type = pref_str, s = p->text_program; CHECK("textURL") type = pref_str, s = p->text_url; CHECK("programs") type = pref_str, s = programs; CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout; CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis; CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout; CHECK("initialDelay") type = pref_time, t = p->initial_delay; CHECK("sgiSaverExtension") continue; /* don't save */ CHECK("mitSaverExtension") continue; /* don't save */ CHECK("xidleExtension") continue; /* don't save */ CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts; CHECK("xinputExtensionDev") type = pref_bool, b = p->use_xinput_extension; CHECK("GetViewPortIsFullOfLies") type = pref_bool, b = p->getviewport_full_of_lies_p; CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p; CHECK("overlayTextBackground") continue; /* don't save */ CHECK("overlayTextForeground") continue; /* don't save */ CHECK("bourneShell") continue; /* don't save */ else abort(); # undef CHECK switch (type) { case pref_str: break; case pref_int: sprintf(buf, "%d", i); s = buf; break; case pref_bool: s = b ? "True" : "False"; break; case pref_time: { unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000); if (sec >= 60) { min += (sec / 60); sec %= 60; } if (min >= 60) { hour += (min / 60); min %= 60; } sprintf (buf, "%u:%02u:%02u", hour, min, sec); s = buf; } break; case pref_byte: { if (i >= (1<<30) && i == ((i >> 30) << 30)) sprintf(buf, "%dG", i >> 30); else if (i >= (1<<20) && i == ((i >> 20) << 20)) sprintf(buf, "%dM", i >> 20); else if (i >= (1<<10) && i == ((i >> 10) << 10)) sprintf(buf, "%dK", i >> 10); else sprintf(buf, "%d", i); s = buf; } break; default: abort(); break; } if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode"))) fprintf(out, "\n"); write_entry (out, pr, s); } fprintf(out, "\n"); if (visual_name) free(visual_name); if (stderr_font) free(stderr_font); if (programs) free(programs); if (fclose(out) == 0) { time_t write_date = 0; if (stat(tmp_name, &st) == 0) { write_date = st.st_mtime; } else { char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name)); sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name); perror(buf); unlink (tmp_name); free(buf); goto END; } if (rename (tmp_name, name) != 0) { char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name)); sprintf(buf, "%s: error renaming \"%s\" to \"%s\"", blurb(), tmp_name, name); perror(buf); unlink (tmp_name); free(buf); goto END; } else { p->init_file_date = write_date; /* Since the .xscreensaver file is used for IPC, let's try and make sure that the bits actually land on the disk right away. */ sync (); status = 0; /* wrote and renamed successfully! */ } } else { char *buf = (char *) malloc(1024 + strlen(name)); sprintf(buf, "%s: error closing \"%s\"", blurb(), name); perror(buf); free(buf); unlink (tmp_name); goto END; } END: if (n2) free (n2); return status; } /* Parsing the resource database */ void free_screenhack (screenhack *hack) { if (hack->visual) free (hack->visual); if (hack->name) free (hack->name); free (hack->command); memset (hack, 0, sizeof(*hack)); free (hack); } static void free_screenhack_list (screenhack **list, int count) { int i; if (!list) return; for (i = 0; i < count; i++) if (list[i]) free_screenhack (list[i]); free (list); } /* Populate `saver_preferences' with the contents of the resource database. Note that this may be called multiple times -- it is re-run each time the ~/.xscreensaver file is reloaded. This function can be very noisy, since it issues resource syntax errors and so on. */ void load_init_file (Display *dpy, saver_preferences *p) { static Bool first_time = True; screenhack **system_default_screenhacks = 0; int system_default_screenhack_count = 0; if (first_time) { /* Get the programs resource before the .xscreensaver file has been parsed and merged into the resource database for the first time: this is the value of *programs from the app-defaults file. Then clear it out so that it will be parsed again later, after the init file has been read. */ get_screenhacks (dpy, p); system_default_screenhacks = p->screenhacks; system_default_screenhack_count = p->screenhacks_count; p->screenhacks = 0; p->screenhacks_count = 0; } if (parse_init_file (p) != 0) /* file might have gone away */ if (!first_time) return; first_time = False; p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous"); p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean"); p->timestamp_p = get_boolean_resource (dpy, "timestamp", "Boolean"); p->lock_p = get_boolean_resource (dpy, "lock", "Boolean"); p->fade_p = get_boolean_resource (dpy, "fade", "Boolean"); p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean"); p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time"); p->fade_ticks = get_integer_resource (dpy, "fadeTicks", "Integer"); p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean"); p->nice_inferior = get_integer_resource (dpy, "nice", "Nice"); p->inferior_memory_limit = get_byte_resource (dpy, "memoryLimit", "MemoryLimit"); p->splash_p = get_boolean_resource (dpy, "splash", "Boolean"); # ifdef QUAD_MODE p->quad_p = get_boolean_resource (dpy, "quad", "Boolean"); # endif p->capture_stderr_p = get_boolean_resource (dpy, "captureStderr", "Boolean"); p->ignore_uninstalled_p = get_boolean_resource (dpy, "ignoreUninstalledPrograms", "Boolean"); p->initial_delay = 1000 * get_seconds_resource (dpy, "initialDelay", "Time"); p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time"); p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time"); p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time"); p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time"); p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time"); p->pointer_timeout = 1000 * get_seconds_resource (dpy, "pointerPollTime", "Time"); p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer"); p->notice_events_timeout = 1000*get_seconds_resource(dpy, "windowCreationTimeout", "Time"); p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean"); p->dpms_quickoff_p = get_boolean_resource (dpy, "dpmsQuickOff", "Boolean"); p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time"); p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time"); p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time"); p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean"); p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean"); p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean"); p->image_directory = get_string_resource (dpy, "imageDirectory", "ImageDirectory"); p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral"); p->text_file = get_string_resource (dpy, "textFile", "TextFile"); p->text_program = get_string_resource (dpy, "textProgram", "TextProgram"); p->text_url = get_string_resource (dpy, "textURL", "TextURL"); p->shell = get_string_resource (dpy, "bourneShell", "BourneShell"); p->demo_command = get_string_resource(dpy, "demoCommand", "URL"); p->prefs_command = get_string_resource(dpy, "prefsCommand", "URL"); p->help_url = get_string_resource(dpy, "helpURL", "URL"); p->load_url_command = get_string_resource(dpy, "loadURL", "LoadURL"); p->new_login_command = get_string_resource(dpy, "newLoginCommand", "NewLoginCommand"); /* If "*splash" is unset, default to true. */ { char *s = get_string_resource (dpy, "splash", "Boolean"); if (s) free (s); else p->splash_p = True; } /* If "*grabDesktopImages" is unset, default to true. */ { char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean"); if (s) free (s); else p->grab_desktop_p = True; } p->use_xidle_extension = get_boolean_resource (dpy, "xidleExtension","Boolean"); #if 0 /* obsolete. */ p->use_sgi_saver_extension = get_boolean_resource (dpy, "sgiSaverExtension", "Boolean"); #endif #if 0 /* obsolete. */ p->use_xinput_extension = get_boolean_resource (dpy, "xinputExtensionDev", "Boolean"); #endif #if 0 /* broken and evil. */ p->use_mit_saver_extension = get_boolean_resource (dpy, "mitSaverExtension", "Boolean"); #endif p->use_proc_interrupts = get_boolean_resource (dpy, "procInterrupts", "Boolean"); p->getviewport_full_of_lies_p = get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean"); get_screenhacks (dpy, p); /* Parse the "programs" resource. */ { char *s = get_string_resource (dpy, "selected", "Integer"); if (!s || !*s) p->selected_hack = -1; else p->selected_hack = get_integer_resource (dpy, "selected", "Integer"); if (s) free (s); if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count) p->selected_hack = -1; } { char *s = get_string_resource (dpy, "mode", "Mode"); if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK; else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY; else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK; else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME; else p->mode = RANDOM_HACKS; if (s) free (s); } { char *s = get_string_resource (dpy, "textMode", "TextMode"); if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL; else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL; else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE; else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM; else p->tmode = TEXT_DATE; if (s) free (s); } if (system_default_screenhack_count) /* note: first_time is also true */ { merge_system_screenhacks (dpy, p, system_default_screenhacks, system_default_screenhack_count); free_screenhack_list (system_default_screenhacks, system_default_screenhack_count); system_default_screenhacks = 0; system_default_screenhack_count = 0; } if (p->debug_p) { p->xsync_p = True; p->verbose_p = True; p->timestamp_p = True; p->initial_delay = 0; } /* Throttle the various timeouts to reasonable values after reading the disk file. */ stop_the_insanity (p); } /* If there are any hacks in the system-wide defaults that are not in the ~/.xscreensaver file, add the new ones to the end of the list. This does *not* actually save the file. */ static void merge_system_screenhacks (Display *dpy, saver_preferences *p, screenhack **system_list, int system_count) { /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy, so fuck it. */ int made_space = 0; int i; for (i = 0; i < system_count; i++) { int j; Bool matched_p = False; for (j = 0; j < p->screenhacks_count; j++) { char *name; if (!system_list[i]->name) system_list[i]->name = make_hack_name (dpy, system_list[i]->command); name = p->screenhacks[j]->name; if (!name) name = make_hack_name (dpy, p->screenhacks[j]->command); matched_p = !strcasecmp (name, system_list[i]->name); if (name != p->screenhacks[j]->name) free (name); if (matched_p) break; } if (!matched_p) { /* We have an entry in the system-wide list that is not in the user's .xscreensaver file. Add it to the end. Note that p->screenhacks is a single malloc block, not a linked list, so we have to realloc it. */ screenhack *oh = system_list[i]; screenhack *nh = (screenhack *) malloc (sizeof(screenhack)); if (made_space == 0) { made_space = 10; p->screenhacks = (screenhack **) realloc (p->screenhacks, (p->screenhacks_count + made_space + 1) * sizeof(screenhack)); if (!p->screenhacks) abort(); } nh->enabled_p = oh->enabled_p; nh->visual = oh->visual ? strdup(oh->visual) : 0; nh->name = oh->name ? strdup(oh->name) : 0; nh->command = oh->command ? strdup(oh->command) : 0; p->screenhacks[p->screenhacks_count++] = nh; p->screenhacks[p->screenhacks_count] = 0; made_space--; #if 0 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(), (nh->name ? nh->name : make_hack_name (dpy, nh->command))); #endif } } } /* Parsing the programs resource. */ screenhack * parse_screenhack (const char *line) { screenhack *h = (screenhack *) calloc (1, sizeof(*h)); const char *s; h->enabled_p = True; while (isspace(*line)) line++; /* skip whitespace */ if (*line == '-') /* handle "-" */ { h->enabled_p = False; line++; while (isspace(*line)) line++; /* skip whitespace */ } s = line; /* handle "visual:" */ while (*line && *line != ':' && *line != '"' && !isspace(*line)) line++; if (*line != ':') line = s; else { h->visual = (char *) malloc (line-s+1); strncpy (h->visual, s, line-s); h->visual[line-s] = 0; if (*line == ':') line++; /* skip ":" */ while (isspace(*line)) line++; /* skip whitespace */ } if (*line == '"') /* handle "name" */ { line++; s = line; while (*line && *line != '"') line++; h->name = (char *) malloc (line-s+1); strncpy (h->name, s, line-s); h->name[line-s] = 0; if (*line == '"') line++; /* skip "\"" */ while (isspace(*line)) line++; /* skip whitespace */ } h->command = format_command (line, False); /* handle command */ return h; } static char * format_command (const char *cmd, Bool wrap_p) { int tab = 30; int col = tab; char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1)); const char *in = cmd; char *out = cmd2; while (*in) { /* shrink all whitespace to one space, for the benefit of the "demo" mode display. We only do this when we can easily tell that the whitespace is not significant (no shell metachars). */ switch (*in) { case '\'': case '"': case '`': case '\\': /* Metachars are scary. Copy the rest of the line unchanged. */ while (*in) *out++ = *in++, col++; break; case ' ': case '\t': /* Squeeze all other whitespace down to one space. */ while (*in == ' ' || *in == '\t') in++; *out++ = ' ', col++; break; default: /* Copy other chars unchanged. */ *out++ = *in++, col++; break; } } *out = 0; /* Strip trailing whitespace */ while (out > cmd2 && isspace (out[-1])) *(--out) = 0; return cmd2; } /* Returns a new string describing the shell command. This may be just the name of the program, capitalized. It also may be something from the resource database (gotten by looking for "hacks.XYZ.name", where XYZ is the program.) */ char * make_hack_name (Display *dpy, const char *shell_command) { char *s = strdup (shell_command); char *s2; char res_name[255]; for (s2 = s; *s2; s2++) /* truncate at first whitespace */ if (isspace (*s2)) { *s2 = 0; break; } s2 = strrchr (s, '/'); /* if pathname, take last component */ if (s2) { s2 = strdup (s2+1); free (s); s = s2; } if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */ s[50] = 0; sprintf (res_name, "hacks.%s.name", s); /* resource? */ s2 = get_string_resource (dpy, res_name, res_name); if (s2) { free (s); return s2; } for (s2 = s; *s2; s2++) /* if it has any capitals, return it */ if (*s2 >= 'A' && *s2 <= 'Z') return s; if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */ s[0] -= 'a'-'A'; if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */ s[1] -= 'a'-'A'; if (s[0] == 'G' && s[1] == 'l' && s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */ s[1] -= 'a'-'A', s[2] -= 'a'-'A'; return s; } char * format_hack (Display *dpy, screenhack *hack, Bool wrap_p) { int tab = 32; int size; char *h2, *out, *s; int col = 0; char *def_name = make_hack_name (dpy, hack->command); /* Don't ever write out a name for a hack if it's the same as the default. */ if (hack->name && !strcmp (hack->name, def_name)) { free (hack->name); hack->name = 0; } free (def_name); size = (2 * (strlen(hack->command) + (hack->visual ? strlen(hack->visual) : 0) + (hack->name ? strlen(hack->name) : 0) + tab)); h2 = (char *) malloc (size); out = h2; if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */ if (hack->visual && *hack->visual) /* write visual name */ { if (hack->enabled_p) *out++ = ' '; *out++ = ' '; strcpy (out, hack->visual); out += strlen (hack->visual); *out++ = ':'; *out++ = ' '; } *out = 0; col = string_columns (h2, strlen (h2), 0); if (hack->name && *hack->name) /* write pretty name */ { int L = (strlen (hack->name) + 2); if (L + col < tab) out = stab_to (out, col, tab - L - 2); else *out++ = ' '; *out++ = '"'; strcpy (out, hack->name); out += strlen (hack->name); *out++ = '"'; *out = 0; col = string_columns (h2, strlen (h2), 0); if (wrap_p && col >= tab) out = stab_to (out, col, 77); else *out++ = ' '; if (out >= h2+size) abort(); } *out = 0; col = string_columns (h2, strlen (h2), 0); out = stab_to (out, col, tab); /* indent */ if (out >= h2+size) abort(); s = format_command (hack->command, wrap_p); strcpy (out, s); out += strlen (s); free (s); *out = 0; return h2; } static void get_screenhacks (Display *dpy, saver_preferences *p) { int i, j; int start = 0; int end = 0; int size; char *d; d = get_string_resource (dpy, "monoPrograms", "MonoPrograms"); if (d && !*d) { free(d); d = 0; } if (!d) d = get_string_resource (dpy, "colorPrograms", "ColorPrograms"); if (d && !*d) { free(d); d = 0; } if (d) { fprintf (stderr, "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\ see the manual for details.\n", blurb()); free(d); } d = get_string_resource (dpy, "programs", "Programs"); free_screenhack_list (p->screenhacks, p->screenhacks_count); p->screenhacks = 0; p->screenhacks_count = 0; if (!d || !*d) return; size = strlen (d); /* Count up the number of newlines (which will be equal to or larger than one less than the number of hacks.) */ for (i = j = 0; d[i]; i++) if (d[i] == '\n') j++; j++; p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *)); /* Iterate over the lines in `d' (the string with newlines) and make new strings to stuff into the `screenhacks' array. */ p->screenhacks_count = 0; while (start < size) { /* skip forward over whitespace. */ while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n') start++; /* skip forward to newline or end of string. */ end = start; while (d[end] != 0 && d[end] != '\n') end++; /* null terminate. */ d[end] = 0; p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start); if (p->screenhacks_count >= i) abort(); start = end+1; } free (d); if (p->screenhacks_count == 0) { free (p->screenhacks); p->screenhacks = 0; } } /* Make sure all the values in the preferences struct are sane. */ static void stop_the_insanity (saver_preferences *p) { if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */ if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */ if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */ if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */ if (p->notice_events_timeout <= 0) p->notice_events_timeout = 10000; /* 10 secs */ if (p->fade_seconds <= 0 || p->fade_ticks <= 0) p->fade_p = False; if (! p->fade_p) p->unfade_p = False; /* The DPMS settings may have the value 0. But if they are negative, or are a range less than 10 seconds, reset them to sensible defaults. (Since that must be a mistake.) */ if (p->dpms_standby != 0 && p->dpms_standby < 10 * 1000) p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */ if (p->dpms_suspend != 0 && p->dpms_suspend < 10 * 1000) p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */ if (p->dpms_off != 0 && p->dpms_off < 10 * 1000) p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */ /* suspend may not be greater than off, unless off is 0. standby may not be greater than suspend, unless suspend is 0. */ if (p->dpms_off != 0 && p->dpms_suspend > p->dpms_off) p->dpms_suspend = p->dpms_off; if (p->dpms_suspend != 0 && p->dpms_standby > p->dpms_suspend) p->dpms_standby = p->dpms_suspend; /* These fixes above ignores the case suspend = 0 and standby > off ... */ if (p->dpms_off != 0 && p->dpms_standby > p->dpms_off) p->dpms_standby = p->dpms_off; if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */ p->dpms_suspend == 0 && p->dpms_off == 0) p->dpms_enabled_p = False; /* Set watchdog timeout to about half of the cycle timeout, but don't let it be faster than 1/2 minute or slower than 1 minute. */ p->watchdog_timeout = p->cycle * 0.6; if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */ if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */ if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0; if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100; } xscreensaver-5.15/driver/prefs.h000066400000000000000000000027061164314150500167440ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 1993-2006 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifndef __XSCREENSAVER_PREFS_H__ #define __XSCREENSAVER_PREFS_H__ #include "types.h" extern void load_init_file (Display *, saver_preferences *); extern Bool init_file_changed_p (saver_preferences *); extern int write_init_file (Display *, saver_preferences *, const char *version_string, Bool verbose_p); const char *init_file_name (void); extern screenhack *parse_screenhack (const char *line); extern void free_screenhack (screenhack *); extern char *format_hack (Display *, screenhack *, Bool wrap_p); char *make_hack_name (Display *, const char *shell_command); /* From dpms.c */ extern void sync_server_dpms_settings (Display *, Bool enabled_p, int standby_secs, int suspend_secs, int off_secs, Bool verbose_p); #endif /* __XSCREENSAVER_PREFS_H__ */ xscreensaver-5.15/driver/remote.c000066400000000000000000000345711164314150500171200ustar00rootroot00000000000000/* xscreensaver-command, Copyright (c) 1991-2009 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #ifdef HAVE_SYS_SELECT_H # include #endif /* HAVE_SYS_SELECT_H */ #ifdef HAVE_UNISTD_H # include #endif #include /* for CARD32 */ #include #include #include /* for XGetClassHint() */ #include #include "remote.h" #ifdef _VROOT_H_ ERROR! you must not include vroot.h in this file #endif extern char *progname; extern Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_RESPONSE; extern Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_EXIT; extern Atom XA_VROOT, XA_SELECT, XA_DEMO, XA_BLANK, XA_LOCK; static XErrorHandler old_handler = 0; static Bool got_badwindow = False; static int BadWindow_ehandler (Display *dpy, XErrorEvent *error) { if (error->error_code == BadWindow) { got_badwindow = True; return 0; } else { fprintf (stderr, "%s: ", progname); if (!old_handler) abort(); return (*old_handler) (dpy, error); } } static Window find_screensaver_window (Display *dpy, char **version) { int i; Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy)); Window root2, parent, *kids; unsigned int nkids; if (version) *version = 0; if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) abort (); if (root != root2) abort (); if (parent) abort (); if (! (kids && nkids)) return 0; for (i = 0; i < nkids; i++) { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *v; int status; /* We're walking the list of root-level windows and trying to find the one that has a particular property on it. We need to trap BadWindows errors while doing this, because it's possible that some random window might get deleted in the meantime. (That window won't have been the one we're looking for.) */ XSync (dpy, False); if (old_handler) abort(); got_badwindow = False; old_handler = XSetErrorHandler (BadWindow_ehandler); status = XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 200, False, XA_STRING, &type, &format, &nitems, &bytesafter, &v); XSync (dpy, False); XSetErrorHandler (old_handler); old_handler = 0; if (got_badwindow) { status = BadWindow; got_badwindow = False; } if (status == Success && type != None) { Window ret = kids[i]; if (version) *version = (char *) v; XFree (kids); return ret; } } if (kids) XFree (kids); return 0; } static int send_xscreensaver_command (Display *dpy, Atom command, long arg, Window *window_ret, char **error_ret) { int status = -1; char *v = 0; Window window = find_screensaver_window (dpy, &v); XWindowAttributes xgwa; char err[2048]; if (window_ret) *window_ret = window; if (!window) { sprintf (err, "no screensaver is running on display %s", DisplayString (dpy)); if (error_ret) { *error_ret = strdup (err); status = -1; goto DONE; } if (command == XA_EXIT) { /* Don't print an error if xscreensaver is already dead. */ status = 1; goto DONE; } fprintf (stderr, "%s: %s\n", progname, err); status = -1; goto DONE; } /* Select for property change events, so that we can read the response. */ XGetWindowAttributes (dpy, window, &xgwa); XSelectInput (dpy, window, xgwa.your_event_mask | PropertyChangeMask); if (command == XA_SCREENSAVER_STATUS || command == XA_SCREENSAVER_VERSION) { XClassHint hint; memset (&hint, 0, sizeof(hint)); if (!v || !*v) { sprintf (err, "version property not set on window 0x%x?", (unsigned int) window); if (error_ret) *error_ret = strdup (err); else fprintf (stderr, "%s: %s\n", progname, err); status = -1; goto DONE; } XGetClassHint(dpy, window, &hint); if (!hint.res_class) { sprintf (err, "class hints not set on window 0x%x?", (unsigned int) window); if (error_ret) *error_ret = strdup (err); else fprintf (stderr, "%s: %s\n", progname, err); status = -1; goto DONE; } fprintf (stdout, "%s %s", hint.res_class, v); if (command != XA_SCREENSAVER_STATUS) { fprintf (stdout, "\n"); } else { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *dataP = 0; if (XGetWindowProperty (dpy, RootWindow (dpy, 0), XA_SCREENSAVER_STATUS, 0, 999, False, XA_INTEGER, &type, &format, &nitems, &bytesafter, &dataP) == Success && type && dataP) { Atom blanked; time_t tt; char *s; Atom *data = (Atom *) dataP; if (type != XA_INTEGER || nitems < 3) { STATUS_LOSE: if (data) free (data); fprintf (stdout, "\n"); fflush (stdout); fprintf (stderr, "bad status format on root window.\n"); status = -1; goto DONE; } blanked = (Atom) data[0]; tt = (time_t) data[1]; if (tt <= (time_t) 666000000L) /* early 1991 */ goto STATUS_LOSE; if (blanked == XA_BLANK) fputs (": screen blanked since ", stdout); else if (blanked == XA_LOCK) fputs (": screen locked since ", stdout); else if (blanked == 0) /* suggestions for a better way to phrase this are welcome. */ fputs (": screen non-blanked since ", stdout); else /* `blanked' has an unknown value - fail. */ goto STATUS_LOSE; s = ctime(&tt); if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = 0; fputs (s, stdout); { int nhacks = nitems - 2; Bool any = False; int i; for (i = 0; i < nhacks; i++) if (data[i + 2] > 0) { any = True; break; } if (any && nhacks == 1) fprintf (stdout, " (hack #%d)\n", (int) data[2]); else if (any) { fprintf (stdout, " (hacks: "); for (i = 0; i < nhacks; i++) { fprintf (stdout, "#%d", (int) data[2 + i]); if (i != nhacks-1) fputs (", ", stdout); } fputs (")\n", stdout); } else fputs ("\n", stdout); } if (data) free (data); } else { if (dataP) XFree (dataP); fprintf (stdout, "\n"); fflush (stdout); fprintf (stderr, "no saver status on root window.\n"); status = -1; goto DONE; } } /* No need to read a response for these commands. */ status = 1; goto DONE; } else { XEvent event; long arg1 = arg; long arg2 = 0; if (arg < 0) abort(); else if (arg == 0 && command == XA_SELECT) abort(); else if (arg != 0 && command == XA_DEMO) { arg1 = 5000; /* version number of the XA_DEMO protocol, */ arg2 = arg; /* since it didn't use to take an argument. */ } event.xany.type = ClientMessage; event.xclient.display = dpy; event.xclient.window = window; event.xclient.message_type = XA_SCREENSAVER; event.xclient.format = 32; memset (&event.xclient.data, 0, sizeof(event.xclient.data)); event.xclient.data.l[0] = (long) command; event.xclient.data.l[1] = arg1; event.xclient.data.l[2] = arg2; if (! XSendEvent (dpy, window, False, 0L, &event)) { sprintf (err, "XSendEvent(dpy, 0x%x ...) failed.\n", (unsigned int) window); if (error_ret) *error_ret = strdup (err); else fprintf (stderr, "%s: %s\n", progname, err); status = -1; goto DONE; } } status = 0; DONE: if (v) free (v); XSync (dpy, 0); return status; } static Bool xscreensaver_command_event_p (Display *dpy, XEvent *event, XPointer arg) { return (event->xany.type == PropertyNotify && event->xproperty.state == PropertyNewValue && event->xproperty.atom == XA_SCREENSAVER_RESPONSE); } static int xscreensaver_command_response (Display *dpy, Window window, Bool verbose_p, Bool exiting_p, char **error_ret) { int sleep_count = 0; char err[2048]; XEvent event; Bool got_event = False; while (!(got_event = XCheckIfEvent(dpy, &event, &xscreensaver_command_event_p, 0)) && sleep_count++ < 10) { # if defined(HAVE_SELECT) /* Wait for an event, but don't wait longer than 1 sec. Note that we might do this multiple times if an event comes in, but it wasn't the event we're waiting for. */ int fd = XConnectionNumber(dpy); fd_set rset; struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO (&rset); FD_SET (fd, &rset); select (fd+1, &rset, 0, 0, &tv); # else /* !HAVE_SELECT */ sleep(1); # endif /* !HAVE_SELECT */ } if (!got_event) { sprintf (err, "no response to command."); if (error_ret) *error_ret = strdup (err); else fprintf (stderr, "%s: %s\n", progname, err); return -1; } else { Status st2; Atom type; int format; unsigned long nitems, bytesafter; unsigned char *msg = 0; XSync (dpy, False); if (old_handler) abort(); old_handler = XSetErrorHandler (BadWindow_ehandler); st2 = XGetWindowProperty (dpy, window, XA_SCREENSAVER_RESPONSE, 0, 1024, True, AnyPropertyType, &type, &format, &nitems, &bytesafter, &msg); XSync (dpy, False); XSetErrorHandler (old_handler); old_handler = 0; if (got_badwindow) { if (exiting_p) return 0; sprintf (err, "xscreensaver window unexpectedly deleted."); if (error_ret) *error_ret = strdup (err); else fprintf (stderr, "%s: %s\n", progname, err); return -1; } if (st2 == Success && type != None) { if (type != XA_STRING || format != 8) { sprintf (err, "unrecognized response property."); if (error_ret) *error_ret = strdup (err); else fprintf (stderr, "%s: %s\n", progname, err); if (msg) XFree (msg); return -1; } else if (!msg || (msg[0] != '+' && msg[0] != '-')) { sprintf (err, "unrecognized response message."); if (error_ret) *error_ret = strdup (err); else fprintf (stderr, "%s: %s\n", progname, err); if (msg) XFree (msg); return -1; } else { int ret = (msg[0] == '+' ? 0 : -1); sprintf (err, "%s: %s\n", progname, (char *) msg+1); if (error_ret) *error_ret = strdup (err); else if (verbose_p || ret != 0) fprintf ((ret < 0 ? stderr : stdout), "%s\n", err); XFree (msg); return ret; } } } return -1; /* warning suppression: not actually reached */ } int xscreensaver_command (Display *dpy, Atom command, long arg, Bool verbose_p, char **error_ret) { Window w = 0; int status = send_xscreensaver_command (dpy, command, arg, &w, error_ret); if (status == 0) status = xscreensaver_command_response (dpy, w, verbose_p, (command == XA_EXIT), error_ret); fflush (stdout); fflush (stderr); return (status < 0 ? status : 0); } void server_xscreensaver_version (Display *dpy, char **version_ret, char **user_ret, char **host_ret) { Window window = find_screensaver_window (dpy, 0); Atom type; int format; unsigned long nitems, bytesafter; if (version_ret) *version_ret = 0; if (user_ret) *user_ret = 0; if (host_ret) *host_ret = 0; if (!window) return; if (version_ret) { unsigned char *v = 0; XGetWindowProperty (dpy, window, XA_SCREENSAVER_VERSION, 0, 1, False, XA_STRING, &type, &format, &nitems, &bytesafter, &v); if (v) { *version_ret = strdup ((char *) v); XFree (v); } } if (user_ret || host_ret) { unsigned char *id = 0; const char *user = 0; const char *host = 0; XGetWindowProperty (dpy, window, XA_SCREENSAVER_ID, 0, 512, False, XA_STRING, &type, &format, &nitems, &bytesafter, &id); if (id && *id) { const char *old_tag = " on host "; const char *s = strstr ((char *) id, old_tag); if (s) { /* found ID of the form "1234 on host xyz". */ user = 0; host = s + strlen (old_tag); } else { char *o = 0, *p = 0, *c = 0; o = strchr ((char *) id, '('); if (o) p = strchr (o, '@'); if (p) c = strchr (p, ')'); if (c) { /* found ID of the form "1234 (user@host)". */ user = o+1; host = p+1; *p = 0; *c = 0; } } } if (user && *user && *user != '?') *user_ret = strdup (user); else *user_ret = 0; if (host && *host && *host != '?') *host_ret = strdup (host); else *host_ret = 0; if (id) XFree (id); } } xscreensaver-5.15/driver/remote.h000066400000000000000000000016061164314150500171160ustar00rootroot00000000000000/* xscreensaver-command, Copyright (c) 1991-1998 * by Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifndef _XSCREENSAVER_REMOTE_H_ #define _XSCREENSAVER_REMOTE_H_ extern int xscreensaver_command (Display *dpy, Atom command, long arg, Bool verbose_p, char **error_ret); extern void server_xscreensaver_version (Display *dpy, char **version_ret, char **user_ret, char **host_ret); #endif /* _XSCREENSAVER_REMOTE_H_ */ xscreensaver-5.15/driver/screens.c000066400000000000000000001016551164314150500172650ustar00rootroot00000000000000/* screens.c --- dealing with RANDR, Xinerama, and VidMode Viewports. * xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* There are a bunch of different mechanisms for multiple monitors * available in X. XScreenSaver needs to care about this for two * reasons: first, to ensure that all visible areas go black; and * second, so that the windows of screen savers exactly fill the * glass of each monitor (instead of one saver spanning multiple * monitors, or a monitor displaying only a sub-rectangle of the * screen saver.) * * 1) Multi-screen: * * This is the original way. Each monitor gets its own display * number. :0.0 is the first one, :0.1 is the next, etc. The * value of $DISPLAY determines which screen windows open on by * default. A single app can open windows on multiple screens * with the same display connection, but windows cannot be moved * from one screen to another. The mouse can be moved from one * screen to another, though. Screens may be different depths * (e.g., one can be TrueColor and one can be PseudoColor.) * Screens cannot be resized or moved without restarting X. * * Everyone hates this way of doing things because of the * inability to move a window from one screen to another without * restarting the application. * * 2) Xinerama: * * There is a single giant root window that spans all the * monitors. All monitors are the same depth, and windows can be * moved around. Applications can learn which rectangles are * actually visible on monitors by querying the Xinerama server * extension. (If you don't do that, you end up with dialog * boxes that try to appear in the middle of the screen actually * spanning the gap between two monitors.) * * Xinerama doesn't work with DRI, which means that if you use * it, you lose hardware acceleration on OpenGL programs. Also, * screens can't be resized or moved without restarting X. * * 3) Vidmode Viewports: * * With this extension, the root window can be bigger than the * monitor. Moving the mouse near the edges of the screen * scrolls around, like a pan-and-scan movie. There can also be * a hot key for changing the monitor's resolution (zooming * in/out). * * Trying to combine this with Xinerama crashes the server, so * you can only use this if you have only a single screen, or are * in old-multi-screen mode. * * Also, half the time it doesn't work at all: it tends to lie * about the size of the rectangle in use. * * 4) RANDR 1.0: * * The first version of the "Resize and Rotate" extension let you * change the resolution of a screen on the fly. The root window * would actually resize. However, it was also incompatible with * Xinerama (did it crash, or just do nothing? I can't remember) * so you needed to be in single-screen or old multi-screen mode. * I believe RANDR could co-exist with Vidmode Viewports, but I'm * not sure. * * 5) RANDR 1.2: * * Finally, RANDR added the functionality of Xinerama, plus some. * Each X screen (in the sense of #1, "multi-screen") can have a * number of sub-rectangles that are displayed on monitors, and * each of those sub-rectangles can be displayed on more than one * monitor. So it's possible (I think) to have a hybrid of * multi-screen and Xinerama (e.g., to have two monitors running * in one depth, and three monitors running in another?) * Typically though, there will be a single X screen, with * Xinerama-like division of that large root window onto multiple * monitors. Also everything's dynamic: monitors can be added, * removed, and resized at runtime. * * I believe that as of RANDR 1.2, the Xinerama extension still * exists but only as a compatiblity layer: it's actually * returning data from the RANDR extension. * * Though RANDR 1.2 allows the same image to be cloned onto more * than one monitor, and also allows one monitor to show a * subsection of something on another monitor (e.g., the * rectangles can be enclosed or overlap). Since there's no way * to put seperate savers on those duplicated-or-overlapping * monitors, xscreensaver just ignores them (which allows them to * display duplicates or overlaps). * * 5a) Nvidia fucks it up: * * Nvidia drivers as of Aug 2008 running in "TwinView" mode * apparently report correct screen geometry via Xinerama, but * report one giant screen via RANDR. The response from the * nvidia developers is, "we don't support RANDR, use Xinerama * instead." Which is a seriously lame answer. So, xscreensaver * has to query *both* extensions, and make a guess as to which * is to be believed. * * 5b) Also sometimes RANDR says stupid shit like, "You have one * screen, and it has no available orientations or sizes." * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_RANDR # include #endif /* HAVE_RANDR */ #ifdef HAVE_XINERAMA # include #endif /* HAVE_XINERAMA */ #ifdef HAVE_XF86VMODE # include #endif /* HAVE_XF86VMODE */ /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer #define XtAppContext void* #define XrmDatabase void* #define XtIntervalId void* #define XtPointer void* #define Widget void* #include "xscreensaver.h" #include "visual.h" typedef enum { S_SANE, S_ENCLOSED, S_DUPLICATE, S_OVERLAP, S_OFFSCREEN, S_DISABLED } monitor_sanity; /* 'typedef monitor' is in types.h */ struct _monitor { int id; char *desc; Screen *screen; int x, y, width, height; monitor_sanity sanity; /* I'm not crazy you're the one who's crazy */ int enemy; /* which monitor it overlaps or duplicates */ char *err; /* msg to print at appropriate later time; exists only on monitor #0. */ }; static Bool layouts_differ_p (monitor **a, monitor **b); static void free_monitors (monitor **monitors) { monitor **m2 = monitors; if (! monitors) return; while (*m2) { if ((*m2)->desc) free ((*m2)->desc); if ((*m2)->err) free ((*m2)->err); free (*m2); m2++; } free (monitors); } static char * append (char *s1, const char *s2) { char *s = (char *) malloc ((s1 ? strlen(s1) : 0) + (s2 ? strlen(s2) : 0) + 3); *s = 0; if (s1) strcat (s, s1); if (s1 && s2) strcat (s, "\n"); if (s2) strcat (s, s2); if (s1) free (s1); return s; } #ifdef HAVE_XINERAMA static monitor ** xinerama_scan_monitors (Display *dpy, char **errP) { Screen *screen = DefaultScreenOfDisplay (dpy); int event, error, nscreens, i; XineramaScreenInfo *xsi; monitor **monitors; if (! XineramaQueryExtension (dpy, &event, &error)) return 0; if (! XineramaIsActive (dpy)) return 0; xsi = XineramaQueryScreens (dpy, &nscreens); if (!xsi) return 0; monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors)); if (!monitors) return 0; for (i = 0; i < nscreens; i++) { monitor *m = (monitor *) calloc (1, sizeof (monitor)); monitors[i] = m; m->id = i; m->screen = screen; m->x = xsi[i].x_org; m->y = xsi[i].y_org; m->width = xsi[i].width; m->height = xsi[i].height; } return monitors; } #endif /* HAVE_XINERAMA */ #ifdef HAVE_XF86VMODE static monitor ** vidmode_scan_monitors (Display *dpy, char **errP) { int event, error, nscreens, i; monitor **monitors; /* Note that XF86VidModeGetViewPort() tends to be full of lies on laptops that have a docking station or external monitor that runs in a different resolution than the laptop's screen: http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=81593 http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=208417 http://bugs.xfree86.org/show_bug.cgi?id=421 Presumably this is fixed by using RANDR instead of VidMode. */ # ifdef HAVE_XINERAMA /* Attempts to use the VidMode extension when the Xinerama extension is active can result in a server crash! Yay! */ if (XQueryExtension (dpy, "XINERAMA", &error, &event, &error)) return 0; # endif /* !HAVE_XINERAMA */ if (! XF86VidModeQueryExtension (dpy, &event, &error)) return 0; nscreens = ScreenCount (dpy); monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors)); if (!monitors) return 0; for (i = 0; i < nscreens; i++) { monitor *m = (monitor *) calloc (1, sizeof (monitor)); XF86VidModeModeLine ml; int dot; Screen *screen = ScreenOfDisplay (dpy, i); monitors[i] = m; m->id = i; m->screen = screen; if (! safe_XF86VidModeGetViewPort (dpy, i, &m->x, &m->y)) m->x = m->y = -1; if (XF86VidModeGetModeLine (dpy, i, &dot, &ml)) { m->width = ml.hdisplay; m->height = ml.vdisplay; } /* Apparently, though the server stores the X position in increments of 1 pixel, it will only make changes to the *display* in some other increment. With XF86_SVGA on a Thinkpad, the display only updates in multiples of 8 pixels when in 8-bit mode, and in multiples of 4 pixels in 16-bit mode. I don't know what it does in 24- and 32-bit mode, because I don't have enough video memory to find out. I consider it a bug that XF86VidModeGetViewPort() is telling me the server's *target* scroll position rather than the server's *actual* scroll position. David Dawes agrees, and says they may fix this in XFree86 4.0, but it's notrivial. He also confirms that this behavior is server-dependent, so the actual scroll position cannot be reliably determined by the client. So... that means the only solution is to provide a ``sandbox'' around the blackout window -- we make the window be up to N pixels larger than the viewport on both the left and right sides. That means some part of the outer edges of each hack might not be visible, but screw it. I'm going to guess that 16 pixels is enough, and that the Y dimension doesn't have this problem. The drawback of doing this, of course, is that some of the screenhacks will still look pretty stupid -- for example, "slidescreen" will cut off the left and right edges of the grid, etc. */ # define FUDGE 16 if (m->x > 0 && m->x < m->width - ml.hdisplay) { /* Not at left edge or right edge: Round X position down to next lower multiple of FUDGE. Increase width by 2*FUDGE in case some server rounds up. */ m->x = ((m->x - 1) / FUDGE) * FUDGE; m->width += (FUDGE * 2); } # undef FUDGE } return monitors; } #endif /* HAVE_XF86VMODE */ #ifdef HAVE_RANDR static monitor ** randr_scan_monitors (Display *dpy, char **errP) { int event, error, major, minor, nscreens, i, j; monitor **monitors; Bool new_randr_p = False; if (! XRRQueryExtension (dpy, &event, &error)) return 0; if (! XRRQueryVersion (dpy, &major, &minor)) return 0; if (major <= 0) /* Protocol was still in flux back then -- fuck it. */ return 0; # ifdef HAVE_RANDR_12 new_randr_p = (major > 1 || (major == 1 && minor >= 2)); # endif if (! new_randr_p) /* RANDR 1.0 -- no Xinerama-like virtual screens. */ nscreens = ScreenCount (dpy); else /* RANDR 1.2 or newer -- built-in Xinerama */ { # ifdef HAVE_RANDR_12 int xsc = ScreenCount (dpy); nscreens = 0; /* Add up the virtual screens on each X screen. */ for (i = 0; i < xsc; i++) { XRRScreenResources *res = XRRGetScreenResources (dpy, RootWindow (dpy, i)); nscreens += res->noutput; XRRFreeScreenResources (res); } # endif /* HAVE_RANDR_12 */ } if (nscreens <= 0) { *errP = append (*errP, "WARNING: RANDR reported no screens! Ignoring it."); return 0; } monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors)); if (!monitors) return 0; for (i = 0, j = 0; i < ScreenCount (dpy); i++) { Screen *screen = ScreenOfDisplay (dpy, i); if (! new_randr_p) /* RANDR 1.0 */ { XRRScreenConfiguration *rrc; monitor *m = (monitor *) calloc (1, sizeof (monitor)); monitors[i] = m; m->screen = screen; m->id = i; rrc = XRRGetScreenInfo (dpy, RootWindowOfScreen (screen)); if (rrc) { SizeID size = -1; Rotation rot = ~0; XRRScreenSize *rrsizes; int nsizes = 0; size = XRRConfigCurrentConfiguration (rrc, &rot); rrsizes = XRRConfigSizes (rrc, &nsizes); if (nsizes <= 0) /* WTF? Shouldn't happen but does. */ { m->width = DisplayWidth (dpy, i); m->height = DisplayHeight (dpy, i); } else if (rot & (RR_Rotate_90|RR_Rotate_270)) { m->width = rrsizes[size].height; m->height = rrsizes[size].width; } else { m->width = rrsizes[size].width; m->height = rrsizes[size].height; } /* don't free 'rrsizes' */ XRRFreeScreenConfigInfo (rrc); } } else /* RANDR 1.2 or newer */ { # ifdef HAVE_RANDR_12 int k; XRRScreenResources *res = XRRGetScreenResources (dpy, RootWindowOfScreen (screen)); for (k = 0; k < res->noutput; k++, j++) { monitor *m = (monitor *) calloc (1, sizeof (monitor)); XRROutputInfo *rroi = XRRGetOutputInfo (dpy, res, res->outputs[k]); RRCrtc crtc = (rroi->crtc ? rroi->crtc : rroi->ncrtc ? rroi->crtcs[0] : 0); XRRCrtcInfo *crtci = (crtc ? XRRGetCrtcInfo(dpy, res, crtc) : 0); monitors[j] = m; m->screen = screen; m->id = (i * 1000) + j; m->desc = (rroi->name ? strdup (rroi->name) : 0); if (crtci) { /* Note: if the screen is rotated, XRRConfigSizes contains the unrotated WxH, but XRRCrtcInfo contains rotated HxW. */ m->x = crtci->x; m->y = crtci->y; m->width = crtci->width; m->height = crtci->height; } if (rroi->connection == RR_Disconnected) m->sanity = S_DISABLED; /* #### do the same for RR_UnknownConnection? */ if (crtci) XRRFreeCrtcInfo (crtci); XRRFreeOutputInfo (rroi); } XRRFreeScreenResources (res); # endif /* HAVE_RANDR_12 */ } } /* Work around more fucking brain damage. */ { int ok = 0; int i = 0; while (monitors[i]) { if (monitors[i]->width != 0 && monitors[i]->height != 0) ok++; i++; } if (! ok) { *errP = append (*errP, "WARNING: RANDR says all screens are 0x0! Ignoring it."); free_monitors (monitors); monitors = 0; } } return monitors; } #endif /* HAVE_RANDR */ static monitor ** basic_scan_monitors (Display *dpy, char **errP) { int nscreens = ScreenCount (dpy); int i; monitor **monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors)); if (!monitors) return 0; for (i = 0; i < nscreens; i++) { Screen *screen = ScreenOfDisplay (dpy, i); monitor *m = (monitor *) calloc (1, sizeof (monitor)); monitors[i] = m; m->id = i; m->screen = screen; m->x = 0; m->y = 0; m->width = WidthOfScreen (screen); m->height = HeightOfScreen (screen); } return monitors; } #if defined(HAVE_RANDR) && defined(HAVE_XINERAMA) /* From: Aaron Plattner Date: August 7, 2008 10:21:25 AM PDT To: linux-bugs@nvidia.com The NVIDIA X driver does not yet support RandR 1.2. The X server has a compatibility layer in it that allows RandR 1.2 clients to talk to RandR 1.1 drivers through an RandR 1.2 pseudo-output called "default". This reports the total combined resolution of the TwinView display, since it doesn't have any visibility into TwinView metamodes. There is no way for the driver to prevent the server from turning on this compatibility layer. The intention is for X client applications to continue to use the Xinerama extension to query the screen geometry. RandR 1.2 reports its own Xinerama info for this purpose. I would recommend against modifying xscreensaver to try to get this information from RandR. */ static monitor ** randr_versus_xinerama_fight (Display *dpy, monitor **randr_monitors, char **errP) { monitor **xinerama_monitors; if (!randr_monitors) return 0; xinerama_monitors = xinerama_scan_monitors (dpy, errP); if (!xinerama_monitors) return randr_monitors; if (! layouts_differ_p (randr_monitors, xinerama_monitors)) { free_monitors (xinerama_monitors); return randr_monitors; } else if ( randr_monitors[0] && !randr_monitors[1] && /* 1 monitor */ xinerama_monitors[0] && xinerama_monitors[1]) /* >1 monitor */ { *errP = append (*errP, "WARNING: RANDR reports 1 screen but Xinerama\n" "\t\treports multiple. Believing Xinerama."); free_monitors (randr_monitors); return xinerama_monitors; } else { *errP = append (*errP, "WARNING: RANDR and Xinerama report different\n" "\t\tscreen layouts! Believing RANDR."); free_monitors (xinerama_monitors); return randr_monitors; } } #endif /* HAVE_RANDR && HAVE_XINERAMA */ #ifdef DEBUG_MULTISCREEN /* If DEBUG_MULTISCREEN is defined, then in "-debug" mode, xscreensaver will pretend that it is changing the number of connected monitors every few seconds, using the geometries in the following list, for stress-testing purposes. */ static monitor ** debug_scan_monitors (Display *dpy, char **errP) { static const char * const geoms[] = { "1600x1028+0+22", "1024x768+0+22", "800x600+0+22", "800x600+0+22,800x600+800+22", "800x600+0+22,800x600+800+22,800x600+300+622", "800x600+0+22,800x600+800+22,800x600+0+622,800x600+800+622", "640x480+0+22,640x480+640+22,640x480+0+502,640x480+640+502", "640x480+240+22,640x480+0+502,640x480+640+502", "640x480+0+200,640x480+640+200", "800x600+400+22", "320x200+0+22,320x200+320+22,320x200+640+22,320x200+960+22,320x200+0+222,320x200+320+222,320x200+640+222,320x200+960+222,320x200+0+422,320x200+320+422,320x200+640+422,320x200+960+422,320x200+0+622,320x200+320+622,320x200+640+622,320x200+960+622,320x200+0+822,320x200+320+822,320x200+640+822,320x200+960+822" }; static int index = 0; monitor **monitors = (monitor **) calloc (100, sizeof(*monitors)); int nscreens = 0; Screen *screen = DefaultScreenOfDisplay (dpy); char *s = strdup (geoms[index]); char *token = strtok (s, ","); while (token) { monitor *m = calloc (1, sizeof (monitor)); char c; m->id = nscreens; m->screen = screen; if (4 != sscanf (token, "%dx%d+%d+%d%c", &m->width, &m->height, &m->x, &m->y, &c)) abort(); m->width -= 2; m->height -= 2; monitors[nscreens++] = m; token = strtok (0, ","); } free (s); index = (index+1) % countof(geoms); return monitors; } #endif /* DEBUG_MULTISCREEN */ #ifdef QUAD_MODE static monitor ** quadruple (monitor **monitors, Bool debug_p, char **errP) { int i, j, count = 0; monitor **monitors2; while (monitors[count]) count++; monitors2 = (monitor **) calloc (count * 4 + 1, sizeof(*monitors)); if (!monitors2) abort(); for (i = 0, j = 0; i < count; i++) { int k; for (k = 0; k < 4; k++) { monitors2[j+k] = (monitor *) calloc (1, sizeof (monitor)); *monitors2[j+k] = *monitors[i]; monitors2[j+k]->width /= (debug_p ? 4 : 2); monitors2[j+k]->height /= 2; monitors2[j+k]->id = (monitors[i]->id * 4) + k; monitors2[j+k]->name = (monitors[i]->name ? strdup (monitors[i]->name) : 0); } monitors2[j+1]->x += monitors2[j]->width; monitors2[j+2]->y += monitors2[j]->height; monitors2[j+3]->x += monitors2[j]->width; monitors2[j+3]->y += monitors2[j]->height; j += 4; } free_monitors (monitors); return monitors2; } #endif /* QUAD_MODE */ static monitor ** scan_monitors (saver_info *si) { saver_preferences *p = &si->prefs; monitor **monitors = 0; char *err = 0; # ifdef DEBUG_MULTISCREEN if (! monitors) monitors = debug_scan_monitors (si->dpy, &err); # endif # ifdef HAVE_RANDR if (! p->getviewport_full_of_lies_p) if (! monitors) monitors = randr_scan_monitors (si->dpy, &err); # ifdef HAVE_XINERAMA monitors = randr_versus_xinerama_fight (si->dpy, monitors, &err); # endif # endif /* HAVE_RANDR */ # ifdef HAVE_XF86VMODE if (! monitors) monitors = vidmode_scan_monitors (si->dpy, &err); # endif # ifdef HAVE_XINERAMA if (! monitors) monitors = xinerama_scan_monitors (si->dpy, &err); # endif if (! monitors) monitors = basic_scan_monitors (si->dpy, &err); # ifdef QUAD_MODE if (p->quad_p) monitors = quadruple (monitors, p->debug_p, &err); # endif if (monitors && err) monitors[0]->err = err; return monitors; } static Bool monitors_overlap_p (monitor *a, monitor *b) { /* Two rectangles overlap if the max of the tops is less than the min of the bottoms and the max of the lefts is less than the min of the rights. */ # undef MAX # undef MIN # define MAX(A,B) ((A)>(B)?(A):(B)) # define MIN(A,B) ((A)<(B)?(A):(B)) int maxleft = MAX(a->x, b->x); int maxtop = MAX(a->y, b->y); int minright = MIN(a->x + a->width - 1, b->x + b->width); int minbot = MIN(a->y + a->height - 1, b->y + b->height); return (maxtop < minbot && maxleft < minright); } static Bool plausible_aspect_ratio_p (monitor **monitors) { /* Modern wide-screen monitors come in the following aspect ratios: One monitor: If you tack a 640x480 monitor onto the right, the ratio is: 16 x 9 --> 1.78 852 x 480 --> 1.77 852+640 x 480 --> 3.11 "SD 480p" 1280 x 720 --> 1.78 1280+640 x 720 --> 2.67 "HD 720p" 1280 x 920 --> 1.39 1280+640 x 920 --> 2.09 1366 x 768 --> 1.78 1366+640 x 768 --> 2.61 "HD 768p" 1440 x 900 --> 1.60 1440+640 x 900 --> 2.31 1680 x 1050 --> 1.60 1680+640 x 1050 --> 2.21 1690 x 1050 --> 1.61 1690+640 x 1050 --> 2.22 1920 x 1080 --> 1.78 1920+640 x 1080 --> 2.37 "HD 1080p" 1920 x 1200 --> 1.60 1920+640 x 1200 --> 2.13 2560 x 1600 --> 1.60 2560+640 x 1600 --> 2.00 So that implies that if we ever see an aspect ratio >= 2.0, we can be pretty sure that the X server is lying to us, and that's actually two monitors, not one. */ if (monitors[0] && !monitors[1] && /* exactly 1 monitor */ monitors[0]->height && monitors[0]->width / (double) monitors[0]->height >= 1.9) return False; else return True; } /* Mark the ones that overlap, etc. */ static void check_monitor_sanity (monitor **monitors) { int i, j, count = 0; while (monitors[count]) count++; # define X1 monitors[i]->x # define X2 monitors[j]->x # define Y1 monitors[i]->y # define Y2 monitors[j]->y # define W1 monitors[i]->width # define W2 monitors[j]->width # define H1 monitors[i]->height # define H2 monitors[j]->height /* If a monitor is enclosed by any other monitor, that's insane. */ for (i = 0; i < count; i++) for (j = 0; j < count; j++) if (i != j && monitors[i]->sanity == S_SANE && monitors[j]->sanity == S_SANE && monitors[i]->screen == monitors[j]->screen && X2 >= X1 && Y2 >= Y1 && (X2+W2) <= (X1+W1) && (Y2+H2) <= (Y1+H1)) { if (X1 == X2 && Y1 == Y2 && W1 == W2 && H1 == H2) monitors[j]->sanity = S_DUPLICATE; else monitors[j]->sanity = S_ENCLOSED; monitors[j]->enemy = i; } /* After checking for enclosure, check for other lossage against earlier monitors. We do enclosure first so that we make sure to pick the larger one. */ for (i = 0; i < count; i++) for (j = 0; j < i; j++) { if (monitors[i]->sanity != S_SANE) continue; /* already marked */ if (monitors[j]->sanity != S_SANE) continue; if (monitors[i]->screen != monitors[j]->screen) continue; if (monitors_overlap_p (monitors[i], monitors[j])) { monitors[i]->sanity = S_OVERLAP; monitors[i]->enemy = j; } } /* Finally, make sure all monitors have sane positions and sizes. Xinerama sometimes reports 1024x768 VPs at -1936862040, -1953705044. */ for (i = 0; i < count; i++) { if (monitors[i]->sanity != S_SANE) continue; /* already marked */ if (X1 < 0 || Y1 < 0 || W1 <= 0 || H1 <= 0 || X1+W1 >= 0x7FFF || Y1+H1 >= 0x7FFF) { monitors[i]->sanity = S_OFFSCREEN; monitors[i]->enemy = 0; } } # undef X1 # undef X2 # undef Y1 # undef Y2 # undef W1 # undef W2 # undef H1 # undef H2 } static Bool layouts_differ_p (monitor **a, monitor **b) { if (!a || !b) return True; while (1) { if (!*a) break; if (!*b) break; if ((*a)->screen != (*b)->screen || (*a)->x != (*b)->x || (*a)->y != (*b)->y || (*a)->width != (*b)->width || (*a)->height != (*b)->height) return True; a++; b++; } if (*a) return True; if (*b) return True; return False; } void describe_monitor_layout (saver_info *si) { monitor **monitors = si->monitor_layout; int count = 0; int good_count = 0; int bad_count = 0; int implausible_p = !plausible_aspect_ratio_p (monitors); while (monitors[count]) { if (monitors[count]->sanity == S_SANE) good_count++; else bad_count++; count++; } if (monitors[0]->err) /* deferred error msg */ { char *token = strtok (monitors[0]->err, "\n"); while (token) { fprintf (stderr, "%s: %s\n", blurb(), token); token = strtok (0, "\n"); } free (monitors[0]->err); monitors[0]->err = 0; } if (count == 0) fprintf (stderr, "%s: no screens!\n", blurb()); else { int i; fprintf (stderr, "%s: screens in use: %d\n", blurb(), good_count); for (i = 0; i < count; i++) { monitor *m = monitors[i]; if (m->sanity != S_SANE) continue; fprintf (stderr, "%s: %3d/%d: %dx%d+%d+%d", blurb(), m->id, screen_number (m->screen), m->width, m->height, m->x, m->y); if (m->desc && *m->desc) fprintf (stderr, " (%s)", m->desc); fprintf (stderr, "\n"); } if (bad_count > 0) { fprintf (stderr, "%s: rejected screens: %d\n", blurb(), bad_count); for (i = 0; i < count; i++) { monitor *m = monitors[i]; monitor *e = monitors[m->enemy]; if (m->sanity == S_SANE) continue; fprintf (stderr, "%s: %3d/%d: %dx%d+%d+%d", blurb(), m->id, screen_number (m->screen), m->width, m->height, m->x, m->y); if (m->desc && *m->desc) fprintf (stderr, " (%s)", m->desc); fprintf (stderr, " -- "); switch (m->sanity) { case S_SANE: abort(); break; case S_ENCLOSED: fprintf (stderr, "enclosed by %d (%dx%d+%d+%d)\n", e->id, e->width, e->height, e->x, e->y); break; case S_DUPLICATE: fprintf (stderr, "duplicate of %d\n", e->id); break; case S_OVERLAP: fprintf (stderr, "overlaps %d (%dx%d+%d+%d)\n", e->id, e->width, e->height, e->x, e->y); break; case S_OFFSCREEN: fprintf (stderr, "off screen (%dx%d)\n", WidthOfScreen (e->screen), HeightOfScreen (e->screen)); break; case S_DISABLED: fprintf (stderr, "output disabled\n"); break; } } } if (implausible_p) fprintf (stderr, "%s: WARNING: single screen aspect ratio is %dx%d = %.2f\n" "%s: probable X server bug in Xinerama/RANDR!\n", blurb(), monitors[0]->width, monitors[0]->height, monitors[0]->width / (double) monitors[0]->height, blurb()); } } /* Synchronize the contents of si->ssi to the current state of the monitors. Doesn't change anything if nothing has changed; otherwise, alters and reuses existing saver_screen_info structs as much as possible. Returns True if anything changed. */ Bool update_screen_layout (saver_info *si) { monitor **monitors = scan_monitors (si); int count = 0; int good_count = 0; int i, j; int seen_screens[100] = { 0, }; if (! layouts_differ_p (monitors, si->monitor_layout)) { free_monitors (monitors); return False; } free_monitors (si->monitor_layout); si->monitor_layout = monitors; check_monitor_sanity (si->monitor_layout); while (monitors[count]) { if (monitors[count]->sanity == S_SANE) good_count++; count++; } if (si->ssi_count == 0) { si->ssi_count = 10; si->screens = (saver_screen_info *) calloc (sizeof(*si->screens), si->ssi_count); } if (si->ssi_count <= good_count) { si->ssi_count = good_count + 10; si->screens = (saver_screen_info *) realloc (si->screens, sizeof(*si->screens) * si->ssi_count); memset (si->screens + si->nscreens, 0, sizeof(*si->screens) * (si->ssi_count - si->nscreens)); } if (! si->screens) abort(); si->nscreens = good_count; /* Regenerate the list of GL visuals as needed. */ if (si->best_gl_visuals) free (si->best_gl_visuals); si->best_gl_visuals = 0; for (i = 0, j = 0; i < count; i++) { monitor *m = monitors[i]; saver_screen_info *ssi = &si->screens[j]; Screen *old_screen = ssi->screen; int sn; if (monitors[i]->sanity != S_SANE) continue; ssi->global = si; ssi->number = j; sn = screen_number (m->screen); ssi->screen = m->screen; ssi->real_screen_number = sn; ssi->real_screen_p = (seen_screens[sn] == 0); seen_screens[sn]++; ssi->default_visual = get_visual_resource (ssi->screen, "visualID", "VisualID", False); ssi->current_visual = ssi->default_visual; ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual); /* If the screen changed (or if this is the first time) we need a new toplevel shell for this screen's depth. */ if (ssi->screen != old_screen) initialize_screen_root_widget (ssi); ssi->poll_mouse_last_root_x = -1; ssi->poll_mouse_last_root_y = -1; ssi->x = m->x; ssi->y = m->y; ssi->width = m->width; ssi->height = m->height; # ifndef DEBUG_MULTISCREEN { saver_preferences *p = &si->prefs; if (p->debug_p # ifdef QUAD_MODE && !p->quad_p # endif ) ssi->width /= 2; } # endif j++; } si->default_screen = &si->screens[0]; return True; } xscreensaver-5.15/driver/screensaver-properties.desktop.in000066400000000000000000000003071164314150500241610ustar00rootroot00000000000000[Desktop Entry] Exec=xscreensaver-demo Icon=xscreensaver Terminal=false _Name=Screensaver _Comment=Change screensaver properties Type=Application Categories=Settings;DesktopSettings;Security;X-XFCE; xscreensaver-5.15/driver/setuid.c000066400000000000000000000246631164314150500171230ustar00rootroot00000000000000/* setuid.c --- management of runtime privileges. * xscreensaver, Copyright (c) 1993-1998, 2005 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include /* not used for much... */ /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer #define XtAppContext void* #define XrmDatabase void* #define XtIntervalId void* #define XtPointer void* #define Widget void* #include "xscreensaver.h" #ifndef EPERM #include #endif #include /* for getpwnam() and struct passwd */ #include /* for getgrgid() and struct group */ static const char * uid_gid_string (uid_t uid, gid_t gid) { static char buf[255]; struct passwd *p = 0; struct group *g = 0; p = getpwuid (uid); g = getgrgid (gid); sprintf (buf, "%.100s/%.100s (%ld/%ld)", (p && p->pw_name ? p->pw_name : "???"), (g && g->gr_name ? g->gr_name : "???"), (long) uid, (long) gid); return buf; } void describe_uids (saver_info *si, FILE *out) { uid_t uid = getuid(); gid_t gid = getgid(); uid_t euid = geteuid(); gid_t egid = getegid(); char *s1 = strdup (uid_gid_string (uid, gid)); char *s2 = strdup (uid_gid_string (euid, egid)); if (si->orig_uid && *si->orig_uid && (!!strcmp (si->orig_uid, s1) || !!strcmp (si->orig_uid, s2))) fprintf (out, "%s: initial effective uid/gid was %s\n", blurb(), si->orig_uid); fprintf (out, "%s: running as %s", blurb(), s1); if (uid != euid || gid != egid) fprintf (out, "; effectively %s", s2); fprintf(out, "\n"); free(s1); free(s2); } /* Returns true if we need to call setgroups(). Without calling setgroups(), the process will retain any supplementary gids associated with the uid, e.g.: % groups root root : root bin daemon sys adm disk wheel However, setgroups() can only be called by root, and returns EPERM for other users even if the call would be a no-op (e.g., setting the group list to the current list.) So, to avoid that spurious error, before calling setgroups() we first check whether the current list of groups contains only one element, our target group. If so, we don't need to call setgroups(). */ static int setgroups_needed_p (uid_t target_group) { gid_t groups[1024]; int n, size; size = sizeof(groups) / sizeof(gid_t); n = getgroups (size - 1, groups); if (n < 0) { char buf [1024]; sprintf (buf, "%s: getgroups(%ld, ...)", blurb(), (long int)(size - 1)); perror (buf); return 1; } else if (n == 0) /* an empty list means only egid is in effect. */ return 0; else if (n == 1 && groups[0] == target_group) /* one element, the target */ return 0; else /* more than one, or the wrong one. */ return 1; } static int set_ids_by_number (uid_t uid, gid_t gid, char **message_ret) { int uid_errno = 0; int gid_errno = 0; int sgs_errno = 0; struct passwd *p = getpwuid (uid); struct group *g = getgrgid (gid); if (message_ret) *message_ret = 0; /* Rumor has it that some implementations of of setuid() do nothing when called with -1; therefore, if the "nobody" user has a uid of -1, then that would be Really Bad. Rumor further has it that such systems really ought to be using -2 for "nobody", since that works. So, if we get a uid (or gid, for good measure) of -1, switch to -2 instead. Note that this must be done after we've looked up the user/group names with getpwuid(-1) and/or getgrgid(-1). */ if (gid == (gid_t) -1) gid = (gid_t) -2; if (uid == (uid_t) -1) uid = (uid_t) -2; errno = 0; if (setgroups_needed_p (gid) && setgroups (1, &gid) < 0) sgs_errno = errno ? errno : -1; errno = 0; if (setgid (gid) != 0) gid_errno = errno ? errno : -1; errno = 0; if (setuid (uid) != 0) uid_errno = errno ? errno : -1; if (uid_errno == 0 && gid_errno == 0 && sgs_errno == 0) { static char buf [1024]; sprintf (buf, "changed uid/gid to %.100s/%.100s (%ld/%ld).", (p && p->pw_name ? p->pw_name : "???"), (g && g->gr_name ? g->gr_name : "???"), (long) uid, (long) gid); if (message_ret) *message_ret = buf; return 0; } else { char buf [1024]; gid_t groups[1024]; int n, size; if (sgs_errno) { sprintf (buf, "%s: couldn't setgroups to %.100s (%ld)", blurb(), (g && g->gr_name ? g->gr_name : "???"), (long) gid); if (sgs_errno == -1) fprintf(stderr, "%s: unknown error\n", buf); else { errno = sgs_errno; perror(buf); } fprintf (stderr, "%s: effective group list: ", blurb()); size = sizeof(groups) / sizeof(gid_t); n = getgroups (size - 1, groups); if (n < 0) fprintf (stderr, "unknown!\n"); else { int i; fprintf (stderr, "["); for (i = 0; i < n; i++) { g = getgrgid (groups[i]); if (i > 0) fprintf (stderr, ", "); if (g && g->gr_name) fprintf (stderr, "%s", g->gr_name); else fprintf (stderr, "%ld", (long) groups[i]); } fprintf (stderr, "]\n"); } } if (gid_errno) { sprintf (buf, "%s: couldn't set gid to %.100s (%ld)", blurb(), (g && g->gr_name ? g->gr_name : "???"), (long) gid); if (gid_errno == -1) fprintf(stderr, "%s: unknown error\n", buf); else { errno = gid_errno; perror(buf); } } if (uid_errno) { sprintf (buf, "%s: couldn't set uid to %.100s (%ld)", blurb(), (p && p->pw_name ? p->pw_name : "???"), (long) uid); if (uid_errno == -1) fprintf(stderr, "%s: unknown error\n", buf); else { errno = uid_errno; perror(buf); } } return -1; } } /* If we've been run as setuid or setgid to someone else (most likely root) turn off the extra permissions so that random user-specified programs don't get special privileges. (On some systems it is necessary to install this program as setuid root in order to read the passwd file to implement lock-mode.) *** WARNING: DO NOT DISABLE ANY OF THE FOLLOWING CODE! If you do so, you will open a security hole. See the sections of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", and "USING XDM". */ void hack_uid (saver_info *si) { /* Discard privileges, and set the effective user/group ids to the real user/group ids. That is, give up our "chmod +s" rights. */ { uid_t euid = geteuid(); gid_t egid = getegid(); uid_t uid = getuid(); gid_t gid = getgid(); si->orig_uid = strdup (uid_gid_string (euid, egid)); if (uid != euid || gid != egid) if (set_ids_by_number (uid, gid, &si->uid_message) != 0) saver_exit (si, 1, 0); } /* Locking can't work when running as root, because we have no way of knowing what the user id of the logged in user is (so we don't know whose password to prompt for.) *** WARNING: DO NOT DISABLE THIS CODE! If you do so, you will open a security hole. See the sections of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", and "USING XDM". */ if (getuid() == (uid_t) 0) { si->locking_disabled_p = True; si->nolock_reason = "running as root"; } /* If we're running as root, switch to a safer user. This is above and beyond the fact that we've disabling locking, above -- the theory is that running graphics demos as root is just always a stupid thing to do, since they have probably never been security reviewed and are more likely to be buggy than just about any other kind of program. (And that assumes non-malicious code. There are also attacks here.) *** WARNING: DO NOT DISABLE THIS CODE! If you do so, you will open a security hole. See the sections of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", and "USING XDM". */ if (getuid() == (uid_t) 0) { struct passwd *p; p = getpwnam ("nobody"); if (! p) p = getpwnam ("noaccess"); if (! p) p = getpwnam ("daemon"); if (! p) { fprintf (stderr, "%s: running as root, and couldn't find a safer uid.\n", blurb()); saver_exit(si, 1, 0); } if (set_ids_by_number (p->pw_uid, p->pw_gid, &si->uid_message) != 0) saver_exit (si, -1, 0); } /* If there's anything even remotely funny looking about the passwd struct, or if we're running as some other user from the list below (a non-comprehensive selection of users known to be privileged in some way, and not normal end-users) then disable locking. If it was possible, switching to "nobody" would be the thing to do, but only root itself has the privs to do that. *** WARNING: DO NOT DISABLE THIS CODE! If you do so, you will open a security hole. See the sections of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", and "USING XDM". */ { uid_t uid = getuid (); /* get it again */ struct passwd *p = getpwuid (uid); /* get it again */ if (!p || uid == (uid_t) 0 || uid == (uid_t) -1 || uid == (uid_t) -2 || p->pw_uid == (uid_t) 0 || p->pw_uid == (uid_t) -1 || p->pw_uid == (uid_t) -2 || !p->pw_name || !*p->pw_name || !strcmp (p->pw_name, "root") || !strcmp (p->pw_name, "nobody") || !strcmp (p->pw_name, "noaccess") || !strcmp (p->pw_name, "operator") || !strcmp (p->pw_name, "daemon") || !strcmp (p->pw_name, "bin") || !strcmp (p->pw_name, "adm") || !strcmp (p->pw_name, "sys") || !strcmp (p->pw_name, "games")) { static char buf [1024]; sprintf (buf, "running as %.100s", (p && p->pw_name && *p->pw_name ? p->pw_name : "")); si->nolock_reason = buf; si->locking_disabled_p = True; si->dangerous_uid_p = True; } } } xscreensaver-5.15/driver/splash.c000066400000000000000000000635201164314150500171130ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "xscreensaver.h" #include "resources.h" #undef MAX #define MAX(a,b) ((a)>(b)?(a):(b)) void draw_shaded_rectangle (Display *dpy, Window window, int x, int y, int width, int height, int thickness, unsigned long top_color, unsigned long bottom_color) { XPoint points[4]; XGCValues gcv; GC gc1, gc2; if (thickness == 0) return; gcv.foreground = top_color; gc1 = XCreateGC (dpy, window, GCForeground, &gcv); gcv.foreground = bottom_color; gc2 = XCreateGC (dpy, window, GCForeground, &gcv); points [0].x = x; points [0].y = y; points [1].x = x + width; points [1].y = y; points [2].x = x + width - thickness; points [2].y = y + thickness; points [3].x = x; points [3].y = y + thickness; XFillPolygon (dpy, window, gc1, points, 4, Convex, CoordModeOrigin); points [0].x = x; points [0].y = y + thickness; points [1].x = x; points [1].y = y + height; points [2].x = x + thickness; points [2].y = y + height - thickness; points [3].x = x + thickness; points [3].y = y + thickness; XFillPolygon (dpy, window, gc1, points, 4, Convex, CoordModeOrigin); points [0].x = x + width; points [0].y = y; points [1].x = x + width - thickness; points [1].y = y + thickness; points [2].x = x + width - thickness; points [2].y = y + height - thickness; points [3].x = x + width; points [3].y = y + height - thickness; XFillPolygon (dpy, window, gc2, points, 4, Convex, CoordModeOrigin); points [0].x = x; points [0].y = y + height; points [1].x = x + width; points [1].y = y + height; points [2].x = x + width; points [2].y = y + height - thickness; points [3].x = x + thickness; points [3].y = y + height - thickness; XFillPolygon (dpy, window, gc2, points, 4, Convex, CoordModeOrigin); XFreeGC (dpy, gc1); XFreeGC (dpy, gc2); } int string_width (XFontStruct *font, char *s) { return XTextWidth(font, s, strlen(s)); } static void update_splash_window (saver_info *si); static void draw_splash_window (saver_info *si); static void destroy_splash_window (saver_info *si); static void unsplash_timer (XtPointer closure, XtIntervalId *id); static void do_demo (saver_screen_info *ssi); #ifdef PREFS_BUTTON static void do_prefs (saver_screen_info *ssi); #endif /* PREFS_BUTTON */ static void do_help (saver_screen_info *ssi); struct splash_dialog_data { saver_screen_info *prompt_screen; XtIntervalId timer; Dimension width; Dimension height; char *heading_label; char *body_label; char *body2_label; char *demo_label; #ifdef PREFS_BUTTON char *prefs_label; #endif /* PREFS_BUTTON */ char *help_label; XFontStruct *heading_font; XFontStruct *body_font; XFontStruct *button_font; Pixel foreground; Pixel background; Pixel border; Pixel button_foreground; Pixel button_background; Pixel shadow_top; Pixel shadow_bottom; Dimension logo_width; Dimension logo_height; Dimension internal_border; Dimension shadow_width; Dimension button_width, button_height; Dimension demo_button_x, demo_button_y; #ifdef PREFS_BUTTON Dimension prefs_button_x, prefs_button_y; #endif /* PREFS_BUTTON */ Dimension help_button_x, help_button_y; Pixmap logo_pixmap; Pixmap logo_clipmask; int logo_npixels; unsigned long *logo_pixels; int pressed; }; void make_splash_dialog (saver_info *si) { int x, y, bw; XSetWindowAttributes attrs; unsigned long attrmask = 0; splash_dialog_data *sp; saver_screen_info *ssi; Colormap cmap; char *f; if (si->sp_data) return; if (!si->prefs.splash_p || si->prefs.splash_duration <= 0) return; ssi = &si->screens[mouse_screen (si)]; if (!ssi || !ssi->screen) return; /* WTF? Trying to splash while no screens connected? */ cmap = DefaultColormapOfScreen (ssi->screen); sp = (splash_dialog_data *) calloc (1, sizeof(*sp)); sp->prompt_screen = ssi; sp->heading_label = get_string_resource (si->dpy, "splash.heading.label", "Dialog.Label.Label"); sp->body_label = get_string_resource (si->dpy, "splash.body.label", "Dialog.Label.Label"); sp->body2_label = get_string_resource (si->dpy, "splash.body2.label", "Dialog.Label.Label"); sp->demo_label = get_string_resource (si->dpy, "splash.demo.label", "Dialog.Button.Label"); #ifdef PREFS_BUTTON sp->prefs_label = get_string_resource (si->dpy, "splash.prefs.label", "Dialog.Button.Label"); #endif /* PREFS_BUTTON */ sp->help_label = get_string_resource (si->dpy, "splash.help.label", "Dialog.Button.Label"); if (!sp->heading_label) sp->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY"); if (!sp->body_label) sp->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY"); if (!sp->body2_label) sp->body2_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY"); if (!sp->demo_label) sp->demo_label = strdup("ERROR"); #ifdef PREFS_BUTTON if (!sp->prefs_label) sp->prefs_label = strdup("ERROR"); #endif /* PREFS_BUTTON */ if (!sp->help_label) sp->help_label = strdup("ERROR"); /* Put the version number in the label. */ { char *s = (char *) malloc (strlen(sp->heading_label) + 20); sprintf(s, sp->heading_label, si->version); free (sp->heading_label); sp->heading_label = s; } f = get_string_resource (si->dpy, "splash.headingFont", "Dialog.Font"); sp->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!sp->heading_font) sp->heading_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); f = get_string_resource(si->dpy, "splash.bodyFont", "Dialog.Font"); sp->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!sp->body_font) sp->body_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); f = get_string_resource(si->dpy, "splash.buttonFont", "Dialog.Font"); sp->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!sp->button_font) sp->button_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); sp->foreground = get_pixel_resource (si->dpy, cmap, "splash.foreground", "Dialog.Foreground"); sp->background = get_pixel_resource (si->dpy, cmap, "splash.background", "Dialog.Background"); sp->border = get_pixel_resource (si->dpy, cmap, "splash.borderColor", "Dialog.borderColor"); if (sp->foreground == sp->background) { /* Make sure the error messages show up. */ sp->foreground = BlackPixelOfScreen (ssi->screen); sp->background = WhitePixelOfScreen (ssi->screen); } sp->button_foreground = get_pixel_resource (si->dpy, cmap, "splash.Button.foreground", "Dialog.Button.Foreground"); sp->button_background = get_pixel_resource (si->dpy, cmap, "splash.Button.background", "Dialog.Button.Background"); sp->shadow_top = get_pixel_resource (si->dpy, cmap, "splash.topShadowColor", "Dialog.Foreground"); sp->shadow_bottom = get_pixel_resource (si->dpy, cmap, "splash.bottomShadowColor", "Dialog.Background"); sp->logo_width = get_integer_resource (si->dpy, "splash.logo.width", "Dialog.Logo.Width"); sp->logo_height = get_integer_resource (si->dpy, "splash.logo.height", "Dialog.Logo.Height"); sp->internal_border = get_integer_resource (si->dpy, "splash.internalBorderWidth", "Dialog.InternalBorderWidth"); sp->shadow_width = get_integer_resource (si->dpy, "splash.shadowThickness", "Dialog.ShadowThickness"); if (sp->logo_width == 0) sp->logo_width = 150; if (sp->logo_height == 0) sp->logo_height = 150; if (sp->internal_border == 0) sp->internal_border = 15; if (sp->shadow_width == 0) sp->shadow_width = 4; { int direction, ascent, descent; XCharStruct overall; sp->width = 0; sp->height = 0; /* Measure the heading_label. */ XTextExtents (sp->heading_font, sp->heading_label, strlen(sp->heading_label), &direction, &ascent, &descent, &overall); if (overall.width > sp->width) sp->width = overall.width; sp->height += ascent + descent; /* Measure the body_label. */ XTextExtents (sp->body_font, sp->body_label, strlen(sp->body_label), &direction, &ascent, &descent, &overall); if (overall.width > sp->width) sp->width = overall.width; sp->height += ascent + descent; /* Measure the body2_label. */ XTextExtents (sp->body_font, sp->body2_label, strlen(sp->body2_label), &direction, &ascent, &descent, &overall); if (overall.width > sp->width) sp->width = overall.width; sp->height += ascent + descent; { Dimension w2 = 0, w3 = 0, w4 = 0; Dimension h2 = 0, h3 = 0, h4 = 0; /* Measure the Demo button. */ XTextExtents (sp->button_font, sp->demo_label, strlen(sp->demo_label), &direction, &ascent, &descent, &overall); w2 = overall.width; h2 = ascent + descent; #ifdef PREFS_BUTTON /* Measure the Prefs button. */ XTextExtents (sp->button_font, sp->prefs_label, strlen(sp->prefs_label), &direction, &ascent, &descent, &overall); w3 = overall.width; h3 = ascent + descent; #else /* !PREFS_BUTTON */ w3 = 0; h3 = 0; #endif /* !PREFS_BUTTON */ /* Measure the Help button. */ XTextExtents (sp->button_font, sp->help_label, strlen(sp->help_label), &direction, &ascent, &descent, &overall); w4 = overall.width; h4 = ascent + descent; w2 = MAX(w2, w3); w2 = MAX(w2, w4); h2 = MAX(h2, h3); h2 = MAX(h2, h4); /* Add some horizontal padding inside the buttons. */ w2 += ascent; w2 += ((ascent + descent) / 2) + (sp->shadow_width * 2); h2 += ((ascent + descent) / 2) + (sp->shadow_width * 2); sp->button_width = w2; sp->button_height = h2; #ifdef PREFS_BUTTON w2 *= 3; #else /* !PREFS_BUTTON */ w2 *= 2; #endif /* !PREFS_BUTTON */ w2 += ((ascent + descent) * 2); /* for space between buttons */ if (w2 > sp->width) sp->width = w2; sp->height += h2; } sp->width += (sp->internal_border * 2); sp->height += (sp->internal_border * 3); if (sp->logo_height > sp->height) sp->height = sp->logo_height; else if (sp->height > sp->logo_height) sp->logo_height = sp->height; sp->logo_width = sp->logo_height; sp->width += sp->logo_width; } attrmask |= CWOverrideRedirect; attrs.override_redirect = True; attrmask |= CWEventMask; attrs.event_mask = (ExposureMask | ButtonPressMask | ButtonReleaseMask); { int sx = 0, sy = 0, w, h; int mouse_x = 0, mouse_y = 0; { Window pointer_root, pointer_child; int root_x, root_y, win_x, win_y; unsigned int mask; if (XQueryPointer (si->dpy, RootWindowOfScreen (ssi->screen), &pointer_root, &pointer_child, &root_x, &root_y, &win_x, &win_y, &mask)) { mouse_x = root_x; mouse_y = root_y; } } x = ssi->x; y = ssi->y; w = ssi->width; h = ssi->height; if (si->prefs.debug_p) w /= 2; x = sx + (((w + sp->width) / 2) - sp->width); y = sy + (((h + sp->height) / 2) - sp->height); if (x < sx) x = sx; if (y < sy) y = sy; } bw = get_integer_resource (si->dpy, "splash.borderWidth", "Dialog.BorderWidth"); si->splash_dialog = XCreateWindow (si->dpy, RootWindowOfScreen(ssi->screen), x, y, sp->width, sp->height, bw, DefaultDepthOfScreen (ssi->screen), InputOutput, DefaultVisualOfScreen(ssi->screen), attrmask, &attrs); XSetWindowBackground (si->dpy, si->splash_dialog, sp->background); XSetWindowBorder (si->dpy, si->splash_dialog, sp->border); sp->logo_pixmap = xscreensaver_logo (ssi->screen, /* same visual as si->splash_dialog */ DefaultVisualOfScreen (ssi->screen), si->splash_dialog, cmap, sp->background, &sp->logo_pixels, &sp->logo_npixels, &sp->logo_clipmask, True); XMapRaised (si->dpy, si->splash_dialog); XSync (si->dpy, False); si->sp_data = sp; sp->timer = XtAppAddTimeOut (si->app, si->prefs.splash_duration, unsplash_timer, (XtPointer) si); draw_splash_window (si); XSync (si->dpy, False); } static void draw_splash_window (saver_info *si) { splash_dialog_data *sp = si->sp_data; XGCValues gcv; GC gc1, gc2; int hspacing, vspacing, height; int x1, x2, x3, y1, y2; int sw; #ifdef PREFS_BUTTON int nbuttons = 3; #else /* !PREFS_BUTTON */ int nbuttons = 2; #endif /* !PREFS_BUTTON */ height = (sp->heading_font->ascent + sp->heading_font->descent + sp->body_font->ascent + sp->body_font->descent + sp->body_font->ascent + sp->body_font->descent + sp->button_font->ascent + sp->button_font->descent); vspacing = ((sp->height - (4 * sp->shadow_width) - (2 * sp->internal_border) - height) / 5); if (vspacing < 0) vspacing = 0; if (vspacing > (sp->heading_font->ascent * 2)) vspacing = (sp->heading_font->ascent * 2); gcv.foreground = sp->foreground; gc1 = XCreateGC (si->dpy, si->splash_dialog, GCForeground, &gcv); gc2 = XCreateGC (si->dpy, si->splash_dialog, GCForeground, &gcv); x1 = sp->logo_width; x3 = sp->width - (sp->shadow_width * 2); y1 = sp->internal_border; /* top heading */ XSetFont (si->dpy, gc1, sp->heading_font->fid); sw = string_width (sp->heading_font, sp->heading_label); x2 = (x1 + ((x3 - x1 - sw) / 2)); y1 += sp->heading_font->ascent; XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1, sp->heading_label, strlen(sp->heading_label)); y1 += sp->heading_font->descent; /* text below top heading */ XSetFont (si->dpy, gc1, sp->body_font->fid); y1 += vspacing + sp->body_font->ascent; sw = string_width (sp->body_font, sp->body_label); x2 = (x1 + ((x3 - x1 - sw) / 2)); XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1, sp->body_label, strlen(sp->body_label)); y1 += sp->body_font->descent; y1 += sp->body_font->ascent; sw = string_width (sp->body_font, sp->body2_label); x2 = (x1 + ((x3 - x1 - sw) / 2)); XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1, sp->body2_label, strlen(sp->body2_label)); y1 += sp->body_font->descent; /* The buttons */ XSetForeground (si->dpy, gc1, sp->button_foreground); XSetForeground (si->dpy, gc2, sp->button_background); /* y1 += (vspacing * 2);*/ y1 = sp->height - sp->internal_border - sp->button_height; x1 += sp->internal_border; y2 = (y1 + ((sp->button_height - (sp->button_font->ascent + sp->button_font->descent)) / 2) + sp->button_font->ascent); hspacing = ((sp->width - x1 - (sp->shadow_width * 2) - sp->internal_border - (sp->button_width * nbuttons)) / 2); x2 = x1 + ((sp->button_width - string_width(sp->button_font, sp->demo_label)) / 2); XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1, sp->button_width, sp->button_height); XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2, sp->demo_label, strlen(sp->demo_label)); sp->demo_button_x = x1; sp->demo_button_y = y1; #ifdef PREFS_BUTTON x1 += hspacing + sp->button_width; x2 = x1 + ((sp->button_width - string_width(sp->button_font,sp->prefs_label)) / 2); XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1, sp->button_width, sp->button_height); XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2, sp->prefs_label, strlen(sp->prefs_label)); sp->prefs_button_x = x1; sp->prefs_button_y = y1; #endif /* PREFS_BUTTON */ #ifdef PREFS_BUTTON x1 += hspacing + sp->button_width; #else /* !PREFS_BUTTON */ x1 = (sp->width - sp->button_width - sp->internal_border - (sp->shadow_width * 2)); #endif /* !PREFS_BUTTON */ x2 = x1 + ((sp->button_width - string_width(sp->button_font,sp->help_label)) / 2); XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1, sp->button_width, sp->button_height); XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2, sp->help_label, strlen(sp->help_label)); sp->help_button_x = x1; sp->help_button_y = y1; /* The logo */ x1 = sp->shadow_width * 6; y1 = sp->shadow_width * 6; x2 = sp->logo_width - (sp->shadow_width * 12); y2 = sp->logo_height - (sp->shadow_width * 12); if (sp->logo_pixmap) { Window root; int x, y; unsigned int w, h, bw, d; XGetGeometry (si->dpy, sp->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d); XSetForeground (si->dpy, gc1, sp->foreground); XSetBackground (si->dpy, gc1, sp->background); XSetClipMask (si->dpy, gc1, sp->logo_clipmask); XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) /2), y1 + ((y2 - (int)h) / 2)); if (d == 1) XCopyPlane (si->dpy, sp->logo_pixmap, si->splash_dialog, gc1, 0, 0, w, h, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2), 1); else XCopyArea (si->dpy, sp->logo_pixmap, si->splash_dialog, gc1, 0, 0, w, h, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2)); } /* Solid border inside the logo box. */ #if 0 XSetForeground (si->dpy, gc1, sp->foreground); XDrawRectangle (si->dpy, si->splash_dialog, gc1, x1, y1, x2-1, y2-1); #endif /* The shadow around the logo */ draw_shaded_rectangle (si->dpy, si->splash_dialog, sp->shadow_width * 4, sp->shadow_width * 4, sp->logo_width - (sp->shadow_width * 8), sp->logo_height - (sp->shadow_width * 8), sp->shadow_width, sp->shadow_bottom, sp->shadow_top); /* The shadow around the whole window */ draw_shaded_rectangle (si->dpy, si->splash_dialog, 0, 0, sp->width, sp->height, sp->shadow_width, sp->shadow_top, sp->shadow_bottom); XFreeGC (si->dpy, gc1); XFreeGC (si->dpy, gc2); update_splash_window (si); } static void update_splash_window (saver_info *si) { splash_dialog_data *sp = si->sp_data; int pressed; if (!sp) return; pressed = sp->pressed; /* The shadows around the buttons */ draw_shaded_rectangle (si->dpy, si->splash_dialog, sp->demo_button_x, sp->demo_button_y, sp->button_width, sp->button_height, sp->shadow_width, (pressed == 1 ? sp->shadow_bottom : sp->shadow_top), (pressed == 1 ? sp->shadow_top : sp->shadow_bottom)); #ifdef PREFS_BUTTON draw_shaded_rectangle (si->dpy, si->splash_dialog, sp->prefs_button_x, sp->prefs_button_y, sp->button_width, sp->button_height, sp->shadow_width, (pressed == 2 ? sp->shadow_bottom : sp->shadow_top), (pressed == 2 ? sp->shadow_top : sp->shadow_bottom)); #endif /* PREFS_BUTTON */ draw_shaded_rectangle (si->dpy, si->splash_dialog, sp->help_button_x, sp->help_button_y, sp->button_width, sp->button_height, sp->shadow_width, (pressed == 3 ? sp->shadow_bottom : sp->shadow_top), (pressed == 3 ? sp->shadow_top : sp->shadow_bottom)); } static void destroy_splash_window (saver_info *si) { splash_dialog_data *sp = si->sp_data; saver_screen_info *ssi = sp->prompt_screen; Colormap cmap = DefaultColormapOfScreen (ssi->screen); Pixel black = BlackPixelOfScreen (ssi->screen); Pixel white = WhitePixelOfScreen (ssi->screen); if (sp->timer) XtRemoveTimeOut (sp->timer); if (si->splash_dialog) { XDestroyWindow (si->dpy, si->splash_dialog); si->splash_dialog = 0; } if (sp->heading_label) free (sp->heading_label); if (sp->body_label) free (sp->body_label); if (sp->body2_label) free (sp->body2_label); if (sp->demo_label) free (sp->demo_label); #ifdef PREFS_BUTTON if (sp->prefs_label) free (sp->prefs_label); #endif /* PREFS_BUTTON */ if (sp->help_label) free (sp->help_label); if (sp->heading_font) XFreeFont (si->dpy, sp->heading_font); if (sp->body_font) XFreeFont (si->dpy, sp->body_font); if (sp->button_font) XFreeFont (si->dpy, sp->button_font); if (sp->foreground != black && sp->foreground != white) XFreeColors (si->dpy, cmap, &sp->foreground, 1, 0L); if (sp->background != black && sp->background != white) XFreeColors (si->dpy, cmap, &sp->background, 1, 0L); if (sp->button_foreground != black && sp->button_foreground != white) XFreeColors (si->dpy, cmap, &sp->button_foreground, 1, 0L); if (sp->button_background != black && sp->button_background != white) XFreeColors (si->dpy, cmap, &sp->button_background, 1, 0L); if (sp->shadow_top != black && sp->shadow_top != white) XFreeColors (si->dpy, cmap, &sp->shadow_top, 1, 0L); if (sp->shadow_bottom != black && sp->shadow_bottom != white) XFreeColors (si->dpy, cmap, &sp->shadow_bottom, 1, 0L); if (sp->logo_pixmap) XFreePixmap (si->dpy, sp->logo_pixmap); if (sp->logo_clipmask) XFreePixmap (si->dpy, sp->logo_clipmask); if (sp->logo_pixels) { if (sp->logo_npixels) XFreeColors (si->dpy, cmap, sp->logo_pixels, sp->logo_npixels, 0L); free (sp->logo_pixels); sp->logo_pixels = 0; sp->logo_npixels = 0; } memset (sp, 0, sizeof(*sp)); free (sp); si->sp_data = 0; } void handle_splash_event (saver_info *si, XEvent *event) { splash_dialog_data *sp = si->sp_data; saver_screen_info *ssi = sp->prompt_screen; int which = 0; if (!sp) return; switch (event->xany.type) { case Expose: draw_splash_window (si); break; case ButtonPress: case ButtonRelease: if (event->xbutton.x >= sp->demo_button_x && event->xbutton.x < sp->demo_button_x + sp->button_width && event->xbutton.y >= sp->demo_button_y && event->xbutton.y < sp->demo_button_y + sp->button_height) which = 1; #ifdef PREFS_BUTTON else if (event->xbutton.x >= sp->prefs_button_x && event->xbutton.x < sp->prefs_button_x + sp->button_width && event->xbutton.y >= sp->prefs_button_y && event->xbutton.y < sp->prefs_button_y + sp->button_height) which = 2; #endif /* PREFS_BUTTON */ else if (event->xbutton.x >= sp->help_button_x && event->xbutton.x < sp->help_button_x + sp->button_width && event->xbutton.y >= sp->help_button_y && event->xbutton.y < sp->help_button_y + sp->button_height) which = 3; if (event->xany.type == ButtonPress) { sp->pressed = which; update_splash_window (si); if (which == 0) XBell (si->dpy, False); } else if (event->xany.type == ButtonRelease) { if (which && sp->pressed == which) { destroy_splash_window (si); sp = si->sp_data; switch (which) { case 1: do_demo (ssi); break; #ifdef PREFS_BUTTON case 2: do_prefs (ssi); break; #endif /* PREFS_BUTTON */ case 3: do_help (ssi); break; default: abort(); } } else if (which == 0 && sp->pressed == 0) { /* click and release on the window but not in a button: treat that as "dismiss the splash dialog." */ destroy_splash_window (si); sp = si->sp_data; } if (sp) sp->pressed = 0; update_splash_window (si); } break; default: break; } } static void unsplash_timer (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; if (si && si->sp_data) destroy_splash_window (si); } /* Button callbacks */ #ifdef VMS # define pid_t int # define fork vfork #endif /* VMS */ static void do_demo (saver_screen_info *ssi) { saver_info *si = ssi->global; saver_preferences *p = &si->prefs; const char *cmd = p->demo_command; if (cmd && *cmd) fork_and_exec (ssi, cmd); else fprintf (stderr, "%s: no demo-mode command has been specified.\n", blurb()); } #ifdef PREFS_BUTTON static void do_prefs (saver_screen_info *ssi) { saver_info *si = ssi->global; saver_preferences *p = &si->prefs; const char *cmd = p->prefs_command; if (command && *command) fork_and_exec (ssi, cmd); else fprintf (stderr, "%s: no preferences command has been specified.\n", blurb()); } #endif /* PREFS_BUTTON */ static void do_help (saver_screen_info *ssi) { saver_info *si = ssi->global; saver_preferences *p = &si->prefs; char *help_command = 0; if (!p->load_url_command || !*p->load_url_command) { fprintf (stderr, "%s: no URL command has been specified.\n", blurb()); return; } if (!p->help_url || !*p->help_url) { fprintf (stderr, "%s: no Help URL has been specified.\n", blurb()); return; } help_command = (char *) malloc (strlen (p->load_url_command) + (strlen (p->help_url) * 4) + 10); sprintf (help_command, p->load_url_command, p->help_url, p->help_url, p->help_url, p->help_url); fork_and_exec (ssi, help_command); free (help_command); } xscreensaver-5.15/driver/stderr.c000066400000000000000000000336141164314150500171250ustar00rootroot00000000000000/* stderr.c --- capturing stdout/stderr output onto the screensaver window. * xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* stderr hackery - Why Unix Sucks, reason number 32767. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_FCNTL # include #endif #include #include "xscreensaver.h" #include "resources.h" #include "visual.h" FILE *real_stderr = 0; FILE *real_stdout = 0; /* It's ok for these to be global, since they refer to the one and only stderr stream, not to a particular screen or window or visual. */ static char stderr_buffer [4096]; static char *stderr_tail = 0; static time_t stderr_last_read = 0; static int stderr_stdout_read_fd = -1; static void make_stderr_overlay_window (saver_screen_info *); /* Recreates the stderr window or GCs: do this when the xscreensaver window on a screen has been re-created. */ void reset_stderr (saver_screen_info *ssi) { saver_info *si = ssi->global; if (si->prefs.debug_p) fprintf ((real_stderr ? real_stderr : stderr), "%s: resetting stderr\n", blurb()); ssi->stderr_text_x = 0; ssi->stderr_text_y = 0; if (ssi->stderr_gc) XFreeGC (si->dpy, ssi->stderr_gc); ssi->stderr_gc = 0; if (ssi->stderr_overlay_window) XDestroyWindow(si->dpy, ssi->stderr_overlay_window); ssi->stderr_overlay_window = 0; if (ssi->stderr_cmap) XFreeColormap(si->dpy, ssi->stderr_cmap); ssi->stderr_cmap = 0; } /* Erases any stderr text overlaying the screen (if possible) and resets the stderr output cursor to the upper left. Do this when the xscreensaver window is cleared. */ void clear_stderr (saver_screen_info *ssi) { saver_info *si = ssi->global; ssi->stderr_text_x = 0; ssi->stderr_text_y = 0; if (ssi->stderr_overlay_window) XClearWindow (si->dpy, ssi->stderr_overlay_window); } /* Draws the string on the screen's window. */ static void print_stderr_1 (saver_screen_info *ssi, char *string) { saver_info *si = ssi->global; Display *dpy = si->dpy; Screen *screen = ssi->screen; Window window = (ssi->stderr_overlay_window ? ssi->stderr_overlay_window : ssi->screensaver_window); int h_border = 20; int v_border = 20; char *head = string; char *tail; if (! ssi->stderr_font) { char *font_name = get_string_resource (dpy, "font", "Font"); if (!font_name) font_name = strdup ("fixed"); ssi->stderr_font = XLoadQueryFont (dpy, font_name); if (! ssi->stderr_font) ssi->stderr_font = XLoadQueryFont (dpy, "fixed"); ssi->stderr_line_height = (ssi->stderr_font->ascent + ssi->stderr_font->descent); free (font_name); } if (! ssi->stderr_gc) { XGCValues gcv; Pixel fg, bg; Colormap cmap = ssi->cmap; if (!ssi->stderr_overlay_window && get_boolean_resource(dpy, "overlayStderr", "Boolean")) { make_stderr_overlay_window (ssi); if (ssi->stderr_overlay_window) window = ssi->stderr_overlay_window; if (ssi->stderr_cmap) cmap = ssi->stderr_cmap; } fg = get_pixel_resource (dpy,cmap,"overlayTextForeground","Foreground"); bg = get_pixel_resource (dpy,cmap,"overlayTextBackground","Background"); gcv.font = ssi->stderr_font->fid; gcv.foreground = fg; gcv.background = bg; ssi->stderr_gc = XCreateGC (dpy, window, (GCFont | GCForeground | GCBackground), &gcv); } if (ssi->stderr_cmap) XInstallColormap(si->dpy, ssi->stderr_cmap); for (tail = string; *tail; tail++) { if (*tail == '\n' || *tail == '\r') { int maxy = HeightOfScreen (screen) - v_border - v_border; if (tail != head) XDrawImageString (dpy, window, ssi->stderr_gc, ssi->stderr_text_x + h_border, ssi->stderr_text_y + v_border + ssi->stderr_font->ascent, head, tail - head); ssi->stderr_text_x = 0; ssi->stderr_text_y += ssi->stderr_line_height; head = tail + 1; if (*tail == '\r' && *head == '\n') head++, tail++; if (ssi->stderr_text_y > maxy - ssi->stderr_line_height) { #if 0 ssi->stderr_text_y = 0; #else int offset = ssi->stderr_line_height * 5; XWindowAttributes xgwa; XGetWindowAttributes (dpy, window, &xgwa); XCopyArea (dpy, window, window, ssi->stderr_gc, 0, v_border + offset, xgwa.width, (xgwa.height - v_border - v_border - offset), 0, v_border); XClearArea (dpy, window, 0, xgwa.height - v_border - offset, xgwa.width, offset, False); ssi->stderr_text_y -= offset; #endif } } } if (tail != head) { int direction, ascent, descent; XCharStruct overall; XDrawImageString (dpy, window, ssi->stderr_gc, ssi->stderr_text_x + h_border, ssi->stderr_text_y + v_border + ssi->stderr_font->ascent, head, tail - head); XTextExtents (ssi->stderr_font, tail, tail - head, &direction, &ascent, &descent, &overall); ssi->stderr_text_x += overall.width; } } static void make_stderr_overlay_window (saver_screen_info *ssi) { saver_info *si = ssi->global; unsigned long transparent_pixel = 0; Visual *visual = get_overlay_visual (ssi->screen, &transparent_pixel); if (visual) { int depth = visual_depth (ssi->screen, visual); XSetWindowAttributes attrs; XWindowAttributes xgwa; unsigned long attrmask; XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa); if (si->prefs.debug_p) fprintf(real_stderr, "%s: using overlay visual 0x%0x for stderr text layer.\n", blurb(), (int) XVisualIDFromVisual (visual)); ssi->stderr_cmap = XCreateColormap(si->dpy, RootWindowOfScreen(ssi->screen), visual, AllocNone); attrmask = (CWColormap | CWBackPixel | CWBackingPixel | CWBorderPixel | CWBackingStore | CWSaveUnder); attrs.colormap = ssi->stderr_cmap; attrs.background_pixel = transparent_pixel; attrs.backing_pixel = transparent_pixel; attrs.border_pixel = transparent_pixel; attrs.backing_store = NotUseful; attrs.save_under = False; ssi->stderr_overlay_window = XCreateWindow(si->dpy, ssi->screensaver_window, 0, 0, xgwa.width, xgwa.height, 0, depth, InputOutput, visual, attrmask, &attrs); XMapRaised(si->dpy, ssi->stderr_overlay_window); } } /* Draws the string on each screen's window as error text. */ static void print_stderr (saver_info *si, char *string) { saver_preferences *p = &si->prefs; int i; /* In verbose mode, copy it to stderr as well. */ if (p->verbose_p) fprintf (real_stderr, "%s", string); for (i = 0; i < si->nscreens; i++) print_stderr_1 (&si->screens[i], string); } /* Polls the stderr buffer every few seconds and if it finds any text, writes it on all screens. */ static void stderr_popup_timer_fn (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; char *s = stderr_buffer; if (*s) { /* If too much data was printed, then something has gone haywire, so truncate it. */ char *trailer = "\n\n<< stderr diagnostics have been truncated >>\n\n"; int max = sizeof (stderr_buffer) - strlen (trailer) - 5; if (strlen (s) > max) strcpy (s + max, trailer); /* Now show the user. */ print_stderr (si, s); } stderr_tail = stderr_buffer; si->stderr_popup_timer = 0; } /* Called when data becomes available on the stderr pipe. Copies it into stderr_buffer where stderr_popup_timer_fn() can find it later. */ static void stderr_callback (XtPointer closure, int *fd, XtIntervalId *id) { saver_info *si = (saver_info *) closure; char *s; int left; int size; int read_this_time = 0; if (!fd || *fd < 0 || *fd != stderr_stdout_read_fd) abort(); if (stderr_tail == 0) stderr_tail = stderr_buffer; left = ((sizeof (stderr_buffer) - 2) - (stderr_tail - stderr_buffer)); s = stderr_tail; *s = 0; /* Read as much data from the fd as we can, up to our buffer size. */ if (left > 0) { while ((size = read (*fd, (void *) s, left)) > 0) { left -= size; s += size; read_this_time += size; } *s = 0; } else { char buf2 [1024]; /* The buffer is full; flush the rest of it. */ while (read (*fd, (void *) buf2, sizeof (buf2)) > 0) ; } stderr_tail = s; stderr_last_read = time ((time_t *) 0); /* Now we have read some data that we would like to put up in a dialog box. But more data may still be coming in - so don't pop up the dialog right now, but instead, start a timer that will pop it up a second from now. Should more data come in in the meantime, we will be called again, and will reset that timer again. So the dialog will only pop up when a second has elapsed with no new data being written to stderr. However, if the buffer is full (meaning lots of data has been written) then we don't reset the timer. */ if (read_this_time > 0) { if (si->stderr_popup_timer) XtRemoveTimeOut (si->stderr_popup_timer); si->stderr_popup_timer = XtAppAddTimeOut (si->app, 1 * 1000, stderr_popup_timer_fn, (XtPointer) si); } } /* If stderr capturing is desired, this replaces `stdout' and `stderr' with a pipe, so that any output written to them will show up on the screen as well as on the original value of those streams. */ void initialize_stderr (saver_info *si) { static Boolean done = False; int fds [2]; int in, out; int new_stdout, new_stderr; int stdout_fd = 1; int stderr_fd = 2; int flags = 0; Boolean stderr_dialog_p; if (done) return; done = True; real_stderr = stderr; real_stdout = stdout; stderr_dialog_p = get_boolean_resource (si->dpy, "captureStderr", "Boolean"); if (!stderr_dialog_p) return; if (pipe (fds)) { perror ("error creating pipe:"); return; } in = fds [0]; out = fds [1]; # ifdef HAVE_FCNTL # if defined(O_NONBLOCK) flags = O_NONBLOCK; # elif defined(O_NDELAY) flags = O_NDELAY; # else ERROR!! neither O_NONBLOCK nor O_NDELAY are defined. # endif /* Set both sides of the pipe to nonblocking - this is so that our reads (in stderr_callback) will terminate, and so that out writes (in the client programs) will silently fail when the pipe is full, instead of hosing the program. */ if (fcntl (in, F_SETFL, flags) != 0) { perror ("fcntl:"); return; } if (fcntl (out, F_SETFL, flags) != 0) { perror ("fcntl:"); return; } # endif /* !HAVE_FCNTL */ if (stderr_dialog_p) { FILE *new_stderr_file; FILE *new_stdout_file; new_stderr = dup (stderr_fd); if (new_stderr < 0) { perror ("could not dup() a stderr:"); return; } if (! (new_stderr_file = fdopen (new_stderr, "w"))) { perror ("could not fdopen() the new stderr:"); return; } real_stderr = new_stderr_file; close (stderr_fd); if (dup2 (out, stderr_fd) < 0) { perror ("could not dup() a new stderr:"); return; } new_stdout = dup (stdout_fd); if (new_stdout < 0) { perror ("could not dup() a stdout:"); return; } if (! (new_stdout_file = fdopen (new_stdout, "w"))) { perror ("could not fdopen() the new stdout:"); return; } real_stdout = new_stdout_file; close (stdout_fd); if (dup2 (out, stdout_fd) < 0) { perror ("could not dup() a new stdout:"); return; } } stderr_stdout_read_fd = in; XtAppAddInput (si->app, in, (XtPointer) XtInputReadMask, stderr_callback, (XtPointer) si); } /* If the "-log file" command-line option has been specified, open the file for append, and redirect stdout/stderr there. This is called very early, before initialize_stderr(). */ void stderr_log_file (saver_info *si) { int stdout_fd = 1; int stderr_fd = 2; const char *filename = get_string_resource (si->dpy, "logFile", "LogFile"); int fd; if (!filename || !*filename) return; fd = open (filename, O_WRONLY | O_APPEND | O_CREAT, 0666); if (fd < 0) { char buf[255]; FAIL: sprintf (buf, "%.100s: %.100s", blurb(), filename); perror (buf); fflush (stderr); fflush (stdout); exit (1); } fprintf (stderr, "%s: logging to file %s\n", blurb(), filename); if (dup2 (fd, stdout_fd) < 0) goto FAIL; if (dup2 (fd, stderr_fd) < 0) goto FAIL; fprintf (stderr, "\n\n" "##########################################################################\n" "%s: logging to \"%s\" at %s\n" "##########################################################################\n" "\n", blurb(), filename, timestring()); } /* If there is anything in the stderr buffer, flush it to the real stderr. This does no X operations. Call this when exiting to make sure any last words actually show up. */ void shutdown_stderr (saver_info *si) { fflush (stdout); fflush (stderr); if (!real_stderr || stderr_stdout_read_fd < 0) return; stderr_callback ((XtPointer) si, &stderr_stdout_read_fd, 0); if (stderr_tail && stderr_buffer < stderr_tail) { *stderr_tail = 0; fprintf (real_stderr, "%s", stderr_buffer); stderr_tail = stderr_buffer; } if (real_stdout) fflush (real_stdout); if (real_stderr) fflush (real_stderr); if (stdout != real_stdout) dup2 (fileno(real_stdout), fileno(stdout)); if (stderr != real_stderr) dup2 (fileno(real_stderr), fileno(stderr)); stderr_stdout_read_fd = -1; } xscreensaver-5.15/driver/subprocs.c000066400000000000000000001063071164314150500174620ustar00rootroot00000000000000/* subprocs.c --- choosing, spawning, and killing screenhacks. * xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include /* not used for much... */ #ifndef ESRCH # include #endif #include /* sys/resource.h needs this for timeval */ #include /* for PATH_MAX */ #ifdef HAVE_SYS_WAIT_H # include /* for waitpid() and associated macros */ #endif #ifdef HAVE_SETRLIMIT # include /* for setrlimit() and RLIMIT_AS */ #endif #ifdef VMS # include # include /* for close */ # include /* for getpid */ # define pid_t int # define fork vfork #endif /* VMS */ #include /* for the signal names */ #if !defined(SIGCHLD) && defined(SIGCLD) # define SIGCHLD SIGCLD #endif #if 0 /* putenv() is declared in stdlib.h on modern linux systems. */ #ifdef HAVE_PUTENV extern int putenv (/* const char * */); /* getenv() is in stdlib.h... */ #endif #endif extern int kill (pid_t, int); /* signal() is in sys/signal.h... */ /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer #define XtAppContext void* #define XrmDatabase void* #define XtIntervalId void* #define XtPointer void* #define Widget void* #include "xscreensaver.h" #include "exec.h" #include "yarandom.h" #include "visual.h" /* for id_to_visual() */ extern saver_info *global_si_kludge; /* I hate C so much... */ /* Used when printing error/debugging messages from signal handlers. */ static const char * no_malloc_number_to_string (long num) { static char string[128] = ""; int num_digits; Bool negative_p = False; num_digits = 0; if (num == 0) return "0"; if (num < 0) { negative_p = True; num = -num; } while ((num > 0) && (num_digits < sizeof(string) - 1)) { int digit; digit = (int) num % 10; num_digits++; string[sizeof(string) - 1 - num_digits] = digit + '0'; num /= 10; } if (negative_p) { num_digits++; string[sizeof(string) - 1 - num_digits] = '-'; } return string + sizeof(string) - 1 - num_digits; } /* Like write(), but runs strlen() on the arg to get the length. */ static int write_string (int fd, const char *str) { return write (fd, str, strlen (str)); } static int write_long (int fd, long n) { const char *str = no_malloc_number_to_string (n); return write_string (fd, str); } /* RLIMIT_AS (called RLIMIT_VMEM on some systems) controls the maximum size of a process's address space, i.e., the maximal brk(2) and mmap(2) values. Setting this lets you put a cap on how much memory a process can allocate. Except the "and mmap()" part kinda makes this useless, since many GL implementations end up using mmap() to pull the whole frame buffer into memory (or something along those lines) making it appear processes are using hundreds of megabytes when in fact they're using very little, and we end up capping their mallocs prematurely. YAY! */ #if defined(RLIMIT_VMEM) && !defined(RLIMIT_AS) # define RLIMIT_AS RLIMIT_VMEM #endif static void limit_subproc_memory (int address_space_limit, Bool verbose_p) { /* This has caused way more problems than it has solved... Let's just completely ignore the "memoryLimit" option now. */ #undef HAVE_SETRLIMIT #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_AS) struct rlimit r; if (address_space_limit < 10 * 1024) /* let's not be crazy */ return; if (getrlimit (RLIMIT_AS, &r) != 0) { char buf [512]; sprintf (buf, "%s: getrlimit(RLIMIT_AS) failed", blurb()); perror (buf); return; } r.rlim_cur = address_space_limit; if (setrlimit (RLIMIT_AS, &r) != 0) { char buf [512]; sprintf (buf, "%s: setrlimit(RLIMIT_AS, {%lu, %lu}) failed", blurb(), r.rlim_cur, r.rlim_max); perror (buf); return; } if (verbose_p) { int i = address_space_limit; char buf[100]; if (i >= (1<<30) && i == ((i >> 30) << 30)) sprintf(buf, "%dG", i >> 30); else if (i >= (1<<20) && i == ((i >> 20) << 20)) sprintf(buf, "%dM", i >> 20); else if (i >= (1<<10) && i == ((i >> 10) << 10)) sprintf(buf, "%dK", i >> 10); else sprintf(buf, "%d bytes", i); fprintf (stderr, "%s: limited pid %lu address space to %s.\n", blurb(), (unsigned long) getpid (), buf); } #endif /* HAVE_SETRLIMIT && RLIMIT_AS */ } /* Management of child processes, and de-zombification. */ enum job_status { job_running, /* the process is still alive */ job_stopped, /* we have sent it a STOP signal */ job_killed, /* we have sent it a TERM signal */ job_dead /* we have wait()ed for it, and it's dead -- this state only occurs so that we can avoid calling free() from a signal handler. Shortly after going into this state, the list element will be removed. */ }; struct screenhack_job { char *name; pid_t pid; int screen; enum job_status status; struct screenhack_job *next; }; static struct screenhack_job *jobs = 0; /* for debugging -- nothing calls this, but it's useful to invoke from gdb. */ void show_job_list (void); void show_job_list (void) { struct screenhack_job *job; fprintf(stderr, "%s: job list:\n", blurb()); for (job = jobs; job; job = job->next) fprintf (stderr, " %5ld: %2d: (%s) %s\n", (long) job->pid, job->screen, (job->status == job_running ? "running" : job->status == job_stopped ? "stopped" : job->status == job_killed ? " killed" : job->status == job_dead ? " dead" : " ???"), job->name); fprintf (stderr, "\n"); } static void clean_job_list (void); static struct screenhack_job * make_job (pid_t pid, int screen, const char *cmd) { struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job)); static char name [1024]; const char *in = cmd; char *out = name; int got_eq = 0; int first = 1; clean_job_list(); AGAIN: while (isspace(*in)) in++; /* skip whitespace */ while (!isspace(*in) && *in != ':') { if (*in == '=') got_eq = 1; *out++ = *in++; /* snarf first token */ } if (got_eq) /* if the first token was FOO=bar */ { /* then get the next token instead. */ got_eq = 0; out = name; first = 0; goto AGAIN; } while (isspace(*in)) in++; /* skip whitespace */ *out = 0; job->name = strdup(name); job->pid = pid; job->screen = screen; job->status = job_running; job->next = jobs; jobs = job; return jobs; } static void free_job (struct screenhack_job *job) { if (!job) return; else if (job == jobs) jobs = jobs->next; else { struct screenhack_job *job2, *prev; for (prev = 0, job2 = jobs; job2; prev = job2, job2 = job2->next) if (job2 == job) { prev->next = job->next; break; } } free(job->name); free(job); } /* Cleans out dead jobs from the jobs list -- this must only be called from the main thread, not from a signal handler. */ static void clean_job_list (void) { struct screenhack_job *job, *prev, *next; for (prev = 0, job = jobs, next = (job ? job->next : 0); job; prev = job, job = next, next = (job ? job->next : 0)) { if (job->status == job_dead) { if (prev) prev->next = next; free_job (job); job = prev; } } } static struct screenhack_job * find_job (pid_t pid) { struct screenhack_job *job; for (job = jobs; job; job = job->next) if (job->pid == pid) return job; return 0; } static void await_dying_children (saver_info *si); #ifndef VMS static void describe_dead_child (saver_info *, pid_t, int wait_status); #endif /* Semaphore to temporarily turn the SIGCHLD handler into a no-op. Don't alter this directly -- use block_sigchld() / unblock_sigchld(). */ static int block_sigchld_handler = 0; #ifdef HAVE_SIGACTION sigset_t #else /* !HAVE_SIGACTION */ int #endif /* !HAVE_SIGACTION */ block_sigchld (void) { #ifdef HAVE_SIGACTION struct sigaction sa; sigset_t child_set; memset (&sa, 0, sizeof (sa)); sa.sa_handler = SIG_IGN; sigaction (SIGPIPE, &sa, NULL); sigemptyset (&child_set); sigaddset (&child_set, SIGCHLD); sigprocmask (SIG_BLOCK, &child_set, 0); #else /* !HAVE_SIGACTION */ signal (SIGPIPE, SIG_IGN); #endif /* !HAVE_SIGACTION */ block_sigchld_handler++; #ifdef HAVE_SIGACTION return child_set; #else /* !HAVE_SIGACTION */ return 0; #endif /* !HAVE_SIGACTION */ } void unblock_sigchld (void) { #ifdef HAVE_SIGACTION struct sigaction sa; sigset_t child_set; memset(&sa, 0, sizeof (sa)); sa.sa_handler = SIG_DFL; sigaction(SIGPIPE, &sa, NULL); sigemptyset(&child_set); sigaddset(&child_set, SIGCHLD); sigprocmask(SIG_UNBLOCK, &child_set, 0); #else /* !HAVE_SIGACTION */ signal(SIGPIPE, SIG_DFL); #endif /* !HAVE_SIGACTION */ block_sigchld_handler--; } static int kill_job (saver_info *si, pid_t pid, int signal) { saver_preferences *p = &si->prefs; struct screenhack_job *job; int status = -1; clean_job_list(); if (block_sigchld_handler) /* This function should not be called from the signal handler. */ abort(); block_sigchld(); /* we control the horizontal... */ job = find_job (pid); if (!job || !job->pid || job->status == job_killed) { if (p->verbose_p) fprintf (stderr, "%s: no child %ld to signal!\n", blurb(), (long) pid); goto DONE; } switch (signal) { case SIGTERM: job->status = job_killed; break; #ifdef SIGSTOP /* #### there must be a way to do this on VMS... */ case SIGSTOP: job->status = job_stopped; break; case SIGCONT: job->status = job_running; break; #endif /* SIGSTOP */ default: abort(); } if (p->verbose_p) fprintf (stderr, "%s: %d: %s pid %lu (%s)\n", blurb(), job->screen, (job->status == job_killed ? "killing" : job->status == job_stopped ? "suspending" : "resuming"), (unsigned long) job->pid, job->name); status = kill (job->pid, signal); if (p->verbose_p && status < 0) { if (errno == ESRCH) fprintf (stderr, "%s: %d: child process %lu (%s) was already dead.\n", blurb(), job->screen, (unsigned long) job->pid, job->name); else { char buf [1024]; sprintf (buf, "%s: %d: couldn't kill child process %lu (%s)", blurb(), job->screen, (unsigned long) job->pid, job->name); perror (buf); } } await_dying_children (si); DONE: unblock_sigchld(); if (block_sigchld_handler < 0) abort(); clean_job_list(); return status; } #ifdef SIGCHLD static RETSIGTYPE sigchld_handler (int sig) { saver_info *si = global_si_kludge; /* I hate C so much... */ if (si->prefs.debug_p) { /* Don't call fprintf() from signal handlers, as it might malloc. fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(), (block_sigchld_handler ? " (blocked)" : "")); */ write_string (STDERR_FILENO, blurb()); write_string (STDERR_FILENO, ": got SIGCHLD"); if (block_sigchld_handler) write_string (STDERR_FILENO, " (blocked)\n"); else write_string (STDERR_FILENO, "\n"); } if (block_sigchld_handler < 0) abort(); else if (block_sigchld_handler == 0) { block_sigchld(); await_dying_children (si); unblock_sigchld(); } init_sigchld(); } #endif /* SIGCHLD */ #ifndef VMS static void await_dying_children (saver_info *si) { while (1) { int wait_status = 0; pid_t kid; errno = 0; kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED); if (si->prefs.debug_p) { if (kid < 0 && errno) { /* Don't call fprintf() from signal handlers, as it might malloc. fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(), (long) kid, errno); */ write_string (STDERR_FILENO, blurb()); write_string (STDERR_FILENO, ": waitpid(-1) ==> "); write_long (STDERR_FILENO, (long) kid); write_string (STDERR_FILENO, " ("); write_long (STDERR_FILENO, (long) errno); write_string (STDERR_FILENO, ")\n"); } else { /* Don't call fprintf() from signal handlers, as it might malloc. fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(), (long) kid); */ write_string (STDERR_FILENO, blurb()); write_string (STDERR_FILENO, ": waitpid(-1) ==> "); write_long (STDERR_FILENO, (long) kid); write_string (STDERR_FILENO, "\n"); } } /* 0 means no more children to reap. -1 means error -- except "interrupted system call" isn't a "real" error, so if we get that, we should just try again. */ if (kid == 0 || (kid < 0 && errno != EINTR)) break; describe_dead_child (si, kid, wait_status); } } static void describe_dead_child (saver_info *si, pid_t kid, int wait_status) { int i; saver_preferences *p = &si->prefs; struct screenhack_job *job = find_job (kid); const char *name = job ? job->name : ""; int screen_no = job ? job->screen : 0; if (WIFEXITED (wait_status)) { int exit_status = WEXITSTATUS (wait_status); /* Treat exit code as a signed 8-bit quantity. */ if (exit_status & 0x80) exit_status |= ~0xFF; /* One might assume that exiting with non-0 means something went wrong. But that loser xswarm exits with the code that it was killed with, so it *always* exits abnormally. Treat abnormal exits as "normal" (don't mention them) if we've just killed the subprocess. But mention them if they happen on their own. */ if (!job || (exit_status != 0 && (p->verbose_p || job->status != job_killed))) { /* Don't call fprintf() from signal handlers, as it might malloc. fprintf (stderr, "%s: %d: child pid %lu (%s) exited abnormally (code %d).\n", blurb(), screen_no, (unsigned long) kid, name, exit_status); */ write_string (STDERR_FILENO, blurb()); write_string (STDERR_FILENO, ": "); write_long (STDERR_FILENO, (long) screen_no); write_string (STDERR_FILENO, ": child pid "); write_long (STDERR_FILENO, (long) kid); write_string (STDERR_FILENO, " ("); write_string (STDERR_FILENO, name); write_string (STDERR_FILENO, ") exited abnormally (code "); write_long (STDERR_FILENO, (long) exit_status); write_string (STDERR_FILENO, ").\n"); } else if (p->verbose_p) { /* Don't call fprintf() from signal handlers, as it might malloc. fprintf (stderr, "%s: %d: child pid %lu (%s) exited normally.\n", blurb(), screen_no, (unsigned long) kid, name); */ write_string (STDERR_FILENO, blurb()); write_string (STDERR_FILENO, ": "); write_long (STDERR_FILENO, (long) screen_no); write_string (STDERR_FILENO, ": child pid "); write_long (STDERR_FILENO, (long) kid); write_string (STDERR_FILENO, " ("); write_string (STDERR_FILENO, name); write_string (STDERR_FILENO, ") exited normally.\n"); } if (job) job->status = job_dead; } else if (WIFSIGNALED (wait_status)) { if (p->verbose_p || !job || job->status != job_killed || WTERMSIG (wait_status) != SIGTERM) { /* Don't call fprintf() from signal handlers, as it might malloc. fprintf (stderr, "%s: %d: child pid %lu (%s) terminated with %s.\n", blurb(), screen_no, (unsigned long) kid, name, signal_name (WTERMSIG(wait_status))); */ write_string (STDERR_FILENO, blurb()); write_string (STDERR_FILENO, ": "); write_long (STDERR_FILENO, (long) screen_no); write_string (STDERR_FILENO, ": child pid "); write_long (STDERR_FILENO, (long) kid); write_string (STDERR_FILENO, " ("); write_string (STDERR_FILENO, name); write_string (STDERR_FILENO, ") terminated with signal "); write_long (STDERR_FILENO, WTERMSIG(wait_status)); write_string (STDERR_FILENO, ".\n"); } if (job) job->status = job_dead; } else if (WIFSTOPPED (wait_status)) { if (p->verbose_p) { /* Don't call fprintf() from signal handlers, as it might malloc. fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n", blurb(), (unsigned long) kid, name, signal_name (WSTOPSIG (wait_status))); */ write_string (STDERR_FILENO, blurb()); write_string (STDERR_FILENO, ": "); write_long (STDERR_FILENO, (long) screen_no); write_string (STDERR_FILENO, ": child pid "); write_long (STDERR_FILENO, (long) kid); write_string (STDERR_FILENO, " ("); write_string (STDERR_FILENO, name); write_string (STDERR_FILENO, ") stopped with signal "); write_long (STDERR_FILENO, WSTOPSIG(wait_status)); write_string (STDERR_FILENO, ".\n"); } if (job) job->status = job_stopped; } else { /* Don't call fprintf() from signal handlers, as it might malloc. fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!", blurb(), (unsigned long) kid, name); */ write_string (STDERR_FILENO, blurb()); write_string (STDERR_FILENO, ": "); write_long (STDERR_FILENO, (long) screen_no); write_string (STDERR_FILENO, ": child pid "); write_long (STDERR_FILENO, (long) kid); write_string (STDERR_FILENO, " ("); write_string (STDERR_FILENO, name); write_string (STDERR_FILENO, ") died in a mysterious way!"); if (job) job->status = job_dead; } /* Clear out the pid so that screenhack_running_p() knows it's dead. */ if (!job || job->status == job_dead) for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (kid == ssi->pid) ssi->pid = 0; } } #else /* VMS */ static void await_dying_children (saver_info *si) { return; } #endif /* VMS */ void init_sigchld (void) { #ifdef SIGCHLD # ifdef HAVE_SIGACTION /* Thanks to Tom Kelly */ static Bool sigchld_initialized_p = 0; if (!sigchld_initialized_p) { struct sigaction action, old; action.sa_handler = sigchld_handler; sigemptyset(&action.sa_mask); action.sa_flags = 0; if (sigaction(SIGCHLD, &action, &old) < 0) { char buf [255]; sprintf (buf, "%s: couldn't catch SIGCHLD", blurb()); perror (buf); } sigchld_initialized_p = True; } # else /* !HAVE_SIGACTION */ if (((long) signal (SIGCHLD, sigchld_handler)) == -1L) { char buf [255]; sprintf (buf, "%s: couldn't catch SIGCHLD", blurb()); perror (buf); } # endif /* !HAVE_SIGACTION */ #endif /* SIGCHLD */ } static Bool select_visual_of_hack (saver_screen_info *ssi, screenhack *hack) { saver_info *si = ssi->global; saver_preferences *p = &si->prefs; Bool selected; if (hack->visual && *hack->visual) selected = select_visual(ssi, hack->visual); else selected = select_visual(ssi, 0); if (!selected && (p->verbose_p || si->demoing_p)) fprintf (stderr, (si->demoing_p ? "%s: warning, no \"%s\" visual for \"%s\".\n" : "%s: no \"%s\" visual; skipping \"%s\".\n"), blurb(), (hack->visual && *hack->visual ? hack->visual : "???"), hack->command); return selected; } static void print_path_error (const char *program) { char buf [512]; char *cmd = strdup (program); char *token = strchr (cmd, ' '); if (token) *token = 0; sprintf (buf, "%s: could not execute \"%.100s\"", blurb(), cmd); free (cmd); perror (buf); if (errno == ENOENT && (token = getenv("PATH"))) { # ifndef PATH_MAX # ifdef MAXPATHLEN # define PATH_MAX MAXPATHLEN # else # define PATH_MAX 2048 # endif # endif char path[PATH_MAX]; fprintf (stderr, "\n"); *path = 0; # if defined(HAVE_GETCWD) if (! getcwd (path, sizeof(path))) *path = 0; # elif defined(HAVE_GETWD) getwd (path); # endif if (*path) fprintf (stderr, " Current directory is: %s\n", path); fprintf (stderr, " PATH is:\n"); token = strtok (strdup(token), ":"); while (token) { fprintf (stderr, " %s\n", token); token = strtok(0, ":"); } fprintf (stderr, "\n"); } } /* Executes the command in another process. Command may be any single command acceptable to /bin/sh. It may include wildcards, but no semicolons. If successful, the pid of the other process is returned. Otherwise, -1 is returned and an error may have been printed to stderr. */ pid_t fork_and_exec (saver_screen_info *ssi, const char *command) { saver_info *si = ssi->global; saver_preferences *p = &si->prefs; pid_t forked; switch ((int) (forked = fork ())) { case -1: { char buf [255]; sprintf (buf, "%s: couldn't fork", blurb()); perror (buf); break; } case 0: close (ConnectionNumber (si->dpy)); /* close display fd */ limit_subproc_memory (p->inferior_memory_limit, p->verbose_p); hack_subproc_environment (ssi->screen, ssi->screensaver_window); if (p->verbose_p) fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n", blurb(), ssi->number, command, (unsigned long) getpid ()); exec_command (p->shell, command, p->nice_inferior); /* If that returned, we were unable to exec the subprocess. Print an error message, if desired. */ if (! p->ignore_uninstalled_p) print_path_error (command); exit (1); /* exits child fork */ break; default: /* parent */ (void) make_job (forked, ssi->number, command); break; } return forked; } void spawn_screenhack (saver_screen_info *ssi) { saver_info *si = ssi->global; saver_preferences *p = &si->prefs; XFlush (si->dpy); if (!monitor_powered_on_p (si)) { if (si->prefs.verbose_p) fprintf (stderr, "%s: %d: X says monitor has powered down; " "not launching a hack.\n", blurb(), ssi->number); return; } if (p->screenhacks_count) { screenhack *hack; pid_t forked; char buf [255]; int new_hack = -1; int retry_count = 0; Bool force = False; AGAIN: if (p->screenhacks_count < 1) { /* No hacks at all */ new_hack = -1; } else if (p->screenhacks_count == 1) { /* Exactly one hack in the list */ new_hack = 0; } else if (si->selection_mode == -1) { /* Select the next hack, wrapping. */ new_hack = (ssi->current_hack + 1) % p->screenhacks_count; } else if (si->selection_mode == -2) { /* Select the previous hack, wrapping. */ if (ssi->current_hack < 0) new_hack = p->screenhacks_count - 1; else new_hack = ((ssi->current_hack + p->screenhacks_count - 1) % p->screenhacks_count); } else if (si->selection_mode > 0) { /* Select a specific hack, by number (via the ACTIVATE command.) */ new_hack = ((si->selection_mode - 1) % p->screenhacks_count); force = True; } else if (p->mode == ONE_HACK && p->selected_hack >= 0) { /* Select a specific hack, by number (via "One Saver" mode.) */ new_hack = p->selected_hack; force = True; } else if (p->mode == BLANK_ONLY || p->mode == DONT_BLANK) { new_hack = -1; } else if (p->mode == RANDOM_HACKS_SAME && ssi->number != 0) { /* Use the same hack that's running on screen 0. (Assumes this function was called on screen 0 first.) */ new_hack = si->screens[0].current_hack; } else /* (p->mode == RANDOM_HACKS) */ { /* Select a random hack (but not the one we just ran.) */ while ((new_hack = random () % p->screenhacks_count) == ssi->current_hack) ; } if (new_hack < 0) /* don't run a hack */ { ssi->current_hack = -1; if (si->selection_mode < 0) si->selection_mode = 0; return; } ssi->current_hack = new_hack; hack = p->screenhacks[ssi->current_hack]; /* If the hack is disabled, or there is no visual for this hack, then try again (move forward, or backward, or re-randomize.) Unless this hack was specified explicitly, in which case, use it regardless. */ if (force) select_visual_of_hack (ssi, hack); if (!force && (!hack->enabled_p || !on_path_p (hack->command) || !select_visual_of_hack (ssi, hack))) { if (++retry_count > (p->screenhacks_count*4)) { /* Uh, oops. Odds are, there are no suitable visuals, and we're looping. Give up. (This is totally lame, what we should do is make a list of suitable hacks at the beginning, then only loop over them.) */ if (p->verbose_p) fprintf(stderr, "%s: %d: no programs enabled, or no suitable visuals.\n", blurb(), ssi->number); return; } else goto AGAIN; } /* Turn off "next" and "prev" modes now, but "demo" mode is only turned off by explicit action. */ if (si->selection_mode < 0) si->selection_mode = 0; forked = fork_and_exec (ssi, hack->command); switch ((int) forked) { case -1: /* fork failed */ case 0: /* child fork (can't happen) */ sprintf (buf, "%s: couldn't fork", blurb()); perror (buf); restore_real_vroot (si); saver_exit (si, 1, "couldn't fork"); break; default: ssi->pid = forked; break; } } store_saver_status (si); /* store current hack number */ } void kill_screenhack (saver_screen_info *ssi) { saver_info *si = ssi->global; if (ssi->pid) kill_job (si, ssi->pid, SIGTERM); ssi->pid = 0; } void suspend_screenhack (saver_screen_info *ssi, Bool suspend_p) { #ifdef SIGSTOP /* older VMS doesn't have it... */ saver_info *si = ssi->global; if (ssi->pid) kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT)); #endif /* SIGSTOP */ } /* Called when we're exiting abnormally, to kill off the subproc. */ void emergency_kill_subproc (saver_info *si) { int i; #ifdef SIGCHLD signal (SIGCHLD, SIG_IGN); #endif /* SIGCHLD */ for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (ssi->pid) { kill_job (si, ssi->pid, SIGTERM); ssi->pid = 0; } } } Bool screenhack_running_p (saver_info *si) { Bool any_running_p = False; int i; for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (ssi->pid) any_running_p = True; } return any_running_p; } /* Environment variables. */ /* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX is defined, the xscreensaver daemon will search that directory for hacks. */ void hack_environment (saver_info *si) { #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX) static const char *def_path = DEFAULT_PATH_PREFIX; if (def_path && *def_path) { const char *opath = getenv("PATH"); char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20); strcpy (npath, "PATH="); strcat (npath, def_path); strcat (npath, ":"); strcat (npath, opath); if (putenv (npath)) abort (); /* don't free (npath) -- some implementations of putenv (BSD 4.4, glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not. So we must leak it (and/or the previous setting). Yay. */ } #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */ } void hack_subproc_environment (Screen *screen, Window saver_window) { /* Store $DISPLAY into the environment, so that the $DISPLAY variable that the spawned processes inherit is correct. First, it must be on the same host and display as the value of -display passed in on our command line (which is not necessarily the same as what our $DISPLAY variable is.) Second, the screen number in the $DISPLAY passed to the subprocess should be the screen on which this particular hack is running -- not the display specification which the driver itself is using, since the driver ignores its screen number and manages all existing screens. Likewise, store a window ID in $XSCREENSAVER_WINDOW -- this will allow us to (eventually) run multiple hacks in Xinerama mode, where each hack has the same $DISPLAY but a different piece of glass. */ Display *dpy = DisplayOfScreen (screen); const char *odpy = DisplayString (dpy); char *ndpy = (char *) malloc (strlen(odpy) + 20); char *nssw = (char *) malloc (40); char *s, *c; strcpy (ndpy, "DISPLAY="); s = ndpy + strlen(ndpy); strcpy (s, odpy); /* We have to find the last colon since it is the boundary between hostname & screen - IPv6 numeric format addresses may have many colons before that point, and DECnet addresses always have two colons */ c = strrchr(s,':'); /* skip to last colon */ if (c != NULL) s = c+1; while (isdigit(*s)) s++; /* skip over dpy number */ while (*s == '.') s++; /* skip over dot */ if (s[-1] != '.') *s++ = '.'; /* put on a dot */ sprintf(s, "%d", screen_number (screen)); /* put on screen number */ sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX", (unsigned long) saver_window); /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems any more, right? It's not Posix, but everyone seems to have it. */ #ifdef HAVE_PUTENV if (putenv (ndpy)) abort (); if (putenv (nssw)) abort (); /* don't free ndpy/nssw -- some implementations of putenv (BSD 4.4, glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not. So we must leak it (and/or the previous setting). Yay. */ #endif /* HAVE_PUTENV */ } /* GL crap */ Visual * get_best_gl_visual (saver_info *si, Screen *screen) { pid_t forked; int fds [2]; int in, out; int errfds[2]; int errin = -1, errout = -1; char buf[1024]; char *av[10]; int ac = 0; av[ac++] = "xscreensaver-gl-helper"; av[ac] = 0; if (pipe (fds)) { perror ("error creating pipe:"); return 0; } in = fds [0]; out = fds [1]; if (!si->prefs.verbose_p) { if (pipe (errfds)) { perror ("error creating pipe:"); return 0; } errin = errfds [0]; errout = errfds [1]; } block_sigchld(); /* This blocks it in the parent and child, to avoid racing. It is never unblocked in the child before the child exits, but that doesn't matter. */ switch ((int) (forked = fork ())) { case -1: { sprintf (buf, "%s: couldn't fork", blurb()); perror (buf); saver_exit (si, 1, 0); } case 0: { close (in); /* don't need this one */ close (ConnectionNumber (si->dpy)); /* close display fd */ if (dup2 (out, STDOUT_FILENO) < 0) /* pipe stdout */ { perror ("could not dup() a new stdout:"); return 0; } if (! si->prefs.verbose_p) { close(errin); if (dup2 (errout, STDERR_FILENO) < 0) { perror ("could not dup() a new stderr:"); return 0; } } hack_subproc_environment (screen, 0); /* set $DISPLAY */ execvp (av[0], av); /* shouldn't return. */ if (errno != ENOENT /* || si->prefs.verbose_p */ ) { /* Ignore "no such file or directory" errors. Issue all other exec errors, though. */ sprintf (buf, "%s: running %s", blurb(), av[0]); perror (buf); } exit (1); /* exits fork */ break; } default: { int result = 0; int wait_status = 0; FILE *f = fdopen (in, "r"); unsigned long v = 0; char c; close (out); /* don't need this one */ *buf = 0; if (! fgets (buf, sizeof(buf)-1, f)) *buf = 0; fclose (f); if (! si->prefs.verbose_p) { close (errout); close (errin); } /* Wait for the child to die. */ waitpid (-1, &wait_status, 0); unblock_sigchld(); /* child is dead and waited, unblock now. */ if (1 == sscanf (buf, "0x%lx %c", &v, &c)) result = (int) v; if (result == 0) { if (si->prefs.verbose_p) { int L = strlen(buf); fprintf (stderr, "%s: %s did not report a GL visual!\n", blurb(), av[0]); if (L && buf[L-1] == '\n') buf[--L] = 0; if (*buf) fprintf (stderr, "%s: %s said: \"%s\"\n", blurb(), av[0], buf); } return 0; } else { Visual *v = id_to_visual (screen, result); if (si->prefs.verbose_p) fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s.\n", blurb(), screen_number (screen), av[0], result, (v == DefaultVisualOfScreen (screen) ? " (default)" : "")); return v; } } } abort(); } /* Restarting the xscreensaver process from scratch. */ static char **saved_argv; void save_argv (int argc, char **argv) { saved_argv = (char **) calloc (argc+2, sizeof (char *)); saved_argv [argc] = 0; while (argc--) { int i = strlen (argv [argc]) + 1; saved_argv [argc] = (char *) malloc (i); memcpy (saved_argv [argc], argv [argc], i); } } /* Re-execs the process with the arguments in saved_argv. Does not return. */ void restart_process (saver_info *si) { fflush (stdout); fflush (stderr); shutdown_stderr (si); if (si->prefs.verbose_p) { int i; fprintf (stderr, "%s: re-executing", blurb()); for (i = 0; saved_argv[i]; i++) fprintf (stderr, " %s", saved_argv[i]); fprintf (stderr, "\n"); } describe_uids (si, stderr); fprintf (stderr, "\n"); fflush (stdout); fflush (stderr); execvp (saved_argv [0], saved_argv); /* shouldn't return */ { char buf [512]; sprintf (buf, "%s: could not restart process", blurb()); perror(buf); fflush(stderr); abort(); } } xscreensaver-5.15/driver/test-apm.c000066400000000000000000000046111164314150500173470ustar00rootroot00000000000000/* test-apm.c --- playing with the APM library. * xscreensaver, Copyright (c) 1999 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #define countof(x) (sizeof((x))/sizeof(*(x))) char *progname = 0; char *progclass = "XScreenSaver"; static const char * blurb (void) { static char buf[255]; time_t now = time ((time_t *) 0); char *ct = (char *) ctime (&now); int n = strlen(progname); if (n > 100) n = 99; strncpy(buf, progname, n); buf[n++] = ':'; buf[n++] = ' '; strncpy(buf+n, ct+11, 8); strcpy(buf+n+9, ": "); return buf; } static void apm_cb (XtPointer closure, int *fd, XtInputId *id) { apm_event_t events[100]; int n, i; while ((n = apm_get_events (*fd, 0, events, countof(events))) > 0) for (i = 0; i < n; i++) { fprintf (stderr, "%s: APM event 0x%x: %s.\n", blurb(), events[i], apm_event_name (events[i])); #if 0 switch (events[i]) { case APM_SYS_STANDBY: case APM_USER_STANDBY: case APM_SYS_SUSPEND: case APM_USER_SUSPEND: case APM_CRITICAL_SUSPEND: break; } #endif } } int main (int argc, char **argv) { XtAppContext app; Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); int fd; XtInputId id; XtGetApplicationNameAndClass (dpy, &progname, &progclass); fd = apm_open (); if (fd <= 0) { fprintf (stderr, "%s: couldn't initialize APM.\n", blurb()); exit (1); } id = XtAppAddInput(app, fd, (XtPointer) (XtInputReadMask | XtInputWriteMask), apm_cb, 0); XtAppMainLoop (app); exit (0); } xscreensaver-5.15/driver/test-fade.c000066400000000000000000000070751164314150500175000ustar00rootroot00000000000000/* test-fade.c --- playing with colormap and/or gamma fading. * xscreensaver, Copyright (c) 2001, 2004 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include "xscreensaver.h" #include "fade.h" #ifdef HAVE_SGI_VC_EXTENSION # include #endif #ifdef HAVE_XF86VMODE_GAMMA # include #endif XrmDatabase db = 0; char *progname = 0; char *progclass = "XScreenSaver"; #define SGI_VC_NAME "SGI-VIDEO-CONTROL" #define XF86_VIDMODE_NAME "XFree86-VidModeExtension" int main (int argc, char **argv) { int seconds = 3; int ticks = 20; int delay = 1; int op, event, error, major, minor; XtAppContext app; Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); Colormap *current_maps; int i; XtGetApplicationNameAndClass (dpy, &progname, &progclass); db = XtDatabase (dpy); current_maps = (Colormap *) calloc(sizeof(Colormap), ScreenCount(dpy)); for (i = 0; i < ScreenCount(dpy); i++) current_maps[i] = DefaultColormap (dpy, i); if (!XQueryExtension (dpy, SGI_VC_NAME, &op, &event, &error)) fprintf(stderr, "%s: no " SGI_VC_NAME " extension\n", progname); else { # ifdef HAVE_SGI_VC_EXTENSION if (!XSGIvcQueryVersion (dpy, &major, &minor)) fprintf(stderr, "%s: unable to get " SGI_VC_NAME " version\n", progname); else fprintf(stderr, "%s: " SGI_VC_NAME " version %d.%d\n", progname, major, minor); # else /* !HAVE_SGI_VC_EXTENSION */ fprintf(stderr, "%s: no support for display's " SGI_VC_NAME " extension\n", progname); # endif /* !HAVE_SGI_VC_EXTENSION */ } if (!XQueryExtension (dpy, XF86_VIDMODE_NAME, &op, &event, &error)) fprintf(stderr, "%s: no " XF86_VIDMODE_NAME " extension\n", progname); else { # ifdef HAVE_XF86VMODE_GAMMA if (!XF86VidModeQueryVersion (dpy, &major, &minor)) fprintf(stderr, "%s: unable to get " XF86_VIDMODE_NAME " version\n", progname); else fprintf(stderr, "%s: " XF86_VIDMODE_NAME " version %d.%d\n", progname, major, minor); # else /* !HAVE_XF86VMODE_GAMMA */ fprintf(stderr, "%s: no support for display's " XF86_VIDMODE_NAME " extension\n", progname); # endif /* !HAVE_XF86VMODE_GAMMA */ } fprintf (stderr, "%s: fading %d screen%s\n", progname, ScreenCount(dpy), ScreenCount(dpy) == 1 ? "" : "s"); while (1) { XSync (dpy, False); fprintf(stderr, "%s: out...", progname); fflush(stderr); fade_screens (dpy, current_maps, 0, 0, seconds, ticks, True, False); fprintf(stderr, "done.\n"); fflush(stderr); if (delay) sleep (delay); fprintf(stderr,"%s: in...", progname); fflush(stderr); fade_screens (dpy, current_maps, 0, 0, seconds, ticks, False, False); fprintf(stderr, "done.\n"); fflush(stderr); if (delay) sleep (delay); } } xscreensaver-5.15/driver/test-grab.c000066400000000000000000000056611164314150500175130ustar00rootroot00000000000000/* test-uid.c --- playing with grabs. * xscreensaver, Copyright (c) 1999, 2004 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include char *progname = 0; char *progclass = "XScreenSaver"; #define ALL_POINTER_EVENTS \ (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \ LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \ Button1MotionMask | Button2MotionMask | Button3MotionMask | \ Button4MotionMask | Button5MotionMask | ButtonMotionMask) int main (int argc, char **argv) { XtAppContext app; int kstatus, mstatus; Cursor cursor = 0; int delay = 60 * 15; Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); Window w = RootWindow (dpy, DefaultScreen(dpy)); XtGetApplicationNameAndClass (dpy, &progname, &progclass); kstatus = XGrabKeyboard (dpy, w, True, GrabModeSync, GrabModeAsync, CurrentTime); fprintf (stderr, "%s: grabbing keyboard on 0x%lx... %s.\n", progname, (unsigned long) w, (kstatus == GrabSuccess ? "GrabSuccess" : kstatus == AlreadyGrabbed ? "AlreadyGrabbed" : kstatus == GrabInvalidTime ? "GrabInvalidTime" : kstatus == GrabNotViewable ? "GrabNotViewable" : kstatus == GrabFrozen ? "GrabFrozen" : "???")); mstatus = XGrabPointer (dpy, w, True, ALL_POINTER_EVENTS, GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime); fprintf (stderr, "%s: grabbing mouse on 0x%lx... %s.\n", progname, (unsigned long) w, (mstatus == GrabSuccess ? "GrabSuccess" : mstatus == AlreadyGrabbed ? "AlreadyGrabbed" : mstatus == GrabInvalidTime ? "GrabInvalidTime" : mstatus == GrabNotViewable ? "GrabNotViewable" : mstatus == GrabFrozen ? "GrabFrozen" : "???")); XSync(dpy, False); if (kstatus == GrabSuccess || mstatus == GrabSuccess) { fprintf (stderr, "%s: sleeping for %d:%02d:%02d...\n", progname, delay / (60 * 60), (delay % (60 * 60)) / 60, delay % 60); fflush(stderr); sleep (delay); XSync(dpy, False); } exit (0); } xscreensaver-5.15/driver/test-mlstring.c000066400000000000000000000226231164314150500204340ustar00rootroot00000000000000/* * (c) 2007, Quest Software, Inc. All rights reserved. * * This file is part of XScreenSaver, * Copyright (c) 1993-2004 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #include #include #include #include "mlstring.c" /* hokey, but whatever */ #define WRAP_WIDTH_PX 100 #undef Bool #undef True #undef False typedef int Bool; #define True 1 #define False 0 #define SKIPPED -1 #define SUCCESS 0 #define FAILURE 1 #define FAIL(msg, ...) \ do { \ ++failcount; \ fprintf(stderr, "[FAIL] "); \ fprintf(stderr, msg, __VA_ARGS__); \ putc('\n', stderr); \ return FAILURE; \ } while (0) #define SUCCEED(testname) \ do { \ fprintf(stderr, "[SUCCESS] %s\n", (testname)); \ } while (0) #define SKIP(testname) \ do { \ fprintf(stderr, "[SKIPPED] %s\n", (testname)); \ } while (0) extern mlstring* mlstring_allocate(const char *msg); extern void mlstring_wrap(mlstring *mstr, XFontStruct *font, Dimension width); static int failcount = 0; static char *mlstring_to_cstr(const mlstring *mlstr) { char *cstr; size_t cstrlen = 0, alloclen = 1024; const struct mlstr_line *line; cstr = malloc(alloclen); if (!cstr) return NULL; cstr[0] = '\0'; for (line = mlstr->lines; line; line = line->next_line) { /* Extend the buffer if necessary. */ if (cstrlen + strlen(line->line) + 1 > alloclen) { cstr = realloc(cstr, alloclen *= 2); if (!cstr) return NULL; } /* If this is not the first line */ if (line != mlstr->lines) { /* Append a newline character */ cstr[cstrlen] = '\n'; ++cstrlen; cstr[cstrlen] = '\0'; } strcat(cstr, line->line); cstrlen += strlen(line->line); } return cstr; } /* Pass -1 for expect_min or expect_exact to not check that value. * expect_empty_p means an empty line is expected at some point in the string. * Also ensures that the string was not too wide after wrapping. */ static int mlstring_expect_lines(const mlstring *mlstr, int expect_min, int expect_exact, Bool expect_empty_p) { int count; Bool got_empty_line = False; const struct mlstr_line *line = mlstr->lines; for (count = 0; line; line = line->next_line) { if (line->line[0] == '\0') { if (!expect_empty_p) FAIL("Not expecting empty lines, but got one on line %d of [%s]", count + 1, mlstring_to_cstr(mlstr)); got_empty_line = True; } ++count; } if (expect_empty_p && !got_empty_line) FAIL("Expecting an empty line, but none found in [%s]", mlstring_to_cstr(mlstr)); if (expect_exact != -1 && expect_exact != count) FAIL("Expected %d lines, got %d", expect_exact, count); if (expect_min != -1 && count < expect_min) FAIL("Expected at least %d lines, got %d", expect_min, count); return SUCCESS; } static int mlstring_expect(const char *msg, int expect_lines, const mlstring *mlstr, Bool expect_empty_p) { char *str, *str_top; const struct mlstr_line *cur; int linecount = 0; /* Duplicate msg so we can chop it up */ str_top = strdup(msg); if (!str_top) return SKIPPED; /* Replace all newlines with NUL */ str = str_top; while ((str = strchr(str, '\n'))) *str++ = '\0'; /* str is now used to point to the expected string */ str = str_top; for (cur = mlstr->lines; cur; cur = cur->next_line) { ++linecount; if (strcmp(cur->line, str)) FAIL("lines didn't match; expected [%s], got [%s]", str, cur->line); str += strlen(str) + 1; /* Point to the next expected string */ } free(str_top); return mlstring_expect_lines(mlstr, -1, expect_lines, expect_empty_p); } /* Ensures that the width has been set properly after wrapping */ static int check_width(const char *msg, const mlstring *mlstr) { if (mlstr->overall_width == 0) FAIL("Overall width was zero for string [%s]", msg); if (mlstr->overall_width > WRAP_WIDTH_PX) FAIL("Overall width was %hu but the maximum wrap width was %d", mlstr->overall_width, WRAP_WIDTH_PX); return SUCCESS; } /* FAIL() actually returns the wrong return codes in main, but it * prints a message which is what we want. */ #define TRY_NEW(str, numl, expect_empty) \ do { \ mlstr = mlstring_allocate((str)); \ if (!mlstr) \ FAIL("%s", #str); \ if (SUCCESS == mlstring_expect((str), (numl), mlstr, (expect_empty))) \ SUCCEED(#str); \ free(mlstr); \ } while (0) /* Expects an XFontStruct* font, and tries to wrap to 100px */ #define TRY_WRAP(str, minl, expect_empty) \ do { \ mltest = mlstring_allocate((str)); \ if (!mltest) \ SKIP(#str); \ else { \ mlstring_wrap(mltest, font, WRAP_WIDTH_PX); \ check_width((str), mltest); \ if (SUCCESS == mlstring_expect_lines(mltest, (minl), -1, (expect_empty))) \ SUCCEED(#str); \ free(mltest); \ mltest = NULL; \ } \ } while (0) /* Ideally this function would use stub functions rather than real Xlib. * Then it would be possible to test for exact line counts, which would be * more reliable. * It also doesn't handle Xlib errors. * * Don't print anything based on the return value of this function, it only * returns a value so that I can use the FAIL() macro without warning. * * Anyone who understands this function wins a cookie ;) */ static int test_wrapping(void) { Display *dpy = NULL; XFontStruct *font = NULL; mlstring *mltest = NULL; int ok = 0; int chars_per_line, chars_first_word, i; const char *test_short = "a"; const char *test_hardwrap = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; const char *test_withnewlines = "a\nb"; char *test_softwrap = NULL; dpy = XOpenDisplay(NULL); if (!dpy) goto end; font = XLoadQueryFont(dpy, "fixed"); if (!font) goto end; TRY_WRAP(test_short, 1, False); TRY_WRAP(test_hardwrap, 2, False); TRY_WRAP(test_withnewlines, 2, False); /* See if wrapping splits on word boundaries like it should */ chars_per_line = WRAP_WIDTH_PX / font->max_bounds.width; if (chars_per_line < 3) goto end; /* Allocate for 2 lines + \0 */ test_softwrap = malloc(chars_per_line * 2 + 1); if (!test_softwrap) goto end; /* 2 = strlen(' a'); that is, the minimum space required to start a new word * on the same line. */ chars_first_word = chars_per_line - 2; for (i = 0; i < chars_first_word; ++i) { test_softwrap[i] = 'a'; /* first word */ test_softwrap[i + chars_per_line] = 'b'; /* second word */ } /* space between first & second words */ test_softwrap[chars_first_word] = ' '; /* first char of second word (last char of first line) */ test_softwrap[chars_first_word + 1] = 'b'; /* after second word */ test_softwrap[chars_per_line * 2] = '\0'; mltest = mlstring_allocate(test_softwrap); mlstring_wrap(mltest, font, WRAP_WIDTH_PX); /* reusing 'i' for a moment here to make freeing mltest easier */ i = strlen(mltest->lines->line); free(mltest); if (i != chars_first_word) FAIL("Soft wrap failed, expected the first line to be %d chars, but it was %d.", chars_first_word, i); SUCCEED("Soft wrap"); ok = 1; end: if (test_softwrap) free(test_softwrap); if (font) XFreeFont(dpy, font); if (dpy) XCloseDisplay(dpy); if (!ok) SKIP("wrapping"); return ok ? SUCCESS : SKIPPED; /* Unused, actually */ } int main(int argc, char *argv[]) { const char *oneline = "1Foo"; const char *twolines = "2Foo\nBar"; const char *threelines = "3Foo\nBar\nWhippet"; const char *trailnewline = "4Foo\n"; const char *trailnewlines = "5Foo\n\n"; const char *embeddednewlines = "6Foo\n\nBar"; mlstring *mlstr; TRY_NEW(oneline, 1, False); TRY_NEW(twolines, 2, False); TRY_NEW(threelines, 3, False); TRY_NEW(trailnewline, 2, True); TRY_NEW(trailnewlines, 3, True); TRY_NEW(embeddednewlines, 3, True); (void) test_wrapping(); fprintf(stdout, "%d test failures.\n", failcount); return !!failcount; } /* vim:ts=8:sw=2:noet */ xscreensaver-5.15/driver/test-passwd.c000066400000000000000000000206021164314150500200710ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 1998-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* This is a kludgy test harness for debugging the password dialog box. It's somewhat easier to debug it here than in the xscreensaver executable itself. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "xscreensaver.h" #include "resources.h" #include "version.h" #include "visual.h" #include "auth.h" char *progname = 0; char *progclass = 0; XrmDatabase db = 0; saver_info *global_si_kludge; FILE *real_stderr, *real_stdout; void monitor_power_on (saver_info *si, Bool on_p) {} Bool monitor_powered_on_p (saver_info *si) { return True; } void initialize_screensaver_window (saver_info *si) {} void raise_window (saver_info *si, Bool i, Bool b, Bool d) {} Bool blank_screen (saver_info *si) {return False;} void unblank_screen (saver_info *si) {} Bool select_visual (saver_screen_info *ssi, const char *v) { return False; } Bool window_exists_p (Display *dpy, Window window) {return True;} void start_notice_events_timer (saver_info *si, Window w, Bool b) {} Bool handle_clientmessage (saver_info *si, XEvent *e, Bool u) { return False; } int BadWindow_ehandler (Display *dpy, XErrorEvent *error) { exit(1); } const char *signal_name(int signal) { return "???"; } Bool restore_real_vroot (saver_info *si) { return False; } void store_saver_status (saver_info *si) {} void saver_exit (saver_info *si, int status, const char *core) { exit(status);} int move_mouse_grab (saver_info *si, Window to, Cursor c, int ts) { return 0; } int mouse_screen (saver_info *si) { return 0; } void check_for_leaks (const char *where) { } void shutdown_stderr (saver_info *si) { } void resize_screensaver_window (saver_info *si) { } void describe_monitor_layout (saver_info *si) { } Bool update_screen_layout (saver_info *si) { return 0; } const char *blurb(void) { return progname; } Atom XA_SCREENSAVER, XA_DEMO, XA_PREFS; void idle_timer (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; XEvent fake_event; fake_event.type = 0; /* XAnyEvent type, ignored. */ fake_event.xany.display = si->dpy; fake_event.xany.window = 0; XPutBackEvent (si->dpy, &fake_event); } static int text_auth_conv ( int num_msg, const struct auth_message *auth_msgs, struct auth_response **resp, saver_info *si) { char *input; char buf[255]; struct auth_response *responses; int i; responses = calloc(num_msg, sizeof(struct auth_response)); if (!responses) return -1; /* The unlock state won't actually be used until this function returns and * the auth module processes the response, but set it anyway for consistency */ si->unlock_state = ul_read; for (i = 0; i < num_msg; ++i) { printf ("\n%s: %s", progname, auth_msgs[i].msg); if ( auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO || auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO) { input = fgets (buf, sizeof(buf)-1, stdin); if (!input || !*input) exit (0); if (input[strlen(input)-1] == '\n') input[strlen(input)-1] = 0; responses[i].response = strdup(input); } } *resp = responses; si->unlock_state = ul_finished; return 0; } #ifdef __GNUC__ __extension__ /* shut up about "string length is greater than the length ISO C89 compilers are required to support" when including the .ad file... */ #endif static char *fallback[] = { #include "XScreenSaver_ad.h" 0 }; extern Bool debug_passwd_window_p; /* lock.c kludge */ int main (int argc, char **argv) { enum { PASS, SPLASH, TTY } which; Widget toplevel_shell = 0; saver_screen_info ssip; saver_info sip; saver_info *si = &sip; saver_preferences *p = &si->prefs; struct passwd *pw; memset(&sip, 0, sizeof(sip)); memset(&ssip, 0, sizeof(ssip)); si->nscreens = 1; si->screens = si->default_screen = &ssip; ssip.global = si; global_si_kludge = si; real_stderr = stderr; real_stdout = stdout; si->version = (char *) malloc (5); memcpy (si->version, screensaver_id + 17, 4); progname = argv[0]; { char *s = strrchr(progname, '/'); if (*s) strcpy (progname, s+1); } if (argc != 2) goto USAGE; else if (!strcmp (argv[1], "pass")) which = PASS; else if (!strcmp (argv[1], "splash")) which = SPLASH; else if (!strcmp (argv[1], "tty")) which = TTY; else { USAGE: fprintf (stderr, "usage: %s [ pass | splash | tty ]\n", progname); exit (1); } #ifdef NO_LOCKING if (which == PASS || which == TTY) { fprintf (stderr, "%s: compiled with NO_LOCKING\n", progname); exit (1); } #endif #ifndef NO_LOCKING /* before hack_uid() for proper permissions */ lock_priv_init (argc, argv, True); hack_uid (si); if (! lock_init (argc, argv, True)) { si->locking_disabled_p = True; si->nolock_reason = "error getting password"; } #endif progclass = "XScreenSaver"; if (!setlocale (LC_CTYPE, "")) fprintf (stderr, "%s: warning: could not set default locale\n", progname); if (which != TTY) { toplevel_shell = XtAppInitialize (&si->app, progclass, 0, 0, &argc, argv, fallback, 0, 0); si->dpy = XtDisplay (toplevel_shell); p->db = XtDatabase (si->dpy); si->default_screen->toplevel_shell = toplevel_shell; si->default_screen->screen = XtScreen(toplevel_shell); si->default_screen->default_visual = si->default_screen->current_visual = DefaultVisualOfScreen(si->default_screen->screen); si->default_screen->screensaver_window = RootWindowOfScreen(si->default_screen->screen); si->default_screen->current_depth = visual_depth(si->default_screen->screen, si->default_screen->current_visual); ssip.width = WidthOfScreen(ssip.screen); ssip.height = HeightOfScreen(ssip.screen); db = p->db; XtGetApplicationNameAndClass (si->dpy, &progname, &progclass); load_init_file (si->dpy, &si->prefs); } p->verbose_p = True; pw = getpwuid (getuid ()); si->user = strdup (pw->pw_name); /* si->nscreens = 0; si->screens = si->default_screen = 0; */ while (1) { #ifndef NO_LOCKING if (which == PASS) { si->unlock_cb = gui_auth_conv; si->auth_finished_cb = auth_finished_cb; debug_passwd_window_p = True; xss_authenticate(si, True); if (si->unlock_state == ul_success) fprintf (stderr, "%s: authentication succeeded\n", progname); else fprintf (stderr, "%s: authentication FAILED!\n", progname); XSync(si->dpy, False); fprintf (stderr, "\n######################################\n\n"); sleep (3); } else #endif if (which == SPLASH) { XEvent event; make_splash_dialog (si); XtAppAddTimeOut (si->app, p->splash_duration + 1000, idle_timer, (XtPointer) si); while (si->splash_dialog) { XtAppNextEvent (si->app, &event); if (event.xany.window == si->splash_dialog) handle_splash_event (si, &event); XtDispatchEvent (&event); } XSync (si->dpy, False); sleep (1); } #ifndef NO_LOCKING else if (which == TTY) { si->unlock_cb = text_auth_conv; printf ("%s: Authenticating user %s\n", progname, si->user); xss_authenticate(si, True); if (si->unlock_state == ul_success) printf ("%s: Ok!\n", progname); else printf ("%s: Wrong!\n", progname); } #endif else abort(); } free(si->user); } xscreensaver-5.15/driver/test-randr.c000066400000000000000000000264631164314150500177110ustar00rootroot00000000000000/* test-randr.c --- playing with the Resize And Rotate extension. * xscreensaver, Copyright (c) 2004-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #include #include char *progname = 0; char *progclass = "XScreenSaver"; static const char * blurb (void) { static char buf[255]; time_t now = time ((time_t *) 0); char *ct = (char *) ctime (&now); int n = strlen(progname); if (n > 100) n = 99; strncpy(buf, progname, n); buf[n++] = ':'; buf[n++] = ' '; strncpy(buf+n, ct+11, 8); strcpy(buf+n+9, ": "); return buf; } static Bool error_handler_hit_p = False; static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) { error_handler_hit_p = True; return 0; } int main (int argc, char **argv) { int event_number = -1, error_number = -1; int major = -1, minor = -1; int nscreens = 0; int i; XtAppContext app; Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); XtGetApplicationNameAndClass (dpy, &progname, &progclass); nscreens = ScreenCount(dpy); if (!XRRQueryExtension(dpy, &event_number, &error_number)) { fprintf(stderr, "%s: XRRQueryExtension(dpy, ...) ==> False\n", blurb()); fprintf(stderr, "%s: server does not support the RANDR extension.\n", blurb()); major = -1; } else { fprintf(stderr, "%s: XRRQueryExtension(dpy, ...) ==> %d, %d\n", blurb(), event_number, error_number); if (!XRRQueryVersion(dpy, &major, &minor)) { fprintf(stderr, "%s: XRRQueryVersion(dpy, ...) ==> False\n", blurb()); fprintf(stderr, "%s: server didn't report RANDR version numbers?\n", blurb()); } else fprintf(stderr, "%s: XRRQueryVersion(dpy, ...) ==> %d, %d\n", blurb(), major, minor); } for (i = 0; i < nscreens; i++) { XRRScreenConfiguration *rrc; XErrorHandler old_handler; XSync (dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); rrc = (major >= 0 ? XRRGetScreenInfo (dpy, RootWindow (dpy, i)) : 0); XSync (dpy, False); XSetErrorHandler (old_handler); XSync (dpy, False); if (error_handler_hit_p) { fprintf(stderr, "%s: XRRGetScreenInfo(dpy, %d) ==> X error:\n", blurb(), i); /* do it again without the error handler to print the error */ rrc = XRRGetScreenInfo (dpy, RootWindow (dpy, i)); } else if (rrc) { SizeID current_size = -1; Rotation current_rotation = ~0; fprintf (stderr, "\n%s: Screen %d\n", blurb(), i); current_size = XRRConfigCurrentConfiguration (rrc, ¤t_rotation); /* Times */ # if 0 /* #### This is wrong -- I don't understand what these two timestamp numbers represent, or how they correlate to the wall clock or to each other. */ { Time server_time, config_time; server_time = XRRConfigTimes (rrc, &config_time); if (config_time == 0 || server_time == 0) fprintf (stderr, "%s: config has never been changed\n", blurb()); else fprintf (stderr, "%s: config changed %lu seconds ago\n", blurb(), (unsigned long) (server_time - config_time)); } # endif /* Rotations */ { Rotation available, current; available = XRRConfigRotations (rrc, ¤t); fprintf (stderr, "%s: Available Rotations:\t", blurb()); if (available & RR_Rotate_0) fprintf (stderr, " 0"); if (available & RR_Rotate_90) fprintf (stderr, " 90"); if (available & RR_Rotate_180) fprintf (stderr, " 180"); if (available & RR_Rotate_270) fprintf (stderr, " 270"); if (! (available & (RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270))) fprintf (stderr, " none"); fprintf (stderr, "\n"); if (current_rotation != current) fprintf (stderr, "%s: WARNING: rotation inconsistency: 0x%X vs 0x%X\n", blurb(), current_rotation, current); fprintf (stderr, "%s: Current Rotation:\t", blurb()); if (current & RR_Rotate_0) fprintf (stderr, " 0"); if (current & RR_Rotate_90) fprintf (stderr, " 90"); if (current & RR_Rotate_180) fprintf (stderr, " 180"); if (current & RR_Rotate_270) fprintf (stderr, " 270"); if (! (current & (RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270))) fprintf (stderr, " none"); fprintf (stderr, "\n"); fprintf (stderr, "%s: Available Reflections:\t", blurb()); if (available & RR_Reflect_X) fprintf (stderr, " X"); if (available & RR_Reflect_Y) fprintf (stderr, " Y"); if (! (available & (RR_Reflect_X | RR_Reflect_Y))) fprintf (stderr, " none"); fprintf (stderr, "\n"); fprintf (stderr, "%s: Current Reflections:\t", blurb()); if (current & RR_Reflect_X) fprintf (stderr, " X"); if (current & RR_Reflect_Y) fprintf (stderr, " Y"); if (! (current & (RR_Reflect_X | RR_Reflect_Y))) fprintf (stderr, " none"); fprintf (stderr, "\n"); } /* Sizes */ { int nsizes, j; XRRScreenSize *rrsizes; rrsizes = XRRConfigSizes (rrc, &nsizes); if (nsizes <= 0) fprintf (stderr, "%s: sizes:\t none\n", blurb()); else for (j = 0; j < nsizes; j++) { short *rates; int nrates, k; fprintf (stderr, "%s: %c size %d: %d x %d\t rates:", blurb(), (j == current_size ? '+' : ' '), j, rrsizes[j].width, rrsizes[j].height); rates = XRRConfigRates (rrc, j, &nrates); if (nrates == 0) fprintf (stderr, " none?"); else for (k = 0; k < nrates; k++) fprintf (stderr, " %d", rates[k]); fprintf (stderr, "\n"); /* don't free 'rates' */ } /* don't free 'rrsizes' */ } XRRFreeScreenConfigInfo (rrc); } else if (major >= 0) { fprintf(stderr, "%s: XRRGetScreenInfo(dpy, %d) ==> NULL\n", blurb(), i); } # ifdef HAVE_RANDR_12 if (major > 1 || (major == 1 && minor >= 2)) { int j; XRRScreenResources *res = XRRGetScreenResources (dpy, RootWindow (dpy, i)); fprintf (stderr, "\n"); for (j = 0; j < res->noutput; j++) { int k; XRROutputInfo *rroi = XRRGetOutputInfo (dpy, res, res->outputs[j]); fprintf (stderr, "%s: Output %d: %s: %s (%d)\n", blurb(), j, rroi->name, (rroi->connection == RR_Disconnected ? "disconnected" : rroi->connection == RR_UnknownConnection ? "unknown" : "connected"), (int) rroi->crtc); for (k = 0; k < rroi->ncrtc; k++) { XRRCrtcInfo *crtci = XRRGetCrtcInfo (dpy, res, rroi->crtcs[k]); fprintf(stderr, "%s: %c CRTC %d (%d): %dx%d+%d+%d\n", blurb(), (rroi->crtc == rroi->crtcs[k] ? '+' : ' '), k, (int) rroi->crtcs[k], crtci->width, crtci->height, crtci->x, crtci->y); XRRFreeCrtcInfo (crtci); } XRRFreeOutputInfo (rroi); fprintf (stderr, "\n"); } XRRFreeScreenResources (res); } # endif /* HAVE_RANDR_12 */ } if (major > 0) { Window w[20]; XWindowAttributes xgwa[20]; for (i = 0; i < nscreens; i++) { XRRSelectInput (dpy, RootWindow (dpy, i), RRScreenChangeNotifyMask); w[i] = RootWindow (dpy, i); XGetWindowAttributes (dpy, w[i], &xgwa[i]); } XSync (dpy, False); fprintf (stderr, "\n%s: awaiting events...\n\n" "\t(If you resize the screen or add/remove monitors, this should\n" "\tnotice that and print stuff. Otherwise, hit ^C.)\n\n", progname); while (1) { XEvent event; XNextEvent (dpy, &event); if (event.type == event_number + RRScreenChangeNotify) { XRRScreenChangeNotifyEvent *xrr_event = (XRRScreenChangeNotifyEvent *) &event; int screen = XRRRootToScreen (dpy, xrr_event->window); fprintf (stderr, "%s: screen %d: RRScreenChangeNotify event\n", progname, screen); fprintf (stderr, "%s: screen %d: old size: \t%d x %d\n", progname, screen, DisplayWidth (dpy, screen), DisplayHeight (dpy, screen)); fprintf (stderr, "%s: screen %d: old root 0x%lx:\t%d x %d\n", progname, screen, (unsigned long) w[screen], xgwa[screen].width, xgwa[screen].height); XRRUpdateConfiguration (&event); XSync (dpy, False); fprintf (stderr, "%s: screen %d: new size: \t%d x %d\n", progname, screen, DisplayWidth (dpy, screen), DisplayHeight (dpy, screen)); w[screen] = RootWindow (dpy, screen); XGetWindowAttributes (dpy, w[screen], &xgwa[screen]); fprintf (stderr, "%s: screen %d: new root 0x%lx:\t%d x %d\n", progname, screen, (unsigned long) w[screen], xgwa[screen].width, xgwa[screen].height); fprintf (stderr, "\n"); } else { fprintf (stderr, "%s: event %d\n", progname, event.type); } } } XSync (dpy, False); exit (0); } xscreensaver-5.15/driver/test-screens.c000066400000000000000000000135371164314150500202430ustar00rootroot00000000000000/* test-screens.c --- some test cases for the "monitor sanity" checks. * xscreensaver, Copyright (c) 2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer #define XtAppContext void* #define XrmDatabase void* #define XtIntervalId void* #define XtPointer void* #define Widget void* #include "xscreensaver.h" #include "visual.h" #undef WidthOfScreen #undef HeightOfScreen #define WidthOfScreen(s) 10240 #define HeightOfScreen(s) 10240 #undef screen_number #define screen_number(s) ((int) s) #include "screens.c" /* to get at static void check_monitor_sanity() */ char *progname = 0; char *progclass = "XScreenSaver"; const char *blurb(void) { return progname; } Bool safe_XF86VidModeGetViewPort(Display *d, int i, int *x, int *y) { abort(); } void initialize_screen_root_widget(saver_screen_info *ssi) { abort(); } Visual *get_best_gl_visual (saver_info *si, Screen *sc) { abort(); } static const char * failstr (monitor_sanity san) { switch (san) { case S_SANE: return "OK"; case S_ENCLOSED: return "ENC"; case S_DUPLICATE: return "DUP"; case S_OVERLAP: return "OVR"; case S_OFFSCREEN: return "OFF"; case S_DISABLED: return "DIS"; default: abort(); break; } } static void test (int testnum, const char *screens, const char *desired) { monitor *monitors[100]; char result[2048]; char *out = result; int i, nscreens = 0; char *token = strtok (strdup(screens), ","); while (token) { monitor *m = calloc (1, sizeof (monitor)); char c; m->id = (testnum * 1000) + nscreens; if (5 == sscanf (token, "%dx%d+%d+%d@%d%c", &m->width, &m->height, &m->x, &m->y, (int *) &m->screen, &c)) ; else if (4 != sscanf (token, "%dx%d+%d+%d%c", &m->width, &m->height, &m->x, &m->y, &c)) { fprintf (stderr, "%s: unparsable geometry: %s\n", blurb(), token); exit (1); } monitors[nscreens] = m; nscreens++; token = strtok (0, ","); } monitors[nscreens] = 0; check_monitor_sanity (monitors); *out = 0; for (i = 0; i < nscreens; i++) { monitor *m = monitors[i]; if (out != result) *out++ = ','; if (m->sanity == S_SANE) { sprintf (out, "%dx%d+%d+%d", m->width, m->height, m->x, m->y); if (m->screen) sprintf (out + strlen(out), "@%d", (int) m->screen); } else strcpy (out, failstr (m->sanity)); out += strlen(out); } *out = 0; if (!strcmp (result, desired)) fprintf (stderr, "%s: test %2d OK\n", blurb(), testnum); else fprintf (stderr, "%s: test %2d FAILED:\n" "%s: given: %s\n" "%s: wanted: %s\n" "%s: got: %s\n", blurb(), testnum, blurb(), screens, blurb(), desired, blurb(), result); # if 0 { saver_info SI; SI.monitor_layout = monitors; describe_monitor_layout (&SI); } # endif } static void run_tests(void) { int i = 1; # define A(a) test (i++, a, a); # define B(a,b) test (i++, a, b) A(""); A("1024x768+0+0"); A("1024x768+0+0,1024x768+1024+0"); A("1024x768+0+0,1024x768+0+768"); A("1024x768+0+0,1024x768+0+768,1024x768+1024+0"); A("800x600+0+0,800x600+0+0@1,800x600+10+0@2"); B("1024x768+999999+0", "OFF"); B("1024x768+-999999+-999999", "OFF"); B("1024x768+0+0,1024x768+0+0", "1024x768+0+0,DUP"); B("1024x768+0+0,1024x768+0+0,1024x768+0+0", "1024x768+0+0,DUP,DUP"); B("1024x768+0+0,1024x768+1024+0,1024x768+0+0", "1024x768+0+0,1024x768+1024+0,DUP"); B("1280x1024+0+0,1024x768+0+64,800x600+0+0,640x480+0+0,720x400+0+0", "1280x1024+0+0,ENC,ENC,ENC,ENC"); B("1024x768+0+64,1280x1024+0+0,800x600+0+0,640x480+0+0,800x600+0+0,720x400+0+0", "ENC,1280x1024+0+0,ENC,ENC,ENC,ENC"); B("1024x768+0+64,1280x1024+0+0,800x600+0+0,640x480+0+0,1280x1024+0+0,720x400+0+0", "ENC,1280x1024+0+0,ENC,ENC,DUP,ENC"); B("720x400+0+0,640x480+0+0,800x600+0+0,1024x768+0+64,1280x1024+0+0", "ENC,ENC,ENC,ENC,1280x1024+0+0"); B("1280x1024+0+0,800x600+1280+0,800x600+1300+0", "1280x1024+0+0,800x600+1280+0,OVR"); B("1280x1024+0+0,800x600+1280+0,800x600+1300+0,1280x1024+0+0,800x600+1280+0", "1280x1024+0+0,800x600+1280+0,OVR,DUP,DUP"); /* +-------------+----+ +------+---+ 1: 1440x900, widescreen display | : | | 3+4 : | 2: 1280x1024, conventional display | 1+2 : 1 | +......+ | 3: 1024x768, laptop | : | | 3 | 4: 800x600, external projector +.............+----+ +----------+ | 2 | | | +-------------+ */ B("1440x900+0+0,1280x1024+0+0,1024x768+1440+0,800x600+1440+0", "1440x900+0+0,OVR,1024x768+1440+0,ENC"); B("800x600+0+0,800x600+0+0,800x600+800+0", "800x600+0+0,DUP,800x600+800+0"); B("1600x1200+0+0,1360x768+0+0", "1600x1200+0+0,ENC"); B("1600x1200+0+0,1360x768+0+0,1600x1200+0+0@1,1360x768+0+0@1", "1600x1200+0+0,ENC,1600x1200+0+0@1,ENC"); } int main (int argc, char **argv) { char *s; progname = argv[0]; s = strrchr(progname, '/'); if (s) progname = s+1; if (argc != 1) { fprintf (stderr, "usage: %s\n", argv[0]); exit (1); } run_tests(); exit (0); } xscreensaver-5.15/driver/test-uid.c000066400000000000000000000107041164314150500173530ustar00rootroot00000000000000/* test-uid.c --- playing with setuid. * xscreensaver, Copyright (c) 1998, 2005 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #include static void print(void) { int uid = getuid(); int gid = getgid(); int euid = geteuid(); int egid = getegid(); struct passwd *p = 0; struct group *g = 0; gid_t groups[1024]; int n, size; p = getpwuid (uid); g = getgrgid (gid); fprintf(stderr, "real user/group: %ld/%ld (%s/%s)\n", (long) uid, (long) gid, (p && p->pw_name ? p->pw_name : "???"), (g && g->gr_name ? g->gr_name : "???")); p = getpwuid (euid); g = getgrgid (egid); fprintf(stderr, "eff. user/group: %ld/%ld (%s/%s)\n", (long)euid, (long)egid, (p && p->pw_name ? p->pw_name : "???"), (g && g->gr_name ? g->gr_name : "???")); size = sizeof(groups) / sizeof(gid_t); n = getgroups(size - 1, groups); if (n < 0) perror("getgroups failed"); else { int i; fprintf (stderr, "eff. group list: ["); for (i = 0; i < n; i++) { g = getgrgid (groups[i]); fprintf(stderr, "%s%s=%ld", (i == 0 ? "" : ", "), (g->gr_name ? g->gr_name : "???"), (long) groups[i]); } fprintf (stderr, "]\n"); } } int main (int argc, char **argv) { int i; struct passwd *p = 0; struct group *g = 0; if (argc <= 1) { fprintf(stderr, "usage: %s [ user/group ... ]\n" "\tEach argument may be a user name, or user/group.\n" "\tThis program will attempt to setuid/setgid to each\n" "\tin turn, and report the results. The user and group\n" "\tnames may be strings, or numeric.\n", argv[0]); exit(1); } print(); for (i = 1; i < argc; i++) { char *user = argv[i]; char *group = strchr(user, '/'); if (!group) group = strchr(user, '.'); if (group) *group++ = 0; if (group && *group) { long gid = 0; int was_numeric = 0; g = 0; if (*group == '-' || (*group >= '0' && *group <= '9')) if (1 == sscanf(group, "%ld", &gid)) { g = getgrgid (gid); was_numeric = 1; } if (!g) g = getgrnam(group); if (g) { gid = g->gr_gid; group = g->gr_name; } else { if (was_numeric) { fprintf(stderr, "no group numbered %s.\n", group); group = ""; } else { fprintf(stderr, "no group named %s.\n", group); goto NOGROUP; } } fprintf(stderr, "setgroups(1, [%ld]) \"%s\"", gid, group); { gid_t g2 = gid; if (setgroups(1, &g2) == 0) fprintf(stderr, " succeeded.\n"); else perror(" failed"); } fprintf(stderr, "setgid(%ld) \"%s\"", gid, group); if (setgid(gid) == 0) fprintf(stderr, " succeeded.\n"); else perror(" failed"); NOGROUP: ; } if (user && *user) { long uid = 0; int was_numeric = 0; p = 0; if (*user == '-' || (*user >= '0' && *user <= '9')) if (1 == sscanf(user, "%ld", &uid)) { p = getpwuid (uid); was_numeric = 1; } if (!p) p = getpwnam(user); if (p) { uid = p->pw_uid; user = p->pw_name; } else { if (was_numeric) { fprintf(stderr, "no user numbered \"%s\".\n", user); user = ""; } else { fprintf(stderr, "no user named %s.\n", user); goto NOUSER; } } fprintf(stderr, "setuid(%ld) \"%s\"", uid, user); if (setuid(uid) == 0) fprintf(stderr, " succeeded.\n"); else perror(" failed"); NOUSER: ; } print(); } fprintf(stderr, "running \"whoami\" and \"groups\" in a sub-process reports:\n"); fflush(stdout); fflush(stderr); system ("/bin/sh -c 'echo \"`whoami` / `groups`\"'"); fflush(stdout); fflush(stderr); exit(0); } xscreensaver-5.15/driver/test-vp.c000066400000000000000000000132631164314150500172220ustar00rootroot00000000000000/* test-xinerama.c --- playing with XF86VidModeGetViewPort * xscreensaver, Copyright (c) 2004 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #include #include #include char *progname = 0; char *progclass = "XScreenSaver"; static const char * blurb (void) { static char buf[255]; time_t now = time ((time_t *) 0); char *ct = (char *) ctime (&now); int n = strlen(progname); if (n > 100) n = 99; strncpy(buf, progname, n); buf[n++] = ':'; buf[n++] = ' '; strncpy(buf+n, ct+11, 8); strcpy(buf+n+9, ": "); return buf; } static Bool error_handler_hit_p = False; static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) { error_handler_hit_p = True; return 0; } static int screen_count (Display *dpy) { int n = ScreenCount(dpy); int xn = 0; int event_number, error_number; if (!XineramaQueryExtension (dpy, &event_number, &error_number)) { fprintf(stderr, "%s: XineramaQueryExtension(dpy, ...) ==> False\n", blurb()); goto DONE; } else fprintf(stderr, "%s: XineramaQueryExtension(dpy, ...) ==> %d, %d\n", blurb(), event_number, error_number); if (!XineramaIsActive(dpy)) { fprintf(stderr, "%s: XineramaIsActive(dpy) ==> False\n", blurb()); goto DONE; } else { int major, minor; XineramaScreenInfo *xsi; fprintf(stderr, "%s: XineramaIsActive(dpy) ==> True\n", blurb()); if (!XineramaQueryVersion(dpy, &major, &minor)) { fprintf(stderr, "%s: XineramaQueryVersion(dpy, ...) ==> False\n", blurb()); goto DONE; } else fprintf(stderr, "%s: XineramaQueryVersion(dpy, ...) ==> %d, %d\n", blurb(), major, minor); xsi = XineramaQueryScreens (dpy, &xn); if (xsi) XFree (xsi); } DONE: fprintf (stderr, "\n"); fprintf (stderr, "%s: X client screens: %d\n", blurb(), n); fprintf (stderr, "%s: Xinerama screens: %d\n", blurb(), xn); fprintf (stderr, "\n"); if (xn > n) return xn; else return n; } int main (int argc, char **argv) { int event_number, error_number; int major, minor; int nscreens = 0; int i; XtAppContext app; Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); XtGetApplicationNameAndClass (dpy, &progname, &progclass); if (!XF86VidModeQueryExtension(dpy, &event_number, &error_number)) { fprintf(stderr, "%s: XF86VidModeQueryExtension(dpy, ...) ==> False\n", blurb()); fprintf(stderr, "%s: server does not support the XF86VidMode extension.\n", blurb()); exit(1); } else fprintf(stderr, "%s: XF86VidModeQueryExtension(dpy, ...) ==> %d, %d\n", blurb(), event_number, error_number); if (!XF86VidModeQueryVersion(dpy, &major, &minor)) { fprintf(stderr, "%s: XF86VidModeQueryVersion(dpy, ...) ==> False\n", blurb()); fprintf(stderr, "%s: server didn't report XF86VidMode version numbers?\n", blurb()); } else fprintf(stderr, "%s: XF86VidModeQueryVersion(dpy, ...) ==> %d, %d\n", blurb(), major, minor); nscreens = screen_count (dpy); for (i = 0; i < nscreens; i++) { int result = 0; int x = 0, y = 0, dot = 0; XF86VidModeModeLine ml = { 0, }; XErrorHandler old_handler; XSync (dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); result = XF86VidModeGetViewPort (dpy, i, &x, &y); XSync (dpy, False); XSetErrorHandler (old_handler); XSync (dpy, False); if (error_handler_hit_p) { fprintf(stderr, "%s: XF86VidModeGetViewPort(dpy, %d, ...) ==> X error\n", blurb(), i); continue; } if (! result) fprintf(stderr, "%s: XF86VidModeGetViewPort(dpy, %d, ...) ==> %d\n", blurb(), i, result); result = XF86VidModeGetModeLine (dpy, i, &dot, &ml); if (! result) fprintf(stderr, "%s: XF86VidModeGetModeLine(dpy, %d, ...) ==> %d\n", blurb(), i, result); fprintf (stderr, "%s: screen %d: %dx%d; viewport: %dx%d+%d+%d\n", blurb(), i, DisplayWidth (dpy, i), DisplayHeight (dpy, i), ml.hdisplay, ml.vdisplay, x, y ); fprintf (stderr, "%s: hsync start %d; end %d; total %d; skew %d;\n", blurb(), ml.hsyncstart, ml.hsyncend, ml.htotal, ml.hskew); fprintf (stderr, "%s: vsync start %d; end %d; total %d; flags 0x%04x;\n", blurb(), ml.vsyncstart, ml.vsyncend, ml.vtotal, ml.flags); fprintf (stderr, "\n"); } XSync (dpy, False); exit (0); } xscreensaver-5.15/driver/test-xdpms.c000066400000000000000000000117551164314150500177340ustar00rootroot00000000000000/* test-xdpms.c --- playing with the XDPMS extension. * xscreensaver, Copyright (c) 1998-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #include #include #include extern Bool DPMSQueryExtension (Display *dpy, int *event_ret, int *error_ret); extern Bool DPMSCapable (Display *dpy); extern Status DPMSForceLevel (Display *dpy, CARD16 level); extern Status DPMSInfo (Display *dpy, CARD16 *power_level, BOOL *state); extern Status DPMSGetVersion (Display *dpy, int *major_ret, int *minor_ret); extern Status DPMSSetTimeouts (Display *dpy, CARD16 standby, CARD16 suspend, CARD16 off); extern Bool DPMSGetTimeouts (Display *dpy, CARD16 *standby, CARD16 *suspend, CARD16 *off); extern Status DPMSEnable (Display *dpy); extern Status DPMSDisable (Display *dpy); char *progname = 0; char *progclass = "XScreenSaver"; static const char * blurb (void) { static char buf[255]; time_t now = time ((time_t *) 0); char *ct = (char *) ctime (&now); int n = strlen(progname); if (n > 100) n = 99; strncpy(buf, progname, n); buf[n++] = ':'; buf[n++] = ' '; strncpy(buf+n, ct+11, 8); strcpy(buf+n+9, ": "); return buf; } static Bool error_handler_hit_p = False; static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) { error_handler_hit_p = True; return 0; } int main (int argc, char **argv) { int delay = 10; int event_number, error_number; int major, minor; CARD16 standby, suspend, off; CARD16 state; BOOL onoff; XtAppContext app; Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); XtGetApplicationNameAndClass (dpy, &progname, &progclass); if (!DPMSQueryExtension(dpy, &event_number, &error_number)) { fprintf(stderr, "%s: DPMSQueryExtension(dpy, ...) ==> False\n", blurb()); fprintf(stderr, "%s: server does not support the XDPMS extension.\n", blurb()); exit(1); } else fprintf(stderr, "%s: DPMSQueryExtension(dpy, ...) ==> %d, %d\n", blurb(), event_number, error_number); if (!DPMSCapable(dpy)) { fprintf(stderr, "%s: DPMSCapable(dpy) ==> False\n", blurb()); fprintf(stderr, "%s: server says hardware doesn't support DPMS.\n", blurb()); exit(1); } else fprintf(stderr, "%s: DPMSCapable(dpy) ==> True\n", blurb()); if (!DPMSGetVersion(dpy, &major, &minor)) { fprintf(stderr, "%s: DPMSGetVersion(dpy, ...) ==> False\n", blurb()); fprintf(stderr, "%s: server didn't report XDPMS version numbers?\n", blurb()); } else fprintf(stderr, "%s: DPMSGetVersion(dpy, ...) ==> %d, %d\n", blurb(), major, minor); if (!DPMSGetTimeouts(dpy, &standby, &suspend, &off)) { fprintf(stderr, "%s: DPMSGetTimeouts(dpy, ...) ==> False\n", blurb()); fprintf(stderr, "%s: server didn't report DPMS timeouts?\n", blurb()); } else fprintf(stderr, "%s: DPMSGetTimeouts(dpy, ...)\n" "\t ==> standby = %d, suspend = %d, off = %d\n", blurb(), standby, suspend, off); while (1) { if (!DPMSInfo(dpy, &state, &onoff)) { fprintf(stderr, "%s: DPMSInfo(dpy, ...) ==> False\n", blurb()); fprintf(stderr, "%s: couldn't read DPMS state?\n", blurb()); onoff = 0; state = -1; } else { fprintf(stderr, "%s: DPMSInfo(dpy, ...) ==> %s, %s\n", blurb(), (state == DPMSModeOn ? "DPMSModeOn" : state == DPMSModeStandby ? "DPMSModeStandby" : state == DPMSModeSuspend ? "DPMSModeSuspend" : state == DPMSModeOff ? "DPMSModeOff" : "???"), (onoff == 1 ? "On" : onoff == 0 ? "Off" : "???")); } if (state == DPMSModeStandby || state == DPMSModeSuspend || state == DPMSModeOff) { XErrorHandler old_handler; int st; fprintf(stderr, "%s: monitor is off; turning it on.\n", blurb()); XSync (dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); XSync (dpy, False); st = DPMSForceLevel (dpy, DPMSModeOn); XSync (dpy, False); if (error_handler_hit_p) st = -666; fprintf (stderr, "%s: DPMSForceLevel (dpy, DPMSModeOn) ==> %s\n", blurb(), (st == -666 ? "X Error" : st ? "Ok" : "Error")); } sleep (delay); } } xscreensaver-5.15/driver/test-xinerama.c000066400000000000000000000060471164314150500204030ustar00rootroot00000000000000/* test-xinerama.c --- playing with the Xinerama extension. * xscreensaver, Copyright (c) 2003 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #include #include char *progname = 0; char *progclass = "XScreenSaver"; static const char * blurb (void) { static char buf[255]; time_t now = time ((time_t *) 0); char *ct = (char *) ctime (&now); int n = strlen(progname); if (n > 100) n = 99; strncpy(buf, progname, n); buf[n++] = ':'; buf[n++] = ' '; strncpy(buf+n, ct+11, 8); strcpy(buf+n+9, ": "); return buf; } int main (int argc, char **argv) { int event_number, error_number; int major, minor; int nscreens = 0; XineramaScreenInfo *xsi; int i; XtAppContext app; Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); XtGetApplicationNameAndClass (dpy, &progname, &progclass); if (!XineramaQueryExtension(dpy, &event_number, &error_number)) { fprintf(stderr, "%s: XineramaQueryExtension(dpy, ...) ==> False\n", blurb()); fprintf(stderr, "%s: server does not support the Xinerama extension.\n", blurb()); exit(1); } else fprintf(stderr, "%s: XineramaQueryExtension(dpy, ...) ==> %d, %d\n", blurb(), event_number, error_number); if (!XineramaIsActive(dpy)) { fprintf(stderr, "%s: XineramaIsActive(dpy) ==> False\n", blurb()); fprintf(stderr, "%s: server says Xinerama is turned off.\n", blurb()); exit(1); } else fprintf(stderr, "%s: XineramaIsActive(dpy) ==> True\n", blurb()); if (!XineramaQueryVersion(dpy, &major, &minor)) { fprintf(stderr, "%s: XineramaQueryVersion(dpy, ...) ==> False\n", blurb()); fprintf(stderr, "%s: server didn't report Xinerama version numbers?\n", blurb()); } else fprintf(stderr, "%s: XineramaQueryVersion(dpy, ...) ==> %d, %d\n", blurb(), major, minor); xsi = XineramaQueryScreens (dpy, &nscreens); fprintf(stderr, "%s: %d Xinerama screens\n", blurb(), nscreens); for (i = 0; i < nscreens; i++) fprintf (stderr, "%s: screen %d: %dx%d+%d+%d\n", blurb(), xsi[i].screen_number, xsi[i].width, xsi[i].height, xsi[i].x_org, xsi[i].y_org); XFree (xsi); XSync (dpy, False); exit (0); } xscreensaver-5.15/driver/timers.c000066400000000000000000001454521164314150500171310ustar00rootroot00000000000000/* timers.c --- detecting when the user is idle, and other timer-related tasks. * xscreensaver, Copyright (c) 1991-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #ifdef HAVE_XMU # ifndef VMS # include # else /* VMS */ # include # endif /* VMS */ # else /* !HAVE_XMU */ # include "xmu.h" #endif /* !HAVE_XMU */ #ifdef HAVE_XIDLE_EXTENSION #include #endif /* HAVE_XIDLE_EXTENSION */ #ifdef HAVE_MIT_SAVER_EXTENSION #include #endif /* HAVE_MIT_SAVER_EXTENSION */ #ifdef HAVE_SGI_SAVER_EXTENSION #include #endif /* HAVE_SGI_SAVER_EXTENSION */ #ifdef HAVE_RANDR #include #endif /* HAVE_RANDR */ #include "xscreensaver.h" #undef ABS #define ABS(x)((x)<0?-(x):(x)) #undef MAX #define MAX(x,y)((x)>(y)?(x):(y)) #ifdef HAVE_PROC_INTERRUPTS static Bool proc_interrupts_activity_p (saver_info *si); #endif /* HAVE_PROC_INTERRUPTS */ static void check_for_clock_skew (saver_info *si); void idle_timer (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; /* What an amazingly shitty design. Not only does Xt execute timeout events from XtAppNextEvent() instead of from XtDispatchEvent(), but there is no way to tell Xt to block until there is an X event OR a timeout happens. Once your timeout proc is called, XtAppNextEvent() still won't return until a "real" X event comes in. So this function pushes a stupid, gratuitous, unnecessary event back on the event queue to force XtAppNextEvent to return Right Fucking Now. When the code in sleep_until_idle() sees an event of type XAnyEvent, which the server never generates, it knows that a timeout has occurred. */ XEvent fake_event; fake_event.type = 0; /* XAnyEvent type, ignored. */ fake_event.xany.display = si->dpy; fake_event.xany.window = 0; XPutBackEvent (si->dpy, &fake_event); /* If we are the timer that just went off, clear the pointer to the id. */ if (id) { if (si->timer_id && *id != si->timer_id) abort(); /* oops, scheduled timer twice?? */ si->timer_id = 0; } } void schedule_wakeup_event (saver_info *si, Time when, Bool verbose_p) { if (si->timer_id) { if (verbose_p) fprintf (stderr, "%s: idle_timer already running\n", blurb()); return; } /* Wake up periodically to ask the server if we are idle. */ si->timer_id = XtAppAddTimeOut (si->app, when, idle_timer, (XtPointer) si); if (verbose_p) fprintf (stderr, "%s: starting idle_timer (%ld, %ld)\n", blurb(), when, si->timer_id); } static void notice_events (saver_info *si, Window window, Bool top_p) { saver_preferences *p = &si->prefs; XWindowAttributes attrs; unsigned long events; Window root, parent, *kids; unsigned int nkids; int screen_no; if (XtWindowToWidget (si->dpy, window)) /* If it's one of ours, don't mess up its event mask. */ return; if (!XQueryTree (si->dpy, window, &root, &parent, &kids, &nkids)) return; if (window == root) top_p = False; /* Figure out which screen this window is on, for the diagnostics. */ for (screen_no = 0; screen_no < si->nscreens; screen_no++) if (root == RootWindowOfScreen (si->screens[screen_no].screen)) break; XGetWindowAttributes (si->dpy, window, &attrs); events = ((attrs.all_event_masks | attrs.do_not_propagate_mask) & KeyPressMask); /* Select for SubstructureNotify on all windows. Select for KeyPress on all windows that already have it selected. Note that we can't select for ButtonPress, because of X braindamage: only one client at a time may select for ButtonPress on a given window, though any number can select for KeyPress. Someone explain *that* to me. So, if the user spends a while clicking the mouse without ever moving the mouse or touching the keyboard, we won't know that they've been active, and the screensaver will come on. That sucks, but I don't know how to get around it. Since X presents mouse wheels as clicks, this applies to those, too: scrolling through a document using only the mouse wheel doesn't count as activity... Fortunately, /proc/interrupts helps, on systems that have it. Oh, if it's a PS/2 mouse, not serial or USB. This sucks! */ XSelectInput (si->dpy, window, SubstructureNotifyMask | events); if (top_p && p->debug_p && (events & KeyPressMask)) { /* Only mention one window per tree (hack hack). */ fprintf (stderr, "%s: %d: selected KeyPress on 0x%lX\n", blurb(), screen_no, (unsigned long) window); top_p = False; } if (kids) { while (nkids) notice_events (si, kids [--nkids], top_p); XFree ((char *) kids); } } int BadWindow_ehandler (Display *dpy, XErrorEvent *error) { /* When we notice a window being created, we spawn a timer that waits 30 seconds or so, and then selects events on that window. This error handler is used so that we can cope with the fact that the window may have been destroyed <30 seconds after it was created. */ if (error->error_code == BadWindow || error->error_code == BadMatch || error->error_code == BadDrawable) return 0; else return saver_ehandler (dpy, error); } struct notice_events_timer_arg { saver_info *si; Window w; }; static void notice_events_timer (XtPointer closure, XtIntervalId *id) { struct notice_events_timer_arg *arg = (struct notice_events_timer_arg *) closure; XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler); saver_info *si = arg->si; Window window = arg->w; free(arg); notice_events (si, window, True); XSync (si->dpy, False); XSetErrorHandler (old_handler); } void start_notice_events_timer (saver_info *si, Window w, Bool verbose_p) { saver_preferences *p = &si->prefs; struct notice_events_timer_arg *arg = (struct notice_events_timer_arg *) malloc(sizeof(*arg)); arg->si = si; arg->w = w; XtAppAddTimeOut (si->app, p->notice_events_timeout, notice_events_timer, (XtPointer) arg); if (verbose_p) fprintf (stderr, "%s: starting notice_events_timer for 0x%X (%lu)\n", blurb(), (unsigned int) w, p->notice_events_timeout); } /* When the screensaver is active, this timer will periodically change the running program. */ void cycle_timer (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; saver_preferences *p = &si->prefs; Time how_long = p->cycle; if (si->selection_mode > 0 && screenhack_running_p (si)) /* If we're in "SELECT n" mode, the cycle timer going off will just restart this same hack again. There's not much point in doing this every 5 or 10 minutes, but on the other hand, leaving one hack running for days is probably not a great idea, since they tend to leak and/or crash. So, restart the thing once an hour. */ how_long = 1000 * 60 * 60; if (si->dbox_up_p) { if (p->verbose_p) fprintf (stderr, "%s: dialog box up; delaying hack change.\n", blurb()); how_long = 30000; /* 30 secs */ } else { int i; maybe_reload_init_file (si); for (i = 0; i < si->nscreens; i++) kill_screenhack (&si->screens[i]); raise_window (si, True, True, False); if (!si->throttled_p) for (i = 0; i < si->nscreens; i++) spawn_screenhack (&si->screens[i]); else { if (p->verbose_p) fprintf (stderr, "%s: not launching new hack (throttled.)\n", blurb()); } } if (how_long > 0) { si->cycle_id = XtAppAddTimeOut (si->app, how_long, cycle_timer, (XtPointer) si); if (p->debug_p) fprintf (stderr, "%s: starting cycle_timer (%ld, %ld)\n", blurb(), how_long, si->cycle_id); } else { if (p->debug_p) fprintf (stderr, "%s: not starting cycle_timer: how_long == %ld\n", blurb(), (unsigned long) how_long); } } void activate_lock_timer (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; saver_preferences *p = &si->prefs; if (p->verbose_p) fprintf (stderr, "%s: timed out; activating lock.\n", blurb()); set_locked_p (si, True); } /* Call this when user activity (or "simulated" activity) has been noticed. */ void reset_timers (saver_info *si) { saver_preferences *p = &si->prefs; if (si->using_mit_saver_extension || si->using_sgi_saver_extension) return; if (si->timer_id) { if (p->debug_p) fprintf (stderr, "%s: killing idle_timer (%ld, %ld)\n", blurb(), p->timeout, si->timer_id); XtRemoveTimeOut (si->timer_id); si->timer_id = 0; } schedule_wakeup_event (si, p->timeout, p->debug_p); /* sets si->timer_id */ if (si->cycle_id) abort (); /* no cycle timer when inactive */ si->last_activity_time = time ((time_t *) 0); /* This will (hopefully, supposedly) tell the server to re-set its DPMS timer. Without this, the -deactivate clientmessage would prevent xscreensaver from blanking, but would not prevent the monitor from powering down. */ #if 0 /* #### With some servers, this causes the screen to flicker every time a key is pressed! Ok, I surrender. I give up on ever having DPMS work properly. */ XForceScreenSaver (si->dpy, ScreenSaverReset); /* And if the monitor is already powered off, turn it on. You'd think the above would do that, but apparently not? */ monitor_power_on (si, True); #endif } /* Returns true if the mouse has moved since the last time we checked. Small motions (of less than "hysteresis" pixels/second) are ignored. */ static Bool pointer_moved_p (saver_screen_info *ssi, Bool mods_p) { saver_info *si = ssi->global; saver_preferences *p = &si->prefs; Window root, child; int root_x, root_y, x, y; unsigned int mask; time_t now = time ((time_t *) 0); unsigned int distance, dps; unsigned long seconds = 0; Bool moved_p = False; /* don't check xinerama pseudo-screens. */ if (!ssi->real_screen_p) return False; if (!XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child, &root_x, &root_y, &x, &y, &mask)) { /* If XQueryPointer() returns false, the mouse is not on this screen. */ x = root_x = -1; y = root_y = -1; root = child = 0; mask = 0; } distance = MAX (ABS (ssi->poll_mouse_last_root_x - root_x), ABS (ssi->poll_mouse_last_root_y - root_y)); seconds = (now - ssi->poll_mouse_last_time); /* When the screen is blanked, we get MotionNotify events, but when not blanked, we poll only every 5 seconds, and that's not enough resolution to do hysteresis based on a 1 second interval. So, assume that any motion we've seen during the 5 seconds when our eyes were closed happened in the last 1 second instead. */ if (seconds > 1) seconds = 1; dps = (seconds <= 0 ? distance : (distance / seconds)); /* Motion only counts if the rate is more than N pixels per second. */ if (dps >= p->pointer_hysteresis && distance > 0) moved_p = True; /* If the mouse is not on this screen but used to be, that's motion. If the mouse was not on this screen, but is now, that's motion. */ { Bool on_screen_p = (root_x != -1 && root_y != -1); Bool was_on_screen_p = (ssi->poll_mouse_last_root_x != -1 && ssi->poll_mouse_last_root_y != -1); if (on_screen_p != was_on_screen_p) moved_p = True; } if (p->debug_p && (distance != 0 || moved_p)) { fprintf (stderr, "%s: %d: pointer %s", blurb(), ssi->number, (moved_p ? "moved: " : "ignored:")); if (ssi->poll_mouse_last_root_x == -1) fprintf (stderr, "off screen"); else fprintf (stderr, "%d,%d", ssi->poll_mouse_last_root_x, ssi->poll_mouse_last_root_y); fprintf (stderr, " -> "); if (root_x == -1) fprintf (stderr, "off screen"); else fprintf (stderr, "%d,%d", root_x, root_y); if (ssi->poll_mouse_last_root_x != -1 && root_x != -1) fprintf (stderr, " (%d,%d; %d/%lu=%d)", ABS(ssi->poll_mouse_last_root_x - root_x), ABS(ssi->poll_mouse_last_root_y - root_y), distance, seconds, dps); fprintf (stderr, ".\n"); } if (!moved_p && mods_p && mask != ssi->poll_mouse_last_mask) { moved_p = True; if (p->debug_p) fprintf (stderr, "%s: %d: modifiers changed: 0x%04x -> 0x%04x.\n", blurb(), ssi->number, ssi->poll_mouse_last_mask, mask); } si->last_activity_screen = ssi; ssi->poll_mouse_last_child = child; ssi->poll_mouse_last_mask = mask; if (moved_p || seconds > 0) { ssi->poll_mouse_last_time = now; ssi->poll_mouse_last_root_x = root_x; ssi->poll_mouse_last_root_y = root_y; } return moved_p; } /* When we aren't using a server extension, this timer is used to periodically wake up and poll the mouse position, which is possibly more reliable than selecting motion events on every window. */ static void check_pointer_timer (XtPointer closure, XtIntervalId *id) { int i; saver_info *si = (saver_info *) closure; saver_preferences *p = &si->prefs; Bool active_p = False; if (!si->using_proc_interrupts && (si->using_xidle_extension || si->using_mit_saver_extension || si->using_sgi_saver_extension)) /* If an extension is in use, we should not be polling the mouse. Unless we're also checking /proc/interrupts, in which case, we should. */ abort (); if (id && *id == si->check_pointer_timer_id) /* this is us - it's expired */ si->check_pointer_timer_id = 0; if (si->check_pointer_timer_id) /* only queue one at a time */ XtRemoveTimeOut (si->check_pointer_timer_id); si->check_pointer_timer_id = /* now re-queue */ XtAppAddTimeOut (si->app, p->pointer_timeout, check_pointer_timer, (XtPointer) si); for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (pointer_moved_p (ssi, True)) active_p = True; } #ifdef HAVE_PROC_INTERRUPTS if (!active_p && si->using_proc_interrupts && proc_interrupts_activity_p (si)) { active_p = True; } #endif /* HAVE_PROC_INTERRUPTS */ if (active_p) reset_timers (si); check_for_clock_skew (si); } /* An unfortunate situation is this: the saver is not active, because the user has been typing. The machine is a laptop. The user closes the lid and suspends it. The CPU halts. Some hours later, the user opens the lid. At this point, Xt's timers will fire, and xscreensaver will blank the screen. So far so good -- well, not really, but it's the best that we can do, since the OS doesn't send us a signal *before* shutdown -- but if the user had delayed locking (lockTimeout > 0) then we should start off in the locked state, rather than only locking N minutes from when the lid was opened. Also, eschewing fading is probably a good idea, to clamp down as soon as possible. We only do this when we'd be polling the mouse position anyway. This amounts to an assumption that machines with APM support also have /proc/interrupts. */ static void check_for_clock_skew (saver_info *si) { saver_preferences *p = &si->prefs; time_t now = time ((time_t *) 0); long shift = now - si->last_wall_clock_time; if (p->debug_p) { int i = (si->last_wall_clock_time == 0 ? 0 : shift); fprintf (stderr, "%s: checking wall clock for hibernation (%d:%02d:%02d).\n", blurb(), (i / (60 * 60)), ((i / 60) % 60), (i % 60)); } if (si->last_wall_clock_time != 0 && shift > (p->timeout / 1000)) { if (p->verbose_p) fprintf (stderr, "%s: wall clock has jumped by %ld:%02ld:%02ld!\n", blurb(), (shift / (60 * 60)), ((shift / 60) % 60), (shift % 60)); si->emergency_lock_p = True; idle_timer ((XtPointer) si, 0); } si->last_wall_clock_time = now; } static void dispatch_event (saver_info *si, XEvent *event) { /* If this is for the splash dialog, pass it along. Note that the password dialog is handled with its own event loop, so events for that window will never come through here. */ if (si->splash_dialog && event->xany.window == si->splash_dialog) handle_splash_event (si, event); XtDispatchEvent (event); } static void swallow_unlock_typeahead_events (saver_info *si, XEvent *e) { XEvent event; char buf [100]; int i = 0; memset (buf, 0, sizeof(buf)); event = *e; do { if (event.xany.type == KeyPress) { char s[2]; int size = XLookupString ((XKeyEvent *) &event, s, 1, 0, 0); if (size != 1) continue; switch (*s) { case '\010': case '\177': /* Backspace */ if (i > 0) i--; break; case '\025': case '\030': /* Erase line */ case '\012': case '\015': /* Enter */ case '\033': /* ESC */ i = 0; break; case '\040': /* Space */ if (i == 0) break; /* ignore space at beginning of line */ /* else, fall through */ default: buf [i++] = *s; break; } } } while (i < sizeof(buf)-1 && XCheckMaskEvent (si->dpy, KeyPressMask, &event)); buf[i] = 0; if (si->unlock_typeahead) { memset (si->unlock_typeahead, 0, strlen(si->unlock_typeahead)); free (si->unlock_typeahead); } if (i > 0) si->unlock_typeahead = strdup (buf); else si->unlock_typeahead = 0; memset (buf, 0, sizeof(buf)); } /* methods of detecting idleness: explicitly informed by SGI SCREEN_SAVER server event; explicitly informed by MIT-SCREEN-SAVER server event; poll server idle time with XIDLE extension; select events on all windows, and note absence of recent events; note that /proc/interrupts has not changed in a while; activated by clientmessage. methods of detecting non-idleness: read events on the xscreensaver window; explicitly informed by SGI SCREEN_SAVER server event; explicitly informed by MIT-SCREEN-SAVER server event; select events on all windows, and note events on any of them; note that /proc/interrupts has changed; deactivated by clientmessage. I trust that explains why this function is a big hairy mess. */ void sleep_until_idle (saver_info *si, Bool until_idle_p) { saver_preferences *p = &si->prefs; /* We have to go through this union bullshit because gcc-4.4.0 has stricter struct-aliasing rules. Without this, the optimizer can fuck things up. */ union { XEvent x_event; # ifdef HAVE_RANDR XRRScreenChangeNotifyEvent xrr_event; # endif /* HAVE_RANDR */ # ifdef HAVE_MIT_SAVER_EXTENSION XScreenSaverNotifyEvent sevent; # endif /* HAVE_MIT_SAVER_EXTENSION */ } event; /* We need to select events on all windows if we're not using any extensions. Otherwise, we don't need to. */ Bool scanning_all_windows = !(si->using_xidle_extension || si->using_mit_saver_extension || si->using_sgi_saver_extension); /* We need to periodically wake up and check for idleness if we're not using any extensions, or if we're using the XIDLE extension. The other two extensions explicitly deliver events when we go idle/non-idle, so we don't need to poll. */ Bool polling_for_idleness = !(si->using_mit_saver_extension || si->using_sgi_saver_extension); /* Whether we need to periodically wake up and check to see if the mouse has moved. We only need to do this when not using any extensions. The reason this isn't the same as `polling_for_idleness' is that the "idleness" poll can happen (for example) 5 minutes from now, whereas the mouse-position poll should happen with low periodicity. We don't need to poll the mouse position with the XIDLE extension, but we do need to periodically wake up and query the server with that extension. For our purposes, polling /proc/interrupts is just like polling the mouse position. It has to happen on the same kind of schedule. */ Bool polling_mouse_position = (si->using_proc_interrupts || !(si->using_xidle_extension || si->using_mit_saver_extension || si->using_sgi_saver_extension) || si->using_xinput_extension); const char *why = 0; /* What caused the idle-state to change? */ if (until_idle_p) { if (polling_for_idleness) /* This causes a no-op event to be delivered to us in a while, so that we come back around through the event loop again. */ schedule_wakeup_event (si, p->timeout, p->debug_p); if (polling_mouse_position) /* Check to see if the mouse has moved, and set up a repeating timer to do so periodically (typically, every 5 seconds.) */ check_pointer_timer ((XtPointer) si, 0); } while (1) { XtAppNextEvent (si->app, &event.x_event); switch (event.x_event.xany.type) { case 0: /* our synthetic "timeout" event has been signalled */ if (until_idle_p) { Time idle; /* We may be idle; check one last time to see if the mouse has moved, just in case the idle-timer went off within the 5 second window between mouse polling. If the mouse has moved, then check_pointer_timer() will reset last_activity_time. */ if (polling_mouse_position) check_pointer_timer ((XtPointer) si, 0); #ifdef HAVE_XIDLE_EXTENSION if (si->using_xidle_extension) { /* The XIDLE extension uses the synthetic event to prod us into re-asking the server how long the user has been idle. */ if (! XGetIdleTime (si->dpy, &idle)) { fprintf (stderr, "%s: XGetIdleTime() failed.\n", blurb()); saver_exit (si, 1, 0); } } else #endif /* HAVE_XIDLE_EXTENSION */ #ifdef HAVE_MIT_SAVER_EXTENSION if (si->using_mit_saver_extension) { /* We don't need to do anything in this case - the synthetic event isn't necessary, as we get sent specific events to wake us up. In fact, this event generally shouldn't be being delivered when the MIT extension is in use. */ idle = 0; } else #endif /* HAVE_MIT_SAVER_EXTENSION */ #ifdef HAVE_SGI_SAVER_EXTENSION if (si->using_sgi_saver_extension) { /* We don't need to do anything in this case - the synthetic event isn't necessary, as we get sent specific events to wake us up. In fact, this event generally shouldn't be being delivered when the SGI extension is in use. */ idle = 0; } else #endif /* HAVE_SGI_SAVER_EXTENSION */ { /* Otherwise, no server extension is in use. The synthetic event was to tell us to wake up and see if the user is now idle. Compute the amount of idle time by comparing the `last_activity_time' to the wall clock. The l_a_t was set by calling `reset_timers()', which is called only in only two situations: when polling the mouse position has revealed the the mouse has moved (user activity) or when we have read an event (again, user activity.) */ idle = 1000 * (si->last_activity_time - time ((time_t *) 0)); } if (idle >= p->timeout) { /* Look, we've been idle long enough. We're done. */ why = "timeout"; goto DONE; } else if (si->emergency_lock_p) { /* Oops, the wall clock has jumped far into the future, so we need to lock down in a hurry! */ why = "large wall clock change"; goto DONE; } else { /* The event went off, but it turns out that the user has not yet been idle for long enough. So re-signal the event. Be economical: if we should blank after 5 minutes, and the user has been idle for 2 minutes, then set this timer to go off in 3 minutes. */ if (polling_for_idleness) schedule_wakeup_event (si, p->timeout - idle, p->debug_p); } } break; case ClientMessage: if (handle_clientmessage (si, &event.x_event, until_idle_p)) { why = "ClientMessage"; goto DONE; } break; case CreateNotify: /* A window has been created on the screen somewhere. If we're supposed to scan all windows for events, prepare this window. */ if (scanning_all_windows) { Window w = event.x_event.xcreatewindow.window; start_notice_events_timer (si, w, p->debug_p); } break; case KeyPress: case ButtonPress: /* Ignore release events so that hitting ESC at the password dialog doesn't result in the password dialog coming right back again when the fucking release key is seen! */ /* case KeyRelease:*/ /* case ButtonRelease:*/ case MotionNotify: if (p->debug_p) { Window root=0, window=0; int x=-1, y=-1; const char *type = 0; if (event.x_event.xany.type == MotionNotify) { /*type = "MotionNotify";*/ root = event.x_event.xmotion.root; window = event.x_event.xmotion.window; x = event.x_event.xmotion.x_root; y = event.x_event.xmotion.y_root; } else if (event.x_event.xany.type == KeyPress) { type = "KeyPress"; root = event.x_event.xkey.root; window = event.x_event.xkey.window; x = y = -1; } else if (event.x_event.xany.type == ButtonPress) { type = "ButtonPress"; root = event.x_event.xkey.root; window = event.x_event.xkey.window; x = event.x_event.xmotion.x_root; y = event.x_event.xmotion.y_root; } if (type) { int i; for (i = 0; i < si->nscreens; i++) if (root == RootWindowOfScreen (si->screens[i].screen)) break; fprintf (stderr,"%s: %d: %s on 0x%lx", blurb(), i, type, (unsigned long) window); /* Be careful never to do this unless in -debug mode, as this could expose characters from the unlock password. */ if (p->debug_p && event.x_event.xany.type == KeyPress) { KeySym keysym; char c = 0; XLookupString (&event.x_event.xkey, &c, 1, &keysym, 0); fprintf (stderr, " (%s%s)", (event.x_event.xkey.send_event ? "synthetic " : ""), XKeysymToString (keysym)); } if (x == -1) fprintf (stderr, "\n"); else fprintf (stderr, " at %d,%d.\n", x, y); } } /* If any widgets want to handle this event, let them. */ dispatch_event (si, &event.x_event); /* If we got a MotionNotify event, figure out what screen it was on and poll the mouse there: if the mouse hasn't moved far enough to count as "real" motion, then ignore this event. */ if (event.x_event.xany.type == MotionNotify) { int i; for (i = 0; i < si->nscreens; i++) if (event.x_event.xmotion.root == RootWindowOfScreen (si->screens[i].screen)) break; if (i < si->nscreens) { if (!pointer_moved_p (&si->screens[i], False)) continue; } } /* We got a user event. If we're waiting for the user to become active, this is it. If we're waiting until the user becomes idle, reset the timers (since now we have longer to wait.) */ if (!until_idle_p) { if (si->demoing_p && (event.x_event.xany.type == MotionNotify || event.x_event.xany.type == KeyRelease)) /* When we're demoing a single hack, mouse motion doesn't cause deactivation. Only clicks and keypresses do. */ ; else { /* If we're not demoing, then any activity causes deactivation. */ why = (event.x_event.xany.type == MotionNotify ?"mouse motion": event.x_event.xany.type == KeyPress?"keyboard activity": event.x_event.xany.type == ButtonPress ? "mouse click" : "unknown user activity"); goto DONE; } } else reset_timers (si); break; default: #ifdef HAVE_MIT_SAVER_EXTENSION if (event.x_event.type == si->mit_saver_ext_event_number) { /* This event's number is that of the MIT-SCREEN-SAVER server extension. This extension has one event number, and the event itself contains sub-codes that say what kind of event it was (an "idle" or "not-idle" event.) */ if (event.sevent.state == ScreenSaverOn) { int i = 0; if (p->verbose_p) fprintf (stderr, "%s: MIT ScreenSaverOn event received.\n", blurb()); /* Get the "real" server window(s) out of the way as soon as possible. */ for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (ssi->server_mit_saver_window && window_exists_p (si->dpy, ssi->server_mit_saver_window)) XUnmapWindow (si->dpy, ssi->server_mit_saver_window); } if (event.sevent.kind != ScreenSaverExternal) { fprintf (stderr, "%s: ScreenSaverOn event wasn't of type External!\n", blurb()); } if (until_idle_p) { why = "MIT ScreenSaverOn"; goto DONE; } } else if (event.sevent.state == ScreenSaverOff) { if (p->verbose_p) fprintf (stderr, "%s: MIT ScreenSaverOff event received.\n", blurb()); if (!until_idle_p) { why = "MIT ScreenSaverOff"; goto DONE; } } else fprintf (stderr, "%s: unknown MIT-SCREEN-SAVER event %d received!\n", blurb(), event.sevent.state); } else #endif /* HAVE_MIT_SAVER_EXTENSION */ #ifdef HAVE_SGI_SAVER_EXTENSION if (event.x_event.type == (si->sgi_saver_ext_event_number + ScreenSaverStart)) { /* The SGI SCREEN_SAVER server extension has two event numbers, and this event matches the "idle" event. */ if (p->verbose_p) fprintf (stderr, "%s: SGI ScreenSaverStart event received.\n", blurb()); if (until_idle_p) { why = "SGI ScreenSaverStart"; goto DONE; } } else if (event.x_event.type == (si->sgi_saver_ext_event_number + ScreenSaverEnd)) { /* The SGI SCREEN_SAVER server extension has two event numbers, and this event matches the "idle" event. */ if (p->verbose_p) fprintf (stderr, "%s: SGI ScreenSaverEnd event received.\n", blurb()); if (!until_idle_p) { why = "SGI ScreenSaverEnd"; goto DONE; } } else #endif /* HAVE_SGI_SAVER_EXTENSION */ #ifdef HAVE_XINPUT if ((!until_idle_p) && (si->num_xinput_devices > 0) && (event.x_event.type == si->xinput_DeviceMotionNotify || event.x_event.type == si->xinput_DeviceButtonPress)) /* Ignore DeviceButtonRelease, see ButtonRelease comment above. */ { dispatch_event (si, &event.x_event); if (si->demoing_p && event.x_event.type == si->xinput_DeviceMotionNotify) /* When we're demoing a single hack, mouse motion doesn't cause deactivation. Only clicks and keypresses do. */ ; else /* If we're not demoing, then any activity causes deactivation. */ { why = (event.x_event.type == si->xinput_DeviceMotionNotify ? "XI mouse motion" : event.x_event.type == si->xinput_DeviceButtonPress ? "XI mouse click" : "unknown XINPUT event"); goto DONE; } } else #endif /* HAVE_XINPUT */ #ifdef HAVE_RANDR if (si->using_randr_extension && (event.x_event.type == (si->randr_event_number + RRScreenChangeNotify))) { /* The Resize and Rotate extension sends an event when the size, rotation, or refresh rate of any screen has changed. */ if (p->verbose_p) { /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */ int screen = XRRRootToScreen (si->dpy, event.xrr_event.window); fprintf (stderr, "%s: %d: screen change event received\n", blurb(), screen); } # ifdef RRScreenChangeNotifyMask /* Inform Xlib that it's ok to update its data structures. */ XRRUpdateConfiguration (&event.x_event); /* Xrandr.h 1.9, 2002/09/29 */ # endif /* RRScreenChangeNotifyMask */ /* Resize the existing xscreensaver windows and cached ssi data. */ if (update_screen_layout (si)) { if (p->verbose_p) { fprintf (stderr, "%s: new layout:\n", blurb()); describe_monitor_layout (si); } resize_screensaver_window (si); } } else #endif /* HAVE_RANDR */ /* Just some random event. Let the Widgets handle it, if desired. */ dispatch_event (si, &event.x_event); } } DONE: if (p->verbose_p) { if (! why) why = "unknown reason"; fprintf (stderr, "%s: %s (%s)\n", blurb(), (until_idle_p ? "user is idle" : "user is active"), why); } /* If there's a user event on the queue, swallow it. If we're using a server extension, and the user becomes active, we get the extension event before the user event -- so the keypress or motion or whatever is still on the queue. This makes "unfade" not work, because it sees that event, and bugs out. (This problem doesn't exhibit itself without an extension, because in that case, there's only one event generated by user activity, not two.) */ if (!until_idle_p && si->locked_p) swallow_unlock_typeahead_events (si, &event.x_event); else while (XCheckMaskEvent (si->dpy, (KeyPressMask|ButtonPressMask|PointerMotionMask), &event.x_event)) ; if (si->check_pointer_timer_id) { XtRemoveTimeOut (si->check_pointer_timer_id); si->check_pointer_timer_id = 0; } if (si->timer_id) { XtRemoveTimeOut (si->timer_id); si->timer_id = 0; } if (until_idle_p && si->cycle_id) /* no cycle timer when inactive */ abort (); } /* Some crap for dealing with /proc/interrupts. On Linux systems, it's possible to see the hardware interrupt count associated with the keyboard. We can therefore use that as another method of detecting idleness. Why is it a good idea to do this? Because it lets us detect keyboard activity that is not associated with X events. For example, if the user has switched to another virtual console, it's good for xscreensaver to not be running graphics hacks on the (non-visible) X display. The common complaint that checking /proc/interrupts addresses is that the user is playing Quake on a non-X console, and the GL hacks are perceptibly slowing the game... This is tricky for a number of reasons. * First, we must be sure to only do this when running on an X server that is on the local machine (because otherwise, we'd be reacting to the wrong keyboard.) The way we do this is by noting that the $DISPLAY is pointing to display 0 on the local machine. It *could* be that display 1 is also on the local machine (e.g., two X servers, each on a different virtual-terminal) but it's also possible that screen 1 is an X terminal, using this machine as the host. So we can't take that chance. * Second, one can only access these interrupt numbers in a completely and utterly brain-damaged way. You would think that one would use an ioctl for this. But no. The ONLY way to get this information is to open the pseudo-file /proc/interrupts AS A FILE, and read the numbers out of it TEXTUALLY. Because this is Unix, and all the world's a file, and the only real data type is the short-line sequence of ASCII bytes. Now it's all well and good that the /proc/interrupts pseudo-file exists; that's a clever idea, and a useful API for things that are already textually oriented, like shell scripts, and users doing interactive debugging sessions. But to make a *C PROGRAM* open a file and parse the textual representation of integers out of it is just insane. * Third, you can't just hold the file open, and fseek() back to the beginning to get updated data! If you do that, the data never changes. And I don't want to call open() every five seconds, because I don't want to risk going to disk for any inodes. It turns out that if you dup() it early, then each copy gets fresh data, so we can get around that in this way (but for how many releases, one might wonder?) * Fourth, the format of the output of the /proc/interrupts file is undocumented, and has changed several times already! In Linux 2.0.33, even on a multiprocessor machine, it looks like this: 0: 309453991 timer 1: 4771729 keyboard but in Linux 2.2 and 2.4 kernels with MP machines, it looks like this: CPU0 CPU1 0: 1671450 1672618 IO-APIC-edge timer 1: 13037 13495 IO-APIC-edge keyboard and in Linux 2.6, it's gotten even goofier: now there are two lines labelled "i8042". One of them is the keyboard, and one of them is the PS/2 mouse -- and of course, you can't tell them apart, except by wiggling the mouse and noting which one changes: CPU0 CPU1 1: 32051 30864 IO-APIC-edge i8042 12: 476577 479913 IO-APIC-edge i8042 Joy! So how are we expected to parse that? Well, this code doesn't parse it: it saves the first line with the string "keyboard" (or "i8042") in it, and does a string-comparison to note when it has changed. If there are two "i8042" lines, we assume the first is the keyboard and the second is the mouse (doesn't matter which is which, really, as long as we don't compare them against each other.) Thanks to Nat Friedman for figuring out most of this crap. Note that if you have a serial or USB mouse, or a USB keyboard, it won't detect it. That's because there's no way to tell the difference between a serial mouse and a general serial port, and all USB devices look the same from here. It would be somewhat unfortunate to have the screensaver turn off when the modem on COM1 burped, or when a USB disk was accessed. */ #ifdef HAVE_PROC_INTERRUPTS #define PROC_INTERRUPTS "/proc/interrupts" Bool query_proc_interrupts_available (saver_info *si, const char **why) { /* We can use /proc/interrupts if $DISPLAY points to :0, and if the "/proc/interrupts" file exists and is readable. */ FILE *f; if (why) *why = 0; if (!display_is_on_console_p (si)) { if (why) *why = "not on primary console"; return False; } f = fopen (PROC_INTERRUPTS, "r"); if (!f) { if (why) *why = "does not exist"; return False; } fclose (f); return True; } static Bool proc_interrupts_activity_p (saver_info *si) { static FILE *f0 = 0; FILE *f1 = 0; int fd; static char last_kbd_line[255] = { 0, }; static char last_ptr_line[255] = { 0, }; char new_line[sizeof(last_kbd_line)]; Bool checked_kbd = False, kbd_changed = False; Bool checked_ptr = False, ptr_changed = False; int i8042_count = 0; if (!f0) { /* First time -- open the file. */ f0 = fopen (PROC_INTERRUPTS, "r"); if (!f0) { char buf[255]; sprintf(buf, "%s: error opening %s", blurb(), PROC_INTERRUPTS); perror (buf); goto FAIL; } } if (f0 == (FILE *) -1) /* means we got an error initializing. */ return False; fd = dup (fileno (f0)); if (fd < 0) { char buf[255]; sprintf(buf, "%s: could not dup() the %s fd", blurb(), PROC_INTERRUPTS); perror (buf); goto FAIL; } f1 = fdopen (fd, "r"); if (!f1) { char buf[255]; sprintf(buf, "%s: could not fdopen() the %s fd", blurb(), PROC_INTERRUPTS); perror (buf); goto FAIL; } /* Actually, I'm unclear on why this fseek() is necessary, given the timing of the dup() above, but it is. */ if (fseek (f1, 0, SEEK_SET) != 0) { char buf[255]; sprintf(buf, "%s: error rewinding %s", blurb(), PROC_INTERRUPTS); perror (buf); goto FAIL; } /* Now read through the pseudo-file until we find the "keyboard", "PS/2 mouse", or "i8042" lines. */ while (fgets (new_line, sizeof(new_line)-1, f1)) { Bool i8042_p = !!strstr (new_line, "i8042"); if (i8042_p) i8042_count++; if (strchr (new_line, ',')) { /* Ignore any line that has a comma on it: this is because a setup like this: 12: 930935 XT-PIC usb-uhci, PS/2 Mouse is really bad news. It *looks* like we can note mouse activity from that line, but really, that interrupt gets fired any time any USB device has activity! So we have to ignore any shared IRQs. */ } else if (!checked_kbd && (strstr (new_line, "keyboard") || (i8042_p && i8042_count == 1))) { /* Assume the keyboard interrupt is the line that says "keyboard", or the *first* line that says "i8042". */ kbd_changed = (*last_kbd_line && !!strcmp (new_line, last_kbd_line)); strcpy (last_kbd_line, new_line); checked_kbd = True; } else if (!checked_ptr && (strstr (new_line, "PS/2 Mouse") || (i8042_p && i8042_count == 2))) { /* Assume the mouse interrupt is the line that says "PS/2 mouse", or the *second* line that says "i8042". */ ptr_changed = (*last_ptr_line && !!strcmp (new_line, last_ptr_line)); strcpy (last_ptr_line, new_line); checked_ptr = True; } if (checked_kbd && checked_ptr) break; } if (checked_kbd || checked_ptr) { fclose (f1); if (si->prefs.debug_p && (kbd_changed || ptr_changed)) fprintf (stderr, "%s: /proc/interrupts activity: %s\n", blurb(), ((kbd_changed && ptr_changed) ? "mouse and kbd" : kbd_changed ? "kbd" : ptr_changed ? "mouse" : "ERR")); return (kbd_changed || ptr_changed); } /* If we got here, we didn't find either a "keyboard" or a "PS/2 Mouse" line in the file at all. */ fprintf (stderr, "%s: no keyboard or mouse data in %s?\n", blurb(), PROC_INTERRUPTS); FAIL: if (f1) fclose (f1); if (f0 && f0 != (FILE *) -1) fclose (f0); f0 = (FILE *) -1; return False; } #endif /* HAVE_PROC_INTERRUPTS */ /* This timer goes off every few minutes, whether the user is idle or not, to try and clean up anything that has gone wrong. It calls disable_builtin_screensaver() so that if xset has been used, or some other program (like xlock) has messed with the XSetScreenSaver() settings, they will be set back to sensible values (if a server extension is in use, messing with xlock can cause xscreensaver to never get a wakeup event, and could cause monitor power-saving to occur, and all manner of heinousness.) If the screen is currently blanked, it raises the window, in case some other window has been mapped on top of it. If the screen is currently blanked, and there is no hack running, it clears the window, in case there is an error message printed on it (we don't want the error message to burn in.) */ static void watchdog_timer (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; saver_preferences *p = &si->prefs; disable_builtin_screensaver (si, False); /* If the DPMS settings on the server have changed, change them back to what ~/.xscreensaver says they should be. */ sync_server_dpms_settings (si->dpy, (p->dpms_enabled_p && p->mode != DONT_BLANK), p->dpms_standby / 1000, p->dpms_suspend / 1000, p->dpms_off / 1000, False); if (si->screen_blanked_p) { Bool running_p = screenhack_running_p (si); if (si->dbox_up_p) { if (si->prefs.debug_p) fprintf (stderr, "%s: dialog box is up: not raising screen.\n", blurb()); } else { if (si->prefs.debug_p) fprintf (stderr, "%s: watchdog timer raising %sscreen.\n", blurb(), (running_p ? "" : "and clearing ")); raise_window (si, True, True, running_p); } if (screenhack_running_p (si) && !monitor_powered_on_p (si)) { int i; if (si->prefs.verbose_p) fprintf (stderr, "%s: X says monitor has powered down; " "killing running hacks.\n", blurb()); for (i = 0; i < si->nscreens; i++) kill_screenhack (&si->screens[i]); } /* Re-schedule this timer. The watchdog timer defaults to a bit less than the hack cycle period, but is never longer than one hour. */ si->watchdog_id = 0; reset_watchdog_timer (si, True); } } void reset_watchdog_timer (saver_info *si, Bool on_p) { saver_preferences *p = &si->prefs; if (si->watchdog_id) { XtRemoveTimeOut (si->watchdog_id); si->watchdog_id = 0; } if (on_p && p->watchdog_timeout) { si->watchdog_id = XtAppAddTimeOut (si->app, p->watchdog_timeout, watchdog_timer, (XtPointer) si); if (p->debug_p) fprintf (stderr, "%s: restarting watchdog_timer (%ld, %ld)\n", blurb(), p->watchdog_timeout, si->watchdog_id); } } /* It's possible that a race condition could have led to the saver window being unexpectedly still mapped. This can happen like so: - screen is blanked - hack is launched - that hack tries to grab a screen image (it does this by first unmapping the saver window, then remapping it.) - hack unmaps window - hack waits - user becomes active - hack re-maps window (*) - driver kills subprocess - driver unmaps window (**) The race is that (*) might have been sent to the server before the client process was killed, but, due to scheduling randomness, might not have been received by the server until after (**). In other words, (*) and (**) might happen out of order, meaning the driver will unmap the window, and then after that, the recently-dead client will re-map it. This leaves the user locked out (it looks like a desktop, but it's not!) To avoid this: after un-blanking the screen, we launch a timer that wakes up once a second for ten seconds, and makes damned sure that the window is still unmapped. */ void de_race_timer (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; saver_preferences *p = &si->prefs; int secs = 1; if (id == 0) /* if id is 0, this is the initialization call. */ { si->de_race_ticks = 10; if (p->verbose_p) fprintf (stderr, "%s: starting de-race timer (%d seconds.)\n", blurb(), si->de_race_ticks); } else { int i; XSync (si->dpy, False); for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; Window w = ssi->screensaver_window; XWindowAttributes xgwa; XGetWindowAttributes (si->dpy, w, &xgwa); if (xgwa.map_state != IsUnmapped) { if (p->verbose_p) fprintf (stderr, "%s: %d: client race! emergency unmap 0x%lx.\n", blurb(), i, (unsigned long) w); XUnmapWindow (si->dpy, w); } else if (p->debug_p) fprintf (stderr, "%s: %d: (de-race of 0x%lx is cool.)\n", blurb(), i, (unsigned long) w); } XSync (si->dpy, False); si->de_race_ticks--; } if (id && *id == si->de_race_id) si->de_race_id = 0; if (si->de_race_id) abort(); if (si->de_race_ticks <= 0) { si->de_race_id = 0; if (p->verbose_p) fprintf (stderr, "%s: de-race completed.\n", blurb()); } else { si->de_race_id = XtAppAddTimeOut (si->app, secs * 1000, de_race_timer, closure); } } xscreensaver-5.15/driver/types.h000066400000000000000000000407241164314150500167730ustar00rootroot00000000000000/* xscreensaver, Copyright (c) 1993-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifndef __XSCREENSAVER_TYPES_H__ #define __XSCREENSAVER_TYPES_H__ typedef struct saver_info saver_info; typedef enum { ul_read, /* reading input or ready to do so */ ul_success, /* auth success, unlock */ ul_fail, /* auth fail */ ul_cancel, /* user cancelled auth (pw_cancel or pw_null) */ ul_time, /* timed out */ ul_finished /* user pressed enter */ } unlock_state; typedef struct screenhack screenhack; struct screenhack { Bool enabled_p; char *visual; char *name; char *command; }; typedef enum { RANDOM_HACKS, ONE_HACK, BLANK_ONLY, DONT_BLANK, RANDOM_HACKS_SAME } saver_mode; typedef enum { TEXT_DATE, TEXT_LITERAL, TEXT_FILE, TEXT_PROGRAM, TEXT_URL } text_mode; struct auth_message; struct auth_response; typedef int (*auth_conv_cb_t) ( int num_msg, const struct auth_message *msg, struct auth_response **resp, saver_info *si); typedef struct saver_preferences saver_preferences; typedef struct saver_screen_info saver_screen_info; typedef struct passwd_dialog_data passwd_dialog_data; typedef struct splash_dialog_data splash_dialog_data; typedef struct _monitor monitor; /* This structure holds all the user-specified parameters, read from the command line, the resource database, or entered through a dialog box. */ struct saver_preferences { XrmDatabase db; /* The resource database into which the init file is merged, and out of which the preferences are parsed. */ time_t init_file_date; /* The date (from stat()) of the .xscreensaver file the last time this process read or wrote it. */ Bool verbose_p; /* whether to print out lots of status info */ Bool timestamp_p; /* whether to mark messages with a timestamp */ Bool capture_stderr_p; /* whether to redirect stdout/stderr */ Bool ignore_uninstalled_p; /* whether to avoid displaying or complaining about hacks that are not on $PATH */ Bool debug_p; /* pay no mind to the man behind the curtain */ Bool xsync_p; /* whether XSynchronize has been called */ Bool lock_p; /* whether to lock as well as save */ Bool fade_p; /* whether to fade to black, if possible */ Bool unfade_p; /* whether to fade from black, if possible */ Time fade_seconds; /* how long that should take */ int fade_ticks; /* how many ticks should be used */ Bool splash_p; /* whether to do a splash screen at startup */ Bool install_cmap_p; /* whether we should use our own colormap when using the screen's default visual. */ # ifdef QUAD_MODE Bool quad_p; /* whether to run four savers per monitor */ # endif screenhack **screenhacks; /* the programs to run */ int screenhacks_count; saver_mode mode; /* hack-selection mode */ int selected_hack; /* in one_hack mode, this is the one */ int nice_inferior; /* nice value for subprocs */ int inferior_memory_limit; /* setrlimit(LIMIT_AS) value for subprocs */ Time initial_delay; /* how long to sleep after launch */ Time splash_duration; /* how long the splash screen stays up */ Time timeout; /* how much idle time before activation */ Time lock_timeout; /* how long after activation locking starts */ Time cycle; /* how long each hack should run */ Time passwd_timeout; /* how much time before pw dialog goes down */ Time pointer_timeout; /* how often to check mouse position */ Time notice_events_timeout; /* how long after window creation to select */ Time watchdog_timeout; /* how often to re-raise and re-blank screen */ int pointer_hysteresis; /* mouse motions less than N/sec are ignored */ Bool dpms_enabled_p; /* Whether to power down the monitor */ Bool dpms_quickoff_p; /* Whether to power down monitor immediately in "Blank Only" mode */ Time dpms_standby; /* how long until monitor goes black */ Time dpms_suspend; /* how long until monitor power-saves */ Time dpms_off; /* how long until monitor powers down */ Bool grab_desktop_p; /* These are not used by "xscreensaver" */ Bool grab_video_p; /* itself: they are used by the external */ Bool random_image_p; /* "xscreensaver-getimage" program, and set */ char *image_directory; /* by the "xscreensaver-demo" configurator. */ text_mode tmode; /* How we generate text to display. */ char *text_literal; /* used when tmode is TEXT_LITERAL. */ char *text_file; /* used when tmode is TEXT_FILE. */ char *text_program; /* used when tmode is TEXT_PROGRAM. */ char *text_url; /* used when tmode is TEXT_URL. */ Bool use_xidle_extension; /* which extension to use, if possible */ Bool use_mit_saver_extension; Bool use_sgi_saver_extension; Bool use_proc_interrupts; Bool use_xinput_extension; Bool getviewport_full_of_lies_p; /* XFree86 bug #421 */ char *shell; /* where to find /bin/sh */ char *demo_command; /* How to enter demo mode. */ char *prefs_command; /* How to edit preferences. */ char *help_url; /* Where the help document resides. */ char *load_url_command; /* How one loads URLs. */ char *new_login_command; /* Command for the "New Login" button. */ }; /* This structure holds all the data that applies to the program as a whole, or to the non-screen-specific parts of the display connection. The saver_preferences structure (prefs.h) holds all the user-specified parameters, read from the command line, the resource database, or entered through a dialog box. */ struct saver_info { char *version; saver_preferences prefs; int nscreens; int ssi_count; saver_screen_info *screens; saver_screen_info *default_screen; /* ...on which dialogs will appear. */ monitor **monitor_layout; /* private to screens.c */ Visual **best_gl_visuals; /* visuals for GL hacks on screen N */ /* ======================================================================= global connection info ======================================================================= */ XtAppContext app; Display *dpy; /* ======================================================================= server extension info ======================================================================= */ Bool using_xidle_extension; /* which extension is being used. */ Bool using_mit_saver_extension; /* Note that `p->use_*' is the *request*, */ Bool using_sgi_saver_extension; /* and `si->using_*' is the *reality*. */ Bool using_proc_interrupts; # ifdef HAVE_MIT_SAVER_EXTENSION int mit_saver_ext_event_number; int mit_saver_ext_error_number; # endif # ifdef HAVE_SGI_SAVER_EXTENSION int sgi_saver_ext_event_number; int sgi_saver_ext_error_number; # endif # ifdef HAVE_RANDR int randr_event_number; int randr_error_number; Bool using_randr_extension; # endif Bool using_xinput_extension; /* Note that `p->use_*' is the *request*, */ /* and `si->using_*' is the *reality*. */ #ifdef HAVE_XINPUT int xinput_ext_event_number; /* may not be used */ int xinput_ext_error_number; int xinput_DeviceButtonPress; /* Extension device event codes. */ int xinput_DeviceButtonRelease; /* Assigned by server at runtime */ int xinput_DeviceMotionNotify; struct xinput_dev_info *xinput_devices; int num_xinput_devices; # endif /* ======================================================================= blanking ======================================================================= */ Bool screen_blanked_p; /* Whether the saver is currently active. */ Window mouse_grab_window; /* Window holding our mouse grab */ Window keyboard_grab_window; /* Window holding our keyboard grab */ int mouse_grab_screen; /* The screen number the mouse grab is on */ int keyboard_grab_screen; /* The screen number the keyboard grab is on */ Bool fading_possible_p; /* Whether fading to/from black is possible. */ Bool throttled_p; /* Whether we should temporarily just blank the screen, not run hacks. (Deprecated: users should use "xset dpms force off" instead.) */ time_t blank_time; /* The time at which the screen was blanked (if currently blanked) or unblanked (if not blanked.) */ /* ======================================================================= locking and runtime privileges ======================================================================= */ Bool locked_p; /* Whether the screen is currently locked. */ Bool dbox_up_p; /* Whether the demo-mode or passwd dialogs are currently visible */ Bool locking_disabled_p; /* Sometimes locking is impossible. */ char *nolock_reason; /* This is why. */ char *orig_uid; /* What uid/gid we had at startup, before discarding privileges. */ char *uid_message; /* Any diagnostics from our attempt to discard privileges (printed only in -verbose mode.) */ Bool dangerous_uid_p; /* Set to true if we're running as a user id which is known to not be a normal, non- privileged user. */ Window passwd_dialog; /* The password dialog, if it's up. */ passwd_dialog_data *pw_data; /* Other info necessary to draw it. */ int unlock_failures; /* Counts failed login attempts while the screen is locked. */ char *unlock_typeahead; /* If the screen is locked, and the user types a character, we assume that it is the first character of the password. It's stored here for the password dialog to use to populate itself. */ char *user; /* The user whose session is locked. */ char *cached_passwd; /* Cached password, used to avoid multiple prompts for password-only auth mechanisms.*/ unlock_state unlock_state; auth_conv_cb_t unlock_cb; /* The function used to prompt for creds. */ void (*auth_finished_cb) (saver_info *si); /* Called when authentication has finished, regardless of success or failure. May be NULL. */ /* ======================================================================= demoing ======================================================================= */ Bool demoing_p; /* Whether we are demoing a single hack (without UI.) */ Window splash_dialog; /* The splash dialog, if its up. */ splash_dialog_data *sp_data; /* Other info necessary to draw it. */ /* ======================================================================= timers ======================================================================= */ XtIntervalId lock_id; /* Timer to implement `prefs.lock_timeout' */ XtIntervalId cycle_id; /* Timer to implement `prefs.cycle' */ XtIntervalId timer_id; /* Timer to implement `prefs.timeout' */ XtIntervalId watchdog_id; /* Timer to implement `prefs.watchdog */ XtIntervalId check_pointer_timer_id; /* `prefs.pointer_timeout' */ XtIntervalId de_race_id; /* Timer to make sure screen un-blanks */ int de_race_ticks; time_t last_activity_time; /* Used only when no server exts. */ time_t last_wall_clock_time; /* Used to detect laptop suspend. */ saver_screen_info *last_activity_screen; Bool emergency_lock_p; /* Set when the wall clock has jumped (presumably due to laptop suspend) and we need to lock down right away instead of waiting for the lock timer to go off. */ /* ======================================================================= remote control ======================================================================= */ int selection_mode; /* Set to -1 if the NEXT ClientMessage has just been received; set to -2 if PREV has just been received; set to N if SELECT or DEMO N has been received. (This is kind of nasty.) */ /* ======================================================================= subprocs ======================================================================= */ XtIntervalId stderr_popup_timer; }; /* This structure holds all the data that applies to the screen-specific parts of the display connection; if the display has multiple screens, there will be one of these for each screen. */ struct saver_screen_info { saver_info *global; int number; /* The internal ordinal of this screen, counting Xinerama rectangles as separate screens. */ int real_screen_number; /* The number of the underlying X screen on which this rectangle lies. */ Screen *screen; /* The X screen in question. */ int x, y, width, height; /* The size and position of this rectangle on its underlying X screen. */ Bool real_screen_p; /* This will be true of exactly one ssi per X screen. */ Widget toplevel_shell; /* ======================================================================= blanking ======================================================================= */ Window screensaver_window; /* The window that will impersonate the root, when the screensaver activates. Note that the window stored here may change, as we destroy and recreate it on different visuals. */ Colormap cmap; /* The colormap that goes with the window. */ Bool install_cmap_p; /* Whether this screen should have its own colormap installed, for whichever of several reasons. This is definitive (even a false value here overrides prefs->install_cmap_p.) */ Visual *current_visual; /* The visual of the window. */ int current_depth; /* How deep the visual (and the window) are. */ Visual *default_visual; /* visual to use when none other specified */ Window real_vroot; /* The original virtual-root window. */ Window real_vroot_value; /* What was in the __SWM_VROOT property. */ Cursor cursor; /* A blank cursor that goes with the real root window. */ unsigned long black_pixel; /* Black, allocated from `cmap'. */ int blank_vp_x, blank_vp_y; /* Where the virtual-scrolling viewport was when the screen went blank. We need to prevent the X server from letting the mouse bump the edges to scroll while the screen is locked, so we reset to this when it has moved, and the lock dialog is up... */ # ifdef HAVE_MIT_SAVER_EXTENSION Window server_mit_saver_window; # endif /* ======================================================================= demoing ======================================================================= */ Colormap demo_cmap; /* The colormap that goes with the dialogs: this might be the same as `cmap' so care must be taken not to free it while it's still in use. */ /* ======================================================================= timers ======================================================================= */ int poll_mouse_last_root_x; /* Used only when no server exts. */ int poll_mouse_last_root_y; Window poll_mouse_last_child; unsigned int poll_mouse_last_mask; time_t poll_mouse_last_time; /* ======================================================================= subprocs ======================================================================= */ int current_hack; /* Index into `prefs.screenhacks' */ pid_t pid; int stderr_text_x; int stderr_text_y; int stderr_line_height; XFontStruct *stderr_font; GC stderr_gc; Window stderr_overlay_window; /* Used if the server has overlay planes */ Colormap stderr_cmap; }; #endif /* __XSCREENSAVER_TYPES_H__ */ xscreensaver-5.15/driver/vms-getpwnam.c000066400000000000000000000067451164314150500202540ustar00rootroot00000000000000/* * getpwnam(name) - retrieves a UAF entry * * Author: Patrick L. Mahan * Location: TGV, Inc * Date: 15-Nov-1991 * * Purpose: Provides emulation for the UNIX getpwname routine. * * Modification History * * Date | Who | Version | Reason * ------------+-----------+---------------+--------------------------- * 15-Nov-1991 | PLM | 1.0 | First Write */ #define PASSWDROUTINES #include #include #include #include #include #include #include "vms-pwd.h" struct uic { unsigned short uid; unsigned short gid; }; #define TEST(ptr, str) { if (ptr == NULL) { \ fprintf(stderr, "getpwnam: memory allocation failure for \"%s\"\n", \ str); \ return ((struct passwd *)(NULL)); \ } } struct passwd *getpwnam(name) char *name; { int istatus; int UserNameLen; int UserOwnerLen; int UserDeviceLen; int UserDirLen; static char UserName[13]; static char UserOwner[32]; static char UserDevice[32]; static char UserDir[64]; char *cptr, *sptr; unsigned long int UserPwd[2]; unsigned short int UserSalt; unsigned long int UserEncrypt; struct uic UicValue; struct passwd *entry; struct dsc$descriptor_s VMSNAME = {strlen(name), DSC$K_DTYPE_T, DSC$K_CLASS_S, name}; struct itmlist3 { unsigned short int length; unsigned short int item; unsigned long int addr; unsigned long int retaddr; } ItemList[] = { {12, UAI$_USERNAME, (unsigned long)&UserName, (unsigned long)&UserNameLen}, {8, UAI$_PWD, (unsigned long)&UserPwd, 0}, {4, UAI$_UIC, (unsigned long)&UicValue, 0}, {32, UAI$_OWNER, (unsigned long)&UserOwner, (unsigned long)&UserOwnerLen}, {32, UAI$_DEFDEV, (unsigned long)&UserDevice, (unsigned long)&UserDeviceLen}, {64, UAI$_DEFDIR, (unsigned long)&UserDir, (unsigned long)&UserDirLen}, {2, UAI$_SALT, (unsigned long)&UserSalt, 0}, {4, UAI$_ENCRYPT, (unsigned long)&UserEncrypt, 0}, {0, 0, 0, 0} }; UserNameLen = 0; istatus = sys$getuai (0, 0, &VMSNAME, &ItemList, 0, 0, 0); if (!(istatus & 1)) { fprintf (stderr, "getpwnam: unable to retrieve passwd entry for %s\n", name); fprintf (stderr, "getpwnam: vms error number is 0x%x\n", istatus); return ((struct passwd *)NULL); } entry = (struct passwd *) calloc (1, sizeof(struct passwd)); TEST(entry, "PASSWD_ENTRY"); entry->pw_uid = UicValue.uid; entry->pw_gid = UicValue.gid; entry->pw_salt = UserSalt; entry->pw_encrypt = UserEncrypt; sptr = UserName; cptr = calloc (UserNameLen+1, sizeof(char)); TEST(cptr, "USERNAME"); strncpy (cptr, sptr, UserNameLen); cptr[UserNameLen] = '\0'; entry->pw_name = cptr; cptr = calloc(8, sizeof(char)); TEST(cptr, "PASSWORD"); memcpy(cptr, UserPwd, 8); entry->pw_passwd = cptr; sptr = UserOwner; sptr++; cptr = calloc ((int)UserOwner[0]+1, sizeof(char)); TEST(cptr, "FULLNAME"); strncpy (cptr, sptr, (int)UserOwner[0]); cptr[(int)UserOwner[0]] = '\0'; entry->pw_gecos = cptr; cptr = calloc ((int)UserDevice[0]+(int)UserDir[0]+1, sizeof(char)); TEST(cptr, "HOME"); sptr = UserDevice; sptr++; strncpy (cptr, sptr, (int)UserDevice[0]); sptr = UserDir; sptr++; strncat (cptr, sptr, (int)UserDir[0]); cptr[(int)UserDevice[0]+(int)UserDir[0]] = '\0'; entry->pw_dir = cptr; cptr = calloc (strlen("SYS$SYSTEM:LOGINOUT.EXE")+1, sizeof(char)); TEST(cptr,"SHELL"); strcpy (cptr, "SYS$SYSTEM:LOGINOUT.EXE"); entry->pw_shell = cptr; return (entry); } xscreensaver-5.15/driver/vms-hpwd.c000066400000000000000000000040751164314150500173660ustar00rootroot00000000000000/* * VAX/VMS Password hashing routines: * * uses the System Service SYS$HASH_PASSWORD * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * */ #include #include #include #include /* * Hashing routine */ hash_vms_password(output_buf,input_buf,input_length,username,encryption_type,salt) char *output_buf; char *input_buf; int input_length; char *username; int encryption_type; unsigned short salt; { struct dsc$descriptor_s password; struct dsc$descriptor_s user; /* * Check the VMS Version. If this is V5.4 or later, then * we can use the new system service SYS$HASH_PASSWORD. Else * fail and return garbage. */ static char VMS_Version[32]; struct { unsigned short int Size; unsigned short int Code; char *Buffer; unsigned short int *Resultant_Size; } Item_List[2]={32, SYI$_VERSION, VMS_Version, 0, 0, 0}; struct {int Size; char *Ptr;} Descr1; /* * Get the information */ sys$getsyiw(0,0,0,Item_List,0,0,0); /* * Call the old routine if this isn't V5.4 or later... */ #ifndef __DECC if ((VMS_Version[1] < '5') || ((VMS_Version[1] == '5') && (VMS_Version[3] < '4'))) { printf("Unsupported OS version\n"); return(1); } #endif /* !__DECC */ /* * Call the SYS$HASH_PASSWORD system service... */ password.dsc$b_dtype = DSC$K_DTYPE_T; password.dsc$b_class = DSC$K_CLASS_S; password.dsc$w_length = input_length; password.dsc$a_pointer = input_buf; user.dsc$b_dtype = DSC$K_DTYPE_T; user.dsc$b_class = DSC$K_CLASS_S; user.dsc$w_length = strlen(username); user.dsc$a_pointer = username; sys$hash_password (&password, encryption_type, salt, &user, output_buf); } xscreensaver-5.15/driver/vms-pwd.h000066400000000000000000000013731164314150500172210ustar00rootroot00000000000000/* @(#)pwd.h 1.7 89/08/24 SMI; from S5R2 1.1 */ #ifndef __pwd_h #define __pwd_h #ifdef vax11c #include #else #include #endif /* vax11c */ #ifdef PASSWDROUTINES #define EXTERN #else #define EXTERN extern #endif /* PASSWDROUTINES */ struct passwd { char *pw_name; char *pw_passwd; int pw_uid; int pw_gid; short pw_salt; int pw_encrypt; char *pw_age; char *pw_comment; char *pw_gecos; char *pw_dir; char *pw_shell; }; #ifndef _POSIX_SOURCE extern struct passwd *getpwent(); struct comment { char *c_dept; char *c_name; char *c_acct; char *c_bin; }; #endif EXTERN struct passwd *getpwuid(/* uid_t uid */); EXTERN struct passwd *getpwnam(/* char *name */); #endif /* !__pwd_h */ xscreensaver-5.15/driver/vms-validate.c000066400000000000000000000032401164314150500202060ustar00rootroot00000000000000/* * validate a password for a user * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* * Includes */ #include #include #include #include "vms-pwd.h" int hash_vms_password(char *output_buf,char *input_buf,int input_length, char *username,int encryption_type,unsigned short salt); /* * * Validate a VMS UserName/Password pair. * */ int validate_user(name,password) char *name; char *password; { char password_buf[64]; char username_buf[31]; char encrypt_buf[8]; register int i; register char *cp,*cp1; struct passwd *user_entry; /* * Get the users UAF entry */ user_entry = getpwnam(name); /* * If user_entry == NULL then we got a bad error * return -1 to indicate a bad error */ if (user_entry == NULL) return (-1); /* * Uppercase the password */ cp = password; cp1 = password_buf; while (*cp) if (islower(*cp)) *cp1++ = toupper(*cp++); else *cp1++ = *cp++; /* * Get the length of the password */ i = strlen(password); /* * Encrypt the password */ hash_vms_password(encrypt_buf,password_buf,i,user_entry->pw_name, user_entry->pw_encrypt, user_entry->pw_salt); if (memcmp(encrypt_buf,user_entry->pw_passwd,8) == 0) return(1); else return(0); } xscreensaver-5.15/driver/vms_axp.opt000066400000000000000000000002401164314150500176440ustar00rootroot00000000000000[-.UTILS]UTILS.OLB_AXP/LIB SYS$SHARE:DECW$XMLIBSHR.EXE/SHARE SYS$SHARE:DECW$XMULIBSHR.EXE/SHARE SYS$SHARE:DECW$XTSHR.EXE/SHARE SYS$SHARE:DECW$XLIBSHR.EXE/SHARE xscreensaver-5.15/driver/vms_axp_12.opt000066400000000000000000000002511164314150500201500ustar00rootroot00000000000000[-.UTILS]UTILS.OLB_AXP/LIB SYS$SHARE:DECW$XMLIBSHR12.EXE/SHARE SYS$SHARE:DECW$XMULIBSHRR5.EXE/SHARE SYS$SHARE:DECW$XTLIBSHRR5.EXE/SHARE SYS$SHARE:DECW$XLIBSHR.EXE/SHARE xscreensaver-5.15/driver/vms_decc.opt000066400000000000000000000002411164314150500177530ustar00rootroot00000000000000[-.UTILS]UTILS.OLB_DECC/LIB SYS$SHARE:DECW$XMLIBSHR.EXE/SHARE SYS$SHARE:DECW$XMULIBSHR.EXE/SHARE SYS$SHARE:DECW$XTSHR.EXE/SHARE SYS$SHARE:DECW$XLIBSHR.EXE/SHARE xscreensaver-5.15/driver/vms_decc_12.opt000066400000000000000000000002521164314150500202570ustar00rootroot00000000000000[-.UTILS]UTILS.OLB_DECC/LIB SYS$SHARE:DECW$XMLIBSHR12.EXE/SHARE SYS$SHARE:DECW$XMULIBSHRR5.EXE/SHARE SYS$SHARE:DECW$XTLIBSHRR5.EXE/SHARE SYS$SHARE:DECW$XLIBSHR.EXE/SHARE xscreensaver-5.15/driver/windows.c000066400000000000000000001621741164314150500173200ustar00rootroot00000000000000/* windows.c --- turning the screen black; dealing with visuals, virtual roots. * xscreensaver, Copyright (c) 1991-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef VMS # include /* for getpid() */ # include "vms-gtod.h" /* for gettimeofday() */ #endif /* VMS */ #ifndef VMS # include /* for getpwuid() */ #else /* VMS */ # include "vms-pwd.h" #endif /* VMS */ #ifdef HAVE_UNAME # include /* for uname() */ #endif /* HAVE_UNAME */ #include /* #include / * for CARD32 */ #include #include /* for XSetClassHint() */ #include #include /* for time() */ #include /* for the signal names */ #include #include /* You might think that to store an array of 32-bit quantities onto a server-side property, you would pass an array of 32-bit data quantities into XChangeProperty(). You would be wrong. You have to use an array of longs, even if long is 64 bits (using 32 of each 64.) */ typedef long PROP32; #ifdef HAVE_MIT_SAVER_EXTENSION # include #endif /* HAVE_MIT_SAVER_EXTENSION */ #ifdef HAVE_XF86VMODE # include #endif /* HAVE_XF86VMODE */ #ifdef HAVE_XINERAMA # include #endif /* HAVE_XINERAMA */ /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer #define XtAppContext void* #define XrmDatabase void* #define XtIntervalId void* #define XtPointer void* #define Widget void* #include "xscreensaver.h" #include "visual.h" #include "fade.h" extern int kill (pid_t, int); /* signal() is in sys/signal.h... */ Atom XA_VROOT, XA_XSETROOT_ID, XA_ESETROOT_PMAP_ID, XA_XROOTPMAP_ID; Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID; Atom XA_SCREENSAVER_STATUS; extern saver_info *global_si_kludge; /* I hate C so much... */ static void maybe_transfer_grabs (saver_screen_info *ssi, Window old_w, Window new_w, int new_screen); #define ALL_POINTER_EVENTS \ (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \ LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \ Button1MotionMask | Button2MotionMask | Button3MotionMask | \ Button4MotionMask | Button5MotionMask | ButtonMotionMask) static const char * grab_string(int status) { switch (status) { case GrabSuccess: return "GrabSuccess"; case AlreadyGrabbed: return "AlreadyGrabbed"; case GrabInvalidTime: return "GrabInvalidTime"; case GrabNotViewable: return "GrabNotViewable"; case GrabFrozen: return "GrabFrozen"; default: { static char foo[255]; sprintf(foo, "unknown status: %d", status); return foo; } } } static int grab_kbd(saver_info *si, Window w, int screen_no) { saver_preferences *p = &si->prefs; int status = XGrabKeyboard (si->dpy, w, True, /* I don't really understand Sync vs Async, but these seem to work... */ GrabModeSync, GrabModeAsync, CurrentTime); if (status == GrabSuccess) { si->keyboard_grab_window = w; si->keyboard_grab_screen = screen_no; } if (p->verbose_p) fprintf(stderr, "%s: %d: grabbing keyboard on 0x%lx... %s.\n", blurb(), screen_no, (unsigned long) w, grab_string(status)); return status; } static int grab_mouse (saver_info *si, Window w, Cursor cursor, int screen_no) { saver_preferences *p = &si->prefs; int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS, GrabModeAsync, GrabModeAsync, w, cursor, CurrentTime); if (status == GrabSuccess) { si->mouse_grab_window = w; si->mouse_grab_screen = screen_no; } if (p->verbose_p) fprintf(stderr, "%s: %d: grabbing mouse on 0x%lx... %s.\n", blurb(), screen_no, (unsigned long) w, grab_string(status)); return status; } static void ungrab_kbd(saver_info *si) { saver_preferences *p = &si->prefs; XUngrabKeyboard(si->dpy, CurrentTime); if (p->verbose_p) fprintf(stderr, "%s: %d: ungrabbing keyboard (was 0x%lx).\n", blurb(), si->keyboard_grab_screen, (unsigned long) si->keyboard_grab_window); si->keyboard_grab_window = 0; } static void ungrab_mouse(saver_info *si) { saver_preferences *p = &si->prefs; XUngrabPointer(si->dpy, CurrentTime); if (p->verbose_p) fprintf(stderr, "%s: %d: ungrabbing mouse (was 0x%lx).\n", blurb(), si->mouse_grab_screen, (unsigned long) si->mouse_grab_window); si->mouse_grab_window = 0; } /* Apparently there is this program called "rdesktop" which is a windows terminal server client for Unix. It would seem that this program holds the keyboard GRABBED the whole time it has focus! This is, of course, completely idiotic: the whole point of grabbing is to get events when you do *not* have focus, so grabbing *only when* you have focus is completely redundant -- unless your goal is to make xscreensaver not able to ever lock the screen when your program is running. If xscreensaver blanks while rdesktop still has a keyboard grab, then when we try to prompt for the password, we won't get the characters: they'll be typed into rdesktop. Perhaps rdesktop will release its keyboard grab if it loses focus? What the hell, let's give it a try. If we fail to grab the keyboard four times in a row, we forcibly set focus to "None" and try four more times. (We don't touch focus unless we're already having a hard time getting a grab.) */ static void nuke_focus (saver_info *si, int screen_no) { saver_preferences *p = &si->prefs; Window focus = 0; int rev = 0; XGetInputFocus (si->dpy, &focus, &rev); if (p->verbose_p) { char w[255], r[255]; if (focus == PointerRoot) strcpy (w, "PointerRoot"); else if (focus == None) strcpy (w, "None"); else sprintf (w, "0x%lx", (unsigned long) focus); if (rev == RevertToParent) strcpy (r, "RevertToParent"); else if (rev == RevertToPointerRoot) strcpy (r, "RevertToPointerRoot"); else if (rev == RevertToNone) strcpy (r, "RevertToNone"); else sprintf (r, "0x%x", rev); fprintf (stderr, "%s: %d: removing focus from %s / %s.\n", blurb(), screen_no, w, r); } XSetInputFocus (si->dpy, None, RevertToNone, CurrentTime); XSync (si->dpy, False); } static void ungrab_keyboard_and_mouse (saver_info *si) { ungrab_mouse (si); ungrab_kbd (si); } static Bool grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor, int screen_no) { Status mstatus = 0, kstatus = 0; int i; int retries = 4; Bool focus_fuckus = False; AGAIN: for (i = 0; i < retries; i++) { XSync (si->dpy, False); kstatus = grab_kbd (si, window, screen_no); if (kstatus == GrabSuccess) break; /* else, wait a second and try to grab again. */ sleep (1); } if (kstatus != GrabSuccess) { fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n", blurb(), grab_string(kstatus)); if (! focus_fuckus) { focus_fuckus = True; nuke_focus (si, screen_no); goto AGAIN; } } for (i = 0; i < retries; i++) { XSync (si->dpy, False); mstatus = grab_mouse (si, window, cursor, screen_no); if (mstatus == GrabSuccess) break; /* else, wait a second and try to grab again. */ sleep (1); } if (mstatus != GrabSuccess) fprintf (stderr, "%s: couldn't grab pointer! (%s)\n", blurb(), grab_string(mstatus)); /* When should we allow blanking to proceed? The current theory is that a keyboard grab is mandatory; a mouse grab is optional. - If we don't have a keyboard grab, then we won't be able to read a password to unlock, so the kbd grab is mandatory. (We can't conditionalize this on locked_p, because someone might run "xscreensaver-command -lock" at any time.) - If we don't have a mouse grab, then we might not see mouse clicks as a signal to unblank -- but we will still see kbd activity, so that's not a disaster. It has been suggested that we should allow blanking if locking is disabled, and we have a mouse grab but no keyboard grab (that is: kstatus != GrabSuccess && mstatus == GrabSuccess && si->locking_disabled_p) That would allow screen blanking (but not locking) while the gdm login screen had the keyboard grabbed, but one would have to use the mouse to unblank. Keyboard characters would go to the gdm login field without unblanking. I have not made this change because I'm not completely convinced it is a safe thing to do. */ if (kstatus != GrabSuccess) /* Do not blank without a kbd grab. */ { /* If we didn't get both grabs, release the one we did get. */ ungrab_keyboard_and_mouse (si); return False; } return True; /* Grab is good, go ahead and blank. */ } int move_mouse_grab (saver_info *si, Window to, Cursor cursor, int to_screen_no) { Window old = si->mouse_grab_window; if (old == 0) return grab_mouse (si, to, cursor, to_screen_no); else { saver_preferences *p = &si->prefs; int status; XSync (si->dpy, False); XGrabServer (si->dpy); /* ############ DANGER! */ XSync (si->dpy, False); if (p->verbose_p) fprintf(stderr, "%s: grabbing server...\n", blurb()); ungrab_mouse (si); status = grab_mouse (si, to, cursor, to_screen_no); if (status != GrabSuccess) /* Augh! */ { sleep (1); /* Note dramatic evil of sleeping with server grabbed. */ XSync (si->dpy, False); status = grab_mouse (si, to, cursor, to_screen_no); } if (status != GrabSuccess) /* Augh! Try to get the old one back... */ grab_mouse (si, old, cursor, to_screen_no); XUngrabServer (si->dpy); XSync (si->dpy, False); /* ###### (danger over) */ if (p->verbose_p) fprintf(stderr, "%s: ungrabbing server.\n", blurb()); return status; } } /* Prints an error message to stderr and returns True if there is another xscreensaver running already. Silently returns False otherwise. */ Bool ensure_no_screensaver_running (Display *dpy, Screen *screen) { Bool status = 0; int i; Window root = RootWindowOfScreen (screen); Window root2, parent, *kids; unsigned int nkids; XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler); if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) abort (); if (root != root2) abort (); if (parent) abort (); for (i = 0; i < nkids; i++) { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *version; if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1, False, XA_STRING, &type, &format, &nitems, &bytesafter, &version) == Success && type != None) { unsigned char *id; if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512, False, XA_STRING, &type, &format, &nitems, &bytesafter, &id) == Success || type == None) id = (unsigned char *) "???"; fprintf (stderr, "%s: already running on display %s (window 0x%x)\n from process %s.\n", blurb(), DisplayString (dpy), (int) kids [i], (char *) id); status = True; } else if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128, False, XA_STRING, &type, &format, &nitems, &bytesafter, &version) == Success && type != None && !strcmp ((char *) version, "gnome-screensaver")) { fprintf (stderr, "%s: \"%s\" is already running on display %s (window 0x%x)\n", blurb(), (char *) version, DisplayString (dpy), (int) kids [i]); status = True; break; } } if (kids) XFree ((char *) kids); XSync (dpy, False); XSetErrorHandler (old_handler); return status; } /* Virtual-root hackery */ #ifdef _VROOT_H_ ERROR! You must not include vroot.h in this file. #endif static void store_vroot_property (Display *dpy, Window win, Window value) { #if 0 if (p->verbose_p) fprintf (stderr, "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(), win, (win == screensaver_window ? "ScreenSaver" : (win == real_vroot ? "VRoot" : (win == real_vroot_value ? "Vroot_value" : "???"))), value, (value == screensaver_window ? "ScreenSaver" : (value == real_vroot ? "VRoot" : (value == real_vroot_value ? "Vroot_value" : "???")))); #endif XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace, (unsigned char *) &value, 1); } static void remove_vroot_property (Display *dpy, Window win) { #if 0 if (p->verbose_p) fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win, (win == screensaver_window ? "ScreenSaver" : (win == real_vroot ? "VRoot" : (win == real_vroot_value ? "Vroot_value" : "???")))); #endif XDeleteProperty (dpy, win, XA_VROOT); } static Bool safe_XKillClient (Display *dpy, XID id); static void kill_xsetroot_data_1 (Display *dpy, Window window, Atom prop, const char *atom_name, Bool verbose_p) { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *dataP = 0; /* If the user has been using xv or xsetroot as a screensaver (to display an image on the screensaver window, as a kind of slideshow) then the pixmap and its associated color cells have been put in RetainPermanent CloseDown mode. Since we're not destroying the xscreensaver window, but merely unmapping it, we need to free these resources or those colormap cells will stay allocated while the screensaver is off. (We could just delete the screensaver window and recreate it later, but that could cause other problems.) This code does an atomic read-and- delete of the _XSETROOT_ID property, and if it held a pixmap, then we cause the RetainPermanent resources of the client which created it (and which no longer exists) to be freed. Update: it seems that Gnome and KDE do this same trick, but with the properties "ESETROOT_PMAP_ID" and/or "_XROOTPMAP_ID" instead of "_XSETROOT_ID". So, we'll kill those too. */ if (XGetWindowProperty (dpy, window, prop, 0, 1, True, AnyPropertyType, &type, &format, &nitems, &bytesafter, &dataP) == Success && type != None) { Pixmap *pixP = (Pixmap *) dataP; if (pixP && *pixP && type == XA_PIXMAP && format == 32 && nitems == 1 && bytesafter == 0) { if (verbose_p) fprintf (stderr, "%s: destroying %s data (0x%lX).\n", blurb(), atom_name, *pixP); safe_XKillClient (dpy, *pixP); } else fprintf (stderr, "%s: deleted unrecognised %s property: \n" "\t%lu, %lu; type: %lu, format: %d, " "nitems: %lu, bytesafter %ld\n", blurb(), atom_name, (unsigned long) pixP, (pixP ? *pixP : 0), type, format, nitems, bytesafter); } } static void kill_xsetroot_data (Display *dpy, Window w, Bool verbose_p) { kill_xsetroot_data_1 (dpy, w, XA_XSETROOT_ID, "_XSETROOT_ID", verbose_p); kill_xsetroot_data_1 (dpy, w, XA_ESETROOT_PMAP_ID, "ESETROOT_PMAP_ID", verbose_p); kill_xsetroot_data_1 (dpy, w, XA_XROOTPMAP_ID, "_XROOTPMAP_ID", verbose_p); } static void save_real_vroot (saver_screen_info *ssi) { saver_info *si = ssi->global; Display *dpy = si->dpy; Screen *screen = ssi->screen; int i; Window root = RootWindowOfScreen (screen); Window root2, parent, *kids; unsigned int nkids; XErrorHandler old_handler; /* It's possible that a window might be deleted between our call to XQueryTree() and our call to XGetWindowProperty(). Don't die if that happens (but just ignore that window, it's not the one we're interested in anyway.) */ XSync (dpy, False); old_handler = XSetErrorHandler (BadWindow_ehandler); XSync (dpy, False); ssi->real_vroot = 0; ssi->real_vroot_value = 0; if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) abort (); if (root != root2) abort (); if (parent) abort (); for (i = 0; i < nkids; i++) { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *dataP = 0; Window *vrootP; int j; /* Skip this window if it is the xscreensaver window of any other screen (this can happen in the Xinerama case.) */ for (j = 0; j < si->nscreens; j++) { saver_screen_info *ssi2 = &si->screens[j]; if (kids[i] == ssi2->screensaver_window) goto SKIP; } if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW, &type, &format, &nitems, &bytesafter, &dataP) != Success) continue; if (! dataP) continue; vrootP = (Window *) dataP; if (ssi->real_vroot) { if (*vrootP == ssi->screensaver_window) abort (); fprintf (stderr, "%s: more than one virtual root window found (0x%x and 0x%x).\n", blurb(), (int) ssi->real_vroot, (int) kids [i]); exit (1); } ssi->real_vroot = kids [i]; ssi->real_vroot_value = *vrootP; SKIP: ; } XSync (dpy, False); XSetErrorHandler (old_handler); XSync (dpy, False); if (ssi->real_vroot) { remove_vroot_property (si->dpy, ssi->real_vroot); XSync (dpy, False); } XFree ((char *) kids); } static Bool restore_real_vroot_1 (saver_screen_info *ssi) { saver_info *si = ssi->global; saver_preferences *p = &si->prefs; if (p->verbose_p && ssi->real_vroot) fprintf (stderr, "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n", blurb(), (unsigned long) ssi->real_vroot); if (ssi->screensaver_window) remove_vroot_property (si->dpy, ssi->screensaver_window); if (ssi->real_vroot) { store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value); ssi->real_vroot = 0; ssi->real_vroot_value = 0; /* make sure the property change gets there before this process terminates! We might be doing this because we have intercepted SIGTERM or something. */ XSync (si->dpy, False); return True; } return False; } Bool restore_real_vroot (saver_info *si) { int i; Bool did_any = False; for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (restore_real_vroot_1 (ssi)) did_any = True; } return did_any; } /* Signal hackery to ensure that the vroot doesn't get left in an inconsistent state */ const char * signal_name(int signal) { switch (signal) { case SIGHUP: return "SIGHUP"; case SIGINT: return "SIGINT"; case SIGQUIT: return "SIGQUIT"; case SIGILL: return "SIGILL"; case SIGTRAP: return "SIGTRAP"; #ifdef SIGABRT case SIGABRT: return "SIGABRT"; #endif case SIGFPE: return "SIGFPE"; case SIGKILL: return "SIGKILL"; case SIGBUS: return "SIGBUS"; case SIGSEGV: return "SIGSEGV"; case SIGPIPE: return "SIGPIPE"; case SIGALRM: return "SIGALRM"; case SIGTERM: return "SIGTERM"; #ifdef SIGSTOP case SIGSTOP: return "SIGSTOP"; #endif #ifdef SIGCONT case SIGCONT: return "SIGCONT"; #endif #ifdef SIGUSR1 case SIGUSR1: return "SIGUSR1"; #endif #ifdef SIGUSR2 case SIGUSR2: return "SIGUSR2"; #endif #ifdef SIGEMT case SIGEMT: return "SIGEMT"; #endif #ifdef SIGSYS case SIGSYS: return "SIGSYS"; #endif #ifdef SIGCHLD case SIGCHLD: return "SIGCHLD"; #endif #ifdef SIGPWR case SIGPWR: return "SIGPWR"; #endif #ifdef SIGWINCH case SIGWINCH: return "SIGWINCH"; #endif #ifdef SIGURG case SIGURG: return "SIGURG"; #endif #ifdef SIGIO case SIGIO: return "SIGIO"; #endif #ifdef SIGVTALRM case SIGVTALRM: return "SIGVTALRM"; #endif #ifdef SIGXCPU case SIGXCPU: return "SIGXCPU"; #endif #ifdef SIGXFSZ case SIGXFSZ: return "SIGXFSZ"; #endif #ifdef SIGDANGER case SIGDANGER: return "SIGDANGER"; #endif default: { static char buf[50]; sprintf(buf, "signal %d\n", signal); return buf; } } } static RETSIGTYPE restore_real_vroot_handler (int sig) { saver_info *si = global_si_kludge; /* I hate C so much... */ signal (sig, SIG_DFL); if (restore_real_vroot (si)) fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n", blurb(), signal_name(sig)); kill (getpid (), sig); } static void catch_signal (saver_info *si, int sig, RETSIGTYPE (*handler) (int)) { # ifdef HAVE_SIGACTION struct sigaction a; a.sa_handler = handler; sigemptyset (&a.sa_mask); a.sa_flags = 0; /* On Linux 2.4.9 (at least) we need to tell the kernel to not mask delivery of this signal from inside its handler, or else when we execvp() the process again, it starts up with SIGHUP blocked, meaning that killing it with -HUP only works *once*. You'd think that execvp() would reset all the signal masks, but it doesn't. */ # if defined(SA_NOMASK) a.sa_flags |= SA_NOMASK; # elif defined(SA_NODEFER) a.sa_flags |= SA_NODEFER; # endif if (sigaction (sig, &a, 0) < 0) # else /* !HAVE_SIGACTION */ if (((long) signal (sig, handler)) == -1L) # endif /* !HAVE_SIGACTION */ { char buf [255]; sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig)); perror (buf); saver_exit (si, 1, 0); } } static RETSIGTYPE saver_sighup_handler (int sig); void handle_signals (saver_info *si) { catch_signal (si, SIGHUP, saver_sighup_handler); catch_signal (si, SIGINT, restore_real_vroot_handler); catch_signal (si, SIGQUIT, restore_real_vroot_handler); catch_signal (si, SIGILL, restore_real_vroot_handler); catch_signal (si, SIGTRAP, restore_real_vroot_handler); #ifdef SIGIOT catch_signal (si, SIGIOT, restore_real_vroot_handler); #endif catch_signal (si, SIGABRT, restore_real_vroot_handler); #ifdef SIGEMT catch_signal (si, SIGEMT, restore_real_vroot_handler); #endif catch_signal (si, SIGFPE, restore_real_vroot_handler); catch_signal (si, SIGBUS, restore_real_vroot_handler); catch_signal (si, SIGSEGV, restore_real_vroot_handler); #ifdef SIGSYS catch_signal (si, SIGSYS, restore_real_vroot_handler); #endif catch_signal (si, SIGTERM, restore_real_vroot_handler); #ifdef SIGXCPU catch_signal (si, SIGXCPU, restore_real_vroot_handler); #endif #ifdef SIGXFSZ catch_signal (si, SIGXFSZ, restore_real_vroot_handler); #endif #ifdef SIGDANGER catch_signal (si, SIGDANGER, restore_real_vroot_handler); #endif } static RETSIGTYPE saver_sighup_handler (int sig) { saver_info *si = global_si_kludge; /* I hate C so much... */ /* Re-establish SIGHUP handler */ catch_signal (si, SIGHUP, saver_sighup_handler); fprintf (stderr, "%s: %s received: restarting...\n", blurb(), signal_name(sig)); if (si->screen_blanked_p) { int i; for (i = 0; i < si->nscreens; i++) kill_screenhack (&si->screens[i]); unblank_screen (si); XSync (si->dpy, False); } restart_process (si); /* Does not return */ abort (); } void saver_exit (saver_info *si, int status, const char *dump_core_reason) { saver_preferences *p = &si->prefs; static Bool exiting = False; Bool bugp; Bool vrs; if (exiting) exit(status); exiting = True; vrs = restore_real_vroot (si); emergency_kill_subproc (si); shutdown_stderr (si); if (p->verbose_p && vrs) fprintf (real_stderr, "%s: old vroot restored.\n", blurb()); fflush(real_stdout); #ifdef VMS /* on VMS, 1 is the "normal" exit code instead of 0. */ if (status == 0) status = 1; else if (status == 1) status = -1; #endif bugp = !!dump_core_reason; if (si->prefs.debug_p && !dump_core_reason) dump_core_reason = "because of -debug"; if (dump_core_reason) { /* Note that the Linux man page for setuid() says If uid is different from the old effective uid, the process will be forbidden from leaving core dumps. */ char cwd[4096]; /* should really be PATH_MAX, but who cares. */ cwd[0] = 0; fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(), dump_core_reason); if (bugp) fprintf(real_stderr, "%s: see http://www.jwz.org/xscreensaver/bugs.html\n" "\t\t\tfor bug reporting information.\n\n", blurb()); # if defined(HAVE_GETCWD) if (!getcwd (cwd, sizeof(cwd))) # elif defined(HAVE_GETWD) if (!getwd (cwd)) # endif strcpy(cwd, "unknown."); fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd); describe_uids (si, real_stderr); /* Do this to drop a core file, so that we can get a stack trace. */ abort(); } exit (status); } /* Managing the actual screensaver window */ Bool window_exists_p (Display *dpy, Window window) { XErrorHandler old_handler; XWindowAttributes xgwa; xgwa.screen = 0; old_handler = XSetErrorHandler (BadWindow_ehandler); XGetWindowAttributes (dpy, window, &xgwa); XSync (dpy, False); XSetErrorHandler (old_handler); return (xgwa.screen != 0); } static void store_saver_id (saver_screen_info *ssi) { XClassHint class_hints; saver_info *si = ssi->global; unsigned long pid = (unsigned long) getpid (); char buf[20]; struct passwd *p = getpwuid (getuid ()); const char *name, *host; char *id; /* First store the name and class on the window. */ class_hints.res_name = progname; class_hints.res_class = progclass; XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints); XStoreName (si->dpy, ssi->screensaver_window, "screensaver"); /* Then store the xscreensaver version number. */ XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_VERSION, XA_STRING, 8, PropModeReplace, (unsigned char *) si->version, strlen (si->version)); /* Now store the XSCREENSAVER_ID property, that says what user and host xscreensaver is running as. */ if (p && p->pw_name && *p->pw_name) name = p->pw_name; else if (p) { sprintf (buf, "%lu", (unsigned long) p->pw_uid); name = buf; } else name = "???"; # if defined(HAVE_UNAME) { struct utsname uts; if (uname (&uts) < 0) host = "???"; else host = uts.nodename; } # elif defined(VMS) host = getenv("SYS$NODE"); # else /* !HAVE_UNAME && !VMS */ host = "???"; # endif /* !HAVE_UNAME && !VMS */ id = (char *) malloc (strlen(name) + strlen(host) + 50); sprintf (id, "%lu (%s@%s)", pid, name, host); XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_ID, XA_STRING, 8, PropModeReplace, (unsigned char *) id, strlen (id)); free (id); } void store_saver_status (saver_info *si) { PROP32 *status; int size = si->nscreens + 2; int i; status = (PROP32 *) calloc (size, sizeof(PROP32)); status[0] = (PROP32) (si->screen_blanked_p ? (si->locked_p ? XA_LOCK : XA_BLANK) : 0); status[1] = (PROP32) si->blank_time; for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; status [2 + i] = ssi->current_hack + 1; } XChangeProperty (si->dpy, RootWindow (si->dpy, 0), /* always screen #0 */ XA_SCREENSAVER_STATUS, XA_INTEGER, 32, PropModeReplace, (unsigned char *) status, size); free (status); } static Bool error_handler_hit_p = False; static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) { error_handler_hit_p = True; return 0; } /* Returns True if successful, False if an X error occurred. We need this because other programs might have done things to our window that will cause XChangeWindowAttributes() to fail: if that happens, we give up, destroy the window, and re-create it. */ static Bool safe_XChangeWindowAttributes (Display *dpy, Window window, unsigned long mask, XSetWindowAttributes *attrs) { XErrorHandler old_handler; XSync (dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); XChangeWindowAttributes (dpy, window, mask, attrs); XSync (dpy, False); XSetErrorHandler (old_handler); XSync (dpy, False); return (!error_handler_hit_p); } /* This might not be necessary, but just in case. */ static Bool safe_XConfigureWindow (Display *dpy, Window window, unsigned long mask, XWindowChanges *changes) { XErrorHandler old_handler; XSync (dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); XConfigureWindow (dpy, window, mask, changes); XSync (dpy, False); XSetErrorHandler (old_handler); XSync (dpy, False); return (!error_handler_hit_p); } /* This might not be necessary, but just in case. */ static Bool safe_XDestroyWindow (Display *dpy, Window window) { XErrorHandler old_handler; XSync (dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); XDestroyWindow (dpy, window); XSync (dpy, False); XSetErrorHandler (old_handler); XSync (dpy, False); return (!error_handler_hit_p); } static Bool safe_XKillClient (Display *dpy, XID id) { XErrorHandler old_handler; XSync (dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); XKillClient (dpy, id); XSync (dpy, False); XSetErrorHandler (old_handler); XSync (dpy, False); return (!error_handler_hit_p); } #ifdef HAVE_XF86VMODE Bool safe_XF86VidModeGetViewPort (Display *dpy, int screen, int *xP, int *yP) { Bool result; XErrorHandler old_handler; XSync (dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); result = XF86VidModeGetViewPort (dpy, screen, xP, yP); XSync (dpy, False); XSetErrorHandler (old_handler); XSync (dpy, False); return (error_handler_hit_p ? False : result); } /* There is no "safe_XF86VidModeGetModeLine" because it fails with an untrappable I/O error instead of an X error -- so one must call safe_XF86VidModeGetViewPort first, and assume that both have the same error condition. Thank you XFree, may I have another. */ #endif /* HAVE_XF86VMODE */ static void initialize_screensaver_window_1 (saver_screen_info *ssi) { saver_info *si = ssi->global; saver_preferences *p = &si->prefs; Bool install_cmap_p = ssi->install_cmap_p; /* not p->install_cmap_p */ /* This resets the screensaver window as fully as possible, since there's no way of knowing what some random client may have done to us in the meantime. We could just destroy and recreate the window, but that has its own set of problems... */ XColor black; XSetWindowAttributes attrs; unsigned long attrmask; static Bool printed_visual_info = False; /* only print the message once. */ Window horked_window = 0; black.red = black.green = black.blue = 0; if (ssi->cmap == DefaultColormapOfScreen (ssi->screen)) ssi->cmap = 0; if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen)) /* It's not the default visual, so we have no choice but to install. */ install_cmap_p = True; if (install_cmap_p) { if (! ssi->cmap) { ssi->cmap = XCreateColormap (si->dpy, RootWindowOfScreen (ssi->screen), ssi->current_visual, AllocNone); if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort (); ssi->black_pixel = black.pixel; } } else { Colormap def_cmap = DefaultColormapOfScreen (ssi->screen); if (ssi->cmap) { XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0); if (ssi->cmap != ssi->demo_cmap && ssi->cmap != def_cmap) XFreeColormap (si->dpy, ssi->cmap); } ssi->cmap = def_cmap; ssi->black_pixel = BlackPixelOfScreen (ssi->screen); } attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap | CWBackPixel | CWBackingPixel | CWBorderPixel); attrs.override_redirect = True; /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't actually be reading these events during normal operation; but we still need to see Button events for demo-mode to work properly. */ attrs.event_mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask); attrs.backing_store = NotUseful; attrs.colormap = ssi->cmap; attrs.background_pixel = ssi->black_pixel; attrs.backing_pixel = ssi->black_pixel; attrs.border_pixel = ssi->black_pixel; if (!p->verbose_p || printed_visual_info) ; else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen)) { fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number); describe_visual (stderr, ssi->screen, ssi->current_visual, install_cmap_p); } else { fprintf (stderr, "%s: using visual: ", blurb()); describe_visual (stderr, ssi->screen, ssi->current_visual, install_cmap_p); fprintf (stderr, "%s: default visual: ", blurb()); describe_visual (stderr, ssi->screen, DefaultVisualOfScreen (ssi->screen), ssi->install_cmap_p); } printed_visual_info = True; #ifdef HAVE_MIT_SAVER_EXTENSION if (si->using_mit_saver_extension) { XScreenSaverInfo *info; Window root = RootWindowOfScreen (ssi->screen); #if 0 /* This call sets the server screensaver timeouts to what we think they should be (based on the resources and args xscreensaver was started with.) It's important that we do this to sync back up with the server - if we have turned on prematurely, as by an ACTIVATE ClientMessage, then the server may decide to activate the screensaver while it's already active. That's ok for us, since we would know to ignore that ScreenSaverActivate event, but a side effect of this would be that the server would map its saver window (which we then hide again right away) meaning that the bits currently on the screen get blown away. Ugly. */ /* #### Ok, that doesn't work - when we tell the server that the screensaver is "off" it sends us a Deactivate event, which is sensible... but causes the saver to never come on. Hmm. */ disable_builtin_screensaver (si, True); #endif /* 0 */ #if 0 /* #### The MIT-SCREEN-SAVER extension gives us access to the window that the server itself uses for saving the screen. However, using this window in any way, in particular, calling XScreenSaverSetAttributes() as below, tends to make the X server crash. So fuck it, let's try and get along without using it... It's also inconvenient to use this window because it doesn't always exist (though the ID is constant.) So to use this window, we'd have to reimplement the ACTIVATE ClientMessage to tell the *server* to tell *us* to turn on, to cause the window to get created at the right time. Gag. */ XScreenSaverSetAttributes (si->dpy, root, 0, 0, width, height, 0, current_depth, InputOutput, visual, attrmask, &attrs); XSync (si->dpy, False); #endif /* 0 */ info = XScreenSaverAllocInfo (); XScreenSaverQueryInfo (si->dpy, root, info); ssi->server_mit_saver_window = info->window; if (! ssi->server_mit_saver_window) abort (); XFree (info); } #endif /* HAVE_MIT_SAVER_EXTENSION */ if (ssi->screensaver_window) { XWindowChanges changes; unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; changes.x = ssi->x; changes.y = ssi->y; changes.width = ssi->width; changes.height = ssi->height; changes.border_width = 0; if (! (safe_XConfigureWindow (si->dpy, ssi->screensaver_window, changesmask, &changes) && safe_XChangeWindowAttributes (si->dpy, ssi->screensaver_window, attrmask, &attrs))) { horked_window = ssi->screensaver_window; ssi->screensaver_window = 0; } } if (!ssi->screensaver_window) { ssi->screensaver_window = XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen), ssi->x, ssi->y, ssi->width, ssi->height, 0, ssi->current_depth, InputOutput, ssi->current_visual, attrmask, &attrs); reset_stderr (ssi); if (horked_window) { fprintf (stderr, "%s: someone horked our saver window (0x%lx)! Recreating it...\n", blurb(), (unsigned long) horked_window); maybe_transfer_grabs (ssi, horked_window, ssi->screensaver_window, ssi->number); safe_XDestroyWindow (si->dpy, horked_window); horked_window = 0; } if (p->verbose_p) fprintf (stderr, "%s: %d: saver window is 0x%lx.\n", blurb(), ssi->number, (unsigned long) ssi->screensaver_window); } store_saver_id (ssi); /* store window name and IDs */ if (!ssi->cursor) { Pixmap bit; bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window, "\000", 1, 1, BlackPixelOfScreen (ssi->screen), BlackPixelOfScreen (ssi->screen), 1); ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black, 0, 0); XFreePixmap (si->dpy, bit); } XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel); if (si->demoing_p) XUndefineCursor (si->dpy, ssi->screensaver_window); else XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor); } void initialize_screensaver_window (saver_info *si) { int i; for (i = 0; i < si->nscreens; i++) initialize_screensaver_window_1 (&si->screens[i]); } /* Called when the RANDR (Resize and Rotate) extension tells us that the size of the screen has changed while the screen was blanked. Call update_screen_layout() first, then call this to synchronize the size of the saver windows to the new sizes of the screens. */ void resize_screensaver_window (saver_info *si) { saver_preferences *p = &si->prefs; int i; for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; XWindowAttributes xgwa; /* Make sure a window exists -- it might not if a monitor was just added for the first time. */ if (! ssi->screensaver_window) { initialize_screensaver_window_1 (ssi); if (p->verbose_p) fprintf (stderr, "%s: %d: newly added window 0x%lx %dx%d+%d+%d\n", blurb(), i, (unsigned long) ssi->screensaver_window, ssi->width, ssi->height, ssi->x, ssi->y); } /* Make sure the window is the right size -- it might not be if the monitor changed resolution, or if a badly-behaved hack screwed with it. */ XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa); if (xgwa.x != ssi->x || xgwa.y != ssi->y || xgwa.width != ssi->width || xgwa.height != ssi->height) { XWindowChanges changes; unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; changes.x = ssi->x; changes.y = ssi->y; changes.width = ssi->width; changes.height = ssi->height; changes.border_width = 0; if (p->verbose_p) fprintf (stderr, "%s: %d: resize 0x%lx from %dx%d+%d+%d to %dx%d+%d+%d\n", blurb(), i, (unsigned long) ssi->screensaver_window, xgwa.width, xgwa.height, xgwa.x, xgwa.y, ssi->width, ssi->height, ssi->x, ssi->y); if (! safe_XConfigureWindow (si->dpy, ssi->screensaver_window, changesmask, &changes)) fprintf (stderr, "%s: %d: someone horked our saver window" " (0x%lx)! Unable to resize it!\n", blurb(), i, (unsigned long) ssi->screensaver_window); } /* Now (if blanked) make sure that it's mapped and running a hack -- it might not be if we just added it. (We also might be re-using an old window that existed for a previous monitor that was removed and re-added.) Note that spawn_screenhack() calls select_visual() which may destroy and re-create the window via initialize_screensaver_window_1(). */ if (si->screen_blanked_p) { if (ssi->cmap) XInstallColormap (si->dpy, ssi->cmap); XMapRaised (si->dpy, ssi->screensaver_window); if (! ssi->pid) spawn_screenhack (ssi); /* Make sure the act of adding a screen doesn't present as pointer motion (and thus cause an unblank). */ { Window root, child; int x, y; unsigned int mask; XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child, &ssi->poll_mouse_last_root_x, &ssi->poll_mouse_last_root_y, &x, &y, &mask); } } } /* Kill off any savers running on no-longer-extant monitors. */ for (; i < si->ssi_count; i++) { saver_screen_info *ssi = &si->screens[i]; if (ssi->pid) kill_screenhack (ssi); if (ssi->screensaver_window) { XUnmapWindow (si->dpy, ssi->screensaver_window); restore_real_vroot_1 (ssi); } } } void raise_window (saver_info *si, Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear) { saver_preferences *p = &si->prefs; int i; if (si->demoing_p) inhibit_fade = True; if (si->emergency_lock_p) inhibit_fade = True; if (!dont_clear) initialize_screensaver_window (si); reset_watchdog_timer (si, True); if (p->fade_p && si->fading_possible_p && !inhibit_fade) { Window *current_windows = (Window *) calloc(sizeof(Window), si->nscreens); Colormap *current_maps = (Colormap *) calloc(sizeof(Colormap), si->nscreens); for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; current_windows[i] = ssi->screensaver_window; current_maps[i] = (between_hacks_p ? ssi->cmap : DefaultColormapOfScreen (ssi->screen)); /* Ensure that the default background of the window is really black, not a pixmap or something. (This does not clear the window.) */ XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel); } if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb()); XGrabServer (si->dpy); /* ############ DANGER! */ /* Clear the stderr layer on each screen. */ if (!dont_clear) for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (ssi->stderr_overlay_window) /* Do this before the fade, since the stderr cmap won't fade even if we uninstall it (beats me...) */ clear_stderr (ssi); } /* Note! The server is grabbed, and this will take several seconds to complete! */ fade_screens (si->dpy, current_maps, current_windows, si->nscreens, p->fade_seconds/1000, p->fade_ticks, True, !dont_clear); free(current_maps); free(current_windows); current_maps = 0; current_windows = 0; if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb()); #ifdef HAVE_MIT_SAVER_EXTENSION for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (ssi->server_mit_saver_window && window_exists_p (si->dpy, ssi->server_mit_saver_window)) XUnmapWindow (si->dpy, ssi->server_mit_saver_window); } #endif /* HAVE_MIT_SAVER_EXTENSION */ XUngrabServer (si->dpy); XSync (si->dpy, False); /* ###### (danger over) */ } else { for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (!dont_clear) XClearWindow (si->dpy, ssi->screensaver_window); if (!dont_clear || ssi->stderr_overlay_window) clear_stderr (ssi); XMapRaised (si->dpy, ssi->screensaver_window); #ifdef HAVE_MIT_SAVER_EXTENSION if (ssi->server_mit_saver_window && window_exists_p (si->dpy, ssi->server_mit_saver_window)) XUnmapWindow (si->dpy, ssi->server_mit_saver_window); #endif /* HAVE_MIT_SAVER_EXTENSION */ } } for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (ssi->cmap) XInstallColormap (si->dpy, ssi->cmap); } } int mouse_screen (saver_info *si) { saver_preferences *p = &si->prefs; Window pointer_root, pointer_child; int root_x, root_y, win_x, win_y; unsigned int mask; int i; if (si->nscreens == 1) return 0; for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (XQueryPointer (si->dpy, RootWindowOfScreen (ssi->screen), &pointer_root, &pointer_child, &root_x, &root_y, &win_x, &win_y, &mask) && root_x >= ssi->x && root_y >= ssi->y && root_x < ssi->x + ssi->width && root_y < ssi->y + ssi->height) { if (p->verbose_p) fprintf (stderr, "%s: mouse is on screen %d of %d\n", blurb(), i, si->nscreens); return i; } } /* couldn't figure out where the mouse is? Oh well. */ return 0; } Bool blank_screen (saver_info *si) { int i; Bool ok; Window w; int mscreen; /* Note: we do our grabs on the root window, not on the screensaver window. If we grabbed on the saver window, then the demo mode and lock dialog boxes wouldn't get any events. By "the root window", we mean "the root window that contains the mouse." We use to always grab the mouse on screen 0, but that has the effect of moving the mouse to screen 0 from whichever screen it was on, on multi-head systems. */ mscreen = mouse_screen (si); w = RootWindowOfScreen(si->screens[mscreen].screen); ok = grab_keyboard_and_mouse (si, w, (si->demoing_p ? 0 : si->screens[0].cursor), mscreen); # if 0 if (si->using_mit_saver_extension || si->using_sgi_saver_extension) /* If we're using a server extension, then failure to get a grab is not a big deal -- even without the grab, we will still be able to un-blank when there is user activity, since the server will tell us. */ /* #### No, that's not true: if we don't have a keyboard grab, then we can't read passwords to unlock. */ ok = True; # endif /* 0 */ if (!ok) return False; for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (ssi->real_screen_p) save_real_vroot (ssi); store_vroot_property (si->dpy, ssi->screensaver_window, ssi->screensaver_window); #ifdef HAVE_XF86VMODE { int ev, er; if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) || !safe_XF86VidModeGetViewPort (si->dpy, i, &ssi->blank_vp_x, &ssi->blank_vp_y)) ssi->blank_vp_x = ssi->blank_vp_y = -1; } #endif /* HAVE_XF86VMODE */ } raise_window (si, False, False, False); si->screen_blanked_p = True; si->blank_time = time ((time_t) 0); si->last_wall_clock_time = 0; store_saver_status (si); /* store blank time */ return True; } void unblank_screen (saver_info *si) { saver_preferences *p = &si->prefs; Bool unfade_p = (si->fading_possible_p && p->unfade_p); int i; monitor_power_on (si, True); reset_watchdog_timer (si, False); if (si->demoing_p) unfade_p = False; if (unfade_p) { Window *current_windows = (Window *) calloc(sizeof(Window), si->nscreens); for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; current_windows[i] = ssi->screensaver_window; /* Ensure that the default background of the window is really black, not a pixmap or something. (This does not clear the window.) */ XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel); } if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb()); XSync (si->dpy, False); XGrabServer (si->dpy); /* ############ DANGER! */ XSync (si->dpy, False); /* Clear the stderr layer on each screen. */ for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; clear_stderr (ssi); } XUngrabServer (si->dpy); XSync (si->dpy, False); /* ###### (danger over) */ fade_screens (si->dpy, 0, current_windows, si->nscreens, p->fade_seconds/1000, p->fade_ticks, False, False); free(current_windows); current_windows = 0; if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb()); } else { for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; if (ssi->cmap) { Colormap c = DefaultColormapOfScreen (ssi->screen); /* avoid technicolor */ XClearWindow (si->dpy, ssi->screensaver_window); if (c) XInstallColormap (si->dpy, c); } XUnmapWindow (si->dpy, ssi->screensaver_window); } } /* If the focus window does has a non-default colormap, then install that colormap as well. (On SGIs, this will cause both the root map and the focus map to be installed simultaneously. It'd be nice to pick up the other colormaps that had been installed, too; perhaps XListInstalledColormaps could be used for that?) */ { Window focus = 0; int revert_to; XGetInputFocus (si->dpy, &focus, &revert_to); if (focus && focus != PointerRoot && focus != None) { XWindowAttributes xgwa; xgwa.colormap = 0; XGetWindowAttributes (si->dpy, focus, &xgwa); if (xgwa.colormap && xgwa.colormap != DefaultColormapOfScreen (xgwa.screen)) XInstallColormap (si->dpy, xgwa.colormap); } } for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p); } store_saver_status (si); /* store unblank time */ ungrab_keyboard_and_mouse (si); restore_real_vroot (si); /* Unmap the windows a second time, dammit -- just to avoid a race with the screen-grabbing hacks. (I'm not sure if this is really necessary; I'm stabbing in the dark now.) */ for (i = 0; i < si->nscreens; i++) XUnmapWindow (si->dpy, si->screens[i].screensaver_window); si->screen_blanked_p = False; si->blank_time = time ((time_t) 0); si->last_wall_clock_time = 0; store_saver_status (si); /* store unblank time */ } /* Transfer any grabs from the old window to the new. Actually I think none of this is necessary, since we always hold our grabs on the root window, but I wrote this before re-discovering that... */ static void maybe_transfer_grabs (saver_screen_info *ssi, Window old_w, Window new_w, int new_screen_no) { saver_info *si = ssi->global; /* If the old window held our mouse grab, transfer the grab to the new window. (Grab the server while so doing, to avoid a race condition.) */ if (old_w == si->mouse_grab_window) { XGrabServer (si->dpy); /* ############ DANGER! */ ungrab_mouse (si); grab_mouse (si, ssi->screensaver_window, (si->demoing_p ? 0 : ssi->cursor), new_screen_no); XUngrabServer (si->dpy); XSync (si->dpy, False); /* ###### (danger over) */ } /* If the old window held our keyboard grab, transfer the grab to the new window. (Grab the server while so doing, to avoid a race condition.) */ if (old_w == si->keyboard_grab_window) { XGrabServer (si->dpy); /* ############ DANGER! */ ungrab_kbd(si); grab_kbd(si, ssi->screensaver_window, ssi->number); XUngrabServer (si->dpy); XSync (si->dpy, False); /* ###### (danger over) */ } } static Visual * get_screen_gl_visual (saver_info *si, int real_screen_number) { int i; int nscreens = ScreenCount (si->dpy); if (! si->best_gl_visuals) si->best_gl_visuals = (Visual **) calloc (nscreens + 1, sizeof (*si->best_gl_visuals)); for (i = 0; i < nscreens; i++) if (! si->best_gl_visuals[i]) si->best_gl_visuals[i] = get_best_gl_visual (si, ScreenOfDisplay (si->dpy, i)); if (real_screen_number < 0 || real_screen_number >= nscreens) abort(); return si->best_gl_visuals[real_screen_number]; } Bool select_visual (saver_screen_info *ssi, const char *visual_name) { XWindowAttributes xgwa; saver_info *si = ssi->global; saver_preferences *p = &si->prefs; Bool install_cmap_p = p->install_cmap_p; Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen)); Visual *new_v = 0; Bool got_it; /* On some systems (most recently, MacOS X) OpenGL programs get confused when you kill one and re-start another on the same window. So maybe it's best to just always destroy and recreate the xscreensaver window when changing hacks, instead of trying to reuse the old one? */ Bool always_recreate_window_p = True; get_screen_gl_visual (si, 0); /* let's probe all the GL visuals early */ /* We make sure the existing window is actually on ssi->screen before trying to use it, in case things moved around radically when monitors were added or deleted. If we don't do this we could get a BadMatch even though the depths match. I think. */ memset (&xgwa, 0, sizeof(xgwa)); if (ssi->screensaver_window) XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa); if (visual_name && *visual_name) { if (!strcmp(visual_name, "default-i") || !strcmp(visual_name, "Default-i") || !strcmp(visual_name, "Default-I") ) { visual_name = "default"; install_cmap_p = True; } else if (!strcmp(visual_name, "default-n") || !strcmp(visual_name, "Default-n") || !strcmp(visual_name, "Default-N")) { visual_name = "default"; install_cmap_p = False; } else if (!strcmp(visual_name, "gl") || !strcmp(visual_name, "Gl") || !strcmp(visual_name, "GL")) { new_v = get_screen_gl_visual (si, ssi->real_screen_number); if (!new_v && p->verbose_p) fprintf (stderr, "%s: no GL visuals.\n", progname); } if (!new_v) new_v = get_visual (ssi->screen, visual_name, True, False); } else { new_v = ssi->default_visual; } got_it = !!new_v; if (new_v && new_v != DefaultVisualOfScreen(ssi->screen)) /* It's not the default visual, so we have no choice but to install. */ install_cmap_p = True; ssi->install_cmap_p = install_cmap_p; if ((ssi->screen != xgwa.screen) || (new_v && (always_recreate_window_p || (ssi->current_visual != new_v) || (install_cmap_p != was_installed_p)))) { Colormap old_c = ssi->cmap; Window old_w = ssi->screensaver_window; if (! new_v) new_v = ssi->current_visual; if (p->verbose_p) { fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number); describe_visual (stderr, ssi->screen, new_v, install_cmap_p); #if 0 fprintf (stderr, "%s: from ", blurb()); describe_visual (stderr, ssi->screen, ssi->current_visual, was_installed_p); #endif } reset_stderr (ssi); ssi->current_visual = new_v; ssi->current_depth = visual_depth(ssi->screen, new_v); ssi->cmap = 0; ssi->screensaver_window = 0; initialize_screensaver_window_1 (ssi); /* stderr_overlay_window is a child of screensaver_window, so we need to destroy that as well (actually, we just need to invalidate and drop our pointers to it, but this will destroy it, which is ok so long as it happens before old_w itself is destroyed.) */ reset_stderr (ssi); raise_window (si, True, True, False); store_vroot_property (si->dpy, ssi->screensaver_window, ssi->screensaver_window); /* Transfer any grabs from the old window to the new. */ maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window, ssi->number); /* Now we can destroy the old window without horking our grabs. */ XDestroyWindow (si->dpy, old_w); if (p->verbose_p) fprintf (stderr, "%s: %d: destroyed old saver window 0x%lx.\n", blurb(), ssi->number, (unsigned long) old_w); if (old_c && old_c != DefaultColormapOfScreen (ssi->screen) && old_c != ssi->demo_cmap) XFreeColormap (si->dpy, old_c); } return got_it; } xscreensaver-5.15/driver/xdpyinfo.c000066400000000000000000000760171164314150500174660ustar00rootroot00000000000000/* * $ TOG: xdpyinfo.c /main/35 1998/02/09 13:57:05 kaleb $ * * xdpyinfo - print information about X display connecton * * Copyright 1988, 1998 The Open Group All Rights Reserved. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. * * Author: Jim Fulton, MIT X Consortium * * GLX and Overlay support added by Jamie Zawinski , 11-Nov-99 * * To compile: * cc -DHAVE_GLX xdpyinfo.c -o xdpyinfo -lGL -lX11 -lXext [-lXtst] -lm * * Other defines to consider: * -DMITSHM -DHAVE_XDBE -DHAVE_XIE -DHAVE_XTEST -DHAVE_SYNC * -DHAVE_XRECORD */ #include #include #include /* for CARD32 */ #include #ifdef HAVE_XIE #include #endif /* HAVE_XIE */ #ifdef HAVE_XTEST #include #endif /* HAVE_XTEST */ #ifdef HAVE_XSYNC #include #endif /* HAVE_XSYNC */ #ifdef HAVE_XDBE #include #endif /* HAVE_XDBE */ #ifdef HAVE_XRECORD #include #endif /* HAVE_XRECORD */ #ifdef MITSHM #include #endif #include #include #ifdef HAVE_GLX # include # include #endif /* HAVE_GLX */ #define HAVE_OVERLAY /* jwz: no compile-time deps, so do this all the time */ char *ProgramName; Bool queryExtensions = False; static int StrCmp(a, b) char **a, **b; { return strcmp(*a, *b); } #ifdef HAVE_GLX /* Added by jwz, 11-Nov-99 */ static void print_glx_versions (dpy) Display *dpy; { /* Note: with Mesa 3.0, this lies: it prints the info from the client's GL library, rather than the info from the GLX server. Note also that we can't protect these calls by only doing them when the GLX extension is present, because with Mesa, the server doesn't have that extension (but the GL library works anyway.) */ int scr = DefaultScreen (dpy); const char *vend, *vers; vend = glXQueryServerString (dpy, scr, GLX_VENDOR); if (!vend) return; vers = glXQueryServerString (dpy, scr, GLX_VERSION); printf ("GLX vendor: %s (%s)\n", vend, (vers ? vers : "unknown version")); } static void print_glx_visual_info (dpy, vip) Display *dpy; XVisualInfo *vip; { int status, value = False; status = glXGetConfig (dpy, vip, GLX_USE_GL, &value); if (status == GLX_NO_EXTENSION) /* dpy does not support the GLX extension. */ return; if (status == GLX_BAD_VISUAL || value == False) { printf (" GLX supported: no\n"); return; } else { printf (" GLX supported: yes\n"); } if (!glXGetConfig (dpy, vip, GLX_LEVEL, &value) && value != 0) printf (" GLX level: %d\n", value); if (!glXGetConfig (dpy, vip, GLX_RGBA, &value) && value) { int r=0, g=0, b=0, a=0; glXGetConfig (dpy, vip, GLX_RED_SIZE, &r); glXGetConfig (dpy, vip, GLX_GREEN_SIZE, &g); glXGetConfig (dpy, vip, GLX_BLUE_SIZE, &b); glXGetConfig (dpy, vip, GLX_ALPHA_SIZE, &a); printf (" GLX type: RGBA (%2d, %2d, %2d, %2d)\n", r, g, b, a); r=0, g=0, b=0, a=0; glXGetConfig (dpy, vip, GLX_ACCUM_RED_SIZE, &r); glXGetConfig (dpy, vip, GLX_ACCUM_GREEN_SIZE, &g); glXGetConfig (dpy, vip, GLX_ACCUM_BLUE_SIZE, &b); glXGetConfig (dpy, vip, GLX_ACCUM_ALPHA_SIZE, &a); printf (" GLX accum: RGBA (%2d, %2d, %2d, %2d)\n", r, g, b, a); } else { value = 0; glXGetConfig (dpy, vip, GLX_BUFFER_SIZE, &value); printf (" GLX type: indexed (%d)\n", value); } # if 0 /* redundant */ if (!glXGetConfig (dpy, vip, GLX_X_VISUAL_TYPE_EXT, &value)) printf (" GLX class: %s\n", (value == GLX_TRUE_COLOR_EXT ? "TrueColor" : value == GLX_DIRECT_COLOR_EXT ? "DirectColor" : value == GLX_PSEUDO_COLOR_EXT ? "PseudoColor" : value == GLX_STATIC_COLOR_EXT ? "StaticColor" : value == GLX_GRAY_SCALE_EXT ? "Grayscale" : value == GLX_STATIC_GRAY_EXT ? "StaticGray" : "???")); # endif # ifdef GLX_VISUAL_CAVEAT_EXT if (!glXGetConfig (dpy, vip, GLX_VISUAL_CAVEAT_EXT, &value) && value != GLX_NONE_EXT) printf (" GLX rating: %s\n", (value == GLX_NONE_EXT ? "none" : value == GLX_SLOW_VISUAL_EXT ? "slow" : # ifdef GLX_NON_CONFORMANT_EXT value == GLX_NON_CONFORMANT_EXT ? "non-conformant" : # endif "???")); # endif if (!glXGetConfig (dpy, vip, GLX_DOUBLEBUFFER, &value)) printf (" GLX double-buffer: %s\n", (value ? "yes" : "no")); if (!glXGetConfig (dpy, vip, GLX_STEREO, &value) && value) printf (" GLX stereo: %s\n", (value ? "yes" : "no")); if (!glXGetConfig (dpy, vip, GLX_AUX_BUFFERS, &value) && value != 0) printf (" GLX aux buffers: %d\n", value); if (!glXGetConfig (dpy, vip, GLX_DEPTH_SIZE, &value)) printf (" GLX depth size: %d\n", value); if (!glXGetConfig (dpy, vip, GLX_STENCIL_SIZE, &value) && value != 0) printf (" GLX stencil size: %d\n", value); # if defined(GL_SAMPLE_BUFFERS) # define SB GL_SAMPLE_BUFFERS # define SM GL_SAMPLES # elif defined(GLX_SAMPLE_BUFFERS) # define SB GLX_SAMPLE_BUFFERS # define SM GLX_SAMPLES # elif defined(GLX_SAMPLE_BUFFERS_ARB) # define SB GLX_SAMPLE_BUFFERS_ARB # define SM GLX_SAMPLES_ARB # elif defined(GLX_SAMPLE_BUFFERS_SGIS) # define SB GLX_SAMPLE_BUFFERS_SGIS # define SM GLX_SAMPLES_SGIS # endif # ifdef SB if (!glXGetConfig (dpy, vip, SB, &value) && value != 0) { int bufs = value; if (!glXGetConfig (dpy, vip, SM, &value)) printf (" GLX multisample: %d, %d\n", bufs, value); } # endif /* SB */ if (!glXGetConfig (dpy, vip, GLX_TRANSPARENT_TYPE_EXT, &value) && value != GLX_NONE_EXT) { if (value == GLX_NONE_EXT) printf (" GLX transparency: none\n"); else if (value == GLX_TRANSPARENT_INDEX_EXT) { if (!glXGetConfig (dpy, vip, GLX_TRANSPARENT_INDEX_VALUE_EXT,&value)) printf (" GLX transparency: indexed (%d)\n", value); } else if (value == GLX_TRANSPARENT_RGB_EXT) { int r=0, g=0, b=0, a=0; glXGetConfig (dpy, vip, GLX_TRANSPARENT_RED_VALUE_EXT, &r); glXGetConfig (dpy, vip, GLX_TRANSPARENT_GREEN_VALUE_EXT, &g); glXGetConfig (dpy, vip, GLX_TRANSPARENT_BLUE_VALUE_EXT, &b); glXGetConfig (dpy, vip, GLX_TRANSPARENT_ALPHA_VALUE_EXT, &a); printf (" GLX transparency: RGBA (%2d, %2d, %2d, %2d)\n", r, g, b, a); } } } #endif /* HAVE_GLX */ #ifdef HAVE_OVERLAY /* Added by jwz, 11-Nov-99 */ /* If the server's root window contains a SERVER_OVERLAY_VISUALS property, then that identifies the visuals which correspond to the video hardware's overlay planes. Windows created in these kinds of visuals may have transparent pixels that let other layers shine through. This might not be an X Consortium standard, but it turns out that SGI, HP, DEC, and IBM all use this same mechanism. So that's close enough for me. Documentation on the SERVER_OVERLAY_VISUALS property can be found at: http://www.hp.com/xwindow/sharedInfo/Whitepapers/Visuals/server_overlay_visuals.html */ struct overlay { CARD32 visual_id; CARD32 transparency; /* 0: none; 1: pixel; 2: mask */ CARD32 value; /* the transparent pixel */ CARD32 layer; /* -1: underlay; 0: normal; 1: popup; 2: overlay */ }; struct overlay_list { int count; struct overlay *list; }; static struct overlay_list *overlays = 0; static void find_overlay_info (dpy) Display *dpy; { int screen; Atom OVERLAY = XInternAtom (dpy, "SERVER_OVERLAY_VISUALS", False); overlays = (struct overlay_list *) calloc (sizeof (struct overlay_list), ScreenCount (dpy)); for (screen = 0; screen < ScreenCount (dpy); screen++) { Window window = RootWindow (dpy, screen); Atom actual_type; int actual_format; unsigned long nitems, bytes_after; struct overlay *data = 0; int result = XGetWindowProperty (dpy, window, OVERLAY, 0, (65536 / sizeof (long)), False, OVERLAY, &actual_type, &actual_format, &nitems, &bytes_after, (unsigned char **) &data); if (result == Success && actual_type == OVERLAY && actual_format == 32 && nitems > 0) { overlays[screen].count = (nitems / (sizeof(struct overlay) / sizeof(CARD32))); overlays[screen].list = data; } else if (data) XFree((char *) data); } } static void print_overlay_visual_info (vip) XVisualInfo *vip; { int i; int vis = vip->visualid; int scr = vip->screen; if (!overlays) return; for (i = 0; i < overlays[scr].count; i++) if (vis == overlays[scr].list[i].visual_id) { struct overlay *ov = &overlays[scr].list[i]; printf (" Overlay info: layer %ld (%s), ", (long) ov->layer, (ov->layer == -1 ? "underlay" : ov->layer == 0 ? "normal" : ov->layer == 1 ? "popup" : ov->layer == 2 ? "overlay" : "???")); if (ov->transparency == 1) printf ("transparent pixel %lu\n", (unsigned long) ov->value); else if (ov->transparency == 2) printf ("transparent mask 0x%x\n", (unsigned long) ov->value); else printf ("opaque\n"); } } #endif /* HAVE_OVERLAY */ void print_extension_info (dpy) Display *dpy; { int n = 0; char **extlist = XListExtensions (dpy, &n); printf ("number of extensions: %d\n", n); if (extlist) { register int i; int opcode, event, error; qsort(extlist, n, sizeof(char *), StrCmp); for (i = 0; i < n; i++) { if (!queryExtensions) { printf (" %s\n", extlist[i]); continue; } XQueryExtension(dpy, extlist[i], &opcode, &event, &error); printf (" %s (opcode: %d", extlist[i], opcode); if (event) printf (", base event: %d", event); if (error) printf (", base error: %d", error); printf(")\n"); } /* do not free, Xlib can depend on contents being unaltered */ /* XFreeExtensionList (extlist); */ } } void print_display_info (dpy) Display *dpy; { char dummybuf[40]; char *cp; int minkeycode, maxkeycode; int i, n; long req_size; XPixmapFormatValues *pmf; Window focuswin; int focusrevert; printf ("name of display: %s\n", DisplayString (dpy)); printf ("version number: %d.%d\n", ProtocolVersion (dpy), ProtocolRevision (dpy)); printf ("vendor string: %s\n", ServerVendor (dpy)); printf ("vendor release number: %d\n", VendorRelease (dpy)); #ifdef HAVE_GLX print_glx_versions (dpy); #endif /* HAVE_GLX */ req_size = XExtendedMaxRequestSize (dpy); if (!req_size) req_size = XMaxRequestSize (dpy); printf ("maximum request size: %ld bytes\n", req_size * 4); printf ("motion buffer size: %d\n", XDisplayMotionBufferSize (dpy)); switch (BitmapBitOrder (dpy)) { case LSBFirst: cp = "LSBFirst"; break; case MSBFirst: cp = "MSBFirst"; break; default: sprintf (dummybuf, "unknown order %d", BitmapBitOrder (dpy)); cp = dummybuf; break; } printf ("bitmap unit, bit order, padding: %d, %s, %d\n", BitmapUnit (dpy), cp, BitmapPad (dpy)); switch (ImageByteOrder (dpy)) { case LSBFirst: cp = "LSBFirst"; break; case MSBFirst: cp = "MSBFirst"; break; default: sprintf (dummybuf, "unknown order %d", ImageByteOrder (dpy)); cp = dummybuf; break; } printf ("image byte order: %s\n", cp); pmf = XListPixmapFormats (dpy, &n); printf ("number of supported pixmap formats: %d\n", n); if (pmf) { printf ("supported pixmap formats:\n"); for (i = 0; i < n; i++) { printf (" depth %d, bits_per_pixel %d, scanline_pad %d\n", pmf[i].depth, pmf[i].bits_per_pixel, pmf[i].scanline_pad); } XFree ((char *) pmf); } /* * when we get interfaces to the PixmapFormat stuff, insert code here */ XDisplayKeycodes (dpy, &minkeycode, &maxkeycode); printf ("keycode range: minimum %d, maximum %d\n", minkeycode, maxkeycode); XGetInputFocus (dpy, &focuswin, &focusrevert); printf ("focus: "); switch (focuswin) { case PointerRoot: printf ("PointerRoot\n"); break; case None: printf ("None\n"); break; default: printf("window 0x%lx, revert to ", focuswin); switch (focusrevert) { case RevertToParent: printf ("Parent\n"); break; case RevertToNone: printf ("None\n"); break; case RevertToPointerRoot: printf ("PointerRoot\n"); break; default: /* should not happen */ printf ("%d\n", focusrevert); break; } break; } print_extension_info (dpy); printf ("default screen number: %d\n", DefaultScreen (dpy)); printf ("number of screens: %d\n", ScreenCount (dpy)); } void print_visual_info (vip) XVisualInfo *vip; { char errorbuf[40]; /* for sprintfing into */ char *class = NULL; /* for printing */ switch (vip->class) { case StaticGray: class = "StaticGray"; break; case GrayScale: class = "GrayScale"; break; case StaticColor: class = "StaticColor"; break; case PseudoColor: class = "PseudoColor"; break; case TrueColor: class = "TrueColor"; break; case DirectColor: class = "DirectColor"; break; default: sprintf (errorbuf, "unknown class %d", vip->class); class = errorbuf; break; } printf (" visual:\n"); printf (" visual id: 0x%lx\n", vip->visualid); printf (" class: %s\n", class); printf (" depth: %d plane%s\n", vip->depth, vip->depth == 1 ? "" : "s"); if (vip->class == TrueColor || vip->class == DirectColor) printf (" available colormap entries: %d per subfield\n", vip->colormap_size); else printf (" available colormap entries: %d\n", vip->colormap_size); printf (" red, green, blue masks: 0x%lx, 0x%lx, 0x%lx\n", vip->red_mask, vip->green_mask, vip->blue_mask); printf (" significant bits in color specification: %d bits\n", vip->bits_per_rgb); } void print_screen_info (dpy, scr) Display *dpy; int scr; { Screen *s = ScreenOfDisplay (dpy, scr); /* opaque structure */ XVisualInfo viproto; /* fill in for getting info */ XVisualInfo *vip; /* retured info */ int nvi; /* number of elements returned */ int i; /* temp variable: iterator */ char eventbuf[80]; /* want 79 chars per line + nul */ static char *yes = "YES", *no = "NO", *when = "WHEN MAPPED"; double xres, yres; int ndepths = 0, *depths = NULL; unsigned int width, height; /* * there are 2.54 centimeters to an inch; so there are 25.4 millimeters. * * dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch)) * = N pixels / (M inch / 25.4) * = N * 25.4 pixels / M inch */ xres = ((((double) DisplayWidth(dpy,scr)) * 25.4) / ((double) DisplayWidthMM(dpy,scr))); yres = ((((double) DisplayHeight(dpy,scr)) * 25.4) / ((double) DisplayHeightMM(dpy,scr))); printf ("\n"); printf ("screen #%d:\n", scr); printf (" dimensions: %dx%d pixels (%dx%d millimeters)\n", DisplayWidth (dpy, scr), DisplayHeight (dpy, scr), DisplayWidthMM(dpy, scr), DisplayHeightMM (dpy, scr)); printf (" resolution: %dx%d dots per inch\n", (int) (xres + 0.5), (int) (yres + 0.5)); depths = XListDepths (dpy, scr, &ndepths); if (!depths) ndepths = 0; printf (" depths (%d): ", ndepths); for (i = 0; i < ndepths; i++) { printf ("%d", depths[i]); if (i < ndepths - 1) { putchar (','); putchar (' '); } } putchar ('\n'); if (depths) XFree ((char *) depths); printf (" root window id: 0x%lx\n", RootWindow (dpy, scr)); printf (" depth of root window: %d plane%s\n", DisplayPlanes (dpy, scr), DisplayPlanes (dpy, scr) == 1 ? "" : "s"); printf (" number of colormaps: minimum %d, maximum %d\n", MinCmapsOfScreen(s), MaxCmapsOfScreen(s)); printf (" default colormap: 0x%lx\n", DefaultColormap (dpy, scr)); printf (" default number of colormap cells: %d\n", DisplayCells (dpy, scr)); printf (" preallocated pixels: black %d, white %d\n", BlackPixel (dpy, scr), WhitePixel (dpy, scr)); printf (" options: backing-store %s, save-unders %s\n", (DoesBackingStore (s) == NotUseful) ? no : ((DoesBackingStore (s) == Always) ? yes : when), DoesSaveUnders (s) ? yes : no); XQueryBestSize (dpy, CursorShape, RootWindow (dpy, scr), 65535, 65535, &width, &height); if (width == 65535 && height == 65535) printf (" largest cursor: unlimited\n"); else printf (" largest cursor: %dx%d\n", width, height); printf (" current input event mask: 0x%lx\n", EventMaskOfScreen (s)); (void) print_event_mask (eventbuf, 79, 4, EventMaskOfScreen (s)); nvi = 0; viproto.screen = scr; vip = XGetVisualInfo (dpy, VisualScreenMask, &viproto, &nvi); printf (" number of visuals: %d\n", nvi); printf (" default visual id: 0x%lx\n", XVisualIDFromVisual (DefaultVisual (dpy, scr))); for (i = 0; i < nvi; i++) { print_visual_info (vip+i); #ifdef HAVE_OVERLAY print_overlay_visual_info (vip+i); #endif /* HAVE_OVERLAY */ #ifdef HAVE_GLX print_glx_visual_info (dpy, vip+i); #endif /* HAVE_GLX */ } if (vip) XFree ((char *) vip); } /* * The following routine prints out an event mask, wrapping events at nice * boundaries. */ #define MASK_NAME_WIDTH 25 static struct _event_table { char *name; long value; } event_table[] = { { "KeyPressMask ", KeyPressMask }, { "KeyReleaseMask ", KeyReleaseMask }, { "ButtonPressMask ", ButtonPressMask }, { "ButtonReleaseMask ", ButtonReleaseMask }, { "EnterWindowMask ", EnterWindowMask }, { "LeaveWindowMask ", LeaveWindowMask }, { "PointerMotionMask ", PointerMotionMask }, { "PointerMotionHintMask ", PointerMotionHintMask }, { "Button1MotionMask ", Button1MotionMask }, { "Button2MotionMask ", Button2MotionMask }, { "Button3MotionMask ", Button3MotionMask }, { "Button4MotionMask ", Button4MotionMask }, { "Button5MotionMask ", Button5MotionMask }, { "ButtonMotionMask ", ButtonMotionMask }, { "KeymapStateMask ", KeymapStateMask }, { "ExposureMask ", ExposureMask }, { "VisibilityChangeMask ", VisibilityChangeMask }, { "StructureNotifyMask ", StructureNotifyMask }, { "ResizeRedirectMask ", ResizeRedirectMask }, { "SubstructureNotifyMask ", SubstructureNotifyMask }, { "SubstructureRedirectMask ", SubstructureRedirectMask }, { "FocusChangeMask ", FocusChangeMask }, { "PropertyChangeMask ", PropertyChangeMask }, { "ColormapChangeMask ", ColormapChangeMask }, { "OwnerGrabButtonMask ", OwnerGrabButtonMask }, { NULL, 0 }}; int print_event_mask (buf, lastcol, indent, mask) char *buf; /* string to write into */ int lastcol; /* strlen(buf)+1 */ int indent; /* amount by which to indent */ long mask; /* event mask */ { struct _event_table *etp; int len; int bitsfound = 0; buf[0] = buf[lastcol] = '\0'; /* just in case */ #define INDENT() { register int i; len = indent; \ for (i = 0; i < indent; i++) buf[i] = ' '; } INDENT (); for (etp = event_table; etp->name; etp++) { if (mask & etp->value) { if (len + MASK_NAME_WIDTH > lastcol) { puts (buf); INDENT (); } strcpy (buf+len, etp->name); len += MASK_NAME_WIDTH; bitsfound++; } } if (bitsfound) puts (buf); #undef INDENT return (bitsfound); } void print_standard_extension_info(dpy, extname, majorrev, minorrev) Display *dpy; char *extname; int majorrev, minorrev; { int opcode, event, error; printf("%s version %d.%d ", extname, majorrev, minorrev); XQueryExtension(dpy, extname, &opcode, &event, &error); printf ("opcode: %d", opcode); if (event) printf (", base event: %d", event); if (error) printf (", base error: %d", error); printf("\n"); } int print_multibuf_info(dpy, extname) Display *dpy; char *extname; { int i, j; /* temp variable: iterator */ int nmono, nstereo; /* count */ XmbufBufferInfo *mono_info = NULL, *stereo_info = NULL; /* arrays */ static char *fmt = " visual id, max buffers, depth: 0x%lx, %d, %d\n"; int scr = 0; int majorrev, minorrev; if (!XmbufGetVersion(dpy, &majorrev, &minorrev)) return 0; print_standard_extension_info(dpy, extname, majorrev, minorrev); for (i = 0; i < ScreenCount (dpy); i++) { if (!XmbufGetScreenInfo (dpy, RootWindow(dpy, scr), &nmono, &mono_info, &nstereo, &stereo_info)) { fprintf (stderr, "%s: unable to get multibuffer info for screen %d\n", ProgramName, scr); } else { printf (" screen %d number of mono multibuffer types: %d\n", i, nmono); for (j = 0; j < nmono; j++) { printf (fmt, mono_info[j].visualid, mono_info[j].max_buffers, mono_info[j].depth); } printf (" number of stereo multibuffer types: %d\n", nstereo); for (j = 0; j < nstereo; j++) { printf (fmt, stereo_info[j].visualid, stereo_info[j].max_buffers, stereo_info[j].depth); } if (mono_info) XFree ((char *) mono_info); if (stereo_info) XFree ((char *) stereo_info); } } return 1; } /* end print_multibuf_info */ /* XIE stuff */ #ifdef HAVE_XIE char *subset_names[] = { NULL, "FULL", "DIS" }; char *align_names[] = { NULL, "Alignable", "Arbitrary" }; char *group_names[] = { /* 0 */ "Default", /* 2 */ "ColorAlloc", /* 4 */ "Constrain", /* 6 */ "ConvertFromRGB", /* 8 */ "ConvertToRGB", /* 10 */ "Convolve", /* 12 */ "Decode", /* 14 */ "Dither", /* 16 */ "Encode", /* 18 */ "Gamut", /* 20 */ "Geometry", /* 22 */ "Histogram", /* 24 */ "WhiteAdjust" }; int print_xie_info(dpy, extname) Display *dpy; char *extname; { XieExtensionInfo *xieInfo; int i; int ntechs; XieTechnique *techs; XieTechniqueGroup prevGroup; if (!XieInitialize(dpy, &xieInfo )) return 0; print_standard_extension_info(dpy, extname, xieInfo->server_major_rev, xieInfo->server_minor_rev); printf(" service class: %s\n", subset_names[xieInfo->service_class]); printf(" alignment: %s\n", align_names[xieInfo->alignment]); printf(" uncnst_mantissa: %d\n", xieInfo->uncnst_mantissa); printf(" uncnst_min_exp: %d\n", xieInfo->uncnst_min_exp); printf(" uncnst_max_exp: %d\n", xieInfo->uncnst_max_exp); printf(" cnst_levels:"); for (i = 0; i < xieInfo->n_cnst_levels; i++) printf(" %d", xieInfo->cnst_levels[i]); printf("\n"); if (!XieQueryTechniques(dpy, xieValAll, &ntechs, &techs)) return 1; prevGroup = -1; for (i = 0; i < ntechs; i++) { if (techs[i].group != prevGroup) { printf(" technique group: %s\n", group_names[techs[i].group >> 1]); prevGroup = techs[i].group; } printf(" %s\tspeed: %d needs_param: %s number: %d\n", techs[i].name, techs[i].speed, (techs[i].needs_param ? "True " : "False"), techs[i].number); } return 1; } /* end print_xie_info */ #endif /* HAVE_XIE */ #ifdef HAVE_XTEST int print_xtest_info(dpy, extname) Display *dpy; char *extname; { int majorrev, minorrev, foo; if (!XTestQueryExtension(dpy, &foo, &foo, &majorrev, &minorrev)) return 0; print_standard_extension_info(dpy, extname, majorrev, minorrev); return 1; } #endif /* HAVE_XTEST */ #ifdef HAVE_XSYNC int print_sync_info(dpy, extname) Display *dpy; char *extname; { int majorrev, minorrev; XSyncSystemCounter *syscounters; int ncounters, i; if (!XSyncInitialize(dpy, &majorrev, &minorrev)) return 0; print_standard_extension_info(dpy, extname, majorrev, minorrev); syscounters = XSyncListSystemCounters(dpy, &ncounters); printf(" system counters: %d\n", ncounters); for (i = 0; i < ncounters; i++) { printf(" %s id: 0x%08x resolution_lo: %d resolution_hi: %d\n", syscounters[i].name, syscounters[i].counter, XSyncValueLow32(syscounters[i].resolution), XSyncValueHigh32(syscounters[i].resolution)); } XSyncFreeSystemCounterList(syscounters); return 1; } #endif /* HAVE_XSYNC */ int print_shape_info(dpy, extname) Display *dpy; char *extname; { int majorrev, minorrev; if (!XShapeQueryVersion(dpy, &majorrev, &minorrev)) return 0; print_standard_extension_info(dpy, extname, majorrev, minorrev); return 1; } #ifdef MITSHM int print_mitshm_info(dpy, extname) Display *dpy; char *extname; { int majorrev, minorrev; Bool sharedPixmaps; if (!XShmQueryVersion(dpy, &majorrev, &minorrev, &sharedPixmaps)) return 0; print_standard_extension_info(dpy, extname, majorrev, minorrev); printf(" shared pixmaps: "); if (sharedPixmaps) { int format = XShmPixmapFormat(dpy); printf("yes, format: %d\n", format); } else { printf("no\n"); } return 1; } #endif /* MITSHM */ #ifdef HAVE_XDBE int print_dbe_info(dpy, extname) Display *dpy; char *extname; { int majorrev, minorrev; XdbeScreenVisualInfo *svi; int numscreens = 0; int iscrn, ivis; if (!XdbeQueryExtension(dpy, &majorrev, &minorrev)) return 0; print_standard_extension_info(dpy, extname, majorrev, minorrev); svi = XdbeGetVisualInfo(dpy, (Drawable *)NULL, &numscreens); for (iscrn = 0; iscrn < numscreens; iscrn++) { printf(" Double-buffered visuals on screen %d\n", iscrn); for (ivis = 0; ivis < svi[iscrn].count; ivis++) { printf(" visual id 0x%lx depth %d perflevel %d\n", svi[iscrn].visinfo[ivis].visual, svi[iscrn].visinfo[ivis].depth, svi[iscrn].visinfo[ivis].perflevel); } } XdbeFreeVisualInfo(svi); return 1; } #endif /* HAVE_XDBE */ #ifdef HAVE_XRECORD int print_record_info(dpy, extname) Display *dpy; char *extname; { int majorrev, minorrev; if (!XRecordQueryVersion(dpy, &majorrev, &minorrev)) return 0; print_standard_extension_info(dpy, extname, majorrev, minorrev); return 1; } #endif /* HAVE_XRECORD */ /* utilities to manage the list of recognized extensions */ typedef int (*ExtensionPrintFunc)( #if NeedFunctionPrototypes Display *, char * #endif ); typedef struct { char *extname; ExtensionPrintFunc printfunc; Bool printit; } ExtensionPrintInfo; ExtensionPrintInfo known_extensions[] = { #ifdef MITSHM {"MIT-SHM", print_mitshm_info, False}, #endif /* MITSHM */ {MULTIBUFFER_PROTOCOL_NAME, print_multibuf_info, False}, {"SHAPE", print_shape_info, False}, #ifdef HAVE_XSYNC {SYNC_NAME, print_sync_info, False}, #endif /* HAVE_XSYNC */ #ifdef HAVE_XIE {xieExtName, print_xie_info, False}, #endif /* HAVE_XIE */ #ifdef HAVE_XTEST {XTestExtensionName, print_xtest_info, False}, #endif /* HAVE_XTEST */ #ifdef HAVE_XDBE {"DOUBLE-BUFFER", print_dbe_info, False}, #endif /* HAVE_XDBE */ #ifdef HAVE_XRECORD {"RECORD", print_record_info, False} #endif /* HAVE_XRECORD */ /* add new extensions here */ /* wish list: PEX XKB LBX */ }; int num_known_extensions = sizeof known_extensions / sizeof known_extensions[0]; void print_known_extensions(f) FILE *f; { int i; for (i = 0; i < num_known_extensions; i++) { fprintf(f, "%s ", known_extensions[i].extname); } } void mark_extension_for_printing(extname) char *extname; { int i; if (strcmp(extname, "all") == 0) { for (i = 0; i < num_known_extensions; i++) known_extensions[i].printit = True; } else { for (i = 0; i < num_known_extensions; i++) { if (strcmp(extname, known_extensions[i].extname) == 0) { known_extensions[i].printit = True; return; } } printf("%s extension not supported by %s\n", extname, ProgramName); } } void print_marked_extensions(dpy) Display *dpy; { int i; for (i = 0; i < num_known_extensions; i++) { if (known_extensions[i].printit) { printf("\n"); if (! (*known_extensions[i].printfunc)(dpy, known_extensions[i].extname)) { printf("%s extension not supported by server\n", known_extensions[i].extname); } } } } static void usage () { fprintf (stderr, "usage: %s [options]\n", ProgramName); fprintf (stderr, "-display displayname\tserver to query\n"); fprintf (stderr, "-queryExtensions\tprint info returned by XQueryExtension\n"); fprintf (stderr, "-ext all\t\tprint detailed info for all supported extensions\n"); fprintf (stderr, "-ext extension-name\tprint detailed info for extension-name if one of:\n "); print_known_extensions(stderr); fprintf (stderr, "\n"); exit (1); } int main (argc, argv) int argc; char *argv[]; { Display *dpy; /* X connection */ char *displayname = NULL; /* server to contact */ int i; /* temp variable: iterator */ Bool multibuf = False; int mbuf_event_base, mbuf_error_base; ProgramName = argv[0]; for (i = 1; i < argc; i++) { char *arg = argv[i]; int len = strlen(arg); if (!strncmp("-display", arg, len)) { if (++i >= argc) usage (); displayname = argv[i]; } else if (!strncmp("-queryExtensions", arg, len)) { queryExtensions = True; } else if (!strncmp("-ext", arg, len)) { if (++i >= argc) usage (); mark_extension_for_printing(argv[i]); } else usage (); } dpy = XOpenDisplay (displayname); if (!dpy) { fprintf (stderr, "%s: unable to open display \"%s\".\n", ProgramName, XDisplayName (displayname)); exit (1); } #ifdef HAVE_OVERLAY find_overlay_info (dpy); #endif /* HAVE_OVERLAY */ print_display_info (dpy); for (i = 0; i < ScreenCount (dpy); i++) { print_screen_info (dpy, i); } print_marked_extensions(dpy); XCloseDisplay (dpy); exit (0); } xscreensaver-5.15/driver/xscreensaver-command.c000066400000000000000000000350761164314150500217520ustar00rootroot00000000000000/* xscreensaver-command, Copyright (c) 1991-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif /* #include / * for CARD32 */ #include #include #include /* for XGetClassHint() */ #include #include /* only needed to get through xscreensaver.h */ /* You might think that to read an array of 32-bit quantities out of a server-side property, you would pass an array of 32-bit data quantities into XGetWindowProperty(). You would be wrong. You have to use an array of longs, even if long is 64 bits (using 32 of each 64.) */ typedef long PROP32; #include "remote.h" #include "version.h" #ifdef _VROOT_H_ ERROR! you must not include vroot.h in this file #endif char *progname; Atom XA_VROOT; Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_RESPONSE; Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO, XA_EXIT; Atom XA_BLANK, XA_LOCK; static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV; static Atom XA_RESTART, XA_PREFS, XA_THROTTLE, XA_UNTHROTTLE; static char *screensaver_version; # ifdef __GNUC__ __extension__ /* don't warn about "string length is greater than the length ISO C89 compilers are required to support" in the usage string... */ # endif static char *usage = "\n\ usage: %s -