From 0b7989a1c28e2055a9ee7c92b9ccbe4130a84d2a Mon Sep 17 00:00:00 2001 From: tanya Date: Mon, 9 Aug 2010 15:27:23 +0300 Subject: [PATCH] began for maemo --- xscreensaver/INSTALL | 183 + xscreensaver/Makefile.in | 381 + xscreensaver/OSX/AppController.h | 21 + xscreensaver/OSX/AppController.m | 200 + xscreensaver/OSX/English.lproj/InfoPlist.strings | Bin 0 -> 92 bytes .../OSX/English.lproj/MainMenu.nib/classes.nib | 9 + .../OSX/English.lproj/MainMenu.nib/info.nib | 21 + .../English.lproj/MainMenu.nib/keyedobjects.nib | Bin 0 -> 11645 bytes xscreensaver/OSX/InvertedSlider.h | 20 + xscreensaver/OSX/InvertedSlider.m | 102 + xscreensaver/OSX/Makefile | 120 + xscreensaver/OSX/PrefsReader.h | 39 + xscreensaver/OSX/PrefsReader.m | 283 + xscreensaver/OSX/README | 10 + xscreensaver/OSX/SaverTester.plist | 30 + xscreensaver/OSX/XScreenSaver.icns | Bin 0 -> 39163 bytes xscreensaver/OSX/XScreenSaver.plist | 30 + xscreensaver/OSX/XScreenSaverConfigSheet.h | 38 + xscreensaver/OSX/XScreenSaverConfigSheet.m | 1778 ++ xscreensaver/OSX/XScreenSaverDMG.icns | Bin 0 -> 35120 bytes xscreensaver/OSX/XScreenSaverGLView.h | 26 + xscreensaver/OSX/XScreenSaverGLView.m | 266 + xscreensaver/OSX/XScreenSaverSubclass.m | 33 + xscreensaver/OSX/XScreenSaverView.h | 42 + xscreensaver/OSX/XScreenSaverView.m | 633 + xscreensaver/OSX/bindist-DS_Store | Bin 0 -> 12292 bytes xscreensaver/OSX/bindist.rtf | 49 + xscreensaver/OSX/jwxyz-timers.h | 29 + xscreensaver/OSX/jwxyz-timers.m | 383 + xscreensaver/OSX/jwxyz.h | 700 + xscreensaver/OSX/jwxyz.m | 2712 ++ xscreensaver/OSX/main.m | 17 + xscreensaver/OSX/osxgrabscreen.m | 228 + xscreensaver/OSX/update-info-plist.pl | 272 + xscreensaver/OSX/xscreensaver_Prefix.pch | 15 + xscreensaver/README | 1284 + xscreensaver/README.VMS | 57 + xscreensaver/README.hacking | 163 + xscreensaver/aclocal.m4 | 440 + xscreensaver/config.guess | 1516 + xscreensaver/config.h-vms | 284 + xscreensaver/config.h.in | 485 + xscreensaver/config.sub | 1626 ++ xscreensaver/configure |22095 +++++++++++++++ xscreensaver/configure.in | 4242 +++ xscreensaver/debian/README.gl | 31 + xscreensaver/debian/changelog | 1208 + xscreensaver/debian/compat | 1 + xscreensaver/debian/control | 89 + xscreensaver/debian/copyright | 70 + .../10_jwz-xscreensaver-randr-patch-3.patch | 4353 +++ .../debian/patches/20_hacks_Makefile.patch | 13 + .../patches/21_driver_xscreensaver-getimage-video | 22 + xscreensaver/debian/patches/22_hacks_barcode.patch | 57 + .../debian/patches/23_hacks_glx_glsnake.patch | 49 + .../debian/patches/30_hacks_xanalogtv.patch | 22 + .../patches/31_utils_images_logo-50-bad-xpm.patch | 123 + .../50_driver_screensaver-properties-desktop.patch | 12 + .../debian/patches/52_driver_XScreenSaver.ad.in | 406 + .../debian/patches/53_XScreenSaver.ad.in.patch | 72 + xscreensaver/debian/patches/60_add-ant-hack.patch | 27 + .../patches/70_fix_local_screenlock_bypass.patch | 11 + xscreensaver/debian/patches/series | 25 + xscreensaver/debian/rules | 129 + .../screensavers-desktop-files/anemone.desktop | 11 + .../screensavers-desktop-files/anemotaxis.desktop | 11 + .../debian/screensavers-desktop-files/ant.desktop | 11 + .../screensavers-desktop-files/antinspect.desktop | 11 + .../screensavers-desktop-files/antmaze.desktop | 11 + .../antspotlight.desktop | 11 + .../screensavers-desktop-files/apollonian.desktop | 11 + .../screensavers-desktop-files/apple2.desktop | 11 + .../screensavers-desktop-files/atlantis.desktop | 11 + .../screensavers-desktop-files/attraction.desktop | 11 + .../screensavers-desktop-files/atunnel.desktop | 11 + .../screensavers-desktop-files/barcode.desktop | 11 + .../screensavers-desktop-files/blaster.desktop | 11 + .../screensavers-desktop-files/blinkbox.desktop | 11 + .../screensavers-desktop-files/blitspin.desktop | 11 + .../screensavers-desktop-files/blocktube.desktop | 11 + .../screensavers-desktop-files/boing.desktop | 11 + .../screensavers-desktop-files/bouboule.desktop | 11 + .../screensavers-desktop-files/bouncingcow.desktop | 11 + .../screensavers-desktop-files/boxed.desktop | 11 + .../screensavers-desktop-files/boxfit.desktop | 11 + .../screensavers-desktop-files/braid.desktop | 11 + .../debian/screensavers-desktop-files/bsod.desktop | 11 + .../screensavers-desktop-files/bubble3d.desktop | 11 + .../screensavers-desktop-files/bubbles.desktop | 11 + .../screensavers-desktop-files/bumps.desktop | 11 + .../debian/screensavers-desktop-files/cage.desktop | 11 + .../screensavers-desktop-files/carousel.desktop | 11 + .../screensavers-desktop-files/ccurve.desktop | 11 + .../screensavers-desktop-files/celtic.desktop | 11 + .../screensavers-desktop-files/circuit.desktop | 11 + .../screensavers-desktop-files/cloudlife.desktop | 11 + .../screensavers-desktop-files/compass.desktop | 11 + .../screensavers-desktop-files/coral.desktop | 11 + .../screensavers-desktop-files/crackberg.desktop | 11 + .../screensavers-desktop-files/critical.desktop | 11 + .../screensavers-desktop-files/crystal.desktop | 11 + .../screensavers-desktop-files/cube21.desktop | 11 + .../screensavers-desktop-files/cubenetic.desktop | 11 + .../screensavers-desktop-files/cubestorm.desktop | 11 + .../screensavers-desktop-files/cubicgrid.desktop | 11 + .../screensavers-desktop-files/cynosure.desktop | 11 + .../screensavers-desktop-files/dangerball.desktop | 11 + .../screensavers-desktop-files/decayscreen.desktop | 11 + .../debian/screensavers-desktop-files/deco.desktop | 11 + .../screensavers-desktop-files/deluxe.desktop | 11 + .../screensavers-desktop-files/demon.desktop | 11 + .../screensavers-desktop-files/discrete.desktop | 11 + .../screensavers-desktop-files/distort.desktop | 11 + .../screensavers-desktop-files/drift.desktop | 11 + .../screensavers-desktop-files/endgame.desktop | 11 + .../screensavers-desktop-files/engine.desktop | 11 + .../screensavers-desktop-files/epicycle.desktop | 11 + .../screensavers-desktop-files/eruption.desktop | 11 + .../screensavers-desktop-files/euler2d.desktop | 11 + .../screensavers-desktop-files/extrusion.desktop | 11 + .../screensavers-desktop-files/fadeplot.desktop | 11 + .../screensavers-desktop-files/fiberlamp.desktop | 11 + .../screensavers-desktop-files/fireworkx.desktop | 11 + .../debian/screensavers-desktop-files/flag.desktop | 11 + .../screensavers-desktop-files/flame.desktop | 11 + .../screensavers-desktop-files/flipflop.desktop | 11 + .../flipscreen3d.desktop | 11 + .../screensavers-desktop-files/fliptext.desktop | 11 + .../debian/screensavers-desktop-files/flow.desktop | 11 + .../screensavers-desktop-files/fluidballs.desktop | 11 + .../screensavers-desktop-files/flurry.desktop | 11 + .../flyingtoasters.desktop | 11 + .../screensavers-desktop-files/fontglide.desktop | 11 + .../screensavers-desktop-files/forest.desktop | 11 + .../screensavers-desktop-files/fuzzyflakes.desktop | 11 + .../screensavers-desktop-files/galaxy.desktop | 11 + .../screensavers-desktop-files/gears.desktop | 11 + .../screensavers-desktop-files/gflux.desktop | 11 + .../screensavers-desktop-files/glblur.desktop | 11 + .../screensavers-desktop-files/glcells.desktop | 11 + .../screensavers-desktop-files/gleidescope.desktop | 11 + .../glforestfire.desktop | 11 + .../screensavers-desktop-files/glhanoi.desktop | 11 + .../screensavers-desktop-files/glknots.desktop | 11 + .../screensavers-desktop-files/glmatrix.desktop | 11 + .../screensavers-desktop-files/glplanet.desktop | 11 + .../screensavers-desktop-files/glschool.desktop | 11 + .../screensavers-desktop-files/glslideshow.desktop | 11 + .../screensavers-desktop-files/glsnake.desktop | 11 + .../screensavers-desktop-files/gltext.desktop | 11 + .../debian/screensavers-desktop-files/goop.desktop | 11 + .../debian/screensavers-desktop-files/grav.desktop | 11 + .../screensavers-desktop-files/greynetic.desktop | 11 + .../screensavers-desktop-files/halftone.desktop | 11 + .../debian/screensavers-desktop-files/halo.desktop | 11 + .../screensavers-desktop-files/helix.desktop | 11 + .../screensavers-desktop-files/hopalong.desktop | 11 + .../screensavers-desktop-files/hyperball.desktop | 11 + .../screensavers-desktop-files/hypercube.desktop | 11 + .../screensavers-desktop-files/hypertorus.desktop | 11 + .../screensavers-desktop-files/hypnowheel.desktop | 11 + .../debian/screensavers-desktop-files/ifs.desktop | 11 + .../screensavers-desktop-files/imsmap.desktop | 11 + .../interaggregate.desktop | 11 + .../interference.desktop | 11 + .../intermomentary.desktop | 11 + .../screensavers-desktop-files/jigglypuff.desktop | 11 + .../screensavers-desktop-files/jigsaw.desktop | 11 + .../screensavers-desktop-files/juggle.desktop | 11 + .../screensavers-desktop-files/juggler3d.desktop | 11 + .../screensavers-desktop-files/julia.desktop | 11 + .../kaleidescope.desktop | 11 + .../screensavers-desktop-files/klein.desktop | 11 + .../screensavers-desktop-files/kumppa.desktop | 11 + .../screensavers-desktop-files/lament.desktop | 11 + .../screensavers-desktop-files/laser.desktop | 11 + .../screensavers-desktop-files/lavalite.desktop | 11 + .../screensavers-desktop-files/lcdscrub.desktop | 11 + .../screensavers-desktop-files/lightning.desktop | 11 + .../debian/screensavers-desktop-files/lisa.desktop | 11 + .../screensavers-desktop-files/lissie.desktop | 11 + .../screensavers-desktop-files/lmorph.desktop | 11 + .../screensavers-desktop-files/lockward.desktop | 11 + .../debian/screensavers-desktop-files/loop.desktop | 11 + .../debian/screensavers-desktop-files/maze.desktop | 11 + .../screensavers-desktop-files/memscroller.desktop | 11 + .../screensavers-desktop-files/menger.desktop | 11 + .../screensavers-desktop-files/metaballs.desktop | 11 + .../screensavers-desktop-files/mirrorblob.desktop | 11 + .../screensavers-desktop-files/mismunch.desktop | 11 + .../screensavers-desktop-files/moebius.desktop | 11 + .../moebiusgears.desktop | 11 + .../screensavers-desktop-files/moire.desktop | 11 + .../screensavers-desktop-files/moire2.desktop | 11 + .../screensavers-desktop-files/molecule.desktop | 11 + .../screensavers-desktop-files/morph3d.desktop | 11 + .../screensavers-desktop-files/mountain.desktop | 11 + .../screensavers-desktop-files/munch.desktop | 11 + .../screensavers-desktop-files/nerverot.desktop | 11 + .../debian/screensavers-desktop-files/noof.desktop | 11 + .../screensavers-desktop-files/noseguy.desktop | 11 + .../screensavers-desktop-files/pacman.desktop | 11 + .../screensavers-desktop-files/pedal.desktop | 11 + .../screensavers-desktop-files/penetrate.desktop | 11 + .../screensavers-desktop-files/penrose.desktop | 11 + .../screensavers-desktop-files/petri.desktop | 11 + .../screensavers-desktop-files/phosphor.desktop | 11 + .../screensavers-desktop-files/piecewise.desktop | 11 + .../screensavers-desktop-files/pinion.desktop | 11 + .../screensavers-desktop-files/pipes.desktop | 11 + .../screensavers-desktop-files/polyhedra.desktop | 11 + .../screensavers-desktop-files/polyominoes.desktop | 11 + .../screensavers-desktop-files/polytopes.desktop | 11 + .../debian/screensavers-desktop-files/pong.desktop | 11 + .../screensavers-desktop-files/popsquares.desktop | 11 + .../screensavers-desktop-files/providence.desktop | 11 + .../screensavers-desktop-files/pulsar.desktop | 11 + .../debian/screensavers-desktop-files/pyro.desktop | 11 + .../debian/screensavers-desktop-files/qix.desktop | 11 + .../screensavers-desktop-files/queens.desktop | 11 + .../screensavers-desktop-files/rd-bomb.desktop | 11 + .../screensavers-desktop-files/ripples.desktop | 11 + .../screensavers-desktop-files/rocks.desktop | 11 + .../screensavers-desktop-files/rorschach.desktop | 11 + .../screensavers-desktop-files/rotor.desktop | 11 + .../screensavers-desktop-files/rotzoomer.desktop | 11 + .../screensavers-desktop-files/rubik.desktop | 11 + .../screensavers-desktop-files/sballs.desktop | 11 + .../screensavers-desktop-files/shadebobs.desktop | 11 + .../screensavers-desktop-files/sierpinski.desktop | 11 + .../sierpinski3d.desktop | 11 + .../screensavers-desktop-files/slidescreen.desktop | 11 + .../debian/screensavers-desktop-files/slip.desktop | 11 + .../screensavers-desktop-files/sonar.desktop | 11 + .../screensavers-desktop-files/speedmine.desktop | 11 + .../screensavers-desktop-files/sphere.desktop | 11 + .../spheremonics.desktop | 11 + .../screensavers-desktop-files/spiral.desktop | 11 + .../screensavers-desktop-files/spotlight.desktop | 11 + .../screensavers-desktop-files/sproingies.desktop | 11 + .../screensavers-desktop-files/squiral.desktop | 11 + .../screensavers-desktop-files/stairs.desktop | 11 + .../screensavers-desktop-files/starfish.desktop | 11 + .../screensavers-desktop-files/starwars.desktop | 11 + .../screensavers-desktop-files/stonerview.desktop | 11 + .../screensavers-desktop-files/strange.desktop | 11 + .../screensavers-desktop-files/substrate.desktop | 11 + .../superquadrics.desktop | 11 + .../screensavers-desktop-files/swirl.desktop | 11 + .../debian/screensavers-desktop-files/t3d.desktop | 11 + .../screensavers-desktop-files/tangram.desktop | 11 + .../screensavers-desktop-files/thornbird.desktop | 11 + .../screensavers-desktop-files/timetunnel.desktop | 11 + .../screensavers-desktop-files/topblock.desktop | 11 + .../screensavers-desktop-files/triangle.desktop | 11 + .../screensavers-desktop-files/truchet.desktop | 11 + .../screensavers-desktop-files/twang.desktop | 11 + .../screensavers-desktop-files/vermiculate.desktop | 11 + .../screensavers-desktop-files/vidwhacker.desktop | 11 + .../screensavers-desktop-files/vines.desktop | 11 + .../screensavers-desktop-files/voronoi.desktop | 11 + .../screensavers-desktop-files/wander.desktop | 11 + .../screensavers-desktop-files/webcollage.desktop | 11 + .../whirlwindwarp.desktop | 11 + .../screensavers-desktop-files/whirlygig.desktop | 11 + .../debian/screensavers-desktop-files/worm.desktop | 11 + .../screensavers-desktop-files/wormhole.desktop | 11 + .../screensavers-desktop-files/xanalogtv.desktop | 11 + .../screensavers-desktop-files/xflame.desktop | 11 + .../screensavers-desktop-files/xjack.desktop | 11 + .../screensavers-desktop-files/xlyap.desktop | 11 + .../screensavers-desktop-files/xmatrix.desktop | 11 + .../screensavers-desktop-files/xrayswarm.desktop | 11 + .../screensavers-desktop-files/xspirograph.desktop | 11 + .../screensavers-desktop-files/xteevee.desktop | 11 + .../debian/screensavers-desktop-files/zoom.desktop | 11 + xscreensaver/debian/watch | 3 + xscreensaver/debian/xscreensaver-data-extra.files | 481 + xscreensaver/debian/xscreensaver-data.files | 71 + xscreensaver/debian/xscreensaver-gl-extra.files | 132 + xscreensaver/debian/xscreensaver-gl.dirs | 1 + xscreensaver/debian/xscreensaver-gl.docs | 1 + xscreensaver/debian/xscreensaver-gl.files | 174 + xscreensaver/debian/xscreensaver-gl.postinst | 22 + xscreensaver/debian/xscreensaver-gl.postrm | 22 + xscreensaver/debian/xscreensaver.desktop | 8 + xscreensaver/debian/xscreensaver.docs | 1 + xscreensaver/debian/xscreensaver.files | 39 + xscreensaver/debian/xscreensaver.install | 2 + xscreensaver/debian/xscreensaver.menu | 27 + xscreensaver/debian/xscreensaver.pam | 6 + xscreensaver/debian/xscreensaver.postinst | 35 + xscreensaver/debian/xscreensaver.postrm | 26 + xscreensaver/debian/xscreensaver.svg | 983 + xscreensaver/driver/Makefile.in | 1001 + xscreensaver/driver/README | 6 + xscreensaver/driver/XScreenSaver-Xm.ad | 126 + xscreensaver/driver/XScreenSaver.ad.in | 553 + xscreensaver/driver/XScreenSaver_Xm_ad.h | 108 + xscreensaver/driver/XScreenSaver_ad.h | 420 + xscreensaver/driver/auth.h | 54 + xscreensaver/driver/compile_axp.com | 15 + xscreensaver/driver/compile_decc.com | 15 + xscreensaver/driver/demo-Gtk-conf.c | 2039 ++ xscreensaver/driver/demo-Gtk-conf.h | 31 + xscreensaver/driver/demo-Gtk-stubs.h | 86 + xscreensaver/driver/demo-Gtk-support.c | 219 + xscreensaver/driver/demo-Gtk-support.h | 61 + xscreensaver/driver/demo-Gtk-widgets.c | 1750 ++ xscreensaver/driver/demo-Gtk-widgets.h | 6 + xscreensaver/driver/demo-Gtk.c | 5111 ++++ xscreensaver/driver/demo-Xm-widgets.c | 907 + xscreensaver/driver/demo-Xm.c | 1875 ++ xscreensaver/driver/dpms.c | 256 + xscreensaver/driver/exec.c | 300 + xscreensaver/driver/exec.h | 21 + xscreensaver/driver/link_axp.com | 15 + xscreensaver/driver/link_decc.com | 15 + xscreensaver/driver/lock.c | 2096 ++ xscreensaver/driver/mlstring.c | 227 + xscreensaver/driver/mlstring.h | 57 + xscreensaver/driver/passwd-helper.c | 162 + xscreensaver/driver/passwd-kerberos.c | 247 + xscreensaver/driver/passwd-pam.c | 485 + xscreensaver/driver/passwd-pwent.c | 312 + xscreensaver/driver/passwd.c | 306 + xscreensaver/driver/pdf2jpeg.m | 132 + xscreensaver/driver/pdf2jpeg.man | 43 + xscreensaver/driver/prefs.c | 1636 ++ xscreensaver/driver/prefs.h | 35 + xscreensaver/driver/remote.c | 594 + xscreensaver/driver/remote.h | 24 + .../driver/screensaver-properties.desktop.in | 8 + xscreensaver/driver/setuid.c | 361 + xscreensaver/driver/splash.c | 853 + xscreensaver/driver/stderr.c | 508 + xscreensaver/driver/subprocs.c | 1357 + xscreensaver/driver/test-apm.c | 101 + xscreensaver/driver/test-fade.c | 123 + xscreensaver/driver/test-grab.c | 89 + xscreensaver/driver/test-mlstring.c | 312 + xscreensaver/driver/test-passwd.c | 305 + xscreensaver/driver/test-randr.c | 298 + xscreensaver/driver/test-uid.c | 209 + xscreensaver/driver/test-vp.c | 213 + xscreensaver/driver/test-xdpms.c | 160 + xscreensaver/driver/test-xinerama.c | 112 + xscreensaver/driver/timers.c | 1527 + xscreensaver/driver/types.h | 410 + xscreensaver/driver/vms-getpwnam.c | 129 + xscreensaver/driver/vms-hpwd.c | 75 + xscreensaver/driver/vms-pwd.h | 48 + xscreensaver/driver/vms-validate.c | 75 + xscreensaver/driver/vms_axp.opt | 5 + xscreensaver/driver/vms_axp_12.opt | 5 + xscreensaver/driver/vms_decc.opt | 5 + xscreensaver/driver/vms_decc_12.opt | 5 + xscreensaver/driver/windows.c | 2211 ++ xscreensaver/driver/xdpyinfo.c | 1085 + xscreensaver/driver/xscreensaver-command.c | 439 + xscreensaver/driver/xscreensaver-command.man | 272 + xscreensaver/driver/xscreensaver-demo.glade2 | 2708 ++ xscreensaver/driver/xscreensaver-demo.glade2p | 19 + xscreensaver/driver/xscreensaver-demo.man | 373 + xscreensaver/driver/xscreensaver-getimage-desktop | 207 + .../driver/xscreensaver-getimage-desktop.man | 55 + xscreensaver/driver/xscreensaver-getimage-file | 550 + xscreensaver/driver/xscreensaver-getimage-file.man | 49 + xscreensaver/driver/xscreensaver-getimage-video | 172 + .../driver/xscreensaver-getimage-video.man | 51 + xscreensaver/driver/xscreensaver-getimage.c | 1899 ++ xscreensaver/driver/xscreensaver-getimage.man | 70 + xscreensaver/driver/xscreensaver-text | 849 + xscreensaver/driver/xscreensaver-text.man | 81 + xscreensaver/driver/xscreensaver.c | 2406 ++ xscreensaver/driver/xscreensaver.h | 205 + xscreensaver/driver/xscreensaver.man | 1216 + xscreensaver/driver/xscreensaver.pam | 16 + xscreensaver/driver/xset.c | 305 + xscreensaver/hacks/Makefile.in | 2718 ++ xscreensaver/hacks/README | 6 + xscreensaver/hacks/abstractile.c | 1586 ++ xscreensaver/hacks/abstractile.man | 48 + xscreensaver/hacks/analogtv.c | 2200 ++ xscreensaver/hacks/analogtv.h | 299 + xscreensaver/hacks/anemone.c | 437 + xscreensaver/hacks/anemone.man | 71 + xscreensaver/hacks/anemotaxis.c | 752 + xscreensaver/hacks/anemotaxis.man | 75 + xscreensaver/hacks/ant.c | 1369 + xscreensaver/hacks/apollonian.c | 832 + xscreensaver/hacks/apollonian.man | 66 + xscreensaver/hacks/apple2-main.c | 2003 ++ xscreensaver/hacks/apple2.c | 848 + xscreensaver/hacks/apple2.h | 121 + xscreensaver/hacks/apple2.man | 202 + xscreensaver/hacks/asm6502.c | 2265 ++ xscreensaver/hacks/asm6502.h | 181 + xscreensaver/hacks/attraction.c | 1049 + xscreensaver/hacks/attraction.man | 223 + xscreensaver/hacks/automata.h | 64 + xscreensaver/hacks/barcode.c | 1983 ++ xscreensaver/hacks/barcode.man | 57 + xscreensaver/hacks/blaster.c | 1186 + xscreensaver/hacks/blaster.man | 61 + xscreensaver/hacks/blitspin.c | 315 + xscreensaver/hacks/blitspin.man | 92 + xscreensaver/hacks/bouboule.c | 846 + xscreensaver/hacks/bouboule.man | 76 + xscreensaver/hacks/boxfit.c | 556 + xscreensaver/hacks/boxfit.man | 98 + xscreensaver/hacks/braid.c | 466 + xscreensaver/hacks/braid.man | 65 + xscreensaver/hacks/bsod.c | 4010 +++ xscreensaver/hacks/bsod.man | 135 + xscreensaver/hacks/bubbles-default.c | 151 + xscreensaver/hacks/bubbles.c | 1437 + xscreensaver/hacks/bubbles.h | 224 + xscreensaver/hacks/bubbles.man | 136 + xscreensaver/hacks/bumps.c | 617 + xscreensaver/hacks/bumps.h | 135 + xscreensaver/hacks/bumps.man | 72 + xscreensaver/hacks/ccurve.c | 835 + xscreensaver/hacks/ccurve.man | 56 + xscreensaver/hacks/celtic.c | 1117 + xscreensaver/hacks/celtic.man | 60 + xscreensaver/hacks/cloudlife.c | 415 + xscreensaver/hacks/cloudlife.man | 85 + xscreensaver/hacks/compass.c | 973 + xscreensaver/hacks/compass.man | 53 + xscreensaver/hacks/compile_axp.com | 150 + xscreensaver/hacks/compile_decc.com | 150 + xscreensaver/hacks/config/README | 249 + xscreensaver/hacks/config/abstractile.xml | 29 + xscreensaver/hacks/config/anemone.xml | 48 + xscreensaver/hacks/config/anemotaxis.xml | 32 + xscreensaver/hacks/config/ant.xml | 54 + xscreensaver/hacks/config/antinspect.xml | 21 + xscreensaver/hacks/config/antmaze.xml | 19 + xscreensaver/hacks/config/antspotlight.xml | 22 + xscreensaver/hacks/config/apollonian.xml | 33 + xscreensaver/hacks/config/apple2.xml | 33 + xscreensaver/hacks/config/atlantis.xml | 41 + xscreensaver/hacks/config/attraction.xml | 84 + xscreensaver/hacks/config/atunnel.xml | 22 + xscreensaver/hacks/config/barcode.xml | 25 + xscreensaver/hacks/config/blaster.xml | 57 + xscreensaver/hacks/config/blinkbox.xml | 29 + xscreensaver/hacks/config/blitspin.xml | 32 + xscreensaver/hacks/config/blocktube.xml | 30 + xscreensaver/hacks/config/boing.xml | 44 + xscreensaver/hacks/config/bouboule.xml | 28 + xscreensaver/hacks/config/bouncingcow.xml | 28 + xscreensaver/hacks/config/boxed.xml | 46 + xscreensaver/hacks/config/boxfit.xml | 52 + xscreensaver/hacks/config/braid.xml | 31 + xscreensaver/hacks/config/bsod.xml | 54 + xscreensaver/hacks/config/bubble3d.xml | 21 + xscreensaver/hacks/config/bubbles.xml | 36 + xscreensaver/hacks/config/bumps.xml | 27 + xscreensaver/hacks/config/cage.xml | 21 + xscreensaver/hacks/config/carousel.xml | 47 + xscreensaver/hacks/config/ccurve.xml | 24 + xscreensaver/hacks/config/celtic.xml | 23 + xscreensaver/hacks/config/circuit.xml | 35 + xscreensaver/hacks/config/cloudlife.xml | 33 + xscreensaver/hacks/config/compass.xml | 20 + xscreensaver/hacks/config/coral.xml | 30 + xscreensaver/hacks/config/cosmos.xml | 12 + xscreensaver/hacks/config/crackberg.xml | 47 + xscreensaver/hacks/config/critical.xml | 22 + xscreensaver/hacks/config/crystal.xml | 40 + xscreensaver/hacks/config/cube21.xml | 61 + xscreensaver/hacks/config/cubenetic.xml | 53 + xscreensaver/hacks/config/cubestorm.xml | 37 + xscreensaver/hacks/config/cubicgrid.xml | 28 + xscreensaver/hacks/config/cwaves.xml | 27 + xscreensaver/hacks/config/cynosure.xml | 26 + xscreensaver/hacks/config/dangerball.xml | 32 + xscreensaver/hacks/config/decayscreen.xml | 40 + xscreensaver/hacks/config/deco.xml | 36 + xscreensaver/hacks/config/deluxe.xml | 34 + xscreensaver/hacks/config/demon.xml | 32 + xscreensaver/hacks/config/discrete.xml | 26 + xscreensaver/hacks/config/distort.xml | 47 + xscreensaver/hacks/config/dnalogo.xml | 21 + xscreensaver/hacks/config/drift.xml | 25 + xscreensaver/hacks/config/electricsheep.xml | 54 + xscreensaver/hacks/config/endgame.xml | 21 + xscreensaver/hacks/config/engine.xml | 38 + xscreensaver/hacks/config/epicycle.xml | 46 + xscreensaver/hacks/config/eruption.xml | 41 + xscreensaver/hacks/config/euler2d.xml | 39 + xscreensaver/hacks/config/extrusion.xml | 41 + xscreensaver/hacks/config/fadeplot.xml | 28 + xscreensaver/hacks/config/fiberlamp.xml | 20 + xscreensaver/hacks/config/fireflies.xml | 329 + xscreensaver/hacks/config/fireworkx.xml | 25 + xscreensaver/hacks/config/flag.xml | 34 + xscreensaver/hacks/config/flame.xml | 33 + xscreensaver/hacks/config/flipflop.xml | 33 + xscreensaver/hacks/config/flipscreen3d.xml | 24 + xscreensaver/hacks/config/fliptext.xml | 44 + xscreensaver/hacks/config/flow.xml | 39 + xscreensaver/hacks/config/fluidballs.xml | 44 + xscreensaver/hacks/config/flurry.xml | 28 + xscreensaver/hacks/config/flyingtoasters.xml | 34 + xscreensaver/hacks/config/fontglide.xml | 46 + xscreensaver/hacks/config/forest.xml | 21 + xscreensaver/hacks/config/fuzzyflakes.xml | 37 + xscreensaver/hacks/config/galaxy.xml | 31 + xscreensaver/hacks/config/gears.xml | 33 + xscreensaver/hacks/config/gflux.xml | 51 + xscreensaver/hacks/config/glblur.xml | 44 + xscreensaver/hacks/config/glcells.xml | 82 + xscreensaver/hacks/config/gleidescope.xml | 31 + xscreensaver/hacks/config/glforestfire.xml | 33 + xscreensaver/hacks/config/glhanoi.xml | 29 + xscreensaver/hacks/config/glknots.xml | 49 + xscreensaver/hacks/config/glmatrix.xml | 48 + xscreensaver/hacks/config/glplanet.xml | 40 + xscreensaver/hacks/config/glschool.xml | 32 + xscreensaver/hacks/config/glslideshow.xml | 46 + xscreensaver/hacks/config/glsnake.xml | 38 + xscreensaver/hacks/config/gltext.xml | 41 + xscreensaver/hacks/config/goban.xml | 16 + xscreensaver/hacks/config/goop.xml | 49 + xscreensaver/hacks/config/grav.xml | 28 + xscreensaver/hacks/config/greynetic.xml | 17 + xscreensaver/hacks/config/halftone.xml | 46 + xscreensaver/hacks/config/halo.xml | 32 + xscreensaver/hacks/config/helix.xml | 16 + xscreensaver/hacks/config/hopalong.xml | 50 + xscreensaver/hacks/config/hyperball.xml | 53 + xscreensaver/hacks/config/hypercube.xml | 63 + xscreensaver/hacks/config/hypertorus.xml | 108 + xscreensaver/hacks/config/hypnowheel.xml | 41 + xscreensaver/hacks/config/ifs.xml | 38 + xscreensaver/hacks/config/imsmap.xml | 39 + xscreensaver/hacks/config/interaggregate.xml | 24 + xscreensaver/hacks/config/interference.xml | 47 + xscreensaver/hacks/config/intermomentary.xml | 29 + xscreensaver/hacks/config/jigglypuff.xml | 91 + xscreensaver/hacks/config/jigsaw.xml | 29 + xscreensaver/hacks/config/juggle.xml | 41 + xscreensaver/hacks/config/juggler3d.xml | 35 + xscreensaver/hacks/config/julia.xml | 33 + xscreensaver/hacks/config/kaleidescope.xml | 34 + xscreensaver/hacks/config/klein.xml | 28 + xscreensaver/hacks/config/kumppa.xml | 26 + xscreensaver/hacks/config/lament.xml | 22 + xscreensaver/hacks/config/laser.xml | 29 + xscreensaver/hacks/config/lavalite.xml | 68 + xscreensaver/hacks/config/lcdscrub.xml | 39 + xscreensaver/hacks/config/lightning.xml | 21 + xscreensaver/hacks/config/lisa.xml | 33 + xscreensaver/hacks/config/lissie.xml | 31 + xscreensaver/hacks/config/lmorph.xml | 36 + xscreensaver/hacks/config/lockward.xml | 78 + xscreensaver/hacks/config/loop.xml | 28 + xscreensaver/hacks/config/m6502.xml | 26 + xscreensaver/hacks/config/maze.xml | 55 + xscreensaver/hacks/config/memscroller.xml | 28 + xscreensaver/hacks/config/menger.xml | 45 + xscreensaver/hacks/config/metaballs.xml | 39 + xscreensaver/hacks/config/mirrorblob.xml | 62 + xscreensaver/hacks/config/mismunch.xml | 31 + xscreensaver/hacks/config/moebius.xml | 22 + xscreensaver/hacks/config/moebiusgears.xml | 37 + xscreensaver/hacks/config/moire.xml | 30 + xscreensaver/hacks/config/moire2.xml | 26 + xscreensaver/hacks/config/molecule.xml | 58 + xscreensaver/hacks/config/morph3d.xml | 28 + xscreensaver/hacks/config/mountain.xml | 25 + xscreensaver/hacks/config/munch.xml | 39 + xscreensaver/hacks/config/nerverot.xml | 52 + xscreensaver/hacks/config/noof.xml | 19 + xscreensaver/hacks/config/noseguy.xml | 16 + xscreensaver/hacks/config/pacman.xml | 20 + xscreensaver/hacks/config/pedal.xml | 26 + xscreensaver/hacks/config/penetrate.xml | 26 + xscreensaver/hacks/config/penrose.xml | 42 + xscreensaver/hacks/config/petri.xml | 82 + xscreensaver/hacks/config/phosphor.xml | 38 + xscreensaver/hacks/config/piecewise.xml | 36 + xscreensaver/hacks/config/pinion.xml | 38 + xscreensaver/hacks/config/pipes.xml | 41 + xscreensaver/hacks/config/polyhedra.xml | 193 + xscreensaver/hacks/config/polyominoes.xml | 28 + xscreensaver/hacks/config/polytopes.xml | 110 + xscreensaver/hacks/config/pong.xml | 25 + xscreensaver/hacks/config/popsquares.xml | 33 + xscreensaver/hacks/config/providence.xml | 23 + xscreensaver/hacks/config/pulsar.xml | 39 + xscreensaver/hacks/config/pyro.xml | 30 + xscreensaver/hacks/config/qix.xml | 69 + xscreensaver/hacks/config/queens.xml | 24 + xscreensaver/hacks/config/rd-bomb.xml | 55 + xscreensaver/hacks/config/rdbomb.xml | 55 + xscreensaver/hacks/config/ripples.xml | 50 + xscreensaver/hacks/config/rocks.xml | 39 + xscreensaver/hacks/config/rorschach.xml | 29 + xscreensaver/hacks/config/rotor.xml | 31 + xscreensaver/hacks/config/rotzoomer.xml | 31 + xscreensaver/hacks/config/rubik.xml | 32 + xscreensaver/hacks/config/sballs.xml | 33 + xscreensaver/hacks/config/shadebobs.xml | 33 + xscreensaver/hacks/config/sierpinski.xml | 29 + xscreensaver/hacks/config/sierpinski3d.xml | 29 + xscreensaver/hacks/config/slidescreen.xml | 38 + xscreensaver/hacks/config/slip.xml | 34 + xscreensaver/hacks/config/sonar.xml | 49 + xscreensaver/hacks/config/speedmine.xml | 51 + xscreensaver/hacks/config/sphere.xml | 21 + xscreensaver/hacks/config/sphereeversion.xml | 18 + xscreensaver/hacks/config/spheremonics.xml | 48 + xscreensaver/hacks/config/spiral.xml | 28 + xscreensaver/hacks/config/spotlight.xml | 24 + xscreensaver/hacks/config/sproingies.xml | 27 + xscreensaver/hacks/config/squiral.xml | 39 + xscreensaver/hacks/config/stairs.xml | 19 + xscreensaver/hacks/config/starfish.xml | 37 + xscreensaver/hacks/config/starwars.xml | 64 + xscreensaver/hacks/config/stonerview.xml | 16 + xscreensaver/hacks/config/strange.xml | 22 + xscreensaver/hacks/config/substrate.xml | 34 + xscreensaver/hacks/config/superquadrics.xml | 32 + xscreensaver/hacks/config/swirl.xml | 26 + xscreensaver/hacks/config/t3d.xml | 44 + xscreensaver/hacks/config/tangram.xml | 36 + xscreensaver/hacks/config/thornbird.xml | 31 + xscreensaver/hacks/config/timetunnel.xml | 26 + xscreensaver/hacks/config/topblock.xml | 51 + xscreensaver/hacks/config/triangle.xml | 22 + xscreensaver/hacks/config/truchet.xml | 34 + xscreensaver/hacks/config/twang.xml | 44 + xscreensaver/hacks/config/vermiculate.xml | 15 + xscreensaver/hacks/config/vidwhacker.xml | 24 + xscreensaver/hacks/config/vines.xml | 21 + xscreensaver/hacks/config/voronoi.xml | 50 + xscreensaver/hacks/config/wander.xml | 40 + xscreensaver/hacks/config/webcollage.xml | 39 + xscreensaver/hacks/config/whirlwindwarp.xml | 22 + xscreensaver/hacks/config/whirlygig.xml | 79 + xscreensaver/hacks/config/worm.xml | 30 + xscreensaver/hacks/config/wormhole.xml | 25 + xscreensaver/hacks/config/xanalogtv.xml | 19 + xscreensaver/hacks/config/xaos.xml | 24 + xscreensaver/hacks/config/xdaliclock.xml | 31 + xscreensaver/hacks/config/xflame.xml | 29 + xscreensaver/hacks/config/xjack.xml | 19 + xscreensaver/hacks/config/xlyap.xml | 36 + xscreensaver/hacks/config/xmatrix.xml | 53 + xscreensaver/hacks/config/xmountains.xml | 110 + xscreensaver/hacks/config/xplanet.xml | 140 + xscreensaver/hacks/config/xrayswarm.xml | 18 + xscreensaver/hacks/config/xsnow.xml | 39 + xscreensaver/hacks/config/xspirograph.xml | 19 + xscreensaver/hacks/config/xss.dtd | 105 + xscreensaver/hacks/config/xss.xsd | 375 + xscreensaver/hacks/config/xteevee.xml | 21 + xscreensaver/hacks/config/zoom.xml | 44 + xscreensaver/hacks/coral.c | 300 + xscreensaver/hacks/coral.man | 60 + xscreensaver/hacks/critical.c | 451 + xscreensaver/hacks/critical.man | 90 + xscreensaver/hacks/crystal.c | 1271 + xscreensaver/hacks/crystal.man | 77 + xscreensaver/hacks/cwaves.c | 208 + xscreensaver/hacks/cwaves.man | 72 + xscreensaver/hacks/cynosure.c | 431 + xscreensaver/hacks/cynosure.man | 60 + xscreensaver/hacks/decayscreen.c | 337 + xscreensaver/hacks/decayscreen.man | 78 + xscreensaver/hacks/deco.c | 333 + xscreensaver/hacks/deco.man | 101 + xscreensaver/hacks/deluxe.c | 444 + xscreensaver/hacks/deluxe.man | 68 + xscreensaver/hacks/demon.c | 976 + xscreensaver/hacks/demon.man | 65 + xscreensaver/hacks/discrete.c | 438 + xscreensaver/hacks/discrete.man | 57 + xscreensaver/hacks/distort.c | 835 + xscreensaver/hacks/distort.man | 119 + xscreensaver/hacks/drift.c | 693 + xscreensaver/hacks/drift.man | 75 + xscreensaver/hacks/epicycle.c | 788 + xscreensaver/hacks/epicycle.man | 200 + xscreensaver/hacks/eruption.c | 506 + xscreensaver/hacks/eruption.man | 73 + xscreensaver/hacks/euler2d.c | 879 + xscreensaver/hacks/euler2d.man | 65 + xscreensaver/hacks/euler2d.tex | 337 + xscreensaver/hacks/fadeplot.c | 234 + xscreensaver/hacks/fadeplot.man | 61 + xscreensaver/hacks/fiberlamp.c | 471 + xscreensaver/hacks/fiberlamp.man | 61 + xscreensaver/hacks/fireworkx.c | 499 + xscreensaver/hacks/fireworkx.man | 86 + xscreensaver/hacks/fireworkx_mmx.S | 226 + xscreensaver/hacks/flag.c | 535 + xscreensaver/hacks/flag.man | 88 + xscreensaver/hacks/flame.c | 453 + xscreensaver/hacks/flame.man | 70 + xscreensaver/hacks/flow.c | 1224 + xscreensaver/hacks/flow.man | 133 + xscreensaver/hacks/fluidballs.c | 809 + xscreensaver/hacks/fluidballs.man | 90 + xscreensaver/hacks/fontglide.c | 1808 ++ xscreensaver/hacks/fontglide.man | 120 + xscreensaver/hacks/forest.c | 258 + xscreensaver/hacks/forest.man | 58 + xscreensaver/hacks/fuzzyflakes.c | 641 + xscreensaver/hacks/fuzzyflakes.man | 108 + xscreensaver/hacks/galaxy.c | 460 + xscreensaver/hacks/galaxy.man | 83 + xscreensaver/hacks/glx/Makefile.in | 2122 ++ xscreensaver/hacks/glx/README | 10 + xscreensaver/hacks/glx/antinspect.c | 675 + xscreensaver/hacks/glx/antinspect.man | 58 + xscreensaver/hacks/glx/antmaze.c | 1560 + xscreensaver/hacks/glx/antmaze.man | 54 + xscreensaver/hacks/glx/ants.h | 45 + xscreensaver/hacks/glx/antspotlight.c | 775 + xscreensaver/hacks/glx/antspotlight.man | 59 + xscreensaver/hacks/glx/atlantis.c | 579 + xscreensaver/hacks/glx/atlantis.h | 123 + xscreensaver/hacks/glx/atlantis.man | 78 + xscreensaver/hacks/glx/atunnel.c | 324 + xscreensaver/hacks/glx/atunnel.man | 85 + xscreensaver/hacks/glx/b_draw.c | 244 + xscreensaver/hacks/glx/b_lockglue.c | 243 + xscreensaver/hacks/glx/b_sphere.c | 219 + xscreensaver/hacks/glx/blinkbox.c | 595 + xscreensaver/hacks/glx/blinkbox.man | 69 + xscreensaver/hacks/glx/blocktube.c | 436 + xscreensaver/hacks/glx/blocktube.man | 73 + xscreensaver/hacks/glx/boing.c | 664 + xscreensaver/hacks/glx/boing.man | 107 + xscreensaver/hacks/glx/bouncingcow.c | 554 + xscreensaver/hacks/glx/bouncingcow.man | 73 + xscreensaver/hacks/glx/boxed.c | 1282 + xscreensaver/hacks/glx/boxed.h | 4116 +++ xscreensaver/hacks/glx/boxed.man | 56 + xscreensaver/hacks/glx/bubble3d.c | 280 + xscreensaver/hacks/glx/bubble3d.h | 96 + xscreensaver/hacks/glx/bubble3d.man | 62 + xscreensaver/hacks/glx/buildlwo.c | 96 + xscreensaver/hacks/glx/buildlwo.h | 32 + xscreensaver/hacks/glx/cage.c | 463 + xscreensaver/hacks/glx/cage.man | 61 + xscreensaver/hacks/glx/carousel.c | 902 + xscreensaver/hacks/glx/carousel.man | 108 + xscreensaver/hacks/glx/chessgames.h | 343 + xscreensaver/hacks/glx/chessmodels.c | 1708 ++ xscreensaver/hacks/glx/chessmodels.h | 44 + xscreensaver/hacks/glx/circuit.c | 2209 ++ xscreensaver/hacks/glx/circuit.man | 72 + xscreensaver/hacks/glx/cow_face.c | 341 + xscreensaver/hacks/glx/cow_hide.c |13055 +++++++++ xscreensaver/hacks/glx/cow_hoofs.c | 1037 + xscreensaver/hacks/glx/cow_horns.c | 1025 + xscreensaver/hacks/glx/cow_tail.c | 464 + xscreensaver/hacks/glx/cow_udder.c | 1520 + xscreensaver/hacks/glx/crackberg.c | 1413 + xscreensaver/hacks/glx/crackberg.man | 123 + xscreensaver/hacks/glx/cube21.c | 899 + xscreensaver/hacks/glx/cube21.man | 149 + xscreensaver/hacks/glx/cubenetic.c | 598 + xscreensaver/hacks/glx/cubenetic.man | 89 + xscreensaver/hacks/glx/cubestorm.c | 427 + xscreensaver/hacks/glx/cubestorm.man | 77 + xscreensaver/hacks/glx/cubicgrid.c | 288 + xscreensaver/hacks/glx/cubicgrid.man | 85 + xscreensaver/hacks/glx/dangerball.c | 386 + xscreensaver/hacks/glx/dangerball.man | 72 + xscreensaver/hacks/glx/dnalogo.c | 1272 + xscreensaver/hacks/glx/dolphin.c | 2061 ++ xscreensaver/hacks/glx/dxf2gl.pl | 501 + xscreensaver/hacks/glx/e_textures.h | 1478 + xscreensaver/hacks/glx/endgame.c | 898 + xscreensaver/hacks/glx/endgame.man | 74 + xscreensaver/hacks/glx/engine.c | 978 + xscreensaver/hacks/glx/engine.man | 80 + xscreensaver/hacks/glx/extrusion-helix2.c | 47 + xscreensaver/hacks/glx/extrusion-helix3.c | 46 + xscreensaver/hacks/glx/extrusion-helix4.c | 63 + xscreensaver/hacks/glx/extrusion-joinoffset.c | 148 + xscreensaver/hacks/glx/extrusion-screw.c | 114 + xscreensaver/hacks/glx/extrusion-taper.c | 218 + xscreensaver/hacks/glx/extrusion-twistoid.c | 213 + xscreensaver/hacks/glx/extrusion.c | 557 + xscreensaver/hacks/glx/extrusion.h | 53 + xscreensaver/hacks/glx/extrusion.man | 71 + xscreensaver/hacks/glx/flipflop.c | 705 + xscreensaver/hacks/glx/flipflop.man | 91 + xscreensaver/hacks/glx/flipscreen3d.c | 535 + xscreensaver/hacks/glx/flipscreen3d.man | 61 + xscreensaver/hacks/glx/fliptext.c | 1115 + xscreensaver/hacks/glx/fliptext.man | 116 + xscreensaver/hacks/glx/flurry-smoke.c | 1444 + xscreensaver/hacks/glx/flurry-spark.c | 285 + xscreensaver/hacks/glx/flurry-star.c | 106 + xscreensaver/hacks/glx/flurry-texture.c | 224 + xscreensaver/hacks/glx/flurry.c | 572 + xscreensaver/hacks/glx/flurry.h | 275 + xscreensaver/hacks/glx/flurry.man | 73 + xscreensaver/hacks/glx/flyingtoasters.c | 860 + xscreensaver/hacks/glx/flyingtoasters.man | 86 + xscreensaver/hacks/glx/font-ximage.c | 255 + xscreensaver/hacks/glx/font-ximage.h | 30 + xscreensaver/hacks/glx/fps.c | 345 + xscreensaver/hacks/glx/gears.c | 945 + xscreensaver/hacks/glx/gears.man | 79 + xscreensaver/hacks/glx/gflux.c | 798 + xscreensaver/hacks/glx/gflux.man | 113 + xscreensaver/hacks/glx/glblur.c | 638 + xscreensaver/hacks/glx/glblur.man | 76 + xscreensaver/hacks/glx/glcells.c | 1317 + xscreensaver/hacks/glx/glcells.man | 99 + xscreensaver/hacks/glx/gleidescope.c | 1065 + xscreensaver/hacks/glx/gleidescope.man | 77 + xscreensaver/hacks/glx/glforestfire.c | 1133 + xscreensaver/hacks/glx/glforestfire.man | 132 + xscreensaver/hacks/glx/glhanoi.c | 1474 + xscreensaver/hacks/glx/glhanoi.man | 72 + xscreensaver/hacks/glx/glknots.c | 462 + xscreensaver/hacks/glx/glknots.man | 81 + xscreensaver/hacks/glx/gllist.c | 9 + xscreensaver/hacks/glx/gllist.h | 22 + xscreensaver/hacks/glx/glmatrix.c | 1081 + xscreensaver/hacks/glx/glmatrix.man | 100 + xscreensaver/hacks/glx/glplanet.c | 671 + xscreensaver/hacks/glx/glplanet.man | 70 + xscreensaver/hacks/glx/glschool.c | 195 + xscreensaver/hacks/glx/glschool.h | 17 + xscreensaver/hacks/glx/glschool.man | 126 + xscreensaver/hacks/glx/glschool_alg.c | 364 + xscreensaver/hacks/glx/glschool_alg.h | 126 + xscreensaver/hacks/glx/glschool_gl.c | 281 + xscreensaver/hacks/glx/glschool_gl.h | 37 + xscreensaver/hacks/glx/glslideshow.c | 1225 + xscreensaver/hacks/glx/glslideshow.man | 130 + xscreensaver/hacks/glx/glsnake.c | 2643 ++ xscreensaver/hacks/glx/glsnake.man | 100 + xscreensaver/hacks/glx/gltext.c | 626 + xscreensaver/hacks/glx/gltext.man | 125 + xscreensaver/hacks/glx/gltrackball.c | 142 + xscreensaver/hacks/glx/gltrackball.h | 55 + xscreensaver/hacks/glx/glut_roman.h | 2454 ++ xscreensaver/hacks/glx/glut_stroke.c | 56 + xscreensaver/hacks/glx/glut_swidth.c | 72 + xscreensaver/hacks/glx/glutstroke.h | 47 + xscreensaver/hacks/glx/glxfonts.c | 233 + xscreensaver/hacks/glx/glxfonts.h | 40 + xscreensaver/hacks/glx/grab-ximage.c | 834 + xscreensaver/hacks/glx/grab-ximage.h | 76 + xscreensaver/hacks/glx/hypertorus.c | 995 + xscreensaver/hacks/glx/hypertorus.man | 185 + xscreensaver/hacks/glx/hypnowheel.c | 298 + xscreensaver/hacks/glx/hypnowheel.man | 80 + xscreensaver/hacks/glx/involute.c | 968 + xscreensaver/hacks/glx/involute.h | 77 + xscreensaver/hacks/glx/jigglypuff.c | 1068 + xscreensaver/hacks/glx/jigglypuff.man | 121 + xscreensaver/hacks/glx/juggler3d.c | 2138 ++ xscreensaver/hacks/glx/juggler3d.man | 88 + xscreensaver/hacks/glx/klein.c | 414 + xscreensaver/hacks/glx/klein.man | 59 + xscreensaver/hacks/glx/lament.c | 2045 ++ xscreensaver/hacks/glx/lament.man | 70 + xscreensaver/hacks/glx/lavalite.c | 1573 ++ xscreensaver/hacks/glx/lavalite.man | 162 + xscreensaver/hacks/glx/lockward.c | 929 + xscreensaver/hacks/glx/lockward.man | 80 + xscreensaver/hacks/glx/marching.c | 639 + xscreensaver/hacks/glx/marching.h | 48 + xscreensaver/hacks/glx/menger.c | 552 + xscreensaver/hacks/glx/menger.man | 78 + xscreensaver/hacks/glx/mirrorblob.c | 1755 ++ xscreensaver/hacks/glx/mirrorblob.man | 107 + xscreensaver/hacks/glx/moebius.c | 754 + xscreensaver/hacks/glx/moebius.man | 65 + xscreensaver/hacks/glx/moebiusgears.c | 413 + xscreensaver/hacks/glx/moebiusgears.man | 86 + xscreensaver/hacks/glx/molecule.c | 1638 ++ xscreensaver/hacks/glx/molecule.man | 162 + xscreensaver/hacks/glx/molecules.sh | 17 + xscreensaver/hacks/glx/morph3d.c | 828 + xscreensaver/hacks/glx/morph3d.man | 57 + xscreensaver/hacks/glx/noof.c | 466 + xscreensaver/hacks/glx/noof.man | 49 + xscreensaver/hacks/glx/normals.c | 52 + xscreensaver/hacks/glx/normals.h | 38 + xscreensaver/hacks/glx/pinion.c | 1488 + xscreensaver/hacks/glx/pinion.man | 84 + xscreensaver/hacks/glx/pipeobjs.c | 3262 +++ xscreensaver/hacks/glx/pipes.c | 1033 + xscreensaver/hacks/glx/pipes.man | 85 + xscreensaver/hacks/glx/polyhedra-gl.c | 612 + xscreensaver/hacks/glx/polyhedra.c | 2447 ++ xscreensaver/hacks/glx/polyhedra.h | 52 + xscreensaver/hacks/glx/polyhedra.man | 124 + xscreensaver/hacks/glx/polytopes.c | 3151 +++ xscreensaver/hacks/glx/polytopes.man | 205 + xscreensaver/hacks/glx/providence.c | 815 + xscreensaver/hacks/glx/providence.man | 68 + xscreensaver/hacks/glx/pulsar.c | 516 + xscreensaver/hacks/glx/pulsar.man | 102 + xscreensaver/hacks/glx/queens.c | 520 + xscreensaver/hacks/glx/queens.man | 68 + xscreensaver/hacks/glx/rotator.c | 247 + xscreensaver/hacks/glx/rotator.h | 60 + xscreensaver/hacks/glx/rubik.c | 2081 ++ xscreensaver/hacks/glx/rubik.man | 69 + xscreensaver/hacks/glx/s1_1.c | 1733 ++ xscreensaver/hacks/glx/s1_2.c | 1733 ++ xscreensaver/hacks/glx/s1_3.c | 1733 ++ xscreensaver/hacks/glx/s1_4.c | 1733 ++ xscreensaver/hacks/glx/s1_5.c | 1733 ++ xscreensaver/hacks/glx/s1_6.c | 1733 ++ xscreensaver/hacks/glx/s1_b.c | 505 + xscreensaver/hacks/glx/sballs.c | 863 + xscreensaver/hacks/glx/sballs.man | 127 + xscreensaver/hacks/glx/shark.c | 1395 + xscreensaver/hacks/glx/sierpinski3d.c | 590 + xscreensaver/hacks/glx/sierpinski3d.man | 67 + xscreensaver/hacks/glx/sphere.c | 105 + xscreensaver/hacks/glx/sphere.h | 22 + xscreensaver/hacks/glx/spheremonics.c | 914 + xscreensaver/hacks/glx/spheremonics.man | 92 + xscreensaver/hacks/glx/sproingies.c | 923 + xscreensaver/hacks/glx/sproingies.h | 25 + xscreensaver/hacks/glx/sproingies.man | 68 + xscreensaver/hacks/glx/sproingiewrap.c | 249 + xscreensaver/hacks/glx/stairs.c | 577 + xscreensaver/hacks/glx/stairs.man | 56 + xscreensaver/hacks/glx/starwars.c | 1112 + xscreensaver/hacks/glx/starwars.man | 183 + xscreensaver/hacks/glx/starwars.txt | 311 + xscreensaver/hacks/glx/stonerview-move.c | 156 + xscreensaver/hacks/glx/stonerview-move.h | 31 + xscreensaver/hacks/glx/stonerview-osc.c | 341 + xscreensaver/hacks/glx/stonerview-osc.h | 175 + xscreensaver/hacks/glx/stonerview-view.c | 105 + xscreensaver/hacks/glx/stonerview.c | 174 + xscreensaver/hacks/glx/stonerview.h | 60 + xscreensaver/hacks/glx/stonerview.man | 49 + xscreensaver/hacks/glx/superquadrics.c | 788 + xscreensaver/hacks/glx/superquadrics.man | 70 + xscreensaver/hacks/glx/swim.c | 232 + xscreensaver/hacks/glx/tangram.c | 1058 + xscreensaver/hacks/glx/tangram.man | 63 + xscreensaver/hacks/glx/tangram_shapes.c | 224 + xscreensaver/hacks/glx/tangram_shapes.h | 15 + xscreensaver/hacks/glx/texfont.c | 517 + xscreensaver/hacks/glx/texfont.h | 37 + xscreensaver/hacks/glx/timetunnel.c | 1233 + xscreensaver/hacks/glx/timetunnel.man | 81 + xscreensaver/hacks/glx/toast.c | 185 + xscreensaver/hacks/glx/toast2.c | 209 + xscreensaver/hacks/glx/toaster.c | 371 + xscreensaver/hacks/glx/toaster_base.c | 125 + xscreensaver/hacks/glx/toaster_handle.c | 59 + xscreensaver/hacks/glx/toaster_handle2.c | 35 + xscreensaver/hacks/glx/toaster_jet.c | 173 + xscreensaver/hacks/glx/toaster_knob.c | 71 + xscreensaver/hacks/glx/toaster_slots.c | 101 + xscreensaver/hacks/glx/toaster_wing.c | 38 + xscreensaver/hacks/glx/topblock.c | 873 + xscreensaver/hacks/glx/topblock.h | 45 + xscreensaver/hacks/glx/topblock.man | 172 + xscreensaver/hacks/glx/trackball.c | 330 + xscreensaver/hacks/glx/trackball.h | 82 + xscreensaver/hacks/glx/tube.c | 236 + xscreensaver/hacks/glx/tube.h | 32 + xscreensaver/hacks/glx/tunnel_draw.c | 502 + xscreensaver/hacks/glx/tunnel_draw.h | 11 + xscreensaver/hacks/glx/voronoi.c | 519 + xscreensaver/hacks/glx/voronoi.man | 88 + xscreensaver/hacks/glx/whale.c | 1887 ++ xscreensaver/hacks/glx/xlock-gl-utils.c | 208 + xscreensaver/hacks/glx/xpm-ximage.c | 462 + xscreensaver/hacks/glx/xpm-ximage.h | 31 + xscreensaver/hacks/glx/xscreensaver-gl-helper.c | 74 + xscreensaver/hacks/glx/xscreensaver-gl-helper.man | 33 + xscreensaver/hacks/goop.c | 595 + xscreensaver/hacks/goop.man | 80 + xscreensaver/hacks/grav.c | 353 + xscreensaver/hacks/grav.man | 74 + xscreensaver/hacks/greynetic.c | 280 + xscreensaver/hacks/greynetic.man | 52 + xscreensaver/hacks/halftone.c | 394 + xscreensaver/hacks/halftone.man | 79 + xscreensaver/hacks/halo.c | 436 + xscreensaver/hacks/halo.man | 71 + xscreensaver/hacks/helix.c | 352 + xscreensaver/hacks/helix.man | 58 + xscreensaver/hacks/hopalong.c | 577 + xscreensaver/hacks/hopalong.man | 78 + xscreensaver/hacks/hyperball.c | 2462 ++ xscreensaver/hacks/hyperball.man | 84 + xscreensaver/hacks/hypercube.c | 571 + xscreensaver/hacks/hypercube.man | 93 + xscreensaver/hacks/ifs.c | 531 + xscreensaver/hacks/ifs.man | 107 + xscreensaver/hacks/images/6x10font.xbm | 190 + xscreensaver/hacks/images/amiga.xpm | 269 + xscreensaver/hacks/images/apple2font.xbm | 41 + xscreensaver/hacks/images/atari.xbm | 6 + xscreensaver/hacks/images/blocktube.xpm | 320 + xscreensaver/hacks/images/bob.xbm | 43 + xscreensaver/hacks/images/bubbles/blood.pov | 24 + xscreensaver/hacks/images/bubbles/blood1.xpm | 75 + xscreensaver/hacks/images/bubbles/blood10.xpm | 159 + xscreensaver/hacks/images/bubbles/blood11.xpm | 170 + xscreensaver/hacks/images/bubbles/blood2.xpm | 101 + xscreensaver/hacks/images/bubbles/blood3.xpm | 112 + xscreensaver/hacks/images/bubbles/blood4.xpm | 116 + xscreensaver/hacks/images/bubbles/blood5.xpm | 121 + xscreensaver/hacks/images/bubbles/blood6.xpm | 128 + xscreensaver/hacks/images/bubbles/blood7.xpm | 135 + xscreensaver/hacks/images/bubbles/blood8.xpm | 143 + xscreensaver/hacks/images/bubbles/blood9.xpm | 149 + xscreensaver/hacks/images/bubbles/blue.pov | 22 + xscreensaver/hacks/images/bubbles/blue1.xpm | 64 + xscreensaver/hacks/images/bubbles/blue10.xpm | 157 + xscreensaver/hacks/images/bubbles/blue11.xpm | 169 + xscreensaver/hacks/images/bubbles/blue2.xpm | 90 + xscreensaver/hacks/images/bubbles/blue3.xpm | 104 + xscreensaver/hacks/images/bubbles/blue4.xpm | 116 + xscreensaver/hacks/images/bubbles/blue5.xpm | 122 + xscreensaver/hacks/images/bubbles/blue6.xpm | 128 + xscreensaver/hacks/images/bubbles/blue7.xpm | 132 + xscreensaver/hacks/images/bubbles/blue8.xpm | 142 + xscreensaver/hacks/images/bubbles/blue9.xpm | 149 + xscreensaver/hacks/images/bubbles/glass.pov | 27 + xscreensaver/hacks/images/bubbles/glass1.xpm | 79 + xscreensaver/hacks/images/bubbles/glass10.xpm | 155 + xscreensaver/hacks/images/bubbles/glass11.xpm | 167 + xscreensaver/hacks/images/bubbles/glass2.xpm | 95 + xscreensaver/hacks/images/bubbles/glass3.xpm | 112 + xscreensaver/hacks/images/bubbles/glass4.xpm | 118 + xscreensaver/hacks/images/bubbles/glass5.xpm | 120 + xscreensaver/hacks/images/bubbles/glass6.xpm | 128 + xscreensaver/hacks/images/bubbles/glass7.xpm | 133 + xscreensaver/hacks/images/bubbles/glass8.xpm | 140 + xscreensaver/hacks/images/bubbles/glass9.xpm | 147 + xscreensaver/hacks/images/bubbles/jade.pov | 24 + xscreensaver/hacks/images/bubbles/jade1.xpm | 76 + xscreensaver/hacks/images/bubbles/jade10.xpm | 158 + xscreensaver/hacks/images/bubbles/jade11.xpm | 171 + xscreensaver/hacks/images/bubbles/jade2.xpm | 96 + xscreensaver/hacks/images/bubbles/jade3.xpm | 113 + xscreensaver/hacks/images/bubbles/jade4.xpm | 116 + xscreensaver/hacks/images/bubbles/jade5.xpm | 123 + xscreensaver/hacks/images/bubbles/jade6.xpm | 128 + xscreensaver/hacks/images/bubbles/jade7.xpm | 133 + xscreensaver/hacks/images/bubbles/jade8.xpm | 143 + xscreensaver/hacks/images/bubbles/jade9.xpm | 149 + xscreensaver/hacks/images/chromesphere.xpm | 350 + xscreensaver/hacks/images/earth.xpm | 303 + xscreensaver/hacks/images/ground.xpm | 227 + xscreensaver/hacks/images/hmac.xpm | 52 + xscreensaver/hacks/images/jigglymap.xpm | 350 + xscreensaver/hacks/images/lament.xpm | 791 + xscreensaver/hacks/images/m6502/breakout.asm | 195 + xscreensaver/hacks/images/m6502/byterun.asm | 100 + xscreensaver/hacks/images/m6502/cellular-30.asm | 67 + xscreensaver/hacks/images/m6502/cellular-600.asm | 209 + xscreensaver/hacks/images/m6502/crunch6502.asm | 292 + xscreensaver/hacks/images/m6502/demoscene.asm | 457 + xscreensaver/hacks/images/m6502/dmsc.asm | 108 + xscreensaver/hacks/images/m6502/dragon-fractal.asm | 49 + xscreensaver/hacks/images/m6502/fullscreenlogo.asm | 107 + xscreensaver/hacks/images/m6502/keftal.asm | 82 + xscreensaver/hacks/images/m6502/matrix.asm | 67 + xscreensaver/hacks/images/m6502/noise.asm | 16 + xscreensaver/hacks/images/m6502/random-walk.asm | 82 + xscreensaver/hacks/images/m6502/random.asm | 11 + xscreensaver/hacks/images/m6502/random2.asm | 11 + xscreensaver/hacks/images/m6502/rorschach.asm | 124 + xscreensaver/hacks/images/m6502/santa.asm | 142 + xscreensaver/hacks/images/m6502/selfmodify.asm | 12 + xscreensaver/hacks/images/m6502/sierpinsky.asm | 131 + xscreensaver/hacks/images/m6502/spacer.asm | 235 + xscreensaver/hacks/images/m6502/wave6502.asm | 164 + xscreensaver/hacks/images/m6502/zookeeper.asm | 109 + xscreensaver/hacks/images/mac.xbm | 14 + xscreensaver/hacks/images/macbomb.xbm | 474 + xscreensaver/hacks/images/matrix1.xbm | 1260 + xscreensaver/hacks/images/matrix1.xpm | 392 + xscreensaver/hacks/images/matrix1b.xbm | 307 + xscreensaver/hacks/images/matrix1b.xpm | 227 + xscreensaver/hacks/images/matrix2.xbm | 1260 + xscreensaver/hacks/images/matrix2.xpm | 393 + xscreensaver/hacks/images/matrix2b.xbm | 307 + xscreensaver/hacks/images/matrix2b.xpm | 233 + xscreensaver/hacks/images/matrix3.xpm | 692 + xscreensaver/hacks/images/molecules/adenine.pdb | 37 + .../hacks/images/molecules/adrenochrome.pdb | 55 + xscreensaver/hacks/images/molecules/bucky.pdb | 156 + xscreensaver/hacks/images/molecules/caffeine.pdb | 54 + .../hacks/images/molecules/chlordecone.pdb | 49 + xscreensaver/hacks/images/molecules/cocaine.pdb | 93 + xscreensaver/hacks/images/molecules/codeine.pdb | 93 + .../hacks/images/molecules/cyclohexane.pdb | 151 + xscreensaver/hacks/images/molecules/cytosine.pdb | 33 + xscreensaver/hacks/images/molecules/dna.pdb | 972 + .../hacks/images/molecules/dodecahedrane.pdb | 87 + xscreensaver/hacks/images/molecules/dthc.pdb | 107 + xscreensaver/hacks/images/molecules/dynamite.pdb | 47 + xscreensaver/hacks/images/molecules/glycol.pdb | 27 + xscreensaver/hacks/images/molecules/guanine.pdb | 39 + xscreensaver/hacks/images/molecules/heroin.pdb | 107 + .../hacks/images/molecules/hexahelicene.pdb | 90 + xscreensaver/hacks/images/molecules/ibuprofen.pdb | 72 + xscreensaver/hacks/images/molecules/lsd.pdb | 104 + xscreensaver/hacks/images/molecules/menthol.pdb | 69 + xscreensaver/hacks/images/molecules/mescaline.pdb | 71 + .../hacks/images/molecules/methamphetamine.pdb | 88 + xscreensaver/hacks/images/molecules/morphine.pdb | 87 + xscreensaver/hacks/images/molecules/nicotine.pdb | 59 + xscreensaver/hacks/images/molecules/novocaine.pdb | 81 + xscreensaver/hacks/images/molecules/olestra.pdb | 913 + xscreensaver/hacks/images/molecules/penicillin.pdb | 89 + xscreensaver/hacks/images/molecules/sarin.pdb | 43 + xscreensaver/hacks/images/molecules/strychnine.pdb | 101 + xscreensaver/hacks/images/molecules/sucrose.pdb | 97 + .../hacks/images/molecules/thalidomide.pdb | 65 + xscreensaver/hacks/images/molecules/thymine.pdb | 37 + xscreensaver/hacks/images/molecules/viagra.pdb | 133 + xscreensaver/hacks/images/molecules/vitaminb6.pdb | 56 + xscreensaver/hacks/images/molecules/vitaminc.pdb | 47 + xscreensaver/hacks/images/molecules/vx.pdb | 92 + xscreensaver/hacks/images/noseguy/nose-f1.xbm | 46 + xscreensaver/hacks/images/noseguy/nose-f1.xpm | 74 + xscreensaver/hacks/images/noseguy/nose-f2.xbm | 46 + xscreensaver/hacks/images/noseguy/nose-f2.xpm | 74 + xscreensaver/hacks/images/noseguy/nose-f3.xbm | 46 + xscreensaver/hacks/images/noseguy/nose-f3.xpm | 74 + xscreensaver/hacks/images/noseguy/nose-f4.xbm | 46 + xscreensaver/hacks/images/noseguy/nose-f4.xpm | 73 + xscreensaver/hacks/images/noseguy/nose-l1.xbm | 46 + xscreensaver/hacks/images/noseguy/nose-l1.xpm | 74 + xscreensaver/hacks/images/noseguy/nose-l2.xbm | 46 + xscreensaver/hacks/images/noseguy/nose-l2.xpm | 74 + xscreensaver/hacks/images/noseguy/nose-r1.xbm | 46 + xscreensaver/hacks/images/noseguy/nose-r1.xpm | 74 + xscreensaver/hacks/images/noseguy/nose-r2.xbm | 46 + xscreensaver/hacks/images/noseguy/nose-r2.xpm | 74 + xscreensaver/hacks/images/osx_10_2.xpm | 298 + xscreensaver/hacks/images/osx_10_3.xpm | 279 + xscreensaver/hacks/images/pacman/eyes-d.xpm | 70 + xscreensaver/hacks/images/pacman/eyes-l.xpm | 70 + xscreensaver/hacks/images/pacman/eyes-r.xpm | 70 + xscreensaver/hacks/images/pacman/eyes-u.xpm | 70 + xscreensaver/hacks/images/pacman/ghost-d1.xpm | 72 + xscreensaver/hacks/images/pacman/ghost-d2.xpm | 72 + xscreensaver/hacks/images/pacman/ghost-l1.xpm | 72 + xscreensaver/hacks/images/pacman/ghost-l2.xpm | 73 + xscreensaver/hacks/images/pacman/ghost-mask.xpm | 69 + xscreensaver/hacks/images/pacman/ghost-r1.xpm | 72 + xscreensaver/hacks/images/pacman/ghost-r2.xpm | 72 + xscreensaver/hacks/images/pacman/ghost-s1.xpm | 71 + xscreensaver/hacks/images/pacman/ghost-s2.xpm | 71 + xscreensaver/hacks/images/pacman/ghost-sf1.xpm | 71 + xscreensaver/hacks/images/pacman/ghost-sf2.xpm | 71 + xscreensaver/hacks/images/pacman/ghost-u1.xpm | 72 + xscreensaver/hacks/images/pacman/ghost-u2.xpm | 72 + xscreensaver/hacks/images/pacman/pacman-0.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-d1.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-d2.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-ds1.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-ds2.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-ds3.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-ds4.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-ds5.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-ds6.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-ds7.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-ds8.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-l1.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-l2.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-r1.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-r2.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-u1.xpm | 69 + xscreensaver/hacks/images/pacman/pacman-u2.xpm | 69 + xscreensaver/hacks/images/sball-bg.xpm | 355 + xscreensaver/hacks/images/sball.xpm | 131 + xscreensaver/hacks/images/sea-texture.xpm | 199 + xscreensaver/hacks/images/som.xbm | 1685 ++ xscreensaver/hacks/images/timetunnel0.xpm | 343 + xscreensaver/hacks/images/timetunnel1.xpm | 1111 + xscreensaver/hacks/images/timetunnel2.xpm | 599 + xscreensaver/hacks/images/toast.xpm | 222 + xscreensaver/hacks/images/tree.xpm | 226 + xscreensaver/hacks/images/tunnel0.xpm | 198 + xscreensaver/hacks/images/tunnel1.xpm | 134 + xscreensaver/hacks/images/tunnel2.xpm | 206 + xscreensaver/hacks/images/tunnel3.xpm | 150 + xscreensaver/hacks/images/tunnel4.xpm | 134 + xscreensaver/hacks/images/tunnel5.xpm | 134 + xscreensaver/hacks/images/tunnelstar.xpm | 323 + xscreensaver/hacks/imsmap.c | 415 + xscreensaver/hacks/imsmap.man | 60 + xscreensaver/hacks/interaggregate.c | 971 + xscreensaver/hacks/interaggregate.man | 68 + xscreensaver/hacks/interference.c | 473 + xscreensaver/hacks/interference.man | 82 + xscreensaver/hacks/intermomentary.c | 572 + xscreensaver/hacks/intermomentary.man | 78 + xscreensaver/hacks/jigsaw.c | 893 + xscreensaver/hacks/jigsaw.man | 62 + xscreensaver/hacks/juggle.c | 2821 ++ xscreensaver/hacks/juggle.man | 184 + xscreensaver/hacks/julia.c | 440 + xscreensaver/hacks/julia.man | 83 + xscreensaver/hacks/kaleidescope.c | 489 + xscreensaver/hacks/kaleidescope.man | 85 + xscreensaver/hacks/kumppa.c | 532 + xscreensaver/hacks/kumppa.man | 61 + xscreensaver/hacks/laser.c | 364 + xscreensaver/hacks/laser.man | 64 + xscreensaver/hacks/lcdscrub | Bin 0 -> 41648 bytes xscreensaver/hacks/lcdscrub.c | 158 + xscreensaver/hacks/lightning.c | 613 + xscreensaver/hacks/lightning.man | 58 + xscreensaver/hacks/link_axp.com | 107 + xscreensaver/hacks/link_decc.com | 107 + xscreensaver/hacks/lisa.c | 749 + xscreensaver/hacks/lisa.man | 67 + xscreensaver/hacks/lissie.c | 327 + xscreensaver/hacks/lissie.man | 65 + xscreensaver/hacks/ljlatest | 18 + xscreensaver/hacks/ljlatest.man | 61 + xscreensaver/hacks/lmorph.c | 568 + xscreensaver/hacks/lmorph.man | 61 + xscreensaver/hacks/loop.c | 1695 ++ xscreensaver/hacks/loop.man | 61 + xscreensaver/hacks/m6502.c | 282 + xscreensaver/hacks/m6502.sh | 20 + xscreensaver/hacks/maze.c | 1882 ++ xscreensaver/hacks/maze.man | 146 + xscreensaver/hacks/memscroller.c | 607 + xscreensaver/hacks/memscroller.man | 74 + xscreensaver/hacks/metaballs.c | 427 + xscreensaver/hacks/metaballs.man | 69 + xscreensaver/hacks/mismunch.c | 376 + xscreensaver/hacks/mismunch.man | 75 + xscreensaver/hacks/moire.c | 294 + xscreensaver/hacks/moire.man | 64 + xscreensaver/hacks/moire2.c | 349 + xscreensaver/hacks/moire2.man | 59 + xscreensaver/hacks/mountain.c | 289 + xscreensaver/hacks/mountain.man | 56 + xscreensaver/hacks/munch.c | 298 + xscreensaver/hacks/munch.man | 133 + xscreensaver/hacks/nerverot.c | 1350 + xscreensaver/hacks/nerverot.man | 128 + xscreensaver/hacks/noseguy.c | 678 + xscreensaver/hacks/noseguy.man | 80 + xscreensaver/hacks/pacman.c | 1797 ++ xscreensaver/hacks/pacman.h | 231 + xscreensaver/hacks/pacman.man | 63 + xscreensaver/hacks/pacman_ai.c | 879 + xscreensaver/hacks/pacman_ai.h | 32 + xscreensaver/hacks/pacman_level.c | 772 + xscreensaver/hacks/pacman_level.h | 32 + xscreensaver/hacks/pedal.c | 325 + xscreensaver/hacks/pedal.man | 56 + xscreensaver/hacks/penetrate.c | 976 + xscreensaver/hacks/penetrate.man | 94 + xscreensaver/hacks/penrose.c | 1346 + xscreensaver/hacks/penrose.man | 102 + xscreensaver/hacks/petri.c | 760 + xscreensaver/hacks/petri.man | 125 + xscreensaver/hacks/phosphor.c | 1556 + xscreensaver/hacks/phosphor.man | 169 + xscreensaver/hacks/piecewise.c | 995 + xscreensaver/hacks/piecewise.man | 73 + xscreensaver/hacks/polyominoes.c | 2373 ++ xscreensaver/hacks/polyominoes.man | 61 + xscreensaver/hacks/pong.c | 791 + xscreensaver/hacks/pong.man | 78 + xscreensaver/hacks/popsquares.c | 237 + xscreensaver/hacks/pyro.c | 368 + xscreensaver/hacks/pyro.man | 60 + xscreensaver/hacks/qix.c | 629 + xscreensaver/hacks/qix.man | 128 + xscreensaver/hacks/rd-bomb.c | 614 + xscreensaver/hacks/rd-bomb.man | 96 + xscreensaver/hacks/ripples.c | 1109 + xscreensaver/hacks/ripples.man | 88 + xscreensaver/hacks/rocks.c | 545 + xscreensaver/hacks/rocks.man | 86 + xscreensaver/hacks/rorschach.c | 205 + xscreensaver/hacks/rorschach.man | 71 + xscreensaver/hacks/rotor.c | 399 + xscreensaver/hacks/rotor.man | 64 + xscreensaver/hacks/rotzoomer.c | 462 + xscreensaver/hacks/rotzoomer.man | 80 + xscreensaver/hacks/screenhack.c | 951 + xscreensaver/hacks/screenhack.h | 61 + xscreensaver/hacks/screenhackI.h | 136 + xscreensaver/hacks/shadebobs.c | 472 + xscreensaver/hacks/shadebobs.man | 61 + xscreensaver/hacks/sierpinski.c | 222 + xscreensaver/hacks/sierpinski.man | 65 + xscreensaver/hacks/slidescreen.c | 456 + xscreensaver/hacks/slidescreen.man | 89 + xscreensaver/hacks/slip.c | 365 + xscreensaver/hacks/slip.man | 82 + xscreensaver/hacks/sonar.c | 2290 ++ xscreensaver/hacks/sonar.man | 217 + xscreensaver/hacks/speedmine.c | 1628 ++ xscreensaver/hacks/speedmine.man | 242 + xscreensaver/hacks/sphere.c | 306 + xscreensaver/hacks/sphere.man | 58 + xscreensaver/hacks/spiral.c | 333 + xscreensaver/hacks/spiral.man | 67 + xscreensaver/hacks/spotlight.c | 306 + xscreensaver/hacks/spotlight.man | 70 + xscreensaver/hacks/squiral.c | 263 + xscreensaver/hacks/squiral.man | 76 + xscreensaver/hacks/starfish.c | 540 + xscreensaver/hacks/starfish.man | 80 + xscreensaver/hacks/strange.c | 448 + xscreensaver/hacks/strange.man | 58 + xscreensaver/hacks/substrate.c | 736 + xscreensaver/hacks/substrate.man | 69 + xscreensaver/hacks/swirl.c | 1480 + xscreensaver/hacks/swirl.man | 66 + xscreensaver/hacks/t3d.c | 969 + xscreensaver/hacks/t3d.man | 129 + xscreensaver/hacks/thornbird.c | 274 + xscreensaver/hacks/thornbird.man | 60 + xscreensaver/hacks/triangle.c | 359 + xscreensaver/hacks/triangle.man | 52 + xscreensaver/hacks/truchet.c | 527 + xscreensaver/hacks/truchet.man | 135 + xscreensaver/hacks/twang.c | 770 + xscreensaver/hacks/twang.man | 107 + xscreensaver/hacks/vermiculate.c | 1213 + xscreensaver/hacks/vermiculate.man | 44 + xscreensaver/hacks/vidwhacker | 505 + xscreensaver/hacks/vidwhacker.man | 85 + xscreensaver/hacks/vines.c | 201 + xscreensaver/hacks/vines.man | 62 + xscreensaver/hacks/vms_axp.opt | 4 + xscreensaver/hacks/vms_axp_12.opt | 4 + xscreensaver/hacks/vms_decc.opt | 4 + xscreensaver/hacks/vms_decc_12.opt | 4 + xscreensaver/hacks/wander.c | 254 + xscreensaver/hacks/wander.man | 72 + xscreensaver/hacks/webcollage | 3616 +++ xscreensaver/hacks/webcollage-cocoa.m | 337 + xscreensaver/hacks/webcollage-helper-cocoa.m | 392 + xscreensaver/hacks/webcollage-helper.c | 504 + xscreensaver/hacks/webcollage.man | 238 + xscreensaver/hacks/whirlwindwarp.c | 498 + xscreensaver/hacks/whirlwindwarp.man | 60 + xscreensaver/hacks/whirlygig.c | 730 + xscreensaver/hacks/whirlygig.man | 133 + xscreensaver/hacks/worm.c | 440 + xscreensaver/hacks/worm.man | 61 + xscreensaver/hacks/wormhole.c | 726 + xscreensaver/hacks/wormhole.man | 65 + xscreensaver/hacks/xanalogtv.c | 617 + xscreensaver/hacks/xanalogtv.man | 80 + xscreensaver/hacks/xflame.c | 814 + xscreensaver/hacks/xflame.man | 68 + xscreensaver/hacks/xjack.c | 470 + xscreensaver/hacks/xjack.man | 51 + xscreensaver/hacks/xlockmore.c | 568 + xscreensaver/hacks/xlockmore.h | 202 + xscreensaver/hacks/xlockmoreI.h | 154 + xscreensaver/hacks/xlyap.c | 1918 ++ xscreensaver/hacks/xlyap.man | 222 + xscreensaver/hacks/xmatrix.c | 1894 ++ xscreensaver/hacks/xmatrix.man | 146 + xscreensaver/hacks/xml2man.pl | 250 + xscreensaver/hacks/xpm-pixmap.c | 363 + xscreensaver/hacks/xpm-pixmap.h | 26 + xscreensaver/hacks/xrayswarm.c | 1213 + xscreensaver/hacks/xrayswarm.man | 49 + xscreensaver/hacks/xscreensaver-sgigl.c | 266 + xscreensaver/hacks/xspirograph.c | 326 + xscreensaver/hacks/xspirograph.man | 70 + xscreensaver/hacks/xsublim.c | 800 + xscreensaver/hacks/xsublim.man | 90 + xscreensaver/hacks/zoom.c | 250 + xscreensaver/hacks/zoom.man | 104 + xscreensaver/install-sh | 250 + xscreensaver/intltool-extract.in | 309 + xscreensaver/intltool-merge.in | 567 + xscreensaver/intltool-update.in | 613 + xscreensaver/makevms.com | 57 + xscreensaver/po/ChangeLog | 300 + xscreensaver/po/Makefile.in.in | 378 + xscreensaver/po/POTFILES.in | 234 + xscreensaver/po/ca.po | 8905 ++++++ xscreensaver/po/da.po | 9938 +++++++ xscreensaver/po/de.po | 9603 +++++++ xscreensaver/po/es.po |10021 +++++++ xscreensaver/po/et.po | 8995 ++++++ xscreensaver/po/fi.po | 8939 ++++++ xscreensaver/po/fr.po | 9248 ++++++ xscreensaver/po/hu.po | 9760 +++++++ xscreensaver/po/it.po | 8967 ++++++ xscreensaver/po/ja.po | 9060 ++++++ xscreensaver/po/ko.po | 9340 ++++++ xscreensaver/po/nb.po | 9276 ++++++ xscreensaver/po/nl.po | 9542 +++++++ xscreensaver/po/no.po | 9276 ++++++ xscreensaver/po/pl.po | 9387 ++++++ xscreensaver/po/pt.po | 9995 +++++++ xscreensaver/po/pt_BR.po | 8902 ++++++ xscreensaver/po/ru.po | 9132 ++++++ xscreensaver/po/sk.po | 9242 ++++++ xscreensaver/po/sv.po |11236 ++++++++ xscreensaver/po/update.sh | 24 + xscreensaver/po/vi.po | 9347 ++++++ xscreensaver/po/wa.po | 9138 ++++++ xscreensaver/po/zh_CN.po | 9513 +++++++ xscreensaver/po/zh_TW.po | 9358 ++++++ xscreensaver/setup.com | 147 + xscreensaver/utils/Makefile.in | 300 + xscreensaver/utils/README | 6 + xscreensaver/utils/ad2c | 42 + xscreensaver/utils/alpha.c | 215 + xscreensaver/utils/alpha.h | 22 + xscreensaver/utils/colorbars.c | 144 + xscreensaver/utils/colorbars.h | 24 + xscreensaver/utils/colors.c | 706 + xscreensaver/utils/colors.h | 140 + xscreensaver/utils/compile_axp.com | 25 + xscreensaver/utils/compile_decc.com | 25 + xscreensaver/utils/erase.c | 761 + xscreensaver/utils/erase.h | 20 + xscreensaver/utils/fade.c | 954 + xscreensaver/utils/fade.h | 21 + xscreensaver/utils/grabclient.c | 839 + xscreensaver/utils/grabscreen.c | 898 + xscreensaver/utils/grabscreen.h | 94 + xscreensaver/utils/hsv.c | 81 + xscreensaver/utils/hsv.h | 27 + xscreensaver/utils/images/logo-180.gif | Bin 0 -> 3328 bytes xscreensaver/utils/images/logo-180.xpm | 207 + xscreensaver/utils/images/logo-50.gif | Bin 0 -> 857 bytes xscreensaver/utils/images/logo-50.xpm | 77 + xscreensaver/utils/images/logo-big.gif | Bin 0 -> 17019 bytes xscreensaver/utils/images/logo.eps | 8058 ++++++ xscreensaver/utils/images/screensaver-cmndln.png | Bin 0 -> 1040 bytes .../utils/images/screensaver-colorselector.png | Bin 0 -> 1104 bytes .../utils/images/screensaver-diagnostic.png | Bin 0 -> 1307 bytes xscreensaver/utils/images/screensaver-locking.png | Bin 0 -> 944 bytes xscreensaver/utils/images/screensaver-power.png | Bin 0 -> 973 bytes xscreensaver/utils/images/screensaver-snap.png | Bin 0 -> 1272 bytes xscreensaver/utils/logo.c | 75 + xscreensaver/utils/minixpm.c | 233 + xscreensaver/utils/minixpm.h | 28 + xscreensaver/utils/overlay.c | 158 + xscreensaver/utils/resources.c | 265 + xscreensaver/utils/resources.h | 33 + xscreensaver/utils/spline.c | 330 + xscreensaver/utils/spline.h | 51 + xscreensaver/utils/usleep.c | 64 + xscreensaver/utils/usleep.h | 20 + xscreensaver/utils/utils.h | 27 + xscreensaver/utils/version.h | 2 + xscreensaver/utils/visual-gl.c | 266 + xscreensaver/utils/visual.c | 544 + xscreensaver/utils/visual.h | 32 + xscreensaver/utils/vms-gtod.c | 31 + xscreensaver/utils/vms-gtod.h | 85 + xscreensaver/utils/vms-strdup.c | 25 + xscreensaver/utils/vroot.h | 156 + xscreensaver/utils/xdbe.c | 75 + xscreensaver/utils/xdbe.h | 27 + xscreensaver/utils/xmu.c | 173 + xscreensaver/utils/xmu.h | 14 + xscreensaver/utils/xscreensaver-intl.h | 36 + xscreensaver/utils/xshm.c | 229 + xscreensaver/utils/xshm.h | 37 + xscreensaver/utils/yarandom.c | 125 + xscreensaver/utils/yarandom.h | 65 + xscreensaver/xscreensaver.spec | 204 + .../xscreensaver.xcodeproj/project.pbxproj |29841 ++++++++++++++++++++ 1488 files changed, 697947 insertions(+) create mode 100644 xscreensaver/INSTALL create mode 100644 xscreensaver/Makefile.in create mode 100644 xscreensaver/OSX/AppController.h create mode 100644 xscreensaver/OSX/AppController.m create mode 100644 xscreensaver/OSX/English.lproj/InfoPlist.strings create mode 100644 xscreensaver/OSX/English.lproj/MainMenu.nib/classes.nib create mode 100644 xscreensaver/OSX/English.lproj/MainMenu.nib/info.nib create mode 100644 xscreensaver/OSX/English.lproj/MainMenu.nib/keyedobjects.nib create mode 100644 xscreensaver/OSX/InvertedSlider.h create mode 100644 xscreensaver/OSX/InvertedSlider.m create mode 100644 xscreensaver/OSX/Makefile create mode 100644 xscreensaver/OSX/PrefsReader.h create mode 100644 xscreensaver/OSX/PrefsReader.m create mode 100644 xscreensaver/OSX/README create mode 100644 xscreensaver/OSX/SaverTester.plist create mode 100644 xscreensaver/OSX/XScreenSaver.icns create mode 100644 xscreensaver/OSX/XScreenSaver.plist create mode 100644 xscreensaver/OSX/XScreenSaverConfigSheet.h create mode 100644 xscreensaver/OSX/XScreenSaverConfigSheet.m create mode 100644 xscreensaver/OSX/XScreenSaverDMG.icns create mode 100644 xscreensaver/OSX/XScreenSaverGLView.h create mode 100644 xscreensaver/OSX/XScreenSaverGLView.m create mode 100644 xscreensaver/OSX/XScreenSaverSubclass.m create mode 100644 xscreensaver/OSX/XScreenSaverView.h create mode 100644 xscreensaver/OSX/XScreenSaverView.m create mode 100644 xscreensaver/OSX/bindist-DS_Store create mode 100644 xscreensaver/OSX/bindist.rtf create mode 100644 xscreensaver/OSX/jwxyz-timers.h create mode 100644 xscreensaver/OSX/jwxyz-timers.m create mode 100644 xscreensaver/OSX/jwxyz.h create mode 100644 xscreensaver/OSX/jwxyz.m create mode 100644 xscreensaver/OSX/main.m create mode 100644 xscreensaver/OSX/osxgrabscreen.m create mode 100644 xscreensaver/OSX/update-info-plist.pl create mode 100644 xscreensaver/OSX/xscreensaver_Prefix.pch create mode 100644 xscreensaver/README create mode 100644 xscreensaver/README.VMS create mode 100644 xscreensaver/README.hacking create mode 100644 xscreensaver/aclocal.m4 create mode 100644 xscreensaver/config.guess create mode 100644 xscreensaver/config.h-vms create mode 100644 xscreensaver/config.h.in create mode 100644 xscreensaver/config.sub create mode 100644 xscreensaver/configure create mode 100644 xscreensaver/configure.in create mode 100644 xscreensaver/debian/README.gl create mode 100644 xscreensaver/debian/changelog create mode 100644 xscreensaver/debian/compat create mode 100644 xscreensaver/debian/control create mode 100644 xscreensaver/debian/copyright create mode 100644 xscreensaver/debian/patches/10_jwz-xscreensaver-randr-patch-3.patch create mode 100644 xscreensaver/debian/patches/20_hacks_Makefile.patch create mode 100644 xscreensaver/debian/patches/21_driver_xscreensaver-getimage-video create mode 100644 xscreensaver/debian/patches/22_hacks_barcode.patch create mode 100644 xscreensaver/debian/patches/23_hacks_glx_glsnake.patch create mode 100644 xscreensaver/debian/patches/30_hacks_xanalogtv.patch create mode 100644 xscreensaver/debian/patches/31_utils_images_logo-50-bad-xpm.patch create mode 100644 xscreensaver/debian/patches/50_driver_screensaver-properties-desktop.patch create mode 100644 xscreensaver/debian/patches/52_driver_XScreenSaver.ad.in create mode 100644 xscreensaver/debian/patches/53_XScreenSaver.ad.in.patch create mode 100644 xscreensaver/debian/patches/60_add-ant-hack.patch create mode 100644 xscreensaver/debian/patches/70_fix_local_screenlock_bypass.patch create mode 100644 xscreensaver/debian/patches/series create mode 100644 xscreensaver/debian/rules create mode 100644 xscreensaver/debian/screensavers-desktop-files/anemone.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/anemotaxis.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/ant.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/antinspect.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/antmaze.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/antspotlight.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/apollonian.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/apple2.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/atlantis.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/attraction.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/atunnel.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/barcode.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/blaster.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/blinkbox.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/blitspin.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/blocktube.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/boing.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/bouboule.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/bouncingcow.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/boxed.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/boxfit.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/braid.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/bsod.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/bubble3d.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/bubbles.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/bumps.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/cage.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/carousel.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/ccurve.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/celtic.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/circuit.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/cloudlife.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/compass.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/coral.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/crackberg.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/critical.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/crystal.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/cube21.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/cubenetic.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/cubestorm.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/cubicgrid.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/cynosure.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/dangerball.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/decayscreen.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/deco.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/deluxe.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/demon.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/discrete.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/distort.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/drift.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/endgame.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/engine.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/epicycle.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/eruption.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/euler2d.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/extrusion.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/fadeplot.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/fiberlamp.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/fireworkx.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/flag.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/flame.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/flipflop.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/flipscreen3d.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/fliptext.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/flow.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/fluidballs.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/flurry.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/flyingtoasters.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/fontglide.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/forest.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/fuzzyflakes.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/galaxy.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/gears.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/gflux.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/glblur.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/glcells.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/gleidescope.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/glforestfire.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/glhanoi.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/glknots.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/glmatrix.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/glplanet.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/glschool.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/glslideshow.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/glsnake.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/gltext.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/goop.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/grav.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/greynetic.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/halftone.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/halo.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/helix.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/hopalong.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/hyperball.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/hypercube.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/hypertorus.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/hypnowheel.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/ifs.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/imsmap.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/interaggregate.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/interference.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/intermomentary.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/jigglypuff.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/jigsaw.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/juggle.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/juggler3d.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/julia.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/kaleidescope.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/klein.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/kumppa.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/lament.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/laser.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/lavalite.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/lcdscrub.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/lightning.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/lisa.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/lissie.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/lmorph.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/lockward.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/loop.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/maze.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/memscroller.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/menger.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/metaballs.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/mirrorblob.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/mismunch.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/moebius.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/moebiusgears.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/moire.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/moire2.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/molecule.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/morph3d.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/mountain.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/munch.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/nerverot.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/noof.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/noseguy.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/pacman.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/pedal.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/penetrate.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/penrose.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/petri.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/phosphor.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/piecewise.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/pinion.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/pipes.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/polyhedra.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/polyominoes.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/polytopes.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/pong.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/popsquares.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/providence.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/pulsar.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/pyro.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/qix.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/queens.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/rd-bomb.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/ripples.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/rocks.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/rorschach.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/rotor.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/rotzoomer.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/rubik.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/sballs.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/shadebobs.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/sierpinski.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/sierpinski3d.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/slidescreen.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/slip.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/sonar.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/speedmine.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/sphere.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/spheremonics.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/spiral.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/spotlight.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/sproingies.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/squiral.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/stairs.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/starfish.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/starwars.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/stonerview.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/strange.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/substrate.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/superquadrics.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/swirl.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/t3d.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/tangram.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/thornbird.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/timetunnel.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/topblock.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/triangle.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/truchet.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/twang.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/vermiculate.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/vidwhacker.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/vines.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/voronoi.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/wander.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/webcollage.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/whirlwindwarp.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/whirlygig.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/worm.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/wormhole.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/xanalogtv.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/xflame.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/xjack.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/xlyap.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/xmatrix.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/xrayswarm.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/xspirograph.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/xteevee.desktop create mode 100644 xscreensaver/debian/screensavers-desktop-files/zoom.desktop create mode 100644 xscreensaver/debian/watch create mode 100644 xscreensaver/debian/xscreensaver-data-extra.files create mode 100644 xscreensaver/debian/xscreensaver-data.files create mode 100644 xscreensaver/debian/xscreensaver-gl-extra.files create mode 100644 xscreensaver/debian/xscreensaver-gl.dirs create mode 100644 xscreensaver/debian/xscreensaver-gl.docs create mode 100644 xscreensaver/debian/xscreensaver-gl.files create mode 100644 xscreensaver/debian/xscreensaver-gl.postinst create mode 100644 xscreensaver/debian/xscreensaver-gl.postrm create mode 100644 xscreensaver/debian/xscreensaver.desktop create mode 100644 xscreensaver/debian/xscreensaver.docs create mode 100644 xscreensaver/debian/xscreensaver.files create mode 100644 xscreensaver/debian/xscreensaver.install create mode 100644 xscreensaver/debian/xscreensaver.menu create mode 100644 xscreensaver/debian/xscreensaver.pam create mode 100644 xscreensaver/debian/xscreensaver.postinst create mode 100644 xscreensaver/debian/xscreensaver.postrm create mode 100644 xscreensaver/debian/xscreensaver.svg create mode 100644 xscreensaver/driver/Makefile.in create mode 100644 xscreensaver/driver/README create mode 100644 xscreensaver/driver/XScreenSaver-Xm.ad create mode 100644 xscreensaver/driver/XScreenSaver.ad.in create mode 100644 xscreensaver/driver/XScreenSaver_Xm_ad.h create mode 100644 xscreensaver/driver/XScreenSaver_ad.h create mode 100644 xscreensaver/driver/auth.h create mode 100644 xscreensaver/driver/compile_axp.com create mode 100644 xscreensaver/driver/compile_decc.com create mode 100644 xscreensaver/driver/demo-Gtk-conf.c create mode 100644 xscreensaver/driver/demo-Gtk-conf.h create mode 100644 xscreensaver/driver/demo-Gtk-stubs.h create mode 100644 xscreensaver/driver/demo-Gtk-support.c create mode 100644 xscreensaver/driver/demo-Gtk-support.h create mode 100644 xscreensaver/driver/demo-Gtk-widgets.c create mode 100644 xscreensaver/driver/demo-Gtk-widgets.h create mode 100644 xscreensaver/driver/demo-Gtk.c create mode 100644 xscreensaver/driver/demo-Xm-widgets.c create mode 100644 xscreensaver/driver/demo-Xm.c create mode 100644 xscreensaver/driver/dpms.c create mode 100644 xscreensaver/driver/exec.c create mode 100644 xscreensaver/driver/exec.h create mode 100644 xscreensaver/driver/link_axp.com create mode 100644 xscreensaver/driver/link_decc.com create mode 100644 xscreensaver/driver/lock.c create mode 100644 xscreensaver/driver/mlstring.c create mode 100644 xscreensaver/driver/mlstring.h create mode 100644 xscreensaver/driver/passwd-helper.c create mode 100644 xscreensaver/driver/passwd-kerberos.c create mode 100644 xscreensaver/driver/passwd-pam.c create mode 100644 xscreensaver/driver/passwd-pwent.c create mode 100644 xscreensaver/driver/passwd.c create mode 100644 xscreensaver/driver/pdf2jpeg.m create mode 100644 xscreensaver/driver/pdf2jpeg.man create mode 100644 xscreensaver/driver/prefs.c create mode 100644 xscreensaver/driver/prefs.h create mode 100644 xscreensaver/driver/remote.c create mode 100644 xscreensaver/driver/remote.h create mode 100644 xscreensaver/driver/screensaver-properties.desktop.in create mode 100644 xscreensaver/driver/setuid.c create mode 100644 xscreensaver/driver/splash.c create mode 100644 xscreensaver/driver/stderr.c create mode 100644 xscreensaver/driver/subprocs.c create mode 100644 xscreensaver/driver/test-apm.c create mode 100644 xscreensaver/driver/test-fade.c create mode 100644 xscreensaver/driver/test-grab.c create mode 100644 xscreensaver/driver/test-mlstring.c create mode 100644 xscreensaver/driver/test-passwd.c create mode 100644 xscreensaver/driver/test-randr.c create mode 100644 xscreensaver/driver/test-uid.c create mode 100644 xscreensaver/driver/test-vp.c create mode 100644 xscreensaver/driver/test-xdpms.c create mode 100644 xscreensaver/driver/test-xinerama.c create mode 100644 xscreensaver/driver/timers.c create mode 100644 xscreensaver/driver/types.h create mode 100644 xscreensaver/driver/vms-getpwnam.c create mode 100644 xscreensaver/driver/vms-hpwd.c create mode 100644 xscreensaver/driver/vms-pwd.h create mode 100644 xscreensaver/driver/vms-validate.c create mode 100644 xscreensaver/driver/vms_axp.opt create mode 100644 xscreensaver/driver/vms_axp_12.opt create mode 100644 xscreensaver/driver/vms_decc.opt create mode 100644 xscreensaver/driver/vms_decc_12.opt create mode 100644 xscreensaver/driver/windows.c create mode 100644 xscreensaver/driver/xdpyinfo.c create mode 100644 xscreensaver/driver/xscreensaver-command.c create mode 100644 xscreensaver/driver/xscreensaver-command.man create mode 100644 xscreensaver/driver/xscreensaver-demo.glade2 create mode 100644 xscreensaver/driver/xscreensaver-demo.glade2p create mode 100644 xscreensaver/driver/xscreensaver-demo.man create mode 100644 xscreensaver/driver/xscreensaver-getimage-desktop create mode 100644 xscreensaver/driver/xscreensaver-getimage-desktop.man create mode 100644 xscreensaver/driver/xscreensaver-getimage-file create mode 100644 xscreensaver/driver/xscreensaver-getimage-file.man create mode 100644 xscreensaver/driver/xscreensaver-getimage-video create mode 100644 xscreensaver/driver/xscreensaver-getimage-video.man create mode 100644 xscreensaver/driver/xscreensaver-getimage.c create mode 100644 xscreensaver/driver/xscreensaver-getimage.man create mode 100644 xscreensaver/driver/xscreensaver-text create mode 100644 xscreensaver/driver/xscreensaver-text.man create mode 100644 xscreensaver/driver/xscreensaver.c create mode 100644 xscreensaver/driver/xscreensaver.h create mode 100644 xscreensaver/driver/xscreensaver.man create mode 100644 xscreensaver/driver/xscreensaver.pam create mode 100644 xscreensaver/driver/xset.c create mode 100644 xscreensaver/hacks/Makefile.in create mode 100644 xscreensaver/hacks/README create mode 100644 xscreensaver/hacks/abstractile.c create mode 100644 xscreensaver/hacks/abstractile.man create mode 100644 xscreensaver/hacks/analogtv.c create mode 100644 xscreensaver/hacks/analogtv.h create mode 100644 xscreensaver/hacks/anemone.c create mode 100644 xscreensaver/hacks/anemone.man create mode 100644 xscreensaver/hacks/anemotaxis.c create mode 100644 xscreensaver/hacks/anemotaxis.man create mode 100644 xscreensaver/hacks/ant.c create mode 100644 xscreensaver/hacks/apollonian.c create mode 100644 xscreensaver/hacks/apollonian.man create mode 100644 xscreensaver/hacks/apple2-main.c create mode 100644 xscreensaver/hacks/apple2.c create mode 100644 xscreensaver/hacks/apple2.h create mode 100644 xscreensaver/hacks/apple2.man create mode 100644 xscreensaver/hacks/asm6502.c create mode 100644 xscreensaver/hacks/asm6502.h create mode 100644 xscreensaver/hacks/attraction.c create mode 100644 xscreensaver/hacks/attraction.man create mode 100644 xscreensaver/hacks/automata.h create mode 100644 xscreensaver/hacks/barcode.c create mode 100644 xscreensaver/hacks/barcode.man create mode 100644 xscreensaver/hacks/blaster.c create mode 100644 xscreensaver/hacks/blaster.man create mode 100644 xscreensaver/hacks/blitspin.c create mode 100644 xscreensaver/hacks/blitspin.man create mode 100644 xscreensaver/hacks/bouboule.c create mode 100644 xscreensaver/hacks/bouboule.man create mode 100644 xscreensaver/hacks/boxfit.c create mode 100644 xscreensaver/hacks/boxfit.man create mode 100644 xscreensaver/hacks/braid.c create mode 100644 xscreensaver/hacks/braid.man create mode 100644 xscreensaver/hacks/bsod.c create mode 100644 xscreensaver/hacks/bsod.man create mode 100644 xscreensaver/hacks/bubbles-default.c create mode 100644 xscreensaver/hacks/bubbles.c create mode 100644 xscreensaver/hacks/bubbles.h create mode 100644 xscreensaver/hacks/bubbles.man create mode 100644 xscreensaver/hacks/bumps.c create mode 100644 xscreensaver/hacks/bumps.h create mode 100644 xscreensaver/hacks/bumps.man create mode 100644 xscreensaver/hacks/ccurve.c create mode 100644 xscreensaver/hacks/ccurve.man create mode 100644 xscreensaver/hacks/celtic.c create mode 100644 xscreensaver/hacks/celtic.man create mode 100644 xscreensaver/hacks/cloudlife.c create mode 100644 xscreensaver/hacks/cloudlife.man create mode 100644 xscreensaver/hacks/compass.c create mode 100644 xscreensaver/hacks/compass.man create mode 100644 xscreensaver/hacks/compile_axp.com create mode 100644 xscreensaver/hacks/compile_decc.com create mode 100644 xscreensaver/hacks/config/README create mode 100644 xscreensaver/hacks/config/abstractile.xml create mode 100644 xscreensaver/hacks/config/anemone.xml create mode 100644 xscreensaver/hacks/config/anemotaxis.xml create mode 100644 xscreensaver/hacks/config/ant.xml create mode 100644 xscreensaver/hacks/config/antinspect.xml create mode 100644 xscreensaver/hacks/config/antmaze.xml create mode 100644 xscreensaver/hacks/config/antspotlight.xml create mode 100644 xscreensaver/hacks/config/apollonian.xml create mode 100644 xscreensaver/hacks/config/apple2.xml create mode 100644 xscreensaver/hacks/config/atlantis.xml create mode 100644 xscreensaver/hacks/config/attraction.xml create mode 100644 xscreensaver/hacks/config/atunnel.xml create mode 100644 xscreensaver/hacks/config/barcode.xml create mode 100644 xscreensaver/hacks/config/blaster.xml create mode 100644 xscreensaver/hacks/config/blinkbox.xml create mode 100644 xscreensaver/hacks/config/blitspin.xml create mode 100644 xscreensaver/hacks/config/blocktube.xml create mode 100644 xscreensaver/hacks/config/boing.xml create mode 100644 xscreensaver/hacks/config/bouboule.xml create mode 100644 xscreensaver/hacks/config/bouncingcow.xml create mode 100644 xscreensaver/hacks/config/boxed.xml create mode 100644 xscreensaver/hacks/config/boxfit.xml create mode 100644 xscreensaver/hacks/config/braid.xml create mode 100644 xscreensaver/hacks/config/bsod.xml create mode 100644 xscreensaver/hacks/config/bubble3d.xml create mode 100644 xscreensaver/hacks/config/bubbles.xml create mode 100644 xscreensaver/hacks/config/bumps.xml create mode 100644 xscreensaver/hacks/config/cage.xml create mode 100644 xscreensaver/hacks/config/carousel.xml create mode 100644 xscreensaver/hacks/config/ccurve.xml create mode 100644 xscreensaver/hacks/config/celtic.xml create mode 100644 xscreensaver/hacks/config/circuit.xml create mode 100644 xscreensaver/hacks/config/cloudlife.xml create mode 100644 xscreensaver/hacks/config/compass.xml create mode 100644 xscreensaver/hacks/config/coral.xml create mode 100644 xscreensaver/hacks/config/cosmos.xml create mode 100644 xscreensaver/hacks/config/crackberg.xml create mode 100644 xscreensaver/hacks/config/critical.xml create mode 100644 xscreensaver/hacks/config/crystal.xml create mode 100644 xscreensaver/hacks/config/cube21.xml create mode 100644 xscreensaver/hacks/config/cubenetic.xml create mode 100644 xscreensaver/hacks/config/cubestorm.xml create mode 100644 xscreensaver/hacks/config/cubicgrid.xml create mode 100644 xscreensaver/hacks/config/cwaves.xml create mode 100644 xscreensaver/hacks/config/cynosure.xml create mode 100644 xscreensaver/hacks/config/dangerball.xml create mode 100644 xscreensaver/hacks/config/decayscreen.xml create mode 100644 xscreensaver/hacks/config/deco.xml create mode 100644 xscreensaver/hacks/config/deluxe.xml create mode 100644 xscreensaver/hacks/config/demon.xml create mode 100644 xscreensaver/hacks/config/discrete.xml create mode 100644 xscreensaver/hacks/config/distort.xml create mode 100644 xscreensaver/hacks/config/dnalogo.xml create mode 100644 xscreensaver/hacks/config/drift.xml create mode 100644 xscreensaver/hacks/config/electricsheep.xml create mode 100644 xscreensaver/hacks/config/endgame.xml create mode 100644 xscreensaver/hacks/config/engine.xml create mode 100644 xscreensaver/hacks/config/epicycle.xml create mode 100644 xscreensaver/hacks/config/eruption.xml create mode 100644 xscreensaver/hacks/config/euler2d.xml create mode 100644 xscreensaver/hacks/config/extrusion.xml create mode 100644 xscreensaver/hacks/config/fadeplot.xml create mode 100644 xscreensaver/hacks/config/fiberlamp.xml create mode 100644 xscreensaver/hacks/config/fireflies.xml create mode 100644 xscreensaver/hacks/config/fireworkx.xml create mode 100644 xscreensaver/hacks/config/flag.xml create mode 100644 xscreensaver/hacks/config/flame.xml create mode 100644 xscreensaver/hacks/config/flipflop.xml create mode 100644 xscreensaver/hacks/config/flipscreen3d.xml create mode 100644 xscreensaver/hacks/config/fliptext.xml create mode 100644 xscreensaver/hacks/config/flow.xml create mode 100644 xscreensaver/hacks/config/fluidballs.xml create mode 100644 xscreensaver/hacks/config/flurry.xml create mode 100644 xscreensaver/hacks/config/flyingtoasters.xml create mode 100644 xscreensaver/hacks/config/fontglide.xml create mode 100644 xscreensaver/hacks/config/forest.xml create mode 100644 xscreensaver/hacks/config/fuzzyflakes.xml create mode 100644 xscreensaver/hacks/config/galaxy.xml create mode 100644 xscreensaver/hacks/config/gears.xml create mode 100644 xscreensaver/hacks/config/gflux.xml create mode 100644 xscreensaver/hacks/config/glblur.xml create mode 100644 xscreensaver/hacks/config/glcells.xml create mode 100644 xscreensaver/hacks/config/gleidescope.xml create mode 100644 xscreensaver/hacks/config/glforestfire.xml create mode 100644 xscreensaver/hacks/config/glhanoi.xml create mode 100644 xscreensaver/hacks/config/glknots.xml create mode 100644 xscreensaver/hacks/config/glmatrix.xml create mode 100644 xscreensaver/hacks/config/glplanet.xml create mode 100644 xscreensaver/hacks/config/glschool.xml create mode 100644 xscreensaver/hacks/config/glslideshow.xml create mode 100644 xscreensaver/hacks/config/glsnake.xml create mode 100644 xscreensaver/hacks/config/gltext.xml create mode 100644 xscreensaver/hacks/config/goban.xml create mode 100644 xscreensaver/hacks/config/goop.xml create mode 100644 xscreensaver/hacks/config/grav.xml create mode 100644 xscreensaver/hacks/config/greynetic.xml create mode 100644 xscreensaver/hacks/config/halftone.xml create mode 100644 xscreensaver/hacks/config/halo.xml create mode 100644 xscreensaver/hacks/config/helix.xml create mode 100644 xscreensaver/hacks/config/hopalong.xml create mode 100644 xscreensaver/hacks/config/hyperball.xml create mode 100644 xscreensaver/hacks/config/hypercube.xml create mode 100644 xscreensaver/hacks/config/hypertorus.xml create mode 100644 xscreensaver/hacks/config/hypnowheel.xml create mode 100644 xscreensaver/hacks/config/ifs.xml create mode 100644 xscreensaver/hacks/config/imsmap.xml create mode 100644 xscreensaver/hacks/config/interaggregate.xml create mode 100644 xscreensaver/hacks/config/interference.xml create mode 100644 xscreensaver/hacks/config/intermomentary.xml create mode 100644 xscreensaver/hacks/config/jigglypuff.xml create mode 100644 xscreensaver/hacks/config/jigsaw.xml create mode 100644 xscreensaver/hacks/config/juggle.xml create mode 100644 xscreensaver/hacks/config/juggler3d.xml create mode 100644 xscreensaver/hacks/config/julia.xml create mode 100644 xscreensaver/hacks/config/kaleidescope.xml create mode 100644 xscreensaver/hacks/config/klein.xml create mode 100644 xscreensaver/hacks/config/kumppa.xml create mode 100644 xscreensaver/hacks/config/lament.xml create mode 100644 xscreensaver/hacks/config/laser.xml create mode 100644 xscreensaver/hacks/config/lavalite.xml create mode 100644 xscreensaver/hacks/config/lcdscrub.xml create mode 100644 xscreensaver/hacks/config/lightning.xml create mode 100644 xscreensaver/hacks/config/lisa.xml create mode 100644 xscreensaver/hacks/config/lissie.xml create mode 100644 xscreensaver/hacks/config/lmorph.xml create mode 100644 xscreensaver/hacks/config/lockward.xml create mode 100644 xscreensaver/hacks/config/loop.xml create mode 100644 xscreensaver/hacks/config/m6502.xml create mode 100644 xscreensaver/hacks/config/maze.xml create mode 100644 xscreensaver/hacks/config/memscroller.xml create mode 100644 xscreensaver/hacks/config/menger.xml create mode 100644 xscreensaver/hacks/config/metaballs.xml create mode 100644 xscreensaver/hacks/config/mirrorblob.xml create mode 100644 xscreensaver/hacks/config/mismunch.xml create mode 100644 xscreensaver/hacks/config/moebius.xml create mode 100644 xscreensaver/hacks/config/moebiusgears.xml create mode 100644 xscreensaver/hacks/config/moire.xml create mode 100644 xscreensaver/hacks/config/moire2.xml create mode 100644 xscreensaver/hacks/config/molecule.xml create mode 100644 xscreensaver/hacks/config/morph3d.xml create mode 100644 xscreensaver/hacks/config/mountain.xml create mode 100644 xscreensaver/hacks/config/munch.xml create mode 100644 xscreensaver/hacks/config/nerverot.xml create mode 100644 xscreensaver/hacks/config/noof.xml create mode 100644 xscreensaver/hacks/config/noseguy.xml create mode 100644 xscreensaver/hacks/config/pacman.xml create mode 100644 xscreensaver/hacks/config/pedal.xml create mode 100644 xscreensaver/hacks/config/penetrate.xml create mode 100644 xscreensaver/hacks/config/penrose.xml create mode 100644 xscreensaver/hacks/config/petri.xml create mode 100644 xscreensaver/hacks/config/phosphor.xml create mode 100644 xscreensaver/hacks/config/piecewise.xml create mode 100644 xscreensaver/hacks/config/pinion.xml create mode 100644 xscreensaver/hacks/config/pipes.xml create mode 100644 xscreensaver/hacks/config/polyhedra.xml create mode 100644 xscreensaver/hacks/config/polyominoes.xml create mode 100644 xscreensaver/hacks/config/polytopes.xml create mode 100644 xscreensaver/hacks/config/pong.xml create mode 100644 xscreensaver/hacks/config/popsquares.xml create mode 100644 xscreensaver/hacks/config/providence.xml create mode 100644 xscreensaver/hacks/config/pulsar.xml create mode 100644 xscreensaver/hacks/config/pyro.xml create mode 100644 xscreensaver/hacks/config/qix.xml create mode 100644 xscreensaver/hacks/config/queens.xml create mode 100644 xscreensaver/hacks/config/rd-bomb.xml create mode 100644 xscreensaver/hacks/config/rdbomb.xml create mode 100644 xscreensaver/hacks/config/ripples.xml create mode 100644 xscreensaver/hacks/config/rocks.xml create mode 100644 xscreensaver/hacks/config/rorschach.xml create mode 100644 xscreensaver/hacks/config/rotor.xml create mode 100644 xscreensaver/hacks/config/rotzoomer.xml create mode 100644 xscreensaver/hacks/config/rubik.xml create mode 100644 xscreensaver/hacks/config/sballs.xml create mode 100644 xscreensaver/hacks/config/shadebobs.xml create mode 100644 xscreensaver/hacks/config/sierpinski.xml create mode 100644 xscreensaver/hacks/config/sierpinski3d.xml create mode 100644 xscreensaver/hacks/config/slidescreen.xml create mode 100644 xscreensaver/hacks/config/slip.xml create mode 100644 xscreensaver/hacks/config/sonar.xml create mode 100644 xscreensaver/hacks/config/speedmine.xml create mode 100644 xscreensaver/hacks/config/sphere.xml create mode 100644 xscreensaver/hacks/config/sphereeversion.xml create mode 100644 xscreensaver/hacks/config/spheremonics.xml create mode 100644 xscreensaver/hacks/config/spiral.xml create mode 100644 xscreensaver/hacks/config/spotlight.xml create mode 100644 xscreensaver/hacks/config/sproingies.xml create mode 100644 xscreensaver/hacks/config/squiral.xml create mode 100644 xscreensaver/hacks/config/stairs.xml create mode 100644 xscreensaver/hacks/config/starfish.xml create mode 100644 xscreensaver/hacks/config/starwars.xml create mode 100644 xscreensaver/hacks/config/stonerview.xml create mode 100644 xscreensaver/hacks/config/strange.xml create mode 100644 xscreensaver/hacks/config/substrate.xml create mode 100644 xscreensaver/hacks/config/superquadrics.xml create mode 100644 xscreensaver/hacks/config/swirl.xml create mode 100644 xscreensaver/hacks/config/t3d.xml create mode 100644 xscreensaver/hacks/config/tangram.xml create mode 100644 xscreensaver/hacks/config/thornbird.xml create mode 100644 xscreensaver/hacks/config/timetunnel.xml create mode 100644 xscreensaver/hacks/config/topblock.xml create mode 100644 xscreensaver/hacks/config/triangle.xml create mode 100644 xscreensaver/hacks/config/truchet.xml create mode 100644 xscreensaver/hacks/config/twang.xml create mode 100644 xscreensaver/hacks/config/vermiculate.xml create mode 100644 xscreensaver/hacks/config/vidwhacker.xml create mode 100644 xscreensaver/hacks/config/vines.xml create mode 100644 xscreensaver/hacks/config/voronoi.xml create mode 100644 xscreensaver/hacks/config/wander.xml create mode 100644 xscreensaver/hacks/config/webcollage.xml create mode 100644 xscreensaver/hacks/config/whirlwindwarp.xml create mode 100644 xscreensaver/hacks/config/whirlygig.xml create mode 100644 xscreensaver/hacks/config/worm.xml create mode 100644 xscreensaver/hacks/config/wormhole.xml create mode 100644 xscreensaver/hacks/config/xanalogtv.xml create mode 100644 xscreensaver/hacks/config/xaos.xml create mode 100644 xscreensaver/hacks/config/xdaliclock.xml create mode 100644 xscreensaver/hacks/config/xflame.xml create mode 100644 xscreensaver/hacks/config/xjack.xml create mode 100644 xscreensaver/hacks/config/xlyap.xml create mode 100644 xscreensaver/hacks/config/xmatrix.xml create mode 100644 xscreensaver/hacks/config/xmountains.xml create mode 100644 xscreensaver/hacks/config/xplanet.xml create mode 100644 xscreensaver/hacks/config/xrayswarm.xml create mode 100644 xscreensaver/hacks/config/xsnow.xml create mode 100644 xscreensaver/hacks/config/xspirograph.xml create mode 100644 xscreensaver/hacks/config/xss.dtd create mode 100644 xscreensaver/hacks/config/xss.xsd create mode 100644 xscreensaver/hacks/config/xteevee.xml create mode 100644 xscreensaver/hacks/config/zoom.xml create mode 100644 xscreensaver/hacks/coral.c create mode 100644 xscreensaver/hacks/coral.man create mode 100644 xscreensaver/hacks/critical.c create mode 100644 xscreensaver/hacks/critical.man create mode 100644 xscreensaver/hacks/crystal.c create mode 100644 xscreensaver/hacks/crystal.man create mode 100644 xscreensaver/hacks/cwaves.c create mode 100644 xscreensaver/hacks/cwaves.man create mode 100644 xscreensaver/hacks/cynosure.c create mode 100644 xscreensaver/hacks/cynosure.man create mode 100644 xscreensaver/hacks/decayscreen.c create mode 100644 xscreensaver/hacks/decayscreen.man create mode 100644 xscreensaver/hacks/deco.c create mode 100644 xscreensaver/hacks/deco.man create mode 100644 xscreensaver/hacks/deluxe.c create mode 100644 xscreensaver/hacks/deluxe.man create mode 100644 xscreensaver/hacks/demon.c create mode 100644 xscreensaver/hacks/demon.man create mode 100644 xscreensaver/hacks/discrete.c create mode 100644 xscreensaver/hacks/discrete.man create mode 100644 xscreensaver/hacks/distort.c create mode 100644 xscreensaver/hacks/distort.man create mode 100644 xscreensaver/hacks/drift.c create mode 100644 xscreensaver/hacks/drift.man create mode 100644 xscreensaver/hacks/epicycle.c create mode 100644 xscreensaver/hacks/epicycle.man create mode 100644 xscreensaver/hacks/eruption.c create mode 100644 xscreensaver/hacks/eruption.man create mode 100644 xscreensaver/hacks/euler2d.c create mode 100644 xscreensaver/hacks/euler2d.man create mode 100644 xscreensaver/hacks/euler2d.tex create mode 100644 xscreensaver/hacks/fadeplot.c create mode 100644 xscreensaver/hacks/fadeplot.man create mode 100644 xscreensaver/hacks/fiberlamp.c create mode 100644 xscreensaver/hacks/fiberlamp.man create mode 100644 xscreensaver/hacks/fireworkx.c create mode 100644 xscreensaver/hacks/fireworkx.man create mode 100644 xscreensaver/hacks/fireworkx_mmx.S create mode 100644 xscreensaver/hacks/flag.c create mode 100644 xscreensaver/hacks/flag.man create mode 100644 xscreensaver/hacks/flame.c create mode 100644 xscreensaver/hacks/flame.man create mode 100644 xscreensaver/hacks/flow.c create mode 100644 xscreensaver/hacks/flow.man create mode 100644 xscreensaver/hacks/fluidballs.c create mode 100644 xscreensaver/hacks/fluidballs.man create mode 100644 xscreensaver/hacks/fontglide.c create mode 100644 xscreensaver/hacks/fontglide.man create mode 100644 xscreensaver/hacks/forest.c create mode 100644 xscreensaver/hacks/forest.man create mode 100644 xscreensaver/hacks/fuzzyflakes.c create mode 100644 xscreensaver/hacks/fuzzyflakes.man create mode 100644 xscreensaver/hacks/galaxy.c create mode 100644 xscreensaver/hacks/galaxy.man create mode 100644 xscreensaver/hacks/glx/Makefile.in create mode 100644 xscreensaver/hacks/glx/README create mode 100644 xscreensaver/hacks/glx/antinspect.c create mode 100644 xscreensaver/hacks/glx/antinspect.man create mode 100644 xscreensaver/hacks/glx/antmaze.c create mode 100644 xscreensaver/hacks/glx/antmaze.man create mode 100644 xscreensaver/hacks/glx/ants.h create mode 100644 xscreensaver/hacks/glx/antspotlight.c create mode 100644 xscreensaver/hacks/glx/antspotlight.man create mode 100644 xscreensaver/hacks/glx/atlantis.c create mode 100644 xscreensaver/hacks/glx/atlantis.h create mode 100644 xscreensaver/hacks/glx/atlantis.man create mode 100644 xscreensaver/hacks/glx/atunnel.c create mode 100644 xscreensaver/hacks/glx/atunnel.man create mode 100644 xscreensaver/hacks/glx/b_draw.c create mode 100644 xscreensaver/hacks/glx/b_lockglue.c create mode 100644 xscreensaver/hacks/glx/b_sphere.c create mode 100644 xscreensaver/hacks/glx/blinkbox.c create mode 100644 xscreensaver/hacks/glx/blinkbox.man create mode 100644 xscreensaver/hacks/glx/blocktube.c create mode 100644 xscreensaver/hacks/glx/blocktube.man create mode 100644 xscreensaver/hacks/glx/boing.c create mode 100644 xscreensaver/hacks/glx/boing.man create mode 100644 xscreensaver/hacks/glx/bouncingcow.c create mode 100644 xscreensaver/hacks/glx/bouncingcow.man create mode 100644 xscreensaver/hacks/glx/boxed.c create mode 100644 xscreensaver/hacks/glx/boxed.h create mode 100644 xscreensaver/hacks/glx/boxed.man create mode 100644 xscreensaver/hacks/glx/bubble3d.c create mode 100644 xscreensaver/hacks/glx/bubble3d.h create mode 100644 xscreensaver/hacks/glx/bubble3d.man create mode 100644 xscreensaver/hacks/glx/buildlwo.c create mode 100644 xscreensaver/hacks/glx/buildlwo.h create mode 100644 xscreensaver/hacks/glx/cage.c create mode 100644 xscreensaver/hacks/glx/cage.man create mode 100644 xscreensaver/hacks/glx/carousel.c create mode 100644 xscreensaver/hacks/glx/carousel.man create mode 100644 xscreensaver/hacks/glx/chessgames.h create mode 100644 xscreensaver/hacks/glx/chessmodels.c create mode 100644 xscreensaver/hacks/glx/chessmodels.h create mode 100644 xscreensaver/hacks/glx/circuit.c create mode 100644 xscreensaver/hacks/glx/circuit.man create mode 100644 xscreensaver/hacks/glx/cow_face.c create mode 100644 xscreensaver/hacks/glx/cow_hide.c create mode 100644 xscreensaver/hacks/glx/cow_hoofs.c create mode 100644 xscreensaver/hacks/glx/cow_horns.c create mode 100644 xscreensaver/hacks/glx/cow_tail.c create mode 100644 xscreensaver/hacks/glx/cow_udder.c create mode 100644 xscreensaver/hacks/glx/crackberg.c create mode 100644 xscreensaver/hacks/glx/crackberg.man create mode 100644 xscreensaver/hacks/glx/cube21.c create mode 100644 xscreensaver/hacks/glx/cube21.man create mode 100644 xscreensaver/hacks/glx/cubenetic.c create mode 100644 xscreensaver/hacks/glx/cubenetic.man create mode 100644 xscreensaver/hacks/glx/cubestorm.c create mode 100644 xscreensaver/hacks/glx/cubestorm.man create mode 100644 xscreensaver/hacks/glx/cubicgrid.c create mode 100644 xscreensaver/hacks/glx/cubicgrid.man create mode 100644 xscreensaver/hacks/glx/dangerball.c create mode 100644 xscreensaver/hacks/glx/dangerball.man create mode 100644 xscreensaver/hacks/glx/dnalogo.c create mode 100644 xscreensaver/hacks/glx/dolphin.c create mode 100644 xscreensaver/hacks/glx/dxf2gl.pl create mode 100644 xscreensaver/hacks/glx/e_textures.h create mode 100644 xscreensaver/hacks/glx/endgame.c create mode 100644 xscreensaver/hacks/glx/endgame.man create mode 100644 xscreensaver/hacks/glx/engine.c create mode 100644 xscreensaver/hacks/glx/engine.man create mode 100644 xscreensaver/hacks/glx/extrusion-helix2.c create mode 100644 xscreensaver/hacks/glx/extrusion-helix3.c create mode 100644 xscreensaver/hacks/glx/extrusion-helix4.c create mode 100644 xscreensaver/hacks/glx/extrusion-joinoffset.c create mode 100644 xscreensaver/hacks/glx/extrusion-screw.c create mode 100644 xscreensaver/hacks/glx/extrusion-taper.c create mode 100644 xscreensaver/hacks/glx/extrusion-twistoid.c create mode 100644 xscreensaver/hacks/glx/extrusion.c create mode 100644 xscreensaver/hacks/glx/extrusion.h create mode 100644 xscreensaver/hacks/glx/extrusion.man create mode 100644 xscreensaver/hacks/glx/flipflop.c create mode 100644 xscreensaver/hacks/glx/flipflop.man create mode 100644 xscreensaver/hacks/glx/flipscreen3d.c create mode 100644 xscreensaver/hacks/glx/flipscreen3d.man create mode 100644 xscreensaver/hacks/glx/fliptext.c create mode 100644 xscreensaver/hacks/glx/fliptext.man create mode 100644 xscreensaver/hacks/glx/flurry-smoke.c create mode 100644 xscreensaver/hacks/glx/flurry-spark.c create mode 100644 xscreensaver/hacks/glx/flurry-star.c create mode 100644 xscreensaver/hacks/glx/flurry-texture.c create mode 100644 xscreensaver/hacks/glx/flurry.c create mode 100644 xscreensaver/hacks/glx/flurry.h create mode 100644 xscreensaver/hacks/glx/flurry.man create mode 100644 xscreensaver/hacks/glx/flyingtoasters.c create mode 100644 xscreensaver/hacks/glx/flyingtoasters.man create mode 100644 xscreensaver/hacks/glx/font-ximage.c create mode 100644 xscreensaver/hacks/glx/font-ximage.h create mode 100644 xscreensaver/hacks/glx/fps.c create mode 100644 xscreensaver/hacks/glx/gears.c create mode 100644 xscreensaver/hacks/glx/gears.man create mode 100644 xscreensaver/hacks/glx/gflux.c create mode 100644 xscreensaver/hacks/glx/gflux.man create mode 100644 xscreensaver/hacks/glx/glblur.c create mode 100644 xscreensaver/hacks/glx/glblur.man create mode 100644 xscreensaver/hacks/glx/glcells.c create mode 100644 xscreensaver/hacks/glx/glcells.man create mode 100644 xscreensaver/hacks/glx/gleidescope.c create mode 100644 xscreensaver/hacks/glx/gleidescope.man create mode 100644 xscreensaver/hacks/glx/glforestfire.c create mode 100644 xscreensaver/hacks/glx/glforestfire.man create mode 100644 xscreensaver/hacks/glx/glhanoi.c create mode 100644 xscreensaver/hacks/glx/glhanoi.man create mode 100644 xscreensaver/hacks/glx/glknots.c create mode 100644 xscreensaver/hacks/glx/glknots.man create mode 100644 xscreensaver/hacks/glx/gllist.c create mode 100644 xscreensaver/hacks/glx/gllist.h create mode 100644 xscreensaver/hacks/glx/glmatrix.c create mode 100644 xscreensaver/hacks/glx/glmatrix.man create mode 100644 xscreensaver/hacks/glx/glplanet.c create mode 100644 xscreensaver/hacks/glx/glplanet.man create mode 100644 xscreensaver/hacks/glx/glschool.c create mode 100644 xscreensaver/hacks/glx/glschool.h create mode 100644 xscreensaver/hacks/glx/glschool.man create mode 100644 xscreensaver/hacks/glx/glschool_alg.c create mode 100644 xscreensaver/hacks/glx/glschool_alg.h create mode 100644 xscreensaver/hacks/glx/glschool_gl.c create mode 100644 xscreensaver/hacks/glx/glschool_gl.h create mode 100644 xscreensaver/hacks/glx/glslideshow.c create mode 100644 xscreensaver/hacks/glx/glslideshow.man create mode 100644 xscreensaver/hacks/glx/glsnake.c create mode 100644 xscreensaver/hacks/glx/glsnake.man create mode 100644 xscreensaver/hacks/glx/gltext.c create mode 100644 xscreensaver/hacks/glx/gltext.man create mode 100644 xscreensaver/hacks/glx/gltrackball.c create mode 100644 xscreensaver/hacks/glx/gltrackball.h create mode 100644 xscreensaver/hacks/glx/glut_roman.h create mode 100644 xscreensaver/hacks/glx/glut_stroke.c create mode 100644 xscreensaver/hacks/glx/glut_swidth.c create mode 100644 xscreensaver/hacks/glx/glutstroke.h create mode 100644 xscreensaver/hacks/glx/glxfonts.c create mode 100644 xscreensaver/hacks/glx/glxfonts.h create mode 100644 xscreensaver/hacks/glx/grab-ximage.c create mode 100644 xscreensaver/hacks/glx/grab-ximage.h create mode 100644 xscreensaver/hacks/glx/hypertorus.c create mode 100644 xscreensaver/hacks/glx/hypertorus.man create mode 100644 xscreensaver/hacks/glx/hypnowheel.c create mode 100644 xscreensaver/hacks/glx/hypnowheel.man create mode 100644 xscreensaver/hacks/glx/involute.c create mode 100644 xscreensaver/hacks/glx/involute.h create mode 100644 xscreensaver/hacks/glx/jigglypuff.c create mode 100644 xscreensaver/hacks/glx/jigglypuff.man create mode 100644 xscreensaver/hacks/glx/juggler3d.c create mode 100644 xscreensaver/hacks/glx/juggler3d.man create mode 100644 xscreensaver/hacks/glx/klein.c create mode 100644 xscreensaver/hacks/glx/klein.man create mode 100644 xscreensaver/hacks/glx/lament.c create mode 100644 xscreensaver/hacks/glx/lament.man create mode 100644 xscreensaver/hacks/glx/lavalite.c create mode 100644 xscreensaver/hacks/glx/lavalite.man create mode 100644 xscreensaver/hacks/glx/lockward.c create mode 100644 xscreensaver/hacks/glx/lockward.man create mode 100644 xscreensaver/hacks/glx/marching.c create mode 100644 xscreensaver/hacks/glx/marching.h create mode 100644 xscreensaver/hacks/glx/menger.c create mode 100644 xscreensaver/hacks/glx/menger.man create mode 100644 xscreensaver/hacks/glx/mirrorblob.c create mode 100644 xscreensaver/hacks/glx/mirrorblob.man create mode 100644 xscreensaver/hacks/glx/moebius.c create mode 100644 xscreensaver/hacks/glx/moebius.man create mode 100644 xscreensaver/hacks/glx/moebiusgears.c create mode 100644 xscreensaver/hacks/glx/moebiusgears.man create mode 100644 xscreensaver/hacks/glx/molecule.c create mode 100644 xscreensaver/hacks/glx/molecule.man create mode 100644 xscreensaver/hacks/glx/molecules.sh create mode 100644 xscreensaver/hacks/glx/morph3d.c create mode 100644 xscreensaver/hacks/glx/morph3d.man create mode 100644 xscreensaver/hacks/glx/noof.c create mode 100644 xscreensaver/hacks/glx/noof.man create mode 100644 xscreensaver/hacks/glx/normals.c create mode 100644 xscreensaver/hacks/glx/normals.h create mode 100644 xscreensaver/hacks/glx/pinion.c create mode 100644 xscreensaver/hacks/glx/pinion.man create mode 100644 xscreensaver/hacks/glx/pipeobjs.c create mode 100644 xscreensaver/hacks/glx/pipes.c create mode 100644 xscreensaver/hacks/glx/pipes.man create mode 100644 xscreensaver/hacks/glx/polyhedra-gl.c create mode 100644 xscreensaver/hacks/glx/polyhedra.c create mode 100644 xscreensaver/hacks/glx/polyhedra.h create mode 100644 xscreensaver/hacks/glx/polyhedra.man create mode 100644 xscreensaver/hacks/glx/polytopes.c create mode 100644 xscreensaver/hacks/glx/polytopes.man create mode 100644 xscreensaver/hacks/glx/providence.c create mode 100644 xscreensaver/hacks/glx/providence.man create mode 100644 xscreensaver/hacks/glx/pulsar.c create mode 100644 xscreensaver/hacks/glx/pulsar.man create mode 100644 xscreensaver/hacks/glx/queens.c create mode 100644 xscreensaver/hacks/glx/queens.man create mode 100644 xscreensaver/hacks/glx/rotator.c create mode 100644 xscreensaver/hacks/glx/rotator.h create mode 100644 xscreensaver/hacks/glx/rubik.c create mode 100644 xscreensaver/hacks/glx/rubik.man create mode 100644 xscreensaver/hacks/glx/s1_1.c create mode 100644 xscreensaver/hacks/glx/s1_2.c create mode 100644 xscreensaver/hacks/glx/s1_3.c create mode 100644 xscreensaver/hacks/glx/s1_4.c create mode 100644 xscreensaver/hacks/glx/s1_5.c create mode 100644 xscreensaver/hacks/glx/s1_6.c create mode 100644 xscreensaver/hacks/glx/s1_b.c create mode 100644 xscreensaver/hacks/glx/sballs.c create mode 100644 xscreensaver/hacks/glx/sballs.man create mode 100644 xscreensaver/hacks/glx/shark.c create mode 100644 xscreensaver/hacks/glx/sierpinski3d.c create mode 100644 xscreensaver/hacks/glx/sierpinski3d.man create mode 100644 xscreensaver/hacks/glx/sphere.c create mode 100644 xscreensaver/hacks/glx/sphere.h create mode 100644 xscreensaver/hacks/glx/spheremonics.c create mode 100644 xscreensaver/hacks/glx/spheremonics.man create mode 100644 xscreensaver/hacks/glx/sproingies.c create mode 100644 xscreensaver/hacks/glx/sproingies.h create mode 100644 xscreensaver/hacks/glx/sproingies.man create mode 100644 xscreensaver/hacks/glx/sproingiewrap.c create mode 100644 xscreensaver/hacks/glx/stairs.c create mode 100644 xscreensaver/hacks/glx/stairs.man create mode 100644 xscreensaver/hacks/glx/starwars.c create mode 100644 xscreensaver/hacks/glx/starwars.man create mode 100644 xscreensaver/hacks/glx/starwars.txt create mode 100644 xscreensaver/hacks/glx/stonerview-move.c create mode 100644 xscreensaver/hacks/glx/stonerview-move.h create mode 100644 xscreensaver/hacks/glx/stonerview-osc.c create mode 100644 xscreensaver/hacks/glx/stonerview-osc.h create mode 100644 xscreensaver/hacks/glx/stonerview-view.c create mode 100644 xscreensaver/hacks/glx/stonerview.c create mode 100644 xscreensaver/hacks/glx/stonerview.h create mode 100644 xscreensaver/hacks/glx/stonerview.man create mode 100644 xscreensaver/hacks/glx/superquadrics.c create mode 100644 xscreensaver/hacks/glx/superquadrics.man create mode 100644 xscreensaver/hacks/glx/swim.c create mode 100644 xscreensaver/hacks/glx/tangram.c create mode 100644 xscreensaver/hacks/glx/tangram.man create mode 100644 xscreensaver/hacks/glx/tangram_shapes.c create mode 100644 xscreensaver/hacks/glx/tangram_shapes.h create mode 100644 xscreensaver/hacks/glx/texfont.c create mode 100644 xscreensaver/hacks/glx/texfont.h create mode 100644 xscreensaver/hacks/glx/timetunnel.c create mode 100644 xscreensaver/hacks/glx/timetunnel.man create mode 100644 xscreensaver/hacks/glx/toast.c create mode 100644 xscreensaver/hacks/glx/toast2.c create mode 100644 xscreensaver/hacks/glx/toaster.c create mode 100644 xscreensaver/hacks/glx/toaster_base.c create mode 100644 xscreensaver/hacks/glx/toaster_handle.c create mode 100644 xscreensaver/hacks/glx/toaster_handle2.c create mode 100644 xscreensaver/hacks/glx/toaster_jet.c create mode 100644 xscreensaver/hacks/glx/toaster_knob.c create mode 100644 xscreensaver/hacks/glx/toaster_slots.c create mode 100644 xscreensaver/hacks/glx/toaster_wing.c create mode 100644 xscreensaver/hacks/glx/topblock.c create mode 100644 xscreensaver/hacks/glx/topblock.h create mode 100644 xscreensaver/hacks/glx/topblock.man create mode 100644 xscreensaver/hacks/glx/trackball.c create mode 100644 xscreensaver/hacks/glx/trackball.h create mode 100644 xscreensaver/hacks/glx/tube.c create mode 100644 xscreensaver/hacks/glx/tube.h create mode 100644 xscreensaver/hacks/glx/tunnel_draw.c create mode 100644 xscreensaver/hacks/glx/tunnel_draw.h create mode 100644 xscreensaver/hacks/glx/voronoi.c create mode 100644 xscreensaver/hacks/glx/voronoi.man create mode 100644 xscreensaver/hacks/glx/whale.c create mode 100644 xscreensaver/hacks/glx/xlock-gl-utils.c create mode 100644 xscreensaver/hacks/glx/xpm-ximage.c create mode 100644 xscreensaver/hacks/glx/xpm-ximage.h create mode 100644 xscreensaver/hacks/glx/xscreensaver-gl-helper.c create mode 100644 xscreensaver/hacks/glx/xscreensaver-gl-helper.man create mode 100644 xscreensaver/hacks/goop.c create mode 100644 xscreensaver/hacks/goop.man create mode 100644 xscreensaver/hacks/grav.c create mode 100644 xscreensaver/hacks/grav.man create mode 100644 xscreensaver/hacks/greynetic.c create mode 100644 xscreensaver/hacks/greynetic.man create mode 100644 xscreensaver/hacks/halftone.c create mode 100644 xscreensaver/hacks/halftone.man create mode 100644 xscreensaver/hacks/halo.c create mode 100644 xscreensaver/hacks/halo.man create mode 100644 xscreensaver/hacks/helix.c create mode 100644 xscreensaver/hacks/helix.man create mode 100644 xscreensaver/hacks/hopalong.c create mode 100644 xscreensaver/hacks/hopalong.man create mode 100644 xscreensaver/hacks/hyperball.c create mode 100644 xscreensaver/hacks/hyperball.man create mode 100644 xscreensaver/hacks/hypercube.c create mode 100644 xscreensaver/hacks/hypercube.man create mode 100644 xscreensaver/hacks/ifs.c create mode 100644 xscreensaver/hacks/ifs.man create mode 100644 xscreensaver/hacks/images/6x10font.xbm create mode 100644 xscreensaver/hacks/images/amiga.xpm create mode 100644 xscreensaver/hacks/images/apple2font.xbm create mode 100644 xscreensaver/hacks/images/atari.xbm create mode 100644 xscreensaver/hacks/images/blocktube.xpm create mode 100644 xscreensaver/hacks/images/bob.xbm create mode 100644 xscreensaver/hacks/images/bubbles/blood.pov create mode 100644 xscreensaver/hacks/images/bubbles/blood1.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blood10.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blood11.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blood2.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blood3.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blood4.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blood5.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blood6.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blood7.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blood8.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blood9.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue.pov create mode 100644 xscreensaver/hacks/images/bubbles/blue1.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue10.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue11.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue2.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue3.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue4.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue5.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue6.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue7.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue8.xpm create mode 100644 xscreensaver/hacks/images/bubbles/blue9.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass.pov create mode 100644 xscreensaver/hacks/images/bubbles/glass1.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass10.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass11.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass2.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass3.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass4.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass5.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass6.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass7.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass8.xpm create mode 100644 xscreensaver/hacks/images/bubbles/glass9.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade.pov create mode 100644 xscreensaver/hacks/images/bubbles/jade1.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade10.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade11.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade2.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade3.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade4.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade5.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade6.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade7.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade8.xpm create mode 100644 xscreensaver/hacks/images/bubbles/jade9.xpm create mode 100644 xscreensaver/hacks/images/chromesphere.xpm create mode 100644 xscreensaver/hacks/images/earth.xpm create mode 100644 xscreensaver/hacks/images/ground.xpm create mode 100644 xscreensaver/hacks/images/hmac.xpm create mode 100644 xscreensaver/hacks/images/jigglymap.xpm create mode 100644 xscreensaver/hacks/images/lament.xpm create mode 100644 xscreensaver/hacks/images/m6502/breakout.asm create mode 100644 xscreensaver/hacks/images/m6502/byterun.asm create mode 100644 xscreensaver/hacks/images/m6502/cellular-30.asm create mode 100644 xscreensaver/hacks/images/m6502/cellular-600.asm create mode 100644 xscreensaver/hacks/images/m6502/crunch6502.asm create mode 100644 xscreensaver/hacks/images/m6502/demoscene.asm create mode 100644 xscreensaver/hacks/images/m6502/dmsc.asm create mode 100644 xscreensaver/hacks/images/m6502/dragon-fractal.asm create mode 100644 xscreensaver/hacks/images/m6502/fullscreenlogo.asm create mode 100644 xscreensaver/hacks/images/m6502/keftal.asm create mode 100644 xscreensaver/hacks/images/m6502/matrix.asm create mode 100644 xscreensaver/hacks/images/m6502/noise.asm create mode 100644 xscreensaver/hacks/images/m6502/random-walk.asm create mode 100644 xscreensaver/hacks/images/m6502/random.asm create mode 100644 xscreensaver/hacks/images/m6502/random2.asm create mode 100644 xscreensaver/hacks/images/m6502/rorschach.asm create mode 100644 xscreensaver/hacks/images/m6502/santa.asm create mode 100644 xscreensaver/hacks/images/m6502/selfmodify.asm create mode 100644 xscreensaver/hacks/images/m6502/sierpinsky.asm create mode 100644 xscreensaver/hacks/images/m6502/spacer.asm create mode 100644 xscreensaver/hacks/images/m6502/wave6502.asm create mode 100644 xscreensaver/hacks/images/m6502/zookeeper.asm create mode 100644 xscreensaver/hacks/images/mac.xbm create mode 100644 xscreensaver/hacks/images/macbomb.xbm create mode 100644 xscreensaver/hacks/images/matrix1.xbm create mode 100644 xscreensaver/hacks/images/matrix1.xpm create mode 100644 xscreensaver/hacks/images/matrix1b.xbm create mode 100644 xscreensaver/hacks/images/matrix1b.xpm create mode 100644 xscreensaver/hacks/images/matrix2.xbm create mode 100644 xscreensaver/hacks/images/matrix2.xpm create mode 100644 xscreensaver/hacks/images/matrix2b.xbm create mode 100644 xscreensaver/hacks/images/matrix2b.xpm create mode 100644 xscreensaver/hacks/images/matrix3.xpm create mode 100644 xscreensaver/hacks/images/molecules/adenine.pdb create mode 100644 xscreensaver/hacks/images/molecules/adrenochrome.pdb create mode 100644 xscreensaver/hacks/images/molecules/bucky.pdb create mode 100644 xscreensaver/hacks/images/molecules/caffeine.pdb create mode 100644 xscreensaver/hacks/images/molecules/chlordecone.pdb create mode 100644 xscreensaver/hacks/images/molecules/cocaine.pdb create mode 100644 xscreensaver/hacks/images/molecules/codeine.pdb create mode 100644 xscreensaver/hacks/images/molecules/cyclohexane.pdb create mode 100644 xscreensaver/hacks/images/molecules/cytosine.pdb create mode 100644 xscreensaver/hacks/images/molecules/dna.pdb create mode 100644 xscreensaver/hacks/images/molecules/dodecahedrane.pdb create mode 100644 xscreensaver/hacks/images/molecules/dthc.pdb create mode 100644 xscreensaver/hacks/images/molecules/dynamite.pdb create mode 100644 xscreensaver/hacks/images/molecules/glycol.pdb create mode 100644 xscreensaver/hacks/images/molecules/guanine.pdb create mode 100644 xscreensaver/hacks/images/molecules/heroin.pdb create mode 100644 xscreensaver/hacks/images/molecules/hexahelicene.pdb create mode 100644 xscreensaver/hacks/images/molecules/ibuprofen.pdb create mode 100644 xscreensaver/hacks/images/molecules/lsd.pdb create mode 100644 xscreensaver/hacks/images/molecules/menthol.pdb create mode 100644 xscreensaver/hacks/images/molecules/mescaline.pdb create mode 100644 xscreensaver/hacks/images/molecules/methamphetamine.pdb create mode 100644 xscreensaver/hacks/images/molecules/morphine.pdb create mode 100644 xscreensaver/hacks/images/molecules/nicotine.pdb create mode 100644 xscreensaver/hacks/images/molecules/novocaine.pdb create mode 100644 xscreensaver/hacks/images/molecules/olestra.pdb create mode 100644 xscreensaver/hacks/images/molecules/penicillin.pdb create mode 100644 xscreensaver/hacks/images/molecules/sarin.pdb create mode 100644 xscreensaver/hacks/images/molecules/strychnine.pdb create mode 100644 xscreensaver/hacks/images/molecules/sucrose.pdb create mode 100644 xscreensaver/hacks/images/molecules/thalidomide.pdb create mode 100644 xscreensaver/hacks/images/molecules/thymine.pdb create mode 100644 xscreensaver/hacks/images/molecules/viagra.pdb create mode 100644 xscreensaver/hacks/images/molecules/vitaminb6.pdb create mode 100644 xscreensaver/hacks/images/molecules/vitaminc.pdb create mode 100644 xscreensaver/hacks/images/molecules/vx.pdb create mode 100644 xscreensaver/hacks/images/noseguy/nose-f1.xbm create mode 100644 xscreensaver/hacks/images/noseguy/nose-f1.xpm create mode 100644 xscreensaver/hacks/images/noseguy/nose-f2.xbm create mode 100644 xscreensaver/hacks/images/noseguy/nose-f2.xpm create mode 100644 xscreensaver/hacks/images/noseguy/nose-f3.xbm create mode 100644 xscreensaver/hacks/images/noseguy/nose-f3.xpm create mode 100644 xscreensaver/hacks/images/noseguy/nose-f4.xbm create mode 100644 xscreensaver/hacks/images/noseguy/nose-f4.xpm create mode 100644 xscreensaver/hacks/images/noseguy/nose-l1.xbm create mode 100644 xscreensaver/hacks/images/noseguy/nose-l1.xpm create mode 100644 xscreensaver/hacks/images/noseguy/nose-l2.xbm create mode 100644 xscreensaver/hacks/images/noseguy/nose-l2.xpm create mode 100644 xscreensaver/hacks/images/noseguy/nose-r1.xbm create mode 100644 xscreensaver/hacks/images/noseguy/nose-r1.xpm create mode 100644 xscreensaver/hacks/images/noseguy/nose-r2.xbm create mode 100644 xscreensaver/hacks/images/noseguy/nose-r2.xpm create mode 100644 xscreensaver/hacks/images/osx_10_2.xpm create mode 100644 xscreensaver/hacks/images/osx_10_3.xpm create mode 100644 xscreensaver/hacks/images/pacman/eyes-d.xpm create mode 100644 xscreensaver/hacks/images/pacman/eyes-l.xpm create mode 100644 xscreensaver/hacks/images/pacman/eyes-r.xpm create mode 100644 xscreensaver/hacks/images/pacman/eyes-u.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-d1.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-d2.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-l1.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-l2.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-mask.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-r1.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-r2.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-s1.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-s2.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-sf1.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-sf2.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-u1.xpm create mode 100644 xscreensaver/hacks/images/pacman/ghost-u2.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-0.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-d1.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-d2.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-ds1.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-ds2.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-ds3.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-ds4.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-ds5.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-ds6.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-ds7.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-ds8.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-l1.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-l2.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-r1.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-r2.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-u1.xpm create mode 100644 xscreensaver/hacks/images/pacman/pacman-u2.xpm create mode 100644 xscreensaver/hacks/images/sball-bg.xpm create mode 100644 xscreensaver/hacks/images/sball.xpm create mode 100644 xscreensaver/hacks/images/sea-texture.xpm create mode 100644 xscreensaver/hacks/images/som.xbm create mode 100644 xscreensaver/hacks/images/timetunnel0.xpm create mode 100644 xscreensaver/hacks/images/timetunnel1.xpm create mode 100644 xscreensaver/hacks/images/timetunnel2.xpm create mode 100644 xscreensaver/hacks/images/toast.xpm create mode 100644 xscreensaver/hacks/images/tree.xpm create mode 100644 xscreensaver/hacks/images/tunnel0.xpm create mode 100644 xscreensaver/hacks/images/tunnel1.xpm create mode 100644 xscreensaver/hacks/images/tunnel2.xpm create mode 100644 xscreensaver/hacks/images/tunnel3.xpm create mode 100644 xscreensaver/hacks/images/tunnel4.xpm create mode 100644 xscreensaver/hacks/images/tunnel5.xpm create mode 100644 xscreensaver/hacks/images/tunnelstar.xpm create mode 100644 xscreensaver/hacks/imsmap.c create mode 100644 xscreensaver/hacks/imsmap.man create mode 100644 xscreensaver/hacks/interaggregate.c create mode 100644 xscreensaver/hacks/interaggregate.man create mode 100644 xscreensaver/hacks/interference.c create mode 100644 xscreensaver/hacks/interference.man create mode 100644 xscreensaver/hacks/intermomentary.c create mode 100644 xscreensaver/hacks/intermomentary.man create mode 100644 xscreensaver/hacks/jigsaw.c create mode 100644 xscreensaver/hacks/jigsaw.man create mode 100644 xscreensaver/hacks/juggle.c create mode 100644 xscreensaver/hacks/juggle.man create mode 100644 xscreensaver/hacks/julia.c create mode 100644 xscreensaver/hacks/julia.man create mode 100644 xscreensaver/hacks/kaleidescope.c create mode 100644 xscreensaver/hacks/kaleidescope.man create mode 100644 xscreensaver/hacks/kumppa.c create mode 100644 xscreensaver/hacks/kumppa.man create mode 100644 xscreensaver/hacks/laser.c create mode 100644 xscreensaver/hacks/laser.man create mode 100644 xscreensaver/hacks/lcdscrub create mode 100644 xscreensaver/hacks/lcdscrub.c create mode 100644 xscreensaver/hacks/lightning.c create mode 100644 xscreensaver/hacks/lightning.man create mode 100644 xscreensaver/hacks/link_axp.com create mode 100644 xscreensaver/hacks/link_decc.com create mode 100644 xscreensaver/hacks/lisa.c create mode 100644 xscreensaver/hacks/lisa.man create mode 100644 xscreensaver/hacks/lissie.c create mode 100644 xscreensaver/hacks/lissie.man create mode 100644 xscreensaver/hacks/ljlatest create mode 100644 xscreensaver/hacks/ljlatest.man create mode 100644 xscreensaver/hacks/lmorph.c create mode 100644 xscreensaver/hacks/lmorph.man create mode 100644 xscreensaver/hacks/loop.c create mode 100644 xscreensaver/hacks/loop.man create mode 100644 xscreensaver/hacks/m6502.c create mode 100644 xscreensaver/hacks/m6502.sh create mode 100644 xscreensaver/hacks/maze.c create mode 100644 xscreensaver/hacks/maze.man create mode 100644 xscreensaver/hacks/memscroller.c create mode 100644 xscreensaver/hacks/memscroller.man create mode 100644 xscreensaver/hacks/metaballs.c create mode 100644 xscreensaver/hacks/metaballs.man create mode 100644 xscreensaver/hacks/mismunch.c create mode 100644 xscreensaver/hacks/mismunch.man create mode 100644 xscreensaver/hacks/moire.c create mode 100644 xscreensaver/hacks/moire.man create mode 100644 xscreensaver/hacks/moire2.c create mode 100644 xscreensaver/hacks/moire2.man create mode 100644 xscreensaver/hacks/mountain.c create mode 100644 xscreensaver/hacks/mountain.man create mode 100644 xscreensaver/hacks/munch.c create mode 100644 xscreensaver/hacks/munch.man create mode 100644 xscreensaver/hacks/nerverot.c create mode 100644 xscreensaver/hacks/nerverot.man create mode 100644 xscreensaver/hacks/noseguy.c create mode 100644 xscreensaver/hacks/noseguy.man create mode 100644 xscreensaver/hacks/pacman.c create mode 100644 xscreensaver/hacks/pacman.h create mode 100644 xscreensaver/hacks/pacman.man create mode 100644 xscreensaver/hacks/pacman_ai.c create mode 100644 xscreensaver/hacks/pacman_ai.h create mode 100644 xscreensaver/hacks/pacman_level.c create mode 100644 xscreensaver/hacks/pacman_level.h create mode 100644 xscreensaver/hacks/pedal.c create mode 100644 xscreensaver/hacks/pedal.man create mode 100644 xscreensaver/hacks/penetrate.c create mode 100644 xscreensaver/hacks/penetrate.man create mode 100644 xscreensaver/hacks/penrose.c create mode 100644 xscreensaver/hacks/penrose.man create mode 100644 xscreensaver/hacks/petri.c create mode 100644 xscreensaver/hacks/petri.man create mode 100644 xscreensaver/hacks/phosphor.c create mode 100644 xscreensaver/hacks/phosphor.man create mode 100644 xscreensaver/hacks/piecewise.c create mode 100644 xscreensaver/hacks/piecewise.man create mode 100644 xscreensaver/hacks/polyominoes.c create mode 100644 xscreensaver/hacks/polyominoes.man create mode 100644 xscreensaver/hacks/pong.c create mode 100644 xscreensaver/hacks/pong.man create mode 100644 xscreensaver/hacks/popsquares.c create mode 100644 xscreensaver/hacks/pyro.c create mode 100644 xscreensaver/hacks/pyro.man create mode 100644 xscreensaver/hacks/qix.c create mode 100644 xscreensaver/hacks/qix.man create mode 100644 xscreensaver/hacks/rd-bomb.c create mode 100644 xscreensaver/hacks/rd-bomb.man create mode 100644 xscreensaver/hacks/ripples.c create mode 100644 xscreensaver/hacks/ripples.man create mode 100644 xscreensaver/hacks/rocks.c create mode 100644 xscreensaver/hacks/rocks.man create mode 100644 xscreensaver/hacks/rorschach.c create mode 100644 xscreensaver/hacks/rorschach.man create mode 100644 xscreensaver/hacks/rotor.c create mode 100644 xscreensaver/hacks/rotor.man create mode 100644 xscreensaver/hacks/rotzoomer.c create mode 100644 xscreensaver/hacks/rotzoomer.man create mode 100644 xscreensaver/hacks/screenhack.c create mode 100644 xscreensaver/hacks/screenhack.h create mode 100644 xscreensaver/hacks/screenhackI.h create mode 100644 xscreensaver/hacks/shadebobs.c create mode 100644 xscreensaver/hacks/shadebobs.man create mode 100644 xscreensaver/hacks/sierpinski.c create mode 100644 xscreensaver/hacks/sierpinski.man create mode 100644 xscreensaver/hacks/slidescreen.c create mode 100644 xscreensaver/hacks/slidescreen.man create mode 100644 xscreensaver/hacks/slip.c create mode 100644 xscreensaver/hacks/slip.man create mode 100644 xscreensaver/hacks/sonar.c create mode 100644 xscreensaver/hacks/sonar.man create mode 100644 xscreensaver/hacks/speedmine.c create mode 100644 xscreensaver/hacks/speedmine.man create mode 100644 xscreensaver/hacks/sphere.c create mode 100644 xscreensaver/hacks/sphere.man create mode 100644 xscreensaver/hacks/spiral.c create mode 100644 xscreensaver/hacks/spiral.man create mode 100644 xscreensaver/hacks/spotlight.c create mode 100644 xscreensaver/hacks/spotlight.man create mode 100644 xscreensaver/hacks/squiral.c create mode 100644 xscreensaver/hacks/squiral.man create mode 100644 xscreensaver/hacks/starfish.c create mode 100644 xscreensaver/hacks/starfish.man create mode 100644 xscreensaver/hacks/strange.c create mode 100644 xscreensaver/hacks/strange.man create mode 100644 xscreensaver/hacks/substrate.c create mode 100644 xscreensaver/hacks/substrate.man create mode 100644 xscreensaver/hacks/swirl.c create mode 100644 xscreensaver/hacks/swirl.man create mode 100644 xscreensaver/hacks/t3d.c create mode 100644 xscreensaver/hacks/t3d.man create mode 100644 xscreensaver/hacks/thornbird.c create mode 100644 xscreensaver/hacks/thornbird.man create mode 100644 xscreensaver/hacks/triangle.c create mode 100644 xscreensaver/hacks/triangle.man create mode 100644 xscreensaver/hacks/truchet.c create mode 100644 xscreensaver/hacks/truchet.man create mode 100644 xscreensaver/hacks/twang.c create mode 100644 xscreensaver/hacks/twang.man create mode 100644 xscreensaver/hacks/vermiculate.c create mode 100644 xscreensaver/hacks/vermiculate.man create mode 100644 xscreensaver/hacks/vidwhacker create mode 100644 xscreensaver/hacks/vidwhacker.man create mode 100644 xscreensaver/hacks/vines.c create mode 100644 xscreensaver/hacks/vines.man create mode 100644 xscreensaver/hacks/vms_axp.opt create mode 100644 xscreensaver/hacks/vms_axp_12.opt create mode 100644 xscreensaver/hacks/vms_decc.opt create mode 100644 xscreensaver/hacks/vms_decc_12.opt create mode 100644 xscreensaver/hacks/wander.c create mode 100644 xscreensaver/hacks/wander.man create mode 100644 xscreensaver/hacks/webcollage create mode 100644 xscreensaver/hacks/webcollage-cocoa.m create mode 100644 xscreensaver/hacks/webcollage-helper-cocoa.m create mode 100644 xscreensaver/hacks/webcollage-helper.c create mode 100644 xscreensaver/hacks/webcollage.man create mode 100644 xscreensaver/hacks/whirlwindwarp.c create mode 100644 xscreensaver/hacks/whirlwindwarp.man create mode 100644 xscreensaver/hacks/whirlygig.c create mode 100644 xscreensaver/hacks/whirlygig.man create mode 100644 xscreensaver/hacks/worm.c create mode 100644 xscreensaver/hacks/worm.man create mode 100644 xscreensaver/hacks/wormhole.c create mode 100644 xscreensaver/hacks/wormhole.man create mode 100644 xscreensaver/hacks/xanalogtv.c create mode 100644 xscreensaver/hacks/xanalogtv.man create mode 100644 xscreensaver/hacks/xflame.c create mode 100644 xscreensaver/hacks/xflame.man create mode 100644 xscreensaver/hacks/xjack.c create mode 100644 xscreensaver/hacks/xjack.man create mode 100644 xscreensaver/hacks/xlockmore.c create mode 100644 xscreensaver/hacks/xlockmore.h create mode 100644 xscreensaver/hacks/xlockmoreI.h create mode 100644 xscreensaver/hacks/xlyap.c create mode 100644 xscreensaver/hacks/xlyap.man create mode 100644 xscreensaver/hacks/xmatrix.c create mode 100644 xscreensaver/hacks/xmatrix.man create mode 100644 xscreensaver/hacks/xml2man.pl create mode 100644 xscreensaver/hacks/xpm-pixmap.c create mode 100644 xscreensaver/hacks/xpm-pixmap.h create mode 100644 xscreensaver/hacks/xrayswarm.c create mode 100644 xscreensaver/hacks/xrayswarm.man create mode 100644 xscreensaver/hacks/xscreensaver-sgigl.c create mode 100644 xscreensaver/hacks/xspirograph.c create mode 100644 xscreensaver/hacks/xspirograph.man create mode 100644 xscreensaver/hacks/xsublim.c create mode 100644 xscreensaver/hacks/xsublim.man create mode 100644 xscreensaver/hacks/zoom.c create mode 100644 xscreensaver/hacks/zoom.man create mode 100644 xscreensaver/install-sh create mode 100644 xscreensaver/intltool-extract.in create mode 100644 xscreensaver/intltool-merge.in create mode 100644 xscreensaver/intltool-update.in create mode 100644 xscreensaver/makevms.com create mode 100644 xscreensaver/po/ChangeLog create mode 100644 xscreensaver/po/Makefile.in.in create mode 100644 xscreensaver/po/POTFILES.in create mode 100644 xscreensaver/po/ca.po create mode 100644 xscreensaver/po/da.po create mode 100644 xscreensaver/po/de.po create mode 100644 xscreensaver/po/es.po create mode 100644 xscreensaver/po/et.po create mode 100644 xscreensaver/po/fi.po create mode 100644 xscreensaver/po/fr.po create mode 100644 xscreensaver/po/hu.po create mode 100644 xscreensaver/po/it.po create mode 100644 xscreensaver/po/ja.po create mode 100644 xscreensaver/po/ko.po create mode 100644 xscreensaver/po/nb.po create mode 100644 xscreensaver/po/nl.po create mode 100644 xscreensaver/po/no.po create mode 100644 xscreensaver/po/pl.po create mode 100644 xscreensaver/po/pt.po create mode 100644 xscreensaver/po/pt_BR.po create mode 100644 xscreensaver/po/ru.po create mode 100644 xscreensaver/po/sk.po create mode 100644 xscreensaver/po/sv.po create mode 100644 xscreensaver/po/update.sh create mode 100644 xscreensaver/po/vi.po create mode 100644 xscreensaver/po/wa.po create mode 100644 xscreensaver/po/zh_CN.po create mode 100644 xscreensaver/po/zh_TW.po create mode 100644 xscreensaver/setup.com create mode 100644 xscreensaver/utils/Makefile.in create mode 100644 xscreensaver/utils/README create mode 100644 xscreensaver/utils/ad2c create mode 100644 xscreensaver/utils/alpha.c create mode 100644 xscreensaver/utils/alpha.h create mode 100644 xscreensaver/utils/colorbars.c create mode 100644 xscreensaver/utils/colorbars.h create mode 100644 xscreensaver/utils/colors.c create mode 100644 xscreensaver/utils/colors.h create mode 100644 xscreensaver/utils/compile_axp.com create mode 100644 xscreensaver/utils/compile_decc.com create mode 100644 xscreensaver/utils/erase.c create mode 100644 xscreensaver/utils/erase.h create mode 100644 xscreensaver/utils/fade.c create mode 100644 xscreensaver/utils/fade.h create mode 100644 xscreensaver/utils/grabclient.c create mode 100644 xscreensaver/utils/grabscreen.c create mode 100644 xscreensaver/utils/grabscreen.h create mode 100644 xscreensaver/utils/hsv.c create mode 100644 xscreensaver/utils/hsv.h create mode 100644 xscreensaver/utils/images/logo-180.gif create mode 100644 xscreensaver/utils/images/logo-180.xpm create mode 100644 xscreensaver/utils/images/logo-50.gif create mode 100644 xscreensaver/utils/images/logo-50.xpm create mode 100644 xscreensaver/utils/images/logo-big.gif create mode 100644 xscreensaver/utils/images/logo.eps create mode 100644 xscreensaver/utils/images/screensaver-cmndln.png create mode 100644 xscreensaver/utils/images/screensaver-colorselector.png create mode 100644 xscreensaver/utils/images/screensaver-diagnostic.png create mode 100644 xscreensaver/utils/images/screensaver-locking.png create mode 100644 xscreensaver/utils/images/screensaver-power.png create mode 100644 xscreensaver/utils/images/screensaver-snap.png create mode 100644 xscreensaver/utils/logo.c create mode 100644 xscreensaver/utils/minixpm.c create mode 100644 xscreensaver/utils/minixpm.h create mode 100644 xscreensaver/utils/overlay.c create mode 100644 xscreensaver/utils/resources.c create mode 100644 xscreensaver/utils/resources.h create mode 100644 xscreensaver/utils/spline.c create mode 100644 xscreensaver/utils/spline.h create mode 100644 xscreensaver/utils/usleep.c create mode 100644 xscreensaver/utils/usleep.h create mode 100644 xscreensaver/utils/utils.h create mode 100644 xscreensaver/utils/version.h create mode 100644 xscreensaver/utils/visual-gl.c create mode 100644 xscreensaver/utils/visual.c create mode 100644 xscreensaver/utils/visual.h create mode 100644 xscreensaver/utils/vms-gtod.c create mode 100644 xscreensaver/utils/vms-gtod.h create mode 100644 xscreensaver/utils/vms-strdup.c create mode 100644 xscreensaver/utils/vroot.h create mode 100644 xscreensaver/utils/xdbe.c create mode 100644 xscreensaver/utils/xdbe.h create mode 100644 xscreensaver/utils/xmu.c create mode 100644 xscreensaver/utils/xmu.h create mode 100644 xscreensaver/utils/xscreensaver-intl.h create mode 100644 xscreensaver/utils/xshm.c create mode 100644 xscreensaver/utils/xshm.h create mode 100644 xscreensaver/utils/yarandom.c create mode 100644 xscreensaver/utils/yarandom.h create mode 100644 xscreensaver/xscreensaver.spec create mode 100644 xscreensaver/xscreensaver.xcodeproj/project.pbxproj diff --git a/xscreensaver/INSTALL b/xscreensaver/INSTALL new file mode 100644 index 0000000..50dbe43 --- /dev/null +++ b/xscreensaver/INSTALL @@ -0,0 +1,183 @@ +Basic 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. + diff --git a/xscreensaver/Makefile.in b/xscreensaver/Makefile.in new file mode 100644 index 0000000..99b6f4a --- /dev/null +++ b/xscreensaver/Makefile.in @@ -0,0 +1,381 @@ +# Makefile.in --- xscreensaver, Copyright (c) 1999-2005 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 +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) generate_potfiles_in + +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... ; \ + 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.) +# +configure:: + autoconf + autoheader + @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 '' ; \ + \ + OLDEST=`ls xscreensaver*.tar.gz | head -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 ; \ + else \ + set -x ; \ + fi ; \ + 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" + +check_years:: + @for file in `find . -name '*.*' \ + \! -name '*~' \! -name '*.o' \! -name '*.gz' \! -name '*.Z' \ + \! -name '*.po' \! -name 'config.*' \! -name '*.glade*' \ + \! -name '*.m4' \! -name '*.pdb' \! -name '*.xpm' \ + \! -name '*.jpg' \! -name '*.gif' \! -name '*.eps' \ + \! -name '*.png' \! -name '*.tif' \! -name '*.bak' \ + | sort` \ + ; do \ + y=`date -r "$$file" '+%Y'` ; \ + if ! ( grep "\b$$y\b" $$file >/dev/null ); then \ + if ( grep "opyright\|(c)\|\.BR" $$file >/dev/null ); then \ + echo "$$file does not mention $$y" ; \ + fi ; \ + fi ; \ + done + +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 diff --git a/xscreensaver/OSX/AppController.h b/xscreensaver/OSX/AppController.h new file mode 100644 index 0000000..98d9f82 --- /dev/null +++ b/xscreensaver/OSX/AppController.h @@ -0,0 +1,21 @@ +/* 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. + */ + +#import +#import + +@interface AppController : NSObject +{ + ScreenSaverView *saverView0; + ScreenSaverView *saverView1; +} + +@end diff --git a/xscreensaver/OSX/AppController.m b/xscreensaver/OSX/AppController.m new file mode 100644 index 0000000..76911b3 --- /dev/null +++ b/xscreensaver/OSX/AppController.m @@ -0,0 +1,200 @@ +/* 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 just a test harness, really -- it makes a window with an + XScreenSaverGLView in it and a Preferences button, because I don't want + to have to debug these programs by installing them as screen savers + first! + */ + +#import "AppController.h" +#import "XScreenSaverGLView.h" + +@implementation AppController + +- (ScreenSaverView *) makeSaverView +{ + const char *var = "SCREENSAVER_MODULE"; + const char *s = getenv (var); + if (!s || !*s) { + NSAssert1 (0, @"set $%s to the name of the module to run", var); + exit(1); + } + NSString *module = [NSString stringWithCString:s + encoding:NSUTF8StringEncoding]; + NSString *dir = [[[NSBundle mainBundle] bundlePath] + stringByDeletingLastPathComponent]; + NSString *name = [module stringByAppendingPathExtension:@"saver"]; + NSString *path = [dir stringByAppendingPathComponent:name]; + NSBundle *bundleToLoad = [NSBundle bundleWithPath:path]; + Class new_class = [bundleToLoad 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); + return (ScreenSaverView *) instance; +} + + +- (void) openPreferences: (int) which +{ + ScreenSaverView *sv = (which ? saverView1 : saverView0); + 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) { + if (saverView0) [saverView0 stopAnimation]; + if (saverView1) [saverView1 stopAnimation]; + if (saverView0) [saverView0 startAnimation]; + if (saverView1) [saverView1 startAnimation]; + } +} + +- (void) openPreferences0: (NSObject *) button +{ + [self openPreferences:0]; +} + +- (void) openPreferences1: (NSObject *) button +{ + [self openPreferences:1]; +} + +- (void) preferencesClosed: (NSWindow *) sheet + returnCode: (int) returnCode + contextInfo: (void *) contextInfo +{ + [NSApp stopModalWithCode:returnCode]; +} + + +- (void) makeWindow: (ScreenSaverView *)sv which:(int)which +{ + NSRect rect; + + if (which) + saverView1 = sv; + else + saverView0 = sv; + + // make a "Preferences" button + // + rect.origin.x = rect.origin.y = 0; + rect.size.width = rect.size.height = 10; + NSButton *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]; + if (which) + [pb setAction:@selector(openPreferences1:)]; + else + [pb setAction:@selector(openPreferences0:)]; + + // 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; + NSBox *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; + NSBox *pbox = [[NSBox alloc] initWithFrame:rect]; + [pbox setTitlePosition:NSNoTitle]; + [pbox setBorderType:NSNoBorder]; + [pbox addSubview:gbox]; + [pbox addSubview:pb]; + [pbox sizeToFit]; + + // and make a window to hold that. + // + NSScreen *screen = [NSScreen mainScreen]; + rect = [pbox 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 * (which ? 0.55 : -0.55); + + NSWindow *window = [[NSWindow alloc] + initWithContentRect:rect + styleMask:(NSTitledWindowMask | + NSClosableWindowMask | + NSMiniaturizableWindowMask | + NSResizableWindowMask) + backing:NSBackingStoreBuffered + defer:YES + screen:screen]; + [window setTitle:@"XScreenSaver"]; + [window setMinSize:[window frameRectForContentRect:rect].size]; + + [[window contentView] addSubview:pbox]; + + [sv setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; + [pb setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin]; + [gbox setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; + [pbox setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; + + [window setFrameAutosaveName:(which + ? @"SaverDebugWindow1" + : @"SaverDebugWindow0")]; + [window setFrameUsingName:[window frameAutosaveName]]; + + [window makeKeyAndOrderFront:window]; + + [sv startAnimation]; +} + + +- (void)applicationDidFinishLaunching: (NSNotification *) n +{ + [self makeWindow:[self makeSaverView] which:0]; + [self makeWindow:[self makeSaverView] which:1]; +} + +/* When the window closes, exit (even if prefs still open.) +*/ +- (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication *) n +{ + return YES; +} + +@end diff --git a/xscreensaver/OSX/English.lproj/InfoPlist.strings b/xscreensaver/OSX/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..dea12de4cad936a6204d4da70d2ca96aef900b31 GIT binary patch literal 92 zcmW-Z!3uyN5C!M#S9tbN-x2r|Q3;V~qzLu#)x*oq?lA28G2*azG7B@2orjH8u89{# bCX+-f2F*!V&^~bXzEEWk)pxI)ej3aVcM}l{ literal 0 HcmV?d00001 diff --git a/xscreensaver/OSX/English.lproj/MainMenu.nib/classes.nib b/xscreensaver/OSX/English.lproj/MainMenu.nib/classes.nib new file mode 100644 index 0000000..cbc6c75 --- /dev/null +++ b/xscreensaver/OSX/English.lproj/MainMenu.nib/classes.nib @@ -0,0 +1,9 @@ +{ + IBClasses = ( + {CLASS = AppController; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + {CLASS = InvertedSlider; LANGUAGE = ObjC; SUPERCLASS = NSSlider; }, + {CLASS = PrefsReader; LANGUAGE = ObjC; SUPERCLASS = NSObject; } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/xscreensaver/OSX/English.lproj/MainMenu.nib/info.nib b/xscreensaver/OSX/English.lproj/MainMenu.nib/info.nib new file mode 100644 index 0000000..a3bd34e --- /dev/null +++ b/xscreensaver/OSX/English.lproj/MainMenu.nib/info.nib @@ -0,0 +1,21 @@ + + + + + IBDocumentLocation + 135 107 356 240 0 0 1680 1028 + IBEditorPositions + + 29 + 132 353 321 44 0 0 1680 1028 + + IBFramework Version + 443.0 + IBOpenObjects + + 29 + + IBSystem Version + 8G32 + + diff --git a/xscreensaver/OSX/English.lproj/MainMenu.nib/keyedobjects.nib b/xscreensaver/OSX/English.lproj/MainMenu.nib/keyedobjects.nib new file mode 100644 index 0000000000000000000000000000000000000000..e6c5e48add7b89c7bbfdaaf8a809dda9476b3e70 GIT binary patch literal 11645 zcmbVy33wF6)^=4-^3tIvqAtdaRWReUdnUI+PiKx|pAYKs! zLEPBFBA}v(fXF7usw@I-Tu~5l_v+;e?((1RNkY8H|2^LaPiCsO_dTcTJ*Q4}O+jT@ zI2ubyxqvWYkOC3JA|*;dn*5|-Rbgp(MyM(;2|l9XNJUOkEK-@56e*Y*DvU)ZbH>c< z;h~yPQCj_tW`yUjc?YSGdg9Rj?c=u<1!F;qx0!GlX^{?fL|su2)DI0ngV8XQi7HSG z%|rh{kDx_pDO!deM=Q}Y=vlM|)uByjE831;K|9bc^g23(j-q4eIC=}6LGPjW(Ff?C z=o9og`Wk(MzD3`o8|Zh8F@qIYiM7~_E!d7dxEW5u&2bCd26uv1XWRw%!2NI!A!f8amyEk?oU z7>+S9X2!xe7@qM%OJJHa?U?q^>cDhndNKDvt2dLz3}J>c!9v6Zo$=C3ZyV7Yzn)= zq2Lv*m{%3873~!%iq48IihC5j6@%eSx?-pzPccPNs+bN(Rf?Emw&Fo(J*b$kc!W6* zt>ubU@VQE{T2ZH1uh^)#Of;yPbRk_yH`1N-AU#QM(g)7;CH>&s05S;LL&-2Q9IYmO zNN@NYK{CiFk_D|CxH6Urk~}gFjwX#j zhq}R~?kE-D+ec$n;ffNVO{;|bR~U?eAC#O!Jy9=o59*y4cQX_n7jIMmMjh&n`k=n@ z5M@-yg3u}1GD==%c4H&%c^oGV^V;vST1hoy>7V`wxf|MuLs~ zAeoR3SBIdXFqHKfG`fbW;)ui1h&nU^WuU$=P7OAMVq?I7P%QrBNL0R!^Py2F3yntE zC8z&{@?Ko6mX=wV=%v;w-33>X>PGFd>d{%OTst;H)V@{0*ii;ElQys!J32Z6z7Xn6@{u~wLwf(6a}k_O51UL$(Vk6xZUv0Vz?cS%eg2ja-H+P=V~6_g?+ax8Br@n_nXYLskC6!fg5M4B zQvrVv;19SPey=9|IK}g5KXEl z$^@d36497Z!DuX$H6xB&Bqajr#O*|YO8_}c#{lx^yCIhWaz#CIHX!Fn$T_1!MUkv= zao`wA9a9CzO>k!dZVep=xMS~zI~#ECugA>;+jAeAj+@|a0^H4XD&Q91iMv%+%kzNyqKsQYO93}5;nwGQ zydBiCgMI*N`7jL0w-VBC8T^;BeF>!Co^j&c@?;}rz;vj%qYc5{AlcW!hAF=Kd?0KN$7rN2>ifHI<>sTw*LzQc@O@=JRkxb)-5fQ_D|)VLclh{Q%f6 z(pkV>eFr;|XH|R&9Q{Ogl&3R*eWv8-m;q&xXeevu#QN$&K5Rt&98kZcvjMgC4%GUH z|EAHWA@tq`pK=F+fMcPtMe2BiRZ{~d7gS`z=<2&bbDG{EJIi$uyN)LSUE z8xR?Usu+gO2gEra{&#iC5Qddx#wZbGRFdP27Rv3MUYz`4X&OS~X>r)DI^U0K_D^1P~Y99kC@Kww4jw0^*~9xL8J1Oq3BvRfT4RK`>eA z4X7!A+KDa))TMVv?Fy*fWz?R4x(rYsmr)a7ei#!C`QsT2-1kE@>X(?ZLJb6cfS@0J z3J9LKV}R=iV*tPplHdV+C4fIEfx{d(th&5Xsw5lXv!=qFUKpx?Dx&e+(2DvcZCX_* z*x3CefHjk@2G(cpV3kygeM}axX3MNY=(E7OD$Z(?Ck#JS3;v8?tgzId9jhq|W#xiB z^30JI9aR<#SLDFh+@5Wjam;vT0!pE4X&qhPxN2plz)E%9M3_12Ywt36`z8fINkf6p z6fvPXCKNBzt#Uy=vKm%FvBoN0UI#EGOlcic%7o?RXZ=z!4a(^nSe?k<@^X=x#?)=& zy5kzA9B;6CAy1lr)6|6y+}vX4)hFtfj&durN`(}+J~N|L+JhV zAWfkzdX^^9bjs5pJwQunEq#d2q0?y(noOUdT3SuJ($*l5s~6)z&1|^6=wvoBv!t*_ zdzsA*$%1(iR+8D(1?8}+f<Io~roP-^|Mo_P(Gug4dedFV8(UaG;bB=CL1ondq^o^7Fit*>F3V^D(+ zjb?``gH=Iz^#ewE8jMoM2mA!8UI&i${X4XHVcrA{y07W;@Irz)50y^*B~OF3?}4>2 zYB!ksV0ov7Uf&LV{$fR2xWPB+|%;VoM!yf`WrwO}gyH34qplUn ziZ*DWq8)vc9tB+=ku|Jn)u>@bM^p~4dSG!5quTKF|7}<&0D`ne2uY9stqbuW#p#NE zjV=s=p&LR^HoEXu&Y+?&y!!$PD+b@Tau_Oq9xudU$WDXslQ^Hr#9u;|o69U==0naI z4S6IT@+6#XKM?q3<;k zy`R@mn8pKP0V=P9$zdN9rp1t!r{4v+KB3N5EN(=89L7pV|JjKA(ck)B|KMfC%Ekwu zg$I-A$Bhquk|VvEh?}zpm9K@DBCY9$@t~jEnE~G1DmF0Tb<&g12y z9i4!yDUi{0^;b5@*WQIssnn64y9D1lcq#=t=q$Y&_wGx?Wb z<9+gM-A}R~X7>4bGx!g3!GoBG(Z{$3dSMB3FFu7%gM1!f#=-0ab3V-Smw~NJj*v}| z+Y&)kwZObxo{gW7?VA7*Qw)|!l1qnpY7KG3fK8V`B1s^@y)dI!gP$iL+LG%nC0f~1 zq9b}@AV$Iw6EPDDu@W1!9mGjo#7#WJOMD~|&iIKy0;CzVnv)i!C22)klVs9{v?c9G zd(wfVkdCAi{epf;zoK8$Z|Jx5JNhsBJ^caN|EAaJf9Q|&CjE*2On-qhztZ36@AMC7 z{Yh_$7>O9R_!to@L`+1?idZRP*i%r6SS@0Wh_&=;lb6A%P1+rsw0p=g3EPw~&F6H4 zfATTug;KM+QPSkb&Q01~q!-kX_N2GW9XrLJNP{OjQSuJ5lY#VxJgbwzB)yKLlOd?D z^mZCHo5sT1Vd=XGR;Q*|O-Z^ny^sQ*!PWX}47@m`q$x@LE#OIg5kaOfb!!?1zpzQWSQ5e*5MeO{-ey$7 zFlY8*v-r1N4 zl3oC1DTSg`gR(*R51>6D`5Cas0R<_Pp*K(v%7!Ee`uR*P8pF)RsW=~YNKV79NDAHu zJ0>BJza15zG6;;>C=WtA%8ZjrR(R2O8g@IL1Hmr7@5TXgvh`Fl!*_bg)Nr2}+W+ za=F{1p||(TJxmUegX9fzh#V$wk|X3OIYy3?6XYa$i@Z%vk<;W1IZNIl=g4_-fxJuJ zBkz+B$cN-2`6u~^Tp}NnPsn9*g?vh`lF!IB@;Ui}d`Z3{Uz2ahx8ytWFY-P4f&80X zC;uTok{jeE`HB2Yej&e--^lOe5Ar9u#Ud6%u2isuWmzSgz^Yg^t6{aQj@7dU*2r?K zi8Zqp5pyCoiP$V+Xj?^W6R};y4iP&=>=LnC#2yiQMeGxCqKJ7B`$a5>I3VI?B2E%< za}l=?aZ3@m5^-x0CyTg^h}(*|orv3uxPypOMBGutokZMO#9c((Rm9yy++D<}BJLsL zo+9og;(J8gTf}`t+*ic?M4Tq#{vsYA;(;O_B;vs$P8abI5f2sdFcA+I@dy!Th&WTk zBSkz)#91O9E#hnu=ZJWWh;v0eR>XNC&KL1G5sw$~1QAaZ@gxyX7V#7j2Sr>U;zAJ@ z(W~4u+%e9_z0Muyp5j>U6!#>zgUjX|+zGA+*PPqTy~^cs!?+i?N4b;SRPJeRJ@*_p zpL>X_T*~3bK=?TbQo+7>GuZX+h? z>_)}UQqcPpztH0?USJE)1c`OL9JhaQm>QWL2WwtaX}*da3BSK3NReD`X|DM z{&cpMUBoVCSF&~NdUhkbjorx}W>2u^lt!gVX;Ipg4y8-!QTmj;QcyNiHdnS(wpO-L zwo`Ucc2ss&c2#y)_E7dx_Ez>)rYQ#~2PxB)LzTmodCDouuyUHRTp3X=Q9hyEpnOgF zhVm`td&;ZIYs#;c-z)#E{Eza6@+ajl37P~`f-iwj5E7atG*4)skdn|lVMIb^!l;DN z3F8vV5~>qsCoE2=OL!?^d%}){oe3ur&LzB`@M*#~Dy&kdbSjIgjjE?AT{ToST$Q1! zP*tj`R58^I)ht!5YO!jG>PgjB)eEXUs=ccHs)MR4s;jDNsxMSuslHKtr}|a(yXsFh zQtQ=5wNuTj1$BFMin^1!i+YTDtU6ykUOiDgSshf*QrD{Is+X!CSFcsqsW+%MsgJ8q zs^3>Y(snOi4xli+eW|?NWW|d~MW~*keX20g3=8)!1%@xg6%{9#zny)n9Xui`jTCKK? zww<<*wx71YcA$2!wnQ7&R%k1=Roa+#hIW>Ao_4-=we}_LcI^)BPVH{(huVK?FKIu~ zUeR9FUeo@fy`|%HW}Q`M*Ew}PbiH)Fb$xYdx&gXDx=dY(E~cBIo29GOJ*|6Iw_3MW zSEt*c+oao}+o{{F+oRjBJE%LMJFPpbyQKR>cSUzqPxMN?O0UuD^aeerx9gpHLElQB ztnaSxq3@;dt)HZyqA$=F>5KKH`l@u*k5?u-vfDu->rI@SNd<;Vr`{!x_UnhVzDZ4WAl5Gkk9N z((s$%52MoPG`fu~jjfGsjO~n>#!<%6#vEg=G0!;87&cBb#*A}~4;q&mA2+Trt~9=8 zeBHRuc)<9E@v!lT@l#I0ah#d6a(1peB%)qiZ>}$w#tnc3F`Uciin&rqMrGV0NIs8o z%OLGM0f}o3q^4K7U677m=RV>-<}P!ea-VUZb6;}bale`pOlp(Xq&IakrJ8!0?lJW- z^)vN14KxilWt+yB#+vd?MW$jCH7znNHa%uqX4-AqW7=!lZ#rl?WO~zd)O6N#&h&}t zis`E9nptDknGI&nY&KiXcC*vm!raQ-)!g0O!`#a}#av)6G8dam%~Q=~<_hxy^Fs3q z^Gfqm=4Z_N%m>VGm=Bwen2(uHm@k<>G2gQ2ECvf_F~whdRTf{dRzKh##-_% z<(B&`)H261&obY#*z%ZVnPshIi{%x|9?LtHivA1psxv9-z?v(B*2vesH>Tkp40 z>m2Jm>wN1&)`zW+SRb`6u`abfZe3wrX?@E2jCGZDjdh)My>+AYIqMed^VS!wFIl%+ zcUX5?cU$*Z_geQ`4_Xgd-?SdJ9=D#fzHL2iJ!?H@yl|9+s*52Np zVjp84YtOfjw@G6_UZN&_LcUh?9bR&+1J?D+3W1j+h4UGv>&p+X+LT|Za-;% z+kV6Tll>R_Z}vazw;b4^aIg-u!{g}W=;G++NOkmd+~eru=;!F~80Z-67~&Y_nC2*V zL>$u{QAf37reld?sbiPpHOK3YeU1Z;Hyno@Z#uqneDC+!=9BcSfDn&Y8{{XRULA^CjnY=MLvi=Wgd7=U(T2 z=RxNY=V|9z=ZDVg&L5pOoj*H&b^h-B(}i4&i@207l}qE&xjMQ!ySlo%yLz~Kxq7?$ zxC&h%SBWd^n&v8ZMO>AxCtXjwo^`Eut##G8Hn=vqHoIPQ?Q*^5I^;U#I^%lBb>8)^ z>wVXUZpKaAO1H|baqHX$H|I9HJ?@Iarb(gu9 zyPt4B>3-V%tb4V4t-H>>!M(-(ihHMfw|kHKsQZljg8Pyu(=*C5+LPnS_2hZRc_w%! zd8T*@JVl;jPpN0Br_58~sq|ENVxAeES)N+YY|s53>Y3x2=b7($$n&u05znKZC7z|8 z$2}`ND?Lwnp7E^mtnsY#toLm6Jm=ZsdEWD)=Oxc}&koN{&u-5i&tA`d&oQsl>-KuR ziC(`q;7#(j@V4?Md)s>3dsDofyj{HAys6%v-g~@#y#2iWy#u|2y+gdiyd%7s-cjDs z-W+ePH_toHJHb22JH=bzE%Fw7OTE**hrDlkk9v=LPkP_>p7x&gp7UPtzUTeGd(r!m z_hav6@2B3+yq|l&^nUIA*84B-58mtEAH6rdKYM@k{^`R$;!E&pe0m?}v-s>jm(S}< z^d8-_MP>8`C488l#+UOEemWoJtNEFH4Sz3xAO8R^@^kqI`33w!{vZ4zelhkbKfyoA zKg~bOujbeCb^Hc?6Tg|?%D=#G<6q`q;a}x<@vrf(^ZWP%{2TmX{s@1JKf%AnpW@H( z@9^jOkNgY$kN6k+m-?6cSNfm!ukx?;ulH~AZ}Gq2f64!ff2aR7|6czA{~`Yo|8f6Y z{?q<<{1^Q1`!D)0`7isg`ak!7<^R_Iz5lxZhW}^(Z~i|8ED#|<&7%xl`fm=+5#do`iLg|7Tv#Ej6rK{E5mpIngmuDtVWaSzutj)Ycu{ys z*e>i4b_%a0c7~Zy+(? z4+H{9ffj*Qf#g8jK>I*Spi`h%;GRIAKzd+EU|1kKkQ2xaOb!$w`5&xp<$r8-{JY^Z G5dD8jcABaH literal 0 HcmV?d00001 diff --git a/xscreensaver/OSX/InvertedSlider.h b/xscreensaver/OSX/InvertedSlider.h new file mode 100644 index 0000000..71efb16 --- /dev/null +++ b/xscreensaver/OSX/InvertedSlider.h @@ -0,0 +1,20 @@ +/* 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 diff --git a/xscreensaver/OSX/InvertedSlider.m b/xscreensaver/OSX/InvertedSlider.m new file mode 100644 index 0000000..e5c23b2 --- /dev/null +++ b/xscreensaver/OSX/InvertedSlider.m @@ -0,0 +1,102 @@ +/* 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 "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; +{ + return [[NSAttributedString alloc] initWithString:[self stringValue]]; +} + + +/* Implment all setter methods in terms of "setDoubleValue", above. + */ + +-(void) setFloatValue:(double)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 diff --git a/xscreensaver/OSX/Makefile b/xscreensaver/OSX/Makefile new file mode 100644 index 0000000..1a222c7 --- /dev/null +++ b/xscreensaver/OSX/Makefile @@ -0,0 +1,120 @@ +# XScreenSaver for MacOS X, Copyright (c) 2006 by Jamie Zawinski. + +XCODE_TARGET = "All Savers" + +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: + codesign -vfs 'Jamie Zawinski' build/Release/*.saver + +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 ; 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 SaverTester.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" ; \ + 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" ; \ + echo + cp -pr "$$SRC/"\*.saver "$$STAGE" ; \ + cp -pr "$$SRC/"*.saver "$$STAGE" ; \ + 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" ; \ + 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. diff --git a/xscreensaver/OSX/PrefsReader.h b/xscreensaver/OSX/PrefsReader.h new file mode 100644 index 0000000..9905aeb --- /dev/null +++ b/xscreensaver/OSX/PrefsReader.h @@ -0,0 +1,39 @@ +/* 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 diff --git a/xscreensaver/OSX/PrefsReader.m b/xscreensaver/OSX/PrefsReader.m new file mode 100644 index 0000000..9ff55bb --- /dev/null +++ b/xscreensaver/OSX/PrefsReader.m @@ -0,0 +1,283 @@ +/* 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 "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, "imageDirectory"))) + NSLog (@"warning: \"%s\" is in options but not defaults", resource); + } + [optsdict setValue:nsresource forKey:nsresource]; + + opts++; + } + + // make sure there's no resource mentioned in defaults and not options. + NSEnumerator *enumerator = [defsdict keyEnumerator]; + NSString *key; + while ((key = [enumerator nextObject])) { +#if 0 + 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, "background") + )) + NSLog(@"warning: no preference \"%s\" [string]", name); + return NULL; + } +#if 0 + if (! [o isKindOfClass:[NSString class]]) { + NSAssert2(0, @"%s = \"%@\" but should have been an NSString", name, o); + abort(); + } +#else + if (! [o isKindOfClass:[NSString class]]) { + NSLog(@"asked for %s as a string, but it is a %@", name, [o class]); + o = [(NSNumber *) o stringValue]; + } +#endif + + NSString *os = (NSString *) o; + const char *result = [os cStringUsingEncoding:NSUTF8StringEncoding]; + return strdup (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, "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 +{ + return (int) [self getFloatResource:name]; +} + + +- (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 diff --git a/xscreensaver/OSX/README b/xscreensaver/OSX/README new file mode 100644 index 0000000..4a9ea80 --- /dev/null +++ b/xscreensaver/OSX/README @@ -0,0 +1,10 @@ + +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.2 or later is required. +To run them, MacOS 10.4.0 or later is required. diff --git a/xscreensaver/OSX/SaverTester.plist b/xscreensaver/OSX/SaverTester.plist new file mode 100644 index 0000000..e5d26f4 --- /dev/null +++ b/xscreensaver/OSX/SaverTester.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.jwz.xscreensaver.${EXECUTABLE_NAME} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 5.05 + CFBundleSignature + ???? + CFBundleVersion + 5.05 + LSMinimumSystemVersion + 10.4.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/xscreensaver/OSX/XScreenSaver.icns b/xscreensaver/OSX/XScreenSaver.icns new file mode 100644 index 0000000000000000000000000000000000000000..15a25c7f7432b3184ac56e8b996683bf4d036e5b GIT binary patch literal 39163 zcmeI4cUTkI*T<7k#Z}j~0)fnskY1^R*gIfb%i7DTTikBJy1ULK0efBR+E&H2_X;W~ zDhO5-yCRBG4IPq93h%iSP^@cxet*0mYREI^d(S=h+>-gsVa%vs#xoczOk*ba`7#&- z@)__|34i7>$c(7_>U73E;%=Qgw{PFNb@S$p>(_~EP1CMky^@-G`Qn9?vu94m$Hg2v z5PRO~;%Npm<3VJG{rwF1qfU3cckk|9Fe7exAhT=N(rB~GmoBCxok@s49&>Qt?i~jT zb|o-oK44`$ctB(j_ZjKLJ=WbjcgWiww#Jarl`E;2FP%S|cq;x_%%Od|wr)=xev-j_ z_%QMT<01K0V`{R#3o#@zp~KruXh z;^^VMJO16U;rL0(amGBbU}UJDYvKkSce;s-5Wa-Cn0-6{-LPiI*%OjujQN@5L&gI# zgHdfl-ex1OZlIW7y_$OILh_k}qlb2HUB7zesg!u(QO1H7EeMGBGm|T+7tbY~ia)Yv z+s0K(w_OF3SjIxygvfBde?L8)9@!Ud&ZnF?adiK-jjNaZbq{R3V;GC7Y{>idX`4HD zp5;&@$e;_!r;Z)mv2oRsc{ji!&g%$c@ly+w2{>dzwFT)|sC`BTcxKBU;A_CdzdDjQpvaa(HBV}C8}%BAy(@rSnkvvl?rs@mcJW7%sh(yoA4ClBvh zziiIml}|19GnPLlA2Bis7);{XU=p|8Zr!?Z4SAIiyE}UMoMn~IZ1yo$yxOMTt((`c zUA>fY>ge8oSI(P9R60LxXD@BT%zU|_&u-qle)V$7sbl*$ubMxftYNcFoPsuQUb}qm)RA55m(GfMW+~jxSehM`<*@KDHazpGg%=pyyL0P$ zYRajYT^pCrAtwIvi<%(F*&BApr)HvwA>77ToWsh_&WXx)T$i=jb>8F01U>BD4^ve0#Y2$wy^X=%--+VXb=ieqQ-jNK}TN%r8BD0+??J|$_jHzh~j^4R> zU7PzxD)a%3`XwXIb^oeJj?x%;G5OPO&-A1 zxp(*0wM)q-4((XKe16pQsgouU<3F1)Vd9kE|0K1u=0(S;HZwH2D|4I|>ZpCvZ?cfB zYv$eCTaz;Gr{BGO1G*c#XY=YM^JZwsKYu6w_-y)gLam)KYwp5DYmN>5ml2uE$RTqV z=kPb*DlOAU)@EfrhP0S8gSYoVM*6)wH?Ced6L)ZX^y;OH=FOfpYo>Zuo!N8eFI==_ z`SK0P|1xIhEzb3dJ6&FO@%z9V+0gj1QNe>VGr{!k&9uufFZONQv~JaMaw)NlSXyV< zisj2!ty;5YXWk~pg1mXT+~k9Loo?$_WOjDegm@<;~3Xy0A~LD?c!^!=s$o zy9Tp~)xmnECm0@}hFR zPb8EV-}iacVsLv*zK9z7Pfjm_&;lzo!<3~Yg-`?GOE2mw?quve^?2KOH9dJ@`GUpk;(6HWKqKKKnNa^^N^(-- z>60hoj>aB7u>VjrW5yHK<zBL`zft8GN52!bAcD0)Wb>JU_4@9* zc>dhkGpArrk39q?QBUk1h4J_cWepYBUVZ8I)woj1-AiR2{?SvmR9aui3rf0j>Eik1 zq|+zkVvj^Km^z*IiG4D!)rDK?o3m~-xqWeZN!g`6qo>`tIWxET$=-<*;PgPG)kSBH zOBXMkKY#9A3X$CCY*NzMgwDJz*V9rj!?p`YilfmCvOuk4CG&bj>1ICpeCDH)(p`uB z((-ec8g<1n(|;iH7`a3aBb#{a0mJwJ<$ZcOY~y!s59V!wE%qt|1GfDW(F|>YgHD&i z3(ubkzBQbCue|8a!8t?o^N*dX(3PE-+l!=?>`^Yl)Ne*>(Ro%s+_uaZy;|wEOj@p z!{%}!B?;*`Af1AO89LW1Nr^e>OHDdm;huz`+$T>K=nHg(nd>{ol6lNrTL-fkFhQGU zLMLHONgv4DflgwujUcsX2BRpl(C!?sn~o@CU79gfJNQWrWQvBJV+e+{oOF5B*l zyKe^j;_C@Bn(?^U;R-@$S9bOfuSL8@(!HPo5@@x8H5oj*4B1{p6gr-#CB|xbja|p@ zcK2+RRRjuGPfv5@d2!E`5jy+Atd%V}i+BzD!}LW8AOU_L2?$RSvM(qs9ID}YaPh>+ zuFa+xbp-{58Hd!(c$nfwNdfey{Lr^-5a$3fWlLOqN3p%UQ_ok^V@s4 zZ#IHSLDBA|ZaiFmqeMp(*q7Xyqr&F!7+jLqFziug<{;i4ltZ-4?HiEFB_-No#|yk} zg~i1qG(0EQwRc4vUP3vct5Z;x@^>JI%S|a$>+DMRjCNnhYZwlT;9%a~hxFM6wv9Wt zqZ#TF@~MKFR|ivM#C7Ch$z=tk&Z%VEC=ahMcInkRhvIvSTYBPA9t>OyI+3aBdsxNS?jY%$E7HHPJnlt*#_typ>rsY{oN}tOxw|}la%AFMrv7) z2lMt-Cl7QEb*xldf;v_N9aHn1UDsB|Z|Hd8csU6@)0N%+NwoXWy&|>FvGB?IuiHL0 zX$*G8)aD$dmYtOaYOv~6d3O(jUFLX+*S(~ybOgz3hD)!Q;`g?_wx>jg#z?0tPwl5% z7gMg$Ih3TY>=RlXS;>rOM)DeQyXEEP4B;Js&Y~3@W^ej4sWOmqVfQL3ONr}{c=&#y zR_CZc(>{=RSgg_6mtWb~V=~B0Y6cE<%gf6}D)bIS%Ru+a%e7TLA))%srW=cH#C)^p z&Y^Pf2!d3nK!2o*_hOV0q|Uw~_HX}(+Dhg`&|`EX^6CxY9fWSuYdiP_30+P+l_Pkc zaoM{J1-W}ygjqM{ftX7HXcm@l>L9&Y3f>jwZs|3*O3k@jeqP>C-oYGL5FBj%gg%x# zUPj?68$s~u>Wlz;~x+g<KmqqFvi|;SbuZCvAe zu}aUSdr@Hl=slqY?T{cuPVWe6<#goi-G-N;3FuUIeogTD#AL_?M@Wp)dk>-4PN<#I z3+zo*YIVC86%`KS#lVJ+{9}O}M6M#zrl;4#NZ5O}$tebRoXcaT_+Q*vtSgAr*%cR| z^aWjAVP5lyDltZPqR1J<3UoF#W)6r|xkgJlaIvF$Wav~-yt%{Ahwc`kp3a4URSfH9 zC95(YRO%p=Y8ggOG=rr#7&K3vB6$s({bHbJbwP11p_qT6bPgRd`vT((c}bO!WB1~s zqG7yPSYUO~Kj`HEQm|R22aX9Z-@l&bid>IM8aCuSZ zUVU!^|*0oYXx#+ zkxijI_K?qmDh0^KQaez9jgMJWghmcrDUCEbf<0st*gJAPFIDbNge`$lFbk3p4Ha4n z@;49aQ(8mC9;twgJi@nE`%Dz=1p`D-tl+ymsdL->XR#@T zAv+HJB8%;i6etzfRI1MfrOM0|Iyj;rdyUQx{TB&KXXya1D=^C@!_MH?qpY+Pbcj+0 z2(ds2Hkx8eYwH-eq~S5$7QjdI^j&4N7vK(~D%N(UHJlUbN>)WTHf%9Hh%y+g<0LHv zF#>;zNQ$f>RL{%%dt$n#*Gdyof*{f3WJnP=k0htF{_Mfg*i=UFj%z@LC}tp$LJj3? zG^RnA!5b})>E%i^vv>FU*F+Dr8bn}w(cf*$_HtD>W3aW=8gbfEJD3^8NFtJQ1OWpG z%;wp1@J*&dlr1QWA>)jyZ2r7W-mvw|u!iW)fw<`BN9v>atBT5#ah zXNnJTn-B3Thngac&7+vcB@1~> zpIE{$L$9STii8774Izzb7R5X>Y%!0;|NRaLQtArSxr&O&a+`xy4Mv$X6w9z}&@vuN z{?mC9e`5atzt%z7OrG{4c(|9tJ+qZ2@@-g*K0oXWR`KJL91ttSBkE#SPGfh+Ccy`~E>fxxtpz|-v%rtBz$2mt#nK*9z zxQHo}i1BvgCQO;29?Kj*o*YYza~wZ;{Md05BgnChCr+L`VeHt6@Hb-8gt6l%{Px@5 zg&GJEJ%ekrd0Qhe2dg^gl+zP=_)YjD^J|rDz*vRp`;$Q%G3+f4OZjYgbszn?ht7lf{^( z{YP&$Kl-EB9=+K(MH~>Y(0r%0tVO51k`dV^bNe_j1j9 zy-wW8(Ce^;CVh)drh`qV%#r61RI{%;8>rQaa_Svq5*CbCY>&2y(0r8f>-kE8$D1+}I^Oe4)3jbKr%-Yw|K$tl?NoRwh^=i}f=0>W+ zWfN82PW*&Q6l77`TUF>owvk4M?E&S6p$Rr+uR^QUQYb$^zn|H%(Z9lSx5(UYxCLUY z_dR5}-G!>~KY)~5RDg1x47+V?JI|zPN14)!O{Q$I`&mjSzd(%{M(IN~f%3od5<~wT zpo})ILc&_d@8#@Z?(f1vW}{t9o{T2HTK^CvS`0AB(3!mn|M92S`U12y3AxT^;%9^@Cv`^iVefe z`T(-Lf-)Zv-YD0bA9=ZTeG)v+yqt5jlp;*7OWF?%H&Wk?D^gvgDq2r2Db*V{%QAC2 zr&K8yfpQ+dlfh~&{YogEYBcxWL|WSFgQWqU2P_q>JygB2EQehMfwRny1>;3=A#==m zoyC%cMice@g!O^~)5gvz%518%t-G>YhH<-Vpr}KM(O^J6gR)&dzpt6FIvF1xlwP;= z){>SGeTaLrwPs62t1sr}QkKJw+ng&kXNpqVk8M8JY%WjVEPZS$3*315i;Jd9^5N~u zsj{}Nk&g}3_NH!uk4YmP3?!V~M3Sem+V?wR?fnO7anZMMZSt4JQV}rKOjtfU=yk_p zHa`?RT-2=DTGHH@YJbUe*Xy1^{*Nh7`gw$js%SNyr0jPzahK(SCOVU$Fhq82_pjAz zf>4ZDV(m>5Ru@Z!-;!x(!s=-V-nlW%WIZ{^X#EZeTP=Hal;4$f{v*&|55fn;B}iCS zP#)ZHvw6!;CR5)d<_S%>?4}{5rk@%on@swUB_O=1mGPTztAhZ-sowe7!iUzwo<-J? ze_4AH)(T0o$@jR?cOM8xBVi$=APDDMrn+n~FP9n2y^k7Oc7L4O-8I#;`ir|D+!BNx zHnlMB4=9DbfF3u(+E?4vY-+W{de?Q%A?`ocAEfglnqHw4hpeMUSt@*|+`sYAasWS* zo@Th~oo6}awA6gM$=WSgQjR3UY|issV>+%VLBaSr{dxu!-Q4h)dz~^GtF;$gw_4W@ zH=84I&mINV`yDWa#zb|Dw;u1LH}$}zg5FdLS0Ljb*7eg_$_DPU=zqaDgUd?}zi`AT$3ok{6;tI3XlCQT?IMyMitCXmwB!sc&HO8?4cW+7NQ(uX%Jv#L=d z!nlAIaYj;lpduQ}Dg@@+xs@*HM%jyl`vvI%*q#Rf8~%%y&dpm0w3N$smG||hY3Y(- zfdi_D;akwslmK_O+mJ2bEfuoe@$Z8-(2nB*VjqC2#uV2LVYiZ=wI#mGmTuKU=%s5LV)lcYv zZGM@bp2fmv=w>|me7wNTwc~Up@YC)d?4~`3{b=K-cs78J7x?J_Z1cB0i=I4rj-RGr zp@^Se)J{@Qai82bR5WY^&z;S7`}SwW)G3qw*d88i*YKXXL$oB7W+FVK{-gal8Zg9Io7*?cv6sHp~sfcq%vKuJ6bNSW_IsgnNKn^~YX@ zsCCYPQ-y`UfDDN>;YfVEIAU*i54M|N`d1)>V{8xTs^@TjkGU8FE@{{oCN~m#fu({B zu+%{s%!%Wzka0M2Pr{=i+x>g+g(LI=LAHl$yMFGGAfl%KHtYn;1PE2b&}ycNgo=uW zXt1W9QlWsu6GNXMF1>zq$2bBnHkd6D*YIbjjmqA!ho(0BO#RpB3Hqr77xDyF(l#Ps_3NJ-u?>@x~J~g@b=+F zKt?AtR)cTAR|jJIo`KOSzKVlb8Hm;4NVv`9THi}+ni9~x#*mRd!7YEa3GQ|sK#8C5Up_?mp+oX(xC(Y+=4s$1hQc;9l_P+ zzo{~{m8^)y5IIIDaMnTCfojeQLtD*RQD>2VE<7=KG3?tOmA^H%IS3)^++HArgrz(0 zHl*cz_+S^&1p0!90%--kYSIdL8b+er@sdrA!0K~^Qa841uv&iSG7tPC*^OavJqI)w zd!jg^Q2KsF^VSH|;ZOiqgYbh-F=^&gIpD3skemQ_XS;h%>O7@#R1-80KeK5J7YWeh zhMB--_fcK}vKi3?^uhqm4Z;pSjixXT3ndgPs9y zLpq8*Nl0RtWi9=eBelkKri21{9fTcvnY=z#tmmopLKDhsQiq7jVND1&3q%Q+8$sU+ z1@0h7c-XlKgGN?)=o|{S>Ot{k_WD$@KF1qk1)j8?B)E~lanrEtLI|&!$$92Mh*$l=Gzsz4#tNU!n5n%>(>t(~tgIK&7+l1$FkrJouo&V6Hv2iS zSzEW^8rT$s?xaV3NJmAh0iBxB-g!VIm?)qQFg{`dLbS+9r!YXYAovPIYtz$U4ho1F zN43m7O=l|*oyEccJPuc5jW{Ac z%3Sz%7l>7!$T2O*B7GD*cOka&S@236K~s#eKPZ4D^i=wERiWQu z!0aR0Ox2Vca*ji=qs7J5;1}*v(&X6Y8-Y1O;J7nouBe;fk2{)gNSk15(cSC5-B7??!-k_UtPYn}8bxyOAwF=;5S0Z|knB&*;v2giArvF%0k~ zNIs8k)j%K|uU1FTiQ{%XE2t^TGnfjtn9X3<0oT|b&00;DXMz@5U066C1_&3_UPicB zI3QdYi3S|Ei|yQuBA6S|n8cV!(0(WgzC?3D&}ba4 z!Cc%fRaz%ErOzU^5TaaTK<_mk_HL?HZJ(gvH9_9NG%Ix&iXD3y=vIr>0kO)WK4?K9 zJ?v0lNe>qIg{GQ^yO%@=Ot;=7G$Al79H=gcy@Kjit1+7b)wRzHZ{`j%Xi-8^w&hgA zX32#o)s=SoP*=36g#p$4jZla661X;$HG0E5RIK(y_KG5VeyEBa5=Kwgffn26Ln*TtG3#wk7^D)P8I3|GM^Vk=)Pf@VRGw>OhBZsRLd%8gwT#yHBcQ#)s@SJID zHx%G1m_NsVZPo;`)?%N*!~_3DO0gHjk92c%brbkXan3@F@x9HqCI+^dKIl3hQWz7+ zR05G)u2O{d3FCV%!kDd6iE_K4SR9bt8Wb4WAPRh7-iAieB!`3He4#+m>+2AVgJH7O zQR}t;IPc7=s3nvEaoEF{UC0QC?uda8V5wj4nbf*#7ijSo>$%G+AC#ZzFOHOc<_y zjtQgdZA^GefAonW3S)`KoFl+j;KFe6b6gl*jJU9UVM`3-F7k5Vdckxe)o?X(;{`H| zuBORw*6iDnx0ttYtE=JgFzDCwaQF(=y#^c}fggV!4)bKIcFx~Ef17$MbKADLTZt_W z+t&GE$Wk>Xx1ejx@(4?vx^9`*pzbvvEtOVn|GZumF-!xYe#m` zm13&=#7&BNeDcskKrd8sCPii3Aq<~At}t9(6TS18USDzjJ^_HZq6!fA6-={a2DdG{ z9wwB8q*_i-Y}0eE*)X%MWYAG_N`TBSu=SH^3LpQVMLT74O&0^dHzl+c$hxLel)lek zy|G`*4CAiQRX2mAeu2S{t_+YUf6sYFhPKi~fb#SO>0scvy_5%v_`lhdX+peCI-tGcrlr69xMkwz z2P(gZmdBPDuQ8VCvWurBqmi;I7L@-MdQZQk2z%Uxb4~xtZj)&--|Gh|S~?-Gxisgj zbRZ~qksZ|GUx9KpA-?Y?6Mbc}^q1`;EOvKg?V%raV!VE^kfO7R&wO^7iD6xiD7roB zBuWHC{Ij=LtKvk_$OK_8Dp7(w4wc2~IiXb`7Z7oS@4Wakt=0j8(L1kO?kd^=3vC&L z57->;`}m}|{f_tt=HZfv*wg0O7$%)0?Uvt75V8BN$yDA&u8^i?2ew=&8AqLwwEXwz z9b>3WJzqSoN*ck@RRZyGt96@PF7rG6NYx7%aSSGsO8Q&k#JDU-WBHmV^zkcL!1Wij zl!Q`$Nfq*zmrV~^O4_VR?*+e^Ci8tIF-SUi z!F4Je^W>|aVoadKKX?h2sI887{YPgg+Up35OAPG5oysgnC2dj(OT7`I$riJDrf_;E z9PbK`5i~g?9d9Bfy zL#U{SaIgWuE6tz@D;LOfscgnLF>k!7 zCs#{LgV$Y@r-sS*JX7x5UViPIEZm|IbRH<|`bZ_};>(|M0{=CCdc@FIDt60V$VN?VjP{=125a>xHc))@v(PL2{d7|J{Gjn!o&{ zHFZU9`I;5$qIVXiEL*uT`fT35LW?E-k;Rg-a@8rbF^@DiIeEcY8eL&B6|Y&kX5-e} zlS_2e%7Zm|@f^UzHeSpss;smTnx~= zB(651Z|Xux?a3EQYBT<`Ha788&5~LMRMY}3e%F#(;-RLN6xWjC*VN$F*3*8xdRpyc zD5|Y0s2%n8sF<0Zy%JT^Y85}$z`bu@P5Xxedq++jnLYO%`7m8c`?Wv!T`Os6IK0Z= z2cKIL@4ihH?eZKgkl`Se-20u$Fe^6)N@0m?r-2Gzam)8&!_L{+fB47)lpuv% z(MG98c(`kKd>5dHr+D}c#k7q4g-}k*OlwcbeSBqY01*G|hXuR~AYPa^E0=k(6(RTV zm3IO_94PS~Oao#l1AeD+TBSnn6C#NQKh*pJ3EMyF zigP6k#GgwAz6yD;4^J)@`N@6S$$1*_0#O68Hy-dx9*jTZ!Cxxneu0udZ`_>fBUk!} zrqDdtshS59PwQzr;;*QuJ&W6T@L1?{kP`9Wo=WJx^w(GNVETkI5P!a&R)eYm0CLjx zv~>v(@(GlT1|IB7$XP+nzkEFp#(@XJuO+BiPm70E@nAprIMZAu+73L}2VVLI`@WV3 z_s3tZrv;s7^|T!na=D^~G#K$<6}(io61}#b7Q_&oe1Qi8!bSD8k(1R^8&2ufMLcXc zbW;J*4wyD&QV5}N0DV=@shY{!NyNma5flEf;b0u$;HpyCi&f>c)i_v#*TcL6`Ir|{ zk_Y%J37pCGC*>?zVDMtRkqB|%0r<-t*zp|AlYiCX^*Azr925C~xx9IxjKot>#P&AaQR7$vYCQu6Td z5_qAO76}v;4b|XHIZ{6%hwB5227HGoYyxsZVJ}y;=3@)-hF#yld}&sz!5zI+{s52t z33(k*@eAg|%_+GWB;+9h93%m>cOYI(dr>qYjwR}1$79D-k2&4ckK6izV1 z0_3fJ1-G2w4Vhm{cLLE~n^s;f(t3uDqZu#IUgVofDfdx%u72n%Macl)Fo1!+!E!mN zaD+W6&{y#Y)xmXLNT1TL^t^&e_8zD!c}IsJAri1-)mm*H&bE84m~8 zI~?5OD11q|OOQVQC1kXniofeTIL$`;=JD+dcu#BASzfht&u7_@&r)tncn zIqwksWzLI-1LuYC*+KZg#zijo3-Q=`S|mer3nsoo4jHKi2U$L@{{l2dV@qS+A^0l_ zZBs!GVP4!0t*6jkSx}(Qy}?34?gCQuH~dG~gnigkZ4?>@##7`Bg4Iy=Dde@uk@)9a z*t~50Yb~6(fsb-mjZ}~*ytbDs1$`0YMeYGs0mchz)p9i%Z?$3rOaY5DEvaZDU(!-a z!VrL*8a%bVs}wc|#!D+Yh9km@zeIRZ^=-AF9W0-aa`j=_2l@;Qm)QCX@324>EmN?c z^Ka3>O^rK+Bf^WnityU>)VL#B3P1#RzuBkD1P+ia@=ay>@jYXzYI;TDp%f}UDYmwp2l($@Gux=`1a!GLx_?-Z;1l#OuJkko5DNoPvt-u>cB{o)I9w>%)eeLm zrfLXi7waMyQP}*s!F26zcQ{BOn93T4AA5=GrrFfAIFr+qXyWY7^F=d2u84$4!KnI4 zFpj1PP~B>U=c%q^8r}_2-PyP^#`RUoP`haTvLzQBGyPP{bq>{w|(KMvxO7A|fVEjEEp6I>L*Hi05JK0;n8*u|T)( zjUuzc=&DOG;@HbrHhiG4&G zn_hTK86Tz_jfU(B!}X2Z_nt85D{f^xhhn9K<#CI4<+pkW1TE7nXQ#I7eb8)}-%d6( z-h9ztqEv?HNEv(zU$$Sq*mT9`CsSg3Z((Q#MH%`HHy8)CdT89=W!;@1xG*5=+8~*V z%&mc9{emjCiAGs|WSF{W*C* z<3R6rokgoC>VdEL0@a@%OHJ|KcuCNbSopVYny_7e#adv5==Uuk6gx%|rxmTY^bzfN zsQfug#wDh;xt_=yZvI1=c}9Rx>?qY40H*(nL>I2gx)*i!K7CF$(L@~+3D#4;ivFes z%g$aFBCt6?cBEJ|-lj~W*l5wPPRiSs{^FCCsXH=devd5KmN@PYmMGbkB*8bJJX#r# zAg_?Cy($gO4&&a`k3C>AE%(NMrM3vCJPDNLo);n%+e;E#z#jt2)hPC%zf?5PWcf;R z2vBUgxLpyT*f{P_mRaKSX9eFQW#qHVLU;)1m^3Ual%E19b}Y}UbxDeFbfT~~l}vlw zTN0<|^#WZ~FNjd=PdIPB)jEVfX74Rax+D~#*f{Qh9lcdqrv>fzBxIVu5luawWL|`0 z(&^H$Cq2BU97qQg8!DDcujK}`St^)BofWp)9(T{!TQYO0Xi}9lLa}Rkk_4-DmsBG1 zJDVlz4=6T{D-;U{T22VO#Hv}AZ@u_ZRpBzOpLcs{S89bsB5Hlrlo_n-$d5MlQ}mXu zqK=7ik%}<(@e&KA`)i=sbv)%&K(Pr6hKRJ;lJ*G2#&J1#w;Pr?0oG<^I!KFuK4M(V z?<4jTcP%r=%;_!u%XC;Y;AiiNrfJ?P(Q)dy7;CZg7S#vy6wa!NVt>QER|1N?uNadl zZ3D^T634}{F6AKHCJj*Rw}L+b#V+J)LUFW$nwum`rt3 zV*A4Sy{ZO^E$twc^)p%1;a8)H!|qGlmsraT@mxT$Ng$0->}bU)fSL=vw@mfIdYP}E zp>C+gm#)T2zv5|*F>>#;$GdPsn(IIb7mPRcQEJo-F%Kq zXhn-bc_0NS_FMigK(ULwcg2V}y{Z1HhYwZ18uDne$7(JYW~r?Pd`D#O0m{#Ud-=FeJ9ytGMy2$nmJ>R zuDA5!3ck7qiVdD-DGyWKy%plkldZGPkl~`1H_l7Hw~kgExhmoDykv(a@x8g;{g(S| zvK&2ZV%v|33s)5#YoOTUfMQd3ms)SG`RAn7 zYL4F#lVR1ZI%7>+lUK3+@96FOi!Q8Oy>KmplFZZ-K(UFbRM~bw zu_fzQY}mFt|MZFiYF+HBQS8Sm-JKBh^N$QhTW9pEj%$7~S@w=~>ej2@puy*F|J9&D zgZlM8b8&XGw_`GEs=f1TCd*sYp!~v##okpIr_iZ13E$=Vv zMjzq;)1mGc{J{5bztZ%(XX6iWfazGzP15=U+OISY`~U}-EXVq6MW+vGztT9!^Sy@v z+Hc_L6Z%2zSDFWVysraHmQw?cU-*Z$UuhZiz79b9pJRUAKCpdofZfRDeWu|@YX7|* zu(jX)L;GJ@X&L-p5%_5Bzo!Equl@IP;3KwQX&LhFL+~-%e;)@vYWwfuz{hR>-5vPI z?Z2}FAG`f`b>O47|BeoP{Py2%9ry(8zmo%>qWyPq;FGlf4i0>p_UY~Ttq!H1sQtHg z;8V2^4!qTo?31-$X?iOMK3)6pxd27rKR%p(!uBhTUDyrlIK0_`PuYHDvCyr7v;7-C z9DmaGE6;d;@!6Y4;M2Bm?8s?U=ZyjQwCz{^BW(8O0DR*1@2WgMcYf3Dkx$&du{p1a z%bNz^6Sr?|EoxTR;SDq36SrU4LFQi1@qgHU7o|siN7n0a3V!PThx&Lnc+(2-soM|p z{ol49?#KC`_P^WzzWvXg-mnFJ>hbU9hyA~9zx)5V|F!M^Z}YD<{x+qLJf+VfZK`K$K)_3G0HgHe0_s6BtwoaR=(ZyAOF+#KSKT0Yr9{Go%;5s-q3ty zCj}dxe|!D;hwu+U&tviK=ck9;uV|{h&79QHEb)XDy zTJOzefwuZ%PrgLqa;nYY_~5O)pP!j2hfoN y?@@pL-<9g9K)r4q$G4~sXEN>V9UL7Uoj&dtC_21dbvzS&u|ED6t^3j^kN*Qp-KKE> literal 0 HcmV?d00001 diff --git a/xscreensaver/OSX/XScreenSaver.plist b/xscreensaver/OSX/XScreenSaver.plist new file mode 100644 index 0000000..7e319f2 --- /dev/null +++ b/xscreensaver/OSX/XScreenSaver.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.jwz.xscreensaver.${EXECUTABLE_NAME} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + BNDL + CFBundleShortVersionString + 5.05 + CFBundleSignature + ???? + CFBundleVersion + 5.05 + LSMinimumSystemVersion + 10.4.0 + NSMainNibFile + MainMenu + NSPrincipalClass + XScreenSaver${EXECUTABLE_NAME}View + + diff --git a/xscreensaver/OSX/XScreenSaverConfigSheet.h b/xscreensaver/OSX/XScreenSaverConfigSheet.h new file mode 100644 index 0000000..0df9d59 --- /dev/null +++ b/xscreensaver/OSX/XScreenSaverConfigSheet.h @@ -0,0 +1,38 @@ +/* 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 diff --git a/xscreensaver/OSX/XScreenSaverConfigSheet.m b/xscreensaver/OSX/XScreenSaverConfigSheet.m new file mode 100644 index 0000000..9601eac --- /dev/null +++ b/xscreensaver/OSX/XScreenSaverConfigSheet.m @@ -0,0 +1,1778 @@ +/* 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 "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) { + NSAssert2 (0, @"duplicate %@: \"%@\", \"%@\"", 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) +{ + 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:NO]; + [txt setBezeled:NO]; + [txt setDrawsBackground:NO]; + [[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 be the same height as the label. + if (lab) { + rect = [txt frame]; + rect.size.height = [lab 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]; + NSString *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 ([type isEqualToString:@"slider"]) { + + NSRect rect; + rect.origin.x = rect.origin.y = 0; + rect.size.width = 150; + rect.size.height = 20; + NSSlider *slider; + if (cvt) + slider = [[InvertedSlider alloc] initWithFrame:rect]; + else + slider = [[NSSlider alloc] initWithFrame:rect]; + + [slider setMaxValue:[high doubleValue]]; + [slider setMinValue:[low doubleValue]]; + + 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.size.height = [txt frame].size.height; + rect.origin.x -= COLUMN_SPACING; // this one goes close + [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]; + + 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, int *row) +{ + 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); + + if (row) + gtk_table_attach (GTK_TABLE (parent), box, 0, 3, *row, *row + 1, + 0, 0, 0, 0); + else + gtk_box_pack_start (GTK_BOX (parent), box, FALSE, FALSE, 0); + + if (row) + (*row)++; + + list2 = make_parameters_1 (filename, node->xmlChildrenNode, box, 0); + 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, row); + } + } + } + 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) +{ + int row = 0; + for (; node; node = node->next) + { + if (node->type == XML_ELEMENT_NODE && + !strcmp ((char *) node->name, "screensaver")) + return make_parameters_1 (filename, node->xmlChildrenNode, + parent, &row); + } + 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)); +} + + + +/* Given a `parameter' struct, allocates an appropriate GtkWidget for it, + and stores it in `p->widget'. + `row' is used for keeping track of our position during table layout. + `parent' must be a GtkTable or a GtkBox. + */ +static void +make_parameter_widget (const char *filename, + parameter *p, GtkWidget *parent, int *row) +{ + const char *label = (char *) p->label; + if (p->widget) return; + + switch (p->type) + { + case STRING: + { + if (label) + { + GtkWidget *w = gtk_label_new (_(label)); + gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_RIGHT); + gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); + gtk_widget_show (w); + if (row) + gtk_table_attach (GTK_TABLE (parent), w, 0, 1, *row, *row + 1, + GTK_FILL, 0, 0, 0); + else + gtk_box_pack_start (GTK_BOX (parent), w, FALSE, FALSE, 4); + } + + p->widget = gtk_entry_new (); + if (p->string) + gtk_entry_set_text (GTK_ENTRY (p->widget), (char *) p->string); + if (row) + gtk_table_attach (GTK_TABLE (parent), p->widget, 1, 3, + *row, *row + 1, + GTK_FILL, 0, 0, 0); + else + 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...")); + 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); + gtk_widget_show (L); + + if (p->string) + gtk_entry_set_text (GTK_ENTRY (entry), (char *) p->string); + + if (row) + { + gtk_table_attach (GTK_TABLE (parent), L, 0, 1, + *row, *row + 1, + GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (parent), entry, 1, 2, + *row, *row + 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (parent), button, 2, 3, + *row, *row + 1, + 0, 0, 0, 0); + } + else + { + 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)); + gtk_label_set_justify (GTK_LABEL (labelw), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (labelw), 0.0, 0.5); + gtk_widget_show (labelw); + } + + if (GTK_IS_VBOX (parent)) + { + /* If we're inside a vbox, we need to put an hbox in it, to get + the low/high labels to be to the left/right of the slider. + */ + GtkWidget *hbox = gtk_hbox_new (FALSE, 0); + + /* But if we have a label, put that above the slider's hbox. */ + if (labelw) + { + gtk_box_pack_start (GTK_BOX (parent), labelw, FALSE, TRUE, 2); + labelw = 0; + } + + gtk_box_pack_start (GTK_BOX (parent), hbox, TRUE, TRUE, 6); + gtk_widget_show (hbox); + parent = hbox; + } + + if (labelw) + { + if (row) + { + gtk_table_attach (GTK_TABLE (parent), labelw, + 0, 3, *row, *row + 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + (*row)++; + } + else + { + if (GTK_IS_HBOX (parent)) + { + GtkWidget *box = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (parent), box, FALSE, TRUE, 0); + gtk_widget_show (box); + gtk_box_pack_start (GTK_BOX (box), labelw, FALSE, TRUE, 4); + parent = box; + box = gtk_hbox_new (FALSE, 0); + gtk_widget_show (box); + gtk_box_pack_start (GTK_BOX (parent), box, TRUE, TRUE, 0); + parent = box; + } + else + gtk_box_pack_start (GTK_BOX (parent), labelw, + FALSE, TRUE, 0); + } + } + + if (p->low_label) + { + GtkWidget *w = gtk_label_new (_((char *) p->low_label)); + gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_RIGHT); + gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); + gtk_widget_show (w); + if (row) + gtk_table_attach (GTK_TABLE (parent), w, 0, 1, *row, *row + 1, + GTK_FILL, 0, 0, 0); + else + 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)); + if (row) + gtk_table_attach (GTK_TABLE (parent), scale, 1, 2, + *row, *row + 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + else + gtk_box_pack_start (GTK_BOX (parent), scale, TRUE, TRUE, 4); + + gtk_widget_show (scale); + + if (p->high_label) + { + GtkWidget *w = gtk_label_new (_((char *) p->high_label)); + gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5); + gtk_widget_show (w); + if (row) + gtk_table_attach (GTK_TABLE (parent), w, 2, 3, *row, *row + 1, + GTK_FILL, 0, 0, 0); + else + 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), adj->value); + + if (label) + { + GtkWidget *w = gtk_label_new (_(label)); + gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_RIGHT); + gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); + gtk_widget_show (w); + if (row) + gtk_table_attach (GTK_TABLE (parent), w, 0, 1, *row, *row + 1, + GTK_FILL, 0, 0, 0); + else + gtk_box_pack_start (GTK_BOX (parent), w, TRUE, TRUE, 4); + } + + gtk_widget_show (spin); + if (row) + { + GtkWidget *hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox); + gtk_table_attach (GTK_TABLE (parent), hbox, 1, 3, + *row, *row + 1, + GTK_EXPAND | GTK_FILL, 0, 8, 0); + gtk_box_pack_start (GTK_BOX (hbox), spin, FALSE, FALSE, 0); + } + else + 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)); + if (row) + gtk_table_attach (GTK_TABLE (parent), p->widget, 0, 3, + *row, *row + 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + else + 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; + + if (label && row) + { + GtkWidget *w = gtk_label_new (_(label)); + gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5); + gtk_widget_show (w); + gtk_table_attach (GTK_TABLE (parent), w, 0, 3, *row, *row + 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + (*row)++; + } + + 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; + if (row) + gtk_table_attach (GTK_TABLE (parent), p->widget, 0, 3, + *row, *row + 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + else + gtk_box_pack_start (GTK_BOX (parent), p->widget, TRUE, TRUE, 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); + if (row) + (*row)++; + } +} + + +/* 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 (dialog->parent) + dialog = dialog->parent; + 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 (dialog->parent) + dialog = dialog->parent; + 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 (adj->lower, adj->upper, adj->value) - 1 + : adj->value); + + 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) +{ + int L = g_list_length (parms); + int LL = 0; + char **strs = (char **) calloc (sizeof (*parms), L); + char *result; + char *out; + int i; + + for (i = 0; parms; parms = parms->next, i++) + { + char *s = parameter_to_switch ((parameter *) parms->data); + strs[i] = s; + LL += (s ? strlen(s) : 0) + 1; + } + + result = (char *) malloc (LL + 10); + out = result; + for (i = 0; i < L; 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 (adj->lower, adj->upper, 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 + 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 *table; + 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. */ + + table = gtk_table_new (1, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + gtk_container_set_border_width (GTK_CONTAINER (table), 8); + gtk_widget_show (table); + + parms = make_parameters (file, doc->xmlRootNode, table); + sanity_check_parameters (file, parms); + + xmlFreeDoc (doc); + + restore_defaults (program, parms); + if (arguments && *arguments) + parse_command_line_into_parameters (program, arguments, parms); + + data->widget = table; + 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) +{ + char *args = parameters_to_cmd_line (data->parameters); + 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 */ diff --git a/xscreensaver/driver/demo-Gtk-conf.h b/xscreensaver/driver/demo-Gtk-conf.h new file mode 100644 index 0000000..9292d81 --- /dev/null +++ b/xscreensaver/driver/demo-Gtk-conf.h @@ -0,0 +1,31 @@ +/* demo-Gtk-conf.c --- implements the dynamic configuration dialogs. + * xscreensaver, Copyright (c) 2001, 2002 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 *); +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_ */ diff --git a/xscreensaver/driver/demo-Gtk-stubs.h b/xscreensaver/driver/demo-Gtk-stubs.h new file mode 100644 index 0000000..65d6548 --- /dev/null +++ b/xscreensaver/driver/demo-Gtk-stubs.h @@ -0,0 +1,86 @@ +#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_ok_cb (GtkButton *button, + gpointer user_data); + +void +settings_cancel_cb (GtkButton *button, + gpointer user_data); diff --git a/xscreensaver/driver/demo-Gtk-support.c b/xscreensaver/driver/demo-Gtk-support.c new file mode 100644 index 0000000..881129c --- /dev/null +++ b/xscreensaver/driver/demo-Gtk-support.c @@ -0,0 +1,219 @@ +/* + * 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; +} + diff --git a/xscreensaver/driver/demo-Gtk-support.h b/xscreensaver/driver/demo-Gtk-support.h new file mode 100644 index 0000000..931bc5a --- /dev/null +++ b/xscreensaver/driver/demo-Gtk-support.h @@ -0,0 +1,61 @@ +/* + * 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); + diff --git a/xscreensaver/driver/demo-Gtk-widgets.c b/xscreensaver/driver/demo-Gtk-widgets.c new file mode 100644 index 0000000..3ac260a --- /dev/null +++ b/xscreensaver/driver/demo-Gtk-widgets.c @@ -0,0 +1,1750 @@ +/* + * 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 operate on random images loaded from disk."), 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 directory 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 *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); + + 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 (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; +} + diff --git a/xscreensaver/driver/demo-Gtk-widgets.h b/xscreensaver/driver/demo-Gtk-widgets.h new file mode 100644 index 0000000..298c517 --- /dev/null +++ b/xscreensaver/driver/demo-Gtk-widgets.h @@ -0,0 +1,6 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +GtkWidget* create_xscreensaver_demo (void); +GtkWidget* create_xscreensaver_settings_dialog (void); diff --git a/xscreensaver/driver/demo-Gtk.c b/xscreensaver/driver/demo-Gtk.c new file mode 100644 index 0000000..d1eb3c8 --- /dev/null +++ b/xscreensaver/driver/demo-Gtk.c @@ -0,0 +1,5111 @@ +/* demo-Gtk.c --- implements the interactive demo-mode and options dialogs. + * 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. + */ + +#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 */ + +/* 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_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); + + +/* 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 (GTK_WIDGET(vp)->window, + &ignore, &ignore, &ignore, &parent_h, &ignore); + gdk_window_get_geometry (GTK_WIDGET(selected)->window, + &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 (shell->parent) + shell = shell->parent; + 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 (GtkWidget *parent, const char *message, + Boolean restart_button_p, 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 && !parent->window) + parent = parent->parent; + + if (!parent || + !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */ + { + fprintf (stderr, "%s: too early for dialog?\n", progname); + return; + } + + 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 (GTK_DIALOG (dialog)->vbox), + 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 (GTK_DIALOG (dialog)->vbox), + label, TRUE, TRUE, 0); + gtk_widget_show (label); + + label = gtk_hbutton_box_new (); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), + label, TRUE, TRUE, 0); + +#ifdef HAVE_GTK2 + if (restart_button_p) + { + 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 (restart_button_p) + { + 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); + STFU GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT); + gtk_widget_show (ok); + gtk_widget_grab_focus (ok); + + if (cancel) + { + STFU GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT); + gtk_widget_show (cancel); + } + gtk_widget_show (label); + gtk_widget_show (dialog); + + if (restart_button_p) + { + gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", + GTK_SIGNAL_FUNC (warning_dialog_restart_cb), + (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 (GTK_WIDGET (dialog)->window, + GTK_WIDGET (parent)->window); + +#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); +} + + +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, False, 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, False, 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, True, 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 (parent->parent) + parent = parent->parent; + + hbox = gtk_hbox_new (FALSE, 20); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), + 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 (GTK_DIALOG (dialog)->action_area), + 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 (GTK_WIDGET (dialog)->window, + GTK_WIDGET (parent)->window); + gdk_window_show (GTK_WIDGET (dialog)->window); + gdk_window_raise (GTK_WIDGET (dialog)->window); + } +} + + +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"), False, 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, False, 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"), + False, 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, False, 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."), + False, 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"); + Bool was = GTK_WIDGET_IS_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, False, 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 */ + + 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"); + 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. + */ + if (p2->image_directory && + *p2->image_directory && + !directory_p (p2->image_directory)) + { + char b[255]; + sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n", + p2->image_directory); + warning_dialog (s->toplevel_widget, b, False, 100); + } + + + /* 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_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 (widget->parent)); + 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; + + STFU 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 = adj->value; + + 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, False, 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, False, 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, False, 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); + 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_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 (list)->style; + 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 ("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 + */ + SENSITIZE ("dpms_frame", dpms_supported); + SENSITIZE ("dpms_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 = s->toplevel_widget->window; + 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), + w->style->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, "doc_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, False, 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; + + if (!s->toplevel_widget) return; /* very early */ + p = name_to_widget (s, "preview"); + window = p->window; + + if (!window) return; + + /* Flush the widget background down into the window, in case a subproc + has changed it. */ + gdk_window_set_background (window, &p->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 (GTK_WIDGET_REALIZED (pr)) + { + Window oid = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0); + Window id; + gtk_widget_hide (pr); + gtk_widget_unrealize (pr); + gtk_widget_realize (pr); + gtk_widget_show (pr); + id = (pr->window ? GDK_WINDOW_XWINDOW (pr->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 (!GTK_WIDGET_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 = widget->window; + GtkStyle *style = gtk_style_copy (widget->style); + 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 = pr->window; + + 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 */ +} + + +/* 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 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, True, 1); + + if (rversion) free (rversion); + if (ruser) free (ruser); + if (rhost) free (rhost); + 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) +{ + 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]" +# 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 = 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 = 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); + 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) + { + 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 = window->parent; + 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 (GTK_WIDGET (s->toplevel_widget)->window); /* 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); + + + /* 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 */ diff --git a/xscreensaver/driver/demo-Xm-widgets.c b/xscreensaver/driver/demo-Xm-widgets.c new file mode 100644 index 0000000..cbe3393 --- /dev/null +++ b/xscreensaver/driver/demo-Xm-widgets.c @@ -0,0 +1,907 @@ +/* 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; +} diff --git a/xscreensaver/driver/demo-Xm.c b/xscreensaver/driver/demo-Xm.c new file mode 100644 index 0000000..56d1aac --- /dev/null +++ b/xscreensaver/driver/demo-Xm.c @@ -0,0 +1,1875 @@ +/* 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 */ diff --git a/xscreensaver/driver/dpms.c b/xscreensaver/driver/dpms.c new file mode 100644 index 0000000..b45f4d2 --- /dev/null +++ b/xscreensaver/driver/dpms.c @@ -0,0 +1,256 @@ +/* dpms.c --- syncing the X Display Power Management values + * xscreensaver, Copyright (c) 2001, 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. + */ + +/* 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 + +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) +{ + if (!monitor_powered_on_p (si)) + { + DPMSForceLevel(si->dpy, DPMSModeOn); + XSync(si->dpy, False); + if (!monitor_powered_on_p (si)) + fprintf (stderr, + "%s: DPMSForceLevel(dpy, DPMSModeOn) did not power the monitor on?\n", + blurb()); + } +} + +#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) +{ + return; +} + +#endif /* !HAVE_DPMS_EXTENSION */ diff --git a/xscreensaver/driver/exec.c b/xscreensaver/driver/exec.c new file mode 100644 index 0000000..0f2aaa2 --- /dev/null +++ b/xscreensaver/driver/exec.c @@ -0,0 +1,300 @@ +/* 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. + */ + + +/* 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 + if (nice != 0) + 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; +} + diff --git a/xscreensaver/driver/exec.h b/xscreensaver/driver/exec.h new file mode 100644 index 0000000..318410b --- /dev/null +++ b/xscreensaver/driver/exec.h @@ -0,0 +1,21 @@ +/* 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__ */ diff --git a/xscreensaver/driver/link_axp.com b/xscreensaver/driver/link_axp.com new file mode 100644 index 0000000..a141892 --- /dev/null +++ b/xscreensaver/driver/link_axp.com @@ -0,0 +1,15 @@ +$! 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 diff --git a/xscreensaver/driver/link_decc.com b/xscreensaver/driver/link_decc.com new file mode 100644 index 0000000..d1de0d0 --- /dev/null +++ b/xscreensaver/driver/link_decc.com @@ -0,0 +1,15 @@ +$! 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 diff --git a/xscreensaver/driver/lock.c b/xscreensaver/driver/lock.c new file mode 100644 index 0000000..e1d29dc --- /dev/null +++ b/xscreensaver/driver/lock.c @@ -0,0 +1,2096 @@ +/* lock.c --- handling the password dialog for locking-mode. + * xscreensaver, Copyright (c) 1993-2007 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 _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; + +struct passwd_dialog_data { + + saver_screen_info *prompt_screen; + int previous_mouse_x, previous_mouse_y; + + char typed_passwd [80]; + 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 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 void +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; + + /* 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" ); + + 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; +} + + +/** + * info_msg and prompt may be NULL. + */ +static void +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 (!si->pw_data) + new_passwd_window (si); + + if (!(pw = si->pw_data)) + return; + + 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; + + 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. */ + { + int x, y, w, h; + get_screen_viewport (pw->prompt_screen, &x, &y, &w, &h, + pw->previous_mouse_x, pw->previous_mouse_y, + si->prefs.verbose_p); + 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); + + /* 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); +} + + +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); /* for uname */ + + 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->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); + + while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event)) + if (p->verbose_p) + fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb()); + + 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; + + XErrorHandler old_handler; + 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 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 < (si->xinerama_p ? 1 : si->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 event, error; + + if (!XF86VidModeQueryExtension (si->dpy, &event, &error)) + return; + + for (screen = 0; screen < si->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; + int pw_size = sizeof (pw->typed_passwd) - 1; + char *typed_passwd = pw->typed_passwd; + char s[2]; + char *stars = 0; + int i; + int size = XLookupString (event, s, 1, 0, compose_status); + + if (size != 1) return; + + s[1] = 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; + + switch (*s) + { + case '\010': case '\177': /* Backspace */ + if (!*typed_passwd) + XBell (si->dpy, 0); + else + typed_passwd [strlen(typed_passwd)-1] = 0; + break; + + case '\025': case '\030': /* Erase line */ + memset (typed_passwd, 0, pw_size); + break; + + case '\012': case '\015': /* Enter */ + finished_typing_passwd(si, pw); + break; + + case '\033': /* Escape */ + si->unlock_state = ul_cancel; + break; + + default: + /* Though technically the only illegal characters in Unix passwords + are LF and NUL, most GUI programs (e.g., GDM) use regular text-entry + fields that only let you type printable characters. So, people + who use funky characters in their passwords are already broken. + We follow that precedent. + */ + if (isprint ((unsigned char) *s)) + { + i = strlen (typed_passwd); + if (i >= pw_size-1) + XBell (si->dpy, 0); + else + { + typed_passwd [i] = *s; + typed_passwd [i+1] = 0; + } + } + else + XBell (si->dpy, 0); + break; + } + + if (pw->echo_input) + { + /* If the input is wider than the text box, only show the last portion. + * This simulates a horizontally scrolling text field. */ + int chars_in_pwfield = (pw->passwd_field_width / + pw->passwd_font->max_bounds.width); + + if (strlen(typed_passwd) > chars_in_pwfield) + typed_passwd += (strlen(typed_passwd) - chars_in_pwfield); + + update_passwd_window(si, typed_passwd, pw->ratio); + } + else if (pw->show_stars_p) + { + i = strlen(typed_passwd); + stars = (char *) malloc(i+1); + memset (stars, '*', i); + stars[i] = 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; + XEvent event; + + passwd_animate_timer ((XtPointer) si, 0); + + while (si->unlock_state == ul_read) + { + XtAppNextEvent (si->app, &event); + if (event.xany.window == si->passwd_dialog && event.xany.type == Expose) + draw_passwd_window (si); + else if (event.xany.type == KeyPress) + { + handle_passwd_key (si, &event.xkey); + si->pw_data->caps_p = (event.xkey.state & LockMask); + } + else if (event.xany.type == ButtonPress || + event.xany.type == ButtonRelease) + { + si->pw_data->button_state_changed_p = True; + handle_unlock_button (si, &event); + if (si->pw_data->login_button_p) + handle_login_button (si, &event); + } + else + XtDispatchEvent (&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; + + 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); + (void) strcpy(newstr, str); + + if (!newstr) + return NULL; + + 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 auth, OOM, ...) + */ +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 (!(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); + + make_passwd_window(si, info_msg_trimmed, prompt_trimmed, + auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO + ? True : False); + + 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); + + 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 == ButtonRelease) + 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 */ diff --git a/xscreensaver/driver/mlstring.c b/xscreensaver/driver/mlstring.c new file mode 100644 index 0000000..d6df844 --- /dev/null +++ b/xscreensaver/driver/mlstring.c @@ -0,0 +1,227 @@ +/* + * (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.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; + + 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 + */ diff --git a/xscreensaver/driver/mlstring.h b/xscreensaver/driver/mlstring.h new file mode 100644 index 0000000..ce36205 --- /dev/null +++ b/xscreensaver/driver/mlstring.h @@ -0,0 +1,57 @@ +/* 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 + */ diff --git a/xscreensaver/driver/passwd-helper.c b/xscreensaver/driver/passwd-helper.c new file mode 100644 index 0000000..a3a6b92 --- /dev/null +++ b/xscreensaver/driver/passwd-helper.c @@ -0,0 +1,162 @@ +/* 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 */ diff --git a/xscreensaver/driver/passwd-kerberos.c b/xscreensaver/driver/passwd-kerberos.c new file mode 100644 index 0000000..bb8d992 --- /dev/null +++ b/xscreensaver/driver/passwd-kerberos.c @@ -0,0 +1,247 @@ +/* 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 */ + + +/* 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 */ diff --git a/xscreensaver/driver/passwd-pam.c b/xscreensaver/driver/passwd-pam.c new file mode 100644 index 0000000..334d12b --- /dev/null +++ b/xscreensaver/driver/passwd-pam.c @@ -0,0 +1,485 @@ +/* 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 (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 */ diff --git a/xscreensaver/driver/passwd-pwent.c b/xscreensaver/driver/passwd-pwent.c new file mode 100644 index 0000000..bb0edfc --- /dev/null +++ b/xscreensaver/driver/passwd-pwent.c @@ -0,0 +1,312 @@ +/* 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 */ diff --git a/xscreensaver/driver/passwd.c b/xscreensaver/driver/passwd.c new file mode 100644 index 0000000..066bc81 --- /dev/null +++ b/xscreensaver/driver/passwd.c @@ -0,0 +1,306 @@ +/* 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; + + 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 (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! */ + } + } + + 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 */ diff --git a/xscreensaver/driver/pdf2jpeg.m b/xscreensaver/driver/pdf2jpeg.m new file mode 100644 index 0000000..60778fe --- /dev/null +++ b/xscreensaver/driver/pdf2jpeg.m @@ -0,0 +1,132 @@ +/* pdf2jpeg -- converts a PDF file to a JPEG file, using Cocoa + * + * Copyright (c) 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. + * + * 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; + 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], "-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] [-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]]; + + // Create an NSPDFImageRep from the data: + NSPDFImageRep *pdf_rep = [NSPDFImageRep imageRepWithData:pdf_data]; + + // Create an NSImage instance + NSImage *image = [[NSImage alloc] initWithSize:[pdf_rep size]]; + + // Draw the PDFImageRep in the NSImage + [image lockFocus]; + [pdf_rep drawAtPoint:NSMakePoint(0.0,0.0)]; + [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] + atomically:YES]; + [image release]; + + [pool release]; + exit (0); +} diff --git a/xscreensaver/driver/pdf2jpeg.man b/xscreensaver/driver/pdf2jpeg.man new file mode 100644 index 0000000..9d80dd7 --- /dev/null +++ b/xscreensaver/driver/pdf2jpeg.man @@ -0,0 +1,43 @@ +.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. diff --git a/xscreensaver/driver/prefs.c b/xscreensaver/driver/prefs.c new file mode 100644 index 0000000..c8e4ae1 --- /dev/null +++ b/xscreensaver/driver/prefs.c @@ -0,0 +1,1636 @@ +/* dotfile.c --- management of the ~/.xscreensaver file. + * xscreensaver, Copyright (c) 1998-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. + */ + +#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 */ + "ignoreUninstalledPrograms", + "font", + "dpmsEnabled", + "dpmsStandby", + "dpmsSuspend", + "dpmsOff", + "grabDesktopImages", + "grabVideoFrames", + "chooseRandomImages", + "imageDirectory", + "mode", + "selected", + "textMode", + "textLiteral", + "textFile", + "textProgram", + "textURL", + "", + "programs", + "", + "pointerPollTime", + "pointerHysteresis", + "windowCreationTimeout", + "initialDelay", + "sgiSaverExtension", + "mitSaverExtension", /* not saved -- obsolete */ + "xidleExtension", + "GetViewPortIsFullOfLies", + "procInterrupts", + "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("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("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")type = pref_bool, b=p->use_sgi_saver_extension; + CHECK("mitSaverExtension") continue; /* don't save */ + CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension; + CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts; + 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; + 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_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 /* ignore this, it is evil. */ + p->use_mit_saver_extension = get_boolean_resource (dpy, + "mitSaverExtension", + "Boolean"); +#endif + p->use_sgi_saver_extension = get_boolean_resource (dpy, + "sgiSaverExtension", + "Boolean"); + 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; +} diff --git a/xscreensaver/driver/prefs.h b/xscreensaver/driver/prefs.h new file mode 100644 index 0000000..eeb272e --- /dev/null +++ b/xscreensaver/driver/prefs.h @@ -0,0 +1,35 @@ +/* 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__ */ diff --git a/xscreensaver/driver/remote.c b/xscreensaver/driver/remote.c new file mode 100644 index 0000000..507da6b --- /dev/null +++ b/xscreensaver/driver/remote.c @@ -0,0 +1,594 @@ +/* xscreensaver-command, Copyright (c) 1991-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 +#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 int +xscreensaver_command_response (Display *dpy, Window window, + Bool verbose_p, Bool exiting_p, + char **error_ret) +{ + int fd = ConnectionNumber (dpy); + int timeout = 10; + int status; + fd_set fds; + struct timeval tv; + char err[2048]; + + while (1) + { + FD_ZERO(&fds); + FD_SET(fd, &fds); + memset(&tv, 0, sizeof(tv)); + tv.tv_sec = timeout; + status = select (fd+1, &fds, 0, &fds, &tv); + + if (status < 0) + { + char buf[1024]; + if (error_ret) + { + sprintf (buf, "error waiting for reply"); + *error_ret = strdup (buf); + } + else + { + sprintf (buf, "%s: error waiting for reply", progname); + perror (buf); + } + return status; + } + else if (status == 0) + { + sprintf (err, "no response to command."); + if (error_ret) + *error_ret = strdup (err); + else + fprintf (stderr, "%s: %s\n", progname, err); + return -1; + } + else + { + XEvent event; + XNextEvent (dpy, &event); + if (event.xany.type == PropertyNotify && + event.xproperty.state == PropertyNewValue && + event.xproperty.atom == XA_SCREENSAVER_RESPONSE) + { + 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; + } + } + } + } + } +} + + +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); + } +} diff --git a/xscreensaver/driver/remote.h b/xscreensaver/driver/remote.h new file mode 100644 index 0000000..e1db351 --- /dev/null +++ b/xscreensaver/driver/remote.h @@ -0,0 +1,24 @@ +/* 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_ */ diff --git a/xscreensaver/driver/screensaver-properties.desktop.in b/xscreensaver/driver/screensaver-properties.desktop.in new file mode 100644 index 0000000..ff745eb --- /dev/null +++ b/xscreensaver/driver/screensaver-properties.desktop.in @@ -0,0 +1,8 @@ +[Desktop Entry] +Exec=xscreensaver-demo +Icon=xscreensaver +Terminal=false +_Name=Screensaver +_Comment=Change screensaver properties +Type=Application +Categories=Settings;DesktopSettings;Security; diff --git a/xscreensaver/driver/setuid.c b/xscreensaver/driver/setuid.c new file mode 100644 index 0000000..3ac78e4 --- /dev/null +++ b/xscreensaver/driver/setuid.c @@ -0,0 +1,361 @@ +/* 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; + } + } +} diff --git a/xscreensaver/driver/splash.c b/xscreensaver/driver/splash.c new file mode 100644 index 0000000..56f66da --- /dev/null +++ b/xscreensaver/driver/splash.c @@ -0,0 +1,853 @@ +/* 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. + */ + +#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 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)]; + 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"); + + 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, sy, 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; + } + } + + get_screen_viewport (ssi, &sx, &sy, &w, &h, mouse_x, mouse_y, False); + 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); + + 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); +} diff --git a/xscreensaver/driver/stderr.c b/xscreensaver/driver/stderr.c new file mode 100644 index 0000000..6246dd3 --- /dev/null +++ b/xscreensaver/driver/stderr.c @@ -0,0 +1,508 @@ +/* stderr.c --- capturing stdout/stderr output onto the screensaver window. + * 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. + */ + +/* 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 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_buffer && + 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; +} diff --git a/xscreensaver/driver/subprocs.c b/xscreensaver/driver/subprocs.c new file mode 100644 index 0000000..88270d6 --- /dev/null +++ b/xscreensaver/driver/subprocs.c @@ -0,0 +1,1357 @@ +/* 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); /* set $DISPLAY */ + + 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; +} + + +static void +spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p) +{ + saver_info *si = ssi->global; + saver_preferences *p = &si->prefs; + raise_window (si, first_time_p, True, False); + XFlush (si->dpy); + + 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; + } + } +} + + +void +spawn_screenhack (saver_info *si, Bool first_time_p) +{ + if (monitor_powered_on_p (si)) + { + int i; + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + spawn_screenhack_1 (ssi, first_time_p); + } + } + else if (si->prefs.verbose_p) + fprintf (stderr, + "%s: X says monitor has powered down; " + "not launching a hack.\n", blurb()); + + store_saver_status (si); /* store current hack numbers */ +} + + +void +kill_screenhack (saver_info *si) +{ + int i; + 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; + } +} + + +void +suspend_screenhack (saver_info *si, Bool suspend_p) +{ +#ifdef SIGSTOP /* older VMS doesn't have it... */ + int i; + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + 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 (saver_screen_info *ssi) +{ + /* 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. + */ + saver_info *si = ssi->global; + const char *odpy = DisplayString (si->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", ssi->real_screen_number); /* put on screen number */ + + sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX", + (unsigned long) ssi->screensaver_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_screen_info *ssi) +{ + saver_info *si = ssi->global; + 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); + saver_exit (si, 1, 0); + } + case 0: + { + int stdout_fd = 1; + + close (in); /* don't need this one */ + close (ConnectionNumber (si->dpy)); /* close display fd */ + + if (dup2 (out, stdout_fd) < 0) /* pipe stdout */ + { + perror ("could not dup() a new stdout:"); + return 0; + } + hack_subproc_environment (ssi); /* 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); + + /* Wait for the child to die. */ + waitpid (-1, &wait_status, 0); + + 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 (ssi->screen, result); + if (si->prefs.verbose_p) + fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s.\n", + blurb(), ssi->number, + av[0], result, + (v == ssi->default_visual ? " (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(); + } +} diff --git a/xscreensaver/driver/test-apm.c b/xscreensaver/driver/test-apm.c new file mode 100644 index 0000000..6b87c7e --- /dev/null +++ b/xscreensaver/driver/test-apm.c @@ -0,0 +1,101 @@ +/* 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); +} diff --git a/xscreensaver/driver/test-fade.c b/xscreensaver/driver/test-fade.c new file mode 100644 index 0000000..9db773d --- /dev/null +++ b/xscreensaver/driver/test-fade.c @@ -0,0 +1,123 @@ +/* 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); + } +} diff --git a/xscreensaver/driver/test-grab.c b/xscreensaver/driver/test-grab.c new file mode 100644 index 0000000..03018eb --- /dev/null +++ b/xscreensaver/driver/test-grab.c @@ -0,0 +1,89 @@ +/* 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); +} diff --git a/xscreensaver/driver/test-mlstring.c b/xscreensaver/driver/test-mlstring.c new file mode 100644 index 0000000..e269a00 --- /dev/null +++ b/xscreensaver/driver/test-mlstring.c @@ -0,0 +1,312 @@ +/* + * (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 + */ diff --git a/xscreensaver/driver/test-passwd.c b/xscreensaver/driver/test-passwd.c new file mode 100644 index 0000000..ac5bf99 --- /dev/null +++ b/xscreensaver/driver/test-passwd.c @@ -0,0 +1,305 @@ +/* xscreensaver, Copyright (c) 1998-2007 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 "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 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) { } + +const char *blurb(void) { return progname; } +Atom XA_SCREENSAVER, XA_DEMO, XA_PREFS; + +void +get_screen_viewport (saver_screen_info *ssi, + int *x_ret, int *y_ret, + int *w_ret, int *h_ret, + int tx, int ty, + Bool verbose_p) +{ + *x_ret = 0; + *y_ret = 0; + *w_ret = WidthOfScreen (ssi->screen); + *h_ret = HeightOfScreen (ssi->screen); + + if (*w_ret > *h_ret * 2) *w_ret /= 2; /* xinerama kludge */ +} + +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 +}; + +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 (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); + + /* I could call get_screen_viewport(), but it is not worthwhile. + * These are used by the save_under pixmap. */ + 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); + + while (1) + { +#ifndef NO_LOCKING + if (which == PASS) + { + si->unlock_cb = gui_auth_conv; + si->auth_finished_cb = auth_finished_cb; + + 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); +} diff --git a/xscreensaver/driver/test-randr.c b/xscreensaver/driver/test-randr.c new file mode 100644 index 0000000..da86e04 --- /dev/null +++ b/xscreensaver/driver/test-randr.c @@ -0,0 +1,298 @@ +/* test-randr.c --- playing with the Resize And Rotate extension. + * xscreensaver, Copyright (c) 2004, 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 +#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); + } + } + + 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", 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); +} diff --git a/xscreensaver/driver/test-uid.c b/xscreensaver/driver/test-uid.c new file mode 100644 index 0000000..6a1f9cc --- /dev/null +++ b/xscreensaver/driver/test-uid.c @@ -0,0 +1,209 @@ +/* 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); +} diff --git a/xscreensaver/driver/test-vp.c b/xscreensaver/driver/test-vp.c new file mode 100644 index 0000000..bf1a0b1 --- /dev/null +++ b/xscreensaver/driver/test-vp.c @@ -0,0 +1,213 @@ +/* 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); +} diff --git a/xscreensaver/driver/test-xdpms.c b/xscreensaver/driver/test-xdpms.c new file mode 100644 index 0000000..a401c38 --- /dev/null +++ b/xscreensaver/driver/test-xdpms.c @@ -0,0 +1,160 @@ +/* test-xdpms.c --- playing with the XDPMS extension. + * xscreensaver, Copyright (c) 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 + +#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; +} + + +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) + { + Status st; + fprintf(stderr, "%s: monitor is off; turning it on.\n", blurb()); + st = DPMSForceLevel (dpy, DPMSModeOn); + fprintf (stderr, "%s: DPMSForceLevel (dpy, DPMSModeOn) ==> %s\n", + blurb(), (st ? "Ok" : "Error")); + } + + sleep (delay); + } +} diff --git a/xscreensaver/driver/test-xinerama.c b/xscreensaver/driver/test-xinerama.c new file mode 100644 index 0000000..8bafbb0 --- /dev/null +++ b/xscreensaver/driver/test-xinerama.c @@ -0,0 +1,112 @@ +/* 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); +} diff --git a/xscreensaver/driver/timers.c b/xscreensaver/driver/timers.c new file mode 100644 index 0000000..c743492 --- /dev/null +++ b/xscreensaver/driver/timers.c @@ -0,0 +1,1527 @@ +/* timers.c --- detecting when the user is idle, and other timer-related tasks. + * 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 +#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 + { + maybe_reload_init_file (si); + kill_screenhack (si); + + if (!si->throttled_p) + spawn_screenhack (si, False); + else + { + raise_window (si, True, True, False); + 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); +#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 */ + 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; + XEvent 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)); + + 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); + + switch (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. */ + 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! */ + 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, until_idle_p)) + 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.xcreatewindow.window; + start_notice_events_timer (si, w, p->debug_p); + } + break; + + case KeyPress: + case KeyRelease: + case ButtonPress: + case ButtonRelease: + case MotionNotify: + + if (p->debug_p) + { + Window root=0, window=0; + int x=-1, y=-1; + const char *type = 0; + if (event.xany.type == MotionNotify) + { + /*type = "MotionNotify";*/ + root = event.xmotion.root; + window = event.xmotion.window; + x = event.xmotion.x_root; + y = event.xmotion.y_root; + } + else if (event.xany.type == KeyPress) + { + type = "KeyPress"; + root = event.xkey.root; + window = event.xkey.window; + x = y = -1; + } + else if (event.xany.type == ButtonPress) + { + type = "ButtonPress"; + root = event.xkey.root; + window = event.xkey.window; + x = event.xmotion.x_root; + y = 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.xany.type == KeyPress) + { + KeySym keysym; + char c = 0; + XLookupString (&event.xkey, &c, 1, &keysym, 0); + fprintf (stderr, " (%s%s)", + (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); + + + /* 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.xany.type == MotionNotify) + { + int i; + for (i = 0; i < si->nscreens; i++) + if (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.xany.type == MotionNotify || + 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. + */ + goto DONE; + } + else + reset_timers (si); + + break; + + default: + +#ifdef HAVE_MIT_SAVER_EXTENSION + if (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.) + */ + XScreenSaverNotifyEvent *sevent = + (XScreenSaverNotifyEvent *) &event; + if (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 (sevent->kind != ScreenSaverExternal) + { + fprintf (stderr, + "%s: ScreenSaverOn event wasn't of type External!\n", + blurb()); + } + + if (until_idle_p) + goto DONE; + } + else if (sevent->state == ScreenSaverOff) + { + if (p->verbose_p) + fprintf (stderr, "%s: MIT ScreenSaverOff event received.\n", + blurb()); + if (!until_idle_p) + goto DONE; + } + else + fprintf (stderr, + "%s: unknown MIT-SCREEN-SAVER event %d received!\n", + blurb(), sevent->state); + } + else + +#endif /* HAVE_MIT_SAVER_EXTENSION */ + + +#ifdef HAVE_SGI_SAVER_EXTENSION + if (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) + goto DONE; + } + else if (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) + goto DONE; + } + else +#endif /* HAVE_SGI_SAVER_EXTENSION */ + +#ifdef HAVE_RANDR + if (event.type == (si->randr_event_number + RRScreenChangeNotify)) + { + /* The Resize and Rotate extension sends an event when the + size, rotation, or refresh rate of the screen has changed. */ + + XRRScreenChangeNotifyEvent *xrr_event = + (XRRScreenChangeNotifyEvent *) &event; + /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */ + int screen = XRRRootToScreen (si->dpy, xrr_event->window); + + if (p->verbose_p) + { + if (si->screens[screen].width == xrr_event->width && + si->screens[screen].height == xrr_event->height) + fprintf (stderr, + "%s: %d: no-op screen size change event (%dx%d)\n", + blurb(), screen, + xrr_event->width, xrr_event->height); + else + fprintf (stderr, + "%s: %d: screen size changed from %dx%d to %dx%d\n", + blurb(), screen, + si->screens[screen].width, + si->screens[screen].height, + xrr_event->width, xrr_event->height); + } + +# ifdef RRScreenChangeNotifyMask + /* Inform Xlib that it's ok to update its data structures. */ + XRRUpdateConfiguration (&event); /* Xrandr.h 1.9, 2002/09/29 */ +# endif /* RRScreenChangeNotifyMask */ + + /* Resize the existing xscreensaver windows and cached ssi data. */ + resize_screensaver_window (si); + } + else +#endif /* HAVE_RANDR */ + + /* Just some random event. Let the Widgets handle it, if desired. */ + dispatch_event (si, &event); + } + } + DONE: + + + /* 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); + else + while (XCheckMaskEvent (si->dpy, + (KeyPressMask|ButtonPressMask|PointerMotionMask), + &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 (); + + return; +} + + + +/* 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)) + { + if (si->prefs.verbose_p) + fprintf (stderr, + "%s: X says monitor has powered down; " + "killing running hacks.\n", blurb()); + kill_screenhack (si); + } + + /* 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); + } +} diff --git a/xscreensaver/driver/types.h b/xscreensaver/driver/types.h new file mode 100644 index 0000000..4832187 --- /dev/null +++ b/xscreensaver/driver/types.h @@ -0,0 +1,410 @@ +/* types.h + * + * 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 TYPES_H +#define TYPES_H + +typedef struct saver_info saver_info; + +/* Unlock states. Old pw_* equivalents in square brackets: + * ul_read - reading input (or ready to do so) [pw_read] + * ul_success - auth success, unlock the screen [pw_ok] + * ul_fail - authentication failed [pw_fail] + * ul_cancel - user cancelled auth [pw_cancel or pw_null] + * ul_time - timed out, user took too long [pw_time] + * ul_finished - user pressed enter on the current prompt, process input + */ +enum unlock_state { ul_read, ul_success, ul_fail, ul_cancel, ul_time, ul_finished }; + +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; + + +/* 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 */ + 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 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; + saver_screen_info *screens; + saver_screen_info *default_screen; /* ...on which dialogs will appear. */ + + + /* ======================================================================= + global connection info + ======================================================================= */ + + XtAppContext app; + Display *dpy; + + /* ======================================================================= + server extension info + ======================================================================= */ + + Bool xinerama_p; /* Whether Xinerama is in use. */ + 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; +# 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.*/ + enum 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 */ + Visual *best_gl_visual; /* visual to use for GL hacks */ + + 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 diff --git a/xscreensaver/driver/vms-getpwnam.c b/xscreensaver/driver/vms-getpwnam.c new file mode 100644 index 0000000..ec0650c --- /dev/null +++ b/xscreensaver/driver/vms-getpwnam.c @@ -0,0 +1,129 @@ +/* + * 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); +} diff --git a/xscreensaver/driver/vms-hpwd.c b/xscreensaver/driver/vms-hpwd.c new file mode 100644 index 0000000..707e3ea --- /dev/null +++ b/xscreensaver/driver/vms-hpwd.c @@ -0,0 +1,75 @@ +/* + * 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); +} diff --git a/xscreensaver/driver/vms-pwd.h b/xscreensaver/driver/vms-pwd.h new file mode 100644 index 0000000..6cb73d3 --- /dev/null +++ b/xscreensaver/driver/vms-pwd.h @@ -0,0 +1,48 @@ +/* @(#)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 */ diff --git a/xscreensaver/driver/vms-validate.c b/xscreensaver/driver/vms-validate.c new file mode 100644 index 0000000..8f7141d --- /dev/null +++ b/xscreensaver/driver/vms-validate.c @@ -0,0 +1,75 @@ +/* + * 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); +} + diff --git a/xscreensaver/driver/vms_axp.opt b/xscreensaver/driver/vms_axp.opt new file mode 100644 index 0000000..04d465d --- /dev/null +++ b/xscreensaver/driver/vms_axp.opt @@ -0,0 +1,5 @@ +[-.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 diff --git a/xscreensaver/driver/vms_axp_12.opt b/xscreensaver/driver/vms_axp_12.opt new file mode 100644 index 0000000..25dd1f1 --- /dev/null +++ b/xscreensaver/driver/vms_axp_12.opt @@ -0,0 +1,5 @@ +[-.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 diff --git a/xscreensaver/driver/vms_decc.opt b/xscreensaver/driver/vms_decc.opt new file mode 100644 index 0000000..65bec03 --- /dev/null +++ b/xscreensaver/driver/vms_decc.opt @@ -0,0 +1,5 @@ +[-.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 diff --git a/xscreensaver/driver/vms_decc_12.opt b/xscreensaver/driver/vms_decc_12.opt new file mode 100644 index 0000000..fdd9a80 --- /dev/null +++ b/xscreensaver/driver/vms_decc_12.opt @@ -0,0 +1,5 @@ +[-.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 diff --git a/xscreensaver/driver/windows.c b/xscreensaver/driver/windows.c new file mode 100644 index 0000000..9dbb500 --- /dev/null +++ b/xscreensaver/driver/windows.c @@ -0,0 +1,2211 @@ +/* windows.c --- turning the screen black; dealing with visuals, virtual roots. + * 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 + +#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 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 manditory; 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 manditory. + (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. + */ + + if (kstatus != GrabSuccess) /* Do not blank without a kbd grab. */ + return False; + + return True; /* Grab is good, go ahead and blank. */ +} + +static void +ungrab_keyboard_and_mouse (saver_info *si) +{ + ungrab_mouse (si); + ungrab_kbd (si); +} + + +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; + } + } + + 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); + +#ifdef HAVE_XF86VMODE +static Bool safe_XF86VidModeGetViewPort (Display *, int, int *, int *); +#endif /* HAVE_XF86VMODE */ + + +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); + 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) + { + unblank_screen (si); + kill_screenhack (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); +} + + + +/* Returns the area of the screen which the xscreensaver window should cover. + Normally this is the whole screen, but if the X server's root window is + actually larger than the monitor's displayable area, then we want to + operate in the currently-visible portion of the desktop instead. + */ +void +get_screen_viewport (saver_screen_info *ssi, + int *x_ret, int *y_ret, + int *w_ret, int *h_ret, + int target_x, int target_y, + Bool verbose_p) +{ + int w = WidthOfScreen (ssi->screen); + int h = HeightOfScreen (ssi->screen); + +# ifdef HAVE_XF86VMODE + saver_info *si = ssi->global; + saver_preferences *p = &si->prefs; + int event, error; + int dot; + XF86VidModeModeLine ml; + int x, y; + Bool xinerama_p = si->xinerama_p; + +# ifndef HAVE_XINERAMA + /* Even if we don't have the client-side Xinerama lib, check to see if + the server supports Xinerama, so that we know to ignore the VidMode + extension -- otherwise a server crash could result. Yay. */ + xinerama_p = XQueryExtension (si->dpy, "XINERAMA", &error, &event, &error); +# endif /* !HAVE_XINERAMA */ + +# ifdef HAVE_XINERAMA + if (xinerama_p) + { + int mouse_p = (target_x != -1 && target_y != -1); + int which = -1; + int i; + + /* If a mouse position wasn't passed in, assume we're talking about + this screen. */ + if (!mouse_p) + { + target_x = ssi->x; + target_y = ssi->y; + which = ssi->number; + } + + /* Find the Xinerama rectangle that contains the mouse position. */ + for (i = 0; i < si->nscreens; i++) + { + if (which == -1 && + target_x >= si->screens[i].x && + target_y >= si->screens[i].y && + target_x < si->screens[i].x + si->screens[i].width && + target_y < si->screens[i].y + si->screens[i].height) + which = i; + } + if (which == -1) which = 0; /* didn't find it? Use the first. */ + *x_ret = si->screens[which].x; + *y_ret = si->screens[which].y; + *w_ret = si->screens[which].width; + *h_ret = si->screens[which].height; + + if (verbose_p) + { + fprintf (stderr, "%s: %d: xinerama vp: %dx%d+%d+%d", + blurb(), which, + si->screens[which].width, si->screens[which].height, + si->screens[which].x, si->screens[which].y); + if (mouse_p) + fprintf (stderr, "; mouse at %d,%d", target_x, target_y); + fprintf (stderr, ".\n"); + } + + return; + } +# endif /* HAVE_XINERAMA */ + + if (!xinerama_p && /* Xinerama + VidMode = broken. */ + XF86VidModeQueryExtension (si->dpy, &event, &error) && + safe_XF86VidModeGetViewPort (si->dpy, ssi->number, &x, &y) && + XF86VidModeGetModeLine (si->dpy, ssi->number, &dot, &ml)) + { + char msg[512]; + *x_ret = x; + *y_ret = y; + *w_ret = ml.hdisplay; + *h_ret = ml.vdisplay; + + if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h) + /* There is no viewport -- the screen does not scroll. */ + return; + + + /* Apparently some versions of XFree86 return nonsense here! + I've had reports of 1024x768 viewports at -1936862040, -1953705044. + So, sanity-check the values and give up if they are out of range. + */ + if (*x_ret < 0 || *x_ret >= w || + *y_ret < 0 || *y_ret >= h || + *w_ret <= 0 || *w_ret > w || + *h_ret <= 0 || *h_ret > h) + { + static int warned_once = 0; + if (!warned_once) + { + fprintf (stderr, "\n" + "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n" + "%s: The XVidMode server extension is returning nonsense.\n" + "%s: Please report this bug to your X server vendor.\n\n", + blurb(), *w_ret, *h_ret, *x_ret, *y_ret, + blurb(), blurb()); + warned_once = 1; + } + *x_ret = 0; + *y_ret = 0; + *w_ret = w; + *h_ret = h; + return; + } + + sprintf (msg, "%s: %d: vp is %dx%d+%d+%d", + blurb(), ssi->number, + *w_ret, *h_ret, *x_ret, *y_ret); + + + if (p->getviewport_full_of_lies_p) + { + /* 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 + + The XFree86 developers have closed the bug. As far as I can + tell, their reason for this was, "this is an X server bug, + but it's pretty hard to fix. Therefore, we are closing it." + + So, now there's a preference item for those unfortunate users to + tell us not to trust a word that XF86VidModeGetViewPort() says. + */ + static int warned_once = 0; + if (!warned_once && verbose_p) + { + warned_once = 1; + fprintf (stderr, + "%s: %d: XF86VidModeGetViewPort() says vp is %dx%d+%d+%d;\n" + "%s: %d: assuming that is a pack of lies;\n" + "%s: %d: using %dx%d+0+0 instead.\n", + blurb(), ssi->number, + *w_ret, *h_ret, *x_ret, *y_ret, + blurb(), ssi->number, + blurb(), ssi->number, w, h); + } + + *x_ret = 0; + *y_ret = 0; + *w_ret = w; + *h_ret = h; + return; + } + + + /* 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 (x > 0 && x < w - 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. + */ + *x_ret = ((x - 1) / FUDGE) * FUDGE; + *w_ret += (FUDGE * 2); + } +# undef FUDGE + + if (*x_ret != x || + *y_ret != y || + *w_ret != ml.hdisplay || + *h_ret != ml.vdisplay) + sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d", + *w_ret, *h_ret, *x_ret, *y_ret); + + if (verbose_p) + fprintf (stderr, "%s.\n", msg); + + return; + } + +# endif /* HAVE_XF86VMODE */ + + *x_ret = 0; + *y_ret = 0; + *w_ret = w; + *h_ret = h; +} + + +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 +static 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; + int x, y, width, height; + static Bool printed_visual_info = False; /* only print the message once. */ + Window horked_window = 0; + + get_screen_viewport (ssi, &x, &y, &width, &height, -1, -1, + (p->verbose_p && !si->screen_blanked_p)); + + 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->debug_p +# ifdef QUAD_MODE + && !p->quad_p +# endif + ) + width = width / 2; + + 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 = x; + changes.y = y; + changes.width = width; + changes.height = 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), + x, y, width, 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. If we + don't do this, then the screen saver will no longer fully fill the + screen, and some of the underlying desktop may be visible. + */ +void +resize_screensaver_window (saver_info *si) +{ + saver_preferences *p = &si->prefs; + int i; + + /* First update the size info in the saver_screen_info structs. + */ + +# ifdef HAVE_XINERAMA + if (si->xinerama_p) + { + /* As of XFree86 4.3.0, the RANDR and XINERAMA extensions cannot coexist. + However, maybe they will someday, so I'm guessing that the right thing + to do in that case will be to re-query the Xinerama rectangles after + a RANDR size change is received: presumably, if the resolution of one + or more of the monitors has changed, then the Xinerama rectangle + corresponding to that monitor will also have been updated. + */ + int nscreens; + XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens); + + if (nscreens != si->nscreens) { + /* Apparently some Xinerama implementations let you use a hot-key + to change the number of screens in use! This is, of course, + documented nowhere. Let's try to do something marginally less + bad than crashing. + */ + fprintf (stderr, "%s: bad craziness: xinerama screen count changed " + "from %d to %d!\n", blurb(), si->nscreens, nscreens); + if (nscreens > si->nscreens) + nscreens = si->nscreens; + } + + if (!xsi) abort(); + for (i = 0; i < nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (p->verbose_p && + (ssi->x != xsi[i].x_org || + ssi->y != xsi[i].y_org || + ssi->width != xsi[i].width || + ssi->height != xsi[i].height)) + fprintf (stderr, + "%s: %d: resize xinerama from %dx%d+%d+%d to %dx%d+%d+%d\n", + blurb(), i, + ssi->width, ssi->height, ssi->x, ssi->y, + xsi[i].width, xsi[i].height, xsi[i].x_org, xsi[i].y_org); + + ssi->x = xsi[i].x_org; + ssi->y = xsi[i].y_org; + ssi->width = xsi[i].width; + ssi->height = xsi[i].height; + } + XFree (xsi); + } + else +# endif /* HAVE_XINERAMA */ + { + /* Not Xinerama -- get the real sizes of the root windows. */ + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + XWindowAttributes xgwa; + XGetWindowAttributes (si->dpy, RootWindowOfScreen (ssi->screen), + &xgwa); + + if (p->verbose_p && + (ssi->x != xgwa.x || + ssi->y != xgwa.y || + ssi->width != xgwa.width || + ssi->height != xgwa.height)) + fprintf (stderr, + "%s: %d: resize screen from %dx%d+%d+%d to %dx%d+%d+%d\n", + blurb(), i, + ssi->width, ssi->height, ssi->x, ssi->y, + xgwa.width, xgwa.height, xgwa.x, xgwa.y); + + ssi->x = xgwa.x; + ssi->y = xgwa.y; + ssi->width = xgwa.width; + ssi->height = xgwa.height; + } + } + + /* Next, ensure that the screensaver windows are the right size, taking + into account both the new size of the screen in question's root window, + and any viewport within that. + */ + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + XWindowAttributes xgwa; + XWindowChanges changes; + int x, y, width, height; + unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; + + XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa); + get_screen_viewport (ssi, &x, &y, &width, &height, -1, -1, + (p->verbose_p && !si->screen_blanked_p)); + if (xgwa.x == x && + xgwa.y == y && + xgwa.width == width && + xgwa.height == height) + continue; /* no change! */ + + changes.x = x; + changes.y = y; + changes.width = width; + changes.height = height; + changes.border_width = 0; + + if (p->debug_p +# ifdef QUAD_MODE + && !p->quad_p +# endif + ) + changes.width = changes.width / 2; + + 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, + width, height, x, 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); + } + } +} + + +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); + 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) */ + } +} + + + +Bool +select_visual (saver_screen_info *ssi, const char *visual_name) +{ + 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; + + 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 = ssi->best_gl_visual; + 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 (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 (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; +} diff --git a/xscreensaver/driver/xdpyinfo.c b/xscreensaver/driver/xdpyinfo.c new file mode 100644 index 0000000..7a0b078 --- /dev/null +++ b/xscreensaver/driver/xdpyinfo.c @@ -0,0 +1,1085 @@ +/* + * $ 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); + +# ifdef GLX_SAMPLE_BUFFERS_SGIS + if (!glXGetConfig (dpy, vip, GLX_SAMPLE_BUFFERS_SGIS, &value) && + value != 0) + { + int bufs = value; + if (!glXGetConfig (dpy, vip, GLX_SAMPLES_SGIS, &value)) + printf (" GLX multisamplers: %d (%d)\n", bufs, value); + } +# endif + + 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); +} diff --git a/xscreensaver/driver/xscreensaver-command.c b/xscreensaver/driver/xscreensaver-command.c new file mode 100644 index 0000000..de507a0 --- /dev/null +++ b/xscreensaver/driver/xscreensaver-command.c @@ -0,0 +1,439 @@ +/* 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 -