From 96a411663168b0ba5432b407a83be55f3df0c802 Mon Sep 17 00:00:00 2001 From: Zygo Blaxell Date: Mon, 2 Mar 2009 00:43:14 -0500 Subject: [PATCH] http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz -rw-r--r-- 1 zblaxell zblaxell 3988251 Oct 30 2003 xscreensaver-4.14.tar.gz 4996718deaceeb578953f400e518b745c1dd32c4 xscreensaver-4.14.tar.gz --- Makefile.in | 1 + README | 9 +- configure | 64 +- configure.in | 50 +- driver/Makefile.in | 50 +- driver/XScreenSaver.ad.in | 48 +- driver/XScreenSaver_ad.h | 22 +- driver/demo-Gtk-conf.c | 12 +- driver/demo-Gtk.c | 21 +- driver/passwd-kerberos.c | 2 + driver/pdf2jpeg.m | 132 + driver/pdf2jpeg.man | 43 + driver/xscreensaver-command.man | 2 +- driver/xscreensaver-demo.man | 2 +- driver/xscreensaver-getimage-desktop | 184 ++ driver/xscreensaver-getimage-desktop.man | 55 + driver/xscreensaver-getimage-file.man | 2 +- driver/xscreensaver-getimage-video | 4 +- driver/xscreensaver-getimage-video.man | 2 +- driver/xscreensaver-getimage.c | 377 ++- driver/xscreensaver-getimage.man | 24 +- driver/xscreensaver.c | 22 +- driver/xscreensaver.man | 2 +- hacks/Makefile.in | 55 +- hacks/analogtv.c | 2138 ++++++++++++++ hacks/analogtv.h | 290 ++ hacks/ant.c | 3 +- hacks/apollonian.c | 2 +- hacks/apple2-main.c | 1321 +++++++++ hacks/apple2.c | 790 ++++++ hacks/apple2.h | 111 + hacks/apple2.man | 149 + hacks/blitspin.c | 2 +- hacks/bouboule.c | 3 +- hacks/braid.c | 3 +- hacks/bsod.c | 1788 +++--------- hacks/bsod.man | 15 +- hacks/bumps.c | 2 +- hacks/compile_axp.com | 6 + hacks/compile_decc.com | 6 + hacks/config/README | 4 +- hacks/config/apple2.xml | 34 + hacks/config/blinkbox.xml | 16 + hacks/config/bsod.xml | 1 + hacks/config/fontglide.xml | 42 + hacks/config/gleidescope.xml | 24 + hacks/config/glslideshow.xml | 2 + hacks/config/mirrorblob.xml | 59 + hacks/config/pong.xml | 20 + hacks/config/strange.xml | 4 - hacks/config/xanalogtv.xml | 18 + hacks/crystal.c | 3 +- hacks/decayscreen.c | 2 +- hacks/deluxe.c | 4 +- hacks/demon.c | 3 +- hacks/discrete.c | 3 +- hacks/distort.c | 2 +- hacks/drift.c | 3 +- hacks/euler2d.c | 3 +- hacks/fadeplot.c | 3 +- hacks/flag.c | 2 +- hacks/flow.c | 3 +- hacks/fluidballs.c | 9 +- hacks/fontglide.c | 1589 +++++++++++ hacks/fontglide.man | 119 + hacks/galaxy.c | 2 +- hacks/glx/Makefile.in | 42 +- hacks/glx/antspotlight.c | 5 +- hacks/glx/atlantis.c | 3 +- hacks/glx/atunnel.c | 2 +- hacks/glx/b_draw.c | 3 +- hacks/glx/b_lockglue.c | 3 +- hacks/glx/b_sphere.c | 3 +- hacks/glx/blinkbox.c | 351 +++ hacks/glx/blinkbox.man | 49 + hacks/glx/boxed.c | 3 +- hacks/glx/bubble3d.c | 3 +- hacks/glx/buildlwo.c | 3 +- hacks/glx/cage.c | 3 +- hacks/glx/chessgames.h | 181 +- hacks/glx/dolphin.c | 3 +- hacks/glx/endgame.c | 137 +- hacks/glx/engine.c | 8 +- hacks/glx/flipscreen3d.c | 2 +- hacks/glx/flurry.c | 11 +- hacks/glx/fps.c | 4 +- hacks/glx/gears.c | 3 +- hacks/glx/gflux.c | 3 +- hacks/glx/gleidescope.c | 1088 +++++++ hacks/glx/gleidescope.man | 77 + hacks/glx/glforestfire.c | 2 +- hacks/glx/glknots.c | 4 +- hacks/glx/glslideshow.c | 146 +- hacks/glx/glslideshow.man | 4 + hacks/glx/glsnake.c | 3315 +++++++++++++++------- hacks/glx/gltext.c | 2 +- hacks/glx/grab-ximage.c | 4 +- hacks/glx/grab-ximage.h | 3 +- hacks/glx/hypertorus.c | 3 +- hacks/glx/lavalite.c | 3 +- hacks/glx/mirrorblob.c | 1250 ++++++++ hacks/glx/mirrorblob.man | 111 + hacks/glx/moebius.c | 3 +- hacks/glx/morph3d.c | 3 +- hacks/glx/pipeobjs.c | 3 +- hacks/glx/pipes.c | 3 +- hacks/glx/polytopes.c | 3 +- hacks/glx/rubik.c | 3 +- hacks/glx/sballs.c | 2 +- hacks/glx/shark.c | 3 +- hacks/glx/sierpinski3d.c | 3 +- hacks/glx/spheremonics.c | 4 +- hacks/glx/sproingies.c | 3 +- hacks/glx/sproingiewrap.c | 3 +- hacks/glx/stairs.c | 3 +- hacks/glx/starwars.c | 2 +- hacks/glx/starwars.man | 18 +- hacks/glx/stonerview.c | 12 +- hacks/glx/superquadrics.c | 3 +- hacks/glx/swim.c | 3 +- hacks/glx/tunnel_draw.c | 2 +- hacks/glx/whale.c | 3 +- hacks/grav.c | 3 +- hacks/halo.c | 2 +- hacks/helix.c | 2 +- hacks/hopalong.c | 3 +- hacks/ifs.c | 3 +- hacks/imsmap.c | 2 +- hacks/jigsaw.c | 2 +- hacks/juggle.c | 11 +- hacks/julia.c | 2 +- hacks/laser.c | 3 +- hacks/lightning.c | 3 +- hacks/lisa.c | 3 +- hacks/lissie.c | 3 +- hacks/ljlatest | 19 +- hacks/ljlatest.man | 5 +- hacks/loop.c | 3 +- hacks/metaballs.c | 2 +- hacks/mountain.c | 3 +- hacks/penrose.c | 3 +- hacks/phosphor.c | 1 - hacks/phosphor.man | 22 +- hacks/polyominoes.c | 3 +- hacks/pong.c | 505 ++++ hacks/pong.man | 74 + hacks/ripples.c | 2 +- hacks/rotor.c | 3 +- hacks/rotzoomer.c | 2 +- hacks/sierpinski.c | 3 +- hacks/slidescreen.c | 2 +- hacks/slip.c | 5 +- hacks/sonar.c | 24 +- hacks/sphere.c | 3 +- hacks/spiral.c | 3 +- hacks/spotlight.c | 2 +- hacks/strange.c | 3 +- hacks/swirl.c | 2 +- hacks/thornbird.c | 3 +- hacks/triangle.c | 3 +- hacks/twang.c | 2 +- hacks/vines.c | 3 +- hacks/webcollage-helper.c | 2 +- hacks/whirlygig.c | 4 +- hacks/worm.c | 3 +- hacks/xanalogtv.c | 448 +++ hacks/xanalogtv.man | 86 + hacks/xflame.c | 2 + hacks/xflame.man | 7 +- hacks/xsublim.c | 19 - hacks/xteevee.c | 2 +- hacks/xteevee.man | 7 + hacks/zoom.c | 2 +- po/Makefile.in.in | 5 +- po/POTFILES.in | 374 +-- po/ca.po | 2 +- po/fi.po | 2 +- po/it.po | 2 +- po/pt_BR.po | 2 +- setup.com | 4 + utils/grabclient.c | 25 +- utils/grabscreen.c | 4 +- utils/grabscreen.h | 20 +- utils/usleep.c | 6 +- utils/version.h | 2 +- xscreensaver.lsm | 18 +- xscreensaver.lsm.sh | 2 +- xscreensaver.spec | 10 +- 188 files changed, 15317 insertions(+), 3197 deletions(-) create mode 100644 driver/pdf2jpeg.m create mode 100644 driver/pdf2jpeg.man create mode 100755 driver/xscreensaver-getimage-desktop create mode 100644 driver/xscreensaver-getimage-desktop.man create mode 100644 hacks/analogtv.c create mode 100644 hacks/analogtv.h create mode 100644 hacks/apple2-main.c create mode 100644 hacks/apple2.c create mode 100644 hacks/apple2.h create mode 100644 hacks/apple2.man create mode 100644 hacks/config/apple2.xml create mode 100644 hacks/config/blinkbox.xml create mode 100644 hacks/config/fontglide.xml create mode 100644 hacks/config/gleidescope.xml create mode 100644 hacks/config/mirrorblob.xml create mode 100644 hacks/config/pong.xml create mode 100644 hacks/config/xanalogtv.xml create mode 100644 hacks/fontglide.c create mode 100644 hacks/fontglide.man create mode 100644 hacks/glx/blinkbox.c create mode 100644 hacks/glx/blinkbox.man create mode 100644 hacks/glx/gleidescope.c create mode 100644 hacks/glx/gleidescope.man create mode 100644 hacks/glx/mirrorblob.c create mode 100644 hacks/glx/mirrorblob.man create mode 100644 hacks/pong.c create mode 100644 hacks/pong.man create mode 100644 hacks/xanalogtv.c create mode 100644 hacks/xanalogtv.man diff --git a/Makefile.in b/Makefile.in index f6f2efb5..67c88cfc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -319,4 +319,5 @@ count:: ( cd hacks/glx; make -s INSTALL=true install-program ) ) | \ grep true | \ grep -v helper | \ + grep -v ljlatest | \ wc -l diff --git a/README b/README index 1a27506e..03628479 100644 --- a/README +++ b/README @@ -65,7 +65,7 @@ there's no need to recompile or reinstall anything. Along with the xscreensaver daemon itself, this package also includes numerous graphics hacks for use as screensavers. There is nothing magic about these: they are just programs that draw on the root window. -More than 160 such programs are included. For details, see the +More than 175 such programs are included. For details, see the xscreensaver web page, or the enclosed manual pages. The latest version of xscreensaver is always available on the web at @@ -76,6 +76,13 @@ the XScreenSaver FAQ about that: http://www.jwz.org/xscreensaver/faq.html ============ +Changes since 4.13: * New hacks, `fontglide', `apple2', `xanalogtv', `pong', + `gleidescope', `mirrorblob', and `blinkbox'. + * New version of `glsnake' (with many more models.) + * Another Windows crash in `bsod'; also HVX/GCOS6/TPS6. + * New version of `endgame'. + * Screen grabbing works on MacOS X. + * Various minor fixes. Changes since 4.12: * On Xinerama systems, xscreensaver now runs one hack on each monitor (just like in "real" multi-head mode) instead of running one hack stretching across all the diff --git a/configure b/configure index 11e238a0..e0126736 100755 --- a/configure +++ b/configure @@ -308,7 +308,7 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA SET_MAKE EGREP PERL X_CFLAGS X_PRE_LIBS X_LIBS X_EXTRA_LIBS INTLTOOL_DESKTOP_RULE INTLTOOL_DIRECTORY_RULE INTLTOOL_KEYS_RULE INTLTOOL_OAF_RULE INTLTOOL_PONG_RULE INTLTOOL_SERVER_RULE INTLTOOL_SHEET_RULE INTLTOOL_SOUNDLIST_RULE INTLTOOL_UI_RULE INTLTOOL_XML_RULE INTLTOOL_CAVES_RULE INTLTOOL_EXTRACT INTLTOOL_MERGE INTLTOOL_UPDATE INTLTOOL_PERL GETTEXT_PACKAGE RANLIB ac_ct_RANLIB ALLOCA USE_NLS MSGFMT GMSGFMT XGETTEXT CATALOGS CATOBJEXT DATADIRNAME GMOFILES INSTOBJEXT INTLDEPS INTLLIBS INTLOBJS POFILES POSUB MKINSTALLDIRS pkg_config glib_config gtk_config gnome_config have_gnome_help xml_config gdk_pixbuf_config fortune_tmp INCLUDES PREFERRED_DEMO_PROGRAM ALL_DEMO_PROGRAMS SAVER_LIBS MOTIF_LIBS GTK_LIBS XML_LIBS JPEG_LIBS HACK_LIBS XPM_LIBS GL_LIBS GLE_LIBS XDPMS_LIBS PASSWD_LIBS INSTALL_SETUID SETUID_HACKS INSTALL_DIRS NEED_SETUID INSTALL_PAM PASSWD_SRCS PASSWD_OBJS XMU_SRCS XMU_OBJS XMU_LIBS SAVER_GL_SRCS SAVER_GL_OBJS SAVER_GL_LIBS LOCK_SRCS LOCK_OBJS JPEG_EXES GL_EXES GL_UTIL_EXES GL_MEN GL_KLUDGE GLE_EXES GLE_KLUDGE GNOMEHELP_Y GNOMEHELP_N HACKDIR GNOME_DATADIR GLADE_DATADIR PO_DATADIR GNOME_PANELDIR HACK_CONF_DIR GTK_EXTRA_OBJS APPDEFAULTS DEPEND DEPEND_FLAGS DEPEND_DEFINES LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA SET_MAKE EGREP PERL X_CFLAGS X_PRE_LIBS X_LIBS X_EXTRA_LIBS INTLTOOL_DESKTOP_RULE INTLTOOL_DIRECTORY_RULE INTLTOOL_KEYS_RULE INTLTOOL_OAF_RULE INTLTOOL_PONG_RULE INTLTOOL_SERVER_RULE INTLTOOL_SHEET_RULE INTLTOOL_SOUNDLIST_RULE INTLTOOL_UI_RULE INTLTOOL_XML_RULE INTLTOOL_CAVES_RULE INTLTOOL_EXTRACT INTLTOOL_MERGE INTLTOOL_UPDATE INTLTOOL_PERL GETTEXT_PACKAGE RANLIB ac_ct_RANLIB ALLOCA USE_NLS MSGFMT GMSGFMT XGETTEXT CATALOGS CATOBJEXT DATADIRNAME GMOFILES INSTOBJEXT INTLDEPS INTLLIBS INTLOBJS POFILES POSUB MKINSTALLDIRS pkg_config glib_config gtk_config gnome_config have_gnome_help xml_config gdk_pixbuf_config fortune_tmp INCLUDES PREFERRED_DEMO_PROGRAM ALL_DEMO_PROGRAMS SAVER_LIBS MOTIF_LIBS GTK_LIBS XML_LIBS JPEG_LIBS HACK_LIBS XPM_LIBS GL_LIBS GLE_LIBS XDPMS_LIBS PASSWD_LIBS INSTALL_SETUID SETUID_HACKS INSTALL_DIRS NEED_SETUID INSTALL_PAM OBJCC EXES_OSX SCRIPTS_OSX MEN_OSX PASSWD_SRCS PASSWD_OBJS XMU_SRCS XMU_OBJS XMU_LIBS SAVER_GL_SRCS SAVER_GL_OBJS SAVER_GL_LIBS LOCK_SRCS LOCK_OBJS JPEG_EXES GL_EXES GL_UTIL_EXES GL_MEN GL_KLUDGE GLE_EXES GLE_KLUDGE GNOMEHELP_Y GNOMEHELP_N HACKDIR GNOME_DATADIR GLADE_DATADIR PO_DATADIR GNOME_PANELDIR HACK_CONF_DIR GTK_EXTRA_OBJS APPDEFAULTS DEPEND DEPEND_FLAGS DEPEND_DEFINES LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -2523,6 +2523,8 @@ echo "${ECHO_T}no idea" >&6 esac fi + OBJCC="$CC" + echo "$as_me:$LINENO: checking whether the compiler works on ANSI C" >&5 echo $ECHO_N "checking whether the compiler works on ANSI C... $ECHO_C" >&6 if test "$cross_compiling" = yes; then @@ -2571,6 +2573,7 @@ fi echo "$as_me:$LINENO: result: Turning on gcc compiler warnings." >&5 echo "${ECHO_T}Turning on gcc compiler warnings." >&6 CC="$CC -pedantic -Wall -Wstrict-prototypes -Wnested-externs" + OBJCC="$OBJCC -Wall" # supposedly gcc 3.4 will have "-Wdeclaration-after-statement" # and then perhaps we can do without -pedantic? else @@ -6448,6 +6451,18 @@ _ACEOF fi +echo "$as_me:$LINENO: checking whether this is MacOS X" >&5 +echo $ECHO_N "checking whether this is MacOS X... $ECHO_C" >&6 + ac_macosx=no + case "$host" in + *-apple-darwin* ) + ac_macosx=yes + ;; + esac +echo "$as_me:$LINENO: result: $ac_macosx" >&5 +echo "${ECHO_T}$ac_macosx" >&6 + + ############################################################################### # @@ -11076,6 +11091,19 @@ else exit 1 fi +# We can't lock on MacOS X, so don't even bother compiling in support for it. +# +if test "$ac_macosx" = yes; then + if test "$enable_locking" = yes; then + echo "$as_me:$LINENO: result: locking disabled: it doesn't work on MacOS X" >&5 +echo "${ECHO_T}locking disabled: it doesn't work on MacOS X" >&6 + enable_locking=no + cat >>confdefs.h <<\_ACEOF +#define NO_LOCKING 1 +_ACEOF + + fi +fi ############################################################################### @@ -15364,7 +15392,7 @@ if test "${ac_cv_mesagl_version_string+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat > conftest.$ac_ext < #ifndef MESA_MAJOR_VERSION @@ -18514,6 +18542,17 @@ else LOCK_OBJS='$(NOLOCK_OBJS_1)' fi +if test "$ac_macosx" = yes; then + EXES_OSX='$(EXES_OSX)' + SCRIPTS_OSX='$(SCRIPTS_OSX)' + MEN_OSX='$(MEN_OSX)' +else + EXES_OSX= + SCRIPTS_OSX= + MEN_OSX= +fi + + INSTALL_SETUID='$(INSTALL_PROGRAM) $(SUID_FLAGS)' if test "$need_setuid" = yes; then @@ -18700,6 +18739,11 @@ INCLUDES=`echo "$INCLUDES" | sed 's@ -I${prefix}/include@@g;'` + + + + + @@ -19440,6 +19484,10 @@ s,@SETUID_HACKS@,$SETUID_HACKS,;t t s,@INSTALL_DIRS@,$INSTALL_DIRS,;t t s,@NEED_SETUID@,$NEED_SETUID,;t t s,@INSTALL_PAM@,$INSTALL_PAM,;t t +s,@OBJCC@,$OBJCC,;t t +s,@EXES_OSX@,$EXES_OSX,;t t +s,@SCRIPTS_OSX@,$SCRIPTS_OSX,;t t +s,@MEN_OSX@,$MEN_OSX,;t t s,@PASSWD_SRCS@,$PASSWD_SRCS,;t t s,@PASSWD_OBJS@,$PASSWD_OBJS,;t t s,@XMU_SRCS@,$XMU_SRCS,;t t @@ -20169,10 +20217,12 @@ fi if test "$gtk2_halfassed" != no ; then warnL "GTK version $gtk2_halfassed was found, but at least one supporting" warn2 "library ($gtk2_halfassed_lib) was not, so GTK 2.x can't be used." - v="$ac_gtk_version_string" - warn2 "GTK $v is also installed, so it will be used instead." - warn2 "Please read the above output and the \`config.log' file" - warn2 "for more details." + if test "$have_gtk" = yes ; then + v="$ac_gtk_version_string" + warn2 "GTK $v is also installed, so it will be used instead." + warn2 "Please read the above output and the \`config.log' file" + warn2 "for more details." + fi fi @@ -20526,7 +20576,7 @@ if test "$do_dir_warning" = yes; then echo ' "xscreensaver-demo", and "xscreensaver-command" executables' echo " will be installed in ${bindir}/." echo "" - echo " The various graphics demos (160+ different executables) will" + echo " The various graphics demos (175+ different executables) will" echo " be installed in ${HACKDIR}/." echo "" echo " If you would prefer the demos to be installed elsewhere," diff --git a/configure.in b/configure.in index 51c8e526..8397156d 100644 --- a/configure.in +++ b/configure.in @@ -56,6 +56,8 @@ AC_DEFUN(AC_PROG_CC_ANSI, esac fi + OBJCC="$CC" + AC_MSG_CHECKING([whether the compiler works on ANSI C]) AC_TRY_RUN([ main(int ac, char **av) { return 0; } ], AC_MSG_RESULT(yes), @@ -66,6 +68,7 @@ AC_DEFUN(AC_PROG_CC_ANSI, if test -n "$GCC"; then AC_MSG_RESULT(Turning on gcc compiler warnings.) CC="$CC -pedantic -Wall -Wstrict-prototypes -Wnested-externs" + OBJCC="$OBJCC -Wall" # supposedly gcc 3.4 will have "-Wdeclaration-after-statement" # and then perhaps we can do without -pedantic? else @@ -849,6 +852,16 @@ AC_PATH_X_APP_DEFAULTS AC_X_RANDOM_PATHS AC_XPOINTER +AC_MSG_CHECKING(whether this is MacOS X) + ac_macosx=no + case "$host" in + *-apple-darwin* ) + ac_macosx=yes + ;; + esac +AC_MSG_RESULT($ac_macosx) + + ############################################################################### # @@ -1459,6 +1472,15 @@ else exit 1 fi +# We can't lock on MacOS X, so don't even bother compiling in support for it. +# +if test "$ac_macosx" = yes; then + if test "$enable_locking" = yes; then + AC_MSG_RESULT(locking disabled: it doesn't work on MacOS X) + enable_locking=no + AC_DEFINE(NO_LOCKING) + fi +fi ############################################################################### @@ -3407,6 +3429,17 @@ else LOCK_OBJS='$(NOLOCK_OBJS_1)' fi +if test "$ac_macosx" = yes; then + EXES_OSX='$(EXES_OSX)' + SCRIPTS_OSX='$(SCRIPTS_OSX)' + MEN_OSX='$(MEN_OSX)' +else + EXES_OSX= + SCRIPTS_OSX= + MEN_OSX= +fi + + INSTALL_SETUID='$(INSTALL_PROGRAM) $(SUID_FLAGS)' if test "$need_setuid" = yes; then @@ -3570,6 +3603,11 @@ AC_SUBST(INSTALL_DIRS) AC_SUBST(NEED_SETUID) AC_SUBST(INSTALL_PAM) +AC_SUBST(OBJCC) +AC_SUBST(EXES_OSX) +AC_SUBST(SCRIPTS_OSX) +AC_SUBST(MEN_OSX) + AC_SUBST(PASSWD_SRCS) AC_SUBST(PASSWD_OBJS) AC_SUBST(XMU_SRCS) @@ -3761,10 +3799,12 @@ fi if test "$gtk2_halfassed" != no ; then warnL "GTK version $gtk2_halfassed was found, but at least one supporting" warn2 "library ($gtk2_halfassed_lib) was not, so GTK 2.x can't be used." - v="$ac_gtk_version_string" - warn2 "GTK $v is also installed, so it will be used instead." - warn2 "Please read the above output and the \`config.log' file" - warn2 "for more details." + if test "$have_gtk" = yes ; then + v="$ac_gtk_version_string" + warn2 "GTK $v is also installed, so it will be used instead." + warn2 "Please read the above output and the \`config.log' file" + warn2 "for more details." + fi fi @@ -4113,7 +4153,7 @@ if test "$do_dir_warning" = yes; then echo ' "xscreensaver-demo", and "xscreensaver-command" executables' echo " will be installed in ${bindir}/." echo "" - echo " The various graphics demos (160+ different executables) will" + echo " The various graphics demos (175+ different executables) will" echo " be installed in ${HACKDIR}/." echo "" echo " If you would prefer the demos to be installed elsewhere," diff --git a/driver/Makefile.in b/driver/Makefile.in index 42d9da23..b7d30828 100644 --- a/driver/Makefile.in +++ b/driver/Makefile.in @@ -3,7 +3,7 @@ @SET_MAKE@ .SUFFIXES: -.SUFFIXES: .c .o +.SUFFIXES: .c .m .o srcdir = @srcdir@ VPATH = @srcdir@ @@ -36,6 +36,7 @@ GNOME_BINDIR = $(bindir) HACK_CONF_DIR = @HACK_CONF_DIR@ CC = @CC@ +OBJCC = @OBJCC@ CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ DEFS = @DEFS@ @@ -192,6 +193,10 @@ DEMO_OBJS_1 = prefs.o dpms.o $(XMU_OBJS) DEMO_SRCS = prefs.c dpms.c remote.c exec.c $(DEMO_UTIL_SRCS) DEMO_OBJS = prefs.o dpms.o remote.o exec.o $(DEMO_UTIL_OBJS) +PDF2JPEG_SRCS = pdf2jpeg.m +PDF2JPEG_OBJS = pdf2jpeg.o +PDF2JPEG_LIBS = -framework Cocoa + SAVER_LIBS = $(LIBS) $(X_LIBS) $(XMU_LIBS) @SAVER_LIBS@ \ $(XDPMS_LIBS) $(GL_LIBS) $(X_PRE_LIBS) \ -lXt -lX11 -lXext $(X_EXTRA_LIBS) \ @@ -204,18 +209,25 @@ GETIMG_LIBS = $(LIBS) $(X_LIBS) $(XPM_LIBS) $(JPEG_LIBS) \ $(X_PRE_LIBS) -lXt -lX11 $(XMU_LIBS) -lXext $(X_EXTRA_LIBS) EXES = xscreensaver xscreensaver-command xscreensaver-demo \ - xscreensaver-getimage + xscreensaver-getimage @EXES_OSX@ EXES2 = @ALL_DEMO_PROGRAMS@ -SCRIPTS = xscreensaver-getimage-file xscreensaver-getimage-video +EXES_OSX = pdf2jpeg + +SCRIPTS_1 = xscreensaver-getimage-file xscreensaver-getimage-video +SCRIPTS_OSX = xscreensaver-getimage-desktop +SCRIPTS = $(SCRIPTS_1) @SCRIPTS_OSX@ HDRS = XScreenSaver_ad.h xscreensaver.h prefs.h remote.h \ demo-Gtk-widgets.h demo-Gtk-stubs.h demo-Gtk-support.h \ demo-Gtk-conf.h -MEN = xscreensaver.man xscreensaver-demo.man \ +MEN_1 = xscreensaver.man xscreensaver-demo.man \ xscreensaver-command.man \ xscreensaver-getimage.man \ xscreensaver-getimage-file.man \ xscreensaver-getimage-video.man +MEN_OSX = xscreensaver-getimage-desktop.man pdf2jpeg.man +MEN = $(MEN_1) @MEN_OSX@ + EXTRAS = README Makefile.in XScreenSaver.ad.in xscreensaver.pam \ xscreensaver-demo.glade xscreensaver-demo.glade2 \ xscreensaver-demo.glade2p \ @@ -230,8 +242,9 @@ VMSFILES = compile_axp.com compile_decc.com link_axp.com link_decc.com \ TARFILES = $(EXTRAS) $(VMSFILES) $(SAVER_SRCS_1) \ $(MOTIF_SRCS) $(GTK_SRCS) $(PWENT_SRCS) \ $(KERBEROS_SRCS) $(PAM_SRCS) $(LOCK_SRCS_1) $(DEMO_SRCS_1) \ - $(CMD_SRCS) $(GETIMG_SRCS_1) $(HDRS) $(SCRIPTS) \ - $(TEST_SRCS) $(MEN) + $(CMD_SRCS) $(GETIMG_SRCS_1) $(PDF2JPEG_SRCS) $(HDRS) \ + $(SCRIPTS_1) $(SCRIPTS_OSX) $(MEN_1) $(MEN_OSX) \ + $(TEST_SRCS) default: $(EXES) @@ -274,18 +287,11 @@ install-program: $(EXES) fi ; \ echo $$inst xscreensaver $(install_prefix)$(bindir)/xscreensaver ; \ $$inst xscreensaver $(install_prefix)$(bindir)/xscreensaver - @echo $(INSTALL_PROGRAM) xscreensaver-command \ - $(install_prefix)$(bindir)/xscreensaver-command ; \ - $(INSTALL_PROGRAM) xscreensaver-command \ - $(install_prefix)$(bindir)/xscreensaver-command - @echo $(INSTALL_PROGRAM) xscreensaver-demo \ - $(install_prefix)$(bindir)/xscreensaver-demo ; \ - $(INSTALL_PROGRAM) xscreensaver-demo \ - $(install_prefix)$(bindir)/xscreensaver-demo - @echo $(INSTALL_PROGRAM) xscreensaver-getimage \ - $(install_prefix)$(bindir)/xscreensaver-getimage ; \ - $(INSTALL_PROGRAM) xscreensaver-getimage \ - $(install_prefix)$(bindir)/xscreensaver-getimage + @for exe in xscreensaver-command xscreensaver-demo \ + xscreensaver-getimage @EXES_OSX@ ; do \ + echo $(INSTALL_PROGRAM) $$exe $(install_prefix)$(bindir)/$$exe ; \ + $(INSTALL_PROGRAM) $$exe $(install_prefix)$(bindir)/$$exe ; \ + done install-ad: XScreenSaver.ad @if [ ! -d $(install_prefix)$(AD_DIR) ]; then \ @@ -831,7 +837,10 @@ $(SAVER_UTIL_OBJS): # How we build object files in this directory. .c.o: - $(CC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $(X_CFLAGS) $< + $(CC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $(X_CFLAGS) $< + +.m.o: + $(OBJCC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $(X_CFLAGS) $< # subprocs takes an extra -D option. subprocs.o: subprocs.c @@ -889,6 +898,9 @@ xscreensaver-demo-Gtk: $(DEMO_OBJS) $(GTK_OBJS) xscreensaver-getimage: $(GETIMG_OBJS) $(CC) $(LDFLAGS) -o $@ $(GETIMG_OBJS) $(GETIMG_LIBS) -lm +pdf2jpeg: $(PDF2JPEG_OBJS) + $(OBJCC) $(LDFLAGS) -o $@ $(PDF2JPEG_OBJS) $(PDF2JPEG_LIBS) -lm + TEST_PASSWD_OBJS = test-passwd.o $(LOCK_OBJS_1) $(PASSWD_OBJS) \ subprocs.o setuid.o splash.o prefs.o \ diff --git a/driver/XScreenSaver.ad.in b/driver/XScreenSaver.ad.in index 109f6403..fde6eeec 100644 --- a/driver/XScreenSaver.ad.in +++ b/driver/XScreenSaver.ad.in @@ -4,8 +4,8 @@ ! a screen saver and locker for the X window system ! by Jamie Zawinski ! -! version 4.13 -! 07-Sep-2003 +! version 4.14 +! 25-Oct-2003 ! ! See "man xscreensaver" for more info. The latest version is always ! available at http://www.jwz.org/xscreensaver/ @@ -146,7 +146,7 @@ "Qix (transparent)" qix -root -count 4 -solid -transparent \n\ "Qix (linear)" qix -root -count 5 -solid -transparent \ -linear -segments 250 -size 100 \n\ -- mono: "Qix (xor)" qix -root -linear -count 5 -size 200 \ +- "Qix (xor)" qix -root -linear -count 5 -size 200 \ -spread 30 -segments 75 -solid -xor \n\ \ "Attraction (balls)" attraction -root -mode balls \n\ @@ -258,6 +258,7 @@ blaster -root \n\ bumps -root \n\ xteevee -root \n\ + xanalogtv -root \n\ xspirograph -root \n\ nerverot -root \n\ - "NerveRot (dense)" nerverot -root -count 1000 \n\ @@ -291,7 +292,11 @@ barcode -root \n\ piecewise -root \n\ cloudlife -root \n\ - color: bubbles -root \n\ + "FontGlide" fontglide -root -page \n\ + "FontGlide (scroller)" fontglide -root -scroll \n\ + apple2 -root \n\ + bubbles -root \n\ + pong -root \n\ - default-n: webcollage -root \n\ - default-n: "WebCollage (whacked)" \ webcollage -root -filter \ @@ -356,8 +361,13 @@ @GL_KLUDGE@ GL: blocktube -root \n\ @GL_KLUDGE@ GL: flipflop -root \n\ @GL_KLUDGE@ GL: antspotlight -root \n\ -@GL_KLUDGE@ GL: polytopes -root \n\ - GL: glslideshow -root \n\ +@GL_KLUDGE@ GL: polytopes -root \n\ +@GL_KLUDGE@ GL: gleidescope -root \n\ +- GL: mirrorblob -root \n\ +@GL_KLUDGE@ GL: "MirrorBlob (color only)" \ + mirrorblob -root -colour -no-texture \n\ +@GL_KLUDGE@ GL: blinkbox -root \n\ \ - xdaliclock -root -builtin3 -cycle \n\ - default-n: xearth -nofork -nostars -ncolors 50 \ @@ -398,28 +408,6 @@ ! colors would be all wrong. "default-i" may also be used as a visual name ! (meaning, "-visual default -install") but you probably won't ever need ! to use that. -! -! -! Some of the GL demos that SGI ships work with XScreenSaver; most don't. -! XScreenSaver includes a program (not built or installed by default) -! called "xscreensaver-sgigl". To use the SGI demos with XScreenSaver, -! build that program, and use it to launch the SGI demos. For example, -! on Irix 6.2, you can do this: -! -! xscreensaver-sgigl /usr/demos/bin/ep -S -! xscreensaver-sgigl /usr/demos/bin/bongo -! -! On Irix 6.3, things have moved, so you need to do it like this: -! -! xscreensaver-sgigl /usr/sbin/ep -S -! -! (But note that, on non-SGIs, the bundled "stonerview" hack is a decent -! clone of "ep". Yay!) -! -! You can also use the "ant" demo, but first you need to wrap a shell script -! around it that cds to its home directory, so that it can find its files; -! and also pass it the -S argument, to prevent it from forking. What a mess! -! Basically, the SGI demo writers went out of their way to make my life hell. @@ -471,7 +459,7 @@ XScreenSaver.bourneShell: /bin/sh *passwd.thermometer.width: 8 *splash.heading.label: XScreenSaver %s -*splash.body.label: Copyright © 1991-2002 by +*splash.body.label: Copyright © 1991-2003 by *splash.body2.label: Jamie Zawinski *splash.demo.label: Settings *splash.help.label: Help @@ -624,6 +612,7 @@ XScreenSaver*doc.fontList: *-helvetica-medium-r-*-*-*-100-*-*-*-iso8859-1 *hacks.shadebobs.name: ShadeBobs *hacks.ccurve.name: C Curve *hacks.xteevee.name: XTeeVee +*hacks.xanalogtv.name: XAnalogTV *hacks.xspirograph.name: XSpiroGraph *hacks.nerverot.name: NerveRot *hacks.webcollage.name: WebCollage @@ -657,6 +646,9 @@ XScreenSaver*doc.fontList: *-helvetica-medium-r-*-*-*-100-*-*-*-iso8859-1 *hacks.blocktube.name: BlockTube *hacks.flipflop.name: FlipFlop *hacks.antspotlight.name: AntSpotlight +*hacks.fontglide.name: FontGlide +*hacks.mirrorblob.name: MirrorBlob +*hacks.blinkbox.name: BlinkBox ! obsolete, but still used by xscreensaver-demo-Xm. *hacks.documentation.isInstalled: True diff --git a/driver/XScreenSaver_ad.h b/driver/XScreenSaver_ad.h index e40784b5..b6ec5815 100644 --- a/driver/XScreenSaver_ad.h +++ b/driver/XScreenSaver_ad.h @@ -48,7 +48,7 @@ \"Qix (transparent)\" qix -root -count 4 -solid -transparent \\n\ \"Qix (linear)\" qix -root -count 5 -solid -transparent \ -linear -segments 250 -size 100 \\n\ -- mono: \"Qix (xor)\" qix -root -linear -count 5 -size 200 \ +- \"Qix (xor)\" qix -root -linear -count 5 -size 200 \ -spread 30 -segments 75 -solid -xor \\n\ \ \"Attraction (balls)\" attraction -root -mode balls \\n\ @@ -160,6 +160,7 @@ blaster -root \\n\ bumps -root \\n\ xteevee -root \\n\ + xanalogtv -root \\n\ xspirograph -root \\n\ nerverot -root \\n\ - \"NerveRot (dense)\" nerverot -root -count 1000 \\n\ @@ -193,7 +194,11 @@ barcode -root \\n\ piecewise -root \\n\ cloudlife -root \\n\ - color: bubbles -root \\n\ + \"FontGlide\" fontglide -root -page \\n\ + \"FontGlide (scroller)\" fontglide -root -scroll \\n\ + apple2 -root \\n\ + bubbles -root \\n\ + pong -root \\n\ - default-n: webcollage -root \\n\ - default-n: \"WebCollage (whacked)\" \ webcollage -root -filter \ @@ -258,8 +263,13 @@ GL: blocktube -root \\n\ GL: flipflop -root \\n\ GL: antspotlight -root \\n\ - GL: polytopes -root \\n\ - GL: glslideshow -root \\n\ + GL: polytopes -root \\n\ + GL: gleidescope -root \\n\ +- GL: mirrorblob -root \\n\ + GL: \"MirrorBlob (color only)\" \ + mirrorblob -root -colour -no-texture \\n\ + GL: blinkbox -root \\n\ \ - xdaliclock -root -builtin3 -cycle \\n\ - default-n: xearth -nofork -nostars -ncolors 50 \ @@ -310,7 +320,7 @@ "*passwd.passwdFont: *-courier-medium-r-*-*-*-140-*-*-*-iso8859-1", "*passwd.thermometer.width: 8", "*splash.heading.label: XScreenSaver %s", -"*splash.body.label: Copyright © 1991-2002 by", +"*splash.body.label: Copyright © 1991-2003 by", "*splash.body2.label: Jamie Zawinski ", "*splash.demo.label: Settings", "*splash.help.label: Help", @@ -435,6 +445,7 @@ "*hacks.shadebobs.name: ShadeBobs", "*hacks.ccurve.name: C Curve", "*hacks.xteevee.name: XTeeVee", +"*hacks.xanalogtv.name: XAnalogTV", "*hacks.xspirograph.name: XSpiroGraph", "*hacks.nerverot.name: NerveRot", "*hacks.webcollage.name: WebCollage", @@ -468,4 +479,7 @@ "*hacks.blocktube.name: BlockTube", "*hacks.flipflop.name: FlipFlop", "*hacks.antspotlight.name: AntSpotlight", +"*hacks.fontglide.name: FontGlide", +"*hacks.mirrorblob.name: MirrorBlob", +"*hacks.blinkbox.name: BlinkBox", "*hacks.documentation.isInstalled: True", diff --git a/driver/demo-Gtk-conf.c b/driver/demo-Gtk-conf.c index 608e488c..39680375 100644 --- a/driver/demo-Gtk-conf.c +++ b/driver/demo-Gtk-conf.c @@ -1433,8 +1433,8 @@ parse_command_line_into_parameters_1 (const char *filename, { GList *p; parameter *match = 0; - int which = -1; - int index = 0; + gint which = -1; + gint index = 0; for (p = parms; p; p = p->next) { @@ -1502,11 +1502,11 @@ parse_command_line_into_parameters_1 (const char *filename, break; case BOOLEAN: if (which != 0 && which != 1) abort(); - parameter_set_switch (match, (gpointer) which); + parameter_set_switch (match, GINT_TO_POINTER(which)); break; case SELECT_OPTION: if (which != 1) abort(); - parameter_set_switch (parent, (gpointer) index); + parameter_set_switch (parent, GINT_TO_POINTER(index)); break; default: break; @@ -1553,13 +1553,13 @@ parameter_set_switch (parameter *p, gpointer value) case BOOLEAN: { GtkToggleButton *b = GTK_TOGGLE_BUTTON (p->widget); - gtk_toggle_button_set_active (b, (int) value); + gtk_toggle_button_set_active (b, GPOINTER_TO_INT(value)); break; } case SELECT: { gtk_option_menu_set_history (GTK_OPTION_MENU (p->widget), - (int) value); + GPOINTER_TO_INT(value)); break; } default: diff --git a/driver/demo-Gtk.c b/driver/demo-Gtk.c index 13033fa7..9c2f0ca8 100644 --- a/driver/demo-Gtk.c +++ b/driver/demo-Gtk.c @@ -1578,7 +1578,6 @@ mode_menu_item_cb (GtkWidget *widget, gpointer user_data) GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent)); int menu_index = 0; saver_mode new_mode; - int old_selected = p->selected_hack; while (menu_items) { @@ -1592,17 +1591,12 @@ mode_menu_item_cb (GtkWidget *widget, gpointer user_data) 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, scroll - to and select "the one". + switching *to* "one screensaver" mode from any other mode, set + "the one" to be that which is currently selected. */ - list_elt = -1; + list_elt = selected_list_element (s); if (new_mode == ONE_HACK) - list_elt = (p->selected_hack >= 0 - ? s->hack_number_to_list_elt[p->selected_hack] - : -1); - - if (list_elt < 0) - list_elt = selected_list_element (s); + p->selected_hack = s->list_elt_to_hack_number[list_elt]; { saver_mode old_mode = p->mode; @@ -1613,9 +1607,6 @@ mode_menu_item_cb (GtkWidget *widget, gpointer user_data) } pref_changed_cb (widget, user_data); - - if (old_selected != p->selected_hack) - abort(); /* dammit, not again... */ } @@ -2374,7 +2365,7 @@ populate_prefs_page (state *s) # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000 THROTTLE (timeout); THROTTLE (cycle); - THROTTLE (passwd_timeout); + /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */ # undef THROTTLE # define FMT_MINUTES(NAME,N) \ @@ -4540,9 +4531,11 @@ main (int argc, char **argv) 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' */ diff --git a/driver/passwd-kerberos.c b/driver/passwd-kerberos.c index 196e3bc5..cdb22b59 100644 --- a/driver/passwd-kerberos.c +++ b/driver/passwd-kerberos.c @@ -161,12 +161,14 @@ kerberos_lock_init (int argc, char **argv, Bool verbose_p) 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. diff --git a/driver/pdf2jpeg.m b/driver/pdf2jpeg.m new file mode 100644 index 00000000..87e89034 --- /dev/null +++ b/driver/pdf2jpeg.m @@ -0,0 +1,132 @@ +/* pdf2jpeg -- converts a PDF file to a JPEG file, using Cocoa + * + * Copyright (c) 2001, 2002, 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/driver/pdf2jpeg.man b/driver/pdf2jpeg.man new file mode 100644 index 00000000..9d80dd76 --- /dev/null +++ b/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/driver/xscreensaver-command.man b/driver/xscreensaver-command.man index ff266911..d8dd718b 100644 --- a/driver/xscreensaver-command.man +++ b/driver/xscreensaver-command.man @@ -11,7 +11,7 @@ .if n .sp 1 .if t .sp .5 .. -.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11" +.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11" .SH NAME xscreensaver-command - control a running xscreensaver process .SH SYNOPSIS diff --git a/driver/xscreensaver-demo.man b/driver/xscreensaver-demo.man index 8acc3f85..22c60d50 100644 --- a/driver/xscreensaver-demo.man +++ b/driver/xscreensaver-demo.man @@ -11,7 +11,7 @@ .if n .sp 1 .if t .sp .5 .. -.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11" +.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11" .SH NAME xscreensaver-demo - interactively control the background xscreensaver daemon .SH SYNOPSIS diff --git a/driver/xscreensaver-getimage-desktop b/driver/xscreensaver-getimage-desktop new file mode 100755 index 00000000..f7d9e765 --- /dev/null +++ b/driver/xscreensaver-getimage-desktop @@ -0,0 +1,184 @@ +#!/usr/bin/perl -w +# Copyright © 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. +# +# This program attempts to grab an image of the desktop, and then load +# it on to the root window using the "xscreensaver-getimage-file" +# program. Various frame-grabbing programs are known, and the first +# one found is used. +# +# NOTE: This script is only used on MacOS X / XDarwin systems, because +# on those systems, it's necessary to use the "screencapture" +# program to get an image of the desktop -- the usual X11 +# mechanism for grabbing the screen doesn't work on OSX. +# +# The various xscreensaver hacks that manipulate images ("slidescreen", +# "jigsaw", etc.) get the image to manipulate by running the +# "xscreensaver-getimage" program. +# +# "xscreensaver-getimage" will invoke this program, depending on the +# value of the "grabDesktopImages" setting in the ~/.xscreensaver file +# (or in /usr/lib/X11/app-defaults/XScreenSaver). +# +# Created: 20-Oct-2003. + +require 5; +use diagnostics; +use strict; + +my $progname = $0; $progname =~ s@.*/@@g; +my $version = q{ $Revision: 1.1 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/; + +my @grabber = ("screencapture", "-x"); +my @converter = ("pdf2jpeg"); + +my $verbose = 0; +my $use_stdout_p = 0; +my $return_filename_p = 0; + + +sub error { + ($_) = @_; + print STDERR "$progname: $_\n"; + exit 1; +} + +# returns the full path of the named program, or undef. +# +sub which { + my ($prog) = @_; + foreach (split (/:/, $ENV{PATH})) { + if (-x "$_/$prog") { + return $prog; + } + } + return undef; +} + +sub check_path { + my $ok = 1; + foreach ($grabber[0], $converter[0]) { + if (! which ($_)) { + print STDERR "$progname: \"$_\" not found on \$PATH.\n"; + $ok = 0; + } + } + exit (1) unless $ok; +} + + +sub grab_image { + + check_path(); + + my $tmpdir = $ENV{TMPDIR}; + $tmpdir = "/tmp" unless $tmpdir; + + my $tmpfile = "$tmpdir/xssgrab.$$.pdf"; + my @cmd = (@grabber, $tmpfile); + + unlink $tmpfile; + + print STDERR "$progname: executing \"" . join(' ', @cmd) . "\"\n" + if ($verbose); + system (@cmd); + + my @st = stat($tmpfile); + my $size = (@st ? $st[7] : 0); + if ($size <= 2048) { + unlink $tmpfile; + if ($size == 0) { + error "\"" . join(' ', @cmd) . "\" produced no data."; + } else { + error "\"" . join(' ', @cmd) . "\" produced only $size bytes."; + } + } + + # Convert the PDF to a JPEG + { + my $jpgfile = $tmpfile; + $jpgfile =~ s/\.[^.]+$//; + $jpgfile .= ".jpg"; + + @cmd = (@converter, $tmpfile, $jpgfile); + push @cmd, "--verbose" if ($verbose); + + print STDERR "$progname: executing \"" . join(' ', @cmd) . "\"\n" + if ($verbose); + system (@cmd); + unlink $tmpfile; + $tmpfile = $jpgfile; + } + + @st = stat($tmpfile); + $size = (@st ? $st[7] : 0); + if ($size <= 2048) { + unlink $tmpfile; + if ($size == 0) { + error "\"" . join(' ', @cmd) . "\" produced no data."; + } else { + error "\"" . join(' ', @cmd) . "\" produced only $size bytes."; + } + } + + if ($return_filename_p) { + print STDERR "$progname: wrote \"$tmpfile\"\n" if ($verbose); + print STDOUT "$tmpfile\n"; + + } elsif ($use_stdout_p) { + local *IN; + my $ppm = ""; + my $reader = "djpeg $tmpfile"; + $reader .= " 2>/dev/null" if ($verbose <= 1); + $reader .= " |"; + + open(IN, $reader) || error "reading $tmpfile: $!"; + print STDERR "$progname: reading $tmpfile\n" if ($verbose > 1); + while () { $ppm .= $_; } + close IN; + unlink $tmpfile; + print STDOUT $ppm; + + } else { + + @cmd = ("xscreensaver-getimage-file"); + push @cmd, "--verbose" if ($verbose); + push @cmd, $tmpfile; + + print STDERR "$progname: executing \"" . join(' ', @cmd) . "\"\n" + if ($verbose); + system (@cmd); + + unlink $tmpfile; + } +} + + +sub usage { + print STDERR "usage: $progname [--verbose] [--name | --stdout]\n"; + exit 1; +} + +sub main { + while ($_ = $ARGV[0]) { + shift @ARGV; + if ($_ eq "--verbose") { $verbose++; } + elsif (m/^-v+$/) { $verbose += length($_)-1; } + elsif (m/^--?stdout$/) { $use_stdout_p = 1; } + elsif (m/^--?name$/) { $return_filename_p = 1; } + elsif (m/^-./) { usage; } + else { usage; } + } + + grab_image(); +} + +main; +exit 0; diff --git a/driver/xscreensaver-getimage-desktop.man b/driver/xscreensaver-getimage-desktop.man new file mode 100644 index 00000000..19745252 --- /dev/null +++ b/driver/xscreensaver-getimage-desktop.man @@ -0,0 +1,55 @@ +.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11" +.SH NAME +xscreensaver-getimage-desktop - put a desktop image on the root window +.SH SYNOPSIS +.B xscreensaver-getimage-desktop +[\-display \fIhost:display.screen\fP] [\--verbose] [\--stdout] +.SH DESCRIPTION +The \fIxscreensaver\-getimage\-desktop\fP program is a helper program +for the xscreensaver hacks that manipulate images. Specifically, it +is invoked by +.BR xscreensaver\-getimage (1) +as needed. This is not a user-level command. + +This program is only used on MacOS X / XDarwin systems, because +on those systems, it's necessary to use the +.BR screencapture (1) +program to get an image of the desktop -- the usual X11 +mechanism for grabbing the screen doesn't work on OSX. + +This script works by running +.BR screencapture (1) +to get a PDF, then converting it to a JPEG with +.BR pdf2jpeg (1), +then loading it onto the window with +.BR xscreensaver\-getimage\-file (1). +.SH OPTIONS +.I xscreensaver-getimage-desktop +accepts the following options: +.TP 4 +.B --verbose +Print diagnostics. +.TP 4 +.B --stdout +Instead of loading the image onto the root window, write it to stdout +as a PBM file. +.SH SEE ALSO +.BR screencapture (1), +.BR pdf2jpeg (1), +.BR X (1), +.BR xscreensaver (1), +.BR xscreensaver\-demo (1), +.BR xscreensaver\-getimage (1), +.BR xscreensaver\-getimage\-file (1), +.BR xscreensaver\-getimage\-video (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/driver/xscreensaver-getimage-file.man b/driver/xscreensaver-getimage-file.man index 62deae30..92a53d9c 100644 --- a/driver/xscreensaver-getimage-file.man +++ b/driver/xscreensaver-getimage-file.man @@ -1,4 +1,4 @@ -.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11" +.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11" .SH NAME xscreensaver-getimage-file - put a randomly-selected image on the root window .SH SYNOPSIS diff --git a/driver/xscreensaver-getimage-video b/driver/xscreensaver-getimage-video index d4e9e865..d35d0ca7 100755 --- a/driver/xscreensaver-getimage-video +++ b/driver/xscreensaver-getimage-video @@ -29,7 +29,7 @@ use diagnostics; use strict; my $progname = $0; $progname =~ s@.*/@@g; -my $version = q{ $Revision: 1.11 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/; +my $version = q{ $Revision: 1.12 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/; my $verbose = 0; @@ -55,7 +55,7 @@ my @programs = ( "bttvgrab -d q -Q -l 1 -o ppm -f $tmpfile", # BTTV "qcam > $tmpfile", # Connectix Qcam "gqcam -t PPM -d $tmpfile", # GTK+ Qcam clone - "streamer -a -s 768x576 -o $tmpfile", # XawTV + "streamer -s 768x576 -o $tmpfile", # XawTV "atitv snap $tmpfile", # ATI video capture card "grab -type ppm -format ntsc -source 1 " . diff --git a/driver/xscreensaver-getimage-video.man b/driver/xscreensaver-getimage-video.man index 84ffbe13..86c38230 100644 --- a/driver/xscreensaver-getimage-video.man +++ b/driver/xscreensaver-getimage-video.man @@ -1,4 +1,4 @@ -.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11" +.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11" .SH NAME xscreensaver-getimage-video - put a video frame on the root window .SH SYNOPSIS diff --git a/driver/xscreensaver-getimage.c b/driver/xscreensaver-getimage.c index b0ce3fa2..e46bc5c9 100644 --- a/driver/xscreensaver-getimage.c +++ b/driver/xscreensaver-getimage.c @@ -65,6 +65,13 @@ #endif +#ifdef __APPLE__ + /* On MacOSX / XDarwin, the usual X11 mechanism of getting a screen shot + doesn't work, and we need to use an external program. */ +# define USE_EXTERNAL_SCREEN_GRABBER +#endif + + #ifdef __GNUC__ __extension__ /* shut up about "string length is greater than the length ISO C89 compilers are required to support" when including @@ -85,9 +92,14 @@ XtAppContext app; extern void grabscreen_verbose (void); +typedef enum { + GRAB_DESK, GRAB_VIDEO, GRAB_FILE, GRAB_BARS +} grab_type; + -#define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video" -#define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file" +#define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video" +#define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file" +#define GETIMAGE_SCREEN_PROGRAM "xscreensaver-getimage-desktop" const char * blurb (void) @@ -269,6 +281,60 @@ compute_image_scaling (int src_w, int src_h, } +/* Scales an XImage, modifying it in place. + This doesn't do dithering or smoothing, so it might have artifacts. + If out of memory, returns False, and the XImage will have been + destroyed and freed. + */ +static Bool +scale_ximage (Screen *screen, Visual *visual, + XImage *ximage, int new_width, int new_height) +{ + Display *dpy = DisplayOfScreen (screen); + int depth = visual_depth (screen, visual); + int x, y; + double xscale, yscale; + + XImage *ximage2 = XCreateImage (dpy, visual, depth, + ZPixmap, 0, 0, + new_width, new_height, 8, 0); + ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line); + + if (!ximage2->data) + { + fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n", + progname, + ximage->width, ximage->height, + ximage2->width, ximage2->height); + if (ximage->data) free (ximage->data); + if (ximage2->data) free (ximage2->data); + ximage->data = 0; + ximage2->data = 0; + XDestroyImage (ximage); + XDestroyImage (ximage2); + return False; + } + + /* Brute force scaling... */ + xscale = (double) ximage->width / ximage2->width; + yscale = (double) ximage->height / ximage2->height; + for (y = 0; y < ximage2->height; y++) + for (x = 0; x < ximage2->width; x++) + XPutPixel (ximage2, x, y, + XGetPixel (ximage, x * xscale, y * yscale)); + + free (ximage->data); + ximage->data = 0; + + (*ximage) = (*ximage2); + + ximage2->data = 0; + XDestroyImage (ximage2); + + return True; +} + + #ifdef HAVE_GDK_PIXBUF /* Reads the given image file and renders it on the Drawable, using GDK. @@ -869,59 +935,6 @@ read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable, } -/* Scales an XImage, modifying it in place. - If out of memory, returns False, and the XImage will have been - destroyed and freed. - */ -static Bool -scale_ximage (Screen *screen, Visual *visual, - XImage *ximage, int new_width, int new_height) -{ - Display *dpy = DisplayOfScreen (screen); - int depth = visual_depth (screen, visual); - int x, y; - double xscale, yscale; - - XImage *ximage2 = XCreateImage (dpy, visual, depth, - ZPixmap, 0, 0, - new_width, new_height, 8, 0); - ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line); - - if (!ximage2->data) - { - fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n", - progname, - ximage->width, ximage->height, - ximage2->width, ximage2->height); - if (ximage->data) free (ximage->data); - if (ximage2->data) free (ximage2->data); - ximage->data = 0; - ximage2->data = 0; - XDestroyImage (ximage); - XDestroyImage (ximage2); - return False; - } - - /* Brute force scaling... */ - xscale = (double) ximage->width / ximage2->width; - yscale = (double) ximage->height / ximage2->height; - for (y = 0; y < ximage2->height; y++) - for (x = 0; x < ximage2->width; x++) - XPutPixel (ximage2, x, y, - XGetPixel (ximage, x * xscale, y * yscale)); - - free (ximage->data); - ximage->data = 0; - - (*ximage) = (*ximage2); - - ximage2->data = 0; - XDestroyImage (ximage2); - - return True; -} - - /* Reads the given image file and renders it on the Drawable, using JPEG lib. Returns False if it fails. */ @@ -1058,7 +1071,7 @@ display_file (Screen *screen, Window window, Drawable drawable, to run. */ static char * -get_filename_1 (Screen *screen, const char *directory, Bool video_p, +get_filename_1 (Screen *screen, const char *directory, grab_type type, Bool verbose_p) { Display *dpy = DisplayOfScreen (screen); @@ -1069,20 +1082,34 @@ get_filename_1 (Screen *screen, const char *directory, Bool video_p, char *av[20]; int ac = 0; - if (!video_p) + switch (type) { + case GRAB_FILE: av[ac++] = GETIMAGE_FILE_PROGRAM; if (verbose_p) av[ac++] = "--verbose"; av[ac++] = "--name"; av[ac++] = (char *) directory; - } - else - { + break; + + case GRAB_VIDEO: av[ac++] = GETIMAGE_VIDEO_PROGRAM; if (verbose_p) av[ac++] = "--verbose"; av[ac++] = "--name"; + break; + +# ifdef USE_EXTERNAL_SCREEN_GRABBER + case GRAB_DESK: + av[ac++] = GETIMAGE_SCREEN_PROGRAM; + if (verbose_p) + av[ac++] = "--verbose"; + av[ac++] = "--name"; + break; +# endif + + default: + abort(); } av[ac] = 0; @@ -1171,7 +1198,7 @@ get_filename_1 (Screen *screen, const char *directory, Bool video_p, static char * get_filename (Screen *screen, const char *directory, Bool verbose_p) { - return get_filename_1 (screen, directory, False, verbose_p); + return get_filename_1 (screen, directory, GRAB_FILE, verbose_p); } @@ -1181,9 +1208,20 @@ get_filename (Screen *screen, const char *directory, Bool verbose_p) static char * get_video_filename (Screen *screen, Bool verbose_p) { - return get_filename_1 (screen, 0, True, verbose_p); + return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p); } +/* Grabs a desktop image to a file, and returns a pathname to that file. + Delete that file when you are done with it (and free the string.) + */ +# ifdef USE_EXTERNAL_SCREEN_GRABBER +static char * +get_desktop_filename (Screen *screen, Bool verbose_p) +{ + return get_filename_1 (screen, 0, GRAB_DESK, verbose_p); +} +#endif /* USE_EXTERNAL_SCREEN_GRABBER */ + /* Grabs a video frame, and renders it on the Drawable. Returns False if it fails; @@ -1218,6 +1256,118 @@ display_video (Screen *screen, Window window, Drawable drawable, } +/* Grabs a desktop screen shot onto the window and the drawable. + If the window and drawable are not the same size, the image in + the drawable is scaled to fit. + Returns False if it fails. + */ +static Bool +display_desktop (Screen *screen, Window window, Drawable drawable, + Bool verbose_p) +{ +# ifdef USE_EXTERNAL_SCREEN_GRABBER + + Display *dpy = DisplayOfScreen (screen); + Bool top_p = top_level_window_p (screen, window); + char *filename; + Bool status; + + if (top_p) + { + if (verbose_p) + fprintf (stderr, "%s: unmapping 0x%lx.\n", progname, + (unsigned long) window); + XUnmapWindow (dpy, window); + XSync (dpy, False); + } + + filename = get_desktop_filename (screen, verbose_p); + + if (top_p) + { + if (verbose_p) + fprintf (stderr, "%s: mapping 0x%lx.\n", progname, + (unsigned long) window); + XMapRaised (dpy, window); + XSync (dpy, False); + } + + if (!filename) + { + if (verbose_p) + fprintf (stderr, "%s: desktop grab failed.\n", progname); + return False; + } + + status = display_file (screen, window, drawable, filename, verbose_p); + + if (unlink (filename)) + { + char buf[512]; + sprintf (buf, "%s: rm %.100s", progname, filename); + perror (buf); + } + else if (verbose_p) + fprintf (stderr, "%s: rm %s\n", progname, filename); + + if (filename) free (filename); + return status; + +# else /* !USE_EXTERNAL_SCREEN_GRABBER */ + + Display *dpy = DisplayOfScreen (screen); + XGCValues gcv; + XWindowAttributes xgwa; + Window root; + int px, py; + unsigned int pw, ph, pbw, pd; + int srcx, srcy, destx, desty, w2, h2; + + if (verbose_p) + { + fprintf (stderr, "%s: grabbing desktop image\n", progname); + grabscreen_verbose(); + } + + XGetWindowAttributes (dpy, window, &xgwa); + XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd); + + grab_screen_image_internal (screen, window); + + compute_image_scaling (xgwa.width, xgwa.height, + pw, ph, verbose_p, + &srcx, &srcy, &destx, &desty, &w2, &h2); + + if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */ + { + GC gc = XCreateGC (dpy, drawable, 0, &gcv); + XCopyArea (dpy, window, drawable, gc, + 0, 0, xgwa.width, xgwa.height, 0, 0); + XFreeGC (dpy, gc); + } + else /* size mismatch -- must scale client-side images to fit drawable */ + { + XImage *ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height, + ~0L, ZPixmap); + GC gc; + if (!ximage || + !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2)) + return False; + gc = XCreateGC (dpy, drawable, 0, &gcv); + clear_drawable (screen, drawable); + XPutImage (dpy, drawable, gc, ximage, + srcx, srcy, destx, desty, ximage->width, ximage->height); + XDestroyImage (ximage); + XFreeGC (dpy, gc); + } + + XSync (dpy, False); + return True; + +# endif /* !USE_EXTERNAL_SCREEN_GRABBER */ +} + + /* Grabs an image (from a file, video, or the desktop) and renders it on the Drawable. If `file' is specified, always use that file. Otherwise, select randomly, based on the other arguments. @@ -1233,9 +1383,10 @@ get_image (Screen *screen, const char *file) { Display *dpy = DisplayOfScreen (screen); - enum { do_desk, do_video, do_image, do_bars } which = do_bars; + grab_type which = GRAB_BARS; int count = 0; struct stat st; + const char *file_prop = 0; if (! drawable_window_p (dpy, window)) { @@ -1271,6 +1422,10 @@ get_image (Screen *screen, # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB)) image_p = False; /* can't load images from files... */ +# ifdef USE_EXTERNAL_SCREEN_GRABBER + desk_p = False; /* ...or from desktops grabbed to files. */ +# endif + if (file) { fprintf (stderr, @@ -1302,12 +1457,15 @@ get_image (Screen *screen, # error Error! This file definitely needs vroot.h! # endif - /* We can grab desktop images if: + /* We can grab desktop images (using the normal X11 method) if: - the window is the real root window; - the window is a toplevel window. - We cannot grab desktop images if: + We cannot grab desktop images that way if: - the window is a non-top-level window. + + Using the MacOS X way, desktops are just like loaded image files. */ +# ifndef USE_EXTERNAL_SCREEN_GRABBER if (desk_p) { if (!top_level_window_p (screen, window)) @@ -1319,6 +1477,7 @@ get_image (Screen *screen, progname, (unsigned int) window); } } +# endif /* !USE_EXTERNAL_SCREEN_GRABBER */ count = 0; if (desk_p) count++; @@ -1326,16 +1485,16 @@ get_image (Screen *screen, if (image_p) count++; if (count == 0) - which = do_bars; + which = GRAB_BARS; else { int i = 0; while (1) /* loop until we get one that's permitted */ { which = (random() % 3); - if (which == do_desk && desk_p) break; - if (which == do_video && video_p) break; - if (which == do_image && image_p) break; + if (which == GRAB_DESK && desk_p) break; + if (which == GRAB_VIDEO && video_p) break; + if (which == GRAB_FILE && image_p) break; if (++i > 200) abort(); } } @@ -1343,12 +1502,12 @@ get_image (Screen *screen, /* If we're to search a directory to find an image file, do so now. */ - if (which == do_image && !file) + if (which == GRAB_FILE && !file) { file = get_filename (screen, dir, verbose_p); if (!file) { - which = do_bars; + which = GRAB_BARS; if (verbose_p) fprintf (stderr, "%s: no image files found.\n", progname); } @@ -1356,48 +1515,54 @@ get_image (Screen *screen, /* Now actually render something. */ - if (which == do_bars) - { - XWindowAttributes xgwa; - COLORBARS: - if (verbose_p) - fprintf (stderr, "%s: drawing colorbars.\n", progname); - XGetWindowAttributes (dpy, window, &xgwa); - draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap, - 0, 0, 0, 0); - XSync (dpy, False); - } - else if (which == do_desk) + switch (which) { - GC gc; - XGCValues gcv; - XWindowAttributes xgwa; + case GRAB_BARS: + { + XWindowAttributes xgwa; + COLORBARS: + if (verbose_p) + fprintf (stderr, "%s: drawing colorbars.\n", progname); + XGetWindowAttributes (dpy, window, &xgwa); + draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap, + 0, 0, 0, 0); + XSync (dpy, False); + } + break; - if (verbose_p) - { - fprintf (stderr, "%s: grabbing desktop image\n", progname); - grabscreen_verbose(); - } - gc = XCreateGC (dpy, drawable, 0, &gcv); - XGetWindowAttributes (dpy, window, &xgwa); - grab_screen_image (screen, window); - XCopyArea (dpy, window, drawable, gc, - 0, 0, xgwa.width, xgwa.height, 0, 0); - XFreeGC (dpy, gc); - XSync (dpy, False); - } - else if (which == do_image) - { + case GRAB_DESK: + if (! display_desktop (screen, window, drawable, verbose_p)) + goto COLORBARS; + file_prop = "desktop"; + break; + + case GRAB_FILE: if (! display_file (screen, window, drawable, file, verbose_p)) goto COLORBARS; - } - else if (which == do_video) - { + file_prop = file; + break; + + case GRAB_VIDEO: if (! display_video (screen, window, drawable, verbose_p)) goto COLORBARS; + file_prop = "video"; + break; + + default: + abort(); + break; } - else - abort(); + + { + Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False); + if (file_prop && *file_prop) + XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace, + (unsigned char *) file_prop, strlen(file_prop)); + else + XDeleteProperty (dpy, window, a); + } + + XSync (dpy, False); } diff --git a/driver/xscreensaver-getimage.man b/driver/xscreensaver-getimage.man index 4b773b7b..f83aed89 100644 --- a/driver/xscreensaver-getimage.man +++ b/driver/xscreensaver-getimage.man @@ -1,18 +1,23 @@ -.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11" +.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11" .SH NAME xscreensaver-getimage - put some randomly-selected image on the root window .SH SYNOPSIS .B xscreensaver-getimage -[\-display \fIhost:display.screen\fP] [\--verbose] window-id +[\-display \fIhost:display.screen\fP] [\--verbose] window-id [pixmap-id] .SH DESCRIPTION The \fIxscreensaver\-getimage\fP program is a helper program for the xscreensaver hacks that manipulate images. This is not a user-level command. This program selects a random image, and puts it on the specified -window. This image might be a snapshot of the desktop; or a frame -captured from the system's video input; or a randomly-selected image -from disk. +window or pixmap. This image might be a snapshot of the desktop; or +a frame captured from the system's video input; or a randomly-selected +image from disk. + +If only a window ID is specified, the image will be painted there. +If both a window ID and a pixmap ID are specified, then the image will +be painted on the pixmap; and the window \fImay\fP be modified as a +side-effect. .SH OPTIONS .I xscreensaver-getimage reads the \fI~/.xscreensaver\fP file for configuration information. @@ -43,12 +48,9 @@ assumed to be images. If none of the three options are set to True, then video colorbars will be displayed instead. .SH BUGS -If the target window is not the root window, then this program -will \fIalways\fP grab a desktop image. This means that if an -image-manipulating xscreensaver mode is run in a normal window, -it will always operate on a desktop image; it's only when it is -run in \fI-root\fP mode (or when invoked by xscreensaver) that -it will load image files or grab video. +When grabbing desktop images, the \fIwindow\fP argument will be unmapped +and have its contents modified, causing flicker. (This does not happen +when loading image files or video frames.) .SH SEE ALSO .BR X (1), .BR xscreensaver (1) diff --git a/driver/xscreensaver.c b/driver/xscreensaver.c index de42a100..09665b66 100644 --- a/driver/xscreensaver.c +++ b/driver/xscreensaver.c @@ -1667,6 +1667,14 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) { if (until_idle_p) { + if (p->mode == DONT_BLANK) + { + clientmessage_response(si, window, True, + "ACTIVATE ClientMessage received in DONT_BLANK mode.", + "screen blanking is currently disabled."); + return False; + } + clientmessage_response(si, window, False, "ACTIVATE ClientMessage received.", "activating."); @@ -1771,6 +1779,14 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) char buf2 [255]; long which = event->xclient.data.l[1]; + if (p->mode == DONT_BLANK) + { + clientmessage_response(si, window, True, + "SELECT ClientMessage received in DONT_BLANK mode.", + "screen blanking is currently disabled."); + return False; + } + sprintf (buf, "SELECT %ld ClientMessage received.", which); sprintf (buf2, "activating (%ld).", which); clientmessage_response (si, window, False, buf, buf2); @@ -1890,7 +1906,11 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) "not compiled with support for locking.", "locking not enabled."); #else /* !NO_LOCKING */ - if (si->locking_disabled_p) + if (p->mode == DONT_BLANK) + clientmessage_response(si, window, True, + "LOCK ClientMessage received in DONT_BLANK mode.", + "screen blanking is currently disabled."); + else if (si->locking_disabled_p) clientmessage_response (si, window, True, "LOCK ClientMessage received, but locking is disabled.", "locking not enabled."); diff --git a/driver/xscreensaver.man b/driver/xscreensaver.man index 9fb8f6b9..7d627791 100644 --- a/driver/xscreensaver.man +++ b/driver/xscreensaver.man @@ -11,7 +11,7 @@ .if n .sp 1 .if t .sp .5 .. -.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11" +.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11" .SH NAME xscreensaver - extensible screen saver framework, plus locking .SH SYNOPSIS diff --git a/hacks/Makefile.in b/hacks/Makefile.in index 5ae3dad6..c818b1fc 100644 --- a/hacks/Makefile.in +++ b/hacks/Makefile.in @@ -73,7 +73,8 @@ UTIL_OBJS = $(UTILS_BIN)/alpha.o $(UTILS_BIN)/colors.o \ $(UTILS_BIN)/spline.o $(UTILS_BIN)/usleep.o \ $(UTILS_BIN)/visual.o $(UTILS_BIN)/logo.o \ $(UTILS_BIN)/yarandom.o $(UTILS_BIN)/erase.o \ - $(UTILS_SRC)/xshm.o $(UTILS_SRC)/xdbe.o + $(UTILS_BIN)/xshm.o $(UTILS_BIN)/xdbe.o \ + $(UTILS_BIN)/colorbars.o SRCS = attraction.c blitspin.c bouboule.c braid.c bubbles.c \ bubbles-default.c decayscreen.c deco.c drift.c flag.c \ @@ -97,7 +98,8 @@ SRCS = attraction.c blitspin.c bouboule.c braid.c bubbles.c \ xpm-pixmap.c webcollage-helper.c twang.c apollonian.c \ euler2d.c juggle.c polyominoes.c thornbird.c fluidballs.c \ anemone.c halftone.c metaballs.c eruption.c popsquares.c \ - barcode.c piecewise.c cloudlife.c + barcode.c piecewise.c cloudlife.c fontglide.c apple2.c \ + apple2-main.c analogtv.c xanalogtv.c pong.c SCRIPTS = vidwhacker webcollage ljlatest OBJS = attraction.o blitspin.o bouboule.o braid.o bubbles.o \ @@ -122,7 +124,8 @@ OBJS = attraction.o blitspin.o bouboule.o braid.o bubbles.o \ xpm-pixmap.o webcollage-helper.o twang.o apollonian.o \ euler2d.o juggle.o polyominoes.o thornbird.o fluidballs.o \ anemone.o halftone.o metaballs.o eruption.o popsquares.o \ - barcode.o piecewise.o cloudlife.o + barcode.o piecewise.o cloudlife.o fontglide.o apple2.o \ + apple2-main.o analogtv.o xanalogtv.o pong.o NEXES = attraction blitspin bouboule braid bubbles decayscreen deco \ drift flag flame forest vines galaxy grav greynetic halo \ @@ -140,6 +143,7 @@ NEXES = attraction blitspin bouboule braid bubbles decayscreen deco \ whirlygig speedmine vermiculate twang apollonian euler2d \ juggle polyominoes thornbird fluidballs anemone halftone \ metaballs eruption popsquares barcode piecewise cloudlife \ + fontglide apple2 xanalogtv pong \ @JPEG_EXES@ SEXES = sonar JPEG_EXES = webcollage-helper @@ -155,7 +159,7 @@ XSHM_OBJS = $(UTILS_BIN)/xshm.o XDBE_OBJS = $(UTILS_BIN)/xdbe.o HDRS = bubbles.h screenhack.h xlockmore.h xlockmoreI.h automata.h \ - bumps.h xpm-pixmap.h + bumps.h xpm-pixmap.h apple2.h analogtv.h MEN = anemone.man ant.man apollonian.man attraction.man \ blaster.man blitspin.man bouboule.man braid.man bsod.man \ bubbles.man bumps.man ccurve.man compass.man coral.man \ @@ -181,7 +185,8 @@ MEN = anemone.man ant.man apollonian.man attraction.man \ worm.man xflame.man xjack.man xlyap.man xmatrix.man \ xrayswarm.man xspirograph.man xsublim.man xteevee.man \ zoom.man halftone.man eruption.man metaballs.man \ - barcode.man piecewise.man cloudlife.man ljlatest.man + barcode.man piecewise.man cloudlife.man ljlatest.man \ + fontglide.man apple2.man xanalogtv.man pong.man STAR = * EXTRAS = README Makefile.in xlock_23.h xml2man.pl .gdbinit \ config/README \ @@ -505,6 +510,8 @@ COL = $(COLOR_OBJS) SHM = $(XSHM_OBJS) DBE = $(XDBE_OBJS) BARS = $(UTILS_BIN)/colorbars.o $(LOGO) +ATV = analogtv.o $(SHM) +APPLE2 = apple2.o $(ATV) CC_HACK = $(CC) $(LDFLAGS) @@ -624,8 +631,14 @@ interference: interference.o $(HACK_OBJS) $(COL) $(SHM) $(DBE) truchet: truchet.o $(HACK_OBJS) $(COL) $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(COL) $(HACK_LIBS) -bsod: bsod.o $(HACK_OBJS) $(GRAB) $(SHM) $(XPM) - $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(GRAB) $(SHM) $(XPM) $(XPM_LIBS) +bsod: bsod.o $(HACK_OBJS) $(GRAB) $(APPLE2) $(XPM) + $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(GRAB) $(APPLE2) $(XPM) $(XPM_LIBS) + +apple2: apple2.o apple2-main.o $(HACK_OBJS) $(ATV) $(GRAB) + $(CC_HACK) -o $@ $@.o apple2-main.o $(HACK_OBJS) $(ATV) $(GRAB) $(XPM_LIBS) + +xanalogtv: xanalogtv.o $(HACK_OBJS) $(ATV) $(GRAB) $(XPM) + $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(ATV) $(GRAB) $(XPM) $(XPM_LIBS) $(HACK_LIBS) distort: distort.o $(HACK_OBJS) $(GRAB) $(SHM) $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(GRAB) $(SHM) $(HACK_LIBS) @@ -750,6 +763,13 @@ piecewise: piecewise.o $(HACK_OBJS) $(COL) $(DBE) cloudlife: cloudlife.o $(HACK_OBJS) $(COL) $(DBE) $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(COL) $(DBE) $(HACK_LIBS) +fontglide: fontglide.o $(HACK_OBJS) $(DBE) + $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(DBE) $(HACK_LIBS) + +pong: pong.o $(HACK_OBJS) $(ATV) $(GRAB) $(XPM) + $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(ATV) $(GRAB) $(XPM) $(XPM_LIBS) $(HACK_LIBS) + + # The rules for those hacks which follow the `xlockmore' API. # @@ -881,6 +901,8 @@ webcollage-helper: webcollage-helper.o # # DO NOT DELETE: updated by make distdepend +analogtv.o: $(srcdir)/analogtv.h +analogtv.o: ../config.h anemone.o: ../config.h anemone.o: $(srcdir)/screenhack.h ant.o: $(srcdir)/automata.h @@ -892,6 +914,14 @@ apollonian.o: ../config.h apollonian.o: $(srcdir)/screenhack.h apollonian.o: $(srcdir)/xlockmore.h apollonian.o: $(srcdir)/xlockmoreI.h +apple2-main.o: $(srcdir)/analogtv.h +apple2-main.o: $(srcdir)/apple2.h +apple2-main.o: ../config.h +apple2-main.o: $(srcdir)/screenhack.h +apple2.o: $(srcdir)/analogtv.h +apple2.o: $(srcdir)/apple2.h +apple2.o: ../config.h +apple2.o: $(srcdir)/screenhack.h attraction.o: ../config.h attraction.o: $(srcdir)/screenhack.h barcode.o: ../config.h @@ -910,6 +940,8 @@ braid.o: ../config.h braid.o: $(srcdir)/screenhack.h braid.o: $(srcdir)/xlockmore.h braid.o: $(srcdir)/xlockmoreI.h +bsod.o: $(srcdir)/analogtv.h +bsod.o: $(srcdir)/apple2.h bsod.o: ../config.h bsod.o: $(srcdir)/images/amiga.xpm bsod.o: $(srcdir)/images/atari.xbm @@ -1034,6 +1066,8 @@ flow.o: $(srcdir)/xlockmore.h flow.o: $(srcdir)/xlockmoreI.h fluidballs.o: ../config.h fluidballs.o: $(srcdir)/screenhack.h +fontglide.o: ../config.h +fontglide.o: $(srcdir)/screenhack.h forest.o: ../config.h forest.o: $(srcdir)/screenhack.h forest.o: $(srcdir)/xlockmore.h @@ -1190,6 +1224,9 @@ polyominoes.o: ../config.h polyominoes.o: $(srcdir)/screenhack.h polyominoes.o: $(srcdir)/xlockmore.h polyominoes.o: $(srcdir)/xlockmoreI.h +pong.o: $(srcdir)/analogtv.h +pong.o: ../config.h +pong.o: $(srcdir)/screenhack.h popsquares.o: ../config.h popsquares.o: $(srcdir)/screenhack.h pyro.o: ../config.h @@ -1281,6 +1318,10 @@ worm.o: ../config.h worm.o: $(srcdir)/screenhack.h worm.o: $(srcdir)/xlockmore.h worm.o: $(srcdir)/xlockmoreI.h +xanalogtv.o: $(srcdir)/analogtv.h +xanalogtv.o: ../config.h +xanalogtv.o: $(srcdir)/screenhack.h +xanalogtv.o: $(srcdir)/xpm-pixmap.h xflame.o: ../config.h xflame.o: $(srcdir)/images/bob.xbm xflame.o: $(srcdir)/screenhack.h diff --git a/hacks/analogtv.c b/hacks/analogtv.c new file mode 100644 index 00000000..86761b7d --- /dev/null +++ b/hacks/analogtv.c @@ -0,0 +1,2138 @@ +/* analogtv, Copyright (c) 2003 Trevor Blackwell + * + * 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 the code for implementing something that looks like a conventional + analog TV set. It simulates the following characteristics of standard + televisions: + + - Realistic rendering of a composite video signal + - Compression & brightening on the right, as the scan gets truncated + because of saturation in the flyback transformer + - Blooming of the picture dependent on brightness + - Overscan, cutting off a few pixels on the left side. + - Colored text in mixed graphics/text modes + + It's amazing how much it makes your high-end monitor look like at large + late-70s TV. All you need is to put a big "Solid State" logo in curly script + on it and you'd be set. + + In DirectColor or TrueColor modes, it generates pixel values + directly from RGB values it calculates across each scan line. In + PseudoColor mode, it consider each possible pattern of 5 preceding + bit values in each possible position modulo 4 and allocates a color + for each. A few things, like the brightening on the right side as + the horizontal trace slows down, aren't done in PseudoColor. + + I originally wrote it for the Apple ][ emulator, and generalized it + here for use with a rewrite of xteevee and possibly others. + + A maxim of technology is that failures reveal underlying mechanism. + A good way to learn how something works is to push it to failure. + The way it fails will usually tell you a lot about how it works. The + corollary for this piece of software is that in order to emulate + realistic failures of a TV set, it has to work just like a TV set. + So there is lots of DSP-style emulation of analog circuitry for + things like color decoding, H and V sync following, and more. In + 2003, computers are just fast enough to do this at television signal + rates. We use a 14 MHz sample rate here, so we can do on the order + of a couple hundred instructions per sample and keep a good frame + rate. + + Trevor Blackwell +*/ + +#include +#include +#include +#include "utils.h" +#include "resources.h" +#include "analogtv.h" +#include "yarandom.h" +#include "grabscreen.h" + +/* #define DEBUG 1 */ + +#ifdef DEBUG +/* only works on linux + freebsd */ +#include + +#define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes +#define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0) +#define DTIME dtimes[n_dtimes++]=rdtsc() +#define DTIME_SHOW(DIV) \ +do { \ + double _dtime_div=(DIV); \ + printf("time/%.1f: ",_dtime_div); \ + for (i=1; ipowerup-start; + double ret; + if (pt<0.0) return 0.0; + if (pt>900.0 || pt/tc>8.0) return 1.0; + + ret=(1.0-exp(-pt/tc))*over; + if (ret>1.0) return 1.0; + return ret*ret; +} + +/* + There are actual standards for TV signals: NTSC and RS-170A describe the + system used in the US and Japan. Europe has slightly different systems, but + not different enough to make substantially different screensaver displays. + Sadly, the standards bodies don't do anything so useful as publish the spec on + the web. Best bets are: + + http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm + http://www.ntsc-tv.com/ntsc-index-02.htm + + In DirectColor or TrueColor modes, it generates pixel values directly from RGB + values it calculates across each scan line. In PseudoColor mode, it consider + each possible pattern of 5 preceding bit values in each possible position + modulo 4 and allocates a color for each. A few things, like the brightening on + the right side as the horizontal trace slows down, aren't done in PseudoColor. + + I'd like to add a bit of visible retrace, but it conflicts with being able to + bitcopy the image when fast scrolling. After another couple of CPU + generations, we could probably regenerate the whole image from scratch every + time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling + looks too slow. +*/ + +/* localbyteorder is MSBFirst or LSBFirst */ +static int localbyteorder; +static const double float_low8_ofs=8388608.0; +static int float_extraction_works; + +typedef union { + float f; + int i; +} float_extract_t; + +static void +analogtv_init(void) +{ + int i; + { + unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0); + localbyteorder=*(char *)&localbyteorder_loc; + } + + if (1) { + float_extract_t fe; + int ans; + + float_extraction_works=1; + for (i=0; i<256*4; i++) { + fe.f=float_low8_ofs+(double)i; + ans=fe.i&0x3ff; + if (ans != i) { +#ifdef DEBUG + printf("Float extraction failed for %d => %d\n",i,ans); +#endif + float_extraction_works=0; + break; + } + } + } + +} + +void +analogtv_set_defaults(analogtv *it, char *prefix) +{ + char buf[256]; + + sprintf(buf,"%sTVTint",prefix); + it->tint_control = get_float_resource(buf,"TVTint"); + sprintf(buf,"%sTVColor",prefix); + it->color_control = get_float_resource(buf,"TVColor")/100.0; + sprintf(buf,"%sTVBrightness",prefix); + it->brightness_control = get_float_resource(buf,"TVBrightness") / 100.0; + sprintf(buf,"%sTVContrast",prefix); + it->contrast_control = get_float_resource(buf,"TVContrast") / 100.0; + it->height_control = 1.0; + it->width_control = 1.0; + it->squish_control = 0.0; + it->powerup=1000.0; + + it->hashnoise_rpm=0; + it->hashnoise_on=0; + it->hashnoise_enable=1; + + it->horiz_desync=frand(10.0)-5.0; + it->squeezebottom=frand(5.0)-1.0; + +#ifdef DEBUG + printf("analogtv: prefix=%s\n",prefix); + printf(" use: shm=%d cmap=%d color=%d\n", + it->use_shm,it->use_cmap,it->use_color); + printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n", + it->tint_control, it->color_control, it->brightness_control, + it->contrast_control); + printf(" freq_error %g: %g %d\n", + it->freq_error, it->freq_error_inc, it->flutter_tint); + printf(" desync: %g %d\n", + it->horiz_desync, it->flutter_horiz_desync); + printf(" hashnoise rpm: %g\n", + it->hashnoise_rpm); + printf(" vis: %d %d %d\n", + it->visclass, it->visbits, it->visdepth); + printf(" shift: %d-%d %d-%d %d-%d\n", + it->red_invprec,it->red_shift, + it->green_invprec,it->green_shift, + it->blue_invprec,it->blue_shift); + printf(" size: %d %d %d %d xrepl=%d\n", + it->usewidth, it->useheight, + it->screen_xo, it->screen_yo, it->xrepl); + + printf(" ANALOGTV_V=%d\n",ANALOGTV_V); + printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP); + printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES); + printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT); + printf(" ANALOGTV_H=%d\n",ANALOGTV_H); + printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START); + printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START); + printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START); + printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START); + printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN); + printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START); + printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END); + printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN); + +#endif + +} + +extern Bool mono_p; /* shoot me */ + +void +analogtv_free_image(analogtv *it) +{ + if (it->image) { + if (it->use_shm) { +#ifdef HAVE_XSHM_EXTENSION + destroy_xshm_image(it->dpy, it->image, &it->shm_info); +#endif + } else { + XDestroyImage(it->image); + } + it->image=NULL; + } +} + +void +analogtv_alloc_image(analogtv *it) +{ + if (it->use_shm) { +#ifdef HAVE_XSHM_EXTENSION + it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, + &it->shm_info, it->usewidth, it->useheight); +#endif + if (!it->image) it->use_shm=0; + } + if (!it->image) { + it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0, + it->usewidth, it->useheight, 8, 0); + it->image->data = (char *)calloc(it->image->height, it->image->bytes_per_line); + } +} + + +void +analogtv_configure(analogtv *it) +{ + int oldwidth=it->usewidth; + int oldheight=it->useheight; + int wlim,hlim,ohlim; + + hlim=it->xgwa.height; + if (hlimxgwa.width; + if (wlim<300) wlim = 300; + + /* require 3:4 aspect ratio */ + if (wlim > hlim*4/3) wlim=hlim*4/3; + if (hlim > wlim*3/4) hlim=wlim*3/4; + + /* height must be a multiple of VISLINES */ + ohlim=hlim; + hlim = (hlim/ANALOGTV_VISLINES)*ANALOGTV_VISLINES; + + /* Scale width proportionally */ + wlim=wlim*hlim/ohlim; + + { + FILE *fp=fopen("/tmp/analogtv.size","w"); + fprintf(fp,"wlim=%d hlim=%d\n", wlim, hlim); + fclose(fp); + } + + /* Most times this doesn't change */ + if (wlim != oldwidth || hlim != oldheight) { + + it->usewidth=wlim; + it->useheight=hlim; + + it->xrepl=1+it->usewidth/640; + if (it->xrepl>2) it->xrepl=2; + it->subwidth=it->usewidth/it->xrepl; + + analogtv_free_image(it); + analogtv_alloc_image(it); + } + + it->screen_xo = (it->xgwa.width-it->usewidth)/2; + it->screen_yo = (it->xgwa.height-it->useheight)/2; + it->need_clear=1; +} + +void +analogtv_reconfigure(analogtv *it) +{ + XGetWindowAttributes (it->dpy, it->window, &it->xgwa); + analogtv_configure(it); +} + +analogtv * +analogtv_allocate(Display *dpy, Window window) +{ + XGCValues gcv; + analogtv *it=NULL; + int i; + + analogtv_init(); + + it=(analogtv *)calloc(1,sizeof(analogtv)); + it->dpy=dpy; + it->window=window; + + it->shrinkpulse=-1; + + it->n_colors=0; + +#ifdef HAVE_XSHM_EXTENSION + it->use_shm=1; +#else + it->use_shm=0; +#endif + + XGetWindowAttributes (it->dpy, it->window, &it->xgwa); + + it->screen=it->xgwa.screen; + it->colormap=it->xgwa.colormap; + it->visclass=it->xgwa.visual->class; + it->visbits=it->xgwa.visual->bits_per_rgb; + it->visdepth=it->xgwa.depth; + if (it->visclass == TrueColor || it->visclass == DirectColor) { + if (get_integer_resource ("use_cmap", "Integer")) { + it->use_cmap=1; + } else { + it->use_cmap=0; + } + it->use_color=!mono_p; + } + else if (it->visclass == PseudoColor || it->visclass == StaticColor) { + it->use_cmap=1; + it->use_color=!mono_p; + } + else { + it->use_cmap=1; + it->use_color=0; + } + + it->red_mask=it->xgwa.visual->red_mask; + it->green_mask=it->xgwa.visual->green_mask; + it->blue_mask=it->xgwa.visual->blue_mask; + it->red_shift=it->red_invprec=-1; + it->green_shift=it->green_invprec=-1; + it->blue_shift=it->blue_invprec=-1; + if (!it->use_cmap) { + /* Is there a standard way to do this? Does this handle all cases? */ + int shift, prec; + for (shift=0; shift<32; shift++) { + for (prec=1; prec<16 && prec<32-shift; prec++) { + unsigned long mask=(0xffffUL>>(16-prec)) << shift; + if (it->red_shift<0 && mask==it->red_mask) + it->red_shift=shift, it->red_invprec=16-prec; + if (it->green_shift<0 && mask==it->green_mask) + it->green_shift=shift, it->green_invprec=16-prec; + if (it->blue_shift<0 && mask==it->blue_mask) + it->blue_shift=shift, it->blue_invprec=16-prec; + } + } + if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) { + if (0) fprintf(stderr,"Can't figure out color space\n"); + goto fail; + } + + for (i=0; i65535) intensity=65535; + it->red_values[i]=((intensity>>it->red_invprec)<red_shift); + it->green_values[i]=((intensity>>it->green_invprec)<green_shift); + it->blue_values[i]=((intensity>>it->blue_invprec)<blue_shift); + } + + } + + gcv.background=get_pixel_resource("background", "Background", + it->dpy, it->colormap); + + it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv); + XSetWindowBackground(it->dpy, it->window, gcv.background); + XClearWindow(dpy,window); + + analogtv_configure(it); + + return it; + + fail: + if (it) free(it); + return NULL; +} + +void +analogtv_release(analogtv *it) +{ + if (it->image) { + if (it->use_shm) { +#ifdef HAVE_XSHM_EXTENSION + destroy_xshm_image(it->dpy, it->image, &it->shm_info); +#endif + } else { + XDestroyImage(it->image); + } + it->image=NULL; + } + if (it->gc) XFreeGC(it->dpy, it->gc); + it->gc=NULL; + if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L); + it->n_colors=0; +} + + +/* + First generate the I and Q reference signals, which we'll multiply + the input signal by to accomplish the demodulation. Normally they + are shifted 33 degrees from the colorburst. I think this was convenient + for inductor-capacitor-vacuum tube implementation. + + The tint control, FWIW, just adds a phase shift to the chroma signal, + and the color control controls the amplitude. + + In text modes (colormode==0) the system disabled the color burst, and no + color was detected by the monitor. + + freq_error gives a mismatch between the built-in oscillator and the + TV's colorbust. Some II Plus machines seemed to occasionally get + instability problems -- the crystal oscillator was a single + transistor if I remember correctly -- and the frequency would vary + enough that the tint would change across the width of the screen. + The left side would be in correct tint because it had just gotten + resynchronized with the color burst. + + If we're using a colormap, set it up. +*/ +int +analogtv_set_demod(analogtv *it) +{ + int y_levels=10,i_levels=5,q_levels=5; + + /* + In principle, we might be able to figure out how to adjust the + color map frame-by-frame to get some nice color bummage. But I'm + terrified of changing the color map because we'll get flashing. + + I can hardly believe we still have to deal with colormaps. They're + like having NEAR PTRs: an enormous hassle for the programmer just + to save on memory. They should have been deprecated by 1995 or + so. */ + + cmap_again: + if (it->use_cmap && !it->n_colors) { + + if (it->n_colors) { + XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L); + it->n_colors=0; + } + + { + int yli,qli,ili; + for (yli=0; yli65535) r=65535; + if (g<0) g=0; + if (g>65535) g=65535; + if (b<0) b=0; + if (b>65535) b=65535; + +#ifdef DEBUG + printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n", + interpy, interpi, interpq, + r/256,g/256,b/256); +#endif + + col.red=r; + col.green=g; + col.blue=b; + col.pixel=0; + if (!XAllocColor(it->dpy, it->colormap, &col)) { + if (q_levels > y_levels*4/12) + q_levels--; + else if (i_levels > y_levels*5/12) + i_levels--; + else + y_levels--; + + if (y_levels<2) + return -1; + goto cmap_again; + } + it->colors[it->n_colors++]=col.pixel; + } + } + } + + it->cmap_y_levels=y_levels; + it->cmap_i_levels=i_levels; + it->cmap_q_levels=q_levels; + } + } + + return 0; +} + +#if 0 +unsigned int +analogtv_line_signature(analogtv_input *input, int lineno) +{ + int i; + char *origsignal=&input->signal[(lineno+input->vsync) + %ANALOGTV_V][input->line_hsync[lineno]]; + unsigned int hash=0; + + /* probably lame */ + for (i=0; iline_hsync[lineno]; + hash ^= hash >> 2; + /* + hash += input->hashnoise_times[lineno]; + hash ^= hash >> 2; + */ + + return hash; +} +#endif + + +/* Here we model the analog circuitry of an NTSC television. + Basically, it splits the signal into 3 signals: Y, I and Q. Y + corresponds to luminance, and you get it by low-pass filtering the + input signal to below 3.57 MHz. + + I and Q are the in-phase and quadrature components of the 3.57 MHz + subcarrier. We get them by multiplying by cos(3.57 MHz*t) and + sin(3.57 MHz*t), and low-pass filtering. Because the eye has less + resolution in some colors than others, the I component gets + low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component + is approximately orange-blue, and Q is roughly purple-green. See + http://www.ntsc-tv.com for details. + + We actually do an awful lot to the signal here. I suspect it would + make sense to wrap them all up together by calculating impulse + response and doing FFT convolutions. + +*/ + +static void +analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal, + int start, int end) +{ + enum {MAXDELAY=32}; + int i; + double *sp; + int phasecorr=(signal-it->rx_signal)&3; + struct analogtv_yiq_s *yiq; + int colormode; + double agclevel=it->agclevel; + double brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL; + double delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp; + double multiq2[4]; + + { + + double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]- + it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0; + double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]- + it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0; + + colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8; + + if (colormode) { + double tint_i = -cos((103 + it->color_control)*3.1415926/180); + double tint_q = sin((103 + it->color_control)*3.1415926/180); + + multiq2[0] = (cb_i*tint_i - cb_q*tint_q) * it->color_control; + multiq2[1] = (cb_q*tint_i + cb_i*tint_q) * it->color_control; + multiq2[2]=-multiq2[0]; + multiq2[3]=-multiq2[1]; + } + } + +#if 0 + if (lineno==100) { + printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ", + it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]); + printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n", + it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1], + it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]); + printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n", + multiq2[0],multiq2[1],multiq2[2],multiq2[3]); + } +#endif + + dp=delay+ANALOGTV_PIC_LEN-MAXDELAY; + for (i=0; i<5; i++) dp[i]=0.0; + + assert(start>=0); + assert(end < ANALOGTV_PIC_LEN+10); + + dp=delay+ANALOGTV_PIC_LEN-MAXDELAY; + for (i=0; i<24; i++) dp[i]=0.0; + for (i=start, yiq=it->yiq+start, sp=signal+start; + iy = dp[8] + brightadd; + } + + if (colormode) { + dp=delay+ANALOGTV_PIC_LEN-MAXDELAY; + for (i=0; i<27; i++) dp[i]=0.0; + + for (i=start, yiq=it->yiq+start, sp=signal+start; + ii=dp[8] = (dp[5] + dp[0] + +3.0*(dp[4] + dp[1]) + +4.0*(dp[3] + dp[2]) + -0.3333333333 * dp[10]); + + dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333; + yiq->q=dp[24] = (dp[16+5] + dp[16+0] + +3.0*(dp[16+4] + dp[16+1]) + +4.0*(dp[16+3] + dp[16+2]) + -0.3333333333 * dp[24+2]); + } + } else { + for (i=start, yiq=it->yiq+start; ii = yiq->q = 0.0; + } + } +} + +void +analogtv_setup_teletext(analogtv_input *input) +{ + int x,y; + int teletext=ANALOGTV_BLACK_LEVEL; + + /* Teletext goes in line 21. But I suspect there are other things + in the vertical retrace interval */ + + for (y=19; y<22; y++) { + for (x=ANALOGTV_PIC_START; xsignal[y][x]=teletext; + } + } +} + +void +analogtv_setup_frame(analogtv *it) +{ + int i,x,y; + + it->redraw_all=0; + + if (it->flutter_horiz_desync) { + /* Horizontal sync during vertical sync instability. */ + it->horiz_desync += -0.10*(it->horiz_desync-3.0) + + ((int)(random()&0xff)-0x80) * + ((int)(random()&0xff)-0x80) * + ((int)(random()&0xff)-0x80) * 0.000001; + } + + for (i=0; ihashnoise_times[i]=0; + } + + if (it->hashnoise_enable && !it->hashnoise_on) { + if (random()%10000==0) { + it->hashnoise_on=1; + it->shrinkpulse=random()%ANALOGTV_V; + } + } + if (random()%1000==0) { + it->hashnoise_on=0; + } + if (it->hashnoise_on) { + it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 + + ((int)(random()%2000)-1000)*0.1; + } else { + it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm; + if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0; + } + if (it->hashnoise_rpm >= 0.0) { + int hni; + int hnc=it->hashnoise_counter; /* in 24.8 format */ + + /* Convert rpm of a 16-pole motor into dots in 24.8 format */ + hni = (int)(ANALOGTV_V * ANALOGTV_H * 256.0 / + (it->hashnoise_rpm * 16.0 / 60.0 / 60.0)); + + while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) { + y=(hnc>>8)/ANALOGTV_H; + x=(hnc>>8)%ANALOGTV_H; + + if (x>0 && xhashnoise_times[y]=x; + } + hnc += hni + (int)(random()%65536)-32768; + } + hnc -= (ANALOGTV_V * ANALOGTV_H)<<8; + } + + it->agclevel = 1.0/it->rx_signal_level; + + +#ifdef DEBUG2 + printf("filter: "); + for (i=0; ighostfir[i]); + } + printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel); +#endif +} + +void +analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi) +{ + int i,lineno,vsync; + char *sig; + + int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL; + + for (lineno=0; lineno=3 && lineno<7; + + sig=input->signal[lineno]; + + i=ANALOGTV_SYNC_START; + if (vsync) { + while (icur_hsync; + int cur_vsync=it->cur_vsync; + int lineno; + int i,j; + double osc,filt; + double *sp; + double cbfc=1.0/128.0; + + sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync; + for (i=-32; i<32; i++) { + lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V; + sp = it->rx_signal + lineno*ANALOGTV_H; + filt=0.0; + for (j=0; jagclevel; + + osc = (double)(ANALOGTV_V+i)/(double)ANALOGTV_V; + + if (osc >= 1.05+0.0002 * filt) break; + } + cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V; + + for (lineno=0; lineno5 && linenorx_signal + ((lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V + )*ANALOGTV_H + cur_hsync; + for (i=-8; i<8; i++) { + osc = (double)(ANALOGTV_H+i)/(double)ANALOGTV_H; + filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel; + + if (osc >= 1.005 + 0.0001*filt) break; + } + cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H; + } + + it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START + + ANALOGTV_H) % ANALOGTV_H; + + /* Now look for the colorburst, which is a few cycles after the H + sync pulse, and store its phase. + The colorburst is 9 cycles long, and we look at the middle 5 + cycles. + */ + + if (lineno>15) { + sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3); + for (i=ANALOGTV_CB_START+8; icb_phase[i&3] = it->cb_phase[i&3]*(1.0-cbfc) + + sp[i]*it->agclevel*cbfc; + } + } + + { + double tot=0.1; + double cbgain; + + for (i=0; i<4; i++) { + tot += it->cb_phase[i]*it->cb_phase[i]; + } + cbgain = 32.0/sqrt(tot); + + for (i=0; i<4; i++) { + it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain; + } + } + +#ifdef DEBUG + if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n", + cur_hsync, + it->cb_phase[0], it->cb_phase[1], + it->cb_phase[2], it->cb_phase[3]); +#endif + + /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */ + } + + it->cur_hsync = cur_hsync; + it->cur_vsync = cur_vsync; +} + +static double +analogtv_levelmult(analogtv *it, int level) +{ + static double levelfac[3]={-7.5, 5.5, 24.5}; + return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0; +} + +static int +analogtv_level(analogtv *it, int y, int ytop, int ybot) +{ + int level; + if (ybot-ytop>=7) { + if (y==ytop || y==ybot-1) level=0; + else if (y==ytop+1 || y==ybot-2) level=1; + else level=2; + } + else if (ybot-ytop>=5) { + if (y==ytop || y==ybot-1) level=0; + else level=2; + } + else if (ybot-ytop>=3) { + if (y==ytop) level=0; + else level=2; + } + else { + level=2; + } + return level; +} + +static void +analogtv_blast_imagerow(analogtv *it, + float *rgbf, float *rgbf_end, + int ytop, int ybot) +{ + int i,j,x,y; + float *rpf; + char *level_copyfrom[3]; + int xrepl=it->xrepl; + for (i=0; i<3; i++) level_copyfrom[i]=NULL; + + for (y=ytop; yimage->data + y*it->image->bytes_per_line; + + /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show + why standard graphics sw has to be fast, or else people will have to + work around it and risk incompatibility. The quickdraw folks + understood this. The other answer would be for X11 to have fewer + formats for bitm.. oh, never mind. If neither of these cases work + (they probably cover 99% of setups) it falls back on the Xlib + routines. */ + + if (level_copyfrom[level]) { + memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line); + } + else { + double levelmult=analogtv_levelmult(it, level); + level_copyfrom[level] = rowdata; + + if (0) { + } + else if (it->image->format==ZPixmap && + it->image->bits_per_pixel==32 && + sizeof(unsigned int)==4 && + it->image->byte_order==localbyteorder) { + /* int is more likely to be 32 bits than long */ + unsigned int *pixelptr=(unsigned int *)rowdata; + unsigned int pix; + + for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) { + int ntscri=rpf[0]*levelmult; + int ntscgi=rpf[1]*levelmult; + int ntscbi=rpf[2]*levelmult; + if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1; + if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1; + if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1; + pix = (it->red_values[ntscri] | + it->green_values[ntscgi] | + it->blue_values[ntscbi]); + pixelptr[0] = pix; + if (xrepl>=2) { + pixelptr[1] = pix; + if (xrepl>=3) pixelptr[2] = pix; + } + pixelptr+=xrepl; + } + } + else if (it->image->format==ZPixmap && + it->image->bits_per_pixel==16 && + sizeof(unsigned short)==2 && + float_extraction_works && + it->image->byte_order==localbyteorder) { + unsigned short *pixelptr=(unsigned short *)rowdata; + double r2,g2,b2; + float_extract_t r1,g1,b1; + unsigned short pix; + + for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) { + r2=rpf[0]; g2=rpf[1]; b2=rpf[2]; + r1.f=r2 * levelmult+float_low8_ofs; + g1.f=g2 * levelmult+float_low8_ofs; + b1.f=b2 * levelmult+float_low8_ofs; + pix = (it->red_values[r1.i & 0x3ff] | + it->green_values[g1.i & 0x3ff] | + it->blue_values[b1.i & 0x3ff]); + pixelptr[0] = pix; + if (xrepl>=2) { + pixelptr[1] = pix; + if (xrepl>=3) pixelptr[2] = pix; + } + pixelptr+=xrepl; + } + } + else if (it->image->format==ZPixmap && + it->image->bits_per_pixel==16 && + sizeof(unsigned short)==2 && + it->image->byte_order==localbyteorder) { + unsigned short *pixelptr=(unsigned short *)rowdata; + unsigned short pix; + + for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) { + int r1=rpf[0] * levelmult; + int g1=rpf[1] * levelmult; + int b1=rpf[2] * levelmult; + if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1; + if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1; + if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1; + pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1]; + pixelptr[0] = pix; + if (xrepl>=2) { + pixelptr[1] = pix; + if (xrepl>=3) pixelptr[2] = pix; + } + pixelptr+=xrepl; + } + } + else { + for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) { + int ntscri=rpf[0]*levelmult; + int ntscgi=rpf[1]*levelmult; + int ntscbi=rpf[2]*levelmult; + if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1; + if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1; + if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1; + for (j=0; jimage, x*xrepl + j, y, + it->red_values[ntscri] | it->green_values[ntscgi] | + it->blue_values[ntscbi]); + } + } + } + } + } +} + +void +analogtv_draw(analogtv *it) +{ + int i,j,x,y,lineno; + int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate; + float *rgb_start, *rgb_end; + double pixbright; + int pixmultinc; + int bigloadchange,drawcount; + double baseload; + double puheight; + int overall_top, overall_bot; + + float *raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float)); + float *raw_rgb_end=raw_rgb_start+3*it->subwidth; + float *rrp; + + analogtv_setup_frame(it); + analogtv_set_demod(it); + + /* rx_signal has an extra 2 lines at the end, where we copy the + first 2 lines so we can index into it while only worrying about + wraparound on a per-line level */ + memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN], + &it->rx_signal[0], + 2*ANALOGTV_H*sizeof(it->rx_signal[0])); + + analogtv_sync(it); + + baseload=0.5; + /* if (it->hashnoise_on) baseload=0.5; */ + + bigloadchange=1; + drawcount=0; + it->crtload[ANALOGTV_TOP-1]=baseload; + puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control * + (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1)); + + overall_top=it->useheight; + overall_bot=0; + + for (lineno=ANALOGTV_TOP; linenouseheight/ANALOGTV_VISLINES - + it->useheight/2)*puheight) + it->useheight/2; + int ybot=(int)(((slineno+1)*it->useheight/ANALOGTV_VISLINES - + it->useheight/2)*puheight) + it->useheight/2; +#if 0 + int linesig=analogtv_line_signature(input,lineno) + + it->hashnoise_times[lineno]; +#endif + double *signal=(it->rx_signal + ((lineno + it->cur_vsync + + ANALOGTV_V)%ANALOGTV_V) * ANALOGTV_H + + it->line_hsync[lineno]); + + if (ytop==ybot) continue; + if (ybot<0 || ytop>it->useheight) continue; + if (ytop<0) ytop=0; + if (ybot>it->useheight) ybot=it->useheight; + + if (ytop < overall_top) overall_top=ytop; + if (ybot > overall_bot) overall_bot=ybot; + + if (lineno==it->shrinkpulse) { + baseload += 0.4; + bigloadchange=1; + it->shrinkpulse=-1; + } + +#if 0 + if (it->hashnoise_rpm>0.0 && + !(bigloadchange || + it->redraw_all || + (slineno<20 && it->flutter_horiz_desync) || + it->gaussiannoise_level>30 || + ((it->gaussiannoise_level>2.0 || + it->multipath) && random()%4) || + linesig != it->onscreen_signature[lineno])) { + continue; + } + it->onscreen_signature[lineno] = linesig; +#endif + drawcount++; + + /* + Interpolate the 600-dotclock line into however many horizontal + screen pixels we're using, and convert to RGB. + + We add some 'bloom', variations in the horizontal scan width with + the amount of brightness, extremely common on period TV sets. They + had a single oscillator which generated both the horizontal scan and + (during the horizontal retrace interval) the high voltage for the + electron beam. More brightness meant more load on the oscillator, + which caused an decrease in horizontal deflection. Look for + (bloomthisrow). + + Also, the A2 did a bad job of generating horizontal sync pulses + during the vertical blanking interval. This, and the fact that the + horizontal frequency was a bit off meant that TVs usually went a bit + out of sync during the vertical retrace, and the top of the screen + would be bent a bit to the left or right. Look for (shiftthisrow). + + We also add a teeny bit of left overscan, just enough to be + annoying, but you can still read the left column of text. + + We also simulate compression & brightening on the right side of the + screen. Most TVs do this, but you don't notice because they overscan + so it's off the right edge of the CRT. But the A2 video system used + so much of the horizontal scan line that you had to crank the + horizontal width down in order to not lose the right few characters, + and you'd see the compression on the right edge. Associated with + compression is brightening; since the electron beam was scanning + slower, the same drive signal hit the phosphor harder. Look for + (squishright_i) and (squishdiv). + */ + + { + int totsignal=0; + double ncl,diff; + + for (i=0; iagclevel; + ncl = 0.95 * it->crtload[lineno-1] + + 0.05*(baseload + + (totsignal-30000)/100000.0 + + (slineno>184 ? (slineno-184)*(lineno-184)*0.001 * it->squeezebottom + : 0.0)); + diff=ncl - it->crtload[lineno]; + bigloadchange = (diff>0.01 || diff<-0.01); + it->crtload[lineno]=ncl; + } + + { + double bloomthisrow,shiftthisrow; + double viswidth,middle; + double scanwidth; + int scw,scl,scr; + + bloomthisrow = -10.0 * it->crtload[lineno]; + if (bloomthisrow<-10.0) bloomthisrow=-10.0; + if (bloomthisrow>2.0) bloomthisrow=2.0; + if (slineno<16) { + shiftthisrow=it->horiz_desync * (exp(-0.17*slineno) * + (0.7+cos(slineno*0.6))); + } else { + shiftthisrow=0.0; + } + + viswidth=ANALOGTV_PIC_LEN * 0.79 - 5.0*bloomthisrow; + middle=ANALOGTV_PIC_LEN/2 - shiftthisrow; + + scanwidth=it->width_control * puramp(it, 0.5, 0.3, 1.0); + + scw=it->subwidth*scanwidth; + if (scw>it->subwidth) scw=it->usewidth; + scl=it->subwidth/2 - scw/2; + scr=it->subwidth/2 + scw/2; + + pixrate=(int)((viswidth*65536.0*1.0)/it->subwidth)/scanwidth; + scanstart_i=(int)((middle-viswidth*0.5)*65536.0); + scanend_i=(ANALOGTV_PIC_LEN-1)*65536; + squishright_i=(int)((middle+viswidth*(0.25 + 0.25*puramp(it, 2.0, 0.0, 1.1) + - it->squish_control)) *65536.0); + squishdiv=it->subwidth/15; + + rgb_start=raw_rgb_start+scl*3; + rgb_end=raw_rgb_start+scr*3; + + assert(scanstart_i>=0); + +#ifdef DEBUG + if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n", + lineno, + scanstart_i/65536.0, + squishright_i/65536.0, + scanend_i/65536.0, + scl,scr,scw); +#endif + } + + if (it->use_cmap) { + for (y=ytop; ycontrast_control + * puramp(it, 1.0, 0.0, 1.0) / (0.5+0.5*puheight) * 0.070; + double levelmult_iq = levelmult * 0.090; + + struct analogtv_yiq_s *yiq=it->yiq; + analogtv_ntsc_to_yiq(it, lineno, signal, + (scanstart_i>>16)-10, (scanend_i>>16)+10); + pixmultinc=pixrate; + + x=0; + i=scanstart_i; + while (i<0 && xusewidth) { + XPutPixel(it->image, x, y, it->colors[0]); + i+=pixmultinc; + x++; + } + + while (iusewidth) { + double pixfrac=(i&0xffff)/65536.0; + double invpixfrac=(1.0-pixfrac); + int pati=i>>16; + int yli,ili,qli,cmi; + + double interpy=(yiq[pati].y*invpixfrac + + yiq[pati+1].y*pixfrac) * levelmult_y; + double interpi=(yiq[pati].i*invpixfrac + + yiq[pati+1].i*pixfrac) * levelmult_iq; + double interpq=(yiq[pati].q*invpixfrac + + yiq[pati+1].q*pixfrac) * levelmult_iq; + + yli = (int)(interpy * it->cmap_y_levels); + ili = (int)((interpi+0.5) * it->cmap_i_levels); + qli = (int)((interpq+0.5) * it->cmap_q_levels); + if (yli<0) yli=0; + if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1; + if (ili<0) ili=0; + if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1; + if (qli<0) qli=0; + if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1; + + cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli); + +#ifdef DEBUG + if ((random()%65536)==0) { + printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n", + interpy, interpi, interpq, + yli, ili, qli, + cmi); + } +#endif + + for (j=0; jxrepl; j++) { + XPutPixel(it->image, x, y, + it->colors[cmi]); + x++; + } + if (i >= squishright_i) { + pixmultinc += pixmultinc/squishdiv; + } + i+=pixmultinc; + } + while (xusewidth) { + XPutPixel(it->image, x, y, it->colors[0]); + x++; + } + } + } + else { + struct analogtv_yiq_s *yiq=it->yiq; + analogtv_ntsc_to_yiq(it, lineno, signal, + (scanstart_i>>16)-10, (scanend_i>>16)+10); + + pixbright=it->contrast_control * puramp(it, 1.0, 0.0, 1.0) + / (0.5+0.5*puheight) * 1024.0/100.0; + pixmultinc=pixrate; + i=scanstart_i; rrp=rgb_start; + while (i<0 && rrp!=rgb_end) { + rrp[0]=rrp[1]=rrp[2]=0; + i+=pixmultinc; + rrp+=3; + } + while (i>16; + double r,g,b; + + double interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac); + double interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac); + double interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac); + + /* + According to the NTSC spec, Y,I,Q are generated as: + + y=0.30 r + 0.59 g + 0.11 b + i=0.60 r - 0.28 g - 0.32 b + q=0.21 r - 0.52 g + 0.31 b + + So if you invert the implied 3x3 matrix you get what standard + televisions implement with a bunch of resistors (or directly in the + CRT -- don't ask): + + r = y + 0.948 i + 0.624 q + g = y - 0.276 i - 0.639 q + b = y - 1.105 i + 1.729 q + */ + + r=(interpy + 0.948*interpi + 0.624*interpq) * pixbright; + g=(interpy - 0.276*interpi - 0.639*interpq) * pixbright; + b=(interpy - 1.105*interpi + 1.729*interpq) * pixbright; + if (r<0.0) r=0.0; + if (g<0.0) g=0.0; + if (b<0.0) b=0.0; + rrp[0]=r; + rrp[1]=g; + rrp[2]=b; + + if (i>=squishright_i) { + pixmultinc += pixmultinc/squishdiv; + pixbright += pixbright/squishdiv/2; + } + i+=pixmultinc; + rrp+=3; + } + while (rrp != rgb_end) { + rrp[0]=rrp[1]=rrp[2]=0.0; + rrp+=3; + } + + analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end, + ytop,ybot); + } + } + free(raw_rgb_start); + +#if 0 + /* poor attempt at visible retrace */ + for (i=0; i<15; i++) { + int ytop=(int)((i*it->useheight/15 - + it->useheight/2)*puheight) + it->useheight/2; + int ybot=(int)(((i+1)*it->useheight/15 - + it->useheight/2)*puheight) + it->useheight/2; + int div=it->usewidth*3/2; + + for (x=0; xusewidth; x++) { + y = ytop + (ybot-ytop)*x / div; + if (y<0 || y>=it->useheight) continue; + XPutPixel(it->image, x, y, 0xffffff); + } + } +#endif + + if (it->need_clear) { + XClearWindow(it->dpy, it->window); + it->need_clear=0; + } + + if (overall_top>0) { + XClearArea(it->dpy, it->window, + it->screen_xo, it->screen_yo, + it->usewidth, overall_top, 0); + } + if (it->useheight > overall_bot) { + XClearArea(it->dpy, it->window, + it->screen_xo, it->screen_yo+overall_bot, + it->usewidth, it->useheight-overall_bot, 0); + } + + if (overall_bot > overall_top) { + if (it->use_shm) { +#ifdef HAVE_XSHM_EXTENSION + XShmPutImage(it->dpy, it->window, it->gc, it->image, + 0, overall_top, + it->screen_xo, it->screen_yo+overall_top, + it->usewidth, overall_bot - overall_top, + False); +#endif + } else { + XPutImage(it->dpy, it->window, it->gc, it->image, + 0, overall_top, + it->screen_xo, it->screen_yo+overall_top, + it->usewidth, overall_bot - overall_top); + } + } + +#ifdef DEBUG + if (0) { + struct timeval tv; + double fps; + char buf[256]; + gettimeofday(&tv,NULL); + + fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec) + + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec)); + sprintf(buf, "FPS=%0.1f",fps); + XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3, + buf, strlen(buf)); + + it->last_display_time=tv; + } +#endif + + XSync(it->dpy,0); +} + +analogtv_input * +analogtv_input_allocate() +{ + analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input)); + + return ret; +} + +/* + This takes a screen image and encodes it as a video camera would, + including all the bandlimiting and YIQ modulation. + This isn't especially tuned for speed. +*/ + +int +analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im) +{ + int i,x,y; + int img_w,img_h; + int fyx[7],fyy[7]; + int fix[4],fiy[4]; + int fqx[4],fqy[4]; + XColor col1[ANALOGTV_PIC_LEN]; + XColor col2[ANALOGTV_PIC_LEN]; + int multiq[ANALOGTV_PIC_LEN+4]; + + img_w=pic_im->width; + img_h=pic_im->height; + + for (i=0; idpy, it->colormap, col1, ANALOGTV_PIC_LEN); + XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN); + + for (i=0; i<7; i++) fyx[i]=fyy[i]=0; + for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0; + + for (x=0; x>7; + rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue + + 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7; + rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue + + 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7; + + /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz + with an extra zero at 3.5 MHz, from + mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */ + + fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3]; + fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6]; + fyx[6] = (rawy * 1897) >> 16; + fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3]; + fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6]; + fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3] + + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16); + filty = fyy[6]; + + /* Filter I at 1.5 MHz. 3 pole Butterworth from + mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */ + + fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3]; + fix[3] = (rawi * 1413) >> 16; + fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3]; + fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2]) + + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16); + filti = fiy[3]; + + /* Filter Q at 0.5 MHz. 3 pole Butterworth from + mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */ + + fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3]; + fqx[3] = (rawq * 75) >> 16; + fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3]; + fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2]) + + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12); + filtq = fqy[3]; + + + composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12); + composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL; + if (composite>125) composite=125; + if (composite<0) composite=0; + input->signal[y+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite; + } + } + + return 1; +} + +#if 0 +void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2) +{ + int x,y,newsig; + int change=random()%ANALOGTV_V; + unsigned int fastrnd=random(); + double hso=(int)(random()%1000)-500; + int yofs=random()%ANALOGTV_V; + int noise; + + for (y=change; yline_hsync[y] = s2->line_hsync[y] + (int)hso; + hso *= 0.9; + for (x=0; x>16; + newsig=s2->signal[s2y][x] + noise; + if (newsig>120) newsig=120; + if (newsig<0) newsig=0; + it->signal[y][x]=newsig; + } + } + s2->vsync=yofs; +} +#endif + + +void analogtv_add_signal(analogtv *it, analogtv_reception *rec) +{ + analogtv_input *inp=rec->input; + double *ps=it->rx_signal; + double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN; + double *p=ps; + char *ss=&inp->signal[0][0]; + char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN; + char *s=ss + ((unsigned)rec->ofs % ANALOGTV_SIGNAL_LEN); + int i; + int ec=it->channel_change_cycles; + double level=rec->level; + double hfloss=rec->hfloss; + unsigned int fastrnd=random(); + double dp[8]; + + /* assert((se-ss)%4==0 && (se-s)%4==0); */ + + /* duplicate the first line into the Nth line to ease wraparound computation */ + memcpy(inp->signal[ANALOGTV_V], inp->signal[0], + ANALOGTV_H * sizeof(inp->signal[0][0])); + + for (i=0; i<8; i++) dp[i]=0.0; + + if (ec) { + double noise_ampl; + + /* Do a big noisy transition. We can make the transition noise of + high constant strength regardless of signal strength. + + There are two separate state machines. here, One is the noise + process and the other is the + + We don't bother with the FIR filter here + */ + + noise_ampl = 1.3; + + while (p!=pe && ec>0) { + + double sig0=(double)s[0]; + double noise = ((int)fastrnd-(int)0x7fffffff) * (50.0/(double)0x7fffffff); + fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu; + + p[0] += sig0 * level * (1.0 - noise_ampl) + noise * noise_ampl; + + noise_ampl *= 0.99995; + + p++; + s++; + if (s>=se) s=ss; + ec--; + } + + } + + while (p != pe) { + double sig0,sig1,sig2,sig3,sigr; + + sig0=(double)s[0]; + sig1=(double)s[1]; + sig2=(double)s[2]; + sig3=(double)s[3]; + + dp[0]=sig0+sig1+sig2+sig3; + + /* Get the video out signal, and add some ghosting, typical of RF + monitor cables. This corresponds to a pretty long cable, but + looks right to me. + */ + + sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] + + dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]); + dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0]; + + p[0] += (sig0+sigr + sig2*hfloss) * level; + p[1] += (sig1+sigr + sig3*hfloss) * level; + p[2] += (sig2+sigr + sig0*hfloss) * level; + p[3] += (sig3+sigr + sig1*hfloss) * level; + + p += 4; + s += 4; + if (s>=se) s = ss + (s-se); + } + + it->rx_signal_level = + sqrt(it->rx_signal_level * it->rx_signal_level + + (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] + + rec->ghostfir[2] + rec->ghostfir[3])))); + + + it->channel_change_cycles=0; + +} + +#ifdef FIXME +/* add hash */ + if (it->hashnoise_times[lineno]) { + int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno]; + + if (hnt>=0 && hnt ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt; + for (i=0; irx_signal; + double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN; + double *p=ps; + unsigned int fastrnd=random(); + double nm1=0.0,nm2=0.0; + double noisemul = sqrt(noiselevel*150)/(double)0x7fffffff; + + while (p != pe) { + nm2=nm1; + nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul; + *p++ = nm1*nm2; + fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu; + } + + it->rx_signal_level = noiselevel; +} + +void +analogtv_reception_update(analogtv_reception *rec) +{ + int i; + + if (rec->multipath > 0.0) { + for (i=0; ighostfir2[i] += + -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01); + } + if (random()%20==0) { + rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)] + = rec->multipath * (frand(0.08)-0.04); + } + for (i=0; ighostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i]; + } + + if (0) { + rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04); + rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2; + } + + } else { + for (i=0; ighostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN + : 0.0; + } + } +} + + +void +analogtv_make_font(Display *dpy, Window window, analogtv_font *f, + int w, int h, char *fontname) +{ + int i; + XFontStruct *font; + Pixmap text_pm; + GC gc; + XGCValues gcv; + XWindowAttributes xgwa; + + f->char_w = w; + f->char_h = h; + + XGetWindowAttributes (dpy, window, &xgwa); + + if (fontname) { + + font = XLoadQueryFont (dpy, fontname); + if (!font) { + fprintf(stderr, "analogtv: can't load font %s\n", fontname); + abort(); + } + + text_pm=XCreatePixmap(dpy, window, 128*f->char_w, f->char_h, xgwa.depth); + + memset(&gcv, 0, sizeof(gcv)); + gcv.foreground=1; + gcv.background=0; + gcv.font=font->fid; + gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv); + + XSetForeground(dpy, gc, 0); + XFillRectangle(dpy, text_pm, gc, 0, 0, 128*f->char_w, f->char_h); + XSetForeground(dpy, gc, 1); + /* Just ASCII */ + for (i=0; i<128; i++) { + char c=i; + int x=f->char_w*i+1; + int y=f->char_h*8/10; + XDrawString(dpy, text_pm, gc, x, y, &c, 1); + } + f->text_im = XGetImage(dpy, text_pm, 0, 0, 128*f->char_w, f->char_h, + ~0L, ZPixmap); + XFreeGC(dpy, gc); + XFreePixmap(dpy, text_pm); + } else { + f->text_im = XCreateImage(dpy, xgwa.visual, xgwa.depth, + ZPixmap, 0, 0, + 128*f->char_w, f->char_h, 8, 0); + f->text_im->data = (char *)calloc(f->text_im->height, + f->text_im->bytes_per_line); + + } + f->x_mult=4; + f->y_mult=2; +} + +int +analogtv_font_pixel(analogtv_font *f, int c, int x, int y) +{ + if (x<0 || x>=f->char_w) return 0; + if (y<0 || y>=f->char_h) return 0; + if (c<0 || c>=128) return 0; + return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0; +} + +void +analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value) +{ + if (x<0 || x>=f->char_w) return; + if (y<0 || y>=f->char_h) return; + if (c<0 || c>=128) return; + + XPutPixel(f->text_im, c*f->char_w + x, y, value); +} + +void +analogtv_font_set_char(analogtv_font *f, int c, char *s) +{ + int value,x,y; + + if (c<0 || c>=128) return; + + for (y=0; ychar_h; y++) { + for (x=0; xchar_w; x++) { + if (!*s) return; + value=(*s==' ') ? 0 : 1; + analogtv_font_set_pixel(f, c, x, y, value); + s++; + } + } +} + +void +analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4]) +{ + int i; + for (i=0; i<4; i++) { + double w=90.0*i + phase; + double val=luma + chroma * (cos(3.1415926/180.0*w)); + if (val<0.0) val=0.0; + if (val>127.0) val=127.0; + ntsc[i]=(int)val; + } +} + +void +analogtv_draw_solid(analogtv_input *input, + int left, int right, int top, int bot, + int ntsc[4]) +{ + int x,y; + + if (right-left<4) right=left+4; + if (bot-top<1) bot=top+1; + + for (y=top; ysignal[y][x] = ntsc[x&3]; + } + } +} + + +void +analogtv_draw_solid_rel_lcp(analogtv_input *input, + double left, double right, double top, double bot, + double luma, double chroma, double phase) +{ + int ntsc[4]; + + int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top); + int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot); + int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left); + int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right); + + analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc); + analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc); +} + + +void +analogtv_draw_char(analogtv_input *input, analogtv_font *f, + int c, int x, int y, int ntsc[4]) +{ + int yc,xc,ys,xs,pix; + + for (yc=0; ycchar_h; yc++) { + for (ys=y + yc*f->y_mult; ysy_mult; ys++) { + if (ys<0 || ys>=ANALOGTV_V) continue; + + for (xc=0; xcchar_w; xc++) { + pix=analogtv_font_pixel(f, c, xc, yc); + + for (xs=x + xc*f->x_mult; xsx_mult; xs++) { + if (xs<0 || xs>=ANALOGTV_H) continue; + if (pix) { + input->signal[ys][xs] = ntsc[xs&3]; + } + } + } + } + } +} + +void +analogtv_draw_string(analogtv_input *input, analogtv_font *f, + char *s, int x, int y, int ntsc[4]) +{ + while (*s) { + analogtv_draw_char(input, f, *s, x, y, ntsc); + x += f->char_w * 4; + s++; + } +} + +void +analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f, + char *s, int x, int y, int ntsc[4]) +{ + int width=strlen(s) * f->char_w * 4; + x -= width/2; + + analogtv_draw_string(input, f, s, x, y, ntsc); +} + + +static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* + Much of this function was adapted from logo.c + */ +void +analogtv_draw_xpm(analogtv *tv, analogtv_input *input, + const char * const *xpm, int left, int top) +{ + int xpmw,xpmh; + int x,y,tvx,tvy,i; + int rawy,rawi,rawq; + int ncolors, nbytes; + char dummyc; + struct { + int r; int g; int b; + } cmap[256]; + + + if (4 != sscanf ((const char *) *xpm, + "%d %d %d %d %c", + &xpmw, &xpmh, &ncolors, &nbytes, &dummyc)) + abort(); + if (ncolors < 1 || ncolors > 255) + abort(); + if (nbytes != 1) /* a serious limitation */ + abort(); + xpm++; + + for (i = 0; i < ncolors; i++) { + const char *line = *xpm; + int colori = ((unsigned char)*line++)&0xff; + while (*line) + { + int r, g, b; + char which; + while (*line == ' ' || *line == '\t') + line++; + which = *line++; + if (which != 'c' && which != 'm') + abort(); + while (*line == ' ' || *line == '\t') + line++; + if (!strncasecmp(line, "None", 4)) + { + r = g = b = -1; + line += 4; + } + else + { + if (*line == '#') + line++; + r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]]; + line += 2; + g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]]; + line += 2; + b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]]; + line += 2; + } + + if (which == 'c') + { + cmap[colori].r = r; + cmap[colori].g = g; + cmap[colori].b = b; + } + } + + xpm++; + } + + for (y=0; y=ANALOGTV_BOT) continue; + + for (x=0; xANALOGTV_PIC_END) continue; + + rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64; + rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64; + rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64; + + ntsc[0]=rawy+rawq; + ntsc[1]=rawy-rawi; + ntsc[2]=rawy-rawq; + ntsc[3]=rawy+rawi; + + for (i=0; i<4; i++) { + if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL; + if (ntsc[i]signal[tvy][tvx+0]= ntsc[(tvx+0)&3]; + input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3]; + input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3]; + input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3]; + } + } +} + +extern XtAppContext app; + +int +analogtv_handle_events (analogtv *it) +{ + XSync(it->dpy, False); + if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput)) + XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput); + + while (XPending (it->dpy)) + { + XEvent event; + XNextEvent (it->dpy, &event); + switch (event.xany.type) + { + case ButtonPress: + return 1; + + case KeyPress: + { + KeySym keysym; + char c = 0; + XLookupString (&event.xkey, &c, 1, &keysym, 0); + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + return 1; + } + break; + + /* I don't seem to get an event when clicking the "full + screen" window manager icon, at least when using + metacity. Thus, it doesn't change the video size. Is this + some separate WM_* message I have to deal with? + */ + case ConfigureNotify: + if (event.xconfigure.width != it->xgwa.width || + event.xconfigure.height != it->xgwa.height) + analogtv_reconfigure(it); + break; + + case Expose: + case GraphicsExpose: + it->need_clear=1; + break; + + default: + break; + } + if (it->event_handler) { + (*it->event_handler) (it->dpy, &event); + } + } + return 0; +} + diff --git a/hacks/analogtv.h b/hacks/analogtv.h new file mode 100644 index 00000000..ff9e8db2 --- /dev/null +++ b/hacks/analogtv.h @@ -0,0 +1,290 @@ +/* analogtv, Copyright (c) 2003 Trevor Blackwell + * + * 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_ANALOGTV_H +#define _XSCREENSAVER_ANALOGTV_H + +#include "xshm.h" + +/* + You'll need these to generate standard NTSC TV signals + */ +enum { + /* We don't handle interlace here */ + ANALOGTV_V=262, + ANALOGTV_TOP=30, + ANALOGTV_VISLINES=200, + ANALOGTV_BOT=ANALOGTV_TOP + ANALOGTV_VISLINES, + + /* This really defines our sampling rate, 4x the colorburst + frequency. Handily equal to the Apple II's dot clock. + You could also make a case for using 3x the colorburst freq, + but 4x isn't hard to deal with. */ + ANALOGTV_H=912, + + /* Each line is 63500 nS long. The sync pulse is 4700 nS long, etc. + Define sync, back porch, colorburst, picture, and front porch + positions */ + ANALOGTV_SYNC_START=0, + ANALOGTV_BP_START=4700*ANALOGTV_H/63500, + ANALOGTV_CB_START=5800*ANALOGTV_H/63500, + /* signal[row][ANALOGTV_PIC_START] is the first displayed pixel */ + ANALOGTV_PIC_START=9400*ANALOGTV_H/63500, + ANALOGTV_PIC_LEN=52600*ANALOGTV_H/63500, + ANALOGTV_FP_START=62000*ANALOGTV_H/63500, + ANALOGTV_PIC_END=ANALOGTV_FP_START, + + /* TVs scan past the edges of the picture tube, so normally you only + want to use about the middle 3/4 of the nominal scan line. + */ + ANALOGTV_VIS_START=ANALOGTV_PIC_START + (ANALOGTV_PIC_LEN*1/8), + ANALOGTV_VIS_END=ANALOGTV_PIC_START + (ANALOGTV_PIC_LEN*7/8), + ANALOGTV_VIS_LEN=ANALOGTV_VIS_END-ANALOGTV_VIS_START, + + ANALOGTV_HASHNOISE_LEN=6, + + ANALOGTV_GHOSTFIR_LEN=4, + + /* analogtv.signal is in IRE units, as defined below: */ + ANALOGTV_WHITE_LEVEL=100, + ANALOGTV_GRAY50_LEVEL=55, + ANALOGTV_GRAY30_LEVEL=35, + ANALOGTV_BLACK_LEVEL=10, + ANALOGTV_BLANK_LEVEL=0, + ANALOGTV_SYNC_LEVEL=-40, + ANALOGTV_CB_LEVEL=20, + + ANALOGTV_SIGNAL_LEN=ANALOGTV_V*ANALOGTV_H, + + /* The number of intensity levels we deal with for gamma correction &c */ + ANALOGTV_CV_MAX=1024 +}; + +typedef struct analogtv_input_s { + char signal[ANALOGTV_V+1][ANALOGTV_H]; + + int do_teletext; + + /* for client use */ + void (*updater)(struct analogtv_input_s *inp); + void *client_data; + double next_update_time; + +} analogtv_input; + +typedef struct analogtv_font_s { + XImage *text_im; + int char_w, char_h; + int x_mult, y_mult; +} analogtv_font; + +typedef struct analogtv_reception_s { + + analogtv_input *input; + double ofs; + double level; + double multipath; + double freqerr; + + double ghostfir[ANALOGTV_GHOSTFIR_LEN]; + double ghostfir2[ANALOGTV_GHOSTFIR_LEN]; + + double hfloss; + double hfloss2; + +} analogtv_reception; + +/* + The rest of this should be considered mostly opaque to the analogtv module. + */ + +typedef struct analogtv_s { + + Display *dpy; + Window window; + Screen *screen; + XWindowAttributes xgwa; + +#if 0 + unsigned int onscreen_signature[ANALOGTV_V]; +#endif + + int n_colors; + + int interlace; + int interlace_counter; + + double agclevel; + + /* If you change these, call analogtv_set_demod */ + double tint_control,color_control,brightness_control,contrast_control; + double height_control, width_control, squish_control; + double horiz_desync; + double squeezebottom; + double powerup; + + /* internal cache */ + int blur_mult; + + /* For fast display, set fakeit_top, fakeit_bot to + the scanlines (0..ANALOGTV_V) that can be preserved on screen. + fakeit_scroll is the number of scan lines to scroll it up, + or 0 to not scroll at all. It will DTRT if asked to scroll from + an offscreen region. + */ + int fakeit_top; + int fakeit_bot; + int fakeit_scroll; + int redraw_all; + + int use_shm,use_cmap,use_color; + int bilevel_signal; + +#ifdef HAVE_XSHM_EXTENSION + XShmSegmentInfo shm_info; +#endif + int visdepth,visclass,visbits; + int red_invprec,red_shift,red_mask; + int green_invprec,green_shift,green_mask; + int blue_invprec,blue_shift,blue_mask; + + Colormap colormap; + int usewidth,useheight,xrepl,subwidth; + XImage *image; /* usewidth * useheight */ + GC gc; + int screen_xo,screen_yo; /* centers image in window */ + + void (*event_handler)(Display *dpy, XEvent *event); + + int flutter_horiz_desync; + int flutter_tint; + + struct timeval last_display_time; + int need_clear; + + + /* Add hash (in the radio sense, not the programming sense.) These + are the small white streaks that appear in quasi-regular patterns + all over the screen when someone is running the vacuum cleaner or + the blender. We also set shrinkpulse for one period which + squishes the image horizontally to simulate the temporary line + voltate drop when someone turns on a big motor */ + double hashnoise_rpm; + int hashnoise_counter; + int hashnoise_times[ANALOGTV_V]; + int hashnoise_signal[ANALOGTV_V]; + int hashnoise_on; + int hashnoise_enable; + int shrinkpulse; + + double crtload[ANALOGTV_V]; + + unsigned int red_values[ANALOGTV_CV_MAX]; + unsigned int green_values[ANALOGTV_CV_MAX]; + unsigned int blue_values[ANALOGTV_CV_MAX]; + + struct analogtv_yiq_s { + float y,i,q; + } yiq[ANALOGTV_PIC_LEN+10]; + + unsigned long colors[256]; + int cmap_y_levels; + int cmap_i_levels; + int cmap_q_levels; + + int cur_hsync; + int line_hsync[ANALOGTV_V]; + int cur_vsync; + double cb_phase[4]; + double line_cb_phase[ANALOGTV_V][4]; + + int channel_change_cycles; + double rx_signal_level; + double rx_signal[ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H]; + +} analogtv; + + +analogtv *analogtv_allocate(Display *dpy, Window window); +analogtv_input *analogtv_input_allocate(void); + +/* call if window size changes */ +void analogtv_reconfigure(analogtv *it); + +void analogtv_set_defaults(analogtv *it, char *prefix); +void analogtv_release(analogtv *it); +int analogtv_set_demod(analogtv *it); +void analogtv_setup_frame(analogtv *it); +void analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi); +void analogtv_draw(analogtv *it); + +int analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im); + +void analogtv_reception_update(analogtv_reception *inp); + +void analogtv_init_signal(analogtv *it, double noiselevel); +void analogtv_add_signal(analogtv *it, analogtv_reception *rec); + +void analogtv_setup_teletext(analogtv_input *input); + + +/* Functions for rendering content into an analogtv_input */ + +void analogtv_make_font(Display *dpy, Window window, + analogtv_font *f, int w, int h, char *fontname); +int analogtv_font_pixel(analogtv_font *f, int c, int x, int y); +void analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value); +void analogtv_font_set_char(analogtv_font *f, int c, char *s); +void analogtv_lcp_to_ntsc(double luma, double chroma, double phase, + int ntsc[4]); + + +void analogtv_draw_solid(analogtv_input *input, + int left, int right, int top, int bot, + int ntsc[4]); + +void analogtv_draw_solid_rel_lcp(analogtv_input *input, + double left, double right, + double top, double bot, + double luma, double chroma, double phase); + +void analogtv_draw_char(analogtv_input *input, analogtv_font *f, + int c, int x, int y, int ntsc[4]); +void analogtv_draw_string(analogtv_input *input, analogtv_font *f, + char *s, int x, int y, int ntsc[4]); +void analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f, + char *s, int x, int y, int ntsc[4]); +void analogtv_draw_xpm(analogtv *tv, analogtv_input *input, + const char * const *xpm, int left, int top); + +int analogtv_handle_events (analogtv *it); + +#ifdef HAVE_XSHM_EXTENSION +#define ANALOGTV_DEFAULTS_SHM "*useSHM: True", +#else +#define ANALOGTV_DEFAULTS_SHM +#endif + +#define ANALOGTV_DEFAULTS \ + "*TVColor: 70", \ + "*TVTint: 5", \ + "*TVBrightness: 2", \ + "*TVContrast: 150",\ + "*Background: Black", \ + "*use_cmap: 0", \ + "*geometry: 800x600", \ + ANALOGTV_DEFAULTS_SHM + +#define ANALOGTV_OPTIONS \ + { "-use-cmap", ".use_cmap", XrmoptionSepArg, 0 }, + + +#endif /* _XSCREENSAVER_ANALOGTV_H */ diff --git a/hacks/ant.c b/hacks/ant.c index f41f19de..d7d07266 100644 --- a/hacks/ant.c +++ b/hacks/ant.c @@ -4,9 +4,8 @@ * as Greg Turk's turmites) whose tape is the screen */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)ant.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/apollonian.c b/hacks/apollonian.c index 4befef4e..0c1888ac 100644 --- a/hacks/apollonian.c +++ b/hacks/apollonian.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* apollonian --- Apollonian Circles */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)apollonian.c 5.02 2001/07/01 xlockmore"; #endif diff --git a/hacks/apple2-main.c b/hacks/apple2-main.c new file mode 100644 index 00000000..ae38787f --- /dev/null +++ b/hacks/apple2-main.c @@ -0,0 +1,1321 @@ +/* xscreensaver, Copyright (c) 1998-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. + * + * Apple ][ CRT simulator, by Trevor Blackwell + * with additional work by Jamie Zawinski + */ + +#include +#include "screenhack.h" +#include "apple2.h" +#include +#include +#include + +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#define DEBUG + +extern XtAppContext app; + +Time subproc_relaunch_delay = 3000; + + +/* Given a bitmask, returns the position and width of the field. + */ +static void +decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret) +{ + int i; + for (i = 0; i < 32; i++) + if (mask & (1L << i)) + { + int j = 0; + *pos_ret = i; + for (; i < 32; i++, j++) + if (! (mask & (1L << i))) + break; + *size_ret = j; + return; + } +} + + +/* Given a value and a field-width, expands the field to fill out 8 bits. + */ +static unsigned char +spread_bits (unsigned char value, unsigned char width) +{ + switch (width) + { + case 8: return value; + case 7: return (value << 1) | (value >> 6); + case 6: return (value << 2) | (value >> 4); + case 5: return (value << 3) | (value >> 2); + case 4: return (value << 4) | (value); + case 3: return (value << 5) | (value << 2) | (value >> 2); + case 2: return (value << 6) | (value << 4) | (value); + default: abort(); break; + } +} + + +/* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array. + Scales it (without dithering) to WxH. + */ +static void +scale_image (Display *dpy, Window window, XImage *in, + int fromx, int fromy, int fromw, int fromh, + unsigned int *out, int w, int h) +{ + float scale; + int x, y, i; + unsigned int rpos=0, gpos=0, bpos=0; /* bitfield positions */ + unsigned int rsiz=0, gsiz=0, bsiz=0; + unsigned int rmsk=0, gmsk=0, bmsk=0; + unsigned char spread_map[3][256]; + XWindowAttributes xgwa; + XColor *colors = 0; + + if (fromx + fromw > in->width || + fromy + fromh > in->height) + abort(); + + XGetWindowAttributes (dpy, window, &xgwa); + + /* Compute the field offsets for RGB decoding in the XImage, + when in TrueColor mode. Otherwise we use the colormap. + */ + if (visual_class (xgwa.screen, xgwa.visual) == PseudoColor || + visual_class (xgwa.screen, xgwa.visual) == GrayScale) + { + int ncolors = visual_cells (xgwa.screen, xgwa.visual); + colors = (XColor *) calloc (sizeof (*colors), ncolors+1); + for (i = 0; i < ncolors; i++) + colors[i].pixel = i; + XQueryColors (dpy, xgwa.colormap, colors, ncolors); + } + else + { + rmsk = xgwa.visual->red_mask; + gmsk = xgwa.visual->green_mask; + bmsk = xgwa.visual->blue_mask; + decode_mask (rmsk, &rpos, &rsiz); + decode_mask (gmsk, &gpos, &gsiz); + decode_mask (bmsk, &bpos, &bsiz); + + for (i = 0; i < 256; i++) + { + spread_map[0][i] = spread_bits (i, rsiz); + spread_map[1][i] = spread_bits (i, gsiz); + spread_map[2][i] = spread_bits (i, bsiz); + } + } + + scale = (fromw > fromh + ? (float) fromw / w + : (float) fromh / h); + + /* Scale the pixmap from window size to Apple][ screen size (but 32bpp) + */ + for (y = 0; y < h-1; y++) /* iterate over dest pixels */ + for (x = 0; x < w-1; x++) + { + int xx, yy; + unsigned int r=0, g=0, b=0; + + int xx1 = x * scale + fromx; + int yy1 = y * scale + fromy; + int xx2 = (x+1) * scale + fromx; + int yy2 = (y+1) * scale + fromy; + + /* Iterate over the source pixels contributing to this one, and sum. */ + for (xx = xx1; xx < xx2; xx++) + for (yy = yy1; yy < yy2; yy++) + { + unsigned char rr, gg, bb; + unsigned long sp = ((xx > in->width || yy > in->height) + ? 0 : XGetPixel (in, xx, yy)); + if (colors) + { + rr = colors[sp].red & 0xFF; + gg = colors[sp].green & 0xFF; + bb = colors[sp].blue & 0xFF; + } + else + { + rr = (sp & rmsk) >> rpos; + gg = (sp & gmsk) >> gpos; + bb = (sp & bmsk) >> bpos; + rr = spread_map[0][rr]; + gg = spread_map[1][gg]; + bb = spread_map[2][bb]; + } + r += rr; + g += gg; + b += bb; + } + + /* Scale summed pixel values down to 8/8/8 range */ + i = (xx2 - xx1) * (yy2 - yy1); + if (i < 1) i = 1; + r /= i; + g /= i; + b /= i; + + out[y * w + x] = (r << 16) | (g << 8) | b; + } +} + + +/* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array. + Picks a random sub-image out of the source image, and scales it to WxH. + */ +static void +pick_a2_subimage (Display *dpy, Window window, XImage *in, + unsigned int *out, int w, int h) +{ + int fromx, fromy, fromw, fromh; + if (in->width <= w || in->height <= h) + { + fromx = 0; + fromy = 0; + fromw = in->width; + fromh = in->height; + } + else + { + int dw, dh; + do { + double scale = (0.5 + frand(0.7) + frand(0.7) + frand(0.7)); + fromw = w * scale; + fromh = h * scale; + } while (fromw > in->width || + fromh > in->height); + + dw = (in->width - fromw) / 2; /* near the center! */ + dh = (in->height - fromh) / 2; + + fromx = (random() % dw) + (dw/2); + fromy = (random() % dh) + (dh/2); + } + + scale_image (dpy, window, in, + fromx, fromy, fromw, fromh, + out, w, h); +} + + +/* Floyd-Steinberg dither. Derived from ppmquant.c, + Copyright (c) 1989, 1991 by Jef Poskanzer. + */ +static void +a2_dither (unsigned int *in, unsigned char *out, int w, int h) +{ + /* + Apple ][ color map. Each pixel can only be 1 or 0, but what that + means depends on whether it's an odd or even pixel, and whether + the high bit in the byte is set or not. If it's 0, it's always + black. + */ + static const int a2_cmap[2][2][3] = { + { + /* hibit=0 */ + {/* odd pixels = blue */ 0x00, 0x80, 0xff}, + {/* even pixels = red */ 0xff, 0x80, 0x00} + }, + { + /* hibit=1 */ + {/* even pixels = purple */ 0xa0, 0x40, 0xa0}, + {/* odd pixels = green */ 0x40, 0xff, 0x40} + } + }; + + int x, y; + unsigned int **pixels; + unsigned int *pP; + int maxval = 255; + long *this_rerr; + long *next_rerr; + long *this_gerr; + long *next_gerr; + long *this_berr; + long *next_berr; + long *temp_err; + int fs_scale = 1024; + int brightness = 75; + int fs_direction; + +#if 0 + { + FILE *pipe = popen ("xv -", "w"); + fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255); + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + { + unsigned int p = in[y * w + x]; + unsigned int r = (p >> 16) & 0xFF; + unsigned int g = (p >> 8) & 0xFF; + unsigned int b = (p ) & 0xFF; + fprintf(pipe, "%c%c%c", r, g, b); + } + fclose (pipe); + } +#endif + + /* Initialize Floyd-Steinberg error vectors. */ + this_rerr = (long *) calloc (w + 2, sizeof(long)); + next_rerr = (long *) calloc (w + 2, sizeof(long)); + this_gerr = (long *) calloc (w + 2, sizeof(long)); + next_gerr = (long *) calloc (w + 2, sizeof(long)); + this_berr = (long *) calloc (w + 2, sizeof(long)); + next_berr = (long *) calloc (w + 2, sizeof(long)); + + + /* #### do we really need more than one element of "pixels" at once? + */ + pixels = (unsigned int **) malloc (h * sizeof (unsigned int *)); + for (y = 0; y < h; y++) + pixels[y] = (unsigned int *) malloc (w * sizeof (unsigned int)); + + for (x = 0; x < w + 2; ++x) + { + this_rerr[x] = random() % (fs_scale * 2) - fs_scale; + this_gerr[x] = random() % (fs_scale * 2) - fs_scale; + this_berr[x] = random() % (fs_scale * 2) - fs_scale; + /* (random errors in [-1 .. 1]) */ + } + fs_direction = 1; + + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + pixels[y][x] = in[y * w + x]; + + for (y = 0; y < h; y++) + { + int xbyte; + int err; + int prev_byte=0; + + for (x = 0; x < w + 2; x++) + next_rerr[x] = next_gerr[x] = next_berr[x] = 0; + + /* It's too complicated to go back and forth on alternate rows, + so we always go left-right here. It doesn't change the result + very much. + + For each group of 7 pixels, we have to try it both with the + high bit=0 and =1. For each high bit value, we add up the + total error and pick the best one. + + Because we have to go through each group of bits twice, we + don't propagate the error values through this_[rgb]err since + it would add them twice. So we keep seperate local_[rgb]err + variables for propagating error within the 7-pixel group. + */ + + pP = pixels[y]; + for (xbyte=0; xbyte<280; xbyte+=7) + { + int best_byte=0; + int best_error=2000000000; + int hibit; + int sr, sg, sb; + int r2, g2, b2; + int local_rerr=0, local_gerr=0, local_berr=0; + + for (hibit=0; hibit<2; hibit++) + { + int byte = hibit<<7; + int tot_error=0; + + for (x=xbyte; x> 16) & 0xFF) * brightness/256; + sg = ((pP[x] >> 8) & 0xFF) * brightness/256; + sb = ((pP[x] ) & 0xFF) * brightness/256; + sr += (this_rerr[x + 1] + local_rerr) / fs_scale; + sg += (this_gerr[x + 1] + local_gerr) / fs_scale; + sb += (this_berr[x + 1] + local_berr) / fs_scale; + + if (sr < 0) sr = 0; + else if (sr > maxval) sr = maxval; + if (sg < 0) sg = 0; + else if (sg > maxval) sg = maxval; + if (sb < 0) sb = 0; + else if (sb > maxval) sb = maxval; + + /* This is the color we'd get if we set the bit 1. For 0, + we get black */ + r2=a2_cmap[hibit][x&1][0]; + g2=a2_cmap[hibit][x&1][1]; + b2=a2_cmap[hibit][x&1][2]; + + /* + dist0 and dist1 are the error (Minkowski 2-metric + distances in the color space) for choosing 0 and + 1 respectively. 0 is black, 1 is the color r2,g2,b2. + */ + dist1= (sr-r2)*(sr-r2) + (sg-g2)*(sg-g2) + (sb-b2)*(sb-b2); + dist0= sr*sr + sg*sg + sb*sb; + + if (dist1>(x-xbyte))&1; + hibit=(best_byte>>7)&1; + + sr = (pP[x] >> 16) & 0xFF; + sg = (pP[x] >> 8) & 0xFF; + sb = (pP[x] ) & 0xFF; + sr += this_rerr[x + 1] / fs_scale; + sg += this_gerr[x + 1] / fs_scale; + sb += this_berr[x + 1] / fs_scale; + + if (sr < 0) sr = 0; + else if (sr > maxval) sr = maxval; + if (sg < 0) sg = 0; + else if (sg > maxval) sg = maxval; + if (sb < 0) sb = 0; + else if (sb > maxval) sb = maxval; + + r2=a2_cmap[hibit][x&1][0] * bit; + g2=a2_cmap[hibit][x&1][1] * bit; + b2=a2_cmap[hibit][x&1][2] * bit; + + pP[x] = (r2<<16) | (g2<<8) | (b2); + + /* Propagate Floyd-Steinberg error terms. */ + err = (sr - r2) * fs_scale; + this_rerr[x + 2] += (err * 7) / 16; + next_rerr[x ] += (err * 3) / 16; + next_rerr[x + 1] += (err * 5) / 16; + next_rerr[x + 2] += (err ) / 16; + err = (sg - g2) * fs_scale; + this_gerr[x + 2] += (err * 7) / 16; + next_gerr[x ] += (err * 3) / 16; + next_gerr[x + 1] += (err * 5) / 16; + next_gerr[x + 2] += (err ) / 16; + err = (sb - b2) * fs_scale; + this_berr[x + 2] += (err * 7) / 16; + next_berr[x ] += (err * 3) / 16; + next_berr[x + 1] += (err * 5) / 16; + next_berr[x + 2] += (err ) / 16; + } + + /* + And put the actual byte into out. + */ + + out[y*(w/7) + xbyte/7] = best_byte; + + } + + temp_err = this_rerr; + this_rerr = next_rerr; + next_rerr = temp_err; + temp_err = this_gerr; + this_gerr = next_gerr; + next_gerr = temp_err; + temp_err = this_berr; + this_berr = next_berr; + next_berr = temp_err; + } + + free (this_rerr); + free (next_rerr); + free (this_gerr); + free (next_gerr); + free (this_berr); + free (next_berr); + + for (y=0; y>16)&0xff; + unsigned int g = (pixels[y][x]>>8)&0xff; + unsigned int b = (pixels[y][x]>>0)&0xff; + fprintf(pipe, "%c%c%c", r, g, b); + } + fclose (pipe); + } +#endif +} + + +static unsigned char * +load_image (Display *dpy, Window window, char **image_filename_r) +{ + XWindowAttributes xgwa; + Pixmap p; + + int w = 280; + int h = 192; + XImage *image; + unsigned int *buf32 = (unsigned int *) calloc (w, h * 4); + unsigned char *buf8 = (unsigned char *) calloc (w/7, h); + + if (!buf32 || !buf8) + { + fprintf (stderr, "%s: out of memory (%dx%d)\n", progname, w, h); + exit (1); + } + + XGetWindowAttributes (dpy, window, &xgwa); + p = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth); + load_random_image (xgwa.screen, window, p, image_filename_r); + image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap); + XFreePixmap (dpy, p); + p = 0; + + /* Make sure the window's background is not set to None, and get the + grabbed bits (if any) off it as soon as possible. */ + XSetWindowBackground (dpy, window, + get_pixel_resource ("background", "Background", + dpy, xgwa.colormap)); + XClearWindow (dpy, window); + + /* Scale the XImage down to Apple][ size, and convert it to a 32bpp + image (regardless of whether it started as TrueColor/PseudoColor.) + */ + pick_a2_subimage (dpy, window, image, buf32, w, h); + + /* Then dither the 32bpp image to a 6-color Apple][ colormap. + */ + a2_dither (buf32, buf8, w, h); + + free (buf32); + return buf8; +} + + +char *progclass = "Apple2"; + +char *defaults [] = { + "*mode: random", + "*duration: 20", + ANALOGTV_DEFAULTS + 0 +}; + +XrmOptionDescRec options [] = { + { "-slideshow", ".mode", XrmoptionNoArg, "slideshow" }, + { "-basic", ".mode", XrmoptionNoArg, "basic" }, + { "-text", ".mode", XrmoptionNoArg, "text" }, + { "-program", ".program", XrmoptionSepArg, 0 }, + { "-duration", ".duration", XrmoptionSepArg, 0 }, + ANALOGTV_OPTIONS + { 0, 0, 0, 0 } +}; + +/* + TODO: this should load 10 images at startup time, then cycle through them + to avoid the pause while it loads. + */ + +void slideshow_controller(apple2_sim_t *sim, int *stepno, + double *next_actiontime) +{ + apple2_state_t *st=sim->st; + int i; + struct mydata { + int slideno; + int render_img_lineno; + u_char *render_img; + char *img_filename; + } *mine; + + if (!sim->controller_data) + sim->controller_data = calloc(sizeof(struct mydata),1); + mine=(struct mydata *) sim->controller_data; + + switch(*stepno) { + + case 0: + a2_invalidate(st); + a2_clear_hgr(st); + a2_cls(st); + sim->typing_rate = 0.3; + sim->dec->powerup=0.0; + + a2_goto(st, 0, 16); + a2_prints(st, "APPLE ]["); + a2_goto(st,23,0); + a2_printc(st,']'); + + *next_actiontime += 4.0; + *stepno=10; + + case 10: + mine->render_img = load_image (sim->dpy, sim->window, &mine->img_filename); + if (st->gr_mode) { + *stepno=30; + } else { + *stepno=20; + } + *next_actiontime += 3.0; + break; + + case 20: + sim->typing="HGR\n"; + *stepno=29; + break; + + case 29: + sim->printing="]"; + *stepno=30; + break; + + case 30: + st->gr_mode=A2_GR_HIRES; + if (mine->img_filename) { + char *basename, *tmp; + char *s; + + basename = tmp = strdup (mine->img_filename); + while (1) + { + char *slash = strchr(basename, '/'); + if (!slash || !slash[1]) break; + basename = slash+1; + } + { + char *dot=strchr(basename,'.'); + if (dot) *dot=0; + } + if (strlen(basename)>20) basename[20]=0; + for (s=basename; *s; s++) *s = toupper (*s); + sprintf(sim->typing_buf, "BLOAD %s\n", basename); + sim->typing = sim->typing_buf; + + free(tmp); + } else { + sim->typing = "BLOAD IMAGE\n"; + } + mine->render_img_lineno=0; + + *stepno=35; + break; + + case 35: + *next_actiontime += 0.7; + *stepno=40; + break; + + case 40: + if (mine->render_img_lineno>=192) { + sim->printing="]"; + sim->typing="POKE 49234,0\n"; + *stepno=50; + return; + } + + for (i=0; i<6 && mine->render_img_lineno<192; i++) { + a2_display_image_loading(st, mine->render_img, + mine->render_img_lineno++); + } + + /* The disk would have to seek every 13 sectors == 78 lines. + (This ain't no newfangled 16-sector operating system) */ + if ((mine->render_img_lineno%78)==0) { + *next_actiontime += 0.5; + } else { + *next_actiontime += 0.08; + } + break; + + case 50: + st->gr_mode |= A2_GR_FULL; + *stepno=60; + *next_actiontime += sim->delay; + break; + + case 60: + sim->printing="]"; + sim->typing="POKE 49235,0\n"; + *stepno=70; + break; + + case 70: + sim->printing="]"; + st->gr_mode &= ~A2_GR_FULL; + if (mine->render_img) { + free(mine->render_img); + mine->render_img=NULL; + } + if (mine->img_filename) { + free(mine->img_filename); + mine->img_filename=NULL; + } + *stepno=10; + break; + + case A2CONTROLLER_FREE: + free(mine->render_img); + free(mine->img_filename); + free(mine); + return; + + } +} + +struct terminal_controller_data { + FILE *pipe; + int pipe_id; + int input_available_p; + XtIntervalId timeout_id; + char curword[256]; + unsigned char lastc; + int fake_nl; + double last_emit_time; +}; + +static void +subproc_cb (XtPointer closure, int *source, XtInputId *id) +{ + struct terminal_controller_data *mine = + (struct terminal_controller_data *) closure; + mine->input_available_p = True; +} + +static void +launch_text_generator (struct terminal_controller_data *mine) +{ + char *oprogram = get_string_resource ("program", "Program"); + char *program; + + if (!oprogram || !*oprogram) + oprogram = FORTUNE_PROGRAM; + + program = (char *) malloc (strlen (oprogram) + 10); + + strcpy (program, "( "); + strcat (program, oprogram); + strcat (program, " ) 2>&1"); + + if (mine->pipe) abort(); + if ((mine->pipe = popen (program, "r"))) + { + if (mine->pipe_id) abort(); + mine->pipe_id = + XtAppAddInput (app, fileno (mine->pipe), + (XtPointer) (XtInputReadMask | XtInputExceptMask), + subproc_cb, (XtPointer) mine); + } + else + { + perror (program); + } +} + +static void +relaunch_generator_timer (XtPointer closure, XtIntervalId *id) +{ + struct terminal_controller_data *mine = + (struct terminal_controller_data *) closure; + mine->timeout_id=0; + launch_text_generator (mine); +} + +static void +terminal_closegen(struct terminal_controller_data *mine) +{ + if (mine->pipe_id) { + XtRemoveInput (mine->pipe_id); + mine->pipe_id = 0; + } + if (mine->pipe) { + pclose (mine->pipe); + mine->pipe = 0; + } + if (mine->timeout_id) { + XtRemoveTimeOut(mine->timeout_id); + mine->timeout_id=0; + } +} + +static int +terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n) +{ + int rc; + if (mine->fake_nl) { + buf[0]='\n'; + mine->fake_nl=0; + return 1; + } + + if (!mine->input_available_p) return 0; + + rc=read (fileno (mine->pipe), (void *) buf, n); + if (rc>0) mine->lastc=buf[rc-1]; + + if (rc<=0) + { + terminal_closegen(mine); + + if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */ + mine->fake_nl=1; + } + + /* Set up a timer to re-launch the subproc in a bit. */ + mine->timeout_id = + XtAppAddTimeOut(app, subproc_relaunch_delay, + relaunch_generator_timer, + (XtPointer) mine); + } + + mine->input_available_p = False; + + return rc; +} + + +/* + It's fun to put things like "gdb" as the command. For one, it's + amusing how the standard mumble (version, no warranty, it's + GNU/Linux dammit) occupies an entire screen on the Apple ][. +*/ + +void +terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime) +{ + apple2_state_t *st=sim->st; + int c; + int i; + + struct terminal_controller_data *mine; + if (!sim->controller_data) + sim->controller_data=calloc(sizeof(struct terminal_controller_data),1); + mine=(struct terminal_controller_data *) sim->controller_data; + + switch(*stepno) { + + case 0: + if (random()%2) + st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's + showing text */ + a2_cls(st); + a2_goto(st,0,16); + a2_prints(st, "APPLE ]["); + a2_goto(st,2,0); + + if (! mine->pipe) + launch_text_generator(mine); + + *next_actiontime += 4.0; + *stepno = 10; + break; + + case 10: + { + unsigned char buf[5]; + int nr,nwant; + double elapsed; + + elapsed=sim->curtime - mine->last_emit_time; + mine->last_emit_time=sim->curtime; + nwant=elapsed*25.0; + if (elapsed>1.0) nwant=1; + if (nwant<1) nwant=1; + if (nwant>4) nwant=4; + + nr=terminal_read(mine, buf, nwant); + for (i=0; i= 'a' && c <= 'z') /* upcase lower-case chars */ + { + a2_printc(st, c&0xDF); + } + else if ((c >= 'A'+128) || /* upcase and blink */ + (c < ' ' && c != 014 && /* high-bit & ctl chrs */ + c != '\r' && c != '\n' && c!='\t')) + { + a2_printc(st, (c & 0x1F) | 0x80); + } + else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */ + { + a2_printc(st, c | 0x80); + } + else { + a2_printc(st, c); + } + } + } + break; + + case A2CONTROLLER_FREE: + terminal_closegen(mine); + free(mine); + return; + } +} + +struct basic_controller_data { + int prog_line; + int x,y,k; + char **progtext; + int progstep; + char *rep_str; + int rep_pos; + double prog_start_time; + char error_buf[256]; +}; + +/* + Adding more programs is easy. Just add a listing here and to all_programs, + then add the state machine to actually execute it to basic_controller. + */ +static char *moire_program[]={ + "10 HGR2\n", + "20 FOR Y = 0 TO 191 STEP 2\n", + "30 HCOLOR=4 : REM BLACK\n", + "40 HLINE 0,191-Y TO 279,Y\n", + "60 HCOLOR=7 : REM WHITE\n", + "80 HLINE 0,190-Y TO 279,Y+1\n", + "90 NEXT Y\n", + "100 FOR X = 0 TO 279 STEP 3\n", + "110 HCOLOR=4\n", + "120 HLINE 279-X,0 TO X,192\n", + "140 HCOLOR=7\n", + "150 HLINE 278-X,0 TO X+1,192\n", + "160 NEXT X\n", + NULL +}; + +static char *sinewave_program[] = { + "10 HGR\n", + "25 K=0\n", + "30 FOR X = 0 TO 279\n", + "32 HCOLOR= 0\n", + "35 HLINE X,0 TO X,159\n", + "38 HCOLOR= 3\n", + "40 Y = 80 + SIN(15*(X-K)/279)\n", + "50 HPLOT X,Y\n", + "60 NEXT X\n", + "70 K=K+4\n", + "80 GOTO 30\n", + NULL +}; + +#if 0 +static char *dumb_program[]={ + "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n", + "20 GOTO 10\n", + NULL +}; +#endif + +static char *random_lores_program[]={ + "1 REM APPLE ][ SCREEN SAVER\n", + "10 GR\n", + "100 COLOR= RND(1)*16\n", + + "110 X=RND(1)*40\n", + "120 Y1=RND(1)*48\n", + "130 Y2=RND(1)*48\n", + "140 FOR Y = Y1 TO Y2\n", + "150 PLOT X,Y\n", + "160 NEXT Y\n", + + "210 Y=RND(1)*48\n", + "220 X1=RND(1)*40\n", + "230 X2=RND(1)*40\n", + "240 FOR X = X1 TO X2\n", + "250 PLOT X,Y\n", + "260 NEXT X\n", + "300 GOTO 100\n", + + NULL +}; + +static char typo_map[256]; + +int make_typo(char *out_buf, char *orig, char *err_buf) +{ + int i,j; + int errc; + int success=0; + err_buf[0]=0; + + typo_map['A']='Q'; + typo_map['S']='A'; + typo_map['D']='S'; + typo_map['F']='G'; + typo_map['G']='H'; + typo_map['H']='J'; + typo_map['J']='H'; + typo_map['K']='L'; + typo_map['L']=';'; + + typo_map['Q']='1'; + typo_map['W']='Q'; + typo_map['E']='3'; + typo_map['R']='T'; + typo_map['T']='Y'; + typo_map['Y']='U'; + typo_map['U']='Y'; + typo_map['I']='O'; + typo_map['O']='P'; + typo_map['P']='['; + + typo_map['Z']='X'; + typo_map['X']='C'; + typo_map['C']='V'; + typo_map['V']='C'; + typo_map['B']='N'; + typo_map['N']='B'; + typo_map['M']='N'; + typo_map[',']='.'; + typo_map['.']=','; + + typo_map['!']='1'; + typo_map['@']='2'; + typo_map['#']='3'; + typo_map['$']='4'; + typo_map['%']='5'; + typo_map['^']='6'; + typo_map['&']='7'; + typo_map['*']='8'; + typo_map['(']='9'; + typo_map[')']='0'; + + typo_map['1']='Q'; + typo_map['2']='W'; + typo_map['3']='E'; + typo_map['4']='R'; + typo_map['5']='T'; + typo_map['6']='Y'; + typo_map['7']='U'; + typo_map['8']='I'; + typo_map['9']='O'; + typo_map['0']='-'; + + strcpy(out_buf, orig); + for (i=0; out_buf[i]; i++) { + char *p = out_buf+i; + + if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M') + break; + + if (isalpha(p[0]) && + isalpha(p[1]) && + p[0] != p[1] && + random()%15==0) + { + int tmp=p[1]; + p[1]=p[0]; + p[0]=tmp; + success=1; + sprintf(err_buf,"?SYNTAX ERROR\n"); + break; + } + + if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(u_char)p[0]])) { + int remain=strlen(p); + int past=random()%(remain-2)+1; + memmove(p+past+past, p, remain+1); + p[0]=errc; + for (j=0; jst; + int i; + + struct basic_controller_data *mine; + if (!sim->controller_data) + sim->controller_data=calloc(sizeof(struct basic_controller_data),1); + mine=(struct basic_controller_data *) sim->controller_data; + + switch (*stepno) { + case 0: + st->gr_mode=0; + a2_cls(st); + a2_goto(st,0,16); + a2_prints(st, "APPLE ]["); + a2_goto(st,23,0); + a2_printc(st,']'); + sim->typing_rate=0.2; + + i=random()%countof(all_programs); + mine->progtext=all_programs[i].progtext; + mine->progstep=all_programs[i].progstep; + mine->prog_line=0; + + *next_actiontime += 1.0; + *stepno=10; + break; + + case 10: + if (st->cursx==0) a2_printc(st,']'); + if (mine->progtext[mine->prog_line]) { + if (random()%4==0) { + int err=make_typo(sim->typing_buf, + mine->progtext[mine->prog_line], + mine->error_buf); + sim->typing=sim->typing_buf; + if (err) { + *stepno=11; + } else { + mine->prog_line++; + } + } else { + sim->typing=mine->progtext[mine->prog_line++]; + } + } else { + *stepno=15; + } + break; + + case 11: + sim->printing=mine->error_buf; + *stepno=12; + break; + + case 12: + if (st->cursx==0) a2_printc(st,']'); + *next_actiontime+=1.0; + *stepno=10; + break; + + case 15: + sim->typing="RUN\n"; + mine->y=0; + mine->x=0; + mine->k=0; + mine->prog_start_time=*next_actiontime; + *stepno=mine->progstep; + break; + + /* moire_program */ + case 100: + st->gr_mode=A2_GR_HIRES|A2_GR_FULL; + for (i=0; i<24 && mine->y<192; i++) + { + a2_hline(st, 4, 0, 191-mine->y, 279, mine->y); + a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1); + mine->y += 2; + } + if (mine->y>=192) { + mine->x = 0; + *stepno = 110; + } + break; + + case 110: + for (i=0; i<24 && mine->x<280; i++) + { + a2_hline(st, 4, 279-mine->x, 0, mine->x, 192); + a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192); + mine->x+=3; + } + if (mine->x >= 280) *stepno=120; + break; + + case 120: + if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999; + break; + + /* dumb_program */ + case 200: + mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!"; + for (i=0; i<30; i++) { + a2_prints(st, mine->rep_str); + } + *stepno=210; + break; + + case 210: + i=random()%strlen(mine->rep_str); + while (mine->rep_pos != i) { + a2_printc(st, mine->rep_str[mine->rep_pos]); + mine->rep_pos++; + if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0; + } + if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999; + break; + + /* sinewave_program */ + case 400: + st->gr_mode=A2_GR_HIRES; + *stepno=410; + break; + + case 410: + for (i=0; i<48; i++) { + int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0)); + a2_hline(st, 0, mine->x, 0, mine->x, 159); + a2_hplot(st, 3, mine->x, y); + mine->x += 1; + if (mine->x>=279) { + mine->x=0; + mine->k+=4; + } + } + if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999; + break; + + case 420: + a2_prints(st, "]"); + *stepno=999; + break; + + /* random_lores_program */ + case 500: + st->gr_mode=A2_GR_LORES|A2_GR_FULL; + a2_clear_gr(st); + *stepno=510; + + case 510: + for (i=0; i<10; i++) { + int color,x,y,x1,x2,y1,y2; + + color=random()%15; + x=random()%40; + y1=random()%48; + y2=random()%48; + for (y=y1; y mine->prog_start_time+sim->delay) *stepno=999; + break; + + case 999: + *stepno=0; + break; + + case A2CONTROLLER_FREE: + free(mine); + break; + } + +} + +void (*controllers[])(apple2_sim_t *sim, int *stepno, + double *next_actiontime) = { + slideshow_controller, + terminal_controller, + basic_controller +}; + +void +screenhack (Display *dpy, Window window) +{ + int duration = get_integer_resource ("duration", "Integer"); + char *s; + void (*controller)(apple2_sim_t *sim, int *stepno, double *next_actiontime); + + if (duration < 1) duration = 1; + + s = get_string_resource ("mode", "Mode"); + if (!s || !*s || !strcasecmp(s, "random")) + controller = controllers[random() % (countof(controllers))]; + else if (!strcasecmp(s, "text")) + controller = terminal_controller; + else if (!strcasecmp(s, "slideshow")) + controller = slideshow_controller; + else if (!strcasecmp(s, "basic")) + controller = basic_controller; + else + { + fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n", + progname, s); + exit (1); + } + + if (!get_boolean_resource ("root", "Boolean")) + { + XWindowAttributes xgwa; + XGetWindowAttributes (dpy, window, &xgwa); + XSelectInput (dpy, window, + xgwa.your_event_mask | + KeyPressMask | ButtonPressMask | ExposureMask); + } + + apple2 (dpy, window, duration, controller); + XSync (dpy, False); +} diff --git a/hacks/apple2.c b/hacks/apple2.c new file mode 100644 index 00000000..184ff68f --- /dev/null +++ b/hacks/apple2.c @@ -0,0 +1,790 @@ +/* xscreensaver, Copyright (c) 1998-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. + * + * Apple ][ CRT simulator, by Trevor Blackwell + * with additional work by Jamie Zawinski + */ + +#include +#include "screenhack.h" +#include "apple2.h" +#include +#include +#include + +#ifdef HAVE_XSHM_EXTENSION +#include "xshm.h" +#endif + +#define DEBUG + +extern XtAppContext app; + +/* + * Implementation notes + * + * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it + * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the + * same memory as the text screen. Each could be one of 16 colors. Hires gave + * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were + * orange or green depending on the setting of the high bit in each byte. + * + * The graphics modes could also have 4 lines of text at the bottom. This was + * fairly unreadable if you had a color monitor. + * + * Each mode had 2 different screens using different memory space. In hires + * mode this was sometimes used for double buffering, but more often the lower + * screen was full of code/data and the upper screen was used for display, so + * you got random garbage on the screen. + * + * The text font is based on X's standard 6x10 font, with a few tweaks like + * putting a slash across the zero. + * + * To use this, you'll call apple2(display, window, duration, + * controller) where the function controller defines what will happen. + * See bsod.c and apple2-main.c for example controllers. The + * controller function gets called whenever the machine ready to start + * something new. By setting sim->printing or sim->typing, it'll be + * busy for some time spitting characters out one at a time. By + * setting *next_actiontime+=X.X, it'll pause and just update the screen + * for that long before calling the controller function again. + * + * By setting stepno to A2CONTROLLER_DONE, the loop will end. It will also end + * after the time specified by the delay parameter. In either case, it calls + * the controller with stepno==A2CONTROLLER_FREE to allow it to release any + * memory. + * + * The void* apple2_sim_t::controller_data is for the use of the controller. + * It will be initialize to NULL, and the controller can store its own state + * there. + * + */ + +void +a2_scroll(apple2_state_t *st) +{ + int i; + for (i=0; i<23; i++) { + memcpy(st->textlines[i],st->textlines[i+1],40); + } + memset(st->textlines[23],0xe0,40); +} + +void +a2_printc(apple2_state_t *st, char c) +{ + st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */ + + if (c == '\n') /* ^J == NL */ + { + if (st->cursy==23) + { + a2_scroll(st); + } + else + { + st->cursy++; + } + st->cursx=0; + } + else if (c == 014) /* ^L == CLS, Home */ + { + a2_cls(st); + a2_goto(st,0,0); + } + else if (c == '\t') /* ^I == tab */ + { + a2_goto(st, st->cursy, (st->cursx+8)&~7); + } + else if (c == 010) /* ^H == backspace */ + { + st->textlines[st->cursy][st->cursx]=0xe0; + a2_goto(st, st->cursy, st->cursx-1); + } + else if (c == '\r') /* ^M == CR */ + { + st->cursx=0; + } + else + { + st->textlines[st->cursy][st->cursx]=c ^ 0xc0; + st->cursx++; + if (st->cursx==40) { + if (st->cursy==23) { + a2_scroll(st); + } else { + st->cursy++; + } + st->cursx=0; + } + } + + st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */ +} + +void +a2_prints(apple2_state_t *st, char *s) +{ + while (*s) a2_printc(st, *s++); +} + +void +a2_goto(apple2_state_t *st, int r, int c) +{ + st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */ + st->cursy=r; + st->cursx=c; + st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */ +} + +void +a2_cls(apple2_state_t *st) +{ + int i; + for (i=0; i<24; i++) { + memset(st->textlines[i],0xe0,40); + } +} + +void +a2_clear_gr(apple2_state_t *st) +{ + int i; + for (i=0; i<24; i++) { + memset(st->textlines[i],0x00,40); + } +} + +void +a2_clear_hgr(apple2_state_t *st) +{ + int i; + for (i=0; i<192; i++) { + memset(st->hireslines[i],0,40); + } +} + +void +a2_invalidate(apple2_state_t *st) +{ +} + +void +a2_poke(apple2_state_t *st, int addr, int val) +{ + + if (addr>=0x400 && addr<0x800) { + /* text memory */ + int row=((addr&0x380)/0x80) + ((addr&0x7f)/0x28)*8; + int col=(addr&0x7f)%0x28; + if (row<24 && col<40) { + st->textlines[row][col]=val; + if (!(st->gr_mode&(A2_GR_HIRES)) || + (!(st->gr_mode&(A2_GR_FULL)) && row>=20)) { + } + } + } + else if (addr>=0x2000 && addr<0x4000) { + int row=(((addr&0x1c00) / 0x400) * 1 + + ((addr&0x0380) / 0x80) * 8 + + ((addr&0x0078) / 0x28) * 64); + int col=((addr&0x07f)%0x28); + if (row<192 && col<40) { + st->hireslines[row][col]=val; + if (st->gr_mode&A2_GR_HIRES) { + } + } + } +} + +void +a2_hplot(apple2_state_t *st, int hcolor, int x, int y) +{ + int highbit,run; + + highbit=((hcolor<<5)&0x80) ^ 0x80; /* capture bit 2 into bit 7 */ + + if (y<0 || y>=192 || x<0 || x>=280) return; + + for (run=0; run<2 && x<280; run++) { + u_char *vidbyte = &st->hireslines[y][x/7]; + u_char whichbit=1<<(x%7); + int masked_bit; + + *vidbyte = (*vidbyte & 0x7f) | highbit; + + /* use either bit 0 or 1 of hcolor for odd or even pixels */ + masked_bit = (hcolor>>(1-(x&1)))&1; + + /* Set whichbit to 1 or 0 depending on color */ + *vidbyte = (*vidbyte & ~whichbit) | (masked_bit ? whichbit : 0); + + x++; + } +} + +void +a2_hline(apple2_state_t *st, int hcolor, int x1, int y1, int x2, int y2) +{ + int dx,dy,incx,incy,x,y,balance; + + /* Bresenham's line drawing algorithm */ + + if (x2>=x1) { + dx=x2-x1; + incx=1; + } else { + dx=x1-x2; + incx=-1; + } + if (y2>=y1) { + dy=y2-y1; + incy=1; + } else { + dy=y1-y2; + incy=-1; + } + + x=x1; y=y1; + + if (dx>=dy) { + dy*=2; + balance=dy-dx; + dx*=2; + while (x!=x2) { + a2_hplot(st, hcolor, x, y); + if (balance>=0) { + y+=incy; + balance-=dx; + } + balance+=dy; + x+=incx; + } + a2_hplot(st, hcolor, x, y); + } else { + dx*=2; + balance=dx-dy; + dy*=2; + while (y!=y2) { + a2_hplot(st, hcolor, x, y); + if (balance>=0) { + x+=incx; + balance-=dy; + } + balance+=dx; + y+=incy; + } + a2_hplot(st, hcolor, x, y); + } +} + +void +a2_plot(apple2_state_t *st, int color, int x, int y) +{ + int textrow=y/2; + u_char byte; + + if (x<0 || x>=40 || y<0 || y>=48) return; + + byte=st->textlines[textrow][x]; + if (y&1) { + byte = (byte&0xf0) | (color&0x0f); + } else { + byte = (byte&0x0f) | ((color&0x0f)<<4); + } + st->textlines[textrow][x]=byte; +} + +void +a2_display_image_loading(apple2_state_t *st, unsigned char *image, + int lineno) +{ + /* + When loading images,it would normally just load the big binary + dump into screen memory while you watched. Because of the way + screen memory was laid out, it wouldn't load from the top down, + but in a funny interleaved way. You should call this with lineno + increasing from 0 thru 191 over a period of a few seconds. + */ + + int row=(((lineno / 24) % 8) * 1 + + ((lineno / 3 ) % 8) * 8 + + ((lineno / 1 ) % 3) * 64); + + memcpy (st->hireslines[row], &image[row * 40], 40); +} + +/* + Simulate plausible initial memory contents for running a program. +*/ +void +a2_init_memory_active(apple2_sim_t *sim) +{ + int i,j,x,y,c; + int addr=0; + apple2_state_t *st=sim->st; + + while (addr<0x4000) { + int n; + + switch (random()%4) { + case 0: + case 1: + n=random()%500; + for (i=0; itext_im->width); + for (i=0; i<100; i++) { + for (y=0; y<8; y++) { + c=0; + for (j=0; j<8; j++) { + c |= XGetPixel(sim->text_im, (x+j)%sim->text_im->width, y)<text_im->width); + } + break; + + case 3: + if (addr>0x2000) { + n=random()%200; + for (i=0; i */ 0x80d3, 0x00d4, 0x84d4, 0x04d5, 0x88d5, 0x08d6, 0x8cd6, 0x0cd7, + 0x90d5, 0x10d6, + 0x94d4, 0x14d5, 0x98d3, 0x18d4, + /* Fix @ */ 0x88e3, 0x08e4, 0x8ce4, 0x98e5, + /* Fix B */ 0x84ef, 0x04f0, 0x88ef, 0x08f0, 0x8cef, 0x90ef, 0x10f0, 0x94ef, + 0x14f0, + /* Fix D */ 0x84fd, 0x04fe, 0x88fd, 0x08fe, 0x8cfd, 0x0cfe, 0x90fd, 0x10fe, + 0x94fd, 0x14fe, + /* Fix G */ 0x8116, 0x0516, 0x9916, + /* Fix J */ 0x0129, 0x012a, 0x052a, 0x852b, 0x092a, 0x892b, 0x0d2a, 0x8d2b, + 0x112a, 0x912b, + 0x152a, 0x952b, 0x992a, + /* Fix M */ 0x853d, 0x853f, 0x093d, 0x893e, 0x093f, + /* Fix Q */ 0x915a, 0x155a, 0x955b, 0x155c, 0x195b, 0x995c, 0x1d5c, + /* Fix V */ 0x8d7b, 0x0d7c, 0x0d7e, 0x8d7f, 0x917b, 0x117c, 0x117e, 0x917f, + /* Fix [ */ 0x819e, 0x81a2, 0x859e, 0x899e, 0x8d9e, 0x919e, 0x959e, 0x999e, + 0x99a2, + /* Fix \ */ 0x01a5, 0x19a9, + /* Fix ] */ 0x81ac, 0x81b0, 0x85b0, 0x89b0, 0x8db0, 0x91b0, 0x95b0, 0x99ac, + 0x99b0, + /* Fix ^ */ 0x01b5, 0x05b4, 0x05b6, 0x09b3, 0x89b5, 0x09b7, 0x8db4, 0x8db6, + 0x91b3, 0x91b7, + /* Fix _ */ 0x9db9, 0x9dbf, + 0, +}; + +static void +a2_make_font(apple2_sim_t *sim) +{ + /* + Generate the font. It used a 5x7 font which looks a lot like the standard X + 6x10 font, with a few differences. So we render up all the uppercase + letters of 6x10, and make a few tweaks (like putting a slash across the + zero) according to fixfont. + */ + + int i; + const char *def_font="6x10"; + XFontStruct *font; + Pixmap text_pm; + GC gc; + XGCValues gcv; + + font = XLoadQueryFont (sim->dpy, def_font); + if (!font) { + fprintf(stderr, "%s: can't load font %s\n", progname, def_font); + abort(); + } + + text_pm=XCreatePixmap(sim->dpy, sim->window, 64*7, 8, sim->dec->xgwa.depth); + + memset(&gcv, 0, sizeof(gcv)); + gcv.foreground=1; + gcv.background=0; + gcv.font=font->fid; + gc=XCreateGC(sim->dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv); + + XSetForeground(sim->dpy, gc, 0); + XFillRectangle(sim->dpy, text_pm, gc, 0, 0, 64*7, 8); + XSetForeground(sim->dpy, gc, 1); + for (i=0; i<64; i++) { + char c=32+i; + int x=7*i+1; + int y=7; + if (c=='0') { + c='O'; + XDrawString(sim->dpy, text_pm, gc, x, y, &c, 1); + } else { + XDrawString(sim->dpy, text_pm, gc, x, y, &c, 1); + } + } + sim->text_im = XGetImage(sim->dpy, text_pm, 0, 0, 64*7, 8, ~0L, ZPixmap); + XFreeGC(sim->dpy, gc); + XFreePixmap(sim->dpy, text_pm); + + for (i=0; a2_fixfont[i]; i++) { + XPutPixel(sim->text_im, a2_fixfont[i]&0x3ff, + (a2_fixfont[i]>>10)&0xf, + (a2_fixfont[i]>>15)&1); + } +} + + +void +apple2(Display *dpy, Window window, int delay, + void (*controller)(apple2_sim_t *sim, + int *stepno, + double *next_actiontime)) +{ + int i,textrow,row,col,stepno; + int c; + double next_actiontime; + apple2_sim_t *sim; + + sim=(apple2_sim_t *)calloc(1,sizeof(apple2_state_t)); + sim->dpy = dpy; + sim->window = window; + sim->delay = delay; + + sim->st = (apple2_state_t *)calloc(1,sizeof(apple2_state_t)); + sim->dec = analogtv_allocate(dpy, window); + sim->dec->event_handler = screenhack_handle_event; + sim->inp = analogtv_input_allocate(); + + sim->reception.input = sim->inp; + sim->reception.level = 1.0; + + sim->prompt=']'; + + if (random()%4==0 && !sim->dec->use_cmap && sim->dec->use_color && sim->dec->visbits>=8) { + sim->dec->flutter_tint=1; + } + else if (random()%3==0) { + sim->dec->flutter_horiz_desync=1; + } + sim->typing_rate = 1.0; + + analogtv_set_defaults(sim->dec, ""); + sim->dec->squish_control=0.05; + analogtv_setup_sync(sim->inp, 1, 0); + + + a2_make_font(sim); + + stepno=0; + a2_goto(sim->st,23,0); + + if (random()%2==0) sim->basetime_tv.tv_sec -= 1; /* random blink phase */ + next_actiontime=0.0; + + sim->curtime=0.0; + next_actiontime=sim->curtime; + (*controller)(sim, &stepno, &next_actiontime); + +# ifdef GETTIMEOFDAY_TWO_ARGS + gettimeofday(&sim->basetime_tv, NULL); +# else + gettimeofday(&sim->basetime_tv); +# endif + + while (1) { + double blinkphase; + + { + struct timeval curtime_tv; +# ifdef GETTIMEOFDAY_TWO_ARGS + struct timezone tzp; + gettimeofday(&curtime_tv, &tzp); +# else + gettimeofday(&curtime_tv); +# endif + sim->curtime=(curtime_tv.tv_sec - sim->basetime_tv.tv_sec) + + 0.000001*(curtime_tv.tv_usec - sim->basetime_tv.tv_usec); + if (sim->curtime > sim->dec->powerup) + sim->dec->powerup=sim->curtime; + } + + if (analogtv_handle_events(sim->dec)) { + sim->typing=NULL; + sim->printing=NULL; + stepno=A2CONTROLLER_FREE; + next_actiontime = sim->curtime; + (*controller)(sim, &stepno, &next_actiontime); + stepno=0; + sim->controller_data=NULL; + sim->st->gr_mode=0; + continue; + } + + blinkphase=sim->curtime/0.8; + + /* The blinking rate was controlled by 555 timer with a resistor/capacitor + time constant. Because the capacitor was electrolytic, the flash rate + varied somewhat between machines. I'm guessing 1.6 seconds/cycle was + reasonable. (I soldered a resistor in mine to make it blink faster.) */ + i=sim->st->blink; + sim->st->blink=((int)blinkphase)&1; + if (sim->st->blink!=i && !(sim->st->gr_mode&A2_GR_FULL)) { + int downcounter=0; + /* For every row with blinking text, set the changed flag. This basically + works great except with random screen garbage in text mode, when we + end up redrawing the whole screen every second */ + for (row=(sim->st->gr_mode ? 20 : 0); row<24; row++) { + for (col=0; col<40; col++) { + c=sim->st->textlines[row][col]; + if ((c & 0xc0) == 0x40) { + downcounter=4; + break; + } + } + if (downcounter>0) { + downcounter--; + } + } + } + + if (sim->printing) { + int nlcnt=0; + while (*sim->printing) { + if (*sim->printing=='\001') { /* pause */ + sim->printing++; + break; + } + else if (*sim->printing=='\n') { + a2_printc(sim->st,*sim->printing); + sim->printing++; + nlcnt++; + if (nlcnt>=2) break; + } + else { + a2_printc(sim->st,*sim->printing); + sim->printing++; + } + } + if (!*sim->printing) sim->printing=NULL; + } + else if (sim->curtime >= next_actiontime) { + if (sim->typing) { + + int c; + /* If we're in the midst of typing a string, emit a character with + random timing. */ + c =*sim->typing++; + if (c==0) { + sim->typing=NULL; + } + else { + a2_printc(sim->st, c); + if (c=='\r' || c=='\n') { + next_actiontime = sim->curtime; + } + else if (c==010) { + next_actiontime = sim->curtime + 0.1; + } + else { + next_actiontime = (sim->curtime + + (((random()%1000)*0.001 + 0.3) * + sim->typing_rate)); + } + } + } else { + next_actiontime=sim->curtime; + + (*controller)(sim, &stepno, &next_actiontime); + if (stepno==A2CONTROLLER_DONE) goto finished; + + } + } + + + analogtv_setup_sync(sim->inp, sim->st->gr_mode? 1 : 0, 0); + analogtv_setup_frame(sim->dec); + + for (textrow=0; textrow<24; textrow++) { + for (row=textrow*8; rowinp->signal[row+ANALOGTV_TOP+4][ANALOGTV_PIC_START+100]; + + if ((sim->st->gr_mode&A2_GR_HIRES) && + (row<160 || (sim->st->gr_mode&A2_GR_FULL))) { + + /* Emulate the mysterious pink line, due to a bit getting + stuck in a shift register between the end of the last + row and the beginning of this one. */ + if ((sim->st->hireslines[row][0] & 0x80) && + (sim->st->hireslines[row][39]&0x40)) { + pp[-1]=ANALOGTV_WHITE_LEVEL; + } + + for (col=0; col<40; col++) { + u_char b=sim->st->hireslines[row][col]; + int shift=(b&0x80)?0:1; + + /* Each of the low 7 bits in hires mode corresponded to 2 dot + clocks, shifted by one if the high bit was set. */ + for (i=0; i<7; i++) { + pp[shift+1] = pp[shift] = (((b>>i)&1) + ?ANALOGTV_WHITE_LEVEL + :ANALOGTV_BLACK_LEVEL); + pp+=2; + } + } + } + else if ((sim->st->gr_mode&A2_GR_LORES) && + (row<160 || (sim->st->gr_mode&A2_GR_FULL))) { + for (col=0; col<40; col++) { + u_char nib=((sim->st->textlines[textrow][col] >> (((row/4)&1)*4)) + & 0xf); + /* The low or high nybble was shifted out one bit at a time. */ + for (i=0; i<14; i++) { + *pp = (((nib>>((col*14+i)&3))&1) + ?ANALOGTV_WHITE_LEVEL + :ANALOGTV_BLACK_LEVEL); + pp++; + } + } + } + else { + for (col=0; col<40; col++) { + int rev; + c=sim->st->textlines[textrow][col]&0xff; + /* hi bits control inverse/blink as follows: + 0x00: inverse + 0x40: blink + 0x80: normal + 0xc0: normal */ + rev=!(c&0x80) && (!(c&0x40) || sim->st->blink); + + for (i=0; i<7; i++) { + unsigned long pix=XGetPixel(sim->text_im, + ((c&0x3f)^0x20)*7+i, + row%8); + pp[1] = pp[2] = ((pix^rev) + ?ANALOGTV_WHITE_LEVEL + :ANALOGTV_BLACK_LEVEL); + pp+=2; + } + } + } + } + } + analogtv_init_signal(sim->dec, 0.02); + analogtv_reception_update(&sim->reception); + analogtv_add_signal(sim->dec, &sim->reception); + analogtv_draw(sim->dec); + } + + finished: + + stepno=A2CONTROLLER_FREE; + (*controller)(sim, &stepno, &next_actiontime); + + XSync(sim->dpy, False); + XClearWindow(sim->dpy, sim->window); +} + +void +a2controller_test(apple2_sim_t *sim, int *stepno, double *next_actiontime) +{ + int row,col; + + switch(*stepno) { + case 0: + a2_invalidate(sim->st); + /* + For testing color rendering. The spec is: + red grn blu + 0 black 0 0 0 + 1 red 227 30 96 + 2 dk blue 96 78 189 + 3 purple 255 68 253 + 4 dk green 0 163 96 + 5 gray 156 156 156 + 6 med blue 20 207 253 + 7 lt blue 208 195 255 + 8 brown 96 114 3 + 9 orange 255 106 60 + 10 grey 156 156 156 + 11 pink 255 160 208 + 12 lt green 20 245 60 + 13 yellow 208 221 141 + 14 aqua 114 255 208 + 15 white 255 255 255 + */ + sim->st->gr_mode=A2_GR_LORES; + for (row=0; row<24; row++) { + for (col=0; col<40; col++) { + sim->st->textlines[row][col]=(row&15)*17; + } + } + *next_actiontime+=0.4; + *stepno=99; + break; + + case 99: + if (sim->curtime > 10) *stepno=-1; + break; + } +} diff --git a/hacks/apple2.h b/hacks/apple2.h new file mode 100644 index 00000000..f445b3fe --- /dev/null +++ b/hacks/apple2.h @@ -0,0 +1,111 @@ +/* xscreensaver, Copyright (c) 1998-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. + * + * Apple ][ CRT simulator, by Trevor Blackwell + * with additional work by Jamie Zawinski + */ + +#ifndef __XSCREENSAVER_APPLE_II__ +#define __XSCREENSAVER_APPLE_II__ + +#include "analogtv.h" + + +typedef struct apple2_state { + u_char hireslines[192][40]; + u_char textlines[24][40]; + int gr_text; + enum { + A2_GR_FULL=1, + A2_GR_LORES=2, + A2_GR_HIRES=4 + } gr_mode; + int cursx; + int cursy; + int blink; + +} apple2_state_t; + + +typedef struct apple2_sim_s { + + void *controller_data; + + apple2_state_t *st; + + analogtv *dec; + analogtv_input *inp; + analogtv_reception reception; + + char *typing; + char typing_buf[1024]; + double typing_rate; + + char *printing; + char printing_buf[1024]; + + char prompt; + + Display *dpy; + Window window; + XWindowAttributes xgwa; + XImage *text_im; + + struct timeval basetime_tv; + double curtime; + double delay; +} apple2_sim_t; + + +enum { + A2_HCOLOR_BLACK=0, + A2_HCOLOR_GREEN=1, + A2_HCOLOR_PURPLE=2, + A2_HCOLOR_WHITE=3, + A2_HCOLOR_ALTBLACK=4, + A2_HCOLOR_RED=5, + A2_HCOLOR_BLUE=6, + A2_HCOLOR_ALTWHITE=7 + }; + +enum { + A2CONTROLLER_DONE=-1, + A2CONTROLLER_FREE=-2 +}; + +extern void +apple2(Display *dpy, Window window, int delay, + void (*)(apple2_sim_t *sim, int *stepno, double *next_actiontime)); + + +void a2_poke(apple2_state_t *st, int addr, int val); +void a2_goto(apple2_state_t *st, int r, int c); +void a2_cls(apple2_state_t *st); +void a2_invalidate(apple2_state_t *st); + +void a2_add_disk_item(apple2_state_t *st, char *name, u_char *data, + int len, char type); +void a2_scroll(apple2_state_t *st); +void a2_printc(apple2_state_t *st, char c); +void a2_prints(apple2_state_t *st, char *s); +void a2_goto(apple2_state_t *st, int r, int c); +void a2_cls(apple2_state_t *st); +void a2_clear_hgr(apple2_state_t *st); +void a2_clear_gr(apple2_state_t *st); +void a2_invalidate(apple2_state_t *st); +void a2_poke(apple2_state_t *st, int addr, int val); +void a2_display_image_loading(apple2_state_t *st, unsigned char *image, + int lineno); +void a2_init_memory_active(apple2_sim_t *sim); +void a2_hplot(apple2_state_t *st, int hcolor, int x, int y); +void a2_hline(apple2_state_t *st, int hcolor, int x1, int y1, int x2, int y2); +void a2_plot(apple2_state_t *st, int color, int x, int y); + +#endif /* __XSCREENSAVER_APPLE_II__ */ diff --git a/hacks/apple2.man b/hacks/apple2.man new file mode 100644 index 00000000..a14400c3 --- /dev/null +++ b/hacks/apple2.man @@ -0,0 +1,149 @@ +.de EX \"Begin example +.ne 5 +.if n .sp 1 +.if t .sp .5 +.nf +.in +.5i +.. +.de EE +.fi +.in -.5i +.if n .sp 1 +.if t .sp .5 +.. +.TH XScreenSaver 1 "30-Oct-99" "X Version 11" +.SH NAME +apple2 - Apple ][ display emulator +.SH SYNOPSIS +.B apple2 +[\-display \fIhost:display.screen\fP] [\-foreground \fIcolor\fP] +[\-background \fIcolor\fP] [\-window] [\-root] [\-mono] [\-install] +[\-visual \fIvisual\fP] [\-delay \fIseconds\fP] +[\-program \fIcommand to run\fP] +[\-text] [\-slideshow] [\-basic] +.SH DESCRIPTION +The +.I apple2 +program simulates an original Apple ][ Plus computer in all its 1979 +glory. It also reproduces the appearance of display on a color +television set of the period. +.PP +There are 3 modes: text, slideshow, and basic. Normally it chooses a +mode randomly, but you can override with the \fI\-text\fP, +\fI\-slideshow\fP, or \fI\-basic\fP options. +.PP +In text mode it displays the output of a command (by default your +system's fortune program, but can be overridden with -program). +.PP +In slideshow mode it chooses a number of images from the image source +you configured into XScreenSaver and displays them within the +limitations of the Apple ][ display hardware. With only 6 available +colors, you can only make out the general shape of the pictures. +.PP +In basic mode a simulated user types in a Basic program and runs it. + +.SH OPTIONS +.I apple2 +accepts the following options: +.TP 8 +.B \-window +Draw on a newly-created window. This is the default. +.TP 8 +.B \-root +Draw on the root window. +.TP 8 +.B \-mono +If on a color display, pretend we're on a monochrome display. +.TP 8 +.B \-install +Install a private colormap for the window. +.TP 8 +.B \-visual \fIvisual\fP +Specify which visual to use. Legal values are the name of a visual class, +or the id number (decimal or hex) of a specific visual. +.TP 8 +.B \-delay \fIdelay\fP +The delay between displaying one crash and another. +.TP 8 +.B \-text +Choose text mode +.TP 8 +.B \-slideshow +Choose slideshow mode +.TP 8 +.B \-basic +Choose basic mode +.TP 8 +.B \-program \fIsh-command\fP +In text mode, the command to run to generate the text to display. This +option may be any string acceptable to /bin/sh. The program will be +run at the end of a pipe, and any characters that it prints to +\fIstdout\fP will be printed on the Apple ][ display. If the program +exits, it will be launched again after 3 seconds. Default: +.BR fortune (1). + +Note that apple2 is \fInot\fP a terminal emulator: programs that try +to directly address the screen will not do what you might expect. It +merely draws the characters on the screen left to right, top to +bottom. Lines wrap when they reach the right edge, and the screen +scrolls when characters reach the bottom. + +In other words, programs like +.BR fortune (1) +will work, but programs like +.BR top (1) +won't. + +For example: +.EX +apple2 -text -program 'cat /usr/src/linux*/README | fold -sw40' +apple2 -text -program 'ping apple.com' +apple2 -text -program 'ps -e' +apple2 -text -program 'od -txCz -w7 /dev/random' +.EE +.SH ENVIRONMENT +.PP +.TP 8 +.B DISPLAY +to get the default host and display number. +.TP 8 +.B XENVIRONMENT +to get the name of a resource file that overrides the global resources +stored in the RESOURCE_MANAGER property. +.SH X RESOURCES +Notable X resources supported include the following which correspond +to standard TV controls: +.BR analogTVTint , +.BR analogTVColor , +.BR analogTVBrightness , +and +.BR analogTVContrast +which correspond to standard TV controls. They range from 0 to +100,except for tint which is an angle between -180 and +180. +.SH TRADEMARKS +Apple ][ and Applesoft are trademarks of Apple Computer. + +.SH SEE ALSO +.BR xscreensaver (1), +.BR bsod (1), +.BR fortune (1), +.BR phosphor (1), +.BR starwars (1), +.BR ljlatest (1), +.BR dadadodo (1), +.BR webcollage (1), +.BR driftnet (1) +.BR EtherPEG , +.BR EtherPeek +.SH COPYRIGHT +Copyright \(co 2002-2003 by Trevor Blackwell. 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 +Television and Apple ][ emulation by Trevor Blackwell . +Slideshow and text mode by Jamie Zawinski . diff --git a/hacks/blitspin.c b/hacks/blitspin.c index a1eaf313..1eebcb82 100644 --- a/hacks/blitspin.c +++ b/hacks/blitspin.c @@ -94,7 +94,7 @@ read_screen (Display *dpy, Window window, int *widthP, int *heightP) gcv.function = GXcopy; gc = XCreateGC (dpy, window, GCFunction, &gcv); - load_random_image (xgwa.screen, window, p); + load_random_image (xgwa.screen, window, p, NULL); /* Reset the window's background color... */ XSetWindowBackground (dpy, window, diff --git a/hacks/bouboule.c b/hacks/bouboule.c index 7e77e464..29544f2d 100644 --- a/hacks/bouboule.c +++ b/hacks/bouboule.c @@ -19,9 +19,8 @@ * other special, indirect and consequential damages. */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)bouboule.c 4.00 97/01/01 xlockmore"; - #endif /*- diff --git a/hacks/braid.c b/hacks/braid.c index 55e4fa7e..efb42428 100644 --- a/hacks/braid.c +++ b/hacks/braid.c @@ -4,9 +4,8 @@ * a rotational pattern */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)braid.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/bsod.c b/hacks/bsod.c index 9e5c9da0..84f5f312 100644 --- a/hacks/bsod.c +++ b/hacks/bsod.c @@ -16,6 +16,7 @@ #include #include "screenhack.h" #include "xpm-pixmap.h" +#include "apple2.h" #include #include #include @@ -293,12 +294,54 @@ windows (Display *dpy, Window window, int delay, int which) const char *wmeb = (" System halted."); +# ifdef __GNUC__ + __extension__ /* don't warn about "string length is greater than the + length ISO C89 compilers are required to support" + in the following string constant... */ +# endif + + const char *wxpa = /* From Wm. Rhodes */ + ("A problem has been detected and windows has been shut down to prevent " + "damage\n" + "to your computer.\n" + "\n" + "If this is the first time you've seen this Stop error screen,\n" + "restart your computer. If this screen appears again, follow\n" + "these steps:\n" + "\n" + "Check to be sure you have adequate disk space. If a driver is\n" + "identified in the Stop message, disable the driver or check\n" + "with the manufacturer for driver updates. Try changing video\n" + "adapters.\n" + "\n" + "Check with you hardware vendor for any BIOS updates. Disable\n" + "BIOS memory options such as caching or shadowing. If you need\n" + "to use Safe Mode to remove or disable compinents, restart your\n" + "computer, press F8 to select Advanced Startup Options, and then\n" + "select Safe Mode.\n" + "\n" + "Technical information:\n" + "\n" + "*** STOP: 0x0000007E (0xC0000005,0xF88FF190,0x0xF8975BA0,0xF89758A0)\n" + "\n" + "\n" + "*** EPUSBDSK.sys - Address F88FF190 base at FF88FE000, datestamp " + "3b9f3248\n" + "\n" + "Beginning dump of physical memory\n"); + const char *wxpb = + ("Physical memory dump complete.\n" + "Contact your system administrator or technical support group for " + "further\n" + "assitance.\n" + ); + if (which < 0 || which > 2) abort(); - /* kludge to lump Win2K and WinME together; seems silly to add another - preference/command line option just for this little one. */ + /* kludge to lump Win2K, WinME, and WinXP together; seems silly to add + another preference/command line option just for these little ones. */ if (which == 2 && (random() % 2)) - which = 3; + which = 3 + (random() % 2); XGetWindowAttributes (dpy, window, &xgwa); @@ -354,6 +397,16 @@ windows (Display *dpy, Window window, int delay, int which) draw_string(dpy, window, gc, &gcv, font, x, y, 10, 10, w2kb, 750); } else if (which == 3) + { + int line_height = font->ascent + font->descent + 1; + int x = 4; + int y = 4; + draw_string(dpy, window, gc, &gcv, font, x, y, 10, 10, wxpa, 750); + y += line_height * 26; + bsod_sleep(dpy, 4); + draw_string(dpy, window, gc, &gcv, font, x, y, 10, 10, wxpb, 750); + } + else if (which == 4) { int line_height = font->ascent + font->descent; int x = 0; @@ -1453,7 +1506,7 @@ blitdamage (Display *dpy, Window window, int delay) XGetWindowAttributes(dpy, window, &xwa); - load_random_image (xwa.screen, window, window); + load_random_image (xwa.screen, window, window, NULL); w = xwa.width; h = xwa.height; @@ -1587,7 +1640,7 @@ make_scrolling_window (Display *dpy, Window window, if (!grab_screen_p) ts->sub_height += ts->sub_y, ts->sub_y = 0; if (grab_screen_p) - load_random_image (xgwa.screen, window, window); + load_random_image (xgwa.screen, window, window, NULL); sprintf (buf1, "%.50s.background", name); sprintf (buf2, "%.50s.Background", name); @@ -2319,6 +2372,71 @@ vms (Display *dpy, Window window, int delay) } +/* HVX (formerly GCOS6) and TPS6 crash + by Brian Garratt + + GCOS6 is a Unix-like operating system developed by Honeywell in the + 1970s in collaboration with MIT and AT&T (who called their version + UNIX). Both are very much like MULTICS which Honeywell got from GE. + + HVX ("High-performance Virtual System on Unix") is an AIX application + which emulates GCOS6 hardware on RS6000-like machines. + */ +static void +hvx (Display *dpy, Window window, int delay) +{ + XWindowAttributes xgwa; + scrolling_window *ts; + + int delay1 = 10000; + int delay2 = 100000; + const char *hvx_panic_1 = + ("(TP) Trap no E Effective address 00000000 Instruction D7DE\n" + "(TP) Registers :\n" + "(TP) B1 -> B7 03801B02 00000000 03880D45 038BABDB 0388AFFD" + " 0389B3F8 03972317\n" + "(TP) R1 -> R7 0001 0007 F10F 090F 0020 0106 0272\n" + "(TP) P I Z M1 0388A18B 3232 0000 FF00\n" + "(TP) Program counter is at offset 0028 from string YTPAD\n" + "(TP) User id of task which trapped is LT 626\n" + "(TP)?\n" + ); + const char *hvx_panic_2 = + ("\n" + "(TP)?\n" + "Core dumps initiated for selected HVX processes ...\n" + "Core dumps complete.\n" + "Fri Jul 19 15:53:09 2002\n" + "Live registers for cp 0:\n" + " P = 7de3 IW=0000 I=32 CI=30000000 S=80006013" + " IV=aa0 Level=13\n" + " R1-7 = 1f 913 13 4 8 0 0\n" + " B1-7 = 64e71b a93 50e 64e73c 6c2c 7000 b54\n" + "Memory dump starting to file /var/hvx/dp01/diag/Level2 ...\n" + "Memory dump complete.\n" + ); + + XGetWindowAttributes (dpy, window, &xgwa); + ts = make_scrolling_window (dpy, window, "HVX", False); + XClearWindow(dpy, window); + + scrolling_puts (ts, hvx_panic_1, delay1); + if (bsod_sleep(dpy, 1)) goto DONE; + scrolling_puts (ts, " TP CLOSE ALL", delay2); + scrolling_puts (ts, "\n(TP)?\n", delay1); + if (bsod_sleep(dpy, 1)) goto DONE; + scrolling_puts (ts, " TP ABORT -LT ALL", delay2); + scrolling_puts (ts, "\n(TP)?\n", delay1); + if (bsod_sleep(dpy, 1)) goto DONE; + scrolling_puts (ts, " TP STOP KILL", delay2); + scrolling_puts (ts, hvx_panic_2, delay1); + + bsod_sleep(dpy, delay); + DONE: + XClearWindow(dpy, window); +} + + /* HPUX panic, by Tobias Klausmann @@ -2562,83 +2680,20 @@ DONE: /* - * Simulate various Apple II crashes. The memory map encouraged many - * programs to use the primary hi-res video page for various storage, - * and the secondary hi-res page for active display. When it crashed - * into Applesoft or the monitor, it would revert to the primary page - * and you'd see memory garbage on the screen. Also, it was common for - * copy-protected games to use the primary text page for important - * code, because that made it really hard to reverse-engineer them. The - * result often looked like what this generates. - * - * Sometimes an imaginary user types some of the standard commands to - * recover from crashes. You can turn off BSOD*apple2SimulateUser to - * prevent this. - * - * It simulates the following characteristics of standard television - * monitors: + * Simulate various Apple ][ crashes. The memory map encouraged many programs + * to use the primary hi-res video page for various storage, and the secondary + * hi-res page for active display. When it crashed into Applesoft or the + * monitor, it would revert to the primary page and you'd see memory garbage on + * the screen. Also, it was common for copy-protected games to use the primary + * text page for important code, because that made it really hard to + * reverse-engineer them. The result often looked like what this generates. * - * - Realistic rendering of a composite video signal - * - Compression & brightening on the right, as the scan gets truncated - * because of saturation in the flyback transformer - * - Blooming of the picture dependent on brightness - * - Overscan, cutting off a few pixels on the left side. - * - Colored text in mixed graphics/text modes - * - * It's amazing how much it makes your high-end monitor look like at - * large late-70s TV. All you need is to put a big "Solid State" logo - * in curly script on it and you'd be set. + * The Apple ][ logic and video hardware is in apple2.c. The TV is emulated by + * analogtv.c for maximum realism * * Trevor Blackwell */ -/* - * Implementation notes: - * - * There are roughly 3 parts to this hack: - * - * - emulation of A2 Basic and Monitor. Not much more than printing random - * plausible messages. Here we work in the A2 memory space. - * - * - emulation of the A2's video output section, which shifted bits out of main - * memory at a 14 MHz dot clock rate, sort of. You could only read one byte - * per MHz, so there were various schemes for turning 8 bits into 14 screen - * pixels. - * - * - simulation of an NTSC television, which turned the bits into colored - * graphics and text. - * - * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it - * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the - * same memory as the text screen. Each could be one of 16 colors. Hires gave - * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were - * orange or green depending on the setting of the high bit in each byte. - * - * The graphics modes could also have 4 lines of text at the bottom. This was - * fairly unreadable if you had a color monitor. - * - * Each mode had 2 different screens using different memory space. In hires - * mode this was sometimes used for double buffering, but more often the lower - * screen was full of code/data and the upper screen was used for display, so - * you got random garbage on the screen. - * - * In DirectColor or TrueColor modes, it generates pixel values directly from - * RGB values it calculates across each scan line. In PseudoColor mode, it - * consider each possible pattern of 5 preceding bit values in each possible - * position modulo 4 and allocates a color for each. A few things, like the - * brightening on the right side as the horizontal trace slows down, aren't - * done in PseudoColor. - * - * The text font is based on X's standard 6x10 font, with a few tweaks like - * putting a slash across the zero. - * - * I'd like to add a bit of visible retrace, but it conflicts with being able - * to bitcopy the image when fast scrolling. After another couple of CPU - * generations, we could probably regenerate the whole image from scratch every - * time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling - * looks too slow. - */ - static char * apple2_basic_errors[]={ "BREAK", "NEXT WITHOUT FOR", @@ -2653,6 +2708,12 @@ static char * apple2_basic_errors[]={ "FORMULA TOO COMPLEX", "UNDEF'D FUNCTION", "OUT OF DATA" +#if 0 + , + "DEFAULT ARGUMENTS ARE NOT ALLOWED IN DECLARATION OF FRIEND " + "TEMPLATE SPECIALIZATION" +#endif + }; static char * apple2_dos_errors[]={ "VOLUME MISMATCH", @@ -2662,1403 +2723,223 @@ static char * apple2_dos_errors[]={ "PROGRAM TOO LARGE", }; -struct apple2_state { - char hireslines[192][40]; - char textlines[24][40]; - int gr_text; - enum { - A2_GR_FULL=1, - A2_GR_LORES=2, - A2_GR_HIRES=4 - } gr_mode; - int cursx; - int cursy; - int blink; - int rowimage[24]; -}; - -enum { - A2_SP_ROWMASK=1023, - A2_SP_PUT=1024, - A2_SP_COPY=2048 -}; - -static void -a2_scroll(struct apple2_state *st) +void a2controller_crash(apple2_sim_t *sim, int *stepno, + double *next_actiontime) { + apple2_state_t *st=sim->st; + char *s; int i; - int top=(st->gr_mode&(A2_GR_LORES|A2_GR_HIRES)) ? 20 : 0; - if ((st->gr_mode&A2_GR_FULL) && (st->gr_mode&A2_GR_HIRES)) return; - if (st->gr_mode&A2_GR_FULL) top=0; - for (i=top; i<23; i++) { - if (memcmp(st->textlines[i],st->textlines[i+1],40)) { - memcpy(st->textlines[i],st->textlines[i+1],40); - st->rowimage[i]=st->rowimage[i+1]; - } - } - memset(st->textlines[23],0xe0,40); - st->rowimage[23]=-1; -} -static void -a2_printc(struct apple2_state *st, char c) -{ - st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */ - if (c=='\n') { - if (st->cursy==23) { - a2_scroll(st); - } else { - st->rowimage[st->cursy]=-1; - st->cursy++; - st->rowimage[st->cursy]=-1; - } - st->cursx=0; - } else { - st->textlines[st->cursy][st->cursx]=c ^ 0xc0; - st->rowimage[st->cursy]=-1; - st->cursx++; - if (st->cursx==40) { - if (st->cursy==23) { - a2_scroll(st); - } else { - st->rowimage[st->cursy]=-1; - st->cursy++; - st->rowimage[st->cursy]=-1; - } - st->cursx=0; - } - } - st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */ -} + struct mydata { + int fillptr; + int fillbyte; + } *mine; -static void -a2_goto(struct apple2_state *st, int r, int c) -{ - st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */ - st->cursy=r; - st->cursx=c; - st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */ -} - -static void -a2_cls(struct apple2_state *st) -{ - int i; - for (i=0; i<24; i++) { - memset(st->textlines[i],0xe0,40); - st->rowimage[i]=-1; - } -} - -static void -a2_invalidate(struct apple2_state *st) -{ - int i; - for (i=0; i<24; i++) { - st->rowimage[i]=-1; - } -} - -static void -a2_poke(struct apple2_state *st, int addr, int val) -{ + if (!sim->controller_data) + sim->controller_data = calloc(sizeof(struct mydata),1); + mine=(struct mydata *) sim->controller_data; - if (addr>=0x400 && addr<0x800) { - /* text memory */ - int row=((addr&0x380)/0x80) + ((addr&0x7f)/0x28)*8; - int col=(addr&0x7f)%0x28; - if (row<24 && col<40) { - st->textlines[row][col]=val; - if (!(st->gr_mode&(A2_GR_HIRES)) || - (!(st->gr_mode&(A2_GR_FULL)) && row>=20)) { - st->rowimage[row]=-1; - } - } - } - else if (addr>=0x2000 && addr<0x4000) { - int row=(((addr&0x1c00) / 0x400) * 1 + - ((addr&0x0380) / 0x80) * 8 + - ((addr&0x0078) / 0x28) * 64); - int col=((addr&0x07f)%0x28); - if (row<192 && col<40) { - st->hireslines[row][col]=val; - if (st->gr_mode&A2_GR_HIRES) { - st->rowimage[row/8]=-1; - } - } - } -} - -/* This table lists fixes for characters that differ from the standard 6x10 - font. Each encodes a pixel, as (charindex*7 + x) + (y<<10) + (value<<15) - where value is 0 for white and 1 for black. */ -static unsigned short a2_fixfont[] = { - /* Fix $ */ 0x8421, 0x941d, - /* Fix % */ 0x8024, 0x0028, 0x8425, 0x0426, 0x0825, 0x1027, 0x1426, 0x9427, - 0x1824, 0x9828, - /* Fix * */ 0x8049, 0x8449, 0x8849, 0x0c47, 0x0c48, 0x0c4a, 0x0c4b, 0x9049, - 0x9449, 0x9849, - /* Fix , */ 0x9057, 0x1458, 0x9856, 0x1857, 0x1c56, - /* Fix . */ 0x1465, 0x1864, 0x1866, 0x1c65, - /* Fix / */ 0x006e, 0x186a, - /* Fix 0 */ 0x8874, 0x8c73, 0x9072, - /* Fix 1 */ 0x0878, 0x1878, 0x187c, - /* Fix 5 */ 0x8895, 0x0c94, 0x0c95, - /* Fix 6 */ 0x809f, 0x8c9c, 0x109c, - /* Fix 7 */ 0x8ca4, 0x0ca5, 0x90a3, 0x10a4, - /* Fix 9 */ 0x08b3, 0x8cb3, 0x98b0, - /* Fix : */ 0x04b9, 0x08b8, 0x08ba, 0x0cb9, 0x90b9, 0x14b9, 0x18b8, 0x18b9, - 0x18ba, 0x1cb9, - /* Fix ; */ 0x04c0, 0x08bf, 0x08c1, 0x0cc0, 0x90c0, 0x14c1, 0x98bf, 0x18c0, - 0x1cbf, - /* Fix < */ 0x80c8, 0x00c9, 0x84c7, 0x04c8, 0x88c6, 0x08c7, 0x8cc5, 0x0cc6, - 0x90c6, 0x10c7, - 0x94c7, 0x14c8, 0x98c8, 0x18c9, - /* Fix > */ 0x80d3, 0x00d4, 0x84d4, 0x04d5, 0x88d5, 0x08d6, 0x8cd6, 0x0cd7, - 0x90d5, 0x10d6, - 0x94d4, 0x14d5, 0x98d3, 0x18d4, - /* Fix @ */ 0x88e3, 0x08e4, 0x8ce4, 0x98e5, - /* Fix B */ 0x84ef, 0x04f0, 0x88ef, 0x08f0, 0x8cef, 0x90ef, 0x10f0, 0x94ef, - 0x14f0, - /* Fix D */ 0x84fd, 0x04fe, 0x88fd, 0x08fe, 0x8cfd, 0x0cfe, 0x90fd, 0x10fe, - 0x94fd, 0x14fe, - /* Fix G */ 0x8116, 0x0516, 0x9916, - /* Fix J */ 0x0129, 0x012a, 0x052a, 0x852b, 0x092a, 0x892b, 0x0d2a, 0x8d2b, - 0x112a, 0x912b, - 0x152a, 0x952b, 0x992a, - /* Fix M */ 0x853d, 0x853f, 0x093d, 0x893e, 0x093f, - /* Fix Q */ 0x915a, 0x155a, 0x955b, 0x155c, 0x195b, 0x995c, 0x1d5c, - /* Fix V */ 0x8d7b, 0x0d7c, 0x0d7e, 0x8d7f, 0x917b, 0x117c, 0x117e, 0x917f, - /* Fix [ */ 0x819e, 0x81a2, 0x859e, 0x899e, 0x8d9e, 0x919e, 0x959e, 0x999e, - 0x99a2, - /* Fix \ */ 0x01a5, 0x19a9, - /* Fix ] */ 0x81ac, 0x81b0, 0x85b0, 0x89b0, 0x8db0, 0x91b0, 0x95b0, 0x99ac, - 0x99b0, - /* Fix ^ */ 0x01b5, 0x05b4, 0x05b6, 0x09b3, 0x89b5, 0x09b7, 0x8db4, 0x8db6, - 0x91b3, 0x91b7, - /* Fix _ */ 0x9db9, 0x9dbf, - 0, -}; - -struct ntsc_dec { - char pattern[600]; - int ntscy[600]; - int ntsci[600]; - int ntscq[600]; - int multi[600]; - int multq[600]; - int brightness_control; -}; - -/* - First generate the I and Q reference signals, which we'll multiply by the - input signal to accomplish the demodulation. Normally they are shifted 33 - degrees from the colorburst. I think this was convenient for - inductor-capacitor-vacuum tube implementation. - - The tint control, FWIW, just adds a phase shift to the chroma signal, and - the color control controls the amplitude. - - In text modes (colormode==0) the system disabled the color burst, and no - color was detected by the monitor. - - freq_error gives a mismatch between the built-in oscillator and the TV's - colorbust. Older II Plus machines seemed to occasionally get instability - problems -- the crystal oscillator was a single transistor if I remember - correctly -- and the frequency would vary enough that the tint would change - across the width of the screen. The left side would be in correct tint - because it had just gotten resynchronized with the color burst. -*/ -static void -ntsc_set_demod(struct ntsc_dec *it, double tint_control, - double color_control, double brightness_control, - double freq_error, - int colormode) -{ - int i; - - it->brightness_control=(int)(1024.0*brightness_control); - - for (i=0; i<600; i++) { - double phase=90.0-90.0*i + freq_error*i/600.0 + tint_control; - it->multi[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 65536.0 * - color_control * colormode * 4); - it->multq[i]=(int)(cos(3.1415926/180.0*(phase-33)) * 65536.0 * - color_control * colormode * 4); - } -} - -/* Here we model the analog circuitry of an NTSC television. Basically, it - splits the signal into 3 signals: Y, I and Q. Y corresponds to luminance, - and you get it by low-pass filtering the input signal to below 3.57 MHz. - - I and Q are the in-phase and quadrature components of the 3.57 MHz - subcarrier. We get them by multiplying by cos(3.57 MHz*t) and sin(3.57 - MHz*t), and low-pass filtering. Because the eye has less resolution in some - colors than others, the I component gets low-pass filtered at 1.5 MHz and - the Q at 0.5 MHz. The I component is approximately orange-blue, and Q is - roughly purple-green. See http://www.ntsc-tv.com for details. - */ -static void -ntsc_to_yiq(struct ntsc_dec *it) -{ - int i; - int fyx[10],fyy[10]; - int fix[10],fiy[10]; - int fqx[10],fqy[10]; - int pixghost; - int iny,ini,inq,pix,blank; - - for (i=0; i<10; i++) fyx[i]=fyy[i]=fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0; - pixghost=0; - for (i=0; i<600; i++) { - /* Get the video out signal, and add a teeny bit of ghosting, typical of RF - monitor cables. This corresponds to a pretty long cable, but looks right - to me. */ - pix=it->pattern[i]*1024; - if (i>=20) pixghost += it->pattern[i-20]*15; - if (i>=30) pixghost -= it->pattern[i-30]*15; - pix += pixghost; - - /* Get Y, I, Q before filtering */ - iny=pix; - ini=(pix*it->multi[i])>>16; - inq=(pix*it->multq[i])>>16; - - blank = (i>=7 && i<596 ? it->brightness_control : -200); - - /* Now filter them. These are infinite impulse response filters calculated - by the script at http://www-users.cs.york.ac.uk/~fisher/mkfilter. This - is fixed-point integer DSP, son. No place for wimps. We do it in integer - because you can count on integer being faster on most CPUs. We care - about speed because we need to recalculate every time we blink text, and - when we spew random bytes into screen memory. This is roughly 16.16 - fixed point arithmetic, but we scale some filter values up by a few bits - to avoid some nasty precision errors. */ - - /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz - with an extra zero at 3.5 MHz, from - mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l - Delay about 2 */ - - fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3]; - fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6]; - fyx[6] = (iny * 1897) >> 13; - fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3]; - fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6]; - fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3] - + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16); - if (i>=2) it->ntscy[i-2] = blank + (fyy[6]>>3); - - /* Filter I and Q at 1.5 MHz. 3 pole Butterworth from - mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 - Delay about 3. - - The NTSC spec says the Q value should be filtered at 0.5 MHz at the - transmit end, But the Apple's video circuitry doesn't any such thing. - AFAIK, oldish televisions (before comb filters) simply applied a 1.5 MHz - filter to both after the demodulator. - */ - - fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3]; - fix[3] = (ini * 1413) >> 14; - fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3]; - fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2]) - + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16); - if (i>=3) it->ntsci[i-3] = fiy[3]>>2; - - fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3]; - fqx[3] = (inq * 1413) >> 14; - fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3]; - fqy[3] = (fqx[0]+fqx[3]) + 3*(fqx[1]+fqx[2]) - + ((16559*fqy[0] - 72008*fqy[1] + 109682*fqy[2]) >> 16); - if (i>=3) it->ntscq[i-3] = fqy[3]>>2; - - } - for (; i<610; i++) { - if (i-2<600) it->ntscy[i-2]=0; - if (i-3<600) it->ntsci[i-3]=0; - if (i-9<600) it->ntscq[i-9]=0; - } -} - -enum { - A2_CMAP_HISTBITS=5, - A2_CMAP_LEVELS=2, - A2_CMAP_OFFSETS=4 -}; - -#define A2_CMAP_INDEX(COLORMODE, LEVEL, HIST, OFFSET) \ -((((COLORMODE)*A2_CMAP_LEVELS+(LEVEL))<class; - red_shift=red_invprec=green_shift=green_invprec=blue_shift=blue_invprec=-1; - if (visclass == TrueColor || xgwa.visual->class == DirectColor) { - use_cmap=0; - use_color=!mono_p; - } - else if (visclass == PseudoColor || visclass == StaticColor) { - use_cmap=1; - use_color=!mono_p; - } - else { - use_cmap=1; - use_color=0; - } - - /* The Apple II screen was 280x192, sort of. We expand the width to 300 - pixels to allow for overscan. We then pick a size within the window - that's an integer multiple of 300x192. The small case happens when - we're displaying in a subwindow. Then it ends up showing the center - of the screen, which is OK. */ - w=xgwa.width; - h = (xgwa.height/192)*192; - if (w<300) w=300; - if (h==0) h=192; - - dec=(struct ntsc_dec *)malloc(sizeof(struct ntsc_dec)); - - if (use_cmap) { - int hist,offset,level; - int colorprec=8; - - cmap_again: - n_colors=0; - /* Typically allocates 214 distinct colors, but will scale back its - ambitions pretty far if it can't get them */ - for (colormode=0; colormode<=use_color; colormode++) { - ntsc_set_demod(dec, tint_control, color_control, brightness_control, - 0.0, colormode); - for (level=0; level<2; level++) { - for (hist=0; hist<(1<dec->powerup = 1000.0; - for (i=0; i<600; i++) dec->pattern[i]=0; - for (i=0; ipattern[64+offset-i]=(hist>>i)&1; - } - - ntsc_to_yiq(dec); - interpy=dec->ntscy[63+offset]; - interpi=dec->ntsci[63+offset]; - interpq=dec->ntscq[63+offset]; - - r=(interpy + ((+68128*interpi+40894*interpq)>>16))*levelmult; - g=(interpy + ((-18087*interpi-41877*interpq)>>16))*levelmult; - b=(interpy + ((-72417*interpi+113312*interpq)>>16))*levelmult; - if (r<0) r=0; - if (r>65535) r=65535; - if (g<0) g=0; - if (g>65535) g=65535; - if (b<0) b=0; - if (b>65535) b=65535; - - col.red=r & precmask; - col.green=g & precmask; - col.blue=b & precmask; - col.pixel=0; - if (!XAllocColor(dpy, xgwa.colormap, &col)) { - XFreeColors(dpy, xgwa.colormap, colors, n_colors, 0L); - n_colors=0; - colorprec--; - if (colorprec<3) { - goto bailout; - } - goto cmap_again; - } - colors[n_colors++]=col.pixel; - } - } - } + if (random()%3==0) { + st->gr_mode=0; + *next_actiontime+=0.4; + *stepno=100; } - } else { - /* Is there a standard way to do this? Does this handle all cases? */ - int shift, prec; - for (shift=0; shift<32; shift++) { - for (prec=1; prec<16 && prec<32-shift; prec++) { - unsigned long mask=(0xffffUL>>(16-prec)) << shift; - if (red_shift<0 && mask==xgwa.visual->red_mask) - red_shift=shift, red_invprec=16-prec; - if (green_shift<0 && mask==xgwa.visual->green_mask) - green_shift=shift, green_invprec=16-prec; - if (blue_shift<0 && mask==xgwa.visual->blue_mask) - blue_shift=shift, blue_invprec=16-prec; - } + else if (random()%4==0) { + st->gr_mode=A2_GR_LORES; + if (random()%3==0) st->gr_mode |= A2_GR_FULL; + *next_actiontime+=0.4; + *stepno=100; } - if (red_shift<0 || green_shift<0 || blue_shift<0) { - if (0) fprintf(stderr,"Can't figure out color space\n"); - goto bailout; + else if (random()%2==0) { + st->gr_mode=A2_GR_HIRES; + *stepno=300; } - raw_rgb=(short *)calloc(w*3, sizeof(short)); - } - - gcv.background=0; - gc = XCreateGC(dpy, window, GCBackground, &gcv); - XSetWindowBackground(dpy, window, gcv.background); - XClearWindow(dpy,window); - - screen_xo=(xgwa.width-w)/2; - screen_yo=(xgwa.height-h)/2; - - if (use_shm) { -#ifdef HAVE_XSHM_EXTENSION - image = create_xshm_image (dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, - &shm_info, w, h); -#endif - if (!image) { - fprintf(stderr, "create_xshm_image failed\n"); - use_shm=0; + else { + st->gr_mode=A2_GR_HIRES; + *next_actiontime+=0.4; + *stepno=100; } - } - if (!image) { - image = XCreateImage(dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, 0, - w, h, 8, 0); - image->data = (char *)calloc(image->height, image->bytes_per_line); - } - - st=(struct apple2_state *)calloc(1,sizeof(struct apple2_state)); - - /* - Generate the font. It used a 5x7 font which looks a lot like the standard X - 6x10 font, with a few differences. So we render up all the uppercase - letters of 6x10, and make a few tweaks (like putting a slash across the - zero) according to fixfont. - */ - { - const char *def_font="6x10"; - XFontStruct *font; - Pixmap text_pm; - GC gc; - - font = XLoadQueryFont (dpy, def_font); - if (!font) { - fprintf(stderr,"Can't load font %s\n",def_font); - goto bailout; - } - - text_pm=XCreatePixmap(dpy, window, 64*7, 8, xgwa.depth); - - gcv.foreground=1; - gcv.background=0; - gcv.font=font->fid; - gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv); - - XSetForeground(dpy, gc, 0); - XFillRectangle(dpy, text_pm, gc, 0, 0, 64*7, 8); - XSetForeground(dpy, gc, 1); - for (i=0; i<64; i++) { - char c=32+i; - int x=7*i+1; - int y=7; - if (c=='0') { - c='O'; - XDrawString(dpy, text_pm, gc, x, y, &c, 1); + break; + + case 100: + /* An illegal instruction or a reset caused it to drop into the + assembly language monitor, where you could disassemble code & view + data in hex. */ + if (random()%3==0) { + char ibytes[128]; + char itext[128]; + int addr=0xd000+random()%0x3000; + sprintf(ibytes, + "%02X",random()%0xff); + sprintf(itext, + "???"); + sprintf(sim->printing_buf, + "\n\n" + "%04X: %-15s %s\n" + " A=%02X X=%02X Y=%02X S=%02X F=%02X\n" + "*", + addr,ibytes,itext, + random()%0xff, random()%0xff, + random()%0xff, random()%0xff, + random()%0xff); + sim->printing=sim->printing_buf; + a2_goto(st,23,1); + if (st->gr_mode) { + *stepno=180; } else { - XDrawString(dpy, text_pm, gc, x, y, &c, 1); + *stepno=200; } + sim->prompt='*'; + *next_actiontime += 2.0 + (random()%1000)*0.0002; } - text_im = XGetImage(dpy, text_pm, 0, 0, 64*7, 8, ~0L, ZPixmap); - XFreeGC(dpy, gc); - XFreePixmap(dpy, text_pm); - - for (i=0; a2_fixfont[i]; i++) { - XPutPixel(text_im, a2_fixfont[i]&0x3ff, - (a2_fixfont[i]>>10)&0xf, - (a2_fixfont[i]>>15)&1); - } - } - - /* - Simulate plausible initial memory contents. - */ - { - int addr=0; - while (addr<0x4000) { - int n; - - switch (random()%4) { - case 0: - case 1: - n=random()%500; - for (i=0; iwidth); - for (i=0; i<100; i++) { - for (y=0; y<8; y++) { - c=0; - for (j=0; j<8; j++) { - c |= XGetPixel(text_im, (x+j)%text_im->width, y)<width); - } - break; - - case 3: - if (addr>0x2000) { - n=random()%200; - for (i=0; iprinting_buf, + "\n" + "\n" + "\n" + "?%s IN %d\n" + "\001]", + apple2_basic_errors[random() % + (sizeof(apple2_basic_errors) + /sizeof(char *))], + (1000*(random()%(random()%59+1)) + + 100*(random()%(random()%9+1)) + + 5*(random()%(random()%199+1)) + + 1*(random()%(random()%(random()%2+1)+1)))); + sim->printing=sim->printing_buf; + a2_goto(st,23,1); + *stepno=110; + sim->prompt=']'; + *next_actiontime += 2.0 + (random()%1000)*0.0002; } - } - - if (random()%4==0 && - !use_cmap && use_color && - xgwa.visual->bits_per_rgb>=8) { - flutter_tint=1; - } - else if (random()%3==0) { - flutter_horiz_desync=1; - } - - crtload[0]=0.0; - stepno=0; - a2_goto(st,23,0); - gettimeofday(&basetime_tv, NULL); - if (random()%2==0) basetime_tv.tv_sec -= 1; /* random blink phase */ - next_actiontime=0.0; - fillptr=fillbyte=0; - while (1) { - double curtime,blinkphase; - int startdisplayrow=0; - int cheapdisplay=0; - int nodelay=0; - { - struct timeval curtime_tv; - gettimeofday(&curtime_tv, NULL); - curtime=(curtime_tv.tv_sec - basetime_tv.tv_sec) + - 0.000001*(curtime_tv.tv_usec - basetime_tv.tv_usec); + break; + + case 110: + if (random()%3==0) { + /* This was how you reset the Basic interpreter. The sort of + incantation you'd have on a little piece of paper taped to the + side of your machine */ + sim->typing="CALL -1370"; + *stepno=120; } - if (curtime>delay) goto finished; - - if (bsod_sleep(dpy,0)) goto finished; - - if (flutter_tint && st->gr_mode && !printing) { - /* Oscillator instability. Look for freq_error below. We should only do - this with color depth>=8, since otherwise you see pixels changing. */ - freq_error_inc += (-0.10*freq_error_inc - + ((int)(random()&0xff)-0x80) * 0.01); - freq_error += freq_error_inc; - a2_invalidate(st); - nodelay=1; + else if (random()%2==0) { + sim->typing="CATALOG\n"; + *stepno=170; } - else if (flutter_horiz_desync) { - /* Horizontal sync during vertical sync instability. */ - horiz_desync += (-0.10*(horiz_desync-3.0) + - ((int)(random()&0xff)-0x80) * - ((int)(random()&0xff)-0x80) * - ((int)(random()&0xff)-0x80) * 0.0000003); - for (i=0; i<3; i++) st->rowimage[i]=-1; - nodelay=1; - } - - /* It's super-important to get the cursor/text flash out at exactly the - right time, or it looks wrong. So if we're almost due for a blink, wait - for it so we don't miss it in the middle of a screen update. */ - blinkphase=curtime/0.8; - if (blinkphase-floor(blinkphase)>0.7 && !printing && !nodelay) { - /* We're about to blink */ - int delay = ((1.0-(blinkphase-floor(blinkphase)))*0.8) * 1000000; - if (delay<1000) delay=1000; - usleep(delay); - continue; + else { + *next_actiontime+=1.0; + *stepno=999; } - - /* The blinking rate was controlled by 555 timer with a resistor/capacitor - time constant. Because the capacitor was electrolytic, the flash rate - varied somewhat between machines. I'm guessing 1.6 seconds/cycle was - reasonable. (I soldered a resistor in mine to make it blink faster.) */ - i=st->blink; - st->blink=((int)blinkphase)&1; - if (st->blink!=i && !(st->gr_mode&A2_GR_FULL)) { - int downcounter=0; - /* For every row with blinking text, set the changed flag. This basically - works great except with random screen garbage in text mode, when we - end up redrawing the whole screen every second */ - for (row=(st->gr_mode ? 20 : 0); row<24; row++) { - for (col=0; col<40; col++) { - c=st->textlines[row][col]; - if ((c & 0xc0) == 0x40) { - downcounter=4; - break; - } - } - if (downcounter>0) { - st->rowimage[row]=-1; - downcounter--; - } - } - st->rowimage[st->cursy]=-1; - startdisplayrow=random()%24; - } - else if (next_actiontime > curtime && !printing && !nodelay) { - int delay = (next_actiontime-curtime)*1000000; - - if (delay>100000) delay=100000; - if (delay<1000) delay=1000; - usleep(delay); - continue; + break; + + case 120: + *stepno=130; + *next_actiontime += 0.5; + break; + + case 130: + st->gr_mode=0; + a2_cls(st); + a2_goto(st,0,16); + for (s="APPLE ]["; *s; s++) a2_printc(st,*s); + a2_goto(st,23,0); + a2_printc(st,']'); + *next_actiontime+=1.0; + *stepno=999; + break; + + case 170: + if (random()%50==0) { + sprintf(sim->printing_buf, + "\nDISK VOLUME 254\n\n" + " A 002 HELLO\n" + "\n" + "]"); + sim->printing=sim->printing_buf; } - - if (printing) { - cheapdisplay=1; - while (*printing) { - if (*printing=='\001') { /* pause */ - printing++; - for (i=20; i<24; i++) st->rowimage[i]=-1; - break; - } - else if (*printing=='\n') { - a2_printc(st,*printing); - printing++; - break; - } - else { - a2_printc(st,*printing); - printing++; - } - } - if (!*printing) printing=NULL; + else { + sprintf(sim->printing_buf,"\n?%s\n]", + apple2_dos_errors[random()% + (sizeof(apple2_dos_errors) / + sizeof(char *))]); + sim->printing=sim->printing_buf; } - else if (curtime >= next_actiontime) { - if (typing) { - /* If we're in the midst of typing a string, emit a character with - random timing. */ - a2_printc(st, *typing); - if (*typing=='\n') { - next_actiontime = curtime; - } else { - next_actiontime = curtime + (random()%1000)*0.0003 + 0.3; - } - typing++; - - if (!*typing) typing=NULL; - - } - else { - next_actiontime=curtime; - - switch(stepno) { - case 0: - a2_invalidate(st); - if (0) { - /* - For testing color rendering. The spec is: - red grn blu - 0 black 0 0 0 - 1 red 227 30 96 - 2 dk blue 96 78 189 - 3 purple 255 68 253 - 4 dk green 0 163 96 - 5 gray 156 156 156 - 6 med blue 20 207 253 - 7 lt blue 208 195 255 - 8 brown 96 114 3 - 9 orange 255 106 60 - 10 grey 156 156 156 - 11 pink 255 160 208 - 12 lt green 20 245 60 - 13 yellow 208 221 141 - 14 aqua 114 255 208 - 15 white 255 255 255 - */ - st->gr_mode=A2_GR_LORES; - for (row=0; row<24; row++) { - for (col=0; col<40; col++) { - st->textlines[row][col]=(row&15)*17; - } - } - next_actiontime+=0.4; - stepno=88; - } - else if (random()%3==0) { - st->gr_mode=0; - next_actiontime+=0.4; - stepno=88; - } - else if (random()%4==0) { - st->gr_mode=A2_GR_LORES; - if (random()%3==0) st->gr_mode |= A2_GR_FULL; - next_actiontime+=0.4; - stepno=88; - } - else if (random()%2==0) { - st->gr_mode=A2_GR_HIRES; - stepno=73; - } - else { - st->gr_mode=A2_GR_HIRES; - next_actiontime+=0.4; - stepno=88; - } - break; - - case 88: - /* An illegal instruction or a reset caused it to drop into the - assembly language monitor, where you could disassemble code & view - data in hex. */ - if (random()%3==0) { - char ibytes[128]; - char itext[128]; - int addr=0xd000+random()%0x3000; - sprintf(ibytes, - "%02X",random()%0xff); - sprintf(itext, - "???"); - sprintf(printbuf, - "\n\n" - "%04X: %-15s %s\n" - " A=%02X X=%02X Y=%02X S=%02X F=%02X\n" - "*", - addr,ibytes,itext, - random()%0xff, random()%0xff, - random()%0xff, random()%0xff, - random()%0xff); - printing=printbuf; - a2_goto(st,23,1); - if (st->gr_mode) { - stepno=11; - } else { - stepno=13; - } - prompt='*'; - next_actiontime += 2.0 + (random()%1000)*0.0002; - } - else { - /* Lots of programs had at least their main functionality in - Applesoft Basic, which had a lot of limits (memory, string - length, etc) and would sometimes crash unexpectedly. */ - sprintf(printbuf, - "\n" - "\n" - "\n" - "?%s IN %d\n" - "\001]", - apple2_basic_errors[random() % - (sizeof(apple2_basic_errors) - /sizeof(char *))], - (1000*(random()%(random()%59+1)) + - 100*(random()%(random()%9+1)) + - 5*(random()%(random()%199+1)) + - 1*(random()%(random()%(random()%2+1)+1)))); - printing=printbuf; - a2_goto(st,23,1); - stepno=1; - prompt=']'; - next_actiontime += 2.0 + (random()%1000)*0.0002; - } - break; - - case 1: - if (simulate_user && random()%3==0) { - /* This was how you reset the Basic interpreter. The sort of - incantation you'd have on a little piece of paper taped to the - side of your machine */ - typing="CALL -1370"; - stepno=2; - } - else if (simulate_user && random()%2==0) { - typing="CATALOG\n"; - stepno=22; - } - else { - next_actiontime+=1.0; - stepno=6; - } - break; - - case 2: - stepno=3; - next_actiontime += 0.5; - break; - - case 3: - st->gr_mode=0; - a2_cls(st); - a2_goto(st,0,16); - for (s="APPLE ]["; *s; s++) a2_printc(st,*s); - a2_goto(st,23,0); - a2_printc(st,']'); - next_actiontime+=1.0; - stepno=6; - break; - - case 6: - if (simulate_user && random()%50==0 && 0) { /* disabled, too goofy */ - typing="10 PRINT \"TRS-80S SUCK!!!\"\n" - "]20 GOTO 10\n" - "]RUN"; - stepno=7; - } - else { - stepno=8; - next_actiontime += delay; - } - break; - - case 7: - for (i=0; i<30; i++) { - for (s="\nTRS-80S SUCK"; *s; s++) a2_printc(st,*s); - } - stepno=8; - next_actiontime+=delay; - - case 8: - break; - - case 22: - if (random()%50==0) { - sprintf(printbuf,"\nDISK VOLUME 254\n\n" - " A 002 HELLO\n" - "\n" - "]"); - printing=printbuf; - } - else { - sprintf(printbuf,"\n?%s\n]", - apple2_dos_errors[random()% - (sizeof(apple2_dos_errors) / - sizeof(char *))]); - printing=printbuf; - } - stepno=6; - next_actiontime+=1.0; - break; - - case 11: - if (simulate_user && random()%2==0) { - /* This was how you went back to text mode in the monitor */ - typing="FB4BG"; - stepno=12; - } else { - next_actiontime+=1.0; - stepno=6; - } - break; - - case 12: - st->gr_mode=0; - a2_invalidate(st); - a2_printc(st,'\n'); - a2_printc(st,'*'); - stepno=13; - next_actiontime+=2.0; - break; - - case 13: - /* This reset things into Basic */ - if (simulate_user && random()%2==0) { - typing="FAA6G"; - stepno=2; - } - else { - stepno=8; - next_actiontime+=delay; - } - break; - - case 73: - for (i=0; i<1500; i++) { - a2_poke(st, fillptr, fillbyte); - fillptr++; - fillbyte = (fillbyte+1)&0xff; - } - next_actiontime += 0.08; - /* When you hit c000, it changed video settings */ - if (fillptr>=0xc000) { - a2_invalidate(st); - st->gr_mode=0; - } - /* And it seemed to reset around here, I dunno why */ - if (fillptr>=0xcf00) stepno=3; - break; - } - } + *stepno=999; + *next_actiontime+=1.0; + break; + + case 180: + if (random()%2==0) { + /* This was how you went back to text mode in the monitor */ + sim->typing="FB4BG"; + *stepno=190; + } else { + *next_actiontime+=1.0; + *stepno=999; } - - /* Now, we turn the data in the Apple II video into a screen display. This - is interesting because of the interaction with the NTSC color decoding - in a color television. */ - - colormode=use_color && st->gr_mode!=0; - if (!use_cmap) { - ntsc_set_demod(dec, tint_control, color_control, brightness_control, - freq_error, colormode); + break; + + case 190: + st->gr_mode=0; + a2_invalidate(st); + a2_printc(st,'\n'); + a2_printc(st,'*'); + *stepno=200; + *next_actiontime+=2.0; + break; + + case 200: + /* This reset things into Basic */ + if (random()%2==0) { + sim->typing="FAA6G"; + *stepno=120; } - imgrow=0; - for (textrow=0; textrow<24; textrow++) { - if (st->rowimage[textrow] == textrow) { - screen_plan[textrow]=0; - } - else if (cheapdisplay && st->rowimage[textrow]>=0 && - textrow<21 && st->rowimage[textrow]<21 && - st->rowimage[textrow]>=2 && textrow>=2 && - (st->rowimage[textrow]+1)*h/24 + screen_xo <= xgwa.height) { - screen_plan[textrow]= A2_SP_COPY | st->rowimage[textrow]; - for (i=0; i<8; i++) { - crtload[textrow*8+i]=crtload[st->rowimage[textrow]*8+i]; - } - startdisplayrow=0; - } - else { - st->rowimage[textrow]=imgrow; - screen_plan[textrow]=imgrow | A2_SP_PUT; - - for (row=textrow*8; rowpattern,0,sizeof(dec->pattern)); - pp=dec->pattern+20; - - if ((st->gr_mode&A2_GR_HIRES) && (row<160 || - (st->gr_mode&A2_GR_FULL))) { - - /* Emulate the mysterious pink line, due to a bit getting - stuck in a shift register between the end of the last - row and the beginning of this one. */ - if ((st->hireslines[row][0] & 0x80) && - (st->hireslines[row][39]&0x40)) { - pp[-1]=1; - } - - for (col=0; col<40; col++) { - u_char b=st->hireslines[row][col]; - int shift=(b&0x80)?0:1; - - /* Each of the low 7 bits in hires mode corresponded to 2 dot - clocks, shifted by one if the high bit was set. */ - for (i=0; i<7; i++) { - pp[shift+1] = pp[shift] =(b>>i)&1; - pp+=2; - } - } - } - else if ((st->gr_mode&A2_GR_LORES) && (row<160 || - (st->gr_mode&A2_GR_FULL))) { - for (col=0; col<40; col++) { - u_char nib=(st->textlines[textrow][col] >> (((row/4)&1)*4))&0xf; - /* The low or high nybble was shifted out one bit at a time. */ - for (i=0; i<14; i++) { - *pp = (nib>>((col*14+i)&3))&1; - pp++; - } - } - } - else { - for (col=0; col<40; col++) { - int rev; - c=st->textlines[textrow][col]; - /* hi bits control inverse/blink as follows: - 0x00: inverse - 0x40: blink - 0x80: normal - 0xc0: normal */ - rev=!(c&0x80) && (!(c&0x40) || st->blink); - - for (i=0; i<7; i++) { - for (i=0; i<7; i++) { - unsigned long pix=XGetPixel(text_im, - ((c&0x3f)^0x20)*7+i, row%8); - pp[1] = pp[2] = pix^rev; - pp+=2; - } - } - } - } - - /* - Interpolate the 600-dotclock line into however many horizontal - screen pixels we're using, and convert to RGB. - - We add some 'bloom', variations in the horizontal scan width with - the amount of brightness, extremely common on period TV sets. They - had a single oscillator which generated both the horizontal scan - and (during the horizontal retrace interval) the high voltage for - the electron beam. More brightness meant more load on the - oscillator, which caused an decrease in horizontal deflection. Look - for (bloomthisrow). - - Also, the A2 did a bad job of generating horizontal sync pulses - during the vertical blanking interval. This, and the fact that the - horizontal frequency was a bit off meant that TVs usually went a - bit out of sync during the vertical retrace, and the top of the - screen would be bent a bit to the left or right. Look for - (shiftthisrow). - - We also add a teeny bit of left overscan, just enough to be - annoying, but you can still read the left column of text. - - We also simulate compression & brightening on the right side of the - screen. Most TVs do this, but you don't notice because they - overscan so it's off the right edge of the CRT. But the A2 video - system used so much of the horizontal scan line that you had to - crank the horizontal width down in order to not lose the right few - characters, and you'd see the compression on the right - edge. Associated with compression is brightening; since the - electron beam was scanning slower, the same drive signal hit the - phosphor harder. Look for (squishright_i) and (squishdiv). - */ - - for (i=j=0; i<600; i++) { - j += dec->pattern[i]; - } - crtload[row] = (crtload[row>1 ? row-1 : 0]) * 0.98 + 0.02*(j/600.0) + - (row>180 ? (row-180)*(row-180)*0.0005 : 0.0); - bloomthisrow = -10.0 * crtload[row]; - shiftthisrow=((row<18) ? ((18-row)*(18-row)* 0.002 + (18-row)*0.05) - * horiz_desync : 0.0); - - scanstart_i=(int)((bloomthisrow+shiftthisrow+18.0)*65536.0); - if (scanstart_i<0) scanstart_i=0; - if (scanstart_i>30*65536) scanstart_i=30*65536; - scanend_i=599*65536; - squishright_i=scanstart_i + 530*65536; - squishdiv=w/15; - pixrate=(int)((560.0-2.0*bloomthisrow)*65536.0/w); - - if (use_cmap) { - for (y=ytop; y=3) && - !(y==ybot-1 && ybot-ytop>=5)); - int hist=0; - int histi=0; - - pixmultinc=pixrate; - for (x=0, i=scanstart_i; - x>16); - int offset=pati&3; - while (pati>=histi) { - hist=(((hist<<1) & ((1<pattern[histi]); - histi++; - } - XPutPixel(image, x, y, - colors[A2_CMAP_INDEX(colormode,level,hist,offset)]); - if (i >= squishright_i) { - pixmultinc += pixmultinc/squishdiv; - } - } - for ( ; x>16; - int r,g,b; - - int interpy=((dec->ntscy[pati]*invpixfrac + - dec->ntscy[pati+1]*pixfrac)>>16); - int interpi=((dec->ntsci[pati]*invpixfrac + - dec->ntsci[pati+1]*pixfrac)>>16); - int interpq=((dec->ntscq[pati]*invpixfrac + - dec->ntscq[pati+1]*pixfrac)>>16); - - /* - According to the NTSC spec, Y,I,Q are generated as: - - y=0.30 r + 0.59 g + 0.11 b - i=0.60 r - 0.28 g - 0.32 b - q=0.21 r - 0.52 g + 0.31 b - - So if you invert the implied 3x3 matrix you get what standard - televisions implement with a bunch of resistors (or directly in - the CRT -- don't ask): - - r = y + 0.948 i + 0.624 q - g = y - 0.276 i - 0.639 q - b = y - 1.105 i + 1.729 q - - These coefficients are below in 16.16 format. - */ - - r=((interpy + ((+68128*interpi+40894*interpq)>>16))*pixbright) - >>16; - g=((interpy + ((-18087*interpi-41877*interpq)>>16))*pixbright) - >>16; - b=((interpy + ((-72417*interpi+113312*interpq)>>16))*pixbright) - >>16; - if (r<0) r=0; - if (g<0) g=0; - if (b<0) b=0; - rrp[0]=r; - rrp[1]=g; - rrp[2]=b; - - if (i>=squishright_i) { - pixmultinc += pixmultinc/squishdiv; - pixbright += pixbright/squishdiv; - } - } - for ( ; xformat==ZPixmap && image->bits_per_pixel==32 && - sizeof(unsigned long)==4 && - image->byte_order==localbyteorder) { - unsigned long *pixelptr = - (unsigned long *) (image->data + y * image->bytes_per_line); - for (x=0, rrp=raw_rgb; x65535) ntscri=65535; - if (ntscgi>65535) ntscgi=65535; - if (ntscbi>65535) ntscbi=65535; - *pixelptr++ = ((ntscri>>red_invprec)<>green_invprec)<>blue_invprec)<format==ZPixmap && image->bits_per_pixel==16 && - sizeof(unsigned short)==2 && - image->byte_order==localbyteorder) { - unsigned short *pixelptr = - (unsigned short *)(image->data + y*image->bytes_per_line); - for (x=0, rrp=raw_rgb; x65535) ntscri=65535; - if (ntscgi>65535) ntscgi=65535; - if (ntscbi>65535) ntscbi=65535; - *pixelptr++ = ((ntscri>>red_invprec)<>green_invprec)<>blue_invprec)<65535) ntscri=65535; - if (ntscgi>65535) ntscgi=65535; - if (ntscbi>65535) ntscbi=65535; - pixel = ((ntscri>>red_invprec)<>green_invprec)<>blue_invprec)<delay; } + break; - /* For just the the rows which changed, blit the image to the screen. */ - for (textrow=0; textrow<24; ) { - int top,bot,srcrow,srctop,nrows; - - nrows=1; - while (textrow+nrows < 24 && - screen_plan[textrow+nrows] == screen_plan[textrow]+nrows) - nrows++; - - top=h*textrow/24; - bot=h*(textrow+nrows)/24; - srcrow=screen_plan[textrow]&A2_SP_ROWMASK; - srctop=srcrow*h/24; - - if (screen_plan[textrow] & A2_SP_COPY) { - if (0) printf("Copy %d screenrows %d to %d\n", nrows, srcrow, textrow); - XCopyArea(dpy, window, window, gc, - screen_xo, screen_yo + srctop, - w, bot-top, - screen_xo, screen_yo + top); - } - else if (screen_plan[textrow] & A2_SP_PUT) { - if (0) printf("Draw %d imgrows %d to %d\n", nrows, srcrow, textrow); - if (use_shm) { -#ifdef HAVE_XSHM_EXTENSION - XShmPutImage(dpy, window, gc, image, - 0, srctop, screen_xo, screen_yo + top, - w, bot-top, False); -#endif - } else { - XPutImage(dpy, window, gc, image, - 0, srctop, - screen_xo, screen_yo + top, - w, bot-top); - } - } - textrow += nrows; + case 300: + for (i=0; i<1500; i++) { + a2_poke(st, mine->fillptr, mine->fillbyte); + mine->fillptr++; + mine->fillbyte = (mine->fillbyte+1)&0xff; } - XSync(dpy,0); - - for (textrow=0; textrow<24; textrow++) { - st->rowimage[textrow]=textrow; + *next_actiontime += 0.08; + /* When you hit c000, it changed video settings */ + if (mine->fillptr>=0xc000) { + a2_invalidate(st); + st->gr_mode=0; } - } + /* And it seemed to reset around here, I dunno why */ + if (mine->fillptr>=0xcf00) *stepno=130; + break; - finished: - XSync(dpy,False); - XClearWindow(dpy, window); - goto cleanup; - - bailout: - ; + case 999: + break; - cleanup: - if (image) { - if (use_shm) { -#ifdef HAVE_XSHM_EXTENSION - destroy_xshm_image(dpy, image, &shm_info); -#endif - } else { - XDestroyImage(image); - } - image=NULL; + case A2CONTROLLER_FREE: + free(mine); + break; } - if (text_im) XDestroyImage(text_im); - if (gc) XFreeGC(dpy, gc); - if (st) free(st); - if (raw_rgb) free(raw_rgb); - if (dec) free(dec); - if (n_colors) XFreeColors(dpy, xgwa.colormap, colors, n_colors, 0L); } +static void +apple2crash (Display* dpy, Window window, int delay) +{ + apple2 (dpy, window, delay, a2controller_crash); +} - char *progclass = "BSOD"; char *defaults [] = { @@ -4084,6 +2965,7 @@ char *defaults [] = { "*doApple2: True", "*doOS390: True", "*doVMS: True", + "*doHVX: True", ".Windows.font: -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*", ".Windows.font2: -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*", @@ -4124,6 +3006,11 @@ char *defaults [] = { ".SCO.foreground: White", ".SCO.background: Black", + ".HVX.font: -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*", + ".HVX.font2: -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*", + ".HVX.foreground: White", + ".HVX.background: Black", + ".Linux.font: 9x15bold", ".Linux.font2: -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*", ".Linux.foreground: White", @@ -4168,6 +3055,8 @@ char *defaults [] = { ".VMS.foreground: White", ".VMS.background: Black", + ANALOGTV_DEFAULTS + #ifdef HAVE_XSHM_EXTENSION "*useSHM: True", #endif @@ -4199,6 +3088,8 @@ XrmOptionDescRec options [] = { { "-no-apple2", ".doApple2", XrmoptionNoArg, "False" }, { "-sco", ".doSCO", XrmoptionNoArg, "True" }, { "-no-sco", ".doSCO", XrmoptionNoArg, "False" }, + { "-hvx", ".doHVX", XrmoptionNoArg, "True" }, + { "-no-hvx", ".doHVX", XrmoptionNoArg, "False" }, { "-bsd", ".doBSD", XrmoptionNoArg, "True" }, { "-no-bsd", ".doBSD", XrmoptionNoArg, "False" }, { "-linux", ".doLinux", XrmoptionNoArg, "True" }, @@ -4215,6 +3106,7 @@ XrmOptionDescRec options [] = { { "-no-os390", ".doOS390", XrmoptionNoArg, "False" }, { "-vms", ".doVMS", XrmoptionNoArg, "True" }, { "-no-vms", ".doVMS", XrmoptionNoArg, "False" }, + ANALOGTV_OPTIONS { 0, 0, 0, 0 } }; @@ -4224,14 +3116,15 @@ static struct { void (*fn) (Display *, Window, int delay); } all_modes[] = { { "Windows", windows_31 }, - { "Nt", windows_nt }, - { "2k", windows_2k }, + { "NT", windows_nt }, + { "Win2K", windows_2k }, { "Amiga", amiga }, { "Mac", mac }, { "MacsBug", macsbug }, { "Mac1", mac1 }, { "MacX", macx }, { "SCO", sco }, + { "HVX", hvx }, { "SparcLinux", sparc_linux }, { "BSD", bsd }, { "Atari", atari }, @@ -4240,7 +3133,7 @@ static struct { { "Linux", linux_fsck }, { "HPUX", hpux }, { "OS390", os390 }, - { "Apple2", apple2 }, + { "Apple2", apple2crash }, { "VMS", vms }, }; @@ -4277,7 +3170,8 @@ screenhack (Display *dpy, Window window) XWindowAttributes xgwa; XGetWindowAttributes (dpy, window, &xgwa); XSelectInput (dpy, window, - xgwa.your_event_mask | KeyPressMask | ButtonPressMask); + xgwa.your_event_mask | + KeyPressMask | ButtonPressMask | ExposureMask); } while (1) diff --git a/hacks/bsod.man b/hacks/bsod.man index b3ddb902..b581d91b 100644 --- a/hacks/bsod.man +++ b/hacks/bsod.man @@ -84,24 +84,21 @@ hacks are displayed and which aren't. .BR doBlitDamage , .BR doSolaris , .BR doHPUX , +.BR doApple2 , .BR doOS390 , +.BR doVMS , and -.BR doApple2 . +.BR doHVX . Each of these is a Boolean resource, they all default to true, except for doAtari, doBSD, and doSparcLinux, which are turned off by default, because they're really not all that interesting looking unless you're a fan of those systems. There are command-line options for all of these: -e.g., \fI\-bsd\fP, \fI\-no-bsd\fP. +e.g., \fI\-bsd\fP, \fI\-no-bsd\fP. (Also note the \fI\-only\fP option.) .SH BUGS -Unlike the systems that the images are borrowed from, -.I bsod -does not require a reboot after running. -.PP -.I bsod -should also emulate more systems, but systems with interesting crash -graphics are not as common as one might hope. +Unlike the systems being simulated, \fIbsod\fP does not require a +reboot after running. .SH SEE ALSO .BR X (1), .BR xscreensaver (1), diff --git a/hacks/bumps.c b/hacks/bumps.c index 1500bcc8..5b73e7c5 100644 --- a/hacks/bumps.c +++ b/hacks/bumps.c @@ -313,7 +313,7 @@ void InitBumpMap( SBumps *pBumps, XWindowAttributes *pXWinAttribs ) p = XCreatePixmap(pBumps->pDisplay, pBumps->Win, pXWinAttribs->width, pXWinAttribs->height, pXWinAttribs->depth); - load_random_image (pXWinAttribs->screen, pBumps->Win, p); + load_random_image (pXWinAttribs->screen, pBumps->Win, p, NULL); pScreenImage = XGetImage( pBumps->pDisplay, p, 0, 0, pBumps->iWinWidth, pBumps->iWinHeight, ~0L, ZPixmap ); XFreePixmap (pBumps->pDisplay, p); diff --git a/hacks/compile_axp.com b/hacks/compile_axp.com index 0612b7b1..7ae7c0a5 100644 --- a/hacks/compile_axp.com +++ b/hacks/compile_axp.com @@ -1,6 +1,9 @@ +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANALOGTV.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANEMONE.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANT.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APOLLONIAN.C +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APPLE2.C +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APPLE2-MAIN.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ATTRACTION.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) BARCODE.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) BLASTER.C @@ -33,6 +36,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLAME.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLOW.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLUIDBALLS.C +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FONTGLIDE.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FOREST.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) GALAXY.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) GOOP.C @@ -73,6 +77,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PHOSPHOR.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PIECEWISE.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) POLYOMINOES.C +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PONG.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) POPSQUARES.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PYRO.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) QIX.C @@ -108,6 +113,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WHIRLWINDWARP.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WHIRLYGIG.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WORM.C +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XANALOGTV.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XFLAME.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XJACK.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XLOCKMORE.C diff --git a/hacks/compile_decc.com b/hacks/compile_decc.com index 0612b7b1..7ae7c0a5 100644 --- a/hacks/compile_decc.com +++ b/hacks/compile_decc.com @@ -1,6 +1,9 @@ +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANALOGTV.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANEMONE.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANT.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APOLLONIAN.C +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APPLE2.C +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APPLE2-MAIN.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ATTRACTION.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) BARCODE.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) BLASTER.C @@ -33,6 +36,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLAME.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLOW.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLUIDBALLS.C +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FONTGLIDE.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FOREST.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) GALAXY.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) GOOP.C @@ -73,6 +77,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PHOSPHOR.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PIECEWISE.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) POLYOMINOES.C +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PONG.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) POPSQUARES.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PYRO.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) QIX.C @@ -108,6 +113,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WHIRLWINDWARP.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WHIRLYGIG.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WORM.C +$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XANALOGTV.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XFLAME.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XJACK.C $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XLOCKMORE.C diff --git a/hacks/config/README b/hacks/config/README index 69aa2028..1975f672 100644 --- a/hacks/config/README +++ b/hacks/config/README @@ -4,8 +4,8 @@ a screen saver and locker for the X window system by Jamie Zawinski - version 4.13 - 07-Sep-2003 + version 4.14 + 25-Oct-2003 http://www.jwz.org/xscreensaver/ diff --git a/hacks/config/apple2.xml b/hacks/config/apple2.xml new file mode 100644 index 00000000..db1388d8 --- /dev/null +++ b/hacks/config/apple2.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + <_description> + +Simulates an original Apple ][ Plus computer in all its 1979 glory. It +also reproduces the appearance of display on a color television set of +the period. + +In "Text Mode", it displays the output of a command (e.g., "fortune".) + +In "Slideshow Mode", it chooses a number of images from the image +source you configured into XScreenSaver and displays them within the +limitations of the Apple ][ display hardware. (Six available colors +in hi-res mode!) + +In "Basic Programming Mode", a simulated user types in a BASIC program +and runs it. + +By Trevor Blackwell. + + diff --git a/hacks/config/blinkbox.xml b/hacks/config/blinkbox.xml new file mode 100644 index 00000000..0c25f39d --- /dev/null +++ b/hacks/config/blinkbox.xml @@ -0,0 +1,16 @@ + + + + + + + + + <_description> +Shows a ball contained inside of a bounding box. +Colored blocks blink in when the ball hits the edges. +Written by Jeremy English. + + diff --git a/hacks/config/bsod.xml b/hacks/config/bsod.xml index d7042e19..35ecf6a8 100644 --- a/hacks/config/bsod.xml +++ b/hacks/config/bsod.xml @@ -27,6 +27,7 @@ + diff --git a/hacks/config/fontglide.xml b/hacks/config/fontglide.xml new file mode 100644 index 00000000..5ac94837 --- /dev/null +++ b/hacks/config/fontglide.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + +
+ + + +
+ + + + + + <_description> +Puts text on the screen using large characters that glide in from the +edges, assemble, then disperse. Alternately, it can simply scroll whole +sentences from right to left. By Jamie Zawinski. + +
diff --git a/hacks/config/gleidescope.xml b/hacks/config/gleidescope.xml new file mode 100644 index 00000000..ec8c1f6e --- /dev/null +++ b/hacks/config/gleidescope.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + <_description> +An OpenGL Kaleidescope. +Written by andrew dean. + + diff --git a/hacks/config/glslideshow.xml b/hacks/config/glslideshow.xml index 4d07efb6..479b2991 100644 --- a/hacks/config/glslideshow.xml +++ b/hacks/config/glslideshow.xml @@ -29,6 +29,8 @@ low="0" high="100000" default="20000" convert="invert"/> + + <_description> diff --git a/hacks/config/mirrorblob.xml b/hacks/config/mirrorblob.xml new file mode 100644 index 00000000..7601266c --- /dev/null +++ b/hacks/config/mirrorblob.xml @@ -0,0 +1,59 @@ + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ +
+ +
+ + + + + + + + +
+ + <_description> +Draws a wobbly blob that distorts the image behind it. +Requires OpenGL hardware acceleration for nice animation. +Written by Jon Dowdall. + +
diff --git a/hacks/config/pong.xml b/hacks/config/pong.xml new file mode 100644 index 00000000..7ed8ee7e --- /dev/null +++ b/hacks/config/pong.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + <_description> +Displays a game of pong. It doesn't keep score +because they are not suppose to miss. +Written by Jeremy English. + + diff --git a/hacks/config/strange.xml b/hacks/config/strange.xml index 7d805cd9..2464c184 100644 --- a/hacks/config/strange.xml +++ b/hacks/config/strange.xml @@ -9,10 +9,6 @@ low="0" high="20000" default="2000" convert="invert"/> - - diff --git a/hacks/config/xanalogtv.xml b/hacks/config/xanalogtv.xml new file mode 100644 index 00000000..e476537d --- /dev/null +++ b/hacks/config/xanalogtv.xml @@ -0,0 +1,18 @@ + + + + + + + + <_description> + +XAnalogTV shows a detailed simulation of an old TV set showing various +test patters, with various picture artifacts like snow, bloom, +distortion, ghosting, and hash noise. It also simulates the TV warming +up. It will cycle through 12 channels, some with images you give it, +and some with color bars or nothing but static. + +By Trevor Blackwell. + + diff --git a/hacks/crystal.c b/hacks/crystal.c index 3ce26cdd..80bcca5b 100644 --- a/hacks/crystal.c +++ b/hacks/crystal.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* crystal --- polygons moving according to plane group rules */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)crystal.c 4.12 98/09/10 xlockmore"; - #endif /*- diff --git a/hacks/decayscreen.c b/hacks/decayscreen.c index 7127bc98..fa6561c1 100644 --- a/hacks/decayscreen.c +++ b/hacks/decayscreen.c @@ -110,7 +110,7 @@ init_decay (Display *dpy, Window window) sizex = xgwa.width; sizey = xgwa.height; - load_random_image (xgwa.screen, window, window); + load_random_image (xgwa.screen, window, window, NULL); if (mode == MELT || mode == STRETCH) { /* make sure screen eventually turns background color */ diff --git a/hacks/deluxe.c b/hacks/deluxe.c index 522e0135..75b98329 100644 --- a/hacks/deluxe.c +++ b/hacks/deluxe.c @@ -335,9 +335,9 @@ screenhack (Display *dpy, Window window) while (1) { - if (!dbeclear_p || + if (!dbeclear_p #ifdef HAVE_DOUBLE_BUFFER_EXTENSION - !backb + || !backb #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ ) XFillRectangle (dpy, b, erase_gc, 0, 0, xgwa.width, xgwa.height); diff --git a/hacks/demon.c b/hacks/demon.c index df7ea3e8..ba6c2b5d 100644 --- a/hacks/demon.c +++ b/hacks/demon.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* demon --- David Griffeath's cellular automata */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)demon.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/discrete.c b/hacks/discrete.c index bf251f57..66c0a3ed 100644 --- a/hacks/discrete.c +++ b/hacks/discrete.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* discrete --- chaotic mappings */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)discrete.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/distort.c b/hacks/distort.c index b9f6cee7..54a12fa9 100644 --- a/hacks/distort.c +++ b/hacks/distort.c @@ -248,7 +248,7 @@ static void init_distort(Display *dpy, Window window) gcflags |= GCSubwindowMode; gc = XCreateGC (dpy, window, gcflags, &gcv); - load_random_image (xgwa.screen, window, window); + load_random_image (xgwa.screen, window, window, NULL); buffer_map = 0; orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height, diff --git a/hacks/drift.c b/hacks/drift.c index 8b4aa63c..38cdf7b7 100644 --- a/hacks/drift.c +++ b/hacks/drift.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* drift --- drifting recursive fractal cosmic flames */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)drift.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/euler2d.c b/hacks/euler2d.c index c73b64ad..7b694e54 100644 --- a/hacks/euler2d.c +++ b/hacks/euler2d.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* euler2d --- 2 Dimensional Incompressible Inviscid Fluid Flow */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)euler2d.c 5.00 2000/11/01 xlockmore"; - #endif /* diff --git a/hacks/fadeplot.c b/hacks/fadeplot.c index ad48b710..1f8ada11 100644 --- a/hacks/fadeplot.c +++ b/hacks/fadeplot.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* fadeplot --- a fading plot of sine squared */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)fadeplot.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/flag.c b/hacks/flag.c index 41311f4a..8d1ab155 100644 --- a/hacks/flag.c +++ b/hacks/flag.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 4 -*- * flag --- a waving flag */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)flag.c 4.02 97/04/01 xlockmore"; #endif diff --git a/hacks/flow.c b/hacks/flow.c index 607994d0..08bd2b04 100644 --- a/hacks/flow.c +++ b/hacks/flow.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* flow --- flow of strange bees */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)flow.c 4.10 98/04/24 xlockmore"; - #endif /*- diff --git a/hacks/fluidballs.c b/hacks/fluidballs.c index 3413a945..0ce65db3 100644 --- a/hacks/fluidballs.c +++ b/hacks/fluidballs.c @@ -370,6 +370,9 @@ init_balls (Display *dpy, Window window) state->m[i] = pow(state->r[i],3) * M_PI * 1.3333; } + memcpy (state->opx, state->px, sizeof (*state->opx) * (state->count + 1)); + memcpy (state->opy, state->py, sizeof (*state->opx) * (state->count + 1)); + return state; } @@ -423,7 +426,7 @@ check_wall_clock (b_state *state, float max_d) if (tick++ > 20) /* don't call gettimeofday() too often -- it's slow. */ { struct timeval now; - static struct timeval last = {0, }; + static struct timeval last = { 0, 0 }; # ifdef GETTIMEOFDAY_TWO_ARGS struct timezone tzp; gettimeofday(&now, &tzp); @@ -483,9 +486,9 @@ repaint_balls (b_state *state) x2b = (state->px[a] + state->r[a] - state->xmin); y2b = (state->py[a] + state->r[a] - state->ymin); - if (!state->dbeclear_p || + if (!state->dbeclear_p #ifdef HAVE_DOUBLE_BUFFER_EXTENSION - !state->backb + || !state->backb #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ ) { diff --git a/hacks/fontglide.c b/hacks/fontglide.c new file mode 100644 index 00000000..02493c85 --- /dev/null +++ b/hacks/fontglide.c @@ -0,0 +1,1589 @@ +/* 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. + * + * fontglide -- reads text from a subprocess and puts it on the screen using + * large characters that glide in from the edges, assemble, then disperse. + * Requires a system with scalable fonts. (X's font handing sucks. A lot.) + */ + +#include +#include "screenhack.h" +#include + +#ifdef HAVE_DOUBLE_BUFFER_EXTENSION +#include "xdbe.h" +#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ + +extern XtAppContext app; + + +typedef struct { + char *text; + int x, y, width, height; + int ascent, lbearing, rbearing; + + int nticks, tick; + int start_x, start_y; + int target_x, target_y; + Pixmap pixmap, mask; +} word; + + +typedef struct { + int id; + XColor fg; + XColor bg; + Bool dark_p; + Bool move_chars_p; + int width; + + char *font_name; + XFontStruct *font; + + GC fg_gc; + + int nwords; + word **words; + + enum { IN, PAUSE, OUT } anim_state; + enum { LEFT, CENTER, RIGHT } alignment; + int pause_tick; + +} sentence; + + +typedef struct { + Display *dpy; + Window window; + XWindowAttributes xgwa; + + Pixmap b, ba; /* double-buffer to reduce flicker */ + GC bg_gc; + +#ifdef HAVE_DOUBLE_BUFFER_EXTENSION + XdbeBackBuffer backb; +#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ + + Bool dbuf; /* Whether we're using double buffering. */ + Bool dbeclear_p; /* ? */ + + int border_width; /* size of the font outline */ + char *charset; /* registry and encoding for font lookups */ + double speed; /* frame rate multiplier */ + double linger; /* multiplier for how long to leave words on screen */ + Bool trails_p; + Bool debug_p; + enum { PAGE, SCROLL } mode; + + char *font_override; /* if -font was specified on the cmd line */ + + FILE *pipe; + XtInputId pipe_id; + Time subproc_relaunch_delay; + Bool input_available_p; + + char buf [40]; /* this only needs to be as big as one "word". */ + int buf_tail; + + int nsentences; + sentence **sentences; + Bool spawn_p; /* whether it is time to create a new sentence */ + int latest_sentence; + +} state; + + +static void launch_text_generator (state *); +static void drain_input (state *s); + + +/* Finds the set of scalable fonts on the system; picks one; + and loads that font in a random pixel size. + Returns False if something went wrong. + */ +static Bool +pick_font_1 (state *s, sentence *se) +{ + char pattern[1024]; + char **names = 0; + char **names2 = 0; + XFontStruct *info = 0; + int count = 0, count2 = 0; + int i; + Bool ok = False; + + if (se->font) + { + XFreeFont (s->dpy, se->font); + free (se->font_name); + se->font = 0; + se->font_name = 0; + } + + if (s->font_override) + sprintf (pattern, "%.200s", s->font_override); + else + sprintf (pattern, "-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s", + "*", /* foundry */ + "*", /* family */ + "*", /* weight */ + "*", /* slant */ + "*", /* swidth */ + "*", /* adstyle */ + "0", /* pixel size */ + "0", /* point size */ + "0", /* resolution x */ + "0", /* resolution y */ + "p", /* spacing */ + "0", /* avg width */ + s->charset); /* registry + encoding */ + + names = XListFonts (s->dpy, pattern, 1000, &count); + + if (count <= 0) + { + if (s->font_override) + fprintf (stderr, "%s: -font option bogus: %s\n", progname, pattern); + else + fprintf (stderr, "%s: no scalable fonts found! (pattern: %s)\n", + progname, pattern); + exit (1); + } + + i = random() % count; + + names2 = XListFontsWithInfo (s->dpy, names[i], 1000, &count2, &info); + if (count2 <= 0) + { + fprintf (stderr, "%s: pattern %s\n" + " gave unusable %s\n\n", + progname, pattern, names[i]); + goto FAIL; + } + + { + XFontStruct *font = &info[0]; + unsigned long value = 0; + char *foundry=0, *family=0, *weight=0, *slant=0, *setwidth=0, *add_style=0; + unsigned long pixel=0, point=0, res_x=0, res_y=0; + char *spacing=0; + unsigned long avg_width=0; + char *registry=0, *encoding=0; + Atom a; + char *bogus = "\"?\""; + +# define STR(ATOM,VAR) \ + bogus = (ATOM); \ + a = XInternAtom (s->dpy, (ATOM), False); \ + if (XGetFontProperty (font, a, &value)) \ + VAR = XGetAtomName (s->dpy, value); \ + else \ + goto FAIL2 + +# define INT(ATOM,VAR) \ + bogus = (ATOM); \ + a = XInternAtom (s->dpy, (ATOM), False); \ + if (!XGetFontProperty (font, a, &VAR) || \ + VAR > 9999) \ + goto FAIL2 + + STR ("FOUNDRY", foundry); + STR ("FAMILY_NAME", family); + STR ("WEIGHT_NAME", weight); + STR ("SLANT", slant); + STR ("SETWIDTH_NAME", setwidth); + STR ("ADD_STYLE_NAME", add_style); + INT ("PIXEL_SIZE", pixel); + INT ("POINT_SIZE", point); + INT ("RESOLUTION_X", res_x); + INT ("RESOLUTION_Y", res_y); + STR ("SPACING", spacing); + INT ("AVERAGE_WIDTH", avg_width); + STR ("CHARSET_REGISTRY", registry); + STR ("CHARSET_ENCODING", encoding); + +#undef INT +#undef STR + + { + double scale = s->xgwa.height / 1024.0; /* shrink for small windows */ + int min, max, r; + + min = scale * 24; + max = scale * 260; + + if (min < 10) min = 10; + if (max < 30) max = 30; + + r = ((max-min)/3)+1; + + pixel = min + ((random() % r) + (random() % r) + (random() % r)); + + if (s->mode == SCROLL) /* scroll mode likes bigger fonts */ + pixel *= 1.5; + } + +#if 0 + /* Occasionally change the aspect ratio of the font, by increasing + either the X or Y resolution (while leaving the other alone.) + + #### Looks like this trick doesn't really work that well: the + metrics of the individual characters are ok, but the + overall font ascent comes out wrong (unscaled.) + */ + if (! (random() % 8)) + { + double n = 2.5 / 3; + double scale = 1 + (frand(n) + frand(n) + frand(n)); + if (random() % 2) + res_x *= scale; + else + res_y *= scale; + } +# endif + + sprintf (pattern, + "-%s-%s-%s-%s-%s-%s-%ld-%s-%ld-%ld-%s-%s-%s-%s", + foundry, family, weight, slant, setwidth, add_style, + pixel, "*", /* point, */ + res_x, res_y, spacing, + "*", /* avg_width */ + registry, encoding); + ok = True; + + FAIL2: + if (!ok) + fprintf (stderr, "%s: font has bogus %s property: %s\n", + progname, bogus, names[i]); + + if (foundry) XFree (foundry); + if (family) XFree (family); + if (weight) XFree (weight); + if (slant) XFree (slant); + if (setwidth) XFree (setwidth); + if (add_style) XFree (add_style); + if (spacing) XFree (spacing); + if (registry) XFree (registry); + if (encoding) XFree (encoding); + } + + FAIL: + + XFreeFontInfo (names2, info, count2); + XFreeFontNames (names); + + if (! ok) return False; + + se->font = XLoadQueryFont (s->dpy, pattern); + if (! se->font) + { + fprintf (stderr, "%s: unable to load font %s\n", + progname, pattern); + return False; + } + + if (se->font->min_bounds.width == se->font->max_bounds.width && + !s->font_override) + { + /* This is to weed out + "-urw-nimbus mono l-medium-o-normal--*-*-*-*-p-*-iso8859-1" and + "-urw-courier-medium-r-normal--*-*-*-*-p-*-iso8859-1". + We asked for only proportional fonts, but this fixed-width font + shows up anyway -- but it has goofy metrics (see below) so it + looks terrible anyway. + */ + if (s->debug_p) + fprintf (stderr, + "%s: skipping bogus monospace non-charcell font: %s\n", + progname, pattern); + return False; + } + + if (s->debug_p) + fprintf(stderr, "%s: %s\n", progname, pattern); + + se->font_name = strdup (pattern); + XSetFont (s->dpy, se->fg_gc, se->font->fid); + return True; +} + + +/* Finds the set of scalable fonts on the system; picks one; + and loads that font in a random pixel size. + */ +static void +pick_font (state *s, sentence *se) +{ + int i; + for (i = 0; i < 20; i++) + if (pick_font_1 (s, se)) + return; + fprintf (stderr, "%s: too many failures: giving up!\n", progname); + exit (1); +} + + +static char *unread_word_text = 0; + +/* Returns a newly-allocated string with one word in it, or NULL if there + is no complete word available. + */ +static char * +get_word_text (state *s) +{ + char *start = s->buf; + char *end; + char *result = 0; + int lfs = 0; + + if (unread_word_text) + { + char *s = unread_word_text; + unread_word_text = 0; + return s; + } + + /* Skip over whitespace at the beginning of the buffer, + and count up how many linebreaks we see while doing so. + */ + while (*start && + (*start == ' ' || + *start == '\t' || + *start == '\r' || + *start == '\n')) + { + if (*start == '\n' || (*start == '\r' && start[1] != '\n')) + lfs++; + start++; + } + + end = start; + + /* If we saw a blank line, then return NULL (treat it as a temporary "eof", + to trigger a sentence break here.) */ + if (lfs >= 2) + goto DONE; + + /* Skip forward to the end of this word (find next whitespace.) */ + while (*end && + (! (*end == ' ' || + *end == '\t' || + *end == '\r' || + *end == '\n'))) + end++; + + /* If we have a word, allocate a string for it */ + if (end > start) + { + result = malloc ((end - start) + 1); + strncpy (result, start, (end-start)); + result [end-start] = 0; + } + + DONE: + + /* Make room in the buffer by compressing out any bytes we've processed. + */ + if (end > s->buf) + { + int n = end - s->buf; + memmove (s->buf, end, sizeof(s->buf) - n); + s->buf_tail -= n; + } + + /* See if there is more to be read, now that there's room in the buffer. */ + drain_input (s); + + return result; +} + + +/* Gets some random text, and creates a "word" object from it. + */ +static word * +new_word (state *s, sentence *se, char *txt, Bool alloc_p) +{ + word *w; + XCharStruct overall; + int dir, ascent, descent; + int bw = s->border_width; + + if (!txt) + return 0; + + w = (word *) calloc (1, sizeof(*w)); + XTextExtents (se->font, txt, strlen(txt), &dir, &ascent, &descent, &overall); + + w->width = overall.rbearing - overall.lbearing + bw + bw; + w->height = overall.ascent + overall.descent + bw + bw; + w->ascent = overall.ascent + bw; + w->lbearing = overall.lbearing - bw; + w->rbearing = overall.width + bw; + +# if 0 + /* The metrics on some fonts are strange -- e.g., + "-urw-nimbus mono l-medium-o-normal--*-*-*-*-p-*-iso8859-1" and + "-urw-courier-medium-r-normal--*-*-*-*-p-*-iso8859-1" both have + an rbearing so wide that it looks like there are two spaces after + each letter. If this character says it has an rbearing that is to + the right of its ink, ignore that. + + #### Of course, this hack only helps when we're in `move_chars_p' mode + and drawing a char at a time -- when we draw the whole word at once, + XDrawString believes the bogus metrics and spaces the font out + crazily anyway. + + Sigh, this causes some text to mis-render in, e.g., + "-adobe-utopia-medium-i-normal--114-*-100-100-p-*-iso8859-1" + (in "ux", we need the rbearing on "r" or we get too much overlap.) + */ + if (w->rbearing > w->width) + w->rbearing = w->width; +# endif /* 0 */ + + if (s->mode == SCROLL && !alloc_p) abort(); + + if (alloc_p) + { + int i, j; + XGCValues gcv; + GC gc0, gc1; + + w->pixmap = XCreatePixmap (s->dpy, s->b, w->width, w->height, 1L); + w->mask = XCreatePixmap (s->dpy, s->b, w->width, w->height, 1L); + + gcv.font = se->font->fid; + gcv.foreground = 0L; + gcv.background = 1L; + gc0 = XCreateGC (s->dpy, w->pixmap, GCFont|GCForeground|GCBackground, + &gcv); + gcv.foreground = 1L; + gcv.background = 0L; + gc1 = XCreateGC (s->dpy, w->pixmap, GCFont|GCForeground|GCBackground, + &gcv); + + XFillRectangle (s->dpy, w->mask, gc0, 0, 0, w->width, w->height); + XFillRectangle (s->dpy, w->pixmap, gc0, 0, 0, w->width, w->height); + + if (s->debug_p) + { + /* bounding box (behind the characters) */ + XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1), + 0, 0, w->width-1, w->height-1); + XDrawRectangle (s->dpy, w->mask, gc1, + 0, 0, w->width-1, w->height-1); + } + + /* Draw foreground text */ + XDrawString (s->dpy, w->pixmap, gc1, -w->lbearing, w->ascent, + txt, strlen(txt)); + + /* Cheesy hack to draw a border */ + /* (I should be able to do this in i*2 time instead of i*i time, + but I can't get it right, so fuck it.) */ + XSetFunction (s->dpy, gc1, GXor); + for (i = -bw; i <= bw; i++) + for (j = -bw; j <= bw; j++) + XCopyArea (s->dpy, w->pixmap, w->mask, gc1, + 0, 0, w->width, w->height, + i, j); + + if (s->debug_p) + { + XSetFunction (s->dpy, gc1, GXset); + if (w->ascent != w->height) + { + /* baseline (on top of the characters) */ + XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1), + 0, w->ascent, w->width-1, w->ascent); + XDrawLine (s->dpy, w->mask, gc1, + 0, w->ascent, w->width-1, w->ascent); + } + + if (w->lbearing != 0) + { + /* left edge of charcell */ + XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1), + w->lbearing, 0, w->lbearing, w->height-1); + XDrawLine (s->dpy, w->mask, gc1, + w->lbearing, 0, w->lbearing, w->height-1); + } + + if (w->rbearing != w->width) + { + /* right edge of charcell */ + XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1), + w->rbearing, 0, w->rbearing, w->height-1); + XDrawLine (s->dpy, w->mask, gc1, + w->rbearing, 0, w->rbearing, w->height-1); + } + } + + XFreeGC (s->dpy, gc0); + XFreeGC (s->dpy, gc1); + } + + w->text = txt; + return w; +} + + +static void +free_word (state *s, word *w) +{ + if (w->text) free (w->text); + if (w->pixmap) XFreePixmap (s->dpy, w->pixmap); + if (w->mask) XFreePixmap (s->dpy, w->mask); +} + + +static sentence * +new_sentence (state *s) +{ + static int id = 0; + XGCValues gcv; + sentence *se = (sentence *) calloc (1, sizeof (*se)); + se->fg_gc = XCreateGC (s->dpy, s->b, 0, &gcv); + se->anim_state = IN; + se->id = ++id; + return se; +} + + +static void +free_sentence (state *s, sentence *se) +{ + int i; + for (i = 0; i < se->nwords; i++) + free_word (s, se->words[i]); + if (se->words) free (se->words); + + if (se->fg.flags) + XFreeColors (s->dpy, s->xgwa.colormap, &se->fg.pixel, 1, 0); + if (se->bg.flags) + XFreeColors (s->dpy, s->xgwa.colormap, &se->bg.pixel, 1, 0); + + if (se->font_name) free (se->font_name); + if (se->font) XFreeFont (s->dpy, se->font); + if (se->fg_gc) XFreeGC (s->dpy, se->fg_gc); + + free (se); +} + + +/* free the word, and put its text back at the front of the input queue, + to be read next time. */ +static void +unread_word (state *s, word *w) +{ + if (unread_word_text) + abort(); + unread_word_text = w->text; + w->text = 0; + free_word (s, w); +} + + +/* Divide each of the words in the sentence into one character words, + without changing the positions of those characters. + */ +static void +split_words (state *s, sentence *se) +{ + word **words2; + int nwords2 = 0; + int i, j; + for (i = 0; i < se->nwords; i++) + nwords2 += strlen (se->words[i]->text); + + words2 = (word **) calloc (nwords2, sizeof(*words2)); + + for (i = 0, j = 0; i < se->nwords; i++) + { + word *ow = se->words[i]; + int L = strlen (ow->text); + int k; + + int x = ow->x; + int y = ow->y; + int sx = ow->start_x; + int sy = ow->start_y; + int tx = ow->target_x; + int ty = ow->target_y; + + for (k = 0; k < L; k++) + { + char *t2 = malloc (2); + word *w2; + int xoff, yoff; + + t2[0] = ow->text[k]; + t2[1] = 0; + w2 = new_word (s, se, t2, True); + words2[j++] = w2; + + xoff = (w2->lbearing - ow->lbearing); + yoff = (ow->ascent - w2->ascent); + + w2->x = x + xoff; + w2->y = y + yoff; + w2->start_x = sx + xoff; + w2->start_y = sy + yoff; + w2->target_x = tx + xoff; + w2->target_y = ty + yoff; + + x += w2->rbearing; + sx += w2->rbearing; + tx += w2->rbearing; + } + + free_word (s, ow); + se->words[i] = 0; + } + free (se->words); + + se->words = words2; + se->nwords = nwords2; +} + + +/* Set the source or destination position of the words to be somewhere + off screen. + */ +static void +scatter_sentence (state *s, sentence *se) +{ + int i = 0; + int off = 100; + + int flock_p = ((random() % 4) == 0); + int mode = (flock_p ? (random() % 12) : 0); + + for (i = 0; i < se->nwords; i++) + { + word *w = se->words[i]; + int x, y; + int r = (flock_p ? mode : (random() % 4)); + switch (r) + { + /* random positions on the edges */ + + case 0: + x = -off - w->width; + y = random() % s->xgwa.height; + break; + case 1: + x = off + s->xgwa.width; + y = random() % s->xgwa.height; + break; + case 2: + x = random() % s->xgwa.width; + y = -off - w->height; + break; + case 3: + x = random() % s->xgwa.width; + y = off + s->xgwa.height; + break; + + /* straight towards the edges */ + + case 4: + x = -off - w->width; + y = w->target_y; + break; + case 5: + x = off + s->xgwa.width; + y = w->target_y; + break; + case 6: + x = w->target_x; + y = -off - w->height; + break; + case 7: + x = w->target_x; + y = off + s->xgwa.height; + break; + + /* corners */ + + case 8: + x = -off - w->width; + y = -off - w->height; + break; + case 9: + x = -off - w->width; + y = off + s->xgwa.height; + break; + case 10: + x = off + s->xgwa.width; + y = off + s->xgwa.height; + break; + case 11: + x = off + s->xgwa.width; + y = -off - w->height; + break; + + default: + abort(); + break; + } + + if (se->anim_state == IN) + { + w->start_x = x; + w->start_y = y; + } + else + { + w->start_x = w->x; + w->start_y = w->y; + w->target_x = x; + w->target_y = y; + } + + w->nticks = ((100 + ((random() % 140) + + (random() % 140) + + (random() % 140))) + / s->speed); + if (w->nticks < 2) + w->nticks = 2; + w->tick = 0; + } +} + + +/* Set the source position of the words to be off the right side, + and the destination to be off the left side. + */ +static void +aim_sentence (state *s, sentence *se) +{ + int i = 0; + int nticks; + int yoff = 0; + + if (se->nwords <= 0) abort(); + + /* Have the sentence shift up or down a little bit; not too far, and + never let it fall off the top or bottom of the screen before its + last character has reached the left edge. + */ + for (i = 0; i < 10; i++) + { + int ty = random() % (s->xgwa.height - se->words[0]->ascent); + yoff = ty - se->words[0]->target_y; + if (yoff < s->xgwa.height/3) /* this one is ok */ + break; + } + + for (i = 0; i < se->nwords; i++) + { + word *w = se->words[i]; + w->start_x = w->target_x + s->xgwa.width; + w->target_x -= se->width; + w->start_y = w->target_y; + w->target_y += yoff; + } + + nticks = ((se->words[0]->start_x - se->words[0]->target_x) + / (s->speed * 10)); + nticks *= (frand(0.9) + frand(0.9) + frand(0.9)); + + if (nticks < 2) + nticks = 2; + + for (i = 0; i < se->nwords; i++) + { + word *w = se->words[i]; + w->nticks = nticks; + w->tick = 0; + } +} + + +/* Randomize the order of the words in the list (since that changes + which ones are "on top".) + */ +static void +shuffle_words (state *s, sentence *se) +{ + int i; + for (i = 0; i < se->nwords-1; i++) + { + int j = i + (random() % (se->nwords - i)); + word *swap = se->words[i]; + se->words[i] = se->words[j]; + se->words[j] = swap; + } +} + + +/* qsort comparitor */ +static int +cmp_sentences (const void *aa, const void *bb) +{ + const sentence *a = *(sentence **) aa; + const sentence *b = *(sentence **) bb; + return ((a ? a->id : 999999) - (b ? b->id : 999999)); +} + + +/* Sort the sentences by id, so that sentences added later are on top. + */ +static void +sort_sentences (state *s) +{ + qsort (s->sentences, s->nsentences, sizeof(*s->sentences), cmp_sentences); +} + + +/* Re-pick the colors of the text and border + */ +static void +recolor (state *s, sentence *se) +{ + if (se->fg.flags) + XFreeColors (s->dpy, s->xgwa.colormap, &se->fg.pixel, 1, 0); + if (se->bg.flags) + XFreeColors (s->dpy, s->xgwa.colormap, &se->bg.pixel, 1, 0); + + se->fg.flags = DoRed|DoGreen|DoBlue; + se->bg.flags = DoRed|DoGreen|DoBlue; + + switch (random() % 2) + { + case 0: /* bright fg, dim bg */ + se->fg.red = (random() % 0x8888) + 0x8888; + se->fg.green = (random() % 0x8888) + 0x8888; + se->fg.blue = (random() % 0x8888) + 0x8888; + se->bg.red = (random() % 0x5555); + se->bg.green = (random() % 0x5555); + se->bg.blue = (random() % 0x5555); + break; + + case 1: /* bright bg, dim fg */ + se->fg.red = (random() % 0x4444); + se->fg.green = (random() % 0x4444); + se->fg.blue = (random() % 0x4444); + se->bg.red = (random() % 0x4444) + 0xCCCC; + se->bg.green = (random() % 0x4444) + 0xCCCC; + se->bg.blue = (random() % 0x4444) + 0xCCCC; + break; + + default: + abort(); + break; + } + + if (s->debug_p) + se->dark_p = (se->fg.red*2 + se->fg.green*3 + se->fg.blue < + se->bg.red*2 + se->bg.green*3 + se->bg.blue); + + if (XAllocColor (s->dpy, s->xgwa.colormap, &se->fg)) + XSetForeground (s->dpy, se->fg_gc, se->fg.pixel); + if (XAllocColor (s->dpy, s->xgwa.colormap, &se->bg)) + XSetBackground (s->dpy, se->fg_gc, se->bg.pixel); +} + + +static void +align_line (state *s, sentence *se, int line_start, int x, int right) +{ + int off, j; + switch (se->alignment) + { + case LEFT: off = 0; break; + case CENTER: off = (right - x) / 2; break; + case RIGHT: off = (right - x); break; + default: abort(); break; + } + + if (off != 0) + for (j = line_start; j < se->nwords; j++) + se->words[j]->target_x += off; +} + + +/* Fill the sentence with new words: in "page" mode, fills the page + with text; in "scroll" mode, just makes one long horizontal sentence. + The sentence might have *no* words in it, if no text is currently + available. + */ +static void +populate_sentence (state *s, sentence *se) +{ + int i = 0; + int left, right, top, x, y; + int space = 0; + int line_start = 0; + Bool done = False; + + int array_size = 100; + + se->move_chars_p = (s->mode == SCROLL ? False : + (random() % 3) ? False : True); + se->alignment = (random() % 3); + + recolor (s, se); + + if (se->words) + { + for (i = 0; i < se->nwords; i++) + free_word (s, se->words[i]); + free (se->words); + } + + se->words = (word **) calloc (array_size, sizeof(*se->words)); + se->nwords = 0; + + switch (s->mode) + { + case PAGE: + left = random() % (s->xgwa.width / 3); + right = s->xgwa.width - (random() % (s->xgwa.width / 3)); + top = random() % (s->xgwa.height * 2 / 3); + break; + case SCROLL: + left = 0; + right = s->xgwa.width; + top = random() % s->xgwa.height; + break; + default: + abort(); + break; + } + + x = left; + y = top; + + while (!done) + { + char *txt = get_word_text (s); + word *w; + if (!txt) + { + if (se->nwords == 0) + return; /* If the stream is empty, bail. */ + else + break; /* If EOF after some words, end of sentence. */ + } + + if (! se->font) /* Got a word: need a font now */ + { + pick_font (s, se); + if (y < se->font->ascent) + y += se->font->ascent; + space = XTextWidth (se->font, " ", 1); + } + + w = new_word (s, se, txt, !se->move_chars_p); + + /* If we have a few words, let punctuation terminate the sentence: + stop gathering more words if the last word ends in a period, etc. */ + if (se->nwords >= 4) + { + char c = w->text[strlen(w->text)-1]; + if (c == '.' || c == '?' || c == '!') + done = True; + } + + /* If the sentence is kind of long already, terminate at commas, etc. */ + if (se->nwords >= 12) + { + char c = w->text[strlen(w->text)-1]; + if (c == ',' || c == ';' || c == ':' || c == '-' || + c == ')' || c == ']' || c == '}') + done = True; + } + + if (se->nwords >= 25) /* ok that's just about enough out of you */ + done = True; + + if (s->mode == PAGE && + x + w->rbearing > right) /* wrap line */ + { + align_line (s, se, line_start, x, right); + line_start = se->nwords; + + x = left; + y += se->font->ascent; + + /* If we're close to the bottom of the screen, stop, and + unread the current word. (But not if this is the first + word, otherwise we might just get stuck on it.) + */ + if (se->nwords > 0 && + y + se->font->ascent > s->xgwa.height) + { + unread_word (s, w); + done = True; + break; + } + } + + w->target_x = x + w->lbearing; + w->target_y = y - w->ascent; + + x += w->rbearing + space; + se->width = x; + + if (se->nwords >= (array_size - 1)) + { + array_size += 100; + se->words = (word **) realloc (se->words, + array_size * sizeof(*se->words)); + if (!se->words) + { + fprintf (stderr, "%s: out of memory (%d words)\n", + progname, array_size); + exit (1); + } + } + + se->words[se->nwords++] = w; + } + + se->width -= space; + + switch (s->mode) + { + case PAGE: + align_line (s, se, line_start, x, right); + if (se->move_chars_p) + split_words (s, se); + scatter_sentence (s, se); + shuffle_words (s, se); + break; + case SCROLL: + aim_sentence (s, se); + break; + default: + abort(); + break; + } + +# ifdef DEBUG + if (s->debug_p) + { + fprintf (stderr, "%s: sentence %d:", progname, se->id); + for (i = 0; i < se->nwords; i++) + fprintf (stderr, " %s", se->words[i]->text); + fprintf (stderr, "\n"); + } +# endif +} + + +/* Render a single word object to the screen. + */ +static void +draw_word (state *s, sentence *se, word *w) +{ + if (! w->pixmap) return; + + if (w->x + w->width < 0 || + w->y + w->height < 0 || + w->x > s->xgwa.width || + w->y > s->xgwa.height) + return; + + XSetClipMask (s->dpy, se->fg_gc, w->mask); + XSetClipOrigin (s->dpy, se->fg_gc, w->x, w->y); + XCopyPlane (s->dpy, w->pixmap, s->b, se->fg_gc, + 0, 0, w->width, w->height, + w->x, w->y, + 1L); +} + + +/* If there is room for more sentences, add one. + */ +static void +more_sentences (state *s) +{ + int i; + Bool any = False; + for (i = 0; i < s->nsentences; i++) + { + sentence *se = s->sentences[i]; + if (! se) + { + se = new_sentence (s); + populate_sentence (s, se); + if (se->nwords > 0) + s->spawn_p = False, any = True; + else + { + free_sentence (s, se); + se = 0; + } + s->sentences[i] = se; + if (se) + s->latest_sentence = se->id; + break; + } + } + + if (any) sort_sentences (s); +} + + +/* Render all the words to the screen, and run the animation one step. + */ +static void +draw_sentence (state *s, sentence *se) +{ + int i; + Bool moved = False; + + if (! se) return; + + for (i = 0; i < se->nwords; i++) + { + word *w = se->words[i]; + + switch (s->mode) + { + case PAGE: + if (se->anim_state != PAUSE && + w->tick <= w->nticks) + { + int dx = w->target_x - w->start_x; + int dy = w->target_y - w->start_y; + double r = sin (w->tick * M_PI / (2 * w->nticks)); + w->x = w->start_x + (dx * r); + w->y = w->start_y + (dy * r); + + w->tick++; + if (se->anim_state == OUT && s->mode == PAGE) + w->tick++; /* go out faster */ + moved = True; + } + break; + case SCROLL: + { + int dx = w->target_x - w->start_x; + int dy = w->target_y - w->start_y; + double r = (double) w->tick / w->nticks; + w->x = w->start_x + (dx * r); + w->y = w->start_y + (dy * r); + w->tick++; + moved = (w->tick <= w->nticks); + + /* Launch a new sentence when: + - the front of this sentence is almost off the left edge; + - the end of this sentence is almost on screen. + - or, randomly + */ + if (se->anim_state != OUT && + i == 0 && + se->id == s->latest_sentence) + { + Bool new_p = (w->x < (s->xgwa.width * 0.4) && + w->x + se->width < (s->xgwa.width * 2.1)); + Bool rand_p = (new_p ? 0 : !(random() % 2000)); + + if (new_p || rand_p) + { + se->anim_state = OUT; + s->spawn_p = True; +# ifdef DEBUG + if (s->debug_p) + fprintf (stderr, "%s: OUT %d (x2 = %d%s)\n", + progname, se->id, + se->words[0]->x + se->width, + rand_p ? " randomly" : ""); +# endif + } + } + } + break; + default: + abort(); + break; + } + + draw_word (s, se, w); + } + + if (moved && se->anim_state == PAUSE) + abort(); + + if (! moved) + { + switch (se->anim_state) + { + case IN: + se->anim_state = PAUSE; + se->pause_tick = (se->nwords * 7 * s->linger); + if (se->move_chars_p) + se->pause_tick /= 5; + scatter_sentence (s, se); + shuffle_words (s, se); +# ifdef DEBUG + if (s->debug_p) + fprintf (stderr, "%s: PAUSE %d\n", progname, se->id); +# endif + break; + case PAUSE: + if (--se->pause_tick <= 0) + { + se->anim_state = OUT; + s->spawn_p = True; +# ifdef DEBUG + if (s->debug_p) + fprintf (stderr, "%s: OUT %d\n", progname, se->id); +# endif + } + break; + case OUT: +# ifdef DEBUG + if (s->debug_p) + fprintf (stderr, "%s: DEAD %d\n", progname, se->id); +# endif + { + int j; + for (j = 0; j < s->nsentences; j++) + if (s->sentences[j] == se) + s->sentences[j] = 0; + free_sentence (s, se); + } + break; + default: + abort(); + break; + } + } +} + + +/* Render all the words to the screen, and run the animation one step. + Clear screen first, swap buffers after. + */ +static void +draw_fontglide (state *s) +{ + int i; + + if (s->spawn_p) + more_sentences (s); + + if (!s->trails_p) + XFillRectangle (s->dpy, s->b, s->bg_gc, + 0, 0, s->xgwa.width, s->xgwa.height); + + for (i = 0; i < s->nsentences; i++) + draw_sentence (s, s->sentences[i]); + +#ifdef HAVE_DOUBLE_BUFFER_EXTENSION + if (s->backb) + { + XdbeSwapInfo info[1]; + info[0].swap_window = s->window; + info[0].swap_action = (s->dbeclear_p ? XdbeBackground : XdbeUndefined); + XdbeSwapBuffers (s->dpy, info, 1); + } + else +#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ + if (s->dbuf) + { + XCopyArea (s->dpy, s->b, s->window, s->bg_gc, + 0, 0, s->xgwa.width, s->xgwa.height, 0, 0); + } +} + + +static void +handle_events (state *s) +{ + while (XPending (s->dpy)) + { + XEvent event; + XNextEvent (s->dpy, &event); + + if (event.xany.type == ConfigureNotify) + { + XGetWindowAttributes (s->dpy, s->window, &s->xgwa); + + if (s->dbuf && (s->ba)) + { + XFreePixmap (s->dpy, s->ba); + s->ba = XCreatePixmap (s->dpy, s->window, + s->xgwa.width, s->xgwa.height, + s->xgwa.depth); + XFillRectangle (s->dpy, s->ba, s->bg_gc, 0, 0, + s->xgwa.width, s->xgwa.height); + s->b = s->ba; + } + } + + screenhack_handle_event (s->dpy, &event); + } +} + + + +/* Subprocess. + (This bit mostly cribbed from phosphor.c) + */ + +static void +subproc_cb (XtPointer closure, int *source, XtInputId *id) +{ + state *s = (state *) closure; + s->input_available_p = True; +} + + +static void +launch_text_generator (state *s) +{ + char *oprogram = get_string_resource ("program", "Program"); + char *program; + + program = (char *) malloc (strlen (oprogram) + 10); + strcpy (program, "( "); + strcat (program, oprogram); + strcat (program, " ) 2>&1"); + + if (s->debug_p) + fprintf (stderr, "%s: forking: %s\n", progname, program); + + if ((s->pipe = popen (program, "r"))) + { + s->pipe_id = + XtAppAddInput (app, fileno (s->pipe), + (XtPointer) (XtInputReadMask | XtInputExceptMask), + subproc_cb, (XtPointer) s); + } + else + { + perror (program); + } +} + + +static void +relaunch_generator_timer (XtPointer closure, XtIntervalId *id) +{ + state *s = (state *) closure; + launch_text_generator (s); +} + + +/* When the subprocess has generated some output, this reads as much as it + can into s->buf at s->buf_tail. + */ +static void +drain_input (state *s) +{ + /* allow subproc_cb() to run, if the select() down in Xt says that + input is available. This tells us whether we can read() without + blocking. */ + if (! s->input_available_p) + if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput)) + XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput); + + if (! s->pipe) return; + if (! s->input_available_p) return; + s->input_available_p = False; + + if (s->buf_tail < sizeof(s->buf) - 2) + { + int target = sizeof(s->buf) - s->buf_tail - 2; + int n; + n = read (fileno (s->pipe), + (void *) (s->buf + s->buf_tail), + target); + if (n > 0) + { + s->buf_tail += n; + s->buf[s->buf_tail] = 0; + } + else + { + XtRemoveInput (s->pipe_id); + s->pipe_id = 0; + pclose (s->pipe); + s->pipe = 0; + + /* If the process didn't print a terminating newline, add one. */ + if (s->buf_tail > 1 && + s->buf[s->buf_tail-1] != '\n') + { + s->buf[s->buf_tail++] = '\n'; + s->buf[s->buf_tail] = 0; + } + + /* Then add one more, to make sure there's a sentence break at EOF. + */ + s->buf[s->buf_tail++] = '\n'; + s->buf[s->buf_tail] = 0; + + /* Set up a timer to re-launch the subproc in a bit. */ + XtAppAddTimeOut (app, s->subproc_relaunch_delay, + relaunch_generator_timer, + (XtPointer) s); + } + } +} + + +/* Window setup and resource loading */ + +static state * +init_fontglide (Display *dpy, Window window) +{ + XGCValues gcv; + state *s = (state *) calloc (1, sizeof(*s)); + s->dpy = dpy; + s->window = window; + + XGetWindowAttributes (s->dpy, s->window, &s->xgwa); + + s->font_override = get_string_resource ("font", "Font"); + if (s->font_override && (!*s->font_override || *s->font_override == '(')) + s->font_override = 0; + + s->charset = get_string_resource ("fontCharset", "FontCharset"); + s->border_width = get_integer_resource ("fontBorderWidth", "Integer"); + if (s->border_width < 0 || s->border_width > 20) + s->border_width = 1; + + s->speed = get_float_resource ("speed", "Float"); + if (s->speed <= 0 || s->speed > 200) + s->speed = 1; + + s->linger = get_float_resource ("linger", "Float"); + if (s->linger <= 0 || s->linger > 200) + s->linger = 1; + + s->debug_p = get_boolean_resource ("debug", "Debug"); + s->trails_p = get_boolean_resource ("trails", "Trails"); + + s->dbuf = get_boolean_resource ("doubleBuffer", "Boolean"); + s->dbeclear_p = get_boolean_resource ("useDBEClear", "Boolean"); + + if (s->trails_p) s->dbuf = False; /* don't need it in this case */ + + { + char *ss = get_string_resource ("mode", "Mode"); + if (!ss || !*ss || !strcasecmp (ss, "random")) + s->mode = ((random() % 2) ? SCROLL : PAGE); + else if (!strcasecmp (ss, "scroll")) + s->mode = SCROLL; + else if (!strcasecmp (ss, "page")) + s->mode = PAGE; + else + { + fprintf (stderr, + "%s: `mode' must be `scroll', `page', or `random', not `%s'\n", + progname, ss); + } + } + + if (s->dbuf) + { +#ifdef HAVE_DOUBLE_BUFFER_EXTENSION + if (s->dbeclear_p) + s->b = xdbe_get_backbuffer (dpy, window, XdbeBackground); + else + s->b = xdbe_get_backbuffer (dpy, window, XdbeUndefined); + s->backb = s->b; +#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ + + if (!s->b) + { + s->ba = XCreatePixmap (s->dpy, s->window, + s->xgwa.width, s->xgwa.height, + s->xgwa.depth); + s->b = s->ba; + } + } + else + { + s->b = s->window; + } + + gcv.foreground = get_pixel_resource ("background", "Background", + s->dpy, s->xgwa.colormap); + s->bg_gc = XCreateGC (s->dpy, s->b, GCForeground, &gcv); + + s->subproc_relaunch_delay = 2 * 1000; + + launch_text_generator (s); + + s->nsentences = 5; /* #### */ + s->sentences = (sentence **) calloc (s->nsentences, sizeof (sentence *)); + s->spawn_p = True; + + return s; +} + + + +char *progclass = "FontGlide"; + +char *defaults [] = { + ".background: #000000", + ".foreground: #DDDDDD", + ".borderColor: #555555", + "*delay: 10000", + "*program: " FORTUNE_PROGRAM, + "*mode: random", + ".font: (default)", + "*fontCharset: iso8859-1", + "*fontBorderWidth: 2", + "*speed: 1.0", + "*linger: 1.0", + "*trails: False", + "*debug: False", + "*doubleBuffer: True", +#ifdef HAVE_DOUBLE_BUFFER_EXTENSION + "*useDBE: True", + "*useDBEClear: True", +#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ + 0 +}; + +XrmOptionDescRec options [] = { + { "-scroll", ".mode", XrmoptionNoArg, "scroll" }, + { "-page", ".mode", XrmoptionNoArg, "page" }, + { "-random", ".mode", XrmoptionNoArg, "random" }, + { "-delay", ".delay", XrmoptionSepArg, 0 }, + { "-speed", ".speed", XrmoptionSepArg, 0 }, + { "-linger", ".linger", XrmoptionSepArg, 0 }, + { "-program", ".program", XrmoptionSepArg, 0 }, + { "-font", ".font", XrmoptionSepArg, 0 }, + { "-fn", ".font", XrmoptionSepArg, 0 }, + { "-bw", ".fontBorderWidth", XrmoptionSepArg, 0 }, + { "-trails", ".trails", XrmoptionNoArg, "True" }, + { "-no-trails", ".trails", XrmoptionNoArg, "False" }, + { "-db", ".doubleBuffer", XrmoptionNoArg, "True" }, + { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" }, + { "-debug", ".debug", XrmoptionNoArg, "True" }, + { 0, 0, 0, 0 } +}; + + +void +screenhack (Display *dpy, Window window) +{ + state *s = init_fontglide (dpy, window); + int delay = get_integer_resource ("delay", "Integer"); + + while (1) + { + handle_events (s); + draw_fontglide (s); + XSync (dpy, False); + if (delay) usleep (delay); + } +} diff --git a/hacks/fontglide.man b/hacks/fontglide.man new file mode 100644 index 00000000..bf036d5c --- /dev/null +++ b/hacks/fontglide.man @@ -0,0 +1,119 @@ +.TH XScreenSaver 1 "30-Oct-99" "X Version 11" +.SH NAME +fontglide - characters float onto the screen to form words +.SH SYNOPSIS +.B fontglide +[\-display \fIhost:display.screen\fP] [\-window] [\-root] [\-install] +[\-visual \fIvisual\fP] +[\-delay \fIusecs\fP] +[\-scroll\fP] +[\-page\fP] +[\-random\fP] +[\-speed \fIfloat\fP] +[\-linger \fIfloat\fP] +[\-program \fIsh-command\fP] +[\-font \fIfont-name\fP] +[\-bw \fIint\fP] +[\-trails] +[\-db] +[\-debug] +.SH DESCRIPTION +The \fIfontglide\fP program reads text from a subprocess and puts it on +the screen using large characters that glide in from the edges, +assemble, then disperse. Alternately, it can simply scroll whole +sentences from right to left. +.SH OPTIONS +.I fontglide +accepts the following options: +.TP 8 +.B \-window +Draw on a newly-created window. This is the default. +.TP 8 +.B \-root +Draw on the root window. +.TP 8 +.B \-install +Install a private colormap for the window. +.TP 8 +.B \-visual \fIvisual\fP\fP +Specify which visual to use. Legal values are the name of a visual class, +or the id number (decimal or hex) of a specific visual. +.TP 8 +.B \-delay \fIusecs\fP +The delay between steps of the animation, in microseconds: default 10000. +.TP 8 +.B \-page +With this option, a page full of text will glide in, and disperse. +.TP 8 +.B \-scroll +With this option, sentences will scroll by from right to left. +.TP 8 +.B \-random +The default is to pick randomly between \fI\-page\fP and \fI\-scroll\fP. +.TP 8 +.B \-speed \fIfloat\fP +How fast to animate; 2 means twice as fast, 0.5 means half as fast. +Default 1.0. +.TP 8 +.B \-linger \fIfloat\fP +How long to leave the assembled text on the screen in \fI\-page\fP mode; +2 means twice as long, 0.5 means half as long. Default 1.0. (The more +words there are on the screen, the longer it lingers.) +.TP 8 +.B \-program \fIsh-command\fP +The command to run to generate the text to display. This option may be +any string acceptable to /bin/sh. The program will be run at the end of +a pipe, and any words that it prints to \fIstdout\fP will end up on +the window. (Whitespace and line breaks are ignored.) If the program +exits, it will be launched again after we have processed all the text +it produced. Default: +.BR fortune (1). +.TP 8 +.B \-font\fP \fIstring\fP +The base font pattern to use when loading fonts. The default is to search +for any Latin1 scalable proportional fonts on the system. Once a base font +is selected, it will be loaded in a random size. +.TP 8 +.B \-bw \fIint\fP +How thick an outline to draw around the characters. Default 2 pixels. +.TP 8 +.B \-trails\fP +Leave "vapor trails" behind the moving text. Default off. +.TP 8 +.B \-no-db\fP +Turn off double-buffering. It may be faster, but will flicker. +.TP 8 +.B \-debug\fP +Draw some boxes showing character metrics, and print the name of the +current font to stderr. +.SH ENVIRONMENT +.PP +.TP 8 +.B DISPLAY +to get the default host and display number. +.TP 8 +.B XENVIRONMENT +to get the name of a resource file that overrides the global resources +stored in the RESOURCE_MANAGER property. +.SH SEE ALSO +.BR xscreensaver (1), +.BR fortune (1), +.BR phosphor (1), +.BR apple2 (1), +.BR starwars (1), +.BR ljlatest (1), +.BR dadadodo (1), +.BR webcollage (1), +.BR driftnet (1) +.BR EtherPEG , +.BR EtherPeek +.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 , 15-Sep-2003. diff --git a/hacks/galaxy.c b/hacks/galaxy.c index 08d870bb..87e698b1 100644 --- a/hacks/galaxy.c +++ b/hacks/galaxy.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* galaxy --- spinning galaxies */ /* #include*/ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)galaxy.c 4.04 97/07/28 xlockmore"; #endif diff --git a/hacks/glx/Makefile.in b/hacks/glx/Makefile.in index 6e683412..84609e08 100644 --- a/hacks/glx/Makefile.in +++ b/hacks/glx/Makefile.in @@ -94,7 +94,8 @@ SRCS = xscreensaver-gl-helper.c \ bouncingcow.c cow_face.c cow_hide.c cow_hoofs.c cow_horns.c \ cow_tail.c cow_udder.c glslideshow.c jigglypuff.c klein.c \ hypertorus.c glmatrix.c cubestorm.c glknots.c blocktube.c \ - flipflop.c antspotlight.c polytopes.c + flipflop.c antspotlight.c polytopes.c gleidescope.c \ + mirrorblob.c blinkbox.c OBJS = xscreensaver-gl-helper.o \ atlantis.o b_draw.o b_lockglue.o b_sphere.o bubble3d.o \ @@ -121,7 +122,8 @@ OBJS = xscreensaver-gl-helper.o \ bouncingcow.o cow_face.o cow_hide.o cow_hoofs.o cow_horns.o \ cow_tail.o cow_udder.o glslideshow.o jigglypuff.o klein.o \ hypertorus.o glmatrix.o cubestorm.o glknots.o blocktube.o \ - flipflop.o antspotlight.o polytopes.o + flipflop.o antspotlight.o polytopes.o gleidescope.o \ + mirrorblob.o blinkbox.o GL_EXES = cage gears moebius pipes sproingies stairs superquadrics \ morph3d rubik atlantis lament bubble3d glplanet pulsar \ @@ -130,7 +132,8 @@ GL_EXES = cage gears moebius pipes sproingies stairs superquadrics \ glforestfire sballs cubenetic spheremonics lavalite queens \ endgame glblur flurry atunnel flyingtoasters bouncingcow \ glslideshow jigglypuff klein hypertorus glmatrix cubestorm \ - glknots blocktube flipflop antspotlight polytopes + glknots blocktube flipflop antspotlight polytopes \ + gleidescope mirrorblob blinkbox GLE_EXES = extrusion GL_UTIL_EXES = xscreensaver-gl-helper HACK_EXES = @GL_EXES@ @GLE_EXES@ @@ -161,7 +164,8 @@ GL_MEN = atlantis.man boxed.man bubble3d.man cage.man circuit.man \ flyingtoasters.man bouncingcow.man glslideshow.man \ jigglypuff.man klein.man hypertorus.man glmatrix.man \ cubestorm.man glknots.man blocktube.man flipflop.man \ - antspotlight.man polytopes.man + antspotlight.man polytopes.man gleidescope.man \ + mirrorblob.man blinkbox.man MEN = @GL_MEN@ EXTRAS = README Makefile.in dxf2gl.pl @@ -378,11 +382,11 @@ TRACK_OBJS=rotator.o trackball.o gltrackball.o ATLANTIS_OBJS = $(HACK_OBJS) dolphin.o shark.o swim.o whale.o xpm-ximage.o atlantis: atlantis.o $(ATLANTIS_OBJS) - $(CC_HACK) -o $@ $@.o $(ATLANTIS_OBJS) $(HACK_LIBS) $(XPM_LIBS) + $(CC_HACK) -o $@ $@.o $(ATLANTIS_OBJS) $(XPM_LIBS) ATUNNEL_OBJS = $(HACK_OBJS) tunnel_draw.o xpm-ximage.o atunnel: atunnel.o $(ATUNNEL_OBJS) - $(CC_HACK) -o $@ $@.o $(ATUNNEL_OBJS) $(HACK_LIBS) $(XPM_LIBS) + $(CC_HACK) -o $@ $@.o $(ATUNNEL_OBJS) $(XPM_LIBS) cage: cage.o $(HACK_OBJS) $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(HACK_LIBS) @@ -432,7 +436,7 @@ glplanet: glplanet.o $(PLANETHACKS) $(HACK_OBJS) $(CC_HACK) -o $@ $@.o $(PLANETHACKS) $(HACK_OBJS) $(XPM_LIBS) pulsar: pulsar.o $(HACK_OBJS) xpm-ximage.o - $(CC_HACK) -o $@ $@.o $(HACK_OBJS) xpm-ximage.o $(HACK_LIBS) $(XPM_LIBS) + $(CC_HACK) -o $@ $@.o $(HACK_OBJS) xpm-ximage.o $(XPM_LIBS) EXTRUSION_OBJS=extrusion.o extrusion-helix2.o extrusion-helix3.o \ extrusion-helix4.o extrusion-joinoffset.o extrusion-screw.o \ @@ -538,7 +542,7 @@ glknots: glknots.o tube.o $(TRACK_OBJS) $(HACK_OBJS) $(CC_HACK) -o $@ $@.o tube.o $(TRACK_OBJS) $(HACK_OBJS) $(HACK_LIBS) blocktube: blocktube.o $(HACK_OBJS) xpm-ximage.o - $(CC_HACK) -o $@ $@.o $(HACK_OBJS) xpm-ximage.o $(HACK_LIBS) $(XPM_LIBS) + $(CC_HACK) -o $@ $@.o $(HACK_OBJS) xpm-ximage.o $(XPM_LIBS) flipflop: flipflop.o $(TRACK_OBJS) $(HACK_OBJS) $(CC_HACK) -o $@ $@.o $(TRACK_OBJS) $(HACK_OBJS) $(HACK_LIBS) @@ -552,7 +556,7 @@ polytopes: polytopes.o $(HACK_OBJS) # This one works differently (it's not xlock-like.) # STONER_OBJS=stonerview.o stonerview-move.o stonerview-osc.o stonerview-view.o \ - $(UTILS_BIN)/yarandom.o + $(UTILS_BIN)/yarandom.o $(UTILS_BIN)/usleep.o stonerview: $(STONER_OBJS) $(CC_HACK) -o $@ $(STONER_OBJS) $(HACK_LIBS) @@ -571,8 +575,17 @@ MOLECULEOBJS=sphere.o tube.o $(TRACK_OBJS) molecule: molecule.o $(MOLECULEOBJS) $(HACK_OBJS) $(CC_HACK) -o $@ $@.o $(MOLECULEOBJS) $(HACK_OBJS) $(HACK_LIBS) -dnalogo: dnalogo.o tube.o $(HACK_OBJS) - $(CC_HACK) -o $@ $@.o tube.o $(HACK_OBJS) $(HACK_LIBS) +gleidescope: gleidescope.o xpm-ximage.o $(HACK_OBJS) $(GRAB_OBJS) + $(CC_HACK) -o $@ $@.o xpm-ximage.o $(HACK_OBJS) $(GRAB_OBJS) $(XPM_LIBS) + +mirrorblob: mirrorblob.o $(HACK_OBJS) $(GRAB_OBJS) + $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(GRAB_OBJS) $(XPM_LIBS) + +blinkbox: blinkbox.o sphere.o $(HACK_OBJS) + $(CC_HACK) -o $@ $@.o sphere.o $(HACK_OBJS) $(HACK_LIBS) + +dnalogo: dnalogo.o tube.o $(TRACK_OBJS) $(HACK_OBJS) + $(CC_HACK) -o $@ $@.o tube.o $(TRACK_OBJS) $(HACK_OBJS) $(HACK_LIBS) ############################################################################## # @@ -595,6 +608,8 @@ atunnel.o: $(srcdir)/tunnel_draw.h atunnel.o: $(srcdir)/xpm-ximage.h b_draw.o: $(srcdir)/bubble3d.h b_draw.o: ../../config.h +blinkbox.o: ../../config.h +blinkbox.o: $(srcdir)/sphere.h b_lockglue.o: $(srcdir)/bubble3d.h b_lockglue.o: ../../config.h blocktube.o: ../../config.h @@ -694,6 +709,9 @@ gflux.o: $(srcdir)/grab-ximage.h glblur.o: ../../config.h glblur.o: $(srcdir)/gltrackball.h glblur.o: $(srcdir)/rotator.h +gleidescope.o: ../../config.h +gleidescope.o: $(srcdir)/grab-ximage.h +gleidescope.o: $(srcdir)/xpm-ximage.h glforestfire.o: ../../config.h glforestfire.o: $(HACK_SRC)/images/ground.xpm glforestfire.o: $(HACK_SRC)/images/tree.xpm @@ -753,6 +771,8 @@ marching.o: $(srcdir)/marching.h menger.o: ../../config.h menger.o: $(srcdir)/gltrackball.h menger.o: $(srcdir)/rotator.h +mirrorblob.o: ../../config.h +mirrorblob.o: $(srcdir)/grab-ximage.h moebius.o: ../../config.h moebius.o: $(srcdir)/e_textures.h moebius.o: $(srcdir)/gltrackball.h diff --git a/hacks/glx/antspotlight.c b/hacks/glx/antspotlight.c index d84982a2..a129b799 100644 --- a/hacks/glx/antspotlight.c +++ b/hacks/glx/antspotlight.c @@ -357,6 +357,7 @@ void build_ant(void) { ant->direction = 0.0; ant->velocity = 0.02; ant->material = MaterialGray5; + ant->step = 0; find_goal(); } @@ -479,6 +480,8 @@ void draw_antspotlight_strip(ModeInfo *mi) { ant->position[0] += ant->velocity * cos(ant->direction); ant->position[2] += ant->velocity * sin(-ant->direction); ant->step += 10*ant->velocity; + while(ant->step > 2*Pi) + ant->step -= 2*Pi; } void reshape_antspotlight(ModeInfo * mi, int width, int height) { @@ -625,7 +628,7 @@ void get_snapshot(ModeInfo *modeinfo) { if(MI_IS_WIREFRAME(modeinfo)) return; - ximage = screen_to_ximage(modeinfo->xgwa.screen, modeinfo->window); + ximage = screen_to_ximage(modeinfo->xgwa.screen, modeinfo->window, NULL); qw = QW; qh = QH; tw = modeinfo->xgwa.width; diff --git a/hacks/glx/atlantis.c b/hacks/glx/atlantis.c index e7fc50f1..e0a90ae8 100644 --- a/hacks/glx/atlantis.c +++ b/hacks/glx/atlantis.c @@ -1,8 +1,7 @@ /* atlantis --- Shows moving 3D sea animals */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)atlantis.c 5.08 2003/04/09 xlockmore"; - #endif /* Copyright (c) E. Lassauge, 1998. */ diff --git a/hacks/glx/atunnel.c b/hacks/glx/atunnel.c index 7f73a7dc..6d3b8b62 100644 --- a/hacks/glx/atunnel.c +++ b/hacks/glx/atunnel.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* atunnels --- OpenGL Advanced Tunnel Screensaver */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)atunnels.c 5.07 2003/02/12 xlockmore"; #endif diff --git a/hacks/glx/b_draw.c b/hacks/glx/b_draw.c index 18c89d9a..0986156d 100644 --- a/hacks/glx/b_draw.c +++ b/hacks/glx/b_draw.c @@ -1,6 +1,5 @@ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)b_draw.c 4.11 98/06/16 xlockmore"; - #endif /*- diff --git a/hacks/glx/b_lockglue.c b/hacks/glx/b_lockglue.c index fba029e4..d0bc7229 100644 --- a/hacks/glx/b_lockglue.c +++ b/hacks/glx/b_lockglue.c @@ -1,6 +1,5 @@ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)b_lockglue.c 4.11 98/06/16 xlockmore"; - #endif /*- diff --git a/hacks/glx/b_sphere.c b/hacks/glx/b_sphere.c index 2f8b25f6..9177b351 100644 --- a/hacks/glx/b_sphere.c +++ b/hacks/glx/b_sphere.c @@ -1,6 +1,5 @@ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)b_sphere.c 4.11 98/06/16 xlockmore"; - #endif /*- diff --git a/hacks/glx/blinkbox.c b/hacks/glx/blinkbox.c new file mode 100644 index 00000000..bbd1e599 --- /dev/null +++ b/hacks/glx/blinkbox.c @@ -0,0 +1,351 @@ +/* blinkbox, Copyright (c) 2003 Jeremy English + * + * 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 + +extern XtAppContext app; + +#define PROGCLASS "BlinkBox" +#define HACK_INIT init_ball +#define HACK_DRAW draw_ball +#define HACK_RESHAPE reshape_ball +#define sws_opts xlockmore_opts + +#define MAX_COUNT 20 + +#define DEFAULTS "*delay: 30000 \n" \ + +#include "xlockmore.h" +#include "sphere.h" +#include + +#ifdef USE_GL /* whole file */ + + +#include + +typedef struct{ + GLfloat x,y,z; +} Tdpos; + +typedef struct{ + int hit; + Tdpos pos; + int counter; + GLfloat color[3]; + GLfloat rot[4]; +}Side; + +struct Bounding_box { + Tdpos top; + Tdpos bottom; +} bbox = {{7,7,10},{-7,-7,-10}}; + +struct Ball { + GLfloat x; + GLfloat y; + GLfloat z; + int d; +} ball = {0,0,0,1}; + +static GLuint ballList; +static GLuint boxList; +Tdpos mo = { 1.0, 1.0, 1.0}; /*motion*/ +Tdpos moh= {-1.0,-1.5,-1.5}; /*hold motion value*/ + +GLfloat bscale[3] = {1,1,0.25}; +GLfloat bpos[3]; + +/*sides*/ +static Side lside = {0, {0, 0, 0,}, MAX_COUNT, {1.0, 0.0, 0.0},{90,0,1,0}};/*Red*/ +static Side rside = {0, {0, 0, 0,}, MAX_COUNT, {0.0, 1.0, 0.0},{90,0,1,0}};/*Green*/ +static Side tside = {0, {0, 0, 0,}, MAX_COUNT, {0.0, 0.0, 1.0},{90,1,0,0}};/*Blue*/ +static Side bside = {0, {0, 0, 0,}, MAX_COUNT, {1.0, 0.5, 0.0},{90,1,0,0}};/*Orange*/ +static Side fside = {0, {0, 0, 0,}, MAX_COUNT, {1.0, 1.0, 0.0},{90,0,0,1}};/*Yellow*/ +static Side aside = {0, {0, 0, 0,}, MAX_COUNT, {0.5, 0.0, 1.0},{90,0,0,1}};/*Purple*/ +Side *sp; + +/* lights */ +static float LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f }; +static float LightPosition[]= { 20.0f, 100.0f, 20.0f, 1.0f }; + +ModeSpecOpt sws_opts = {0,NULL,0,NULL,NULL}; + +void +swap(GLfloat *a, GLfloat *b) +{ + GLfloat t = *a; + *a = *b; + *b = t; +} + +float +get_rand(void) +{ + GLfloat j = 1+(random() % 2); + return (j); +} + +void +swap_mov(GLfloat *a, GLfloat *b) +{ + int j; + swap(a,b); + j = get_rand(); + if (*a < 0) + *a = (j *= -1); + else + *a = j; +} + +void +cp_b_pos(Tdpos *s_pos){ + s_pos->x = ball.x; + s_pos->y = ball.y; + s_pos->z = ball.z; +} + +void +hit_side(void) +{ + if ((ball.x - ball.d) <= bbox.bottom.x){ + lside.hit = 1; + lside.counter = MAX_COUNT; + cp_b_pos(&lside.pos); + swap_mov(&mo.x,&moh.x); + }else + if ((ball.x + ball.d) >= bbox.top.x){ + rside.hit = 1; + rside.counter = MAX_COUNT; + cp_b_pos(&rside.pos); + swap_mov(&mo.x,&moh.x); + } +} + +void +hit_top_bottom(void) +{ + if ((ball.y - ball.d) <= bbox.bottom.y){ + bside.hit = 1; + bside.counter = MAX_COUNT; + cp_b_pos(&bside.pos); + swap_mov(&mo.y,&moh.y); + }else + if ((ball.y + ball.d) >= bbox.top.y){ + tside.hit = 1; + tside.counter = MAX_COUNT; + cp_b_pos(&tside.pos); + swap_mov(&mo.y,&moh.y); + } +} + +void +hit_front_back(void) +{ + if ((ball.z - ball.d) <= bbox.bottom.z){ + aside.hit = 1; + aside.counter = MAX_COUNT; + cp_b_pos(&aside.pos); + swap_mov(&mo.z,&moh.z); + }else + if((ball.z + ball.d) >= bbox.top.z){ + fside.hit = 1; + fside.counter = MAX_COUNT; + cp_b_pos(&fside.pos); + swap_mov(&mo.z,&moh.z); + } +} + +void +reshape_ball (ModeInfo *mi, int width, int height) +{ + GLfloat h = (GLfloat) height / (GLfloat) width; + + glViewport (0, 0, (GLint) width, (GLint) height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective (30.0, 1/h, 1.0, 100.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( 0.0, 0.0, 40.0, + 0.0, 0.0, 0.0, + 0.0, 2.0, 10.0); + +} + +void +unit_cube(void) +{ + glBegin(GL_QUADS); + glNormal3f( 0.0f, -1.0f, 0.0f); + glVertex3f(-1.0f, -1.0f, -1.0f); + glVertex3f( 1.0f, -1.0f, -1.0f); + glVertex3f( 1.0f, -1.0f, 1.0f); + glVertex3f(-1.0f, -1.0f, 1.0f); + glNormal3f( 0.0f, 0.0f, 1.0f); + glVertex3f(-1.0f, -1.0f, 1.0f); + glVertex3f( 1.0f, -1.0f, 1.0f); + glVertex3f( 1.0f, 1.0f, 1.0f); + glVertex3f(-1.0f, 1.0f, 1.0f); + glNormal3f( 0.0f, 0.0f, -1.0f); + glVertex3f(-1.0f, -1.0f, -1.0f); + glVertex3f(-1.0f, 1.0f, -1.0f); + glVertex3f( 1.0f, 1.0f, -1.0f); + glVertex3f( 1.0f, -1.0f, -1.0f); + glNormal3f( 1.0f, 0.0f, 0.0f); + glVertex3f( 1.0f, -1.0f, -1.0f); + glVertex3f( 1.0f, 1.0f, -1.0f); + glVertex3f( 1.0f, 1.0f, 1.0f); + glVertex3f( 1.0f, -1.0f, 1.0f); + glNormal3f( -1.0f, 0.0f, 0.0f); + glVertex3f(-1.0f, -1.0f, -1.0f); + glVertex3f(-1.0f, -1.0f, 1.0f); + glVertex3f(-1.0f, 1.0f, 1.0f); + glVertex3f(-1.0f, 1.0f, -1.0f); + glNormal3f( 1.0f, 1.0f, 0.0f); + glVertex3f(-1.0f, 1.0f, -1.0f); + glVertex3f(-1.0f, 1.0f, 1.0f); + glVertex3f( 1.0f, 1.0f, 1.0f); + glVertex3f( 1.0f, 1.0f, -1.0f); + glEnd(); +} + +void +init_ball (ModeInfo *mi) +{ + #define SPHERE_SLICES 32 /* how densely to render spheres */ + #define SPHERE_STACKS 16 + sp = malloc(sizeof(*sp)); + if(sp == NULL){ + fprintf(stderr,"Could not allocate memory\n"); + exit(1); + } + init_GL(mi); + reshape_ball(mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + ballList = glGenLists(1); + glNewList(ballList, GL_COMPILE); + unit_sphere (SPHERE_STACKS, SPHERE_SLICES, False); + glEndList (); + + boxList = glGenLists(1); + glNewList(boxList, GL_COMPILE); + unit_cube(); + glEndList(); + + glEnable(GL_COLOR_MATERIAL); + glShadeModel(GL_SMOOTH); + glClearColor(0.0f, 0.0f, 0.0f, 0.5f); + glClearDepth(1.0f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glEnable(GL_LIGHTING); + glClearDepth(1); + glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); + glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); + glEnable(GL_LIGHT1); +} + +void +draw_ball (ModeInfo *mi) +{ + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + /*int dem = 1;*/ + int i = 0; + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + hit_top_bottom(); + hit_front_back(); + hit_side(); + + glRotated(0.25,0,0,1); + glRotated(0.25,0,1,0); + glRotated(0.25,1,0,0); + + glColor3f(1,1,1); + glPushMatrix(); + glTranslatef(ball.x += mo.x, + ball.y += mo.y, + ball.z += mo.z); + + glCallList(ballList); + glPopMatrix(); + + while(i < 6){ + switch(i){ + case 0:{ + sp = &lside; + bpos[0] = lside.pos.z*-1; + bpos[1] = lside.pos.y; + bpos[2] = bbox.bottom.x - bscale[3]; + break; + } + case 1:{ + sp = &rside; + bpos[0] = rside.pos.z*-1; + bpos[1] = rside.pos.y; + bpos[2] = bbox.top.x + bscale[3]; + break; + } + case 2:{ + sp = &tside; + bpos[0] = tside.pos.x; + bpos[1] = tside.pos.z; + bpos[2] = bbox.bottom.y - bscale[3]; + break; + } + case 3:{ + sp = &bside; + bpos[0] = bside.pos.x; + bpos[1] = bside.pos.z; + bpos[2] = bbox.top.y + bscale[3]; + break; + } + case 4:{ + sp = &fside; + bpos[0] = fside.pos.y; + bpos[1] = fside.pos.x*-1; + bpos[2] = bbox.top.z + bscale[3]; + break; + } + case 5:{ + sp = &aside; + bpos[0] = aside.pos.y; + bpos[1] = aside.pos.x*-1; + bpos[2] = bbox.bottom.z + bscale[3]; + break; + } + } + if(sp->hit){ + glColor3fv(sp->color); + glPushMatrix(); + glRotatef(sp->rot[0],sp->rot[1],sp->rot[2],sp->rot[3]); + glTranslatef(bpos[0],bpos[1],bpos[2]); + /*dem = (MAX_COUNT-(sp->counter+1));*/ + /*glScalef(bscale[0]/dem,bscale[1]/dem,bscale[2]);*/ + glScalef(bscale[0],bscale[1],bscale[2]); + glCallList(boxList); + glPopMatrix(); + sp->counter--; + if(!sp->counter) + sp->hit = 0; + } + i++; + } + + + glFinish(); + glXSwapBuffers(dpy, window); + +} + +#endif /* USE_GL */ diff --git a/hacks/glx/blinkbox.man b/hacks/glx/blinkbox.man new file mode 100644 index 00000000..e8815d7c --- /dev/null +++ b/hacks/glx/blinkbox.man @@ -0,0 +1,49 @@ +.TH XScreenSaver 1 "" "X Version 11" +.SH NAME +blinkbox +.SH SYNOPSIS +.B boundingbox +[\-display \fIhost:display.screen\fP] +[\-visual \fIvisual\fP] +[\-window] +[\-root] +[\-delay \fInumber\fP] +.SH DESCRIPTION +Shows a ball contained inside of a bounding box. Colored blocks blink in +when the ball hits the edges. +.SH OPTIONS +.TP 8 +.B \-visual \fIvisual\fP +Specify which visual to use. Legal values are the name of a visual class, +or the id number (decimal or hex) of a specific visual. +.TP 8 +.B \-window +Draw on a newly-created window. This is the default. +.TP 8 +.B \-root +Draw on the root window. +.TP 8 +.B \-delay \fInumber\fP +Per-frame delay, in microseconds. Default: 30000 (0.03 seconds.). +.SH ENVIRONMENT +.PP +.TP 8 +.B DISPLAY +to get the default host and display number. +.TP 8 +.B XENVIRONMENT +to get the name of a resource file that overrides the global resources +stored in the RESOURCE_MANAGER property. +.SH SEE ALSO +.BR X (1), +.BR xscreensaver (1) +.SH COPYRIGHT +Copyright \(co 2003 by Jeremy English. 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 +Jeremy English. diff --git a/hacks/glx/boxed.c b/hacks/glx/boxed.c index 3159a243..238cb3c8 100644 --- a/hacks/glx/boxed.c +++ b/hacks/glx/boxed.c @@ -1,8 +1,7 @@ /* thebox --- 3D bouncing balls that explode */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)boxed.c 0.9 01/09/26 xlockmore"; - #endif /*- diff --git a/hacks/glx/bubble3d.c b/hacks/glx/bubble3d.c index dd5b68e4..52dbc33e 100644 --- a/hacks/glx/bubble3d.c +++ b/hacks/glx/bubble3d.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* bubble3d.c - 3D bubbles */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)bubble3d.c 4.11 98/06/16 xlockmore"; - #endif /*- diff --git a/hacks/glx/buildlwo.c b/hacks/glx/buildlwo.c index 1aa86eda..7ae9e5f5 100644 --- a/hacks/glx/buildlwo.c +++ b/hacks/glx/buildlwo.c @@ -1,7 +1,6 @@ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)buildlwo.c 4.02 97/04/20 xlockmore"; - #endif /*- diff --git a/hacks/glx/cage.c b/hacks/glx/cage.c index 18c2bda5..85bd17fb 100644 --- a/hacks/glx/cage.c +++ b/hacks/glx/cage.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* cage --- the Impossible Cage, an Escher like scene. */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)cage.c 5.01 2001/03/01 xlockmore"; - #endif /*- diff --git a/hacks/glx/chessgames.h b/hacks/glx/chessgames.h index 1d61eaa3..a752d4f7 100644 --- a/hacks/glx/chessgames.h +++ b/hacks/glx/chessgames.h @@ -1,7 +1,7 @@ /* * endgame -- plays through a chess game ending. enjoy. * - * Copyright (C) 2002 Blair Tennessy (tennessb@unbc.ca) + * Copyright (C) 2002 Blair Tennessy (tennessy@cs.ubc.ca) * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -35,15 +35,13 @@ typedef struct { in case the move promotes a pawn, we assume a queening. (see drawMovingPiece()) - whats lacking? - castling, en passant, under-promotions. hack at will. - more games! feel free to encode favorites! - and this moves[40][4] junk. i love c! + what's lacking? + castling, en passant, under-promotions. */ int moves[40][4]; } ChessGame; -#define GAMES 3 +#define GAMES 7 ChessGame games[GAMES] = { /** @@ -170,7 +168,176 @@ ChessGame games[GAMES] = { {0, 0, 0, 0}, /* mull it over... */ {0, 0, 3, 1} } - } + }, + + /** + game 4: + + M.B. Newman + White to play and win. + + "Chess Amateur" + 1913 + */ + { + { + { 0, 0, 0, 0, BQUEEN, 0, 0, 0}, + {BKNIGHT, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, PAWN}, + { BKING, 0, BISHOP, 0, KNIGHT, 0, 0, 0}, + { PAWN, 0, 0, 0, KNIGHT, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0}, + { KING, 0, 0, 0, 0, 0, 0, 0}, + }, + + 15, + + { + {4, 2, 3, 1}, + {0, 4, 3, 1}, /* queen wins bishop */ + {4, 4, 5, 2}, + {4, 0, 5, 0}, /* king takes pawn */ + {5, 2, 3, 1}, /* knight takes queen, check */ + {1, 0, 3, 1}, /* knight takes knight */ + {3, 7, 2, 7}, /* pawn advances */ + {3, 1, 2, 3}, + {5, 4, 4, 2}, + {2, 3, 4, 2}, + {2, 7, 1, 7}, /* pawn advances */ + {4, 2, 2, 3}, + {1, 7, 0, 7}, + {0, 0, 0, 0}, + {0, 0, 5, 0} + } + }, + + /** + game 5: + + V.A. Korolikov + White to play and win + + First Prize - "Truda" + 1935 + */ + { + { + { 0, 0, BISHOP, 0, 0, 0, 0, 0}, + { BPAWN, ROOK, 0, 0, 0, 0, 0, 0}, + { 0, 0, BPAWN, PAWN, 0, BKING, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, KING, BBISHOP}, + { 0, 0, 0, 0, BPAWN, 0, PAWN, 0}, + { 0, 0, 0, 0, 0, BPAWN, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0}, + }, + + 21, + + { + {2, 3, 1, 3}, /* pawn to q7 */ + {2, 5, 1, 4}, /* cover with king */ + {1, 1, 0, 1}, + {4, 7, 5, 6}, /* bishop takes pawn */ + {0, 1, 0, 0}, /* r - r8 */ + {6, 5, 7, 5}, /* queened */ + {1, 3, 0, 3}, /* white pawn promoted */ + {1, 4, 0, 3}, /* king takes queen */ + {0, 2, 2, 0}, /* discovered check */ + {5, 6, 0, 1}, /* pull back bishop */ + {2, 0, 7, 5}, /* bishop takes queen */ + {0, 3, 1, 2}, + {7, 5, 2, 0}, /* save rook */ + {5, 4, 6, 4}, + {2, 0, 6, 4}, /* bishop takes pawn */ + {1, 2, 1, 1}, /* king moves in */ + {6, 4, 5, 5}, + {1, 1, 0, 0}, + {5, 5, 2, 2}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + } + }, + + /** + game 6: + + T.B. Gorgiev + White to play and win + + First Prize - "64" + 1929 + */ + { + { + { 0, 0, 0, 0, 0, 0, KNIGHT, 0}, + { BKNIGHT, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, BKING, BKNIGHT, 0, 0, 0}, + { KING, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, KNIGHT, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, BISHOP, 0, 0, 0}, + }, + + 13, + + { + {3, 0, 2, 1}, /* king on move */ + {1, 0, 0, 2}, /* check */ + {2, 1, 1, 1}, + {0, 2, 1, 4}, /* knight moves on */ + {7, 4, 5, 6}, /* bishop puts king in check */ + {2, 3, 1, 3}, /* king moves back */ + {0, 6, 2, 5}, /* knight moves in, check */ + {1, 3, 0, 3}, /* king moves back queen */ + {5, 6, 1, 2}, /* bishop - b7 ch!! */ + {2, 4, 1, 2}, /* black knight takes bishop */ + {4, 6, 3, 4}, /* knight to k5 */ + {0, 0, 0, 0}, /* mate */ + {0, 0, 0, 0} + } + }, + + /** + game 7: + + K. A. L. Kubbel + White to play and win + + "Schachmatny Listok" + 1922 + */ + { + { + { 0, KNIGHT, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0}, + { KING, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, BKING, 0, 0, 0, 0}, + { 0, 0, 0, BPAWN, 0, 0, 0, BISHOP}, + { BPAWN, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, PAWN, PAWN, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0}, + }, + + 12, + + { + {0, 1, 2, 2}, /* kt-b6 */ + {3, 3, 2, 2}, /* k x kt */ + {4, 7, 2, 5}, /* b-b6 */ + {2, 2, 3, 3}, /* king back to original position */ + {6, 3, 5, 3}, /* p-q3! */ + {5, 0, 6, 0}, /* p-r7 */ + {6, 2, 4, 2}, /* p-b4ch */ + {3, 3, 3, 2}, /* king moves, black cannot capture in passing */ + {2, 0, 1, 1}, /* k-kt7! */ + {6, 0, 7, 0}, /* promo */ + {2, 5, 1, 4}, /* mate */ + {0, 0, 3, 2}, + } + }, }; #endif /* __CHESSGAMES_H__ */ diff --git a/hacks/glx/dolphin.c b/hacks/glx/dolphin.c index 67406d20..91d0db97 100644 --- a/hacks/glx/dolphin.c +++ b/hacks/glx/dolphin.c @@ -1,8 +1,7 @@ /* atlantis --- Shows moving 3D sea animals */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)dolphin.c 1.2 98/06/16 xlockmore"; - #endif /* Copyright (c) E. Lassauge, 1998. */ diff --git a/hacks/glx/endgame.c b/hacks/glx/endgame.c index 83ce4e41..5bac990a 100644 --- a/hacks/glx/endgame.c +++ b/hacks/glx/endgame.c @@ -29,6 +29,7 @@ #define DEFAULTS "*delay: 20000 \n" \ "*showFPS: False \n" \ "*wireframe: False \n" \ + "*reflections: True \n" \ # include "xlockmore.h" @@ -48,12 +49,18 @@ static XrmOptionDescRec opts[] = { {"+rotate", ".chess.rotate", XrmoptionNoArg, (caddr_t) "false" }, {"-rotate", ".chess.rotate", XrmoptionNoArg, (caddr_t) "true" }, + {"+reflections", ".chess.reflections", XrmoptionNoArg, (caddr_t) "false" }, + {"-reflections", ".chess.reflections", XrmoptionNoArg, (caddr_t) "true" }, + {"+smooth", ".chess.smooth", XrmoptionNoArg, (caddr_t) "false" }, + {"-smooth", ".chess.smooth", XrmoptionNoArg, (caddr_t) "true" }, }; -int rotate; +int rotate, reflections, smooth; static argtype vars[] = { {(caddr_t *) &rotate, "rotate", "Rotate", "True", t_Bool}, + {(caddr_t *) &reflections, "reflections", "Reflections", "True", t_Bool}, + {(caddr_t *) &smooth, "smooth", "Smooth", "True", t_Bool}, }; ModeSpecOpt chess_opts = {countof(opts), opts, countof(vars), vars, NULL}; @@ -94,71 +101,35 @@ GLfloat colors[2][3] = {0.5, 0.5, 0.5}, }; -/* well, i prefer silver tip */ -GLfloat whites[3][3] = +#define WHITES 5 + +/* i prefer silvertip */ +GLfloat whites[WHITES][3] = { {1.0, 0.5, 0.0}, {0.8, 0.45, 1.0}, - {0.37, 0.56, 0.87}, + {0.43, 0.54, 0.76}, + {0.8, 0.8, 0.8}, + {0.15, 0.77, 0.54}, }; -/* int board[BOARDSIZE][BOARDSIZE]; */ - #include "chessgames.h" ChessGame game; - -/* void buildBoard(void) { */ -/* board[0][5] = BKING; */ -/* board[1][4] = BPAWN; */ -/* board[1][2] = BPAWN; */ -/* board[1][0] = BPAWN; */ -/* board[2][2] = BPAWN; */ -/* board[2][4] = BPAWN; */ -/* board[2][7] = KNIGHT; */ -/* board[3][0] = PAWN; */ -/* board[3][2] = ROOK; */ -/* board[4][0] = PAWN; */ -/* board[4][4] = KING; */ -/* board[4][5] = PAWN; */ -/* board[6][0] = BPAWN; */ -/* board[6][7] = PAWN; */ -/* board[7][0] = BBISHOP; */ -/* } */ +int oldwhite = -1; void build_colors(void) { - int white = random()%3; - colors[0][0] = whites[white][0]; - colors[0][1] = whites[white][1]; - colors[0][2] = whites[white][2]; -} -/* int moves[MOVES][4] = */ -/* { {3, 2, 6, 2}, */ -/* {7, 0, 6, 1}, */ -/* {6, 2, 6, 6}, */ -/* {0, 5, 0, 4}, */ -/* {6, 6, 0, 6}, */ -/* {0, 4, 1, 3}, */ -/* {2, 7, 1, 5}, */ -/* {2, 2, 3, 2}, */ -/* {0, 6, 0, 3}, */ -/* {1, 3, 2, 2}, */ -/* {0, 3, 6, 3}, */ -/* {3, 2, 4, 2}, /\* pawn to bishop 5 *\/ */ -/* {1, 5, 0, 3}, /\* check *\/ */ -/* {2, 2, 3, 2}, */ -/* {0, 3, 2, 4}, /\* takes pawn *\/ */ -/* {3, 2, 2, 2}, */ -/* {2, 4, 0, 3}, */ -/* {2, 2, 3, 2}, */ -/* {6, 3, 6, 1}, /\* rook takes bishop *\/ */ -/* {6, 0, 7, 0}, */ -/* {6, 1, 3, 1}, */ -/* {3, 2, 2, 3}, */ -/* {3, 1, 3, 3}, */ -/* {0, 0, 2, 3}, */ -/* }; */ + /* find new white */ + int newwhite = oldwhite; + while(newwhite == oldwhite) + newwhite = random()%WHITES; + oldwhite = newwhite; + + colors[0][0] = whites[oldwhite][0]; + colors[0][1] = whites[oldwhite][1]; + colors[0][2] = whites[oldwhite][2]; +} /* yay its c */ int mpiece = 0, tpiece, steps = 0, done = 1; @@ -192,13 +163,23 @@ Bool chess_handle_event (ModeInfo *mi, XEvent *event) { return False; } -GLfloat position[] = { 3.0, 7.0, 3.0, 1.0 }; +GLfloat position[] = { 0.0, 5.0, 5.0, 1.0 }; +GLfloat position2[] = { 5.0, 5.0, 5.0, 1.0 }; +GLfloat diffuse2[] = {1.0, 1.0, 1.0, 1.0}; +GLfloat ambient2[] = {0.6, 0.6, 0.6, 1.0}; /* configure lighting */ void setup_lights(void) { glEnable(GL_LIGHTING); glLightfv(GL_LIGHT0, GL_POSITION, position); + glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse2); glEnable(GL_LIGHT0); + +/* glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient2); */ + + glLightfv(GL_LIGHT1, GL_SPECULAR, diffuse2); + glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse2); + glEnable(GL_LIGHT1); } /** draw pieces */ @@ -377,14 +358,32 @@ void display(Chesscreen *c) { glRotatef(theta*100, 0.0, 1.0, 0.0); glTranslatef(-0.5*BOARDSIZE, 0.0, -0.5*BOARDSIZE); + position[0] = 4.0 + 1.0*-sin(theta*100*M_PI/180.0); + position[2] = 4.0 + 1.0*cos(theta*100*M_PI/180.0); + position[1] = 8.0; + + position2[0] = 4.0 + 8.0*-sin(theta*100*M_PI/180.0); + position2[2] = 4.0 + 8.0*cos(theta*100*M_PI/180.0); + + glEnable(GL_LIGHTING); + glLightfv(GL_LIGHT0, GL_POSITION, position); + glLightfv(GL_LIGHT1, GL_POSITION, position2); + glEnable(GL_LIGHT0); + /** draw board, pieces */ if(!wire) { glEnable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); - draw_reflections(); - glEnable(GL_BLEND); + + if(reflections) { + draw_reflections(); + glEnable(GL_BLEND); + } + drawBoard(); - glDisable(GL_BLEND); + + if(reflections) + glDisable(GL_BLEND); } else drawBoard(); @@ -442,7 +441,7 @@ void init_chess(ModeInfo *mi) { if(!wire) { setup_lights(); glColorMaterial(GL_FRONT, GL_DIFFUSE); - glShadeModel(GL_SMOOTH); + glShadeModel(smooth ? GL_SMOOTH : GL_FLAT); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_DEPTH_TEST); } @@ -450,6 +449,8 @@ void init_chess(ModeInfo *mi) { glPolygonMode(GL_FRONT, GL_LINE); } +int oldgame = -1; + /** does dirty work drawing scene, moving pieces */ void draw_chess(ModeInfo *mi) { Chesscreen *c = &qs[MI_SCREEN(mi)]; @@ -494,8 +495,13 @@ void draw_chess(ModeInfo *mi) { moving = 1; } else if(done == 1) { - /* copy over new game */ - game = games[random()%GAMES]; + int newgame = oldgame; + while(newgame == oldgame) + newgame = random()%GAMES; + + /* same old game */ + oldgame = newgame; + game = games[oldgame]; build_colors(); done = 2; count = 0; @@ -507,9 +513,14 @@ void draw_chess(ModeInfo *mi) { } /* set lighting */ - if(done) + if(done) { glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, done == 1 ? 1.0+0.1*count : 100.0/count); + glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, + done == 1 ? 1.0+0.1*count : 100.0/count); + glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.15); + glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.15); + } display(c); diff --git a/hacks/glx/engine.c b/hacks/glx/engine.c index 2febb1d4..f2a956f5 100644 --- a/hacks/glx/engine.c +++ b/hacks/glx/engine.c @@ -253,7 +253,7 @@ engine_type engines[] = { /* given a number of cylinders and an included angle, finds matching engine */ int find_engine(const char *name) { - int i; + unsigned int i; if (!name || !*name || !strcasecmp (name, "(none)")) return (random() % countof(engines)); @@ -762,8 +762,8 @@ void makeshaft (void) { int i; int j; - const static float crankThick = 0.2; - const static float crankDiam = 0.3; + static const float crankThick = 0.2; + static const float crankDiam = 0.3; i = glGenLists(1); glNewList(i, GL_COMPILE); @@ -866,7 +866,7 @@ print_title_string (ModeInfo *mi, const char *string, GLfloat x, GLfloat y) glMatrixMode(GL_MODELVIEW); glPushMatrix(); { - int i; + unsigned int i; int x2 = x; glLoadIdentity(); diff --git a/hacks/glx/flipscreen3d.c b/hacks/glx/flipscreen3d.c index e72d8dce..735c8d2c 100644 --- a/hacks/glx/flipscreen3d.c +++ b/hacks/glx/flipscreen3d.c @@ -405,7 +405,7 @@ void getSnapshot (ModeInfo *modeinfo) if (MI_IS_WIREFRAME(modeinfo)) return; - ximage = screen_to_ximage (modeinfo->xgwa.screen, modeinfo->window); + ximage = screen_to_ximage (modeinfo->xgwa.screen, modeinfo->window, NULL); qw = QW; qh = QH; tw = modeinfo->xgwa.width; diff --git a/hacks/glx/flurry.c b/hacks/glx/flurry.c index 63b89f66..3571bece 100644 --- a/hacks/glx/flurry.c +++ b/hacks/glx/flurry.c @@ -37,9 +37,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* flurry */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)flurry.c 4.07 97/11/24 xlockmore"; - #endif /*- @@ -107,8 +106,12 @@ static double gTimeCounter = 0.0; double currentTime(void) { struct timeval tv; - - gettimeofday(&tv, NULL); +# ifdef GETTIMEOFDAY_TWO_ARGS + struct timezone tzp; + gettimeofday(&tv, &tzp); +# else + gettimeofday(&tv); +# endif return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; } diff --git a/hacks/glx/fps.c b/hacks/glx/fps.c index 33d4250a..237ef1fe 100644 --- a/hacks/glx/fps.c +++ b/hacks/glx/fps.c @@ -186,8 +186,8 @@ fps_1 (ModeInfo *mi) static int last_ifps = -1; static GLfloat last_fps = -1; static int frame_count = 0; - static struct timeval prev = { 0, }; - static struct timeval now = { 0, }; + static struct timeval prev = { 0, 0 }; + static struct timeval now = { 0, 0 }; if (!initted_p) { diff --git a/hacks/glx/gears.c b/hacks/glx/gears.c index 52bdab82..e1c08bdc 100644 --- a/hacks/glx/gears.c +++ b/hacks/glx/gears.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* gears --- 3D gear wheels */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)gears.c 4.07 97/11/24 xlockmore"; - #endif /*- diff --git a/hacks/glx/gflux.c b/hacks/glx/gflux.c index f9b1cb2e..27a6bd45 100644 --- a/hacks/glx/gflux.c +++ b/hacks/glx/gflux.c @@ -621,7 +621,8 @@ grabTexture(void) int real_width = gflux->modeinfo->xgwa.width; int real_height = gflux->modeinfo->xgwa.height; XImage *ximage = screen_to_ximage (gflux->modeinfo->xgwa.screen, - gflux->window); + gflux->window, + NULL); Bool bigimage = False; int size = 0; diff --git a/hacks/glx/gleidescope.c b/hacks/glx/gleidescope.c new file mode 100644 index 00000000..7470d3f3 --- /dev/null +++ b/hacks/glx/gleidescope.c @@ -0,0 +1,1088 @@ +/* -*- Mode: C; tab-width: 4 -*- */ + +#if !defined( lint ) && !defined( SABER ) +static const char sccsid[] = "@(#)gleidescope.c 1.0 03/06/27 xlockmore"; +#endif + +/* enable -grab switch */ +/*#define GRAB*/ + +/*- + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + * + * Revision History: + * + * 20030627 1.0 acd First Release. + * Texture loading code from 'glplanet' + * by Jamie Zawinski + * 20030810 1.1 acd Added size flag. + * Now grabs screen / video / picture + * (uses code from 'glslideshow' by + * Mike Oliphant, Ben Buxton, Jamie Zawinski). + * Added -duration. + * Added mouse code. + * Added fade code (also from glslideshow). + * 20031013 1.2 acd Migrated to compile without warnings under + * xscreensaver 4.13. + * 20031023 1.3 acd Better code to limit twisting speeds. + * Tweaked initial rotation values. + * Move, Rotate, Zoom now chosen at random if + * no preference is given. + * Made grid slightly bigger so you can't see + * the edge when zooming and moving. + */ + +#include +#include "colors.h" + +#include "xpm-ximage.h" + +/* +**---------------------------------------------------------------------------- +** Defines +**---------------------------------------------------------------------------- +*/ + +#ifdef STANDALONE +# define PROGCLASS "gleidescope" +# define HACK_INIT init_gleidescope +# define HACK_DRAW draw_gleidescope +# define HACK_RESHAPE reshape_gleidescope +# define HACK_HANDLE_EVENT gleidescope_handle_event +# define EVENT_MASK PointerMotionMask +# define gleidescope_opts xlockmore_opts +# define DEFAULTS \ + "*delay: 20000 \n" \ + "*showFPS: False \n" \ + "*move: False \n" \ + "*rotate: False \n" \ + "*zoom: False \n" \ + "*image: DEFAULT \n" \ + "*size: -1 \n" \ + "*duration: 30 \n" \ + +# include "xlockmore.h" /* from the xscreensaver distribution */ +#else /* !STANDALONE */ +# include "xlock.h" /* from the xlockmore distribution */ +#endif /* !STANDALONE */ + +#ifdef USE_GL + +#include + +/* acd TODO should all these be in gleidestruct? */ +#ifdef GRAB +static Bool grab; /* grab images */ +#endif +static Bool move; /* moving camera */ +static Bool nomove; /* no moving camera */ +static Bool rotate; /* rotate in place */ +static Bool norotate; /* no rotate in place */ +static int size = -1; /* size */ +static Bool zoom; /* zooming camera */ +static Bool nozoom; /* no zooming camera */ +static char *image; /* name of texture to load */ +static int duration; /* length of time to display grabbed image */ + +#define MAX_TANGLE_VEL 2.0 + +static float tangle = 0; /* texture angle */ +static float tangle_vel = 0.0; /* texture velocity */ +static float tangle_acc = 0.0; /* texture acceleration */ + +#define MAX_RANGLE_VEL 1.5 + +static float rangle = 0; /* rotate angle */ +static float rangle_vel = 0.0; /* rotate velocity */ +static float rangle_acc = 0.0; /* rotate acceleration */ + +static XrmOptionDescRec opts[] = +{ +#ifdef GRAB + {"-grab", (char *) ".gleidescope.grab", XrmoptionNoArg, "true"}, +#endif + {"-move", (char *) ".gleidescope.move", XrmoptionNoArg, "true"}, + {"-no-move", (char *) ".gleidescope.nomove", XrmoptionNoArg, "true"}, + {"-rotate", (char *) ".gleidescope.rotate", XrmoptionNoArg, "true"}, + {"-no-rotate", (char *) ".gleidescope.norotate", XrmoptionNoArg, "true"}, + /*{"-size", (char *) ".gleidescope.size", XrmoptionNoArg, "-1"},*/ + {"-zoom", (char *) ".gleidescope.zoom", XrmoptionNoArg, "true"}, + {"-no-zoom", (char *) ".gleidescope.nozoom", XrmoptionNoArg, "true"}, + {"-image", (char *) ".gleidescope.image", XrmoptionSepArg, "DEFAULT"}, + {"-duration", (char *) ".gleidescope.duration", XrmoptionSepArg, "30"}, +}; + + +static argtype vars[] = { +#ifdef GRAB + {(caddr_t *) &grab, "grab", "Grab", "False", t_Bool}, +#endif + {(caddr_t *) &move, "move", "Move", "False", t_Bool}, + {(caddr_t *) &nomove, "nomove", "noMove", "False", t_Bool}, + {(caddr_t *) &rotate, "rotate", "Rotate", "False", t_Bool}, + {(caddr_t *) &norotate, "norotate", "noRotate", "False", t_Bool}, + /*{(caddr_t *) &size, "size", "Size", "-1", t_Int},*/ + {(caddr_t *) &zoom, "zoom", "Zoom", "False", t_Bool}, + {(caddr_t *) &nozoom, "nozoom", "noZoom", "False", t_Bool}, + {(caddr_t *) &image, "image", "Image", "DEFAULT", t_String}, + {(caddr_t *) &duration, "duration", "Duration", "30", t_Int}, +}; + +static OptionStruct desc[] = { +#ifdef GRAB + {"-grab", "grab images to create animation"}, +#endif + {"-move", "camera will move"}, + {"-no-move", "camera won't move"}, + {"-rotate", "camera will rotate"}, + {"-no-rotate", "camera won't rotate"}, + /*{"-size", "size of the hexagons (1-10)"},*/ + {"-zoom", "camera will zoom"}, + {"-no-zoom", "camera won't zoom"}, + {"-image", "xpm / xbm image file to use for texture"}, + {"-duration", "length of time texture will be used"}, +}; + +ModeSpecOpt gleidescope_opts = { + sizeof opts / sizeof opts[0], opts, + sizeof vars / sizeof vars[0], vars, + desc +}; + +#ifdef USE_MODULES +ModStruct gleidescope_description = { + "gleidescope", "init_gleidescope", "draw_gleidescope", "release_gleidescope", + "draw_gleidescope", "init_gleidescope", NULL, &gleidescope_opts, + 1000, 1, 2, 1, 4, 1.0, "", + "GL Kaleidescope", 0, NULL}; +#endif + +/* +**----------------------------------------------------------------------------- +** Typedefs +**----------------------------------------------------------------------------- +*/ + +typedef struct hex_s { + GLfloat x, y, z; /* position */ +} hex_t; + +typedef struct { + GLfloat x; + GLfloat y; + GLfloat z; +} vectorf; + +#define MAX_FADE 500 /* number of fade cycles */ + +typedef struct { + float cam_x_speed, cam_z_speed, cam_y_speed; + int cam_x_phase, cam_z_phase, cam_y_phase; + float tic; + GLXContext *glx_context; + Window window; + GLfloat max_tx, max_ty; /* maximum texture sizes */ + GLuint textures[2]; /* texture handles */ + GLuint visible; /* texture handle for new texture */ + GLint fade; + time_t start_time; + Bool button_down_p; +} gleidestruct; + +#define XOFFSET (0.8660254f) /* sin 60' */ +#define YOFFSET (1.5000000f) /* cos 60' + 1 */ + +#if 0 + +#define SIZE 3 + +/* generates a grid with edges of given size */ +/* acd TODO - replace hex[] with this and allow size and distance as parameters */ + +int +generate_grid(int size) + + int i, x, y; + + size--; + + i = size; + for (y = -size ; y <= size ; y++) { + for (x = -i ; x <= i ; x += 2) { + printf("{XOFFSET * %d, YOFFSET * %d, 0},\n", x, y); + } + printf("\n"); + if (y < 0) { + i++; + } else { + i--; + } + } + return 0; +} +#endif + +hex_t hex[] = { + /* edges of size 7 */ + /* number of hexagons required to cover screen depends on camera distance */ + /* at a distance of 10 this is just about enough. */ + {XOFFSET * -6, YOFFSET * -6, 0}, + {XOFFSET * -4, YOFFSET * -6, 0}, + {XOFFSET * -2, YOFFSET * -6, 0}, + {XOFFSET * 0, YOFFSET * -6, 0}, + {XOFFSET * 2, YOFFSET * -6, 0}, + {XOFFSET * 4, YOFFSET * -6, 0}, + {XOFFSET * 6, YOFFSET * -6, 0}, + + {XOFFSET * -7, YOFFSET * -5, 0}, + {XOFFSET * -5, YOFFSET * -5, 0}, + {XOFFSET * -3, YOFFSET * -5, 0}, + {XOFFSET * -1, YOFFSET * -5, 0}, + {XOFFSET * 1, YOFFSET * -5, 0}, + {XOFFSET * 3, YOFFSET * -5, 0}, + {XOFFSET * 5, YOFFSET * -5, 0}, + {XOFFSET * 7, YOFFSET * -5, 0}, + + {XOFFSET * -8, YOFFSET * -4, 0}, + {XOFFSET * -6, YOFFSET * -4, 0}, + {XOFFSET * -4, YOFFSET * -4, 0}, + {XOFFSET * -2, YOFFSET * -4, 0}, + {XOFFSET * 0, YOFFSET * -4, 0}, + {XOFFSET * 2, YOFFSET * -4, 0}, + {XOFFSET * 4, YOFFSET * -4, 0}, + {XOFFSET * 6, YOFFSET * -4, 0}, + {XOFFSET * 8, YOFFSET * -4, 0}, + + {XOFFSET * -9, YOFFSET * -3, 0}, + {XOFFSET * -7, YOFFSET * -3, 0}, + {XOFFSET * -5, YOFFSET * -3, 0}, + {XOFFSET * -3, YOFFSET * -3, 0}, + {XOFFSET * -1, YOFFSET * -3, 0}, + {XOFFSET * 1, YOFFSET * -3, 0}, + {XOFFSET * 3, YOFFSET * -3, 0}, + {XOFFSET * 5, YOFFSET * -3, 0}, + {XOFFSET * 7, YOFFSET * -3, 0}, + {XOFFSET * 9, YOFFSET * -3, 0}, + + {XOFFSET * -10, YOFFSET * -2, 0}, + {XOFFSET * -8, YOFFSET * -2, 0}, + {XOFFSET * -6, YOFFSET * -2, 0}, + {XOFFSET * -4, YOFFSET * -2, 0}, + {XOFFSET * -2, YOFFSET * -2, 0}, + {XOFFSET * 0, YOFFSET * -2, 0}, + {XOFFSET * 2, YOFFSET * -2, 0}, + {XOFFSET * 4, YOFFSET * -2, 0}, + {XOFFSET * 6, YOFFSET * -2, 0}, + {XOFFSET * 8, YOFFSET * -2, 0}, + {XOFFSET * 10, YOFFSET * -2, 0}, + + {XOFFSET * -11, YOFFSET * -1, 0}, + {XOFFSET * -9, YOFFSET * -1, 0}, + {XOFFSET * -7, YOFFSET * -1, 0}, + {XOFFSET * -5, YOFFSET * -1, 0}, + {XOFFSET * -3, YOFFSET * -1, 0}, + {XOFFSET * -1, YOFFSET * -1, 0}, + {XOFFSET * 1, YOFFSET * -1, 0}, + {XOFFSET * 3, YOFFSET * -1, 0}, + {XOFFSET * 5, YOFFSET * -1, 0}, + {XOFFSET * 7, YOFFSET * -1, 0}, + {XOFFSET * 9, YOFFSET * -1, 0}, + {XOFFSET * 11, YOFFSET * -1, 0}, + + {XOFFSET * -12, YOFFSET * 0, 0}, + {XOFFSET * -10, YOFFSET * 0, 0}, + {XOFFSET * -8, YOFFSET * 0, 0}, + {XOFFSET * -6, YOFFSET * 0, 0}, + {XOFFSET * -4, YOFFSET * 0, 0}, + {XOFFSET * -2, YOFFSET * 0, 0}, + {XOFFSET * 0, YOFFSET * 0, 0}, + {XOFFSET * 2, YOFFSET * 0, 0}, + {XOFFSET * 4, YOFFSET * 0, 0}, + {XOFFSET * 6, YOFFSET * 0, 0}, + {XOFFSET * 8, YOFFSET * 0, 0}, + {XOFFSET * 10, YOFFSET * 0, 0}, + {XOFFSET * 12, YOFFSET * 0, 0}, + + {XOFFSET * -11, YOFFSET * 1, 0}, + {XOFFSET * -9, YOFFSET * 1, 0}, + {XOFFSET * -7, YOFFSET * 1, 0}, + {XOFFSET * -5, YOFFSET * 1, 0}, + {XOFFSET * -3, YOFFSET * 1, 0}, + {XOFFSET * -1, YOFFSET * 1, 0}, + {XOFFSET * 1, YOFFSET * 1, 0}, + {XOFFSET * 3, YOFFSET * 1, 0}, + {XOFFSET * 5, YOFFSET * 1, 0}, + {XOFFSET * 7, YOFFSET * 1, 0}, + {XOFFSET * 9, YOFFSET * 1, 0}, + {XOFFSET * 11, YOFFSET * 1, 0}, + + {XOFFSET * -10, YOFFSET * 2, 0}, + {XOFFSET * -8, YOFFSET * 2, 0}, + {XOFFSET * -6, YOFFSET * 2, 0}, + {XOFFSET * -4, YOFFSET * 2, 0}, + {XOFFSET * -2, YOFFSET * 2, 0}, + {XOFFSET * 0, YOFFSET * 2, 0}, + {XOFFSET * 2, YOFFSET * 2, 0}, + {XOFFSET * 4, YOFFSET * 2, 0}, + {XOFFSET * 6, YOFFSET * 2, 0}, + {XOFFSET * 8, YOFFSET * 2, 0}, + {XOFFSET * 10, YOFFSET * 2, 0}, + + {XOFFSET * -9, YOFFSET * 3, 0}, + {XOFFSET * -7, YOFFSET * 3, 0}, + {XOFFSET * -5, YOFFSET * 3, 0}, + {XOFFSET * -3, YOFFSET * 3, 0}, + {XOFFSET * -1, YOFFSET * 3, 0}, + {XOFFSET * 1, YOFFSET * 3, 0}, + {XOFFSET * 3, YOFFSET * 3, 0}, + {XOFFSET * 5, YOFFSET * 3, 0}, + {XOFFSET * 7, YOFFSET * 3, 0}, + {XOFFSET * 9, YOFFSET * 3, 0}, + + {XOFFSET * -8, YOFFSET * 4, 0}, + {XOFFSET * -6, YOFFSET * 4, 0}, + {XOFFSET * -4, YOFFSET * 4, 0}, + {XOFFSET * -2, YOFFSET * 4, 0}, + {XOFFSET * 0, YOFFSET * 4, 0}, + {XOFFSET * 2, YOFFSET * 4, 0}, + {XOFFSET * 4, YOFFSET * 4, 0}, + {XOFFSET * 6, YOFFSET * 4, 0}, + {XOFFSET * 8, YOFFSET * 4, 0}, + + {XOFFSET * -7, YOFFSET * 5, 0}, + {XOFFSET * -5, YOFFSET * 5, 0}, + {XOFFSET * -3, YOFFSET * 5, 0}, + {XOFFSET * -1, YOFFSET * 5, 0}, + {XOFFSET * 1, YOFFSET * 5, 0}, + {XOFFSET * 3, YOFFSET * 5, 0}, + {XOFFSET * 5, YOFFSET * 5, 0}, + {XOFFSET * 7, YOFFSET * 5, 0}, + + {XOFFSET * -6, YOFFSET * 6, 0}, + {XOFFSET * -4, YOFFSET * 6, 0}, + {XOFFSET * -2, YOFFSET * 6, 0}, + {XOFFSET * 0, YOFFSET * 6, 0}, + {XOFFSET * 2, YOFFSET * 6, 0}, + {XOFFSET * 4, YOFFSET * 6, 0}, + {XOFFSET * 6, YOFFSET * 6, 0}, +}; + +/* +**---------------------------------------------------------------------------- +** Local Variables +**---------------------------------------------------------------------------- +*/ + +static gleidestruct *gleidescope = NULL; + +/* + *load defaults in config structure + */ +void setdefaultconfig(void) +{ +#ifdef GRAB + grab = False; +#endif + move = False; + rotate = False; + zoom = False; + image = NULL; +} + +static int xstart; +static int ystart; +static double xmouse = 0.0; +static double ymouse = 0.0; + +Bool +gleidescope_handle_event(ModeInfo *mi, XEvent *event) +{ + gleidestruct *gp = &gleidescope[MI_SCREEN(mi)]; + + /* + printf("event:%d\n", event->xany.type); + printf("button:%d\n", event->xbutton.button); + */ + switch(event->xany.type) + { + case ButtonPress: + + if (event->xbutton.button == Button1 || event->xbutton.button == Button3) + { + /* store initial values of mouse */ + xstart = event->xbutton.x; + ystart = event->xbutton.y; + + /* button is down */ + gp->button_down_p = True; + return True; + } +#if 0 /* TODO */ + else if (event->xbutton.button == Button4) + { + /* zoom in */ + return True; + } + else if (event->xbutton.button == Button5) + { + /* zoom out */ + return True; + } +#endif + break; + + case ButtonRelease: + + if (event->xbutton.button == Button1 || event->xbutton.button == Button3) + { + /* button is up */ + gp->button_down_p = False; + return True; + } + break; + + case MotionNotify: + + if (gp->button_down_p) + { + /* update mouse position */ + xmouse += (double)(event->xmotion.x - xstart) / MI_WIDTH(mi); + ymouse += (double)(event->xmotion.y - ystart) / MI_HEIGHT(mi); + xstart = event->xmotion.x; + ystart = event->xmotion.y; + + return True; + } + break; + } + + return False; +} + +#include "grab-ximage.h" + +static void +getSnapshot(ModeInfo *mi, GLuint name) +{ + XImage *ximage; + int status; + int tw, th; + gleidestruct *gp = &gleidescope[MI_SCREEN(mi)]; + + if (MI_IS_WIREFRAME(mi)) + return; + + ximage = screen_to_ximage(mi->xgwa.screen, mi->window, 0); + + tw = mi->xgwa.width; + th = mi->xgwa.height; + + gp->max_tx = (GLfloat) tw / (GLfloat) ximage->width; + gp->max_ty = (GLfloat) th / (GLfloat) ximage->height; + + glBindTexture (GL_TEXTURE_2D, name); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR); + + clear_gl_error(); + status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3, + ximage->width, ximage->height, + GL_RGBA, GL_UNSIGNED_BYTE, ximage->data); + + if (!status && glGetError()) + /* Some implementations of gluBuild2DMipmaps(), but set a GL error anyway. + ** We could just call check_gl_error(), but that would exit. */ + status = -1; + + if (status) + { + const GLubyte *s = gluErrorString (status); + if (s) + { + fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n", + progname, ximage->width, ximage->height, s); + } + else + { + fprintf (stderr, "%s: error mipmapping %dx%d texture: (unknown)\n", + progname, ximage->width, ximage->height); + } + clear_gl_error(); + } + check_gl_error("mipmapping"); /* should get a return code instead of a + GL error, but just in case... */ + + free(ximage->data); + ximage->data = 0; + XDestroyImage (ximage); + + /* remember time of last image change */ + gp->start_time = time ((time_t *) 0); +} + +static void +setup_file_texture (ModeInfo *mi, char *filename, GLuint name) +{ + Display *dpy = mi->dpy; + Visual *visual = mi->xgwa.visual; + char buf[1024]; + + Colormap cmap = mi->xgwa.colormap; + XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename); + + /* use this texture */ + glBindTexture(GL_TEXTURE_2D, name); + + clear_gl_error(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + image->width, image->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image->data); + sprintf (buf, "texture: %.100s (%dx%d)", + filename, image->width, image->height); + check_gl_error(buf); + + /* setup parameters for texturing */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +static void +setup_texture(ModeInfo * mi, GLuint id) +{ + gleidestruct *gp = &gleidescope[MI_SCREEN(mi)]; + + if (!image || !*image || !strcmp(image, "DEFAULT")) { + /* no image specified - grab screen */ + getSnapshot(mi, id); + /* max_tx and max_ty set in getSnapshot() */ + } else { + /* use supplied image file */ + setup_file_texture(mi, image, id); + + /* set tx params to use whole image */ + gp->max_tx = 1.0f; + gp->max_ty = 1.0f; + } + + check_gl_error("texture initialization"); + + /* Need to flip the texture top for bottom for some reason. */ + glMatrixMode (GL_TEXTURE); + glScalef (1, -1, 1); + glMatrixMode (GL_MODELVIEW); +} + +#define VERTEX0 glVertex3f( 0.0000f, 0.000f, 0.0f); +#define VERTEX1 glVertex3f( 0.0000f, 1.000f, 0.0f); +#define VERTEX2 glVertex3f( XOFFSET, 0.500f, 0.0f); +#define VERTEX3 glVertex3f( XOFFSET, -0.500f, 0.0f); +#define VERTEX4 glVertex3f( 0.0000f, -1.000f, 0.0f); +#define VERTEX5 glVertex3f(-XOFFSET, -0.500f, 0.0f); +#define VERTEX6 glVertex3f(-XOFFSET, 0.500f, 0.0f); + +static void +draw_hexagons(ModeInfo *mi, int translucency, GLuint texture) +{ + int i; + GLfloat col[4]; + GLfloat t1x, t1y, t2x, t2y, t3x, t3y; + gleidestruct *gp = &gleidescope[MI_SCREEN(mi)]; + GLfloat tangle2; + + col[0] = 1.0; + col[1] = 1.0; + col[2] = 1.0; + col[3] = (float)translucency / MAX_FADE; + + /* calculate vertices of equilateral triangle within image. */ + /* t1 is always in centre */ + t1x = gp->max_tx / 2; + t1y = gp->max_ty / 2; + /* t2 rotates */ + t2x = (gp->max_tx / 2) * (1 + cos((ymouse * 2 * M_PI) + (tangle * M_PI / 180))); + t2y = (gp->max_ty / 2) * (1 + sin((ymouse * 2 * M_PI) + (tangle * M_PI / 180))); + /* t3 is always 60' further around than t2 */ + tangle2 = (ymouse * 2 * M_PI) + (tangle * M_PI / 180) + (M_PI * 2 / 6); + t3x = (gp->max_tx / 2) * (1 + (cos(tangle2))); + t3y = (gp->max_ty / 2) * (1 + (sin(tangle2))); + /* NB image is flipped vertically hence: */ + t1y = 1 - t1y; + t2y = 1 - t2y; + t3y = 1 - t3y; + /*printf("texcoords:[%f,%f]->[%f,%f](%f,%f)\n", t1x, t1y, t2x, t2y, gp->max_tx, gp->max_ty);*/ + + glColor4f(1.0, 1.0, 1.0, (float)translucency / MAX_FADE); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(GL_FALSE); + glBindTexture(GL_TEXTURE_2D, gp->textures[texture]); + + for (i = 0 ; i < sizeof(hex) / sizeof(hex[0]) ; i++) { + + glPushMatrix(); + + glTranslatef(hex[i].x, hex[i].y, 0.0); + + glBegin(GL_TRIANGLES); + + /* + ** six triangles to each hexagon + */ + + glTexCoord2f(t1x, t1y); + VERTEX0; + glTexCoord2f(t2x, t2y); + VERTEX1; + glTexCoord2f(t3x, t3y); + VERTEX6; + + glTexCoord2f(t1x, t1y); + VERTEX0; + glTexCoord2f(t3x, t3y); + VERTEX6; + glTexCoord2f(t2x, t2y); + VERTEX5; + + glTexCoord2f(t1x, t1y); + VERTEX0; + glTexCoord2f(t2x, t2y); + VERTEX5; + glTexCoord2f(t3x, t3y); + VERTEX4; + + glTexCoord2f(t1x, t1y); + VERTEX0; + glTexCoord2f(t3x, t3y); + VERTEX4; + glTexCoord2f(t2x, t2y); + VERTEX3; + + glTexCoord2f(t1x, t1y); + VERTEX0; + glTexCoord2f(t2x, t2y); + VERTEX3; + glTexCoord2f(t3x, t3y); + VERTEX2; + + glTexCoord2f(t1x, t1y); + VERTEX0; + glTexCoord2f(t3x, t3y); + VERTEX2; + glTexCoord2f(t2x, t2y); + VERTEX1; + + glEnd(); + + glPopMatrix(); + } + +#ifdef DISPLAY_TEXTURE + glPushMatrix(); + /* acd debug - display (bigger, centred) texture */ + glScalef(2.0, 2.0, 2.0); + glTranslatef(-0.5, -0.5, 0.0); + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); + glVertex3f(0.0, 0.0, -0.1); + glTexCoord2f(1.0, 0.0); + glVertex3f(1.0, 0.0, -0.1); + glTexCoord2f(1.0, 1.0); + glVertex3f(1.0, 1.0, -0.1); + glTexCoord2f(0.0, 1.0); + glVertex3f(0.0, 1.0, -0.1); + glEnd(); + /* acd debug - display texture triangle */ + glColor4f(1.0, 1.0, 1.0, 1.0); + glBegin(GL_LINE_LOOP); + glVertex3f(t1x, t1y, -0.11); + glVertex3f(t2x, t2y, -0.11); + glVertex3f(t3x, t3y, -0.11); + glEnd(); + glPopMatrix(); +#endif + + glDisable(GL_TEXTURE_2D); + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); +} + +/* + * main rendering loop + */ +static void +draw(ModeInfo * mi) +{ + GLfloat x_angle, y_angle, z_angle; + gleidestruct *gp = &gleidescope[MI_SCREEN(mi)]; + vectorf v1; + GLfloat pos[4]; + + glClearColor(0.5, 0.5, 0.5, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + gp->tic += 0.005f; + + x_angle = gp->cam_x_phase + gp->tic * gp->cam_x_speed; + y_angle = gp->cam_y_phase + gp->tic * gp->cam_y_speed; + z_angle = gp->cam_z_phase + gp->tic * gp->cam_z_speed; + + if (move) { + v1.x = 2 * sin(x_angle); + v1.y = 2 * sin(y_angle); + } else { + v1.x = 0; + v1.y = 0; + } + + /* size is changed in pinit() to be distance from plane */ + size = MI_SIZE(mi); + if (size > 10) { + size = 10; + } + if (size < -1) { + size = -1; + } + if (size != -1) { + /* user defined size */ + v1.z = size; + } else if (zoom) { + /* max distance given by adding the constant and the multiplier */ + v1.z = 5.0 + 4.0 * sin(z_angle); + } else { + /* default */ + v1.z = 7.0; + } + + /* update rotation angle (but not if mouse button down) */ + if (rotate && !gp->button_down_p) + { + float new_rangle_vel = 0.0; + + /* update camera rotation angle and velocity */ + rangle += rangle_vel; + new_rangle_vel = rangle_vel + rangle_acc; + if (new_rangle_vel > -MAX_RANGLE_VEL && new_rangle_vel < MAX_RANGLE_VEL) + { + /* new velocity is within limits */ + rangle_vel = new_rangle_vel; + } + + /* randomly change twisting speed */ + if ((random() % 1000) < 1) + { + rangle_acc = frand(0.002) - 0.001; + } + } + +#ifdef WOBBLE + /* this makes the image wobble - requires -move and a larger grid */ + gluLookAt(0, 0, v1.z, v1.x, v1.y, 0.0, 0.0, 1.0, 0.0); +#else + /* no wobble - camera always perpendicular to grid */ + + /* rotating camera rather than entire space - smoother */ + gluLookAt( + v1.x, v1.y, v1.z, + v1.x, v1.y, 0.0, + sin((xmouse * M_PI * 2) + rangle * M_PI / 180), + cos((xmouse * M_PI * 2) + rangle * M_PI / 180), + 0.0); +#endif + + /* light position same as camera */ + pos[0] = v1.x; + pos[1] = v1.y; + pos[2] = v1.z; + pos[3] = 0; + + if (gp->fade == 0) + { + /* not fading */ + draw_hexagons(mi, MAX_FADE, gp->visible); + } + else + { + /* fading - show both textures with alpha */ + draw_hexagons(mi, MAX_FADE - gp->fade, gp->visible); + draw_hexagons(mi, gp->fade, 1 - gp->visible); + + /* fade some more */ + gp->fade++; + + /* have we faded enough? */ + if (gp->fade > MAX_FADE) + { + /* stop fading */ + gp->fade = 0; + gp->visible = 1 - gp->visible; + } + } + + /* increment texture angle based on time, velocity etc */ + /* but only if button is not down */ + if (!gp->button_down_p) + { + float new_tangle_vel = 0.0; + + tangle += tangle_vel; + + /* work out new texture angle velocity */ + new_tangle_vel = tangle_vel + tangle_acc; + if (new_tangle_vel > -MAX_TANGLE_VEL && new_tangle_vel < MAX_TANGLE_VEL) + { + /* new velocity is inside limits */ + tangle_vel = new_tangle_vel; + } + + /* randomly change texture angle acceleration */ + if ((random() % 1000) < 1) + { + tangle_acc = frand(0.002) - 0.001; + } + } + + glFlush(); +} + +/* + * new window size or exposure + */ +void reshape_gleidescope(ModeInfo *mi, int width, int height) +{ + GLfloat h = (GLfloat) height / (GLfloat) width; + + glViewport(0, 0, (GLint) width, (GLint) height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(50.0, 1/h, 0.1, 2000.0); + glMatrixMode (GL_MODELVIEW); + + glLineWidth(1); + glPointSize(1); +} + +static void +pinit(ModeInfo * mi) +{ + gleidestruct *gp = &gleidescope[MI_SCREEN(mi)]; + + /* set start time - star_time = 0 implies non-dynamic texture */ + gp->start_time = (time_t)0; + + /* set the texture size to default */ + gp->max_tx = 1.0; + gp->max_ty = 1.0; + + /* no fading */ + gp->fade = 0; + + glShadeModel(GL_SMOOTH); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + + /* space for textures */ + glGenTextures(1, &gp->textures[0]); + glGenTextures(1, &gp->textures[1]); + gp->visible = 0; + + setup_texture(mi, gp->textures[gp->visible]); + + /* + ** want to choose a value for arg randomly if neither -arg nor -no-arg + ** is specified. xscreensaver libraries don't seem to let you do this - + ** if something isn't true then it is false (pesky two-state boolean values). + ** so, i've defined both -arg and -no-arg to arguments and added the + ** following logic. + ** (btw if both -arg and -no-arg are defined then arg is set to False) + */ + if (zoom == False && nozoom == False) + { + /* no zoom preference - randomise */ + zoom = (((random() & 0x1) == 0x1) ? True : False); + } + else if (nozoom == True) + { + /* definately no zoom */ + zoom = False; + } + + if (move == False && nomove == False) + { + /* no move preference - randomise */ + move = (((random() & 0x1) == 0x1) ? True : False); + } + else if (nomove == True) + { + /* definately no move */ + move = False; + } + + if (rotate == False && norotate == False) + { + /* no rotate preference - randomise */ + rotate = (((random() & 0x1) == 0x1) ? True : False); + } + else if (norotate == True) + { + /* definately no rotate */ + rotate = False; + } + + /* define cam variables */ + gp->cam_x_speed = frand(3.0) - 1.5; + gp->cam_x_phase = random() % 360; + gp->cam_y_speed = frand(3.0) - 1.5; + gp->cam_y_phase = random() % 360; + gp->cam_z_speed = frand(3.0) - 1.5; + gp->cam_z_phase = random() % 360; + + /* initial angular speeds */ + rangle_vel = frand(0.2) - 0.1; + tangle_vel = frand(0.2) - 0.1; + rangle_acc = frand(0.002) - 0.001; + tangle_acc = frand(0.002) - 0.001; + + /* jwz */ + { + GLfloat speed = 15; + rangle_vel *= speed; + tangle_vel *= speed; + rangle_acc *= speed; + tangle_acc *= speed; + } + + /* distance is 11 - size */ + if (size != -1) { + if (zoom) { + fprintf(stderr, "-size given. ignoring -zoom.\n"); + zoom = False; + } + if (size < 1) { + size = 1; + } else if (size >= 10) { + size = 10; + } + size = 11 - size; + } + +#ifdef DEBUG +printf("phases [%d, %d, %d]\n", gp->cam_x_phase, gp->cam_y_phase, gp->cam_z_phase); +#endif +} + +void +init_gleidescope(ModeInfo * mi) +{ + gleidestruct *gp; + int screen = MI_SCREEN(mi); + + + if (gleidescope == NULL) { + gleidescope = (gleidestruct *) calloc(MI_NUM_SCREENS(mi), sizeof (gleidestruct)); + if (gleidescope == NULL) { + return; + } + } + gp = &gleidescope[screen]; + gp->window = MI_WINDOW(mi); + + if ((gp->glx_context = init_GL(mi)) != NULL) { + + reshape_gleidescope(mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + + glDrawBuffer(GL_BACK); + + /* do initialisation */ + pinit(mi); + + } else { + MI_CLEARWINDOW(mi); + } +} + +void +draw_gleidescope(ModeInfo * mi) +{ + gleidestruct *gp = &gleidescope[MI_SCREEN(mi)]; + Display *display = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + + + if (!gp->glx_context) + return; + + glDrawBuffer(GL_BACK); + + glXMakeCurrent(display, window, *(gp->glx_context)); + draw(mi); + + if (mi->fps_p) { + do_fps (mi); + } + + glFinish(); + glXSwapBuffers(display, window); + +#ifdef GRAB + if (grab) { + grab_frame(mi); + } +#endif + + /* need to change texture? */ + if ((gp->start_time != 0) && (duration != -1) && gp->fade == 0) { + if (gp->start_time + duration <= time((time_t *)0)) { + /* get new snapshot (into back buffer) and start fade count */ + getSnapshot(mi, gp->textures[1 - gp->visible]); + gp->fade = 1; + } + } +} + +void +release_gleidescope(ModeInfo * mi) +{ + if (gleidescope != NULL) { + int screen; + + for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) { + gleidestruct *gp = &gleidescope[screen]; + + /* acd - is this needed? */ + if (gp->glx_context) { + /* Display lists MUST be freed while their glXContext is current. */ + glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context)); + + /* acd - was code here for freeing things that are no longer in struct */ + } + } + (void) free((void *) gleidescope); + gleidescope = NULL; + } + + FreeAllGL(mi); +} + +#endif diff --git a/hacks/glx/gleidescope.man b/hacks/glx/gleidescope.man new file mode 100644 index 00000000..97ffacf4 --- /dev/null +++ b/hacks/glx/gleidescope.man @@ -0,0 +1,77 @@ +.TH XScreenSaver 1 "" "X Version 11" +.SH NAME +gleidescope - a tiled OpenGL kaleidescope +.SH SYNOPSIS +.B gleidescope +[\-display \fIhost:display.screen\fP] +[\-visual \fIvisual\fP] +[\-window] +[\-root] +[-delay \fInumber\fP] +[-move] +[-rotate] +[-zoom] +[-image \fIfile\fP] +[-fps] +[-size \fInumber\fP] +[-duration \fInumber\fP] +.SH DESCRIPTION +A tiled kaleidescope using OpenGL. +.SH OPTIONS +.TP 8 +.B \-visual \fIvisual\fP +Specify which visual to use. Legal values are the name of a visual class, +or the id number (decimal or hex) of a specific visual. +.TP 8 +.B \-window +Draw on a newly-created window. This is the default. +.TP 8 +.B \-root +Draw on the root window. +.TP 8 +.B \-delay \fInumber\fP +Per-frame delay, in microseconds. Default: 20000 (0.02 seconds.). +.TP 8 +.B \-move +Move the camera. +.TP 8 +.B \-rotate +Rotate the camera. +.TP 8 +.B \-zoom +Zoom the camera in and out. +.TP 8 +.B \-image \fIfile\fP +The texture map to use at the end of the kaleidescope. +.TP 8 +.B \-fps | \-no-fps +Whether to show a frames-per-second display at the bottom of the screen. +.TP 8 +.B \-size \fInumber\fP +The size of the hexagons being displayed [1(small)-10(large)] +.TP 8 +.B \-duration \fInumber\fP +The time in seconds before another image is chosen. +.TP 8 +.SH ENVIRONMENT +.PP +.TP 8 +.B DISPLAY +to get the default host and display number. +.TP 8 +.B XENVIRONMENT +to get the name of a resource file that overrides the global resources +stored in the RESOURCE_MANAGER property. +.SH SEE ALSO +.BR X (1), +.BR xscreensaver (1) +.SH COPYRIGHT +Copyright \(co 2003 by Andrew Dean 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 +Andrew Dean. diff --git a/hacks/glx/glforestfire.c b/hacks/glx/glforestfire.c index 2f4846d0..2a950731 100644 --- a/hacks/glx/glforestfire.c +++ b/hacks/glx/glforestfire.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* fire --- 3D fire or rain landscape */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)fire.c 5.02 2001/09/26 xlockmore"; #endif diff --git a/hacks/glx/glknots.c b/hacks/glx/glknots.c index ab514646..4ce911ee 100644 --- a/hacks/glx/glknots.c +++ b/hacks/glx/glknots.c @@ -80,7 +80,7 @@ static char *do_spin; static GLfloat speed; static Bool do_wander; static GLfloat thickness; -static int segments; +static unsigned int segments; static int duration; static XrmOptionDescRec opts[] = { @@ -114,7 +114,7 @@ make_knot (ModeInfo *mi) GLfloat diam = (4 * thickness); int faces = (wire ? 3 : 6); - int i; + unsigned int i; double x, y, z, ox=0, oy=0, oz=0; double mu; diff --git a/hacks/glx/glslideshow.c b/hacks/glx/glslideshow.c index 5a88a4e5..fa150bbd 100644 --- a/hacks/glx/glslideshow.c +++ b/hacks/glx/glslideshow.c @@ -33,6 +33,7 @@ # define DEF_IMAGE_DURATION "30" # define DEF_ZOOM "75" # define DEF_FPS_CUTOFF "5" +# define DEF_TITLES "False" # define DEF_DEBUG "False" #define DEFAULTS "*delay: 20000 \n" \ @@ -45,6 +46,8 @@ "*wireframe: False \n" \ "*showFPS: False \n" \ "*fpsSolid: True \n" \ + "*titles: " DEF_TITLES "\n" \ + "*titleFont: -*-times-bold-r-normal-*-180-*\n" \ "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n" # include "xlockmore.h" @@ -69,6 +72,7 @@ typedef struct { GLuint texid; /* which texture to draw */ enum { IN, OUT, DEAD } state; /* how to draw it */ rect from, to; /* the journey this quad is taking */ + char *title; } gls_quad; @@ -100,6 +104,9 @@ typedef struct { Bool low_fps_p; /* Whether we have compensated for a low frame rate. */ + XFontStruct *xfont; + GLuint font_dlist; + } slideshow_state; static slideshow_state *sss = NULL; @@ -107,15 +114,18 @@ static slideshow_state *sss = NULL; /* Command-line arguments */ -int fade_seconds; /* Duration in seconds of fade transitions. - If 0, jump-cut instead of fading. */ -int pan_seconds; /* Duration of each pan through an image. */ -int image_seconds; /* How many seconds until loading a new image. */ -int zoom; /* How far in to zoom when panning, in percent of image - size: that is, 75 means "when zoomed all the way in, - 75% of the image will be on screen." */ -int fps_cutoff; /* If the frame-rate falls below this, turn off zooming.*/ -Bool debug_p; /* Be loud and do weird things. */ +static int fade_seconds; /* Duration in seconds of fade transitions. + If 0, jump-cut instead of fading. */ +static int pan_seconds; /* Duration of each pan through an image. */ +static int image_seconds; /* How many seconds until loading a new image. */ +static int zoom; /* How far in to zoom when panning, in percent of + image size: that is, 75 means "when zoomed all + the way in, 75% of the image will be visible." + */ +static int fps_cutoff; /* If the frame-rate falls below this, turn off + zooming.*/ +static Bool do_titles; /* Display image titles. */ +static Bool debug_p; /* Be loud and do weird things. */ static XrmOptionDescRec opts[] = { @@ -124,6 +134,8 @@ static XrmOptionDescRec opts[] = { {"-duration", ".slideshow.imageDuration", XrmoptionSepArg, 0 }, {"-zoom", ".slideshow.zoom", XrmoptionSepArg, 0 }, {"-cutoff", ".slideshow.FPScutoff", XrmoptionSepArg, 0 }, + {"-titles", ".slideshow.titles", XrmoptionNoArg, "True" }, + {"+titles", ".slideshow.titles", XrmoptionNoArg, "True" }, {"-debug", ".slideshow.debug", XrmoptionNoArg, "True" }, }; @@ -134,6 +146,7 @@ static argtype vars[] = { { &zoom, "zoom", "Zoom", DEF_ZOOM, t_Int}, { &fps_cutoff, "FPScutoff", "FPSCutoff", DEF_FPS_CUTOFF, t_Int}, { &debug_p, "debug", "Debug", DEF_DEBUG, t_Bool}, + { &do_titles, "titles", "Titles", DEF_TITLES, t_Bool}, }; ModeSpecOpt slideshow_opts = {countof(opts), opts, countof(vars), vars, NULL}; @@ -156,6 +169,90 @@ double_time (void) } +static void +load_font (ModeInfo *mi, char *res, XFontStruct **fontP, GLuint *dlistP) +{ + const char *font = get_string_resource (res, "Font"); + XFontStruct *f; + Font id; + int first, last; + + if (!font) font = "-*-times-bold-r-normal-*-180-*"; + + f = XLoadQueryFont(mi->dpy, font); + if (!f) f = XLoadQueryFont(mi->dpy, "fixed"); + + id = f->fid; + first = f->min_char_or_byte2; + last = f->max_char_or_byte2; + + clear_gl_error (); + *dlistP = glGenLists ((GLuint) last+1); + check_gl_error ("glGenLists"); + glXUseXFont(id, first, last-first+1, *dlistP + first); + check_gl_error ("glXUseXFont"); + + *fontP = f; +} + + +static void +print_title_string (ModeInfo *mi, const char *string, GLfloat x, GLfloat y) +{ + slideshow_state *ss = &sss[MI_SCREEN(mi)]; + XFontStruct *font = ss->xfont; + GLfloat line_height = font->ascent + font->descent; + + y -= line_height; + + glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */ + GL_ENABLE_BIT); /* for various glDisable calls */ + glDisable (GL_LIGHTING); + glDisable (GL_DEPTH_TEST); + { + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + { + glLoadIdentity(); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + { + unsigned int i; + int x2 = x; + glLoadIdentity(); + + gluOrtho2D (0, mi->xgwa.width, 0, mi->xgwa.height); + + glRasterPos2f (x, y); + for (i = 0; i < strlen(string); i++) + { + char c = string[i]; + if (c == '\n') + { + glRasterPos2f (x, (y -= line_height)); + x2 = x; + } + else + { + glCallList (ss->font_dlist + (int)(c)); + x2 += (font->per_char + ? font->per_char[c - font->min_char_or_byte2].width + : font->min_bounds.width); + } + } + } + glPopMatrix(); + } + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + } + glPopAttrib(); + + glMatrixMode(GL_MODELVIEW); +} + + static void draw_quad (ModeInfo *mi, gls_quad *q) { @@ -252,6 +349,20 @@ draw_quad (ModeInfo *mi, gls_quad *q) glEnd(); } + if (do_titles && + q->state != DEAD && + q->title && *q->title) + { + /* #### this is wrong -- I really want to draw this with + "1,1,1,opacity", so that the text gets laid down on top + of the image with alpha, but that doesn't work, and I + don't know why... + */ + glColor4f (opacity, opacity, opacity, 1); + print_title_string (mi, q->title, + 10, mi->xgwa.height - 10); + } + glPopMatrix(); if (debug_p) @@ -441,7 +552,19 @@ load_quad (ModeInfo *mi, gls_quad *q) if (wire) goto DONE; - ximage = screen_to_ximage (mi->xgwa.screen, mi->window); + if (q->title) free (q->title); + q->title = 0; + ximage = screen_to_ximage (mi->xgwa.screen, mi->window, &q->title); + + if (q->title) /* strip filename to part after last /. */ + { + char *s = strrchr (q->title, '/'); + if (s) strcpy (q->title, s+1); + } + + if (debug_p) + fprintf (stderr, "%s: debug: loaded image %d (%s)\n", + progname, q->texid, (q->title ? q->title : "(null)")); glBindTexture (GL_TEXTURE_2D, q->texid); glPixelStorei (GL_UNPACK_ALIGNMENT, 1); @@ -521,7 +644,6 @@ load_quad (ModeInfo *mi, gls_quad *q) } - void reshape_slideshow (ModeInfo *mi, int width, int height) { @@ -642,6 +764,8 @@ init_slideshow (ModeInfo *mi) ss->pan_start_time = ss->now; ss->image_start_time = ss->now; + load_font (mi, "titleFont", &ss->xfont, &ss->font_dlist); + for (i = 0; i < countof(ss->texids); i++) glGenTextures (1, &ss->texids[i]); ss->current_texid = 0; diff --git a/hacks/glx/glslideshow.man b/hacks/glx/glslideshow.man index b5208dc8..53c15249 100644 --- a/hacks/glx/glslideshow.man +++ b/hacks/glx/glslideshow.man @@ -12,6 +12,7 @@ glslideshow - slideshow of images using smooth zooming and fades [\-zoom \fIpercent\fP] [\-delay \fIusecs\fP] [\-cutoff \fIint\fP] +[\-titles] [\-fps] [\-debug] [\-wireframe] @@ -67,6 +68,9 @@ The idea here is that if your machine can't maintain a decent frame rate, then it must not have fast 3D hardware, so we might as well behave in a simpler manner. Set this to 0 to disable this check. .TP 8 +.B \-titles +Whether to print the file name of the current image in the upper left corner. +.TP 8 .B \-fps Whether to show a frames-per-second display at the bottom of the screen. .TP 8 diff --git a/hacks/glx/glsnake.c b/hacks/glx/glsnake.c index 90a28ced..dd533539 100644 --- a/hacks/glx/glsnake.c +++ b/hacks/glx/glsnake.c @@ -1,10 +1,9 @@ -/* glsnake, Copyright (c) 2001,2002 - * Jamie Wilkinson, Andrew Bennetts, Peter Aylett - * jaq@spacepants.org, andrew@puzzling.org, peter@ylett.com - * - * ported to xscreensaver 15th Jan 2002 by Jamie Wilkinson - * http://spacepants.org/src/glsnake/ - * +/* glsnake.c - OpenGL imitation of Rubik's Snake + * + * (c) 2001-2003 Jamie Wilkinson + * (c) 2001-2003 Andrew Bennetts + * (c) 2001-2003 Peter Aylett + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -20,42 +19,137 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -extern XtAppContext app; +/* HAVE_GLUT defined if we're building a standalone glsnake, + * and not defined if we're building as an xscreensaver hack */ +#ifdef HAVE_GLUT +# include +#else +# include +# include +#endif -#define PROGCLASS "glsnake" -#define HACK_INIT glsnake_init -#define HACK_DRAW glsnake_draw -#define HACK_RESHAPE glsnake_reshape -#define sws_opts xlockmore_opts +#include +#include +#include +#include +#include + +/* angles */ +#define ZERO 0.0 +#define LEFT 90.0 +#define PIN 180.0 +#define RIGHT 270.0 + +#ifdef HAVE_GETTIMEOFDAY +#ifdef GETTIMEOFDAY_TWO_ARGS +# include +# include + typedef struct timeval snaketime; +# define GETSECS(t) ((t).tv_sec) +# define GETMSECS(t) ((t).tv_usec/1000) +#else /* GETTIMEOFDAY_TWO_ARGS */ +# include +# include + typedef struct timeval snaketime; +# define GETSECS(t) ((t).tv_sec) +# define GETMSECS(t) ((t).tv_usec/1000) +#endif +#else /* HAVE_GETTIMEOFDAY */ +#ifdef HAVE_FTIME +# include + typedef struct timeb snaketime; +# define GETSECS(t) ((long)(t).time) +# define GETMSECS(t) ((t).millitm/1000) +#endif /* HAVE_FTIME */ +#endif /* HAVE_GETTIMEOFDAY */ + +#include + +#ifndef M_SQRT1_2 /* Win32 doesn't have this constant */ +#define M_SQRT1_2 0.70710678118654752440084436210485 +#endif -#define DEF_SPEED "0.05" +#define NODE_COUNT 24 + +#ifdef HAVE_GLUT +#define DEF_YANGVEL 0.10 +#define DEF_ZANGVEL 0.14 +#define DEF_EXPLODE 0.03 +#define DEF_ANGVEL 1.0 +#define DEF_ACCEL 0.1 +#define DEF_STATICTIME 5000 +#define DEF_ALTCOLOUR 0 +#define DEF_TITLES 1 +#define DEF_INTERACTIVE 0 +#define DEF_ZOOM 25.0 +#define DEF_WIREFRAME 0 +#else +/* xscreensaver options doobies prefer strings */ +#define DEF_YANGVEL "0.10" +#define DEF_ZANGVEL "0.14" #define DEF_EXPLODE "0.03" -#define DEF_VELOCITY "1.0" +#define DEF_ANGVEL "1.0" #define DEF_ACCEL "0.1" #define DEF_STATICTIME "5000" -#define DEF_YSPIN "0.10" -#define DEF_ZSPIN "0.14" -#define DEF_SCARYCOLOUR "False" -#define DEF_LABELS "True" -#define DEF_BBOX "False" - -#define DEFAULTS "*delay: 30000 \n" \ - "*count: 30 \n" \ - "*showFPS: False \n" \ - "*wireframe: False \n" \ - "*speed: " DEF_SPEED " \n" \ - "*explode: " DEF_EXPLODE " \n" \ - "*velocity: " DEF_VELOCITY " \n" \ -/* "*accel: " DEF_ACCEL " \n" */ \ - "*statictime: " DEF_STATICTIME " \n" \ - "*yspin: " DEF_YSPIN " \n" \ - "*zspin: " DEF_ZSPIN " \n" \ - "*scarycolour:" DEF_SCARYCOLOUR " \n" \ - "*labels: " DEF_LABELS " \n" \ - "*labelfont: -*-times-bold-r-normal-*-180-*\n" \ - "*bbox: " DEF_BBOX "\n" \ +#define DEF_ALTCOLOUR "False" +#define DEF_TITLES "True" +#define DEF_INTERACTIVE "False" +#define DEF_ZOOM "25.0" +#define DEF_WIREFRAME "False" +#endif + +/* static variables */ +#ifndef HAVE_GLUT +# include +#else +/* xscreensaver boolean type */ +# define Bool int +#endif + +static GLfloat explode; +static GLfloat accel; +static long statictime; +static GLfloat yspin = 0; +static GLfloat zspin = 0; +static GLfloat yangvel; +static GLfloat zangvel; +static Bool altcolour; +static Bool titles; +static Bool interactive; +static Bool wireframe; +static GLfloat zoom; +static GLfloat angvel; + +#ifndef HAVE_GLUT +/* xscreensaver setup */ +extern XtAppContext app; + +#define PROGCLASS "glsnake" +#define HACK_INIT glsnake_init +#define HACK_DRAW glsnake_display +#define HACK_RESHAPE glsnake_reshape +#define sws_opts xlockmore_opts + + +/* xscreensaver defaults */ +#define DEFAULTS "*delay: 30000 \n" \ + "*count: 30 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + "*explode: " DEF_EXPLODE " \n" \ + "*angvel: " DEF_ANGVEL " \n" \ + "*accel: " DEF_ACCEL " \n" \ + "*statictime: " DEF_STATICTIME " \n" \ + "*yangvel: " DEF_YANGVEL " \n" \ + "*zangvel: " DEF_ZANGVEL " \n" \ + "*altcolour: " DEF_ALTCOLOUR " \n" \ + "*titles: True \n" \ + "*labelfont: -*-times-bold-r-normal-*-180-*\n" \ + "*zoom: " DEF_ZOOM " \n" \ @@ -64,688 +158,1334 @@ extern XtAppContext app; #include "xlockmore.h" -#ifdef USE_GL /* whole file */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ZERO 0 -#define LEFT 1 -#define PIN 2 -#define RIGHT 3 - -typedef struct model_s { - char * name; - float node[24]; -} model_t; - -typedef struct nodeang_s { - float cur_angle; - float dest_angle; -} nodeang_t; - -typedef struct { - GLXContext * glx_context; - - int node_list; /* name of the display list */ - int is_cyclic; - int is_legal; - int last_turn; - int selected; - struct timeval last_iteration; - struct timeval last_morph; - int morphing; - nodeang_t node[24]; - GLfloat roty; - GLfloat rotz; - int paused; - int dragging; - int m_count; - int m; - int cur_model; - int interactive; - GLfloat colour_t[3]; - GLfloat colour_i[3]; - GLfloat colour[3]; - model_t * models; - - XFontStruct * font; - GLuint font_list; -} glsnake_configuration; - -static glsnake_configuration *glc = NULL; - -static GLfloat speed; -static GLfloat explode; -static GLfloat velocity; -/* static GLfloat accel; */ -static long statictime; -static GLfloat yspin; -static GLfloat zspin; -static Bool scarycolour; -static Bool labels; -static Bool do_bbox; - static XrmOptionDescRec opts[] = { - { "-speed", ".speed", XrmoptionSepArg, 0 }, - { "-explode", ".explode", XrmoptionSepArg, 0 }, - { "-velocity", ".velocity", XrmoptionSepArg, 0 }, -/* { "-accel", ".accel", XrmoptionSepArg, 0 }, */ - { "-statictime", ".statictime", XrmoptionSepArg, 0 }, - { "-yspin", ".yspin", XrmoptionSepArg, 0 }, - { "-zspin", ".zspin", XrmoptionSepArg, 0 }, - { "-scarycolour", ".scarycolour", XrmoptionNoArg, "True" }, - { "+scarycolour", ".scarycolour", XrmoptionNoArg, "False" }, - { "-labels", ".labels", XrmoptionNoArg, "True" }, - { "+labels", ".labels", XrmoptionNoArg, "False" }, -#if 0 - { "-bbox", ".bbox", XrmoptionNoArg, "True" }, - { "+bbox", ".bbox", XrmoptionNoArg, "False" }, -#endif + { "-explode", ".explode", XrmoptionSepArg, DEF_EXPLODE }, + { "-angvel", ".angvel", XrmoptionSepArg, DEF_ANGVEL }, + { "-accel", ".accel", XrmoptionSepArg, DEF_ACCEL }, + { "-statictime", ".statictime", XrmoptionSepArg, DEF_STATICTIME }, + { "-yangvel", ".yangvel", XrmoptionSepArg, DEF_YANGVEL }, + { "-zangvel", ".zangvel", XrmoptionSepArg, DEF_ZANGVEL }, + { "-altcolour", ".altcolour", XrmoptionNoArg, (caddr_t) "True" }, + { "-no-altcolour", ".altcolour", XrmoptionNoArg, (caddr_t) "False" }, + { "-titles", ".titles", XrmoptionNoArg, (caddr_t) "True" }, + { "-no-titles", ".titles", XrmoptionNoArg, (caddr_t) "False" }, + { "-zoom", ".zoom", XrmoptionSepArg, DEF_ZOOM }, + { "-wireframe", ".wireframe", XrmoptionNoArg, (caddr_t) "true" }, + { "-no-wireframe", ".wireframe", XrmoptionNoArg, (caddr_t) "false" }, }; static argtype vars[] = { - {(caddr_t *) &speed, "speed", "Speed", DEF_SPEED, t_Float}, - {(caddr_t *) &explode, "explode", "Explode", DEF_EXPLODE, t_Float}, - {(caddr_t *) &velocity, "velocity", "Velocity", DEF_VELOCITY, t_Float}, -/* {(caddr_t *) &accel, "accel", "Acceleration", DEF_ACCEL, t_Float}, */ - {(caddr_t *) &statictime, "statictime", "Static Time", DEF_STATICTIME, t_Int}, - {(caddr_t *) &yspin, "yspin", "Y Spin", DEF_YSPIN, t_Float}, - {(caddr_t *) &zspin, "zspin", "Z Spin", DEF_ZSPIN, t_Float}, -/* {(caddr_t *) &interactive, "interactive", "Interactive", DEF_INTERACTIVE, t_Bool}, */ - {(caddr_t *) &scarycolour, "scarycolour", "Scary Colour", DEF_SCARYCOLOUR, t_Bool}, - {(caddr_t *) &labels, "labels", "Labels", DEF_LABELS, t_Bool}, - {(caddr_t *) &do_bbox, "bbox", "BBox", DEF_BBOX, t_Bool}, + {(caddr_t *) &explode, "explode", "Explode", DEF_EXPLODE, t_Float}, + {(caddr_t *) &angvel, "angvel", "Angular Velocity", DEF_ANGVEL, t_Float}, + {(caddr_t *) &accel, "accel", "Acceleration", DEF_ACCEL, t_Float}, + {(caddr_t *) &statictime, "statictime", "Static Time", DEF_STATICTIME, t_Int}, + {(caddr_t *) &yangvel, "yangvel", "Angular Velocity about Y axis", DEF_YANGVEL, t_Float}, + {(caddr_t *) &zangvel, "zangvel", "Angular Velocity about X axis", DEF_ZANGVEL, t_Float}, + {(caddr_t *) &interactive, "interactive", "Interactive", DEF_INTERACTIVE, t_Bool}, + {(caddr_t *) &altcolour, "altcolour", "Alternate Colour Scheme", DEF_ALTCOLOUR, t_Bool}, + {(caddr_t *) &titles, "titles", "Titles", DEF_TITLES, t_Bool}, + {(caddr_t *) &zoom, "zoom", "Zoom", DEF_ZOOM, t_Float}, + {(caddr_t *) &wireframe, "wireframe", "Wireframe", DEF_WIREFRAME, t_Bool}, }; ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL}; +#endif -/* prism models */ -#define VOFFSET 0.045 -float solid_prism_v[][3] = { - /* first corner, bottom left front */ - { VOFFSET, VOFFSET, 1.0 }, - { VOFFSET, 0.0, 1.0 - VOFFSET }, - { 0.0, VOFFSET, 1.0 - VOFFSET }, - /* second corner, rear */ - { VOFFSET, VOFFSET, 0.00 }, - { VOFFSET, 0.0, VOFFSET }, - { 0.0, VOFFSET, VOFFSET }, - /* third, right front */ - { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 1.0 }, - { 1.0 - VOFFSET / M_SQRT1_2, 0.0, 1.0 - VOFFSET }, - { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, 1.0 - VOFFSET }, - /* fourth, right rear */ - { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 0.0 }, - { 1.0 - VOFFSET / M_SQRT1_2, 0.0, VOFFSET }, - { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, VOFFSET }, - /* fifth, upper front */ - { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 1.0 }, - { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, 1.0 - VOFFSET }, - { 0.0, 1.0 - VOFFSET / M_SQRT1_2, 1.0 - VOFFSET}, - /* sixth, upper rear */ - { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 0.0 }, - { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, VOFFSET }, - { 0.0, 1.0 - VOFFSET / M_SQRT1_2, VOFFSET } +struct model_s { + char * name; + float node[NODE_COUNT]; }; -/* normals */ -float solid_prism_n[][3] = { - /* corners */ - { -VOFFSET, -VOFFSET, VOFFSET }, - { VOFFSET, -VOFFSET, VOFFSET }, - { -VOFFSET, VOFFSET, VOFFSET }, - { -VOFFSET, -VOFFSET, -VOFFSET }, - { VOFFSET, -VOFFSET, -VOFFSET }, - { -VOFFSET, VOFFSET, -VOFFSET }, - /* edges */ - { -VOFFSET, 0.0, VOFFSET }, - { 0.0, -VOFFSET, VOFFSET }, - { VOFFSET, VOFFSET, VOFFSET }, - { -VOFFSET, 0.0, -VOFFSET }, - { 0.0, -VOFFSET, -VOFFSET }, - { VOFFSET, VOFFSET, -VOFFSET }, - { -VOFFSET, -VOFFSET, 0.0 }, - { VOFFSET, -VOFFSET, 0.0 }, - { -VOFFSET, VOFFSET, 0.0 }, - /* faces */ - { 0.0, 0.0, 1.0 }, - { 0.0, -1.0, 0.0 }, - { M_SQRT1_2, M_SQRT1_2, 0.0 }, - { -1.0, 0.0, 0.0 }, - { 0.0, 0.0, -1.0 } -}; +struct glsnake_cfg { +#ifndef HAVE_GLUT + GLXContext * glx_context; + XFontStruct * font; + GLuint font_list; +#else + /* font list number */ + int font; +#endif + + /* window id */ + int window; + + /* is a morph in progress? */ + int morphing; + + /* has the model been paused? */ + int paused; + + /* snake metrics */ + int is_cyclic; + int is_legal; + int last_turn; + int debug; + + /* the shape of the model */ + float node[NODE_COUNT]; + + /* currently selected node for interactive mode */ + int selected; -/* vertices */ -float wire_prism_v[][3] = { - { 0.0, 0.0, 1.0 }, - { 1.0, 0.0, 1.0 }, - { 0.0, 1.0, 1.0 }, - { 0.0, 0.0, 0.0 }, - { 1.0, 0.0, 0.0 }, - { 0.0, 1.0, 0.0 } + /* models */ + int prev_model; + int next_model; + + /* model morphing */ + int new_morph; + + /* colours */ + float colour[2][3]; + int next_colour; + int prev_colour; + + /* timing variables */ + snaketime last_iteration; + snaketime last_morph; + + /* window size */ + int width, height; + int old_width, old_height; + + /* the id of the display lists for drawing a node */ + int node_solid, node_wire; + + /* is the window fullscreen? */ + int fullscreen; }; -/* normals */ -float wire_prism_n[][3] = { - { 0.0, 0.0, 1.0}, - { 0.0,-1.0, 0.0}, - { M_SQRT1_2, M_SQRT1_2, 0.0}, - {-1.0, 0.0, 0.0}, - { 0.0, 0.0,-1.0} +#define COLOUR_CYCLIC 0 +#define COLOUR_ACYCLIC 1 +#define COLOUR_INVALID 2 +#define COLOUR_AUTHENTIC 3 + +float colour[][2][3] = { + /* cyclic - green */ + { { 0.4, 0.8, 0.2 }, + { 1.0, 1.0, 1.0 } }, + /* acyclic - blue */ + { { 0.3, 0.1, 0.9 }, + { 1.0, 1.0, 1.0 } }, + /* invalid - grey */ + { { 0.3, 0.1, 0.9 }, + { 1.0, 1.0, 1.0 } }, + /* authentic - purple and green */ + { { 0.38, 0.0, 0.55 }, + { 0.0, 0.5, 0.34 } } }; -/* default models */ -#define Z 0.0 -#define L 90.0 -#define P 180.0 -#define R 270.0 - -static model_t default_models[] = { - { "Ball", - { R, R, L, L, R, L, R, R, L, R, L, L, R, R, L, L, R, L, R, R, L, R, L } - }, - { "Snow", - { R, R, R, R, L, L, L, L, R, R, R, R, L, L, L, L, R, R, R, R, L, L, L } - }, - { "Propellor", - { Z, Z, Z, R, L, R, Z, L, Z, Z, Z, R, L, R, Z, L, Z, Z, Z, R, L, R, Z } - }, - { "Flamingo", - { Z, P, Z, Z, Z, Z, Z, P, R, R, P, R, L, P, L, R, P, R, R, Z, Z, Z, P } - }, - { "Cat", - { Z, P, P, Z, P, P, Z, L, Z, P, P, Z, P, P, Z, P, P, Z, Z, Z, Z, Z, Z } - }, - { "Rooster", - { Z, Z, P, P, Z, L, Z, L, R, P, R, Z, P, P, Z, R, P, R, L, Z, L, Z, P } - } +struct model_s model[] = { +#define STRAIGHT_MODEL 0 + { "straight", + { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, + ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, + ZERO, ZERO } + }, + /* the models in the Rubik's snake manual */ +#define START_MODEL 1 + { "ball", + { RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, + RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, + RIGHT, LEFT, RIGHT, LEFT } + }, + { "snow", + { RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, + RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, + RIGHT, LEFT, LEFT, LEFT } + }, + { "propellor", + { ZERO, ZERO, ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT, ZERO, ZERO, + ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT, ZERO, ZERO, ZERO, RIGHT, + LEFT, RIGHT, ZERO, LEFT } + }, + { "flamingo", + { ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, RIGHT, PIN, + RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, ZERO, ZERO, + ZERO, PIN } + }, + { "cat", + { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, + ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, + ZERO } + }, + { "rooster", + { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, LEFT, RIGHT, PIN, RIGHT, + ZERO, PIN, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, ZERO, LEFT, ZERO, + PIN } + }, + /* These models were taken from Andrew and Peter's original snake.c + * as well as some newer ones made up by Jamie, Andrew and Peter. */ + { "half balls", + { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, + LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, + RIGHT, LEFT, LEFT, LEFT } + }, + { "zigzag1", + { RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, + LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, + RIGHT, RIGHT, LEFT, LEFT } + }, + { "zigzag2", + { PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, + ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN } + }, + { "zigzag3", + { PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, + LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN } + }, + { "caterpillar", + { RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, + LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, + LEFT, LEFT } + }, + { "bow", + { RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, + LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, + RIGHT, RIGHT, LEFT, LEFT } + }, + { "turtle", + { ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, + LEFT, RIGHT, LEFT, LEFT, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, + RIGHT, RIGHT, RIGHT } + }, + { "basket", + { RIGHT, PIN, ZERO, ZERO, PIN, LEFT, ZERO, LEFT, LEFT, ZERO, + LEFT, PIN, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO, + PIN, LEFT } + }, + { "thing", + { PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, + LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, + RIGHT, LEFT, LEFT } + }, + { "hexagon", + { ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, + ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, + LEFT, ZERO, ZERO, RIGHT } + }, + { "tri1", + { ZERO, ZERO, LEFT, RIGHT, ZERO, LEFT, ZERO, RIGHT, ZERO, ZERO, + LEFT, RIGHT, ZERO, LEFT, ZERO, RIGHT, ZERO, ZERO, LEFT, RIGHT, + ZERO, LEFT, ZERO, RIGHT } + }, + { "triangle", + { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, + ZERO, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, ZERO, ZERO, + ZERO, ZERO, LEFT, RIGHT } + }, + { "flower", + { ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, + RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, + RIGHT, RIGHT, PIN } + }, + { "crucifix", + { ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, + PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN } + }, + { "kayak", + { PIN, RIGHT, LEFT, PIN, LEFT, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, + ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, + PIN, RIGHT } + }, + { "bird", + { ZERO, ZERO, ZERO, ZERO, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, + ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, + LEFT, ZERO, PIN } + }, + { "seal", + { RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, + LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, PIN, RIGHT, + RIGHT, LEFT } + }, + { "dog", + { ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, + ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN } + }, + { "frog", + { RIGHT, RIGHT, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, + RIGHT, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, + RIGHT, LEFT, LEFT } + }, + { "quavers", + { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, ZERO, ZERO, + RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, ZERO, LEFT, LEFT, + RIGHT, LEFT, RIGHT, RIGHT } + }, + { "fly", + { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, PIN, ZERO, ZERO, + LEFT, PIN, RIGHT, ZERO, ZERO, PIN, ZERO, LEFT, LEFT, RIGHT, LEFT, + RIGHT, RIGHT } + }, + { "puppy", + { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, + RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, + LEFT } + }, + { "stars", + { LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, + ZERO, ZERO, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, + RIGHT, LEFT } + }, + { "mountains", + { RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, + LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, + PIN, LEFT, PIN } + }, + { "quad1", + { RIGHT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN, + LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, + LEFT, PIN, LEFT, PIN } + }, + { "quad2", + { ZERO, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN, + ZERO, PIN, ZERO, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, + PIN, ZERO, PIN } + }, + { "glasses", + { ZERO, PIN, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, PIN, + ZERO, PIN, ZERO, PIN, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, + PIN, ZERO, PIN } + }, + { "em", + { ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, + ZERO, PIN, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, + PIN, ZERO, PIN } + }, + { "quad3", + { ZERO, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, + ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, + LEFT, ZERO, PIN } + }, + { "vee", + { ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, + ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, + ZERO, ZERO, PIN } + }, + { "square", + { ZERO, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, ZERO, + ZERO, PIN, ZERO, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, + ZERO, ZERO, PIN } + }, + { "eagle", + { RIGHT, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, ZERO, + LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, + ZERO, ZERO, LEFT, PIN } + }, + { "volcano", + { RIGHT, ZERO, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, + ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, RIGHT, RIGHT, PIN, LEFT, + LEFT, RIGHT, ZERO, LEFT, PIN } + }, + { "saddle", + { RIGHT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO, + LEFT, PIN, RIGHT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, + ZERO, LEFT, PIN } + }, + { "c3d", + { ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT, ZERO, + ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT, + ZERO, ZERO, PIN } + }, + { "block", + { ZERO, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, + RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT, + PIN, RIGHT } + }, + { "duck", + { LEFT, PIN, LEFT, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, LEFT, + PIN, RIGHT, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN, + LEFT } + }, + { "prayer", + { RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, ZERO, + ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, RIGHT, RIGHT, LEFT, + RIGHT, LEFT, LEFT, LEFT, PIN } + }, + { "giraffe", + { ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, RIGHT, + RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, + PIN, LEFT, LEFT, LEFT } + }, + { "tie fighter", + { PIN, LEFT, RIGHT, LEFT, LEFT, PIN, RIGHT, ZERO, RIGHT, LEFT, + ZERO, PIN, LEFT, LEFT, RIGHT, RIGHT, RIGHT, PIN, LEFT, ZERO, + LEFT, RIGHT, ZERO } + }, + { "Strong Arms", + { PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, RIGHT, + RIGHT, PIN, RIGHT, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, + ZERO, PIN, PIN, ZERO } + }, + + /* the following modesl were created during the slug/compsoc codefest + * febrray 2003 */ + { "cool gegl", + { PIN, PIN, ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, + ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, ZERO, + ZERO, ZERO } + }, + { "knuckledusters", + { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, + PIN, ZERO, RIGHT, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, + RIGHT, ZERO } + }, + { "k's turd", + { RIGHT, RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, + RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, + RIGHT, LEFT, RIGHT, PIN, ZERO } + }, + { "lightsabre", + { ZERO, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, + ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, + ZERO, ZERO } + }, + { "not a stairway", + { LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, + RIGHT, LEFT, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, + RIGHT, ZERO, LEFT, ZERO } + }, + { "arse gegl", + { ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, + PIN, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, ZERO, PIN, PIN, ZERO, + PIN, ZERO } + }, + { "box", + { ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, + ZERO, PIN, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, + ZERO, ZERO, ZERO } + }, + { "kissy box", + { PIN, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, + ZERO, PIN, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, + ZERO, PIN, ZERO } + }, + { "erect penis", /* thanks benno */ + { PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, PIN, + PIN, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, + ZERO, ZERO } + }, + { "flaccid penis", + { PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, PIN, + PIN, ZERO, ZERO, ZERO, RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, + ZERO, ZERO } + }, + { "vagina", + { RIGHT, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, + LEFT, ZERO, ZERO, ZERO, LEFT, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, + PIN, RIGHT, ZERO } + }, + { "mask", + { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN, + ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, + ZERO, ZERO, ZERO } + }, + { "poles or columns or something", + { LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, + ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, + ZERO, LEFT, ZERO } + }, + { "crooked v", + { ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, + ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, + ZERO, ZERO, ZERO } + }, + { "dog leg", + { ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, + LEFT, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, + ZERO, ZERO } + }, + { "scrubby", + { ZERO, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, + LEFT, RIGHT, ZERO, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, + LEFT, PIN, ZERO, ZERO } + }, + { "voltron's eyes", + { ZERO, ZERO, PIN, RIGHT, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, + LEFT, PIN, ZERO, ZERO, PIN, ZERO, LEFT, ZERO, RIGHT, LEFT, ZERO, + RIGHT, ZERO, ZERO } + }, + { "flying toaster", + { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, + RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, + PIN, ZERO } + }, + { "dubbya", + { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, + ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, + PIN, ZERO } + }, + { "tap handle", + { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, + LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, + PIN, ZERO } + }, + { "wingnut", + { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, + PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, + PIN, ZERO } + }, + { "tight twist", + { RIGHT, ZERO, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, + RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, + ZERO, ZERO, RIGHT, ZERO } + }, + { "double helix", + { RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT, + ZERO, RIGHT, ZERO, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, ZERO, + RIGHT, ZERO, RIGHT, ZERO, ZERO } + }, + + /* These models come from the website at + * http://www.geocities.com/stigeide/snake */ + { "Abstract", + { RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, RIGHT, ZERO } + }, + { "AlanH1", + { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN } + }, + { "AlanH2", + { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT } + }, + { "AlanH3", + { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN } + }, + { "AlanH4", + { ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, PIN, ZERO, ZERO } + }, + { "Alien", + { RIGHT, LEFT, RIGHT, PIN, ZERO, ZERO, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, ZERO, PIN, PIN } + }, + { "Angel", + { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT } + }, + { "AnotherFigure", + { LEFT, PIN, RIGHT, ZERO, ZERO, PIN, RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, ZERO } + }, + { "Ball", + { LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT } + }, + { "Basket", + { ZERO, RIGHT, RIGHT, ZERO, RIGHT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, ZERO, LEFT } + }, + { "Beetle", + { PIN, LEFT, RIGHT, ZERO, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, LEFT, RIGHT, PIN, RIGHT } + }, + { "Bone", + { PIN, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, PIN } + }, + { "Bow", + { LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT } + }, + { "Bra", + { RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT } + }, + { "BronchoSaurian", + { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, PIN } + }, + { "Cactus", + { PIN, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, ZERO, ZERO } + }, + { "Camel", + { RIGHT, ZERO, PIN, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, ZERO, ZERO, LEFT } + }, + { "Candlestick", + { LEFT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT } + }, + { "Cat", + { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO } + }, + { "Cave", + { RIGHT, ZERO, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, ZERO, ZERO } + }, + { "Chains", + { PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO } + }, + { "Chair", + { RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, LEFT, RIGHT, LEFT, LEFT } + }, + { "Chick", + { RIGHT, RIGHT, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, LEFT, LEFT } + }, + { "Clockwise", + { RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT } + }, + { "Cobra", + { ZERO, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, LEFT, ZERO, LEFT, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT } + }, + { "Cobra2", + { LEFT, ZERO, PIN, ZERO, PIN, LEFT, ZERO, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, PIN, ZERO, RIGHT } + }, + { "Cobra3", + { ZERO, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, LEFT } + }, + { "Compact1", + { ZERO, ZERO, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN } + }, + { "Compact2", + { LEFT, PIN, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO } + }, + { "Compact3", + { ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN } + }, + { "Compact4", + { PIN, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO } + }, + { "Compact5", + { LEFT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT } + }, + { "Contact", + { PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN, RIGHT, PIN } + }, + { "Contact2", + { RIGHT, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, LEFT } + }, + { "Cook", + { ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, ZERO, RIGHT, ZERO, PIN } + }, + { "Counterclockwise", + { LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT } + }, + { "Cradle", + { LEFT, LEFT, ZERO, PIN, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, RIGHT, LEFT, LEFT, ZERO, ZERO, RIGHT } + }, + { "Crankshaft", + { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, PIN, RIGHT } + }, + { "Cross", + { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN } + }, + { "Cross2", + { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO } + }, + { "Cross3", + { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO } + }, + { "CrossVersion1", + { PIN, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN } + }, + { "CrossVersion2", + { RIGHT, LEFT, PIN, LEFT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, LEFT, PIN, LEFT, RIGHT } + }, + { "Crown", + { LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, PIN, ZERO } + }, + { "DNAStrand", + { RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT } + }, + { "Diamond", + { ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, LEFT } + }, + { "Dog", + { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, LEFT, RIGHT } + }, + { "DogFace", + { ZERO, ZERO, PIN, PIN, ZERO, LEFT, LEFT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, RIGHT, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, PIN } + }, + { "DoublePeak", + { ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, LEFT, ZERO, PIN, ZERO, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT } + }, + { "DoubleRoof", + { ZERO, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT } + }, + { "DoubleToboggan", + { ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN } + }, + { "Doubled", + { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT } + }, + { "Doubled1", + { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, ZERO, RIGHT, ZERO, RIGHT, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT } + }, + { "Doubled2", + { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, RIGHT, ZERO, RIGHT, LEFT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT } + }, + { "DumblingSpoon", + { PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO } + }, + { "Embrace", + { PIN, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO } + }, + { "EndlessBelt", + { ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, RIGHT } + }, + { "Entrance", + { LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT } + }, + { "Esthetic", + { LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT } + }, + { "Explotion", + { RIGHT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT } + }, + { "F-ZeroXCar", + { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT } + }, + { "Face", + { ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT } + }, + { "Fantasy", + { LEFT, LEFT, RIGHT, PIN, ZERO, RIGHT, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, ZERO, LEFT, ZERO, PIN, LEFT, RIGHT, RIGHT, RIGHT, PIN } + }, + { "Fantasy1", + { PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN } + }, + { "FaserGun", + { ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, RIGHT, ZERO, PIN } + }, + { "FelixW", + { ZERO, RIGHT, ZERO, PIN, LEFT, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, RIGHT, PIN, ZERO, LEFT, ZERO } + }, + { "Flamingo", + { ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, LEFT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, ZERO, ZERO, ZERO, PIN } + }, + { "FlatOnTheTop", + { ZERO, PIN, PIN, ZERO, PIN, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, PIN } + }, + { "Fly", + { ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT } + }, + { "Fountain", + { LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN } + }, + { "Frog", + { LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, RIGHT, LEFT, RIGHT, RIGHT } + }, + { "Frog2", + { LEFT, ZERO, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, ZERO, RIGHT } + }, + { "Furby", + { PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN } + }, + { "Gate", + { ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, ZERO, PIN, PIN, ZERO } + }, + { "Ghost", + { LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT } + }, + { "Globus", + { RIGHT, LEFT, ZERO, PIN, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, LEFT, ZERO } + }, + { "Grotto", + { PIN, PIN, ZERO, LEFT, RIGHT, LEFT, ZERO, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, ZERO, RIGHT, LEFT, RIGHT } + }, + { "H", + { PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO } + }, + { "HeadOfDevil", + { PIN, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, ZERO } + }, + { "Heart", + { RIGHT, ZERO, ZERO, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, RIGHT, ZERO, PIN, ZERO, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN, ZERO, ZERO, ZERO, LEFT } + }, + { "Heart2", + { ZERO, PIN, ZERO, ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO } + }, + { "Hexagon", + { ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO } + }, + { "HoleInTheMiddle1", + { ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT } + }, + { "HoleInTheMiddle2", + { ZERO, LEFT, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT } + }, + { "HouseBoat", + { RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT, PIN } + }, + { "HouseByHouse", + { LEFT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT } + }, + { "Infinity", + { LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT } + }, + { "Integral", + { RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT } + }, + { "Iron", + { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, RIGHT } + }, + { "JustSquares", + { RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, LEFT } + }, + { "Kink", + { ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO } + }, + { "Knot", + { LEFT, LEFT, PIN, LEFT, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, RIGHT, PIN, RIGHT, RIGHT, LEFT } + }, + { "Leaf", + { ZERO, PIN, PIN, ZERO, ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO } + }, + { "LeftAsRight", + { RIGHT, PIN, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, PIN, LEFT } + }, + { "Long-necked", + { PIN, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, PIN, PIN, ZERO } + }, + { "LunaModule", + { PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT } + }, + { "MagnifyingGlass", + { ZERO, ZERO, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO } + }, + { "Mask", + { ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT } + }, + { "Microscope", + { PIN, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN } + }, + { "Mirror", + { PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, PIN, ZERO } + }, + { "MissPiggy", + { ZERO, LEFT, LEFT, PIN, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, LEFT, PIN, RIGHT, RIGHT, ZERO, RIGHT } + }, + { "Mole", + { ZERO, RIGHT, ZERO, RIGHT, LEFT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, ZERO, RIGHT, RIGHT, PIN, LEFT } + }, + { "Monk", + { LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT } + }, + { "Mountain", + { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, LEFT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO } + }, + { "Mountains", + { ZERO, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO } + }, + { "MouseWithoutTail", + { ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO } + }, + { "Mushroom", + { PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN } + }, + { "Necklace", + { ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO } + }, + { "NestledAgainst", + { LEFT, ZERO, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT } + }, + { "NoClue", + { ZERO, RIGHT, PIN, LEFT, LEFT, LEFT, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, RIGHT, RIGHT, RIGHT, PIN, LEFT, ZERO } + }, + { "Noname", + { LEFT, PIN, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT } + }, + { "Obelisk", + { PIN, ZERO, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO, ZERO } + }, + { "Ostrich", + { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN } + }, + { "Ostrich2", + { PIN, PIN, ZERO, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO } + }, + { "PairOfGlasses", + { ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO } + }, + { "Parrot", + { ZERO, ZERO, ZERO, ZERO, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, ZERO, PIN } + }, + { "Penis", + { PIN, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, PIN, PIN } + }, + { "PictureCommingSoon", + { LEFT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, RIGHT, RIGHT } + }, + { "Pitti", + { LEFT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT } + }, + { "Plait", + { LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT } + }, + { "Platform", + { RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT } + }, + { "PodRacer", + { ZERO, PIN, ZERO, PIN, RIGHT, PIN, ZERO, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, ZERO, PIN, LEFT } + }, + { "Pokemon", + { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT } + }, + { "Prawn", + { RIGHT, PIN, ZERO, PIN, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, PIN, ZERO, PIN, LEFT } + }, + { "Propeller", + { ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT } + }, + { "Pyramid", + { ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, LEFT } + }, + { "QuarterbackTiltedAndReadyToHut", + { PIN, ZERO, RIGHT, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, LEFT, ZERO, PIN } + }, + { "Ra", + { PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT } + }, + { "Rattlesnake", + { LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, PIN, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT } + }, + { "Revelation", + { ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN } + }, + { "Revolution1", + { LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT } + }, + { "Ribbon", + { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT } + }, + { "Rocket", + { RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, RIGHT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT } + }, + { "Roofed", + { ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, ZERO, PIN, ZERO, RIGHT } + }, + { "Roofs", + { PIN, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, PIN } + }, + { "RowHouses", + { RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT } + }, + { "Sculpture", + { RIGHT, LEFT, PIN, ZERO, ZERO, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT } + }, + { "Seal", + { LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, LEFT } + }, + { "Seal2", + { RIGHT, PIN, ZERO, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO } + }, + { "Sheep", + { RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, LEFT } + }, + { "Shelter", + { LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT } + }, + { "Ship", + { PIN, RIGHT, LEFT, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, LEFT, ZERO, PIN, PIN } + }, + { "Shpongle", + { LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO } + }, + { "Slide", + { LEFT, RIGHT, LEFT, RIGHT, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, RIGHT, LEFT } + }, + { "SmallShip", + { ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, LEFT, RIGHT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, LEFT } + }, + { "SnakeReadyToStrike", + { LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, LEFT } + }, + { "Snakes14", + { RIGHT, RIGHT, PIN, ZERO, RIGHT, LEFT, RIGHT, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, LEFT, RIGHT } + }, + { "Snakes15", + { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, PIN, RIGHT } + }, + { "Snakes18", + { PIN, PIN, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, PIN } + }, + { "Snowflake", + { LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT } + }, + { "Snowman", + { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO } + }, + { "Source", + { PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN } + }, + { "Spaceship", + { PIN, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, PIN } + }, + { "Spaceship2", + { PIN, PIN, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, LEFT, LEFT, PIN, PIN } + }, + { "Speedboat", + { LEFT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT } + }, + { "Speedboat2", + { PIN, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, ZERO, RIGHT, PIN, LEFT } + }, + { "Spider", + { RIGHT, RIGHT, ZERO, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, RIGHT, LEFT, RIGHT, ZERO, ZERO, LEFT } + }, + { "Spitzbergen", + { PIN, LEFT, ZERO, RIGHT, RIGHT, LEFT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, LEFT, LEFT, ZERO } + }, + { "Square", + { ZERO, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, ZERO } + }, + { "SquareHole", + { PIN, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN } + }, + { "Stage", + { RIGHT, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO } + }, + { "Stairs", + { ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO } + }, + { "Stairs2", + { ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO } + }, + { "Straight", + { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO } + }, + { "Swan", + { ZERO, PIN, ZERO, PIN, LEFT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT } + }, + { "Swan2", + { PIN, ZERO, PIN, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, PIN } + }, + { "Swan3", + { PIN, PIN, ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, RIGHT } + }, + { "Symbol", + { RIGHT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, RIGHT } + }, + { "Symmetry", + { RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT } + }, + { "Symmetry2", + { ZERO, PIN, LEFT, LEFT, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, PIN, LEFT } + }, + { "TableFireworks", + { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, RIGHT, PIN } + }, + { "Tapering", + { ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, ZERO, ZERO } + }, + { "TaperingTurned", + { ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, ZERO, ZERO } + }, + { "TeaLightStick", + { RIGHT, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN } + }, + { "Tent", + { RIGHT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO } + }, + { "Terraces", + { RIGHT, LEFT, ZERO, RIGHT, LEFT, PIN, LEFT, LEFT, PIN, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT } + }, + { "Terrier", + { PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO } + }, + { "Three-Legged", + { RIGHT, ZERO, LEFT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, RIGHT, ZERO, PIN, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, RIGHT, ZERO, LEFT } + }, + { "ThreePeaks", + { RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT } + }, + { "ToTheFront", + { ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, LEFT, LEFT, PIN, ZERO, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT } + }, + { "Top", + { PIN, LEFT, LEFT, PIN, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, ZERO } + }, + { "Transport", + { PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO } + }, + { "Triangle", + { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT } + }, + { "Tripple", + { PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN } + }, + { "Turtle", + { RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO } + }, + { "Twins", + { ZERO, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, ZERO, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO, ZERO } + }, + { "TwoSlants", + { ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN } + }, + { "TwoWings", + { PIN, LEFT, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, ZERO } + }, + { "UFO", + { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT } + }, + { "USSEnterprice", + { LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, ZERO } + }, + { "UpAndDown", + { ZERO, PIN, ZERO, PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO } + }, + { "Upright", + { ZERO, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO } + }, + { "Upside-down", + { PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, RIGHT, LEFT, LEFT, PIN, RIGHT, RIGHT, LEFT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN } + }, + { "Valley", + { ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO } + }, + { "Viaduct", + { PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO, PIN, RIGHT, ZERO, RIGHT, RIGHT, ZERO, RIGHT, PIN, ZERO, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO } + }, + { "View", + { ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT } + }, + { "Waterfall", + { LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT } + }, + { "WindWheel", + { PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO } + }, + { "Window", + { PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO } + }, + { "WindowToTheWorld", + { PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO } + }, + { "Windshield", + { PIN, PIN, ZERO, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, ZERO, PIN, PIN, ZERO, PIN } + }, + { "WingNut", + { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, RIGHT, PIN, ZERO, ZERO, ZERO, ZERO } + }, + { "Wings2", + { RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, ZERO } + }, + { "WithoutName", + { PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN } + }, + { "Wolf", + { ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN } + }, + { "X", + { LEFT, ZERO, ZERO, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, ZERO, ZERO } + }, }; -/* add a model to the model list */ -model_t * add_model(model_t * models, char * name, int * rotations, int * count) -{ - int i; - - (*count)++; - models = realloc(models, sizeof(model_t) * (*count)); - models[(*count)-1].name = strdup(name); -#ifdef DEBUG - fprintf(stderr, "resized models to %d bytes for model %s\n", sizeof(model_t) * (*count), models[(*count)-1].name); -#endif - for (i = 0; i < 24; i++) { - models[(*count)-1].node[i] = rotations[i] * 90.0; - } - return models; -} +int models = (sizeof(model) / sizeof(struct model_s)); -/* filename is the name of the file to load - * models is the pointer to where the models will be kept - * returns a new pointer to models - * count is number of models read - */ -model_t * load_modelfile(char * filename, model_t * models, int * count) -{ - int c; - FILE * f; - char buffy[256]; - int rotations[24]; - int name = 1; - int rots = 0; - - f = fopen(filename, "r"); - if (f == NULL) { - int error_msg_len = strlen(filename) + 33 + 1; - char * error_msg = (char *) malloc(sizeof(char) * error_msg_len); - sprintf(error_msg, "Unable to open model data file \"%s\"", filename); - perror(error_msg); - free(error_msg); - return models; - } - - while ((c = getc(f)) != EOF) { - switch (c) { - /* ignore comments */ - case '#': - while (c != '\n') - c = getc(f); - break; - case ':': - buffy[name-1] = '\0'; - name = 0; - break; - case '\n': - if (rots > 0) { -#ifdef DEBUG - /* print out the model we just read in */ - int i; - printf("%s: ", buffy); - for (i = 0; i < rots; i++) { - switch (rotations[i]) { - case LEFT: - printf("L"); - break; - case RIGHT: - printf("R"); - break; - case PIN: - printf("P"); - break; - case ZERO: - printf("Z"); - break; - } - } - printf("\n"); -#endif - models = add_model(models, buffy, rotations, count); - } - name = 1; - rots = 0; - break; - default: - if (name) { - buffy[name-1] = c; - name++; - if (name > 255) - fprintf(stderr, "buffy overflow warning\n"); - } else { - switch (c) { - case '0': - case 'Z': - rotations[rots] = ZERO; - rots++; - break; - case '1': - case 'L': - rotations[rots] = LEFT; - rots++; - break; - case '2': - case 'P': - rotations[rots] = PIN; - rots++; - break; - case '3': - case 'R': - rotations[rots] = RIGHT; - rots++; - break; - default: - break; - } - } - break; - } - } - return models; -} +#define VOFFSET 0.045 -model_t * load_models(char * dirpath, model_t * models, int * count) -{ - char name[1024]; - struct dirent * dp; - DIR * dfd; - - if ((dfd = opendir(dirpath)) == NULL) { - if (strstr(dirpath, "data") == NULL) -/* fprintf(stderr, "load_models: can't read %s/\n", dirpath); */ - return models; - } - while ((dp = readdir(dfd)) != NULL) { - if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) - continue; - if (strlen(dirpath) + strlen(dp->d_name) + 2 > sizeof(name)) - fprintf(stderr, "load_models: name %s/%s too long\n", dirpath, dp->d_name); - else { - sprintf(name, "%s/%s", dirpath, dp->d_name); - if (strcmp(&name[(int) strlen(name) - 7], "glsnake") == 0) { -#ifdef DEBUG - fprintf(stderr, "load_models: opening %s\n", name); -#endif - models = load_modelfile(name, models, count); - } - } - } - closedir(dfd); - return models; -} +#define X_MASK 1 +#define Y_MASK 2 +#define Z_MASK 4 -/* snake metrics */ -#define X_MASK 1 -#define Y_MASK 2 -#define Z_MASK 4 -#define GETSCALAR(vec,mask) ((vec)==(mask) ? 1 : ((vec)==-(mask) ? -1 : 0 )) +/* the connecting string that holds the snake together */ +#define MAGICAL_RED_STRING 0 -int cross_product(int src_dir, int dest_dir) -{ - return X_MASK * (GETSCALAR(src_dir, Y_MASK) * GETSCALAR(dest_dir, Z_MASK) - - GETSCALAR(src_dir, Z_MASK) * GETSCALAR(dest_dir, Y_MASK)) + - Y_MASK * (GETSCALAR(src_dir, Z_MASK) * GETSCALAR(dest_dir, X_MASK) - - GETSCALAR(src_dir, X_MASK) * GETSCALAR(dest_dir, Z_MASK)) + - Z_MASK * (GETSCALAR(src_dir, X_MASK) * GETSCALAR(dest_dir, Y_MASK) - - GETSCALAR(src_dir, Y_MASK) * GETSCALAR(dest_dir, X_MASK)); -} +#define GETSCALAR(vec,mask) ((vec)==(mask) ? 1 : ((vec)==-(mask) ? -1 : 0 )) -void calc_snake_metrics(glsnake_configuration * bp) -{ - int src_dir, dest_dir; - int i, x, y, z; - int prev_src_dir = -Y_MASK; - int prev_dest_dir = Z_MASK; - int grid[25][25][25]; - - /* zero the grid */ - memset(&grid, 0, sizeof(int) * 25*25*25); - - bp->is_legal = 1; - x = y = z = 12; - - /* trace path of snake and keep record for is_legal */ - for (i = 0; i < 23; i++) { - /* establish new state variables */ - src_dir = -prev_dest_dir; - x += GETSCALAR(prev_dest_dir, X_MASK); - y += GETSCALAR(prev_dest_dir, Y_MASK); - z += GETSCALAR(prev_dest_dir, Z_MASK); - - switch ((int) (bp->node[i].dest_angle / 90.0)) { - case ZERO: - dest_dir = -prev_src_dir; - break; - case PIN: - dest_dir = prev_src_dir; - break; - case RIGHT: - case LEFT: - dest_dir = cross_product(prev_src_dir, prev_dest_dir); - if (bp->node[i].dest_angle == (int) (RIGHT * 90.0)) - dest_dir = -dest_dir; - break; - default: - /* prevent spurious "might be used uninitialised" warnings */ - dest_dir = 0; - break; - } +#ifndef MAX +# define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef MIN +# define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif - if (grid[x][y][z] == 0) - grid[x][y][z] = src_dir + dest_dir; - else if (grid[x][y][z] + src_dir + dest_dir == 0) - grid[x][y][z] = 8; - else - bp->is_legal = 0; - - prev_src_dir = src_dir; - prev_dest_dir = dest_dir; - } - - /* determine if the snake is cyclic */ - bp->is_cyclic = (dest_dir == Y_MASK && x == 12 && y == 11 && x == 12); - - /* determine last turn */ - bp->last_turn = -1; - if (bp->is_cyclic) { - switch (src_dir) { - case -Z_MASK: - bp->last_turn = ZERO * 90.0; - break; - case Z_MASK: - bp->last_turn = PIN * 90.0; - break; - case X_MASK: - bp->last_turn = LEFT * 90.0; - break; - case -X_MASK: - bp->last_turn = RIGHT * 90.0; - break; - } - } -} +#define RAND(n) ((random() & 0x7fffffff) % ((long) (n))) +#define RANDSIGN() ((random() & 1) ? 1 : -1) -void set_colours(glsnake_configuration * bp, int immediate) -{ - /* set target colour */ - if (!bp->is_legal) { - bp->colour_t[0] = 0.5; - bp->colour_t[1] = 0.5; - bp->colour_t[2] = 0.5; - } else if (bp->is_cyclic) { - bp->colour_t[0] = 0.4; - bp->colour_t[1] = 0.8; - bp->colour_t[2] = 0.2; - } else { - bp->colour_t[0] = 0.3; - bp->colour_t[1] = 0.1; - bp->colour_t[2] = 0.9; - } - if (immediate) { - bp->colour_i[0] = bp->colour_t[0] - bp->colour[0]; - bp->colour_i[1] = bp->colour_t[1] - bp->colour[1]; - bp->colour_i[2] = bp->colour_t[2] - bp->colour[2]; - } else { - /* instead of 50.0, I should actually work out how many times this gets - * called during a morph */ - bp->colour_i[0] = (bp->colour_t[0] - bp->colour[0]) / 50.0; - bp->colour_i[1] = (bp->colour_t[1] - bp->colour[1]) / 50.0; - bp->colour_i[2] = (bp->colour_t[2] - bp->colour[2]) / 50.0; - } -} +/* the triangular prism what makes up the basic unit */ +float solid_prism_v[][3] = { + /* first corner, bottom left front */ + { VOFFSET, VOFFSET, 1.0 }, + { VOFFSET, 0.00, 1.0 - VOFFSET }, + { 0.00, VOFFSET, 1.0 - VOFFSET }, + /* second corner, rear */ + { VOFFSET, VOFFSET, 0.00 }, + { VOFFSET, 0.00, VOFFSET }, + { 0.00, VOFFSET, VOFFSET }, + /* third, right front */ + { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 1.0 }, + { 1.0 - VOFFSET / M_SQRT1_2, 0.0, 1.0 - VOFFSET }, + { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, 1.0 - VOFFSET }, + /* fourth, right rear */ + { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 0.0 }, + { 1.0 - VOFFSET / M_SQRT1_2, 0.0, VOFFSET }, + { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, VOFFSET }, + /* fifth, upper front */ + { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 1.0 }, + { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, 1.0 - VOFFSET }, + { 0.0, 1.0 - VOFFSET / M_SQRT1_2, 1.0 - VOFFSET}, + /* sixth, upper rear */ + { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 0.0 }, + { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, VOFFSET }, + { 0.0, 1.0 - VOFFSET / M_SQRT1_2, VOFFSET }}; + +float solid_prism_n[][3] = {/* corners */ + { -VOFFSET, -VOFFSET, VOFFSET }, + { VOFFSET, -VOFFSET, VOFFSET }, + { -VOFFSET, VOFFSET, VOFFSET }, + { -VOFFSET, -VOFFSET, -VOFFSET }, + { VOFFSET, -VOFFSET, -VOFFSET }, + { -VOFFSET, VOFFSET, -VOFFSET }, + /* edges */ + { -VOFFSET, 0.0, VOFFSET }, + { 0.0, -VOFFSET, VOFFSET }, + { VOFFSET, VOFFSET, VOFFSET }, + { -VOFFSET, 0.0, -VOFFSET }, + { 0.0, -VOFFSET, -VOFFSET }, + { VOFFSET, VOFFSET, -VOFFSET }, + { -VOFFSET, -VOFFSET, 0.0 }, + { VOFFSET, -VOFFSET, 0.0 }, + { -VOFFSET, VOFFSET, 0.0 }, + /* faces */ + { 0.0, 0.0, 1.0 }, + { 0.0, -1.0, 0.0 }, + { M_SQRT1_2, M_SQRT1_2, 0.0 }, + { -1.0, 0.0, 0.0 }, + { 0.0, 0.0, -1.0 }}; + +float wire_prism_v[][3] = {{ 0.0, 0.0, 1.0 }, + { 1.0, 0.0, 1.0 }, + { 0.0, 1.0, 1.0 }, + { 0.0, 0.0, 0.0 }, + { 1.0, 0.0, 0.0 }, + { 0.0, 1.0, 0.0 }}; + +float wire_prism_n[][3] = {{ 0.0, 0.0, 1.0}, + { 0.0,-1.0, 0.0}, + { M_SQRT1_2, M_SQRT1_2, 0.0}, + {-1.0, 0.0, 0.0}, + { 0.0, 0.0,-1.0}}; + +static struct glsnake_cfg * glc = NULL; +#ifdef HAVE_GLUT +# define bp glc +#endif -void start_morph(int model_index, int immediate, glsnake_configuration * bp) -{ - int i; - - for (i = 0; i < 23; i++) { - bp->node[i].dest_angle = bp->models[model_index].node[i]; - if (immediate) - bp->node[i].cur_angle = bp->models[model_index].node[i]; - } - - calc_snake_metrics(bp); - set_colours(bp, 0); - bp->cur_model = model_index; - bp->morphing = 1; -} +typedef float (*morphFunc)(long); -/* convex hull */ +#ifdef HAVE_GLUT +/* forward definitions for GLUT functions */ +void calc_rotation(); +inline void ui_mousedrag(); +#endif -/* model labels */ -void draw_label(ModeInfo * mi) -{ - glsnake_configuration *bp = &glc[MI_SCREEN(mi)]; - - glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT); - glDisable(GL_LIGHTING); - glDisable(GL_DEPTH_TEST); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height); - glColor3f(1.0, 1.0, 0.0); - { - char * s; - int i, /* w, */ l; - - if (bp->interactive) - s = "interactive"; - else - s = bp->models[bp->cur_model].name; - - l = strlen(s); - /* - w = 0; - for (i = 0; i < l; i++) { - w += (bp->font->per_char - ? bp->font->per_char[((int)s[i]) - bp->font->min_char_or_byte2].rbearing - : bp->font->min_bounds.rbearing); +void gl_init( +#ifndef HAVE_GLUT + ModeInfo * mi +#endif + ) { + float light_pos[][3] = {{0.0, 0.0, 20.0}, {0.0, 20.0, 0.0}}; + float light_dir[][3] = {{0.0, 0.0,-20.0}, {0.0,-20.0, 0.0}}; + + glClearColor(0.0, 0.0, 0.0, 0.0); + glEnable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glEnable(GL_NORMALIZE); + + if (!wireframe) { + glColor3f(1.0, 1.0, 1.0); + glLightfv(GL_LIGHT0, GL_POSITION, light_pos[0]); + glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_dir[0]); + glLightfv(GL_LIGHT1, GL_POSITION, light_pos[1]); + glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir[1]); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + glEnable(GL_COLOR_MATERIAL); } - */ - - glRasterPos2f(10, mi->xgwa.height - 10 - (bp->font->ascent + bp->font->descent)); - /* mi->xgwa.width - w, bp->font->descent + bp->font->ascent); */ - - /* fprintf(stderr, "afaf.width = %d, w = %d\n", mi->xgwa.width, w); */ - - for (i = 0; i < l; i++) - glCallList(bp->font_list + (int)s[i]); - } - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); } -/* load the fonts -- this function borrowed from molecule.c */ -static void load_font(ModeInfo * mi, char * res, XFontStruct ** fontp, GLuint * dlistp) +void gettime(snaketime *t) { - const char * font = get_string_resource(res, "Font"); - XFontStruct * f; - Font id; - int first, last; - - if (!font) - font = "-*-helvetica-medium-r-*-*-*-120-*"; - - f = XLoadQueryFont(mi->dpy, font); - if (!f) - f = XLoadQueryFont(mi->dpy, "fixed"); - - id = f->fid; - first = f->min_char_or_byte2; - last = f->max_char_or_byte2; - - clear_gl_error(); - *dlistp = glGenLists((GLuint) last + 1); - check_gl_error("glGenLists"); - glXUseXFont(id, first, last - first + 1, *dlistp + first); - check_gl_error("glXUseXFont"); - - *fontp = f; +#ifdef HAVE_GETTIMEOFDAY +#ifdef GETTIMEOFDAY_TWO_ARGS + struct timezone tzp; + gettimeofday(t, &tzp); +#else /* !GETTIMEOFDAY_TWO_ARGS */ + gettimeofday(t); +#endif /* !GETTIMEOFDAY_TWO_ARGS */ +#else /* !HAVE_GETTIMEOFDAY */ +#ifdef HAVE_FTIME + ftime(t); +#endif /* HAVE_FTIME */ +#endif /* !HAVE_GETTIMEOFDAY */ } +#ifndef HAVE_GLUT +static void load_font(ModeInfo * mi, char * res, XFontStruct ** fontp, GLuint * dlistp) { + const char * font = get_string_resource(res, "Font"); + XFontStruct * f; + Font id; + int first, last; + if (!font) + font = "-*-helvetica-medium-r-*-*-*-120-*"; -/* window management */ -void glsnake_reshape(ModeInfo *mi, int w, int h) -{ - glViewport (0, 0, (GLint) w, (GLint) h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(25.0, w/(GLfloat)h, 1.0, 100.0 ); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -} + f = XLoadQueryFont(mi->dpy, font); + if (!f) + f = XLoadQueryFont(mi->dpy, "fixed"); -static void gl_init(ModeInfo *mi) -{ - /* glsnake_configuration *bp = &glc[MI_SCREEN(mi)]; */ - int wire = MI_IS_WIREFRAME(mi); - float light_pos[][3] = {{0.0,0.0,20.0},{0.0,20.0,0.0}}; - float light_dir[][3] = {{0.0,0.0,-20.0},{0.0,-20.0,0.0}}; - - glClearColor(0.0, 0.0, 0.0, 0.0); - glEnable(GL_DEPTH_TEST); - glShadeModel(GL_SMOOTH); - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); - glEnable(GL_NORMALIZE); - - if (!wire) { - glColor3f(1.0, 1.0, 1.0); - glLightfv(GL_LIGHT0, GL_POSITION, light_pos[0]); - glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_dir[0]); - glLightfv(GL_LIGHT1, GL_POSITION, light_pos[1]); - glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir[1]); - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_LIGHT1); - glEnable(GL_COLOR_MATERIAL); - } + id = f->fid; + first = f->min_char_or_byte2; + last = f->max_char_or_byte2; + + clear_gl_error(); + *dlistp = glGenLists((GLuint) last + 1); + check_gl_error("glGenLists"); + glXUseXFont(id, first, last - first + 1, *dlistp + first); + check_gl_error("glXUseXFont"); + + *fontp = f; } +#endif -/* lifted from lament.c */ -#define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n)))) -#define RANDSIGN() ((random() & 1) ? 1 : -1) +void start_morph(int model_index, int immediate); -void glsnake_init(ModeInfo *mi) -{ - glsnake_configuration * bp; - int wire = MI_IS_WIREFRAME(mi); +/* wot initialises it */ +void glsnake_init( +#ifndef HAVE_GLUT +ModeInfo * mi +#endif +) { +#ifndef HAVE_GLUT + struct glsnake_cfg * bp; - if (!glc) { - glc = (glsnake_configuration *) calloc(MI_NUM_SCREENS(mi), sizeof(glsnake_configuration)); + /* set up the conf struct and glx contexts */ if (!glc) { - fprintf(stderr, "%s: out of memory\n", progname); - exit(1); + glc = (struct glsnake_cfg *) calloc(MI_NUM_SCREENS(mi), sizeof(struct glsnake_cfg)); + if (!glc) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(1); + } } bp = &glc[MI_SCREEN(mi)]; - } - - bp = &glc[MI_SCREEN(mi)]; - - if ((bp->glx_context = init_GL(mi)) != NULL) { - gl_init(mi); - glsnake_reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi)); - } - - /* initialise config variables */ - memset(&bp->node, 0, sizeof(nodeang_t) * 24); - bp->m_count = sizeof(default_models) / sizeof(model_t); /* overwrite this in a bit */ - bp->selected = 11; - bp->is_cyclic = 0; - bp->is_legal = 1; - bp->last_turn = -1; - bp->roty = 0.0; - bp->rotz = 0.0; - bp->morphing = 0; - bp->paused = 0; - bp->dragging = 0; - bp->interactive = 0; - - { -# ifdef GETTIMEOFDAY_TWO_ARGS - struct timezone tzp; - gettimeofday(&bp->last_iteration, &tzp); -# else - gettimeofday(&bp->last_iteration); -# endif - } - - memcpy(&bp->last_morph, &(bp->last_iteration), - sizeof(bp->last_morph)); - /* srand((unsigned int) bp->last_iteration.time); */ - - /* load the model files */ - /* first copy the defaults to bp->m_count */ - bp->models = (model_t *) malloc(sizeof(model_t) * bp->m_count); - memcpy(bp->models, default_models, bp->m_count * sizeof(model_t)); - /* then add on models from the Debian model file location */ - bp->models = load_models("/usr/share/glsnake", bp->models, &(bp->m_count)); - - bp->m = bp->cur_model = RAND(bp->m_count); - start_morph(bp->cur_model, 1, bp); - - calc_snake_metrics(bp); - set_colours(bp, 1); - - /* set up a font for the labels */ - if (labels) - load_font(mi, "labelfont", &bp->font, &bp->font_list); - - bp->node_list = glGenLists(1); - glNewList(bp->node_list, GL_COMPILE); - if (!wire) { + + if ((bp->glx_context = init_GL(mi)) != NULL) { + gl_init(mi); + glsnake_reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + } +#else + gl_init(); +#endif + + /* initialise conf struct */ + memset(&bp->node, 0, sizeof(float) * NODE_COUNT); + + bp->selected = 11; + bp->is_cyclic = 0; + bp->is_legal = 1; + bp->last_turn = -1; + bp->morphing = 0; + bp->paused = 0; + bp->new_morph = 0; + + gettime(&bp->last_iteration); + memcpy(&bp->last_morph, &bp->last_iteration, sizeof(bp->last_morph)); + + bp->prev_colour = bp->next_colour = COLOUR_ACYCLIC; + bp->next_model = RAND(models); + bp->prev_model = START_MODEL; + start_morph(bp->prev_model, 1); + + /* set up a font for the labels */ +#ifndef HAVE_GLUT + if (titles) + load_font(mi, "labelfont", &bp->font, &bp->font_list); +#endif + + /* build a solid display list */ + glc->node_solid = glGenLists(1); + glNewList(glc->node_solid, GL_COMPILE); /* corners */ glBegin(GL_TRIANGLES); glNormal3fv(solid_prism_n[0]); @@ -757,7 +1497,7 @@ void glsnake_init(ModeInfo *mi) glVertex3fv(solid_prism_v[6]); glVertex3fv(solid_prism_v[7]); glVertex3fv(solid_prism_v[8]); - + glNormal3fv(solid_prism_n[2]); glVertex3fv(solid_prism_v[12]); glVertex3fv(solid_prism_v[13]); @@ -767,18 +1507,17 @@ void glsnake_init(ModeInfo *mi) glVertex3fv(solid_prism_v[3]); glVertex3fv(solid_prism_v[4]); glVertex3fv(solid_prism_v[5]); - + glNormal3fv(solid_prism_n[4]); glVertex3fv(solid_prism_v[9]); glVertex3fv(solid_prism_v[11]); glVertex3fv(solid_prism_v[10]); - + glNormal3fv(solid_prism_n[5]); glVertex3fv(solid_prism_v[16]); glVertex3fv(solid_prism_v[15]); glVertex3fv(solid_prism_v[17]); glEnd(); - /* edges */ glBegin(GL_QUADS); glNormal3fv(solid_prism_n[6]); @@ -786,90 +1525,93 @@ void glsnake_init(ModeInfo *mi) glVertex3fv(solid_prism_v[12]); glVertex3fv(solid_prism_v[14]); glVertex3fv(solid_prism_v[2]); - + glNormal3fv(solid_prism_n[7]); glVertex3fv(solid_prism_v[0]); glVertex3fv(solid_prism_v[1]); glVertex3fv(solid_prism_v[7]); glVertex3fv(solid_prism_v[6]); - + glNormal3fv(solid_prism_n[8]); glVertex3fv(solid_prism_v[6]); glVertex3fv(solid_prism_v[8]); glVertex3fv(solid_prism_v[13]); glVertex3fv(solid_prism_v[12]); - + glNormal3fv(solid_prism_n[9]); glVertex3fv(solid_prism_v[3]); glVertex3fv(solid_prism_v[5]); glVertex3fv(solid_prism_v[17]); glVertex3fv(solid_prism_v[15]); - + glNormal3fv(solid_prism_n[10]); glVertex3fv(solid_prism_v[3]); glVertex3fv(solid_prism_v[9]); glVertex3fv(solid_prism_v[10]); glVertex3fv(solid_prism_v[4]); - + glNormal3fv(solid_prism_n[11]); glVertex3fv(solid_prism_v[15]); glVertex3fv(solid_prism_v[16]); glVertex3fv(solid_prism_v[11]); glVertex3fv(solid_prism_v[9]); - + glNormal3fv(solid_prism_n[12]); glVertex3fv(solid_prism_v[1]); glVertex3fv(solid_prism_v[2]); glVertex3fv(solid_prism_v[5]); glVertex3fv(solid_prism_v[4]); - + glNormal3fv(solid_prism_n[13]); glVertex3fv(solid_prism_v[8]); glVertex3fv(solid_prism_v[7]); glVertex3fv(solid_prism_v[10]); glVertex3fv(solid_prism_v[11]); - + glNormal3fv(solid_prism_n[14]); glVertex3fv(solid_prism_v[13]); glVertex3fv(solid_prism_v[16]); glVertex3fv(solid_prism_v[17]); glVertex3fv(solid_prism_v[14]); glEnd(); - + /* faces */ glBegin(GL_TRIANGLES); glNormal3fv(solid_prism_n[15]); glVertex3fv(solid_prism_v[0]); glVertex3fv(solid_prism_v[6]); glVertex3fv(solid_prism_v[12]); - + glNormal3fv(solid_prism_n[19]); glVertex3fv(solid_prism_v[3]); glVertex3fv(solid_prism_v[15]); glVertex3fv(solid_prism_v[9]); glEnd(); - + glBegin(GL_QUADS); glNormal3fv(solid_prism_n[16]); glVertex3fv(solid_prism_v[1]); glVertex3fv(solid_prism_v[4]); glVertex3fv(solid_prism_v[10]); glVertex3fv(solid_prism_v[7]); - + glNormal3fv(solid_prism_n[17]); glVertex3fv(solid_prism_v[8]); glVertex3fv(solid_prism_v[11]); glVertex3fv(solid_prism_v[16]); glVertex3fv(solid_prism_v[13]); - + glNormal3fv(solid_prism_n[18]); glVertex3fv(solid_prism_v[2]); glVertex3fv(solid_prism_v[14]); glVertex3fv(solid_prism_v[17]); glVertex3fv(solid_prism_v[5]); glEnd(); - } else { + glEndList(); + /* build wire display list */ + glc->node_wire = glGenLists(1); + glNewList(glc->node_wire, GL_COMPILE); glBegin(GL_LINE_STRIP); glVertex3fv(wire_prism_v[0]); glVertex3fv(wire_prism_v[1]); @@ -880,333 +1622,944 @@ void glsnake_init(ModeInfo *mi) glVertex3fv(wire_prism_v[5]); glVertex3fv(wire_prism_v[3]); glEnd(); - glBegin(GL_LINES); glVertex3fv(wire_prism_v[1]); glVertex3fv(wire_prism_v[4]); glVertex3fv(wire_prism_v[2]); glVertex3fv(wire_prism_v[5]); glEnd(); - } - glEndList(); + glEndList(); + +#ifdef HAVE_GLUT + /* initialise the rotation */ + calc_rotation(); +#endif } -/* "jwz? no way man, he's my idle" -- Jaq, 2001. - * I forget the context :( */ -void glsnake_idol(glsnake_configuration * bp) -{ - /* time since last iteration */ - long iter_msec; - /* time since the beginning of last morph */ - long morf_msec; - float iter_angle_max; - int i; - struct timeval current_time; - int still_morphing; - - /* Do nothing to the model if we are paused */ - if (bp->paused) { - /* Avoid busy waiting when nothing is changing */ - usleep(1); - return; - } - - { -# ifdef GETTIMEOFDAY_TWO_ARGS - struct timezone tzp; - gettimeofday(¤t_time, &tzp); -# else - gettimeofday(¤t_time); -# endif - } - - /* Well, ftime gives time with millisecond resolution. - * if current time is exactly equal to last iteration, - * then don't do this block - * (or worse, perhaps... who knows what the OS will do) - * So if no discernable amount of time has passed: - * a) There's no point updating the screen, because - * it would be the same - * b) The code will divide by zero - */ - iter_msec = ((long) current_time.tv_usec - bp->last_iteration.tv_usec)/1000L + - ((long) current_time.tv_sec - bp->last_iteration.tv_sec) * 1000L; - if (iter_msec) { - /* save the current time */ - memcpy(&bp->last_iteration, ¤t_time, - sizeof(bp->last_iteration)); +void draw_title( +#ifndef HAVE_GLUT + ModeInfo * mi +#endif + ) { +#ifndef HAVE_GLUT + struct glsnake_cfg * bp = &glc[MI_SCREEN(mi)]; +#endif + + /* draw some text */ + glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); +#ifdef HAVE_GLUT + gluOrtho2D(0, glc->width, 0, glc->height); +#else + gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height); +#endif + glColor3f(1.0, 1.0, 1.0); + { + char interactstr[] = "interactive"; + char * s; + int i = 0; +#ifdef HAVE_GLUT + int w; +#endif + + if (interactive) + s = interactstr; + else + s = model[glc->next_model].name; +#ifdef HAVE_GLUT + w = glutBitmapLength(GLUT_BITMAP_HELVETICA_12, (unsigned char *) s); + glRasterPos2f(glc->width - w - 3, 4); + while (s[i] != '\0') + glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, s[i++]); +#else + glRasterPos2f(10, mi->xgwa.height - 10 - (bp->font->ascent + bp->font->descent)); + while (s[i] != '\0') + glCallList(bp->font_list + (int)s[i++]); +#endif + } + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); +} + +/* apply the matrix to the origin and stick it in vec */ +void matmult_origin(float rotmat[16], float vec[4]) { +#if 1 + vec[0] = 0.5 * rotmat[0] + 0.5 * rotmat[4] + 0.5 * rotmat [8] + 1 * rotmat[12]; + vec[1] = 0.5 * rotmat[1] + 0.5 * rotmat[5] + 0.5 * rotmat [9] + 1 * rotmat[13]; + vec[2] = 0.5 * rotmat[2] + 0.5 * rotmat[6] + 0.5 * rotmat[10] + 1 * rotmat[14]; + vec[3] = 0.5 * rotmat[3] + 0.5 * rotmat[7] + 0.5 * rotmat[11] + 1 * rotmat[15]; +#else + vec[0] = 0 * rotmat [0] + 0 * rotmat [1] + 0 * rotmat [2] + 1 * rotmat [3]; + vec[1] = 0 * rotmat [4] + 0 * rotmat [5] + 0 * rotmat [6] + 1 * rotmat [7]; + vec[2] = 0 * rotmat [8] + 0 * rotmat [9] + 0 * rotmat[10] + 1 * rotmat[11]; + vec[3] = 0 * rotmat[12] + 0 * rotmat[13] + 0 * rotmat[14] + 1 * rotmat[15]; +#endif + vec[0] /= vec[3]; + vec[1] /= vec[3]; + vec[2] /= vec[3]; + vec[3] = 1.0; +} + +/* wot gets called when the winder is resized */ +void glsnake_reshape( +#ifndef HAVE_GLUT + ModeInfo * mi, +#endif + int w, int h) { + glViewport(0, 0, (GLint) w, (GLint) h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(zoom, w/(GLfloat)h, 0.05, 100.0); + gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); + glMatrixMode(GL_MODELVIEW); + /*gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);*/ + glLoadIdentity(); +#ifdef HAVE_GLUT + glc->width = w; + glc->height = h; +#endif +} + +/* Returns the new dst_dir for the given src_dir and dst_dir */ +int cross_product(int src_dir, int dst_dir) { + return X_MASK*(GETSCALAR(src_dir,Y_MASK) * GETSCALAR(dst_dir,Z_MASK) - + GETSCALAR(src_dir,Z_MASK) * GETSCALAR(dst_dir,Y_MASK))+ + Y_MASK*(GETSCALAR(src_dir,Z_MASK) * GETSCALAR(dst_dir,X_MASK) - + GETSCALAR(src_dir,X_MASK) * GETSCALAR(dst_dir,Z_MASK))+ + Z_MASK*(GETSCALAR(src_dir,X_MASK) * GETSCALAR(dst_dir,Y_MASK) - + GETSCALAR(src_dir,Y_MASK) * GETSCALAR(dst_dir,X_MASK)); +} + +/* calculate orthogonal snake metrics + * is_legal = true if model does not pass through itself + * is_cyclic = true if last node connects back to first node + * last_turn = for cyclic snakes, specifes what the 24th turn would be + */ +void calc_snake_metrics(void) { + int srcDir, dstDir; + int i, x, y, z; + int prevSrcDir = -Y_MASK; + int prevDstDir = Z_MASK; + int grid[25][25][25]; - /* work out if we have to switch models */ - morf_msec = (bp->last_iteration.tv_usec - bp->last_morph.tv_usec)/1000L + - ((long) (bp->last_iteration.tv_sec - bp->last_morph.tv_sec) * 1000L); - - if ((morf_msec > statictime) && !bp->interactive) { - memcpy(&bp->last_morph, &(bp->last_iteration), - sizeof(bp->last_morph)); - start_morph(RAND(bp->m_count), 0, bp); + /* zero the grid */ + memset(&grid, 0, sizeof(int) * 25*25*25); + + glc->is_legal = 1; + x = y = z = 12; + + /* trace path of snake - and keep record for is_legal */ + for (i = 0; i < NODE_COUNT - 1; i++) { + /*int ang_card;*/ /* cardinal direction of node angle */ + /* establish new state vars */ + srcDir = -prevDstDir; + x += GETSCALAR(prevDstDir, X_MASK); + y += GETSCALAR(prevDstDir, Y_MASK); + z += GETSCALAR(prevDstDir, Z_MASK); + + switch ((int) model[glc->next_model].node[i]) { + case (int) (ZERO): + dstDir = -prevSrcDir; + break; + case (int) (PIN): + dstDir = prevSrcDir; + break; + case (int) (RIGHT): + case (int) (LEFT): + dstDir = cross_product(prevSrcDir, prevDstDir); + if (model[glc->next_model].node[i] == (int) (RIGHT)) + dstDir = -dstDir; + break; + default: + /* Prevent spurious "might be used + * uninitialised" warnings when compiling + * with -O2 */ + dstDir = 0; + break; + } + + if (grid[x][y][z] == 0) + grid[x][y][z] = srcDir + dstDir; + else if (grid[x][y][z] + srcDir + dstDir == 0) + grid[x][y][z] = 8; + else + glc->is_legal = 0; + + prevSrcDir = srcDir; + prevDstDir = dstDir; + } + + /* determine if the snake is cyclic */ + glc->is_cyclic = (dstDir == Y_MASK && x == 12 && y == 11 && z == 12); + + /* determine last_turn */ + glc->last_turn = -1; + if (glc->is_cyclic) + switch (srcDir) { + case -Z_MASK: glc->last_turn = ZERO; break; + case Z_MASK: glc->last_turn = PIN; break; + case X_MASK: glc->last_turn = LEFT; break; + case -X_MASK: glc->last_turn = RIGHT; break; + } +} + +/* work out how far through the current morph we are */ +float morph_percent(void) { + float retval; + int i; + + /* extend this function later with a case statement for each of the + * morph schemes */ + + /* when morphing all nodes at once, the longest morph will be the node + * that needs to rotate 180 degrees. For each node, work out how far it + * has to go, and store the maximum rotation and current largest angular + * difference, returning the angular difference over the maximum. */ + { + float rot_max = 0.0, ang_diff_max = 0.0; + + for (i = 0; i < NODE_COUNT - 1; i++) { + float rot, ang_diff; + + /* work out the maximum rotation this node has to go through + * from the previous to the next model, taking into account that + * the snake always morphs through the smaller angle */ + rot = fabs(model[glc->prev_model].node[i] - + model[glc->next_model].node[i]); + if (rot > 180.0) rot = 180.0 - rot; + /* work out the difference between the current position and the + * target */ + ang_diff = fabs(glc->node[i] - + model[glc->next_model].node[i]); + if (ang_diff > 180.0) ang_diff = 180 - ang_diff; + /* if it's the biggest so far, record it */ + if (rot > rot_max) rot_max = rot; + if (ang_diff > ang_diff_max) ang_diff_max = ang_diff; + } + + /* ang_diff / rot approaches 0, we want the complement */ + retval = 1.0 - (ang_diff_max / rot_max); + /* protect against naan */ + if (isnan(retval) || isinf(retval)) retval = 1.0; } + /*printf("morph_pct = %f\n", retval);*/ + return retval; +} + +void morph_colour(void) { + float percent, compct; /* complement of percentage */ + + percent = morph_percent(); + compct = 1.0 - percent; + + glc->colour[0][0] = colour[glc->prev_colour][0][0] * compct + colour[glc->next_colour][0][0] * percent; + glc->colour[0][1] = colour[glc->prev_colour][0][1] * compct + colour[glc->next_colour][0][1] * percent; + glc->colour[0][2] = colour[glc->prev_colour][0][2] * compct + colour[glc->next_colour][0][2] * percent; + + glc->colour[1][0] = colour[glc->prev_colour][1][0] * compct + colour[glc->next_colour][1][0] * percent; + glc->colour[1][1] = colour[glc->prev_colour][1][1] * compct + colour[glc->next_colour][1][1] * percent; + glc->colour[1][2] = colour[glc->prev_colour][1][2] * compct + colour[glc->next_colour][1][2] * percent; +} + +/* Start morph process to this model */ +void start_morph(int model_index, int immediate) { + /* if immediate, don't bother morphing, go straight to the next model */ + if (immediate) { + int i; - if (bp->interactive && !bp->morphing) { - usleep(1); - return; + for (i = 0; i < NODE_COUNT; i++) + glc->node[i] = model[model_index].node[i]; } - if (!bp->dragging && !bp->interactive) { - bp->roty += 360/((1000/yspin)/iter_msec); - bp->rotz += 360/((1000/zspin)/iter_msec); + glc->prev_model = glc->next_model; + glc->next_model = model_index; + glc->prev_colour = glc->next_colour; + + calc_snake_metrics(); + if (!glc->is_legal) + glc->next_colour = COLOUR_INVALID; + else if (altcolour) + glc->next_colour = COLOUR_AUTHENTIC; + else if (glc->is_cyclic) + glc->next_colour = COLOUR_CYCLIC; + else + glc->next_colour = COLOUR_ACYCLIC; + + if (immediate) { + glc->colour[0][0] = colour[glc->next_colour][0][0]; + glc->colour[0][1] = colour[glc->next_colour][0][1]; + glc->colour[0][2] = colour[glc->next_colour][0][2]; + glc->colour[1][0] = colour[glc->next_colour][1][0]; + glc->colour[1][1] = colour[glc->next_colour][1][1]; + glc->colour[1][2] = colour[glc->next_colour][1][2]; } + glc->morphing = 1; - /* work out the maximum angle for this iteration */ - iter_angle_max = 90.0 * (velocity/1000.0) * iter_msec; + morph_colour(); +} +/* Returns morph progress */ +float morph(long iter_msec) { + /* work out the maximum angle for this iteration */ + int still_morphing; + float iter_angle_max, largest_diff, largest_progress; + int i; + + if (glc->new_morph) + glc->new_morph = 0; + + iter_angle_max = 90.0 * (angvel/1000.0) * iter_msec; + still_morphing = 0; - for (i = 0; i < 24; i++) { - float cur_angle = bp->node[i].cur_angle; - float dest_angle = bp->node[i].dest_angle; - if (cur_angle != dest_angle) { - still_morphing = 1; - if (fabs(cur_angle - dest_angle) <= iter_angle_max) - bp->node[i].cur_angle = dest_angle; - else if (fmod(cur_angle - dest_angle + 360, 360) > 180) - bp->node[i].cur_angle = fmod(cur_angle + iter_angle_max, 360); - else - bp->node[i].cur_angle = fmod(cur_angle + 360 - iter_angle_max, 360); - } + largest_diff = largest_progress = 0.0; + for (i = 0; i < NODE_COUNT; i++) { + float curAngle = glc->node[i]; + float destAngle = model[glc->next_model].node[i]; + if (curAngle != destAngle) { + still_morphing = 1; + if (fabs(curAngle-destAngle) <= iter_angle_max) + glc->node[i] = destAngle; + else if (fmod(curAngle-destAngle+360,360) > 180) + glc->node[i] = fmod(curAngle + iter_angle_max, 360); + else + glc->node[i] = fmod(curAngle+360 - iter_angle_max, 360); + largest_diff = MAX(largest_diff, fabs(destAngle-glc->node[i])); + largest_progress = MAX(largest_diff, fabs(glc->node[i] - model[glc->prev_model].node[i])); + } } + + return MIN(largest_diff / largest_progress, 1.0); +} - if (!still_morphing) - bp->morphing = 0; +#ifdef HAVE_GLUT +void glsnake_idle(); - /* colour cycling */ - if (fabs(bp->colour[0] - bp->colour_t[0]) <= fabs(bp->colour_i[0])) - bp->colour[0] = bp->colour_t[0]; - else - bp->colour[0] += bp->colour_i[0]; - if (fabs(bp->colour[1] - bp->colour_t[1]) <= fabs(bp->colour_i[1])) - bp->colour[1] = bp->colour_t[1]; - else - bp->colour[1] += bp->colour_i[1]; - if (fabs(bp->colour[2] - bp->colour_t[2]) <= fabs(bp->colour_i[2])) - bp->colour[2] = bp->colour_t[2]; - else - bp->colour[2] += bp->colour_i[2]; - } else { - /* We are going too fast, so we may as well let the - * cpu relax a little by sleeping for a millisecond. */ - usleep(1); - } +void restore_idle(int value) +{ + glutIdleFunc(glsnake_idle); } +#endif -static void -snake_bounding_box (ModeInfo *mi, - GLfloat *x1, GLfloat *y1, GLfloat *z1, - GLfloat *x2, GLfloat *y2, GLfloat *z2) +void quick_sleep(void) { - glsnake_configuration *bp = &glc[MI_SCREEN(mi)]; - int i; - GLdouble identity[16] = { 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 }; - GLint vp[4] = { 0, 0, 1, 1 }; - *x1 = *x2 = 0; - *y1 = *y2 = 0; - *z1 = *z2 = 0; - - glPushMatrix(); - glLoadIdentity(); - for (i = 0; i < 24; i++) - { - GLdouble model[16]; - GLdouble x, y, z; - GLfloat ang = bp->node[i].cur_angle; - - glGetDoublev (GL_MODELVIEW_MATRIX, model); - gluProject (0, 0, 0, model, identity, vp, &x, &y, &z); -fprintf (stderr, "%2d: %5.2f %5.2f %5.2f\n", i, (float)x, (float)y, (float)z); - - if (x < *x1) *x1 = x; - else if (x > *x2) *x2 = x; - if (y < *y1) *y1 = y; - else if (y > *y2) *y2 = y; - if (z < *z1) *z1 = z; - else if (z > *z2) *z2 = z; - - glTranslatef(0.5, 0.5, 0.5); - glRotatef(90, 0.0, 0.0, -1.0); - glTranslatef(1.0 + explode, 0.0, 0.0); - glRotatef(180 + ang, 1.0, 0.0, 0.0); - glTranslatef(-0.5, -0.5, -0.5); - } -fprintf(stderr, "\n"); - glPopMatrix(); - -#if 0 - *x1 -= 1; - *y1 -= 1; - *z1 -= 1; - *x2 += 1; - *y2 += 1; - *z2 += 1; +#ifdef HAVE_GLUT + /* By using glutTimerFunc we can keep responding to + * mouse and keyboard events, unlike using something like + * usleep. */ + glutIdleFunc(NULL); + glutTimerFunc(1, restore_idle, 0); +#else + usleep(1); #endif } +void glsnake_idle( +#ifndef HAVE_GLUT + struct glsnake_cfg * bp +#endif + ) { + /* time since last iteration */ + long iter_msec; + /* time since the beginning of last morph */ + long morf_msec; + float iter_angle_max; + snaketime current_time; + /* morphFunc transition; */ + int still_morphing; + int i; + + /* Do nothing to the model if we are paused */ + if (glc->paused) { + /* Avoid busy waiting when nothing is changing */ + quick_sleep(); +#ifdef HAVE_GLUT + glutSwapBuffers(); + glutPostRedisplay(); +#endif + return; + } -static void -draw_bounding_box (ModeInfo *mi) -{ - static GLfloat c1[4] = { 0.4, 0.4, 0.4, 1.0 }; - static GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 }; - int wire = MI_IS_WIREFRAME(mi); - GLfloat x1, y1, z1, x2, y2, z2; - snake_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2); - - glColor3f (c1[0], c1[1], c1[2]); -/* glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c1);*/ - glFrontFace(GL_CCW); - - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(0, 1, 0); - glVertex3f(x1, y1, z1); glVertex3f(x1, y1, z2); - glVertex3f(x2, y1, z2); glVertex3f(x2, y1, z1); - glEnd(); - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(0, -1, 0); - glVertex3f(x2, y2, z1); glVertex3f(x2, y2, z2); - glVertex3f(x1, y2, z2); glVertex3f(x1, y2, z1); - glEnd(); - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(0, 0, 1); - glVertex3f(x1, y1, z1); glVertex3f(x2, y1, z1); - glVertex3f(x2, y2, z1); glVertex3f(x1, y2, z1); - glEnd(); - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(0, 0, -1); - glVertex3f(x1, y2, z2); glVertex3f(x2, y2, z2); - glVertex3f(x2, y1, z2); glVertex3f(x1, y1, z2); - glEnd(); - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(1, 0, 0); - glVertex3f(x1, y2, z1); glVertex3f(x1, y2, z2); - glVertex3f(x1, y1, z2); glVertex3f(x1, y1, z1); - glEnd(); - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(-1, 0, 0); - glVertex3f(x2, y1, z1); glVertex3f(x2, y1, z2); - glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1); - glEnd(); - - glPushAttrib (GL_LIGHTING); - glDisable (GL_LIGHTING); - - glColor3f (c2[0], c2[1], c2[2]); - glBegin(GL_LINES); - if (x1 > 0) x1 = 0; if (x2 < 0) x2 = 0; - if (y1 > 0) y1 = 0; if (y2 < 0) y2 = 0; - if (z1 > 0) z1 = 0; if (z2 < 0) z2 = 0; - glVertex3f(x1, 0, 0); glVertex3f(x2, 0, 0); - glVertex3f(0 , y1, 0); glVertex3f(0, y2, 0); - glVertex3f(0, 0, z1); glVertex3f(0, 0, z2); - glEnd(); - - glPopAttrib(); + /* Well, ftime gives time with millisecond resolution. + * (or worse, perhaps... who knows what the OS will do) + * So if no discernable amount of time has passed: + * a) There's no point updating the screen, because + * it would be the same + * b) The code will divide by zero + */ + gettime(¤t_time); + + iter_msec = (long) GETMSECS(current_time) - GETMSECS(glc->last_iteration) + + ((long) GETSECS(current_time) - GETSECS(glc->last_iteration)) * 1000L; + + if (iter_msec) { + /* save the current time */ + memcpy(&glc->last_iteration, ¤t_time, sizeof(snaketime)); + + /* work out if we have to switch models */ + morf_msec = GETMSECS(glc->last_iteration) - GETMSECS(glc->last_morph) + + ((long) (GETSECS(glc->last_iteration)-GETSECS(glc->last_morph)) * 1000L); + + if ((morf_msec > statictime) && !interactive && !glc->morphing) { + /*printf("starting morph\n");*/ + memcpy(&glc->last_morph, &(glc->last_iteration), sizeof(glc->last_morph)); + start_morph(RAND(models), 0); + } + + if (interactive && !glc->morphing) { + quick_sleep(); + return; + } + + /* if (!glc->dragging && !glc->interactive) { */ + if (!interactive) { + + yspin += 360/((1000/yangvel)/iter_msec); + zspin += 360/((1000/zangvel)/iter_msec); + /* + yspin += 360 * (yangvel/1000.0) * iter_msec; + zspin += 360 * (zangvel/1000.0) * iter_msec; + */ + + /*printf("yspin: %f, zspin: %f\n", yspin, zspin);*/ + + } + + /* work out the maximum angle we could turn this node in this + * timeslice, iter_msec milliseconds long */ + iter_angle_max = 90.0 * (angvel/1000.0) * iter_msec; + + still_morphing = 0; + for (i = 0; i < NODE_COUNT; i++) { + float cur_angle = glc->node[i]; + float dest_angle = model[glc->next_model].node[i]; + if (cur_angle != dest_angle) { + still_morphing = 1; + if (fabs(cur_angle - dest_angle) <= iter_angle_max) + glc->node[i] = dest_angle; + else if (fmod(cur_angle - dest_angle + 360, 360) > 180) + glc->node[i] = fmod(cur_angle + iter_angle_max, 360); + else + glc->node[i] = fmod(cur_angle + 360 - iter_angle_max, 360); + } + } + + if (!still_morphing) + glc->morphing = 0; + + /* colour cycling */ + morph_colour(); + +#ifdef HAVE_GLUT + glutSwapBuffers(); + glutPostRedisplay(); +#endif + } else { + /* We are going too fast, so we may as well let the + * cpu relax a little by sleeping for a millisecond. */ + quick_sleep(); + } } +/* wot draws it */ +void glsnake_display( +#ifndef HAVE_GLUT + ModeInfo * mi +#endif + ) { +#ifndef HAVE_GLUT + struct glsnake_cfg * bp = &glc[MI_SCREEN(mi)]; + Display * dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); +#endif -void glsnake_draw(ModeInfo *mi) -{ - glsnake_configuration *bp = &glc[MI_SCREEN(mi)]; - Display *dpy = MI_DISPLAY(mi); - Window window = MI_WINDOW(mi); - - int i; - float ang; - - if (!bp->glx_context) - return; - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); - - /* rotate and translate into snake space */ - glRotatef(45.0, -5.0, 0.0, 1.0); - glTranslatef(-0.5, 0.0, 0.5); - - /* rotate the 0th junction */ - glTranslatef(0.5, 0.0, 0.5); - /* glMultMatrix(rotation); -- quaternion rotation */ - glRotatef(bp->roty, 0.0, 1.0, 0.0); - glRotatef(bp->rotz, 0.0, 0.0, 1.0); - glTranslated(-0.5, 0.0, -0.5); - - /* translate middle node to centre */ - for (i = 11; i >= 0; i--) { - ang = bp->node[i].cur_angle; - glTranslatef(0.5, 0.5, 0.5); - glRotatef(180+ang, -1.0, 0.0, 0.0); - glTranslatef(-1.0 - explode, 0.0, 0.0); - glRotatef(90, 0.0, 0.0, 1.0); - glTranslatef(-0.5, -0.5, -0.5); - } - - /* now draw each node along the snake */ - glPushMatrix(); - for (i = 0; i < 24; i++) { - - /* choose a colour for this node */ - if (bp->interactive && (i == bp->selected || i == bp->selected+1)) - glColor3f(1.0, 1.0, 0.0); - else { - if (i % 2) { - if (scarycolour) - glColor3f(0.6, 0.0, 0.9); - else - glColor3fv(bp->colour); - } else { - if (scarycolour) - glColor3f(0.2, 0.9, 1.0); - else - glColor3f(1.0, 1.0, 1.0); - } + int i; + float ang; + float positions[NODE_COUNT][4]; /* origin points for each node */ + float com[4]; /* it's the CENTRE of MASS */ + +#ifndef HAVE_GLUT + if (!bp->glx_context) + return; +#endif + + /* clear the buffer */ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* go into the modelview stack */ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + /* get the centre of each node, by moving through the snake and + * performing the rotations, then grabbing the matrix at each point + * and applying it to the origin */ + glPushMatrix(); + +#ifdef HAVE_GLUT + /* apply the mouse drag rotation */ + ui_mousedrag(); +#endif + + /* apply the continuous rotation */ + glRotatef(yspin, 0.0, 1.0, 0.0); + glRotatef(zspin, 0.0, 0.0, 1.0); + + com[0] = 0.0; + com[1] = 0.0; + com[2] = 0.0; + com[3] = 0.0; + for (i = 0; i < NODE_COUNT; i++) { + float rotmat[16]; + + ang = glc->node[i]; + + /*printf("ang = %f\n", ang);*/ + + glTranslatef(0.5, 0.5, 0.5); /* move to center */ + glRotatef(90, 0.0, 0.0, -1.0); /* reorient */ + glTranslatef(1.0 + explode, 0.0, 0.0); /* move to new pos. */ + glRotatef(180 + ang, 1.0, 0.0, 0.0); /* pivot to new angle */ + glTranslatef(-0.5, -0.5, -0.5); /* return from center */ + + glGetFloatv(GL_MODELVIEW_MATRIX, rotmat); + + matmult_origin(rotmat, positions[i]); + + /*printf("positions %f %f %f %f\n", positions[i][0], positions[i][1], positions[i][2], positions[i][3]);*/ + + com[0] += positions[i][0]; + com[1] += positions[i][1]; + com[2] += positions[i][2]; + com[3] += positions[i][3]; } + glPopMatrix(); + com[0] /= NODE_COUNT; + com[1] /= NODE_COUNT; + com[2] /= NODE_COUNT; + com[3] /= NODE_COUNT; + + com[0] /= com[3]; + com[1] /= com[3]; + com[2] /= com[3]; -#if 0 /* #### */ - if (i == 0) glColor3f(0.0, 1.0, 1.0); + /*printf("com: %f, %f, %f, %f\n", com[0], com[1], com[2], com[3]);*/ + +#if MAGICAL_RED_STRING + glPushMatrix(); + glTranslatef(-com[0], -com[1], -com[2]); + + glDisable(GL_LIGHTING); + glColor3f(1.0, 0.0, 0.0); + glBegin(GL_LINE_STRIP); + for (i = 0; i < NODE_COUNT - 1; i++) { + glVertex3fv(positions[i]); + } + glEnd(); + glEnable(GL_LIGHTING); + /*glTranslatef(com[0], com[1], com[2]);*/ + glPopMatrix(); #endif - /* draw the node */ - glCallList(bp->node_list); + glPushMatrix(); + glTranslatef(-com[0], -com[1], -com[2]); - /* now work out where to draw the next one */ +#ifdef HAVE_GLUT + /* apply the mouse drag rotation */ + ui_mousedrag(); +#endif + + /* apply the continuous rotation */ + glRotatef(yspin, 0.0, 1.0, 0.0); + glRotatef(zspin, 0.0, 0.0, 1.0); + + /* now draw each node along the snake -- this is quite ugly :p */ + for (i = 0; i < NODE_COUNT; i++) { + /* choose a colour for this node */ + if ((i == glc->selected || i == glc->selected+1) && interactive) + /* yellow */ + glColor3f(1.0, 1.0, 0.0); + else + glColor3fv(glc->colour[(i+1)%2]); + + /* draw the node */ + if (wireframe) + glCallList(glc->node_wire); + else + glCallList(glc->node_solid); + + /* now work out where to draw the next one */ + + /* Interpolate between models */ + ang = glc->node[i]; + + glTranslatef(0.5, 0.5, 0.5); /* move to center */ + glRotatef(90, 0.0, 0.0, -1.0); /* reorient */ + glTranslatef(1.0 + explode, 0.0, 0.0); /* move to new pos. */ + glRotatef(180 + ang, 1.0, 0.0, 0.0); /* pivot to new angle */ + glTranslatef(-0.5, -0.5, -0.5); /* return from center */ + } - /* interpolate between models */ - ang = bp->node[i].cur_angle; + glPopMatrix(); + + if (titles) +#ifdef HAVE_GLUT + draw_title(); +#else + draw_title(mi); +#endif - glTranslatef(0.5, 0.5, 0.5); - glRotatef(90, 0.0, 0.0, -1.0); - glTranslatef(1.0 + explode, 0.0, 0.0); - glRotatef(180 + ang, 1.0, 0.0, 0.0); - glTranslatef(-0.5, -0.5, -0.5); - } +#ifndef HAVE_GLUT + glsnake_idle(bp); +#endif + + glFlush(); +#ifdef HAVE_GLUT + glutSwapBuffers(); +#else + glXSwapBuffers(dpy, window); +#endif +} - glPopMatrix(); +#ifdef HAVE_GLUT +/* anything that needs to be cleaned up goes here */ +void unmain() { + glutDestroyWindow(glc->window); + free(glc); +} - if (do_bbox) - draw_bounding_box (mi); +void ui_init(int *, char **); - if (labels) - draw_label(mi); - - if (mi->fps_p) - do_fps (mi); +int main(int argc, char ** argv) { + glc = malloc(sizeof(struct glsnake_cfg)); + memset(glc, 0, sizeof(struct glsnake_cfg)); - glsnake_idol(bp); - - glFlush(); - glXSwapBuffers(dpy, window); + glc->width = 320; + glc->height = 240; + + ui_init(&argc, argv); + + gettime(&glc->last_iteration); + memcpy(&glc->last_morph, &glc->last_iteration, sizeof(snaketime)); + srand((unsigned int)GETSECS(glc->last_iteration)); + + glc->prev_colour = glc->next_colour = COLOUR_ACYCLIC; + glc->next_model = RAND(models); + glc->prev_model = 0; + start_morph(glc->prev_model, 1); + + glsnake_init(); + + atexit(unmain); + glutSwapBuffers(); + glutMainLoop(); + + return 0; +} +#endif + +/* + * GLUT FUNCTIONS + */ + +#ifdef HAVE_GLUT + +/* trackball quaternions */ +float cumquat[4] = {0.0,0.0,0.0,0.0}, oldquat[4] = {0.0,0.0,0.0,0.1}; + +/* rotation matrix */ +float rotation[16]; + +/* mouse drag vectors: start and end */ +float m_s[3], m_e[3]; + +/* dragging boolean */ +int dragging = 0; + +/* this function calculates the rotation matrix based on the quaternions + * generated from the mouse drag vectors */ +void calc_rotation() { + double Nq, s; + double xs, ys, zs, wx, wy, wz, xx, xy, xz, yy, yz, zz; + + /* this bit ripped from Shoemake's quaternion notes from SIGGRAPH */ + Nq = cumquat[0] * cumquat[0] + cumquat[1] * cumquat[1] + + cumquat[2] * cumquat[2] + cumquat[3] * cumquat[3]; + s = (Nq > 0.0) ? (2.0 / Nq) : 0.0; + xs = cumquat[0] * s; ys = cumquat[1] * s; zs = cumquat[2] * s; + wx = cumquat[3] * xs; wy = cumquat[3] * ys; wz = cumquat[3] * zs; + xx = cumquat[0] * xs; xy = cumquat[0] * ys; xz = cumquat[0] * zs; + yy = cumquat[1] * ys; yz = cumquat[1] * zs; zz = cumquat[2] * zs; + + rotation[0] = 1.0 - (yy + zz); + rotation[1] = xy + wz; + rotation[2] = xz - wy; + rotation[4] = xy - wz; + rotation[5] = 1.0 - (xx + zz); + rotation[6] = yz + wx; + rotation[8] = xz + wy; + rotation[9] = yz - wx; + rotation[10] = 1.0 - (xx + yy); + rotation[3] = rotation[7] = rotation[11] = 0.0; + rotation[12] = rotation[13] = rotation[14] = 0.0; + rotation[15] = 1.0; +} + +inline void ui_mousedrag() { + glMultMatrixf(rotation); } -#endif /* USE_GL */ +void ui_keyboard(unsigned char c, int x, int y) { + int i; + + switch (c) { + case 27: /* ESC */ + case 'q': + exit(0); + break; + case 'e': + explode += DEF_EXPLODE; + glutPostRedisplay(); + break; + case 'E': + explode -= DEF_EXPLODE; + if (explode < 0.0) explode = 0.0; + glutPostRedisplay(); + break; + case '.': + /* next model */ + glc->next_model++; + glc->next_model %= models; + start_morph(glc->next_model, 0); + + /* Reset last_morph time */ + gettime(&glc->last_morph); + break; + case ',': + /* previous model */ + glc->next_model = (glc->next_model + models - 1) % models; + start_morph(glc->next_model, 0); + + /* Reset glc->last_morph time */ + gettime(&glc->last_morph); + break; + case '+': + angvel += DEF_ACCEL; + break; + case '-': + if (angvel > DEF_ACCEL) + angvel -= DEF_ACCEL; + break; + case 'i': + if (interactive) { + /* Reset last_iteration and last_morph time */ + gettime(&glc->last_iteration); + gettime(&glc->last_morph); + } + interactive = 1 - interactive; + glutPostRedisplay(); + break; + case 'w': + wireframe = 1 - wireframe; + if (wireframe) + glDisable(GL_LIGHTING); + else + glEnable(GL_LIGHTING); + glutPostRedisplay(); + break; + case 'p': + if (glc->paused) { + /* unpausing, reset last_iteration and last_morph time */ + gettime(&glc->last_iteration); + gettime(&glc->last_morph); + } + glc->paused = 1 - glc->paused; + break; + case 'd': + /* dump the current model so we can add it! */ + printf("# %s\nnoname:\t", model[glc->next_model].name); + for (i = 0; i < NODE_COUNT; i++) { + if (glc->node[i] == ZERO) + printf("Z"); + else if (glc->node[i] == LEFT) + printf("L"); + else if (glc->node[i] == PIN) + printf("P"); + else if (glc->node[i] == RIGHT) + printf("R"); + /* + else + printf("%f", node[i].curAngle); + */ + if (i < NODE_COUNT - 1) + printf(" "); + } + printf("\n"); + break; + case 'f': + glc->fullscreen = 1 - glc->fullscreen; + if (glc->fullscreen) { + glc->old_width = glc->width; + glc->old_height = glc->height; + glutFullScreen(); + } else { + glutReshapeWindow(glc->old_width, glc->old_height); + glutPositionWindow(50,50); + } + break; + case 't': + titles = 1 - titles; + if (interactive || glc->paused) + glutPostRedisplay(); + break; + case 'a': + altcolour = 1 - altcolour; + break; + case 'z': + zoom += 1.0; + glsnake_reshape(glc->width, glc->height); + break; + case 'Z': + zoom -= 1.0; + glsnake_reshape(glc->width, glc->height); + break; + default: + break; + } +} + +void ui_special(int key, int x, int y) { + int i; + float *destAngle = &(model[glc->next_model].node[glc->selected]); + int unknown_key = 0; + + if (interactive) { + switch (key) { + case GLUT_KEY_UP: + glc->selected = (glc->selected + (NODE_COUNT - 2)) % (NODE_COUNT - 1); + break; + case GLUT_KEY_DOWN: + glc->selected = (glc->selected + 1) % (NODE_COUNT - 1); + break; + case GLUT_KEY_LEFT: + *destAngle = fmod(*destAngle+(LEFT), 360); + glc->morphing = glc->new_morph = 1; + break; + case GLUT_KEY_RIGHT: + *destAngle = fmod(*destAngle+(RIGHT), 360); + glc->morphing = glc->new_morph = 1; + break; + case GLUT_KEY_HOME: + start_morph(STRAIGHT_MODEL, 0); + break; + default: + unknown_key = 1; + break; + } + } + calc_snake_metrics(); + + if (!unknown_key) + glutPostRedisplay(); +} + +void ui_mouse(int button, int state, int x, int y) { + if (button==0) { + switch (state) { + case GLUT_DOWN: + dragging = 1; + m_s[0] = M_SQRT1_2 * + (x - (glc->width / 2.0)) / (glc->width / 2.0); + m_s[1] = M_SQRT1_2 * + ((glc->height / 2.0) - y) / (glc->height / 2.0); + m_s[2] = sqrt(1-(m_s[0]*m_s[0]+m_s[1]*m_s[1])); + break; + case GLUT_UP: + dragging = 0; + oldquat[0] = cumquat[0]; + oldquat[1] = cumquat[1]; + oldquat[2] = cumquat[2]; + oldquat[3] = cumquat[3]; + break; + default: + break; + } + } + glutPostRedisplay(); +} + +void ui_motion(int x, int y) { + double norm; + float q[4]; + + if (dragging) { + /* construct the motion end vector from the x,y position on the + * window */ + m_e[0] = (x - (glc->width/ 2.0)) / (glc->width / 2.0); + m_e[1] = ((glc->height / 2.0) - y) / (glc->height / 2.0); + /* calculate the normal of the vector... */ + norm = m_e[0] * m_e[0] + m_e[1] * m_e[1]; + /* check if norm is outside the sphere and wraparound if necessary */ + if (norm > 1.0) { + m_e[0] = -m_e[0]; + m_e[1] = -m_e[1]; + m_e[2] = sqrt(norm - 1); + } else { + /* the z value comes from projecting onto an elliptical spheroid */ + m_e[2] = sqrt(1 - norm); + } + + /* now here, build a quaternion from m_s and m_e */ + q[0] = m_s[1] * m_e[2] - m_s[2] * m_e[1]; + q[1] = m_s[2] * m_e[0] - m_s[0] * m_e[2]; + q[2] = m_s[0] * m_e[1] - m_s[1] * m_e[0]; + q[3] = m_s[0] * m_e[0] + m_s[1] * m_e[1] + m_s[2] * m_e[2]; + + /* new rotation is the product of the new one and the old one */ + cumquat[0] = q[3] * oldquat[0] + q[0] * oldquat[3] + + q[1] * oldquat[2] - q[2] * oldquat[1]; + cumquat[1] = q[3] * oldquat[1] + q[1] * oldquat[3] + + q[2] * oldquat[0] - q[0] * oldquat[2]; + cumquat[2] = q[3] * oldquat[2] + q[2] * oldquat[3] + + q[0] * oldquat[1] - q[1] * oldquat[0]; + cumquat[3] = q[3] * oldquat[3] - q[0] * oldquat[0] - + q[1] * oldquat[1] - q[2] * oldquat[2]; + + calc_rotation(); + } + glutPostRedisplay(); +} + +void ui_init(int * argc, char ** argv) { + glutInit(argc, argv); + glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); + glutInitWindowSize(glc->width, glc->height); + glc->window = glutCreateWindow("glsnake"); + + glutDisplayFunc(glsnake_display); + glutReshapeFunc(glsnake_reshape); + glutIdleFunc(glsnake_idle); + glutKeyboardFunc(ui_keyboard); + glutSpecialFunc(ui_special); + glutMouseFunc(ui_mouse); + glutMotionFunc(ui_motion); + + yangvel = DEF_YANGVEL; + zangvel = DEF_ZANGVEL; + explode = DEF_EXPLODE; + angvel = DEF_ANGVEL; + statictime = DEF_STATICTIME; + altcolour = DEF_ALTCOLOUR; + titles = DEF_TITLES; + interactive = DEF_INTERACTIVE; + zoom = DEF_ZOOM; + wireframe = DEF_WIREFRAME; +} +#endif /* HAVE_GLUT */ diff --git a/hacks/glx/gltext.c b/hacks/glx/gltext.c index 75073fb3..aa3e876e 100644 --- a/hacks/glx/gltext.c +++ b/hacks/glx/gltext.c @@ -389,9 +389,9 @@ text_extents (const char *string, int *wP, int *hP) if (w > *wP) *wP = w; *hP += line_height; - s++; lines++; if (*s == 0) break; + s++; } else s++; diff --git a/hacks/glx/grab-ximage.c b/hacks/glx/grab-ximage.c index c0b5a2f0..99e92317 100644 --- a/hacks/glx/grab-ximage.c +++ b/hacks/glx/grab-ximage.c @@ -100,7 +100,7 @@ bigendian (void) extra byte set to 0xFF. */ XImage * -screen_to_ximage (Screen *screen, Window window) +screen_to_ximage (Screen *screen, Window window, char **filename_return) { Display *dpy = DisplayOfScreen (screen); Pixmap pixmap = 0; @@ -113,7 +113,7 @@ screen_to_ximage (Screen *screen, Window window) win_height = xgwa.height; pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth); - load_random_image (screen, window, pixmap); + load_random_image (screen, window, pixmap, filename_return); /* GL texture sizes must be powers of two. */ tex_width = to_pow2(win_width); diff --git a/hacks/glx/grab-ximage.h b/hacks/glx/grab-ximage.h index ba0e6994..215e9504 100644 --- a/hacks/glx/grab-ximage.h +++ b/hacks/glx/grab-ximage.h @@ -18,6 +18,7 @@ This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the extra byte set to 0xFF. */ -XImage * screen_to_ximage (Screen *screen, Window window); +XImage * screen_to_ximage (Screen *screen, Window window, + char **filename_return); #endif /* __GRAB_XIMAGE_H__ */ diff --git a/hacks/glx/hypertorus.c b/hacks/glx/hypertorus.c index bdcec9ec..9449c318 100644 --- a/hacks/glx/hypertorus.c +++ b/hacks/glx/hypertorus.c @@ -1,8 +1,7 @@ /* hypertorus --- Shows a hypertorus that rotates in 4d */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)hypertorus.c 1.1 03/05/18 xlockmore"; - #endif /* Copyright (c) 2003 Carsten Steger . */ diff --git a/hacks/glx/lavalite.c b/hacks/glx/lavalite.c index 8c2d800c..743d6575 100644 --- a/hacks/glx/lavalite.c +++ b/hacks/glx/lavalite.c @@ -96,6 +96,7 @@ extern XtAppContext app; #define DEFAULTS "*delay: 10000 \n" \ "*showFPS: False \n" \ "*wireframe: False \n" \ + "*geometry: 640x640 \n" \ "*count: " DEF_COUNT " \n" \ "*style: " DEF_STYLE " \n" \ "*speed: " DEF_SPEED " \n" \ @@ -557,7 +558,7 @@ draw_wing (GLfloat w, GLfloat h, GLfloat d, Bool wire) int polys = 0; int maxx = coords[0][countof(coords[0])-1][0]; int maxy = coords[0][countof(coords[0])-1][1]; - int x; + unsigned int x; for (x = 1; x < countof(coords[0]); x++) { diff --git a/hacks/glx/mirrorblob.c b/hacks/glx/mirrorblob.c new file mode 100644 index 00000000..679b7266 --- /dev/null +++ b/hacks/glx/mirrorblob.c @@ -0,0 +1,1250 @@ +/* -*- Mode: C; tab-width: 4 -*- */ + +/* mirrorblob Copyright (c) 2003 Jon Dowdall */ +/* + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + * Revision History: + * 23-Sep-2003: jon.dowdall@bigpond.com Created module "mirrorblob" + * 19-Oct-2003: jon.dowdall@bigpond.com Added texturing + * + * + * The mirrorblob screensaver draws a pulsing blob on the screen. + * Options include adding a background (via screen_to_ximage), + * texturing the blob, making the blob semi-transparent and varying the + * resolution of the blob tessellation. + * + * The mirrorblob was inspired by a lavalamp is in no way a simulation. + * The code is just an attempt to generate some eye-candy. + * + * Much of xscreensaver code framework is taken from the pulsar module by David + * Konerding and the glslideshow by Mike Oliphant and Jamie Zawinski. + * + */ + +#include +#include +#include + +/*- + * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock. + * otherwise caddr_t is not defined correctly + */ + +#include + +#ifdef STANDALONE +# define PROGCLASS "MirrorBlob" +# define HACK_INIT init_mirrorblob +# define HACK_DRAW draw_mirrorblob +# define HACK_RESHAPE reshape_mirrorblob +# define screensaver_opts xlockmore_opts + +# define DEF_DELAY "10000" +# define DEF_FPS "False" +# define DEF_WIRE "False" +# define DEF_BLEND "False" +# define DEF_FOG "False" +# define DEF_ANTIALIAS "False" +# define DEF_WALLS "True" +# define DEF_TEXTURE "True" +# define DEF_COLOUR "False" +# define DEF_OFFSET_TEXTURE "False" +# define DEF_PAINT_BACKGROUND "True" +# define DEF_X_RES "60" +# define DEF_Y_RES "32" +# define DEF_FIELD_POINTS "5" +# define DEF_INCREMENTAL "0" +# define DEF_FADE_SPEED "1" +# define DEF_HOLD_FRAMES "10240" + +#define DEFAULTS "*delay: " DEF_DELAY "\n" \ + "*showFPS: " DEF_FPS "\n" \ + "*wire: " DEF_WIRE "\n" \ + "*blend: " DEF_BLEND "\n" \ + "*fog: " DEF_FOG "\n" \ + "*antialias: " DEF_ANTIALIAS "\n" \ + "*walls: " DEF_WALLS "\n" \ + "*texture: " DEF_TEXTURE "\n" \ + "*colour: " DEF_COLOUR "\n" \ + "*offset_texture: " DEF_OFFSET_TEXTURE "\n" \ + "*bgimage: " DEF_PAINT_BACKGROUND "\n" \ + "*x_resolution: " DEF_X_RES "\n" \ + "*y_resolution: " DEF_Y_RES "\n" \ + "*field_points: " DEF_FIELD_POINTS "\n" \ + "*incremental: " DEF_INCREMENTAL "\n" \ + "*fade_speed: " DEF_FADE_SPEED "\n" \ + "*hold_frames: " DEF_HOLD_FRAMES "\n" \ + +# include "xlockmore.h" /* from the xscreensaver distribution */ +#else /* !STANDALONE */ +# include "xlock.h" /* from the xlockmore distribution */ +#endif /* !STANDALONE */ + +#ifdef USE_GL /* whole file */ + +#ifdef HAVE_XMU +# ifndef VMS +# include +#else /* VMS */ +# include +# endif /* VMS */ +#endif + +#include +#include +#include "GL/glx.h" + +#include +#include +/*#include */ +#include "grab-ximage.h" + +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#define WIDTH 1024 +#define HEIGHT 768 + +#define PI 3.1415926535897 + +/* */ +static int do_wire; +static int do_blend; +static int do_fog; +static int do_antialias; +static int do_walls; +static int do_texture; +static int do_paint_background; +static int do_colour; +static int offset_texture; +static int x_resolution; +static int y_resolution; +static int field_points; +static int incremental; +static int fade_speed; +static int hold_frames; + +static XrmOptionDescRec opts[] = { + {"-wire", ".mirrorblob.wire", XrmoptionNoArg, (caddr_t) "true" }, + {"+wire", ".mirrorblob.wire", XrmoptionNoArg, (caddr_t) "false" }, + {"-blend", ".mirrorblob.blend", XrmoptionNoArg, (caddr_t) "true" }, + {"+blend", ".mirrorblob.blend", XrmoptionNoArg, (caddr_t) "false" }, + {"-fog", ".mirrorblob.fog", XrmoptionNoArg, (caddr_t) "true" }, + {"+fog", ".mirrorblob.fog", XrmoptionNoArg, (caddr_t) "false" }, + {"-antialias", ".mirrorblob.antialias", XrmoptionNoArg, (caddr_t) "true" }, + {"+antialias", ".mirrorblob.antialias", XrmoptionNoArg, (caddr_t) "false" }, + {"-walls", ".mirrorblob.walls", XrmoptionNoArg, (caddr_t) "true" }, + {"+walls", ".mirrorblob.walls", XrmoptionNoArg, (caddr_t) "false" }, + {"-texture", ".mirrorblob.texture", XrmoptionNoArg, (caddr_t) "true" }, + {"+texture", ".mirrorblob.texture", XrmoptionNoArg, (caddr_t) "false" }, + {"-colour", ".mirrorblob.colour", XrmoptionNoArg, (caddr_t) "true" }, + {"+colour", ".mirrorblob.colour", XrmoptionNoArg, (caddr_t) "false" }, + {"-offset_texture", ".mirrorblob.offset_texture", XrmoptionNoArg, (caddr_t) "true" }, + {"+offset_texture", ".mirrorblob.offset_texture", XrmoptionNoArg, (caddr_t) "false" }, + {"-bgimage", ".mirrorblob.bgimage", XrmoptionNoArg, (caddr_t) "true" }, + {"+bgimage", ".mirrorblob.bgimage", XrmoptionNoArg, (caddr_t) "false" }, + {"-x_res", ".mirrorblob.x_res", XrmoptionSepArg, (caddr_t) NULL }, + {"-y_res", ".mirrorblob.y_res", XrmoptionSepArg, (caddr_t) NULL }, + {"-field_points", ".mirrorblob.field_points", XrmoptionSepArg, (caddr_t) NULL }, + {"-incremental", ".mirrorblob.incremental", XrmoptionSepArg, (caddr_t) NULL }, + {"-fade_speed", ".mirrorblob.fade_speed", XrmoptionSepArg, (caddr_t) NULL }, + {"-hold_frames", ".mirrorblob.hold_frames", XrmoptionSepArg, (caddr_t) NULL }, +}; + + +static argtype vars[] = { + {(caddr_t *) &do_wire, "wire", "Wire", DEF_WIRE, t_Bool}, + {(caddr_t *) &do_blend, "blend", "Blend", DEF_BLEND, t_Bool}, + {(caddr_t *) &do_fog, "fog", "Fog", DEF_FOG, t_Bool}, + {(caddr_t *) &do_antialias, "antialias", "Antialias", DEF_ANTIALIAS, t_Bool}, + {(caddr_t *) &do_walls, "walls", "Walls", DEF_WALLS, t_Bool}, + {(caddr_t *) &do_texture, "texture", "texture", DEF_TEXTURE, t_Bool}, + {(caddr_t *) &do_colour, "colour", "colour", DEF_COLOUR, t_Bool}, + {(caddr_t *) &offset_texture, "offset_texture","offset_texture", DEF_OFFSET_TEXTURE, t_Bool}, + {(caddr_t *) &do_paint_background,"bgimage","bgimage", DEF_PAINT_BACKGROUND, t_Bool}, + {(caddr_t *) &x_resolution, "x_res", "X_res", DEF_X_RES, t_Int}, + {(caddr_t *) &y_resolution, "y_res", "Y_res", DEF_Y_RES, t_Int}, + {(caddr_t *) &field_points, "field_points", "Field_points", DEF_FIELD_POINTS, t_Int}, + {(caddr_t *) &incremental, "incremental", "Incremental", DEF_INCREMENTAL, t_Int}, + {(caddr_t *) &fade_speed, "fade_speed", "fade_speed", DEF_FADE_SPEED, t_Int}, + {(caddr_t *) &hold_frames, "hold_frames", "hold_frames", DEF_HOLD_FRAMES, t_Int}, +}; + + +static OptionStruct desc[] = +{ + {"-/+ wire", "whether to do use wireframe instead of filled (faster)"}, + {"-/+ blend", "whether to do enable blending (slower)"}, + {"-/+ fog", "whether to do enable fog (slower)"}, + {"-/+ antialias", "whether to do enable antialiased lines (slower)"}, + {"-/+ walls", "whether to add walls to the blob space (slower)"}, + {"-/+ texture", "whether to add a texture to the blob (slower)"}, + {"-/+ colour", "whether to colour the blob"}, + {"-/+ offset_texture", "whether to offset texture co-ordinates"}, + {"-/+ bgimage", "whether to display a background texture (slower)"}, + {"-x_res", "Blob resolution in x direction"}, + {"-y_res", "Blob resolution in y direction"}, + {"-field_points", "Number of field points used to disturb blob"}, + {"-incremental", "Field summation method"}, + {"-fade_speed", "speed of transistion"}, + {"-hold_frames", "Number of frames before next image"}, +}; + +ModeSpecOpt screensaver_opts = {countof(opts), opts, countof(vars), vars, desc}; + +#ifdef USE_MODULES +ModStruct screensaver_description = +{"screensaver", "init_mirrorblob", "draw_mirrorblob", "release_mirrorblob", + "draw_mirrorblob", "init_mirrorblob", NULL, &screensaver_opts, + 1000, 1, 2, 1, 4, 1.0, "", + "OpenGL screensaver", 0, NULL}; +#endif + + +/* structure for holding the screensaver data */ +typedef struct { + int screen_width, screen_height; + GLXContext *glx_context; + Window window; + XColor fg, bg; +} screensaverstruct; + +static screensaverstruct *Screensaver = NULL; + +/***************************************************************************** + * Types used in blob code + *****************************************************************************/ + +typedef struct +{ + GLdouble x, y; +} Vector2D; + +typedef struct +{ + GLdouble x, y, z; +} Vector3D; + +typedef struct +{ + GLubyte red, green, blue, alpha; +} Colour; + +/* Data used for sphere tessellation */ +typedef struct +{ + double cosyd, sinyd; + + /* Number of x points at each row of the blob */ + int num_x_points; +} Row_Data; + +/* Structure to hold sphere distortion data */ +typedef struct +{ + double cx, cy, cpower; + double mx, my, mpower; + double ax, ay, apower; + double vx, vy, vpower; + Vector3D pos; +} Field_Data; + +/***************************************************************************** + * Static blob data + *****************************************************************************/ + +static Row_Data *row_data; + +/* Parameters controlling the position of the blob as a whole */ +static Vector3D blob_center = {0.0, 0.0, 0.0}; +static Vector3D blob_anchor = {0.0, 0.0, 0.0}; +static Vector3D blob_velocity = {0.0, 0.0, 0.0}; +static Vector3D blob_force = {0.0, 0.0, 0.0}; + +/* Count of the total number of points */ +static int num_points; + +static Vector3D *dots = NULL; +static Vector3D *normals = NULL; +static Colour *colours = NULL; +static Vector2D *tex_coords = NULL; + +/* Pointer to the field function results */ +static double *field = 0, *wall_field = 0; + +Field_Data *field_data; + +/* Use 2 textures to allow a gradualr fade between images */ +#define NUM_TEXTURES 2 +static int current_texture; + +/* Ratio of used texture size to total texture size */ +GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES]; +GLuint textures[NUM_TEXTURES]; + +static int holding = 1; +static int transitioning = 0; +static int colour_cycle = 0; + +/****************************************************************************** + * + * Change to the projection matrix and set our viewing volume. + * + */ +static void +resetProjection(int width, int height) +{ + glMatrixMode( GL_PROJECTION ); + glLoadIdentity( ); + gluPerspective( 60.0, 1.0, 1.0, 1024.0 ); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +/**************************************************************************** + * + * Load a texture using the screen_to_ximage function. + */ +void +grab_texture(ModeInfo *mi, int texture_index) +{ + XImage *ximage; + + ximage = screen_to_ximage (mi->xgwa.screen, mi->window, 0); + + glBindTexture (GL_TEXTURE_2D, textures[texture_index]); + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ximage->width, ximage->height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, ximage->data); + + tex_width[texture_index] = mi->xgwa.width / (GLfloat)ximage->width; + tex_height[texture_index] = mi->xgwa.height / (GLfloat)ximage->height; + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + free(ximage->data); + ximage->data = 0; + XDestroyImage (ximage); +} + +/****************************************************************************** + * + * Initialise the data used to calculate the blob shape. + */ +static void +initializeGL(ModeInfo *mi, GLsizei width, GLsizei height) +{ + GLfloat fogColor[4] = { 0.1, 0.1, 0.1, 0.1 }; + /* Lighting values */ + GLfloat lightPos0[] = {50.0f, 10.0f, 20.0f, 1.0f }; + GLfloat whiteLight0[] = { 0.1f, 0.1f, 0.1f, 1.0f }; + GLfloat sourceLight0[] = { 1.0f, 1.0f, 1.0f, 1.0f }; + GLfloat specularLight0[] = { 0.7f, 0.6f, 0.3f, 1.0f }; + + GLfloat lightPos1[] = {0.0f, -50.0f, 50.0f, 1.0f }; + GLfloat whiteLight1[] = { 0.1f, 0.1f, 0.1f, 1.0f }; + GLfloat sourceLight1[] = { 1.0f, 0.3f, 0.3f, 1.0f }; + GLfloat specularLight1[] = { 0.7f, 0.6f, 0.3f, 1.0f }; + + GLfloat specref[] = { 1.0f, 1.0f, 1.0f, 1.0f }; + + /* Setup our viewport. */ + glViewport( 0, 0, width, height ); + + glEnable(GL_DEPTH_TEST); + + if (do_antialias) + { + do_blend = 1; + glEnable(GL_LINE_SMOOTH); + } + + /* The blend function is used for trasitioning between two images even when + * blend is not selected. + */ + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (do_wire) + { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + } + else + { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + + if (do_fog) + { + glEnable(GL_FOG); + glFogi(GL_FOG_MODE, GL_LINEAR); + glFogfv(GL_FOG_COLOR, fogColor); + glFogf(GL_FOG_DENSITY, 0.35); + glFogf(GL_FOG_START, 2.0); + glFogf(GL_FOG_END, 10.0); + } + + /* Our shading model--Gouraud (smooth). */ + glShadeModel (GL_SMOOTH); + + /* Culling. */ + glCullFace (GL_BACK); + glEnable (GL_CULL_FACE); + glEnable (GL_DEPTH_TEST); + glFrontFace (GL_CCW); + + + /* Set the clear color. */ + glClearColor( 0, 0, 0, 0 ); + + glViewport( 0, 0, width, height ); + + glLightfv (GL_LIGHT0, GL_AMBIENT, whiteLight0); + glLightfv (GL_LIGHT0, GL_DIFFUSE, sourceLight0); + glLightfv (GL_LIGHT0, GL_SPECULAR, specularLight0); + glLightfv (GL_LIGHT0, GL_POSITION, lightPos0); + glEnable (GL_LIGHT0); + glLightfv (GL_LIGHT1, GL_AMBIENT, whiteLight1); + glLightfv (GL_LIGHT1, GL_DIFFUSE, sourceLight1); + glLightfv (GL_LIGHT1, GL_SPECULAR, specularLight1); + glLightfv (GL_LIGHT1, GL_POSITION, lightPos1); + glEnable (GL_LIGHT1); + glEnable (GL_LIGHTING); + + /* Enable color tracking */ + glEnable (GL_COLOR_MATERIAL); + + /* Set Material properties to follow glColor values */ + glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE); + + /* Set all materials to have specular reflectivity */ + glMaterialfv (GL_FRONT, GL_SPECULAR, specref); + glMateriali (GL_FRONT, GL_SHININESS, 64); + + glEnable (GL_NORMALIZE); + + /* Enable Arrays */ + if (do_texture) + { + glLightModeli (GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); + + glEnable (GL_TEXTURE_2D); + current_texture = 0; + glGenTextures (NUM_TEXTURES, textures); + grab_texture (mi, current_texture); + grab_texture (mi, 1 - current_texture); + + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + } + + if (do_colour) + { + glEnableClientState (GL_COLOR_ARRAY); + } + glEnableClientState (GL_NORMAL_ARRAY); + glEnableClientState (GL_VERTEX_ARRAY); +} + +/****************************************************************************** + * + * Calculate the normal vector for a plane given three points in the plane. + */ +static void +calculate_normal (Vector3D point1, + Vector3D point2, + Vector3D point3, + Vector3D *normal) +{ + Vector3D vector1, vector2; + double magnitude; + + vector1.x = point2.x - point1.x; + vector1.y = point2.y - point1.y; + vector1.z = point2.z - point1.z; + + vector2.x = point3.x - point2.x; + vector2.y = point3.y - point2.y; + vector2.z = point3.z - point2.z; + + (*normal).x = vector1.y * vector2.z - vector1.z * vector2.y; + (*normal).y = vector1.z * vector2.x - vector1.x * vector2.z; + (*normal).z = vector1.x * vector2.y - vector1.y * vector2.x; + + /* Adjust the normal to unit magnitude */ + magnitude = sqrt ((*normal).x * (*normal).x + + (*normal).y * (*normal).y + + (*normal).z * (*normal).z); + + /* Watch out for divide by zero/underflow */ + if (magnitude > 1e-300) + { + (*normal).x /= magnitude; + (*normal).y /= magnitude; + (*normal).z /= magnitude; + } +} + +/****************************************************************************** + * + * Initialise the data required to draw the blob allocating the memory as + * necessary. + * + * Return 0 on success. + */ +static int +initialise_blob(int width, + int height, + int field_array_size) +{ + /* Loop variables */ + int x, y, i; + double xd; + + colour_cycle = 0; + + row_data = (Row_Data *) malloc (y_resolution * sizeof (Row_Data)); + if (!row_data) + { + fprintf(stderr, "Couldn't allocate row data buffer\n"); + return -1; + } + + field_data = (Field_Data *) malloc (field_points * sizeof (Field_Data)); + if (!field_data) + { + fprintf(stderr, "Couldn't allocate row data buffer\n"); + return -1; + } + + field = (double *)malloc(field_array_size * sizeof(double)); + if (!field) + { + fprintf(stderr, "Couldn't allocate field buffer\n"); + return -1; + } + + wall_field = (double *)malloc(field_array_size * sizeof(double)); + if (!wall_field) + { + fprintf(stderr, "Couldn't allocate wall_field buffer\n"); + return -1; + } + + dots = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D)); + if (!dots) + { + fprintf(stderr, "Couldn't allocate points buffer\n"); + return -1; + } + glVertexPointer (3, GL_DOUBLE, 0, (GLvoid *) dots); + + normals = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D)); + if (!normals) + { + fprintf(stderr, "Couldn't allocate normals buffer\n"); + return -1; + } + glNormalPointer (GL_DOUBLE, 0, (GLvoid *) normals); + + if (do_colour) + { + colours = (Colour *)malloc(x_resolution * y_resolution * sizeof(Colour)); + if (!colours) + { + fprintf(stderr, "Couldn't allocate colours buffer\n"); + return -1; + } + glColorPointer (4, GL_UNSIGNED_BYTE, 0, (GLvoid *) colours); + } + + if (do_texture) + { + tex_coords = (Vector2D *)malloc(x_resolution * y_resolution + * sizeof(Vector2D)); + if (!tex_coords) + { + fprintf(stderr, "Couldn't allocate tex_coords buffer\n"); + return -1; + } + glTexCoordPointer (2, GL_DOUBLE, 0, (GLvoid *) tex_coords); + } + + if (! do_texture) + do_paint_background = False; + + + num_points = 0; + /* Generate constant row data and count of total number of points */ + for (y = 0; y < y_resolution; y++) + { + row_data[y].cosyd = cos(PI * (double)(y * (y_resolution + 1)) + / (double)(y_resolution * y_resolution)); + row_data[y].sinyd = sin(PI * (double)(y * (y_resolution + 1)) + / (double)(y_resolution * y_resolution)); + row_data[y].num_x_points = (int)(x_resolution * row_data[y].sinyd + 1.0); + num_points += row_data[y].num_x_points; + } + + /* Initialise field data */ + for (i = 0; i < field_points; i++) + { + field_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5); + field_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5); + field_data[i].apower = (((double)random() / (double)RAND_MAX) - 0.5); + + field_data[i].pos.x = 1.5 * sin(PI * field_data[i].ay) + * cos(PI * field_data[i].ax); + field_data[i].pos.y = 1.5 * cos(PI * field_data[i].ay); + field_data[i].pos.z = 1.5 * sin(PI * field_data[i].ay) + * sin(PI * field_data[i].ax); + + field_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5); + field_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5); + field_data[i].cpower = (((double)random() / (double)RAND_MAX) - 0.5); + + field_data[i].vx = 0.0; + field_data[i].vy = 0.0; + field_data[i].vpower = 0.0; + + field_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX); + field_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX); + field_data[i].mpower = 0.003 * ((double)random() / (double)RAND_MAX); + } + + /* Initialise lookup table of field strength */ + for (i = 0; i < field_array_size; i++) + { + xd = 2.0 * (((double)i / (double)field_array_size)); + + xd = 3.0 * xd * xd * xd * xd - 0.25 * xd * xd; + field[i] = 0.4 / (field_points * (xd + 0.1)); + + xd = 10.0 * (((double)i / (double)field_array_size)); + wall_field[i] = 0.4 / (xd * xd * xd * xd + 1.0); + } + + for (y = 0; y < y_resolution; y++) + { + for (x = 0; x < row_data[y].num_x_points; x++) + { + i = x + y * x_resolution; + xd = 2.0 * (((double)x / (double)row_data[y].num_x_points) - 0.5); + + dots[i].x = row_data[y].sinyd * cos(PI * xd); + dots[i].y = row_data[y].cosyd; + dots[i].z = row_data[y].sinyd * sin(PI * xd); + normals[i].x = row_data[y].sinyd * cos(PI * xd); + normals[i].y = row_data[y].cosyd; + normals[i].z = row_data[y].sinyd * sin(PI * xd); + if (do_texture) + { + tex_coords[i].x = 2.0 - 2.0 * x / (float) row_data[y].num_x_points; + tex_coords[i].y = 1.0 - y / (float) y_resolution; + } + } + } + return 0; +} + + +/****************************************************************************** + * + * Calculate the blob shape. + */ +static void +calc_blob(int width, + int height, + int field_array_size, + float limit) +{ + static double freak = 0.0; + + static double v_freak = 0.0007; + + /* Loop variables */ + int x, y, i, index; + /* position of a point */ + double xd, yd, zd, offset_x, offset_y, offset_z; + double strength, radius; + double xdist, ydist, zdist; + int dist; + + /* Color components */ + + colour_cycle++; + + blob_force.x = 0.0; + blob_force.y = 0.0; + blob_force.z = 0.0; + for (y = 0; y < y_resolution; y++) + { + for (x = 0; x < row_data[y].num_x_points; x++) + { + index = x + y * x_resolution; + xd = 2.0 * PI * (((double)x / (double)row_data[y].num_x_points) - 0.5); + + radius = 1.0 + 0.0 * sin (xd * 10); + + zd = radius * row_data[y].sinyd * sin(xd); + xd = radius * row_data[y].sinyd * cos(xd); + yd = radius * row_data[y].cosyd; + + offset_x = 0.0; + offset_y = 0.0; + offset_z = 0.0; + strength = 0.0; + for ( i = 0; i < field_points; i++) + { + xdist = field_data[i].pos.x - xd; + ydist = field_data[i].pos.y - yd; + zdist = field_data[i].pos.z - zd; + dist = field_array_size * (xdist * xdist + ydist * ydist + + zdist * zdist) * 0.1; + + strength += PI * field_data[i].apower; + + if (dist < field_array_size) + { + offset_x += xd * field_data[i].apower * field[dist]; + offset_y += yd * field_data[i].apower * field[dist]; + offset_z += zd * field_data[i].apower * field[dist]; + + blob_force.x += 2.0 * xd * field_data[i].apower * field[dist]; + blob_force.y += 2.0 * yd * field_data[i].apower * field[dist]; + blob_force.z += 2.0 * zd * field_data[i].apower * field[dist]; + + strength *= 2.0 * field[dist]; + } + + if (incremental) + { + xd += offset_x * freak * freak; + yd += offset_y * freak * freak; + zd += offset_z * freak * freak; + } + if (incremental == 1) + { + offset_x = 0.0; + offset_y = 0.0; + offset_z = 0.0; + } + } + + if (incremental < 3) + { + xd += offset_x; + yd += offset_y; + zd += offset_z; + } + xd += blob_center.x; + yd += blob_center.y; + zd += blob_center.z; + + if (do_colour) + { + colours[index].red = 128 + (int)(sin(strength + colour_cycle * 0.01 + 2.0 * PI * x / row_data[y].num_x_points) * 127.0); + colours[index].green = 128 + (int)(cos(strength + colour_cycle * 0.025) * 127.0); + colours[index].blue = 128 + (int)(sin(strength + colour_cycle * 0.03 + 2.0 * PI * y / y_resolution) * 127.0); + colours[index].alpha = 128 + (int)(cos(strength + colour_cycle * 0.015) * 63.0); + } + + /* Add walls */ + if (do_walls) + { + if (zd < -limit) zd = -limit; + if (zd > limit) zd = limit; + + dist = field_array_size * (zd + limit) * (zd + limit) * 0.5; + if (dist < field_array_size) + { + xd += (xd - blob_center.x) * wall_field[dist]; + yd += (yd - blob_center.y) * wall_field[dist]; + blob_force.z += (zd + limit); + } + else + { + dist = field_array_size * (zd - limit) * (zd - limit) * 0.5; + if (dist < field_array_size) + { + xd += (xd - blob_center.x) * wall_field[dist]; + yd += (yd - blob_center.y) * wall_field[dist]; + blob_force.z -= (zd - limit); + } + + if (yd < -limit) yd = -limit; + if (yd > limit) yd = limit; + + dist = field_array_size * (yd + limit) * (yd + limit) * 0.5; + if (dist < field_array_size) + { + xd += (xd - blob_center.x) * wall_field[dist]; + zd += (zd - blob_center.z) * wall_field[dist]; + blob_force.y += (yd + limit); + } + else + { + dist = field_array_size * (yd - limit) * (yd - limit) * 0.5; + if (dist < field_array_size) + { + xd += (xd - blob_center.x) * wall_field[dist]; + zd += (zd - blob_center.z) * wall_field[dist]; + blob_force.y -= (yd - limit); + } + } + + if (xd < -limit) xd = -limit; + if (xd > limit) xd = limit; + + dist = field_array_size * (xd + limit) * (xd + limit) * 0.5; + if (dist < field_array_size) + { + yd += (yd - blob_center.y) * wall_field[dist]; + zd += (zd - blob_center.z) * wall_field[dist]; + blob_force.x += (xd + limit); + } + else + { + dist = field_array_size * (xd - limit) * (xd - limit) * 0.5; + if (dist < field_array_size) + { + yd += (yd - blob_center.y) * wall_field[dist]; + zd += (zd - blob_center.z) * wall_field[dist]; + blob_force.x -= (xd - limit); + } + } + + if (yd < -limit) yd = -limit; + if (yd > limit) yd = limit; + } + } + + dots[index].x = xd; + dots[index].y = yd; + dots[index].z = zd; + } + } + + /* Calculate the normals for each vertex and the texture mapping if required. + * Although the code actually calculates the normal for one of the triangles + * attached to a vertex rather than the vertex itself the results are not too + * bad for with a reasonable number of verticies. + */ + for (y = 1; y < y_resolution - 1; y++) + { + int index1, index2, index3; + if (row_data[y - 1].num_x_points) + { + + for (x = 0; x < row_data[y].num_x_points; x++) + { + if (x == row_data[y].num_x_points - 1) + { + index1 = y * x_resolution; + } + else + { + index1 = x + 1 + y * x_resolution; + } + index2 = x + y * x_resolution; + index3 = ((x + 0.5) * row_data[y - 1].num_x_points + / row_data[y].num_x_points) + (y - 1) * x_resolution; + calculate_normal (dots[index1], dots[index2], dots[index3], + &normals[index1]); + if (do_texture) + { + if (offset_texture) + { + tex_coords[index1].x = dots[index1].x * 0.125 + 0.5 + * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI)); + tex_coords[index1].y = dots[index1].y * 0.125 + 0.5 + * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI)); + } + else + { + tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x) + / (0.5 * PI))); + tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y) + / (0.5 * PI))); + } + tex_coords[index1].x *= tex_width[current_texture]; + tex_coords[index1].y *= tex_height[current_texture]; + } + } + } + } + + freak += v_freak; + v_freak += -freak / 2000000.0; + + /* Update position and strength of points used to distort the blob */ + for (i = 0; i < field_points; i++) + { + field_data[i].vx += field_data[i].mx*(field_data[i].cx - field_data[i].ax); + field_data[i].vy += field_data[i].my*(field_data[i].cy - field_data[i].ay); + field_data[i].vpower += field_data[i].mpower + * (field_data[i].cpower - field_data[i].apower); + + field_data[i].ax += 0.1 * field_data[i].vx; + field_data[i].ay += 0.1 * field_data[i].vy; + field_data[i].apower += 0.1 * field_data[i].vpower; + + field_data[i].pos.x = 1.5 * sin(PI * field_data[i].ay) + * cos(PI * field_data[i].ax); + field_data[i].pos.y = 1.5 * cos(PI * field_data[i].ay); + field_data[i].pos.z = 1.5 * sin(PI * field_data[i].ay) + * sin(PI * field_data[i].ax); + } + + /* Update the center of the whole blob */ + blob_velocity.x += (blob_anchor.x - blob_center.x) / 80.0 + + 0.01 * blob_force.x / num_points; + blob_velocity.y += (blob_anchor.y - blob_center.y) / 80.0 + + 0.01 * blob_force.y / num_points; + blob_velocity.z += (blob_anchor.z - blob_center.z) / 80.0 + + 0.01 * blob_force.z / num_points; + + blob_center.x += blob_velocity.x * 0.5; + blob_center.y += blob_velocity.y * 0.5; + blob_center.z += blob_velocity.z * 0.5; + + blob_velocity.x *= 0.99; + blob_velocity.y *= 0.99; + blob_velocity.z *= 0.99; +} + +/****************************************************************************** + * + * Draw the blob shape. + * + * The horrendous indexing to calculate the verticies that form a particular + * traiangle is the result of the conversion of my first non-openGL version of + * blob to this openGL version. This may be tidied up when I finally playing + * with the more interesting bits of the code. + */ +static void +draw_blob (void) +{ + int x, y, x2, x3; + int index1, index2, index3; + int lower, upper; + + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + + /* Move down the z-axis. */ + glTranslatef( 0.0, 0.0, -5.0 ); + + for (y = 1; y < y_resolution; y++) + { + if (row_data[y - 1].num_x_points) + { + for (x = 0; x < row_data[y].num_x_points; x++) + { + glBegin (GL_TRIANGLES); + if (x == row_data[y].num_x_points - 1) + { + index1 = y * x_resolution; + } + else + { + index1 = x + 1 + y * x_resolution; + } + index2 = x + y * x_resolution; + index3 = ((x + 0.5) * row_data[y - 1].num_x_points + / row_data[y].num_x_points) + (y - 1) * x_resolution; + glArrayElement(index1); + glArrayElement(index2); + glArrayElement(index3); + glEnd(); + + lower = floorf((x - 0.5) * row_data[y - 1].num_x_points + / (float)row_data[y].num_x_points); + upper = floorf((x + 0.5) * row_data[y - 1].num_x_points + / (float)row_data[y].num_x_points); + + if (upper > lower) + { + glBegin (GL_TRIANGLE_FAN); + index1 = x + y * x_resolution; + + for (x2 = lower; x2 <= upper; x2++) + { + x3 = x2; + while (x3 < 0) x3 += row_data[y - 1].num_x_points; + while (x3 >= row_data[y - 1].num_x_points) + x3 -= row_data[y - 1].num_x_points; + index2 = x3 + (y - 1) * x_resolution; + + if (x2 < upper) + { + x3 = x2 + 1; + while (x3 < 0) x3 += row_data[y - 1].num_x_points; + while (x3 >= row_data[y - 1].num_x_points) + x3 -= row_data[y - 1].num_x_points; + index3 = x3 + (y - 1) * x_resolution; + if (x2 == lower) + { + glArrayElement(index1); + } + } + glArrayElement(index2); + } + glEnd (); + } + } + } + } +} + +/****************************************************************************** + * + * Draw the background image simply map a texture onto a full screen quad. + */ +static void +draw_background (ModeInfo *mi) +{ + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + glEnable (GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + + /* Reset the projection matrix to make it easier to get the size of the quad + * correct + */ + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glOrtho(0.0, MI_WIDTH(mi), MI_HEIGHT(mi), 0.0, -1000.0, 1000.0); + + glBegin (GL_QUADS); + + glTexCoord2f (0.0, tex_height[current_texture]); + glVertex2i (0, 0); + + glTexCoord2f (0.0, 0.0); + glVertex2i (0, MI_HEIGHT(mi)); + + glTexCoord2f (tex_width[current_texture], 0.0); + glVertex2i (MI_WIDTH(mi), MI_HEIGHT(mi)); + + glTexCoord2f (tex_width[current_texture], tex_height[current_texture]); + glVertex2i (MI_WIDTH(mi), 0); + glEnd(); + + glPopMatrix (); + glMatrixMode (GL_MODELVIEW); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +} + +/****************************************************************************** + * + * Update the scene. + */ +static GLvoid +drawScene(ModeInfo * mi) +{ + check_gl_error ("drawScene"); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glDisable (GL_BLEND); + + /* Ensure the background is drawn with the correct texture */ + if (do_texture) + { + glBindTexture (GL_TEXTURE_2D, textures[current_texture]); + } + + glColor4ub(255, 255, 255, 255); + if (do_paint_background && !do_wire) + { + draw_background (mi); + + /* When transitioning between two images paint the new image over the old + * image with a varying alpha value to get a smooth fade. + */ + if (transitioning) + { + glDisable (GL_DEPTH_TEST); + glEnable (GL_BLEND); + glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]); + glColor4ub (255, 255, 255, transitioning); + + draw_background (mi); + + glBindTexture (GL_TEXTURE_2D, textures[current_texture]); + glEnable (GL_DEPTH_TEST); + } + /* Clear the depth buffer bit so the backgound is behind the blob */ + glClear(GL_DEPTH_BUFFER_BIT); + } + else + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + calc_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024, 2.5); + + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + + if (do_blend) + { + glEnable (GL_BLEND); + if (transitioning) + { + glColor4ub (255, 255, 255, 128 - transitioning / 2); + } + else + { + glColor4ub (255, 255, 255, 128); + } + } + else + { + glDisable (GL_BLEND); + glColor4ub (255, 255, 255, 255); + } + draw_blob(); + + /* While transitioning draw the blob twice with a modified alpha channel. + * The trasitioning state machine is very crude, it simply counts frames + * rather than elapsed time but it works. + */ + if (do_texture) + { + if (transitioning) + { + glClear(GL_DEPTH_BUFFER_BIT); + glEnable (GL_BLEND); + glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]); + if (do_blend) + { + glColor4ub (255, 255, 255, transitioning / 2); + } + else + { + glColor4ub (255, 255, 255, transitioning); + } + draw_blob (); + transitioning += fade_speed; + if (transitioning > 255) + { + transitioning = 0; + current_texture = 1 - current_texture; + holding = 1; + } + } + if (holding) + { + holding++; + if (holding > hold_frames) + { + grab_texture (mi, 1 - current_texture); + transitioning = 1; + holding = 0; + } + } + } + + /* increment frame-counter */ +/* frame++; */ + +/* check_gl_error ("drawScene"); */ +} + + +void +draw_mirrorblob(ModeInfo * mi) +{ + screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)]; + Display *display = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + + if (!gp->glx_context) + return; + + glXMakeCurrent(display, window, *(gp->glx_context)); + drawScene(mi); + if (mi->fps_p) do_fps (mi); + glXSwapBuffers(display, window); +} + +/* Standard reshape function */ +void +reshape_mirrorblob(ModeInfo *mi, int width, int height) +{ + glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) ); + resetProjection(width, height); +} + +void +init_mirrorblob(ModeInfo * mi) +{ + int screen = MI_SCREEN(mi); + + screensaverstruct *gp; + + if (Screensaver == NULL) { + if ((Screensaver = (screensaverstruct *) + calloc(MI_NUM_SCREENS(mi), sizeof (screensaverstruct))) == NULL) + return; + } + gp = &Screensaver[screen]; + + gp->window = MI_WINDOW(mi); + if ((gp->glx_context = init_GL(mi)) != NULL) { + reshape_mirrorblob(mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + initializeGL(mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + } else { + MI_CLEARWINDOW(mi); + } + /* Set the environment variable used by NVIDIA cards to control updating + * during the blankning interval */ + setenv ("__GL_SYNC_TO_VBLANK", "1", 1); + setenv ("__GL_FSAA_MODE", "1", 1); + + initialise_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024); +} + + +/* all sorts of nice cleanup code should go here! */ +void release_mirrorblob(ModeInfo * mi) +{ + int screen; + + if (row_data) free(row_data); + if (field_data) free(field_data); + if (colours) free(colours); + if (tex_coords) free(tex_coords); + if (dots) free(dots); + if (wall_field) free(wall_field); + if (field) free(field); + + if (Screensaver != NULL) { + for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) { +/* screensaverstruct *gp = &Screensaver[screen];*/ + } + (void) free((void *) Screensaver); + Screensaver = NULL; + } + FreeAllGL(mi); +} +#endif diff --git a/hacks/glx/mirrorblob.man b/hacks/glx/mirrorblob.man new file mode 100644 index 00000000..af5965d4 --- /dev/null +++ b/hacks/glx/mirrorblob.man @@ -0,0 +1,111 @@ +.TH XScreenSaver 1 "" "X Version 11" +.SH NAME +mirrorblob - Draws a wobbly blob that distorts the image behind it. +.SH SYNOPSIS +.B mirrorblob +[\-display \fIhost:display.screen\fP] +[\-visual \fIvisual\fP] +[\-window] +[\-root] +[\-wire] +[\-delay \fInumber\fP] +[\-fog] +[\-walls] +[\-colour] +[\-texture] +[\-bgimage] +[\-offset_texture] +[\-blend] +[\-antialias] +[\-x_res \fInumber\fP] +[\-y_res \fInumber\fP] +[\-field_points \fInumber\fP] +[\-hold_frames \fInumber\fP] +[\-fps] +.SH DESCRIPTION +Draws a wobbling blob, making use of alpha blending, fog, +textures, and lighting, plus a ``frames per second'' meter so that you can +tell how fast your graphics card is... Requires OpenGL. +.SH OPTIONS +.TP 8 +.B \-visual \fIvisual\fP +Specify which visual to use. Legal values are the name of a visual class, +or the id number (decimal or hex) of a specific visual. +.TP 8 +.B \-window +Draw on a newly-created window. This is the default. +.TP 8 +.B \-root +Draw on the root window. +.B \-wire +Render in wireframe instead of solid. +.TP 8 +.B \-delay \fInumber\fP +Per-frame delay, in microseconds. Default: 10000 (0.01 seconds.). +.TP 8 +.B \-fog | \-no-fog +Whether to enable fog. +.TP 8 +.B \-walls | \-no-walls +Add walls for blob to hit. +.TP 8 +.B \-colour | \-no-colour +Draw coloured blob. If also textured, the texture will have color mixed in. +.TP 8 +.B \-texture | \-no-texture +Whether to wrap a texture image on the blob. +.TP 8 +.B \-bgimage | \-no-bgimage +Whether to also draw the texture on the background. +.TP 8 +.B \-offset_texture | \-no-offset_texture +Whether to ofset the texture calculations to only use a region of the image + under the blob. This works well when blend is enabled. +.TP 8 +.B \-blend | \-no-blend +Whether to enable blending. (see also offset_texture above) +.TP 8 +.B \-antialias | \-no-antialias +Whether to anti-alias lines. +.TP 8 +.B \-x_res \fInumber\fP +Number of verticies around the middle row of the blob (see also y_res). +.TP 8 +.B \-y_res \fInumber\fP +Number of rows of verticies used to calculatethe blob, combined with x_res to + indirectly determine the number of triangles used to draw blob. Larger + numbers give a smoother blob but increase calculation times exponentially. +.TP 8 +.B \-field_points \fInumber\fP +Number of points used to distort the blob. +.TP 8 +.B \-hold_frames \fInumber\fP +Number of frames between changing images. +.TP 8 +.B \-fade_speed \fInumber\fP +Speed at which fading occurs when changing images. +.TP 8 +.B \-fps | \-no-fps +Whether to show a frames-per-second display at the bottom of the screen. +.SH ENVIRONMENT +.PP +.TP 8 +.B DISPLAY +to get the default host and display number. +.TP 8 +.B XENVIRONMENT +to get the name of a resource file that overrides the global resources +stored in the RESOURCE_MANAGER property. +.SH SEE ALSO +.BR X (1), +.BR xscreensaver (1) +.SH COPYRIGHT +Copyright \(co 2003 by Jon Dowdall. 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 +Jon Dowdall. diff --git a/hacks/glx/moebius.c b/hacks/glx/moebius.c index eebef51d..f9693ae1 100644 --- a/hacks/glx/moebius.c +++ b/hacks/glx/moebius.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* moebius --- Moebius Strip II, an Escher-like GL scene with ants. */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)moebius.c 5.01 2001/03/01 xlockmore"; - #endif /*- diff --git a/hacks/glx/morph3d.c b/hacks/glx/morph3d.c index ab3613c5..f2691fa8 100644 --- a/hacks/glx/morph3d.c +++ b/hacks/glx/morph3d.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* morph3d --- Shows 3D morphing objects */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)morph3d.c 5.01 2001/03/01 xlockmore"; - #endif #undef DEBUG_CULL_FACE diff --git a/hacks/glx/pipeobjs.c b/hacks/glx/pipeobjs.c index e89f7009..7aa01a5b 100644 --- a/hacks/glx/pipeobjs.c +++ b/hacks/glx/pipeobjs.c @@ -1,6 +1,5 @@ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)pipeobjs.c 4.04 97/07/28 xlockmore"; - #endif /*- diff --git a/hacks/glx/pipes.c b/hacks/glx/pipes.c index db1e792d..e20266b5 100644 --- a/hacks/glx/pipes.c +++ b/hacks/glx/pipes.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* pipes --- 3D selfbuiding pipe system */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)pipes.c 4.07 97/11/24 xlockmore"; - #endif /*- diff --git a/hacks/glx/polytopes.c b/hacks/glx/polytopes.c index d4a9eaa5..911efeb9 100644 --- a/hacks/glx/polytopes.c +++ b/hacks/glx/polytopes.c @@ -1,8 +1,7 @@ /* polytopes --- Shows one of the six regular polytopes rotating in 4d */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)polytopes.c 1.1 03/05/18 xlockmore"; - #endif /* Copyright (c) 2003 Carsten Steger . */ diff --git a/hacks/glx/rubik.c b/hacks/glx/rubik.c index f57b5328..034122c5 100644 --- a/hacks/glx/rubik.c +++ b/hacks/glx/rubik.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* rubik --- Shows an auto-solving Rubik's cube */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)rubik.c 5.01 2001/03/01 xlockmore"; - #endif /*- diff --git a/hacks/glx/sballs.c b/hacks/glx/sballs.c index 442bcb05..25300483 100644 --- a/hacks/glx/sballs.c +++ b/hacks/glx/sballs.c @@ -1,6 +1,6 @@ /* sballs --- balls spinning like crazy in GL */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)sballs.c 5.02 2001/03/10 xlockmore"; #endif diff --git a/hacks/glx/shark.c b/hacks/glx/shark.c index a3de1b9e..3658db6a 100644 --- a/hacks/glx/shark.c +++ b/hacks/glx/shark.c @@ -1,8 +1,7 @@ /* atlantis --- Shows moving 3D sea animals */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)shark.c 1.2 98/06/16 xlockmore"; - #endif /* Copyright (c) E. Lassauge, 1998. */ diff --git a/hacks/glx/sierpinski3d.c b/hacks/glx/sierpinski3d.c index 3a0a158b..d3df8cdf 100644 --- a/hacks/glx/sierpinski3d.c +++ b/hacks/glx/sierpinski3d.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* Sierpinski3D --- 3D sierpinski gasket */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)sierpinski3D.c 00.01 99/11/04 xlockmore"; - #endif /*- diff --git a/hacks/glx/spheremonics.c b/hacks/glx/spheremonics.c index a4c06902..8e45a4a4 100644 --- a/hacks/glx/spheremonics.c +++ b/hacks/glx/spheremonics.c @@ -768,7 +768,7 @@ static void draw_label (ModeInfo *mi, const char *s) { spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)]; - int i; + unsigned int i; glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT); glDisable(GL_LIGHTING); @@ -871,7 +871,7 @@ init_spheremonics (ModeInfo *mi) cc->m_max = 4; /* 9? */ { - int i; + unsigned int i; for (i = 0; i < countof(cc->dm); i++) cc->dm[i] = 1; /* going up! */ diff --git a/hacks/glx/sproingies.c b/hacks/glx/sproingies.c index 13974a79..6248ec95 100644 --- a/hacks/glx/sproingies.c +++ b/hacks/glx/sproingies.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* sproingies.c - 3D sproingies */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)sproingies.c 4.04 97/07/28 xlockmore"; - #endif /*- diff --git a/hacks/glx/sproingiewrap.c b/hacks/glx/sproingiewrap.c index d00197d7..34db7b56 100644 --- a/hacks/glx/sproingiewrap.c +++ b/hacks/glx/sproingiewrap.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* sproingiewrap.c - sproingies wrapper */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)sproingiewrap.c 4.07 97/11/24 xlockmore"; - #endif /*- diff --git a/hacks/glx/stairs.c b/hacks/glx/stairs.c index 33e73ab0..53a30faf 100644 --- a/hacks/glx/stairs.c +++ b/hacks/glx/stairs.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* stairs --- Infinite Stairs, and Escher-like scene. */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)stairs.c 4.07 97/11/24 xlockmore"; - #endif #undef DEBUG_LISTS diff --git a/hacks/glx/starwars.c b/hacks/glx/starwars.c index 197f323f..5e41af2a 100644 --- a/hacks/glx/starwars.c +++ b/hacks/glx/starwars.c @@ -443,7 +443,7 @@ get_more_lines (sws_configuration *sc) if (sc->buf_tail > (s - sc->buf)) { int i = sc->buf_tail - (s - sc->buf); - memcpy (sc->buf, s, i); + memmove (sc->buf, s, i); sc->buf_tail = i; sc->buf[sc->buf_tail] = 0; } diff --git a/hacks/glx/starwars.man b/hacks/glx/starwars.man index f2a83b6e..050d40a3 100644 --- a/hacks/glx/starwars.man +++ b/hacks/glx/starwars.man @@ -71,12 +71,11 @@ won't. Some examples: .EX -starwars -columns 30 -program \\ - 'wget -qO- http://webcrawler.com/cgi-bin/SearchTicker' -starwars -columns 76 -program 'cat /usr/src/linux/README' +starwars -columns 76 -program 'cat /usr/src/linux*/README' starwars -program 'ping www.starwars.com' starwars -no-wrap -left -program 'finger @gnu.org' starwars -no-wrap -left -program 'ps -ef' +starwars -no-wrap -left -program 'od -txC /dev/random' .EE .TP 8 .B \-size \fIinteger\fP @@ -138,8 +137,17 @@ to get the default host and display number. to get the name of a resource file that overrides the global resources stored in the RESOURCE_MANAGER property. .SH SEE ALSO -.BR X (1), -.BR xscreensaver (1) +.BR xscreensaver (1), +.BR fortune (1), +.BR phosphor (1), +.BR apple2 (1), +.BR fontglide (1), +.BR ljlatest (1), +.BR dadadodo (1), +.BR webcollage (1), +.BR driftnet (1) +.BR EtherPEG , +.BR EtherPeek .SH COPYRIGHT Copyright \(co 1998-2001 by Jamie Zawinski and Claudio Matsuoka. Permission to use, copy, modify, distribute, and sell this software and diff --git a/hacks/glx/stonerview.c b/hacks/glx/stonerview.c index b926f62b..1ac784cb 100644 --- a/hacks/glx/stonerview.c +++ b/hacks/glx/stonerview.c @@ -26,21 +26,13 @@ #include #include "yarandom.h" +#include "usleep.h" #include "stonerview-move.h" extern int init_view(int *argc, char *argv[]); extern void win_draw(void); -static void -stonerview_usleep (unsigned long usecs) -{ - struct timeval tv; - tv.tv_sec = usecs / 1000000L; - tv.tv_usec = usecs % 1000000L; - (void) select (0, 0, 0, 0, &tv); -} - int main(int argc, char *argv[]) { @@ -58,7 +50,7 @@ int main(int argc, char *argv[]) { win_draw(); move_increment(); - stonerview_usleep (20000); + usleep (20000); } return 0; diff --git a/hacks/glx/superquadrics.c b/hacks/glx/superquadrics.c index b711debd..f274741b 100644 --- a/hacks/glx/superquadrics.c +++ b/hacks/glx/superquadrics.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* superquadrics --- 3D mathematical shapes */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)superquadrics.c 4.07 97/11/24 xlockmore"; - #endif /*- diff --git a/hacks/glx/swim.c b/hacks/glx/swim.c index 0848d46d..a7fccfeb 100644 --- a/hacks/glx/swim.c +++ b/hacks/glx/swim.c @@ -1,8 +1,7 @@ /* atlantis --- Shows moving 3D sea animals */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)swim.c 1.3 98/06/18 xlockmore"; - #endif /* Copyright (c) E. Lassauge, 1998. */ diff --git a/hacks/glx/tunnel_draw.c b/hacks/glx/tunnel_draw.c index 71954100..94e7bc0e 100644 --- a/hacks/glx/tunnel_draw.c +++ b/hacks/glx/tunnel_draw.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* atunnels --- OpenGL Advanced Tunnel Demo */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)tunnel_draw.c 5.02 2002/03/16 xlockmore"; #endif diff --git a/hacks/glx/whale.c b/hacks/glx/whale.c index afff1d38..15826e4f 100644 --- a/hacks/glx/whale.c +++ b/hacks/glx/whale.c @@ -1,8 +1,7 @@ /* atlantis --- Shows moving 3D sea animals */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)whale.c 1.3 98/06/18 xlockmore"; - #endif /* Copyright (c) E. Lassauge, 1998. */ diff --git a/hacks/grav.c b/hacks/grav.c index ccadc2a6..aec897d5 100644 --- a/hacks/grav.c +++ b/hacks/grav.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* grav --- planets spinning around a pulsar */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)grav.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/halo.c b/hacks/halo.c index 2c002b54..fc69ea73 100644 --- a/hacks/halo.c +++ b/hacks/halo.c @@ -257,7 +257,7 @@ run_circles (Display *dpy, Window window) if (cycle_p && cmode != seuss_mode) { struct timeval now; - static struct timeval then = { 0, }; + static struct timeval then = { 0, 0 }; unsigned long diff; #ifdef GETTIMEOFDAY_TWO_ARGS struct timezone tzp; diff --git a/hacks/helix.c b/hacks/helix.c index 3e05531b..7ca03da7 100644 --- a/hacks/helix.c +++ b/hacks/helix.c @@ -307,7 +307,7 @@ char *defaults [] = { XrmOptionDescRec options [] = { { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-subdelay", ".subdelay", XrmoptionSepArg, 0 }, - { 0 }, + { 0, 0, 0, 0 }, }; int options_size = (sizeof (options) / sizeof (options[0])); diff --git a/hacks/hopalong.c b/hacks/hopalong.c index 949c5b9f..71bbdc8e 100644 --- a/hacks/hopalong.c +++ b/hacks/hopalong.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* hop --- real plane fractals */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)hop.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/ifs.c b/hacks/ifs.c index e3e0dc62..29a72c23 100644 --- a/hacks/ifs.c +++ b/hacks/ifs.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* ifs --- modified iterated functions system */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)ifs.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/imsmap.c b/hacks/imsmap.c index 5db775d2..fad6a8ca 100644 --- a/hacks/imsmap.c +++ b/hacks/imsmap.c @@ -419,7 +419,7 @@ draw_map (Display *dpy, Window window) if (cycle_p) { struct timeval now; - static struct timeval then = { 0, }; + static struct timeval then = { 0, 0 }; unsigned long diff; #ifdef GETTIMEOFDAY_TWO_ARGS struct timezone tzp; diff --git a/hacks/jigsaw.c b/hacks/jigsaw.c index ca52f59d..6c28205f 100644 --- a/hacks/jigsaw.c +++ b/hacks/jigsaw.c @@ -150,7 +150,7 @@ read_screen (Display *dpy, Window window, int *widthP, int *heightP) p = XCreatePixmap(dpy, window, *widthP, *heightP, xgwa.depth); XClearWindow(dpy, window); - load_random_image (xgwa.screen, window, p); + load_random_image (xgwa.screen, window, p, NULL); XClearWindow(dpy, window); return p; diff --git a/hacks/juggle.c b/hacks/juggle.c index 2650ad27..8080ccb3 100644 --- a/hacks/juggle.c +++ b/hacks/juggle.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* juggle */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)juggle.c 5.00 2000/11/01 xlockmore"; - #endif /*- @@ -1474,7 +1473,13 @@ draw_juggle(ModeInfo * mi) { struct timeval tv; - (void)gettimeofday(&tv, NULL); +# ifdef GETTIMEOFDAY_TWO_ARGS + struct timezone tzp; + gettimeofday(&tv, &tzp); +# else + gettimeofday(&tv); +# endif + sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000); } for (traj = sp->head->next; traj != sp->head; traj = traj->next) { diff --git a/hacks/julia.c b/hacks/julia.c index 29cbb558..bff47db3 100644 --- a/hacks/julia.c +++ b/hacks/julia.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 4 -*- * julia --- continuously varying Julia set. */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)julia.c 4.03 97/04/10 xlockmore"; #endif diff --git a/hacks/laser.c b/hacks/laser.c index 2b0622dd..adce8931 100644 --- a/hacks/laser.c +++ b/hacks/laser.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* laser --- spinning lasers */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)laser.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/lightning.c b/hacks/lightning.c index deb37411..8da6bb58 100644 --- a/hacks/lightning.c +++ b/hacks/lightning.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* lightning --- fractal lightning bolds */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)lightning.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/lisa.c b/hacks/lisa.c index 63d7d837..235ceab9 100644 --- a/hacks/lisa.c +++ b/hacks/lisa.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* lisa --- animated full-loop lisajous figures */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)lisa.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/lissie.c b/hacks/lissie.c index 1f2e7c15..f0ce93d7 100644 --- a/hacks/lissie.c +++ b/hacks/lissie.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* lissie --- the Lissajous worm */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)lissie.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/ljlatest b/hacks/ljlatest index 2af69a2f..c0856fda 100755 --- a/hacks/ljlatest +++ b/hacks/ljlatest @@ -27,7 +27,7 @@ use Text::Wrap qw(wrap); use bytes; # Larry can take Unicode and shove it up his ass sideways. my $progname = $0; $progname =~ s@.*/@@g; -my $version = q{ $Revision: 1.8 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/; +my $version = q{ $Revision: 1.9 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/; my $verbose = 0; @@ -275,14 +275,19 @@ sub lj_latest { $|=1; # unbuffer stdout - if ($verbose) { - $_ = $url; - s@^[a-z]+:/+([^/?\#]+).*$@$1@; - my $host = $_; - print STDOUT "Contacting $host..."; - } + $_ = $url; + s@^[a-z]+:/+([^/?\#]+).*$@$1@; + my $host = $_; + + print STDOUT "Contacting $host..." if ($verbose); my ($http, $head, $body) = get_document ($url); + + if (!$body) { + print STDOUT "$progname: no response from $host\n"; + return; + } + print STDOUT "\n\n" if ($verbose); # $body = `cat /tmp/last`; diff --git a/hacks/ljlatest.man b/hacks/ljlatest.man index 1bbc7d11..00184aed 100644 --- a/hacks/ljlatest.man +++ b/hacks/ljlatest.man @@ -32,8 +32,9 @@ for use with those .BR xscreensaver (1) programs that run an external program to generate text. For example: .EX -phosphor -scale 2 -program 'ljlatest --cols 40' +fontglide -program ljlatest starwars -program ljlatest +phosphor -scale 2 -program 'ljlatest --cols 40' .EE .SH OPTIONS .I ljlatest @@ -72,7 +73,9 @@ The output is always ISO-8859-1, regardless of locale. .BR xscreensaver (1), .BR fortune (1), .BR phosphor (1), +.BR apple2 (1), .BR starwars (1), +.BR fontglide (1), .BR dadadodo (1), .BR webcollage (1), .BR driftnet (1) diff --git a/hacks/loop.c b/hacks/loop.c index 04419801..f492ee44 100644 --- a/hacks/loop.c +++ b/hacks/loop.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* loop --- Chris Langton's self-producing loops */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)loop.c 5.01 2000/03/15 xlockmore"; - #endif /*- diff --git a/hacks/metaballs.c b/hacks/metaballs.c index cc804793..d814a80c 100644 --- a/hacks/metaballs.c +++ b/hacks/metaballs.c @@ -56,7 +56,7 @@ typedef struct short xpos,ypos; } BLOB; -static unsigned char nBlobCount; +static unsigned int nBlobCount; static unsigned char radius; static unsigned char delta; static unsigned char dradius; diff --git a/hacks/mountain.c b/hacks/mountain.c index 6b237889..037101aa 100644 --- a/hacks/mountain.c +++ b/hacks/mountain.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* mountain -- square grid mountains */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)mountain.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/penrose.c b/hacks/penrose.c index aa62ab03..03e93ac9 100644 --- a/hacks/penrose.c +++ b/hacks/penrose.c @@ -18,9 +18,8 @@ http://www.nine.org/notw/notw.html */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)penrose.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/phosphor.c b/hacks/phosphor.c index 8298cf25..8ba63dd2 100644 --- a/hacks/phosphor.c +++ b/hacks/phosphor.c @@ -450,7 +450,6 @@ static void set_cursor (p_state *state, Bool on) { if (set_cursor_1 (state, on)) -; { if (state->cursor_timer) XtRemoveTimeOut (state->cursor_timer); diff --git a/hacks/phosphor.man b/hacks/phosphor.man index 3d1fbe12..989150ec 100644 --- a/hacks/phosphor.man +++ b/hacks/phosphor.man @@ -75,10 +75,12 @@ will work, but programs like .BR top (1) won't. -Here's a good trick, to get phosphor to display recent web search terms: +For example: .EX -phosphor -program \\ - 'wget -qO- http://webcrawler.com/cgi-bin/SearchTicker' +phosphor -program 'cat /usr/src/linux*/README' +phosphor -program 'ping localhost' +phosphor -program 'ps -e' +phosphor -program 'od -txC -w6 /dev/random' .EE .SH ENVIRONMENT .PP @@ -90,9 +92,17 @@ to get the default host and display number. to get the name of a resource file that overrides the global resources stored in the RESOURCE_MANAGER property. .SH SEE ALSO -.BR wget (1), -.BR X (1), -.BR xscreensaver (1) +.BR xscreensaver (1), +.BR fortune (1), +.BR apple2 (1), +.BR starwars (1), +.BR fontglide (1), +.BR ljlatest (1), +.BR dadadodo (1), +.BR webcollage (1), +.BR driftnet (1) +.BR EtherPEG , +.BR EtherPeek .SH COPYRIGHT Copyright \(co 1999 by Jamie Zawinski. Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is diff --git a/hacks/polyominoes.c b/hacks/polyominoes.c index 87b5c744..3ed5227b 100644 --- a/hacks/polyominoes.c +++ b/hacks/polyominoes.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* polyominoes --- Shows attempts to place polyominoes into a rectangle */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)polyominoes.c 5.01 2000/12/18 xlockmore"; - #endif /* diff --git a/hacks/pong.c b/hacks/pong.c new file mode 100644 index 00000000..bcfc3fc7 --- /dev/null +++ b/hacks/pong.c @@ -0,0 +1,505 @@ +/* pong, Copyright (c) 2003 Jeremy English + * A pong screen saver + * + * Modified by Trevor Blackwell to use analogtv.[ch] display. + * Also added gradual acceleration of the ball, shrinking of paddles, and + * scorekeeping. + * + * 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. + */ + +/* + * TLB sez: I haven't actually seen a pong game since I was about 9. Can + * someone who has one make this look more realistic? Issues: + * + * - the font for scores is wrong. For example '0' was square. + * - was there some kind of screen display when someone won? + * - did the ball move smoothly, or was the X or Y position quantized? + * + * It could also use better player logic: moving the paddle even when the ball + * is going away, and making mistakes instead of just not keeping up with the + * speeding ball. + * + * There is some info at http://www.mameworld.net/discrete/Atari/Atari.htm#Pong + * + * It says that the original Pong game did not have a microprocessor, or even a + * custom integrated circuit. It was all discrete logic. + * + */ + +#include "screenhack.h" +#include "analogtv.h" +/* #define OUTPUT_POS */ + +typedef struct _paddle { + int x; + int y; + int w; + int h; + int wait; + int lock; + int score; +} Paddle; + +typedef struct _ball { + int x; + int y; + int w; + int h; +} Ball; + +static int delay; +Paddle l_paddle; +Paddle r_paddle; +Ball ball; +static int bx,by; +static int m_unit; +static int paddle_rate; + +static analogtv *tv; +static analogtv_input *inp; +static analogtv_reception reception; + +static int paddle_ntsc[4]; +static int field_ntsc[4]; +static int ball_ntsc[4]; +static int score_ntsc[4]; +static int net_ntsc[4]; + +analogtv_font score_font; + +enum { + PONG_W = ANALOGTV_VIS_LEN, + PONG_H = ANALOGTV_VISLINES, + PONG_TMARG = 10 +}; + +static void +hit_top_bottom(void) +{ + if ( (ball.y <= PONG_TMARG) || + (ball.y+ball.h >= PONG_H) ) + by=-by; +} + +void +start_game(void) +{ + /*Init the ball*/ + ball.x = PONG_W/2; + ball.y = PONG_H/2; + bx = m_unit; + by = m_unit; + + l_paddle.wait = 1; + l_paddle.lock = 0; + r_paddle.wait = 0; + r_paddle.lock = 0; + paddle_rate = m_unit-1; + + if (l_paddle.h > 10) l_paddle.h= l_paddle.h*19/20; + if (r_paddle.h > 10) r_paddle.h= r_paddle.h*19/20; +} + +static void +hit_paddle(void) +{ + if ( ball.x + ball.w >= r_paddle.x && + bx > 0 ) /*we are traveling to the right*/ + { + if ((ball.y + ball.h > r_paddle.y) && + (ball.y < r_paddle.y + r_paddle.h)) + { + bx=-bx; + l_paddle.wait = 0; + r_paddle.wait = 1; + r_paddle.lock = 0; + l_paddle.lock = 0; + } + else + { + r_paddle.score++; + start_game(); + } + } + + if (ball.x <= l_paddle.x + l_paddle.w && + bx < 0 ) /*we are traveling to the left*/ + { + if ( ball.y + ball.h > l_paddle.y && + ball.y < l_paddle.y + l_paddle.h) + { + bx=-bx; + l_paddle.wait = 1; + r_paddle.wait = 0; + r_paddle.lock = 0; + l_paddle.lock = 0; + } + else + { + l_paddle.score++; + start_game(); + } + } +} + +static void +init_pong (Display *dpy, Window window) +{ + tv=analogtv_allocate(dpy, window); + analogtv_set_defaults(tv, ""); + tv->event_handler = screenhack_handle_event; + + analogtv_make_font(dpy, window, &score_font, + 4, 6, NULL ); + + /* If you think we haven't learned anything since the early 70s, + look at this font for a while */ + analogtv_font_set_char(&score_font, '0', + "****" + "* *" + "* *" + "* *" + "* *" + "****"); + analogtv_font_set_char(&score_font, '1', + " *" + " *" + " *" + " *" + " *" + " *"); + analogtv_font_set_char(&score_font, '2', + "****" + " *" + "****" + "* " + "* " + "****"); + analogtv_font_set_char(&score_font, '3', + "****" + " *" + "****" + " *" + " *" + "****"); + analogtv_font_set_char(&score_font, '4', + "* *" + "* *" + "****" + " *" + " *" + " *"); + analogtv_font_set_char(&score_font, '5', + "****" + "* " + "****" + " *" + " *" + "****"); + analogtv_font_set_char(&score_font, '6', + "****" + "* " + "****" + "* *" + "* *" + "****"); + analogtv_font_set_char(&score_font, '7', + "****" + " *" + " *" + " *" + " *" + " *"); + analogtv_font_set_char(&score_font, '8', + "****" + "* *" + "****" + "* *" + "* *" + "****"); + analogtv_font_set_char(&score_font, '9', + "****" + "* *" + "****" + " *" + " *" + " *"); + + score_font.y_mult *= 2; + score_font.x_mult *= 2; + +#ifdef OUTPUT_POS + printf("screen(%d,%d,%d,%d)\n",0,0,PONG_W,PONG_H); +#endif + + inp=analogtv_input_allocate(); + analogtv_setup_sync(inp, 0, 0); + + reception.input = inp; + reception.level = 2.0; + reception.ofs=0; +#if 0 + if (random()) { + reception.multipath = frand(1.0); + } else { +#endif + reception.multipath=0.0; +#if 0 + } +#endif + + delay = get_integer_resource ("delay", "Integer"); + if (delay < 0) delay = 0; + + /*Init the paddles*/ + l_paddle.x = 8; + l_paddle.y = 100; + l_paddle.w = 16; + l_paddle.h = PONG_H/4; + l_paddle.wait = 1; + l_paddle.lock = 0; + r_paddle = l_paddle; + r_paddle.x = PONG_W - 8 - r_paddle.w; + r_paddle.wait = 0; + /*Init the ball*/ + ball.x = PONG_W/2; + ball.y = PONG_H/2; + ball.w = 16; + ball.h = 8; + + m_unit = get_integer_resource ("speed", "Integer"); + + start_game(); + + analogtv_lcp_to_ntsc(ANALOGTV_BLACK_LEVEL, 0.0, 0.0, field_ntsc); + analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, ball_ntsc); + analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, paddle_ntsc); + analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, score_ntsc); + analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, net_ntsc); + + analogtv_draw_solid(inp, + ANALOGTV_VIS_START, ANALOGTV_VIS_END, + ANALOGTV_TOP, ANALOGTV_BOT, + field_ntsc); +} + +static void +p_logic(Paddle *p) +{ + int targ; + if (bx > 0) { + targ = ball.y + by * (r_paddle.x-ball.x) / bx; + } + else if (bx < 0) { + targ = ball.y - by * (ball.x - l_paddle.x - l_paddle.w) / bx; + } + else { + targ = ball.y; + } + if (targ > PONG_H) targ=PONG_H; + if (targ < 0) targ=0; + + if (targ < p->y && !p->lock) + { + p->y -= paddle_rate; + } + else if (targ > (p->y + p->h) && !p->lock) + { + p->y += paddle_rate; + } + else + { + int move=targ - (p->y + p->h/2); + if (move>paddle_rate) move=paddle_rate; + if (move<-paddle_rate) move=-paddle_rate; + p->y += move; + p->lock = 1; + } +} + +static void +p_hit_top_bottom(Paddle *p) +{ + if(p->y <= PONG_TMARG) + { + p->y = PONG_TMARG; + } + if((p->y + p->h) >= PONG_H) + { + p->y = PONG_H - p->h; + } +} + +/* + XFillRectangle (dpy, window, gc, p->x, p->y, p->w, p->h); + if (old_v > p->y) + { + XClearArea(dpy,window, p->x, p->y + p->h, + p->w, (old_v + p->h) - (p->y + p->h), 0); + } + else if (old_v < p->y) + { + XClearArea(dpy,window, p->x, old_v, p->w, p->y - old_v, 0); + } +*/ +static void +paint_paddle(analogtv_input *inp, Paddle *p) +{ + analogtv_draw_solid(inp, + ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w, + ANALOGTV_TOP, ANALOGTV_BOT, + field_ntsc); + + analogtv_draw_solid(inp, + ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w, + ANALOGTV_TOP + p->y, ANALOGTV_TOP + p->y + p->h, + paddle_ntsc); +} + +/* + XClearArea(dpy,window, old_ballx, old_bally, ball.d, ball.d, 0); + XFillRectangle (dpy, window, gc, ball.x, ball.y, ball.d, ball.d); + XFillRectangle (dpy, window, gc, xgwa.width / 2, 0, ball.d, xgwa.height); +*/ + +static void +erase_ball(analogtv_input *inp) +{ + analogtv_draw_solid(inp, + ANALOGTV_VIS_START + ball.x, ANALOGTV_VIS_START + ball.x + ball.w, + ANALOGTV_TOP + ball.y, ANALOGTV_TOP + ball.y + ball.h, + field_ntsc); +} + +static void +paint_ball(analogtv_input *inp) +{ + analogtv_draw_solid(inp, + ANALOGTV_VIS_START + ball.x, ANALOGTV_VIS_START + ball.x + ball.w, + ANALOGTV_TOP + ball.y, ANALOGTV_TOP + ball.y + ball.h, + ball_ntsc); +} + +static void +paint_score(analogtv_input *inp) +{ + char buf[256]; + + analogtv_draw_solid(inp, + ANALOGTV_VIS_START, ANALOGTV_VIS_END, + ANALOGTV_TOP, ANALOGTV_TOP + 10+ score_font.char_h * score_font.y_mult, + field_ntsc); + + sprintf(buf, "%d",r_paddle.score%256); + analogtv_draw_string(inp, &score_font, buf, + ANALOGTV_VIS_START + 130, ANALOGTV_TOP + 8, + score_ntsc); + + sprintf(buf, "%d",l_paddle.score%256); + analogtv_draw_string(inp, &score_font, buf, + ANALOGTV_VIS_END - 200, ANALOGTV_TOP + 8, + score_ntsc); + +} + +static void +paint_net(analogtv_input *inp) +{ + int x,y; + + x=(ANALOGTV_VIS_START + ANALOGTV_VIS_END)/2; + + for (y=ANALOGTV_TOP; y0) bx++; else bx--; + } + + if (!r_paddle.wait) + { + p_logic(&r_paddle); + } + if (!l_paddle.wait) + { + p_logic(&l_paddle); + } + + p_hit_top_bottom(&r_paddle); + p_hit_top_bottom(&l_paddle); + + hit_top_bottom(); + hit_paddle(); + + #ifdef OUTPUT_POS + printf("(%d,%d,%d,%d)\n",ball.x,ball.y,ball.w,ball.h); + #endif + + paint_score(inp); + + paint_net(inp); + + if (1) { + paint_paddle(inp, &r_paddle); + paint_paddle(inp, &l_paddle); + } + if (1) paint_ball(inp); + + analogtv_handle_events(tv); + + analogtv_init_signal(tv, 0.04); + analogtv_reception_update(&reception); + analogtv_add_signal(tv, &reception); + analogtv_draw(tv); +} + + +char *progclass = "pong"; + +char *defaults [] = { + "*delay: 10000", + "*speed: 6", + ANALOGTV_DEFAULTS + "*TVContrast: 150", + 0 +}; + +XrmOptionDescRec options [] = { + { "-delay", ".delay", XrmoptionSepArg, 0 }, + { "-percent", ".percent", XrmoptionSepArg, 0 }, + { "-speed", ".speed", XrmoptionSepArg, 0 }, + ANALOGTV_OPTIONS + { 0, 0, 0, 0 } +}; + +void +screenhack (Display *dpy, Window window) +{ + init_pong (dpy, window); + while (1) + { + play_pong (); + } +} + diff --git a/hacks/pong.man b/hacks/pong.man new file mode 100644 index 00000000..833486ed --- /dev/null +++ b/hacks/pong.man @@ -0,0 +1,74 @@ +.TH XScreenSaver 1 "" "X Version 11" +.SH NAME +pong - Pong Home Video Game Emulator +.SH SYNOPSIS +.B pong +[\-display \fIhost:display.screen\fP] [\-foreground \fIcolor\fP] +[\-background \fIcolor\fP] [\-window] [\-root] [\-mono] [\-install] +[\-visual \fIvisual\fP] [\-delay \fIseconds\fP] +.SH DESCRIPTION +The +.I pong +The pong program simulates an ancient Pong home video game, as well as +various artifacts from displaying it on a color TV set. +.SH OPTIONS +.I pong +accepts the following options: +.TP 8 +.B \-window +Draw on a newly-created window. This is the default. +.TP 8 +.B \-root +Draw on the root window. +.TP 8 +.B \-mono +If on a color display, pretend we're on a monochrome display. +.TP 8 +.B \-install +Install a private colormap for the window. +.TP 8 +.B \-visual \fIvisual\fP +Specify which visual to use. Legal values are the name of a visual class, +or the id number (decimal or hex) of a specific visual. +.TP 8 +.B \-delay \fIdelay\fP +The delay between displaying one crash and another. +.SH ENVIRONMENT +.PP +.TP 8 +.B DISPLAY +to get the default host and display number. +.TP 8 +.B XENVIRONMENT +to get the name of a resource file that overrides the global resources +stored in the RESOURCE_MANAGER property. +.SH X RESOURCES +Notable X resources supported include the following which correspond +to standard TV controls: +.BR analogTVTint , +.BR analogTVColor , +.BR analogTVBrightness , +and +.BR analogTVContrast + +which correspond to standard TV controls. They range from 0 to +100,except for tint which is an angle between -180 and +180. +.SH TRADEMARKS +Pong may be a trademark. + +.SH SEE ALSO +.BR X (1), +.BR xscreensaver (1), +.BR xanalogtv (1), +.BR apple2 (1) +.SH COPYRIGHT +2003 by Jeremy English. 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 +Original Pong program by Jeremy English . Scoring +and television emulation by Trevor Blackwell . diff --git a/hacks/ripples.c b/hacks/ripples.c index 323f1c1c..d90cea5c 100644 --- a/hacks/ripples.c +++ b/hacks/ripples.c @@ -466,7 +466,7 @@ setup_X(Display * disp, Window win) gc = XCreateGC(display, window, gcflags, &gcv); - load_random_image (xwa.screen, window, window); + load_random_image (xwa.screen, window, window, NULL); orig_map = XGetImage(display, window, 0, 0, xwa.width, xwa.height, ~0L, ZPixmap); diff --git a/hacks/rotor.c b/hacks/rotor.c index 65a1fdd7..dde56239 100644 --- a/hacks/rotor.c +++ b/hacks/rotor.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* rotor --- a swirly rotor */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)rotor.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/rotzoomer.c b/hacks/rotzoomer.c index 76b3b3fb..9425dc54 100644 --- a/hacks/rotzoomer.c +++ b/hacks/rotzoomer.c @@ -321,7 +321,7 @@ static void setup_X (Display * disp, Window win) if (use_subwindow_mode_p (xwa.screen, window)) /* see grabscreen.c */ gcflags |= GCSubwindowMode; gc = XCreateGC (display, window, gcflags, &gcv); - load_random_image (xwa.screen, window, window); + load_random_image (xwa.screen, window, window, NULL); orig_map = XGetImage (display, window, 0, 0, width, height, ~0L, ZPixmap); diff --git a/hacks/sierpinski.c b/hacks/sierpinski.c index e0a5ac21..8d521563 100644 --- a/hacks/sierpinski.c +++ b/hacks/sierpinski.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* sierpinski --- Sierpinski's triangle fractal */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)sierpinski.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/slidescreen.c b/hacks/slidescreen.c index ae08b2ca..78a30843 100644 --- a/hacks/slidescreen.c +++ b/hacks/slidescreen.c @@ -36,7 +36,7 @@ init_slide (Display *dpy, Window window) Visual *visual; XGetWindowAttributes (dpy, window, &xgwa); - load_random_image (xgwa.screen, window, window); + load_random_image (xgwa.screen, window, window, NULL); cmap = xgwa.colormap; visual = xgwa.visual; max_width = xgwa.width; diff --git a/hacks/slip.c b/hacks/slip.c index 1a3ac158..93ace4fc 100644 --- a/hacks/slip.c +++ b/hacks/slip.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* slip --- lots of slipping blits */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)slip.c 5.00 2000/11/01 xlockmore"; - #endif /*- @@ -113,7 +112,7 @@ prepare_screen(ModeInfo * mi, slipstruct * sp) #ifdef STANDALONE /* jwz -- sometimes hack the desktop image! */ if (halfrandom(sp, 2) == 0) { load_random_image (DefaultScreenOfDisplay(display), - MI_WINDOW(mi), MI_WINDOW(mi)); + MI_WINDOW(mi), MI_WINDOW(mi), NULL); } #endif diff --git a/hacks/sonar.c b/hacks/sonar.c index add3d6f1..00f7b3f6 100644 --- a/hacks/sonar.c +++ b/hacks/sonar.c @@ -38,7 +38,7 @@ * software for any purpose. It is provided "as is" without express or * implied warranty. * - * $Revision: 1.25 $ + * $Revision: 1.29 $ * * Version 1.0 April 27, 1998. * - Initial version @@ -482,7 +482,7 @@ lookupHost(ping_target *target) (ip[2] << 16) | (ip[1] << 8) | (ip[0])); - hent = gethostbyaddr (ip, 4, AF_INET); + hent = gethostbyaddr ((const char *) ip, 4, AF_INET); if (debug_p > 1) fprintf (stderr, "%s: %s => %s\n", @@ -1012,8 +1012,13 @@ sendping(ping_info *pi, ping_target *pt) ICMP_CHECKSUM(icmph) = 0; ICMP_ID(icmph) = pi->pid; ICMP_SEQ(icmph) = pi->seq++; +# ifdef GETTIMEOFDAY_TWO_ARGS gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)], (struct timezone *) 0); +# else + gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]); +# endif + strcpy((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)], pt->name); ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz); @@ -1117,7 +1122,7 @@ getping(sonar_info *si, ping_info *pi) /* Local Variables */ struct sockaddr from; - unsigned int fromlen; + unsigned int fromlen; /* Posix says socklen_t, but that's not portable */ int result; u_char packet[1024]; struct timeval now; @@ -1175,7 +1180,11 @@ getping(sonar_info *si, ping_info *pi) /* Check the packet */ +# ifdef GETTIMEOFDAY_TWO_ARGS gettimeofday(&now, (struct timezone *) 0); +# else + gettimeofday(&now); +# endif ip = (struct ip *) packet; iphdrlen = IP_HDRLEN(ip) << 2; icmph = (struct ICMP *) &packet[iphdrlen]; @@ -1904,6 +1913,7 @@ parse_mode (Bool ping_works_p) # endif /* HAVE_PING */ for (next = token; + *next && *next != ',' && *next != ' ' && *next != '\t' && *next != '\n'; next++) ; @@ -2032,7 +2042,11 @@ screenhack(Display *dpy, Window win) /* Call the sensor and display the results */ +# ifdef GETTIMEOFDAY_TWO_ARGS gettimeofday(&start, (struct timezone *) 0); +# else + gettimeofday(&start); +# endif bl = sensor(si, sensor_info); Sonar(si, bl); @@ -2042,7 +2056,11 @@ screenhack(Display *dpy, Window win) if (si->current == 0) si->sweepnum++; XSync (dpy, False); +# ifdef GETTIMEOFDAY_TWO_ARGS gettimeofday(&finish, (struct timezone *) 0); +# else + gettimeofday(&finish); +# endif sleeptime = si->delay - delta(&start, &finish); screenhack_handle_events (dpy); if (sleeptime > 0L) diff --git a/hacks/sphere.c b/hacks/sphere.c index c8ca46ca..2323eb6e 100644 --- a/hacks/sphere.c +++ b/hacks/sphere.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* sphere --- a bunch of shaded spheres */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)sphere.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/spiral.c b/hacks/spiral.c index 214cbfbb..a471acc1 100644 --- a/hacks/spiral.c +++ b/hacks/spiral.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* spiral --- spiraling dots */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)spiral.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/spotlight.c b/hacks/spotlight.c index 4f76dd28..eebe5f37 100644 --- a/hacks/spotlight.c +++ b/hacks/spotlight.c @@ -111,7 +111,7 @@ init_hack (Display *dpy, Window window) /* grab screen to pixmap */ pm = XCreatePixmap(dpy, window, sizex, sizey, xgwa.depth); - load_random_image (xgwa.screen, window, pm); + load_random_image (xgwa.screen, window, pm, NULL); XClearWindow(dpy, window); XFlush (dpy); diff --git a/hacks/strange.c b/hacks/strange.c index f8f88a66..c730259c 100644 --- a/hacks/strange.c +++ b/hacks/strange.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* strange --- strange attractors */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)strange.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/swirl.c b/hacks/swirl.c index 9f1801b3..93f0c0a6 100644 --- a/hacks/swirl.c +++ b/hacks/swirl.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 4 -*- * swirl --- swirly color-cycling patterns. */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)swirl.c 4.00 97/01/01 xlockmore"; #endif diff --git a/hacks/thornbird.c b/hacks/thornbird.c index eb35ae2b..44a2a59f 100644 --- a/hacks/thornbird.c +++ b/hacks/thornbird.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* thornbird --- continuously varying Thornbird set */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)thornbird.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/triangle.c b/hacks/triangle.c index 83bdc067..1fa8c62a 100644 --- a/hacks/triangle.c +++ b/hacks/triangle.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* triangle --- create a triangle-mountain */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)triangle.c 4.04 97/07/28 xlockmore"; - #endif /*- diff --git a/hacks/twang.c b/hacks/twang.c index e51621f4..e118851c 100644 --- a/hacks/twang.c +++ b/hacks/twang.c @@ -143,7 +143,7 @@ static void grabImage (XWindowAttributes *xwa) XGetImage (display, window, 0, 0, windowWidth, windowHeight, ~0L, ZPixmap); - load_random_image (screen, window, window); + load_random_image (screen, window, window, NULL); sourceImage = XGetImage (display, window, 0, 0, windowWidth, windowHeight, ~0L, ZPixmap); diff --git a/hacks/vines.c b/hacks/vines.c index f4151cd4..a816d736 100644 --- a/hacks/vines.c +++ b/hacks/vines.c @@ -1,9 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* vines --- vine fractals */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)vines.c 5.00 2000/11/01 xlockmore"; - #endif /*- diff --git a/hacks/webcollage-helper.c b/hacks/webcollage-helper.c index 99fef88b..a661b3a4 100644 --- a/hacks/webcollage-helper.c +++ b/hacks/webcollage-helper.c @@ -279,7 +279,7 @@ write_pixbuf (GdkPixbuf *pb, const char *file) perror (buf); exit (1); } - fprintf (stderr, " %ldK\n", (st.st_size + 1023) / 1024); + fprintf (stderr, " %luK\n", (st.st_size + 1023) / 1024); } fclose (out); diff --git a/hacks/whirlygig.c b/hacks/whirlygig.c index 523fcc43..118d7dd9 100644 --- a/hacks/whirlygig.c +++ b/hacks/whirlygig.c @@ -603,9 +603,9 @@ screenhack (Display *display, Window window) int size; size = (int)(15.0 + 5.0 * sin((double)internal_time / 180.0)); /* First delete the old circle... */ - if (!info->trail && ( !dbeclear_p || + if (!info->trail && ( !dbeclear_p #ifdef HAVE_DOUBLE_BUFFER_EXTENSION - !backb + || !backb #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ )) { XSetForeground(display, bgc, BlackPixel(display, screen)); diff --git a/hacks/worm.c b/hacks/worm.c index 7eb5fdf2..e57367d5 100644 --- a/hacks/worm.c +++ b/hacks/worm.c @@ -2,9 +2,8 @@ /* -*- Mode: C; tab-width: 4 -*- */ /* worm --- draw wiggly worms */ -#if !defined( lint ) && !defined( SABER ) +#if 0 static const char sccsid[] = "@(#)worm.c 4.04 97/07/28 xlockmore"; - #endif /*- diff --git a/hacks/xanalogtv.c b/hacks/xanalogtv.c new file mode 100644 index 00000000..605acac6 --- /dev/null +++ b/hacks/xanalogtv.c @@ -0,0 +1,448 @@ +/* xanalogtv, Copyright (c) 2003 Trevor Blackwell + * + * 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. + * + * + * Simulate test patterns on an analog TV. Concept similar to xteevee + * in this distribution, but a totally different implementation based + * on the simulation of an analog TV set in utils/analogtv.c. Much + * more realistic, but needs more video card bandwidth. + * + * It flips around through simulated channels 2 through 13. Some show + * pictures from your images directory, some show color bars, and some + * just have static. Some channels receive two stations simultaneously + * so you see a ghostly, misaligned image. + * + * It's easy to add some test patterns by compiling in an XPM, but I + * can't find any that are clearly freely redistributable. + * + */ + +#include +#include "screenhack.h" +#include "xpm-pixmap.h" +#include "analogtv.h" +#include +#include +#include +#include +#include + +#include "images/logo-50.xpm" + +/* #define DEBUG 1 */ +/* #define USE_TEST_PATTERNS */ + +#define countof(x) (sizeof((x))/sizeof((*x))) + +static analogtv *tv=NULL; + +analogtv_font ugly_font; + +static void +update_smpte_colorbars(analogtv_input *input) +{ + int col; + int xpos, ypos; + int black_ntsc[4]; + + /* + SMPTE is the society of motion picture and television engineers, and + these are the standard color bars in the US. Following the partial spec + at http://broadcastengineering.com/ar/broadcasting_inside_color_bars/ + These are luma, chroma, and phase numbers for each of the 7 bars. + */ + double top_cb_table[7][3]={ + {75, 0, 0.0}, /* gray */ + {69, 31, 167.0}, /* yellow */ + {56, 44, 283.5}, /* cyan */ + {48, 41, 240.5}, /* green */ + {36, 41, 60.5}, /* magenta */ + {28, 44, 103.5}, /* red */ + {15, 31, 347.0} /* blue */ + }; + double mid_cb_table[7][3]={ + {15, 31, 347.0}, /* blue */ + {7, 0, 0}, /* black */ + {36, 41, 60.5}, /* magenta */ + {7, 0, 0}, /* black */ + {56, 44, 283.5}, /* cyan */ + {7, 0, 0}, /* black */ + {75, 0, 0.0} /* gray */ + }; + + analogtv_lcp_to_ntsc(0.0, 0.0, 0.0, black_ntsc); + + analogtv_setup_sync(input, 1, 0); + analogtv_setup_teletext(input); + + for (col=0; col<7; col++) { + analogtv_draw_solid_rel_lcp(input, col*(1.0/7.0), (col+1)*(1.0/7.0), 0.00, 0.68, + top_cb_table[col][0], + top_cb_table[col][1], top_cb_table[col][2]); + + analogtv_draw_solid_rel_lcp(input, col*(1.0/7.0), (col+1)*(1.0/7.0), 0.68, 0.75, + mid_cb_table[col][0], + mid_cb_table[col][1], mid_cb_table[col][2]); + } + + analogtv_draw_solid_rel_lcp(input, 0.0, 1.0/6.0, + 0.75, 1.00, 7, 40, 303); /* -I */ + analogtv_draw_solid_rel_lcp(input, 1.0/6.0, 2.0/6.0, + 0.75, 1.00, 100, 0, 0); /* white */ + analogtv_draw_solid_rel_lcp(input, 2.0/6.0, 3.0/6.0, + 0.75, 1.00, 7, 40, 33); /* +Q */ + analogtv_draw_solid_rel_lcp(input, 3.0/6.0, 4.0/6.0, + 0.75, 1.00, 7, 0, 0); /* black */ + analogtv_draw_solid_rel_lcp(input, 12.0/18.0, 13.0/18.0, + 0.75, 1.00, 3, 0, 0); /* black -4 */ + analogtv_draw_solid_rel_lcp(input, 13.0/18.0, 14.0/18.0, + 0.75, 1.00, 7, 0, 0); /* black */ + analogtv_draw_solid_rel_lcp(input, 14.0/18.0, 15.0/18.0, + 0.75, 1.00, 11, 0, 0); /* black +4 */ + analogtv_draw_solid_rel_lcp(input, 5.0/6.0, 6.0/6.0, + 0.75, 1.00, 7, 0, 0); /* black */ + + + ypos=ANALOGTV_V/5; + xpos=ANALOGTV_VIS_START + ANALOGTV_VIS_LEN/2; + + { + char localname[256]; + if (gethostname (localname, sizeof (localname))==0) { + localname[sizeof(localname)-1]=0; /* "The returned name is null- + terminated unless insufficient + space is provided" */ + localname[24]=0; /* limit length */ + + analogtv_draw_string_centered(input, &ugly_font, localname, + xpos, ypos, black_ntsc); + } + } + ypos += ugly_font.char_h*5/2; + + analogtv_draw_xpm(tv, input, + logo_50_xpm, xpos - 100, ypos); + + ypos += 58; + +#if 0 + analogtv_draw_string_centered(input, &ugly_font, "Please Stand By", xpos, ypos); + ypos += ugly_font.char_h*4; +#endif + + { + char timestamp[256]; + time_t t = time ((time_t *) 0); + struct tm *tm = localtime (&t); + + /* Y2K: It is OK for this to use a 2-digit year because it's simulating a + TV display and is purely decorative. */ + strftime(timestamp, sizeof(timestamp)-1, "%y.%m.%d %H:%M:%S ", tm); + analogtv_draw_string_centered(input, &ugly_font, timestamp, + xpos, ypos, black_ntsc); + } + + + input->next_update_time += 1.0; +} + +#if 0 +static void +draw_color_square(analogtv_input *input) +{ + double xs,ys; + + analogtv_draw_solid_rel_lcp(input, 0.0, 1.0, 0.0, 1.0, + 30.0, 0.0, 0.0); + + for (xs=0.0; xs<0.9999; xs+=1.0/15.0) { + analogtv_draw_solid_rel_lcp(input, xs, xs, 0.0, 1.0, + 100.0, 0.0, 0.0); + } + + for (ys=0.0; ys<0.9999; ys+=1.0/11.0) { + analogtv_draw_solid_rel_lcp(input, 0.0, 1.0, ys, ys, + 100.0, 0.0, 0.0); + } + + for (ys=0.0; ys<0.9999; ys+=0.01) { + + analogtv_draw_solid_rel_lcp(input, 0.0/15, 1.0/15, ys, ys+0.01, + 40.0, 45.0, 103.5*(1.0-ys) + 347.0*ys); + + analogtv_draw_solid_rel_lcp(input, 14.0/15, 15.0/15, ys, ys+0.01, + 40.0, 45.0, 103.5*(ys) + 347.0*(1.0-ys)); + } + + for (ys=0.0; ys<0.9999; ys+=0.02) { + analogtv_draw_solid_rel_lcp(input, 1.0/15, 2.0/15, ys*2.0/11.0+1.0/11.0, + (ys+0.01)*2.0/11.0+1.0/11.0, + 100.0*(1.0-ys), 0.0, 0.0); + } + + +} +#endif + +char *progclass = "XAnalogTV"; + +char *defaults [] = { + "*delay: 5", + ANALOGTV_DEFAULTS + 0, +}; + +XrmOptionDescRec options [] = { + { "-delay", ".delay", XrmoptionSepArg, 0 }, + ANALOGTV_OPTIONS + { 0, 0, 0, 0 } +}; + + +#ifdef USE_TEST_PATTERNS + +#include "images/earth.xpm" + +char **test_patterns[] = { + earth_xpm, +}; + +#endif + + +enum { + N_CHANNELS=12, /* Channels 2 through 13 on VHF */ + MAX_MULTICHAN=2 +}; + +typedef struct chansetting_s { + + analogtv_reception recs[MAX_MULTICHAN]; + double noise_level; + + int dur; +} chansetting; + +static struct timeval basetime; + +static int +getticks(void) +{ + struct timeval tv; + gettimeofday(&tv,NULL); + return ((tv.tv_sec - basetime.tv_sec)*1000 + + (tv.tv_usec - basetime.tv_usec)/1000); +} + +int +analogtv_load_random_image(analogtv *it, analogtv_input *input) +{ + Pixmap pixmap; + XImage *image=NULL; + int width=ANALOGTV_PIC_LEN; + int height=width*3/4; + int rc; + + pixmap=XCreatePixmap(it->dpy, it->window, width, height, it->visdepth); + XSync(it->dpy, False); + load_random_image(it->screen, it->window, pixmap, NULL); + image = XGetImage(it->dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap); + XFreePixmap(it->dpy, pixmap); + + /* Make sure the window's background is not set to None, and get the + grabbed bits (if any) off it as soon as possible. */ + XSetWindowBackground (it->dpy, it->window, + get_pixel_resource ("background", "Background", + it->dpy, it->xgwa.colormap)); + XClearWindow (it->dpy, it->window); + + analogtv_setup_sync(input, 1, (random()%20)==0); + rc=analogtv_load_ximage(it, input, image); + if (image) XDestroyImage(image); + XSync(it->dpy, False); + return rc; +} + +int +analogtv_load_xpm(analogtv *it, analogtv_input *input, char **xpm) +{ + Pixmap pixmap; + XImage *image; + int width,height; + int rc; + + pixmap=xpm_data_to_pixmap (it->dpy, it->window, xpm, + &width, &height, NULL); + image = XGetImage(it->dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap); + XFreePixmap(it->dpy, pixmap); + rc=analogtv_load_ximage(it, input, image); + if (image) XDestroyImage(image); + XSync(it->dpy, False); + return rc; +} + +enum { MAX_STATIONS = 6 }; +static int n_stations; +static analogtv_input *stations[MAX_STATIONS]; + + +void add_stations(void) +{ + while (n_stations < MAX_STATIONS) { + analogtv_input *input=analogtv_input_allocate(); + stations[n_stations++]=input; + + if (n_stations==1) { + input->updater = update_smpte_colorbars; + input->do_teletext=1; + } +#ifdef USE_TEST_PATTERNS + else if (random()%5==0) { + j=random()%countof(test_patterns); + analogtv_setup_sync(input); + analogtv_load_xpm(tv, input, test_patterns[j]); + analogtv_setup_teletext(input); + } +#endif + else { + analogtv_load_random_image(tv, input); + input->do_teletext=1; + } + } +} + +void +screenhack (Display *dpy, Window window) +{ + int i; + int curinputi; + int change_ticks; + int using_mouse=0; + int change_now; + chansetting chansettings[N_CHANNELS]; + chansetting *cs; + int last_station=42; + int delay = get_integer_resource("delay", "Integer"); + if (delay < 1) delay = 1; + + analogtv_make_font(dpy, window, &ugly_font, 7, 10, "6x10"); + + tv=analogtv_allocate(dpy, window); + tv->event_handler = screenhack_handle_event; + + add_stations(); + + analogtv_set_defaults(tv, ""); + tv->need_clear=1; + + if (random()%4==0) { + tv->tint_control += pow(frand(2.0)-1.0, 7) * 180.0; + } + if (1) { + tv->color_control += frand(0.3); + } + + for (i=0; iinput = stations[station]; + rec->level = pow(frand(1.0), 3.0) * 2.0 + 0.05; + rec->ofs=random()%ANALOGTV_SIGNAL_LEN; + if (random()%3) { + rec->multipath = frand(1.0); + } else { + rec->multipath=0.0; + } + if (stati) { + /* We only set a frequency error for ghosting stations, + because it doesn't matter otherwise */ + rec->freqerr = (frand(2.0)-1.0) * 3.0; + } + + if (rec->level > 0.3) break; + if (random()%4) break; + } + } + } + + gettimeofday(&basetime,NULL); + + curinputi=0; + cs=&chansettings[curinputi]; + change_ticks = cs->dur + 1500; + + tv->powerup=0.0; + while (1) { + int curticks=getticks(); + double curtime=curticks*0.001; + + change_now=0; + if (analogtv_handle_events(tv)) { + using_mouse=1; + change_now=1; + } + if (change_now || (!using_mouse && curticks>=change_ticks + && tv->powerup > 10.0)) { + curinputi=(curinputi+1)%N_CHANNELS; + cs=&chansettings[curinputi]; + change_ticks = curticks + cs->dur; + /* Set channel change noise flag */ + tv->channel_change_cycles=200000; + } + + for (i=0; irecs[i]; + analogtv_input *inp=rec->input; + if (!inp) continue; + + if (inp->updater) { + inp->next_update_time = curtime; + (inp->updater)(inp); + } + rec->ofs += rec->freqerr; + } + + tv->powerup=curtime; + + analogtv_init_signal(tv, cs->noise_level); + for (i=0; irecs[i]; + analogtv_input *inp=rec->input; + if (!inp) continue; + + analogtv_reception_update(rec); + analogtv_add_signal(tv, rec); + } + analogtv_draw(tv); + } + + XSync(dpy, False); + XClearWindow(dpy, window); + + if (tv) analogtv_release(tv); +} + diff --git a/hacks/xanalogtv.man b/hacks/xanalogtv.man new file mode 100644 index 00000000..15b4c993 --- /dev/null +++ b/hacks/xanalogtv.man @@ -0,0 +1,86 @@ +.TH XScreenSaver 1 "10-Oct-03" "X Version 11" +.SH NAME +xanalogtv - Simulate reception on an old analog TV set +.SH SYNOPSIS +.B xanalogtv +[\-display \fIhost:display.screen\fP] [\-window] [\-root] [\-install] +[\-visual \fIvisual\fP] +[\-cycle] [\-no-cycle] +.SH DESCRIPTION +.I xanalogtv +shows a simulation of an old TV set showing test patterns and any +other images you have provided. It reproduces a wide range of TV +reception bummage: snow, bloom, ghosting, and loss of vertical and +horizontal sync. It also simulates the TV warming up. It will cycle +through 12 channels, some with images you give it, and some with color +bars or nothing but static. +.PP +The images that it uses will be grabbed from the portion of the screen +underlying the window, or from the system's video input, or from a +random file on disk, as indicated by the \fIgrabDesktopImages\fP, +\fIgrabVideoFrames\fP, and \fIchooseRandomImages\fP options in the +\fI~/.xscreensaver\fP file; see +.BR xscreensaver-demo (1) +for more details. It looks best with a video input or +your digital photo collection. +.PP +.SH OPTIONS +.I xanalogtv +accepts the following options: +.TP 8 +.B \-window +Draw on a newly-created window. This is the default. +.TP 8 +.B \-root +Draw on the root window. +.TP 8 +.B \-install +Install a private colormap for the window. +.TP 8 +.B \-visual \fIvisual\fP +Specify which visual to use. Legal values are the name of a visual class, +or the id number (decimal or hex) of a specific visual. +.TP 8 +.B \-cycle +Cycle through all the available modes. This is the default. +.TP 8 +.B \-no-cycle +Don't cycle modes. +.SH ENVIRONMENT +.PP +.TP 8 +.B DISPLAY +to get the default host and display number. +.TP 8 +.B XENVIRONMENT +to get the name of a resource file that overrides the global resources +stored in the RESOURCE_MANAGER property. +.SH X RESOURCES +Notable X resources supported include the following which correspond +to standard TV controls: +.BR analogTVTint , +.BR analogTVColor , +.BR analogTVBrightness , +and +.BR analogTVContrast . +They range from 0 to 100, except for tint which is an angle +between -180 and +180. + +.SH SEE ALSO +.BR X (1), +.BR xteevee (1), +.BR apple2 (1), +.BR bsod (1), +.BR xscreensaver (1), +.BR xscreensaver\-demo (1), +.BR xscreensaver\-getimage (1) +.SH COPYRIGHT +Copyright \(co 2003 by Trevor Blackwell. 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 +Trevor Blackwell diff --git a/hacks/xflame.c b/hacks/xflame.c index 622a59de..b492561a 100755 --- a/hacks/xflame.c +++ b/hacks/xflame.c @@ -718,6 +718,8 @@ char *defaults [] = { }; XrmOptionDescRec options [] = { + { "-foreground",".foreground", XrmoptionSepArg, 0 }, + { "-fg", ".foreground", XrmoptionSepArg, 0 }, { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-bitmap", ".bitmap", XrmoptionSepArg, 0 }, { "-baseline", ".bitmapBaseline", XrmoptionSepArg, 0 }, diff --git a/hacks/xflame.man b/hacks/xflame.man index 3fc5c2c1..ecfa04b5 100644 --- a/hacks/xflame.man +++ b/hacks/xflame.man @@ -4,7 +4,9 @@ xflame - draws animated flames .SH SYNOPSIS .B xflame [\-display \fIhost:display.screen\fP] [\-window] [\-root] [\-install] -[\-visual \fIvisual\fP] [\-hspread \fIint\fP] [\-vspread \fIint\fP] +[\-visual \fIvisual\fP] +[\-foreground \fIcolor\fP] +[\-hspread \fIint\fP] [\-vspread \fIint\fP] [\-residual \fIint\fP] [\-variance \fIint\fP] [\-vartrend \fIint\fP] [\-bloom \| \-no\-bloom] [\-bitmap \fIxbm\-file\fP] [\-baseline \fIint\fP] @@ -29,6 +31,9 @@ Install a private colormap for the window. Specify which visual to use. Legal values are the name of a visual class, or the id number (decimal or hex) of a specific visual. .TP 8 +.B \-foreground \fIcolor\fP\fP or \fB\-fg\fP \fIcolor\fP\fP +The color of the flames; default red. (The background color is always black.) +.TP 8 .B \-bitmap \fIfilename\fP\fP Specifies the bitmap file to use (a monochrome XBM file.) The name "none" means not to use a bitmap at all. diff --git a/hacks/xsublim.c b/hacks/xsublim.c index f2036f10..0bb91cb4 100644 --- a/hacks/xsublim.c +++ b/hacks/xsublim.c @@ -231,26 +231,7 @@ void xsublim_Sig_Catch(int sig_Number) Xsublim_Sig_Last = sig_Number; } -/* Get the screensaver's window ============================================ */ -static XErrorHandler Xsublim_Ss_Handler = NULL; -static int Xsublim_Ss_Status; - /* This was all basically swiped from driver/remote.c and util/vroot.h */ -static int xsublim_Ss_Handler(Display* handle_Display, - XErrorEvent* handle_Error) -{ - if (handle_Error->error_code == BadWindow) - { - Xsublim_Ss_Status = BadWindow; - return 0; - } - if (Xsublim_Ss_Handler == NULL) - { - fprintf(stderr,"%s: ",progname); - abort(); - } - return (*Xsublim_Ss_Handler)(handle_Display,handle_Error); -} static Window xsublim_Ss_GetWindow(Display* ss_Display) { Screen *s = DefaultScreenOfDisplay (ss_Display); diff --git a/hacks/xteevee.c b/hacks/xteevee.c index a1d6932f..11d1c036 100644 --- a/hacks/xteevee.c +++ b/hacks/xteevee.c @@ -364,7 +364,7 @@ void screenhack(Display* x_Disp,Window x_Win) /* Grab the screen to give us time to do whatever we want */ XGetWindowAttributes(x_Disp,x_Win,&x_WinAttr); - load_random_image (x_WinAttr.screen, x_Win, x_Win); + load_random_image (x_WinAttr.screen, x_Win, x_Win, NULL); x_GcVal.subwindow_mode = IncludeInferiors; x_Gc = XCreateGC(x_Disp,x_Win,GCSubwindowMode,&x_GcVal); diff --git a/hacks/xteevee.man b/hacks/xteevee.man index 39b50248..d80fa39f 100644 --- a/hacks/xteevee.man +++ b/hacks/xteevee.man @@ -29,6 +29,10 @@ file; see .BR xscreensaver-demo (1) for more details. .PP +See also +.BR xanalogtv (1) +for a more sophisticated (but more graphics-intensive) implementation +of this concept. .SH OPTIONS .I xteevee accepts the following options: @@ -107,6 +111,9 @@ mode, as a percentage. should simulate more TV problems. .SH SEE ALSO .BR X (1), +.BR xanalogtv (1), +.BR apple2 (1), +.BR bsod (1), .BR xscreensaver (1), .BR xscreensaver\-demo (1), .BR xscreensaver\-getimage (1) diff --git a/hacks/zoom.c b/hacks/zoom.c index 8911c7ba..eb34ee37 100644 --- a/hacks/zoom.c +++ b/hacks/zoom.c @@ -102,7 +102,7 @@ static void init_hack(Display *dpy, Window window) orig_map = NULL; pm = XCreatePixmap(dpy, window, sizex, sizey, xgwa.depth); - load_random_image (xgwa.screen, window, pm); + load_random_image (xgwa.screen, window, pm, NULL); if (!lenses) { orig_map = XGetImage(dpy, pm, 0, 0, sizex, sizey, ~0L, ZPixmap); diff --git a/po/Makefile.in.in b/po/Makefile.in.in index 25faa4b2..c82f0066 100644 --- a/po/Makefile.in.in +++ b/po/Makefile.in.in @@ -279,7 +279,10 @@ maintainer-clean: distclean rm -f $(GMOFILES) depend: -distdepend: generate_potfiles_in update-po $(DISTFILES) +# fuck off. love, jwz. +#distdepend: generate_potfiles_in update-po $(DISTFILES) +distdepend:: + # jwz: Generates po/POTFILES.in by examining the source tree: # that way we don't have to keep this list up to date as files are added. diff --git a/po/POTFILES.in b/po/POTFILES.in index 3f12a9f8..0ef28022 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,191 +1,191 @@ -# Auto-generated: Sun Sep 7 17:00:03 PDT 2003 +# Auto-generated: Mon Oct 13 17:43:29 PDT 2003 driver/demo-Gtk.c driver/demo-Gtk-conf.c driver/demo-Gtk-support.c driver/demo-Gtk-widgets.c -driver/screensaver-properties.desktop.in.h -driver/screensaver-properties.desktop.in.h -driver/xscreensaver-demo.glade.h -driver/xscreensaver-demo.glade2.h -driver/xscreensaver-demo.glade2.h -driver/xscreensaver-demo.glade.h -hacks/config/anemone.xml.h -hacks/config/antspotlight.xml.h -hacks/config/ant.xml.h -hacks/config/apollonian.xml.h -hacks/config/atlantis.xml.h -hacks/config/attraction.xml.h -hacks/config/atunnel.xml.h -hacks/config/barcode.xml.h -hacks/config/blaster.xml.h -hacks/config/blitspin.xml.h -hacks/config/blocktube.xml.h -hacks/config/bouboule.xml.h -hacks/config/bouncingcow.xml.h -hacks/config/boxed.xml.h -hacks/config/braid.xml.h -hacks/config/bsod.xml.h -hacks/config/bubble3d.xml.h -hacks/config/bubbles.xml.h -hacks/config/bumps.xml.h -hacks/config/cage.xml.h -hacks/config/ccurve.xml.h -hacks/config/circuit.xml.h -hacks/config/cloudlife.xml.h -hacks/config/compass.xml.h -hacks/config/coral.xml.h -hacks/config/cosmos.xml.h -hacks/config/critical.xml.h -hacks/config/crystal.xml.h -hacks/config/cubenetic.xml.h -hacks/config/cubestorm.xml.h -hacks/config/cynosure.xml.h -hacks/config/dangerball.xml.h -hacks/config/decayscreen.xml.h -hacks/config/deco.xml.h -hacks/config/deluxe.xml.h -hacks/config/demon.xml.h -hacks/config/discrete.xml.h -hacks/config/distort.xml.h -hacks/config/drift.xml.h -hacks/config/electricsheep.xml.h -hacks/config/endgame.xml.h -hacks/config/engine.xml.h -hacks/config/epicycle.xml.h -hacks/config/eruption.xml.h -hacks/config/euler2d.xml.h -hacks/config/extrusion.xml.h -hacks/config/fadeplot.xml.h -hacks/config/fireflies.xml.h -hacks/config/flag.xml.h -hacks/config/flame.xml.h -hacks/config/flipflop.xml.h -hacks/config/flipscreen3d.xml.h -hacks/config/flow.xml.h -hacks/config/fluidballs.xml.h -hacks/config/flurry.xml.h -hacks/config/flyingtoasters.xml.h -hacks/config/forest.xml.h -hacks/config/galaxy.xml.h -hacks/config/gears.xml.h -hacks/config/gflux.xml.h -hacks/config/glblur.xml.h -hacks/config/glforestfire.xml.h -hacks/config/glknots.xml.h -hacks/config/glmatrix.xml.h -hacks/config/glplanet.xml.h -hacks/config/glslideshow.xml.h -hacks/config/glsnake.xml.h -hacks/config/gltext.xml.h -hacks/config/goban.xml.h -hacks/config/goop.xml.h -hacks/config/grav.xml.h -hacks/config/greynetic.xml.h -hacks/config/halftone.xml.h -hacks/config/halo.xml.h -hacks/config/helix.xml.h -hacks/config/hopalong.xml.h -hacks/config/hyperball.xml.h -hacks/config/hypercube.xml.h -hacks/config/hypertorus.xml.h -hacks/config/ifs.xml.h -hacks/config/imsmap.xml.h -hacks/config/interference.xml.h -hacks/config/jigglypuff.xml.h -hacks/config/jigsaw.xml.h -hacks/config/juggle.xml.h -hacks/config/julia.xml.h -hacks/config/kaleidescope.xml.h -hacks/config/klein.xml.h -hacks/config/kumppa.xml.h -hacks/config/lament.xml.h -hacks/config/laser.xml.h -hacks/config/lavalite.xml.h -hacks/config/lightning.xml.h -hacks/config/lisa.xml.h -hacks/config/lissie.xml.h -hacks/config/lmorph.xml.h -hacks/config/loop.xml.h -hacks/config/maze.xml.h -hacks/config/menger.xml.h -hacks/config/metaballs.xml.h -hacks/config/moebius.xml.h -hacks/config/moire2.xml.h -hacks/config/moire.xml.h -hacks/config/molecule.xml.h -hacks/config/morph3d.xml.h -hacks/config/mountain.xml.h -hacks/config/munch.xml.h -hacks/config/nerverot.xml.h -hacks/config/noseguy.xml.h -hacks/config/pedal.xml.h -hacks/config/penetrate.xml.h -hacks/config/penrose.xml.h -hacks/config/petri.xml.h -hacks/config/phosphor.xml.h -hacks/config/piecewise.xml.h -hacks/config/pipes.xml.h -hacks/config/polyominoes.xml.h -hacks/config/polytopes.xml.h -hacks/config/popsquares.xml.h -hacks/config/pulsar.xml.h -hacks/config/pyro.xml.h -hacks/config/qix.xml.h -hacks/config/queens.xml.h -hacks/config/rd-bomb.xml.h -hacks/config/ripples.xml.h -hacks/config/rocks.xml.h -hacks/config/rorschach.xml.h -hacks/config/rotor.xml.h -hacks/config/rotzoomer.xml.h -hacks/config/rubik.xml.h -hacks/config/sballs.xml.h -hacks/config/shadebobs.xml.h -hacks/config/sierpinski3d.xml.h -hacks/config/sierpinski.xml.h -hacks/config/slidescreen.xml.h -hacks/config/slip.xml.h -hacks/config/sonar.xml.h -hacks/config/speedmine.xml.h -hacks/config/sphereEversion.xml.h -hacks/config/spheremonics.xml.h -hacks/config/sphere.xml.h -hacks/config/spiral.xml.h -hacks/config/spotlight.xml.h -hacks/config/sproingies.xml.h -hacks/config/squiral.xml.h -hacks/config/ssystem.xml.h -hacks/config/stairs.xml.h -hacks/config/starfish.xml.h -hacks/config/starwars.xml.h -hacks/config/stonerview.xml.h -hacks/config/strange.xml.h -hacks/config/superquadrics.xml.h -hacks/config/swirl.xml.h -hacks/config/t3d.xml.h -hacks/config/thornbird.xml.h -hacks/config/triangle.xml.h -hacks/config/truchet.xml.h -hacks/config/twang.xml.h -hacks/config/vermiculate.xml.h -hacks/config/vidwhacker.xml.h -hacks/config/vines.xml.h -hacks/config/wander.xml.h -hacks/config/webcollage.xml.h -hacks/config/whirlwindwarp.xml.h -hacks/config/whirlygig.xml.h -hacks/config/worm.xml.h -hacks/config/xaos.xml.h -hacks/config/xdaliclock.xml.h -hacks/config/xearth.xml.h -hacks/config/xfishtank.xml.h -hacks/config/xflame.xml.h -hacks/config/xjack.xml.h -hacks/config/xlyap.xml.h -hacks/config/xmatrix.xml.h -hacks/config/xmountains.xml.h -hacks/config/xrayswarm.xml.h -hacks/config/xsnow.xml.h -hacks/config/xspirograph.xml.h -hacks/config/xteevee.xml.h -hacks/config/zoom.xml.h +driver/screensaver-properties.desktop.in +driver/xscreensaver-demo.glade +driver/xscreensaver-demo.glade2 +hacks/config/anemone.xml +hacks/config/antspotlight.xml +hacks/config/ant.xml +hacks/config/apollonian.xml +hacks/config/apple2.xml +hacks/config/atlantis.xml +hacks/config/attraction.xml +hacks/config/atunnel.xml +hacks/config/barcode.xml +hacks/config/blaster.xml +hacks/config/blitspin.xml +hacks/config/blocktube.xml +hacks/config/bouboule.xml +hacks/config/bouncingcow.xml +hacks/config/boxed.xml +hacks/config/braid.xml +hacks/config/bsod.xml +hacks/config/bubble3d.xml +hacks/config/bubbles.xml +hacks/config/bumps.xml +hacks/config/cage.xml +hacks/config/ccurve.xml +hacks/config/circuit.xml +hacks/config/cloudlife.xml +hacks/config/compass.xml +hacks/config/coral.xml +hacks/config/cosmos.xml +hacks/config/critical.xml +hacks/config/crystal.xml +hacks/config/cubenetic.xml +hacks/config/cubestorm.xml +hacks/config/cynosure.xml +hacks/config/dangerball.xml +hacks/config/decayscreen.xml +hacks/config/deco.xml +hacks/config/deluxe.xml +hacks/config/demon.xml +hacks/config/discrete.xml +hacks/config/distort.xml +hacks/config/drift.xml +hacks/config/electricsheep.xml +hacks/config/endgame.xml +hacks/config/engine.xml +hacks/config/epicycle.xml +hacks/config/eruption.xml +hacks/config/euler2d.xml +hacks/config/extrusion.xml +hacks/config/fadeplot.xml +hacks/config/fireflies.xml +hacks/config/flag.xml +hacks/config/flame.xml +hacks/config/flipflop.xml +hacks/config/flipscreen3d.xml +hacks/config/flow.xml +hacks/config/fluidballs.xml +hacks/config/flurry.xml +hacks/config/flyingtoasters.xml +hacks/config/fontglide.xml +hacks/config/forest.xml +hacks/config/galaxy.xml +hacks/config/gears.xml +hacks/config/gflux.xml +hacks/config/glblur.xml +hacks/config/glforestfire.xml +hacks/config/glknots.xml +hacks/config/glmatrix.xml +hacks/config/glplanet.xml +hacks/config/glslideshow.xml +hacks/config/glsnake.xml +hacks/config/gltext.xml +hacks/config/goban.xml +hacks/config/goop.xml +hacks/config/grav.xml +hacks/config/greynetic.xml +hacks/config/halftone.xml +hacks/config/halo.xml +hacks/config/helix.xml +hacks/config/hopalong.xml +hacks/config/hyperball.xml +hacks/config/hypercube.xml +hacks/config/hypertorus.xml +hacks/config/ifs.xml +hacks/config/imsmap.xml +hacks/config/interference.xml +hacks/config/jigglypuff.xml +hacks/config/jigsaw.xml +hacks/config/juggle.xml +hacks/config/julia.xml +hacks/config/kaleidescope.xml +hacks/config/klein.xml +hacks/config/kumppa.xml +hacks/config/lament.xml +hacks/config/laser.xml +hacks/config/lavalite.xml +hacks/config/lightning.xml +hacks/config/lisa.xml +hacks/config/lissie.xml +hacks/config/lmorph.xml +hacks/config/loop.xml +hacks/config/maze.xml +hacks/config/menger.xml +hacks/config/metaballs.xml +hacks/config/moebius.xml +hacks/config/moire2.xml +hacks/config/moire.xml +hacks/config/molecule.xml +hacks/config/morph3d.xml +hacks/config/mountain.xml +hacks/config/munch.xml +hacks/config/nerverot.xml +hacks/config/noseguy.xml +hacks/config/pedal.xml +hacks/config/penetrate.xml +hacks/config/penrose.xml +hacks/config/petri.xml +hacks/config/phosphor.xml +hacks/config/piecewise.xml +hacks/config/pipes.xml +hacks/config/polyominoes.xml +hacks/config/polytopes.xml +hacks/config/popsquares.xml +hacks/config/pulsar.xml +hacks/config/pyro.xml +hacks/config/qix.xml +hacks/config/queens.xml +hacks/config/rd-bomb.xml +hacks/config/ripples.xml +hacks/config/rocks.xml +hacks/config/rorschach.xml +hacks/config/rotor.xml +hacks/config/rotzoomer.xml +hacks/config/rubik.xml +hacks/config/sballs.xml +hacks/config/shadebobs.xml +hacks/config/sierpinski3d.xml +hacks/config/sierpinski.xml +hacks/config/slidescreen.xml +hacks/config/slip.xml +hacks/config/sonar.xml +hacks/config/speedmine.xml +hacks/config/sphereEversion.xml +hacks/config/spheremonics.xml +hacks/config/sphere.xml +hacks/config/spiral.xml +hacks/config/spotlight.xml +hacks/config/sproingies.xml +hacks/config/squiral.xml +hacks/config/ssystem.xml +hacks/config/stairs.xml +hacks/config/starfish.xml +hacks/config/starwars.xml +hacks/config/stonerview.xml +hacks/config/strange.xml +hacks/config/superquadrics.xml +hacks/config/swirl.xml +hacks/config/t3d.xml +hacks/config/thornbird.xml +hacks/config/triangle.xml +hacks/config/truchet.xml +hacks/config/twang.xml +hacks/config/vermiculate.xml +hacks/config/vidwhacker.xml +hacks/config/vines.xml +hacks/config/wander.xml +hacks/config/webcollage.xml +hacks/config/whirlwindwarp.xml +hacks/config/whirlygig.xml +hacks/config/worm.xml +hacks/config/xanalogtv.xml +hacks/config/xaos.xml +hacks/config/xdaliclock.xml +hacks/config/xearth.xml +hacks/config/xfishtank.xml +hacks/config/xflame.xml +hacks/config/xjack.xml +hacks/config/xlyap.xml +hacks/config/xmatrix.xml +hacks/config/xmountains.xml +hacks/config/xrayswarm.xml +hacks/config/xsnow.xml +hacks/config/xspirograph.xml +hacks/config/xteevee.xml +hacks/config/zoom.xml diff --git a/po/ca.po b/po/ca.po index 1263c134..5396e455 100644 --- a/po/ca.po +++ b/po/ca.po @@ -11,7 +11,7 @@ msgstr "" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" +"Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" #: driver/demo-Gtk-conf.c:731 diff --git a/po/fi.po b/po/fi.po index b1f5f457..23ec69a9 100644 --- a/po/fi.po +++ b/po/fi.po @@ -11,7 +11,7 @@ msgstr "" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" +"Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" #: driver/demo-Gtk-conf.c:731 diff --git a/po/it.po b/po/it.po index 7978e2f7..fe677639 100644 --- a/po/it.po +++ b/po/it.po @@ -11,7 +11,7 @@ msgstr "" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" +"Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" #: driver/demo-Gtk-conf.c:731 diff --git a/po/pt_BR.po b/po/pt_BR.po index 1454e1ac..deb65d2d 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -11,7 +11,7 @@ msgstr "" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" "Content-Transfer-Encoding: 8bit\n" #: driver/demo-Gtk-conf.c:731 diff --git a/setup.com b/setup.com index 81a68fc0..b6eb530a 100644 --- a/setup.com +++ b/setup.com @@ -6,6 +6,7 @@ $ mydir = mydisk+f$directory() $ anemone :== $'mydir'anemone $ ant :== $'mydir'ant $ apollonian :== $'mydir'apollonian +$ apple2 :== $'mydir'apple2 $ attraction :== $'mydir'attraction $ barcode :== $'mydir'barcode $ blaster :== $'mydir'blaster @@ -37,6 +38,7 @@ $ flag :== $'mydir'flag $ flame :== $'mydir'flame $ flow :== $'mydir'flow $ fluidballs :== $'mydir'fluidballs +$ fontglide :== $'mydir'fontglide $ forest :== $'mydir'forest $ galaxy :== $'mydir'galaxy $ goop :== $'mydir'goop @@ -77,6 +79,7 @@ $ petri :== $'mydir'petri $ phosphor :== $'mydir'phosphor $ piecewise :== $'mydir'piecewise $ polyominoes :== $'mydir'polyominoes +$ pong :== $'mydir'pong $ popsquares :== $'mydir'popsquares $ pyro :== $'mydir'pyro $ qix :== $'mydir'qix @@ -111,6 +114,7 @@ $ webcollage-helper :== $'mydir'webcollage-helper $ whirlwindwarp :== $'mydir'whirlwindwarp $ whirlygig :== $'mydir'whirlygig $ worm :== $'mydir'worm +$ xanalogtv :== $'mydir'xanalogtv $ xflame :== $'mydir'xflame $ xjack :== $'mydir'xjack $ xlyap :== $'mydir'xlyap diff --git a/utils/grabclient.c b/utils/grabclient.c index 2c426be8..0568d9b9 100644 --- a/utils/grabclient.c +++ b/utils/grabclient.c @@ -104,7 +104,7 @@ static void checkerboard (Screen *screen, Drawable drawable) { Display *dpy = DisplayOfScreen (screen); - int x, y; + unsigned int x, y; int size = 24; XColor fg, bg; XGCValues gcv; @@ -180,7 +180,8 @@ hack_subproc_environment (Display *dpy) When grabbing desktop images, the Window will be unmapped first. */ void -load_random_image (Screen *screen, Window window, Drawable drawable) +load_random_image (Screen *screen, Window window, Drawable drawable, + char **name_ret) { Display *dpy = DisplayOfScreen (screen); char *grabber = get_string_resource ("desktopGrabber", "DesktopGrabber"); @@ -218,4 +219,24 @@ load_random_image (Screen *screen, Window window, Drawable drawable) system (cmd); free (cmd); XSync (dpy, True); + + if (name_ret) + { + Atom type; + int format; + unsigned long nitems, bytesafter; + char *name=NULL; + + *name_ret = NULL; + + if (XGetWindowProperty (dpy, window, + XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, + False), + 0, 1024, False, XA_STRING, + &type, &format, &nitems, &bytesafter, + (unsigned char **) &name) + == Success + && type != None) + *name_ret = strdup(name); + } } diff --git a/utils/grabscreen.c b/utils/grabscreen.c index d9a52097..f9ce753c 100644 --- a/utils/grabscreen.c +++ b/utils/grabscreen.c @@ -223,7 +223,7 @@ use_subwindow_mode_p(Screen *screen, Window window) static void install_screen_colormaps (Screen *screen) { - int i; + unsigned int i; Display *dpy = DisplayOfScreen (screen); Window real_root; Window parent, *kids = 0; @@ -260,7 +260,7 @@ install_screen_colormaps (Screen *screen) void -grab_screen_image (Screen *screen, Window window) +grab_screen_image_internal (Screen *screen, Window window) { Display *dpy = DisplayOfScreen (screen); XWindowAttributes xgwa; diff --git a/utils/grabscreen.h b/utils/grabscreen.h index cf8a96ef..3d9b16eb 100644 --- a/utils/grabscreen.h +++ b/utils/grabscreen.h @@ -25,16 +25,16 @@ desktop, or from the system's video input, depending on user preferences. + If it is from a file, then it will be returned in `filename_return'. + filename_return may be NULL; also, NULL may be returned (e.g., if + it's a screenshot or video capture.) + Many colors may be allocated from the window's colormap. */ extern void load_random_image (Screen *screen, Window top_level_window, - Drawable target_window_or_pixmap); - - -/* Uh, don't call this. */ -extern void grab_screen_image (Screen *, Window); - + Drawable target_window_or_pixmap, + char **filename_return); /* Whether one should use GCSubwindowMode when drawing on this window (assuming a screen image has been grabbed onto it.) Yes, this is a @@ -49,4 +49,12 @@ extern Bool use_subwindow_mode_p(Screen *screen, Window window); */ extern Bool top_level_window_p(Screen *screen, Window window); + +/* Don't call this: this is for the "xscreensaver-getimage" program only. */ +extern void grab_screen_image_internal (Screen *, Window); + +/* Don't use this: this is how "xscreensaver-getimage" and "grabclient.c" + pass the file name around. */ +#define XA_XSCREENSAVER_IMAGE_FILENAME "_SCREENSAVER_IMAGE_FILENAME" + #endif /* __GRABSCREEN_H__ */ diff --git a/utils/usleep.c b/utils/usleep.c index 70667591..6eec9a5f 100644 --- a/utils/usleep.c +++ b/utils/usleep.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1992, 1996, 1997 +/* xscreensaver, Copyright (c) 1992, 1996, 1997, 2003 * Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its @@ -22,6 +22,10 @@ # include #endif +#ifdef HAVE_UNISTD_H +# include +#endif + #if defined(VMS) # include # include diff --git a/utils/version.h b/utils/version.h index ca230993..9efafdef 100644 --- a/utils/version.h +++ b/utils/version.h @@ -1,2 +1,2 @@ static const char screensaver_id[] = - "@(#)xscreensaver 4.13 (07-Sep-2003), by Jamie Zawinski (jwz@jwz.org)"; + "@(#)xscreensaver 4.14 (25-Oct-2003), by Jamie Zawinski (jwz@jwz.org)"; diff --git a/xscreensaver.lsm b/xscreensaver.lsm index 1bb616dc..3b58b8ae 100644 --- a/xscreensaver.lsm +++ b/xscreensaver.lsm @@ -1,25 +1,25 @@ Begin3 Title: xscreensaver -Version: 4.13 -Entered-date: 07SEP03 +Version: 4.14 +Entered-date: 25OCT03 Description: A modular screen saver and locker for the X Window System. Highly customizable: allows the use of any program that can draw on the root window as a display mode. - More than 160 display modes are included in this package. + More than 175 display modes are included in this package. Keywords: screen saver, screen lock, lock, xlock, X11 Author: jwz@jwz.org (Jamie Zawinski) Maintained-by: jwz@jwz.org (Jamie Zawinski) Primary-site: http://www.jwz.org/xscreensaver/ - xscreensaver-4.13.tar.gz - 65K xscreensaver.README + 3988K xscreensaver-4.14.tar.gz + 66K xscreensaver.README 1K xscreensaver.lsm Alternate-site: sunsite.unc.edu /pub/Linux/X11/screensavers/ - xscreensaver-4.13.tar.gz - 65K xscreensaver.README + 3988K xscreensaver-4.14.tar.gz + 66K xscreensaver.README 1K xscreensaver.lsm Alternate-site: ftp.x.org /contrib/applications/ - xscreensaver-4.13.tar.gz - 65K xscreensaver.README + 3988K xscreensaver-4.14.tar.gz + 66K xscreensaver.README 1K xscreensaver.lsm Platforms: Linux, Irix, SunOS, Solaris, HPUX, AIX, FreeBSD, NetBSD, BSDI, SCO, OSF1, Ultrix, VMS. diff --git a/xscreensaver.lsm.sh b/xscreensaver.lsm.sh index 552817dc..a0b59c91 100755 --- a/xscreensaver.lsm.sh +++ b/xscreensaver.lsm.sh @@ -27,7 +27,7 @@ Entered-date: $DATE Description: A modular screen saver and locker for the X Window System. Highly customizable: allows the use of any program that can draw on the root window as a display mode. - More than 160 display modes are included in this package. + More than 175 display modes are included in this package. Keywords: screen saver, screen lock, lock, xlock, X11 Author: jwz@jwz.org (Jamie Zawinski) Maintained-by: jwz@jwz.org (Jamie Zawinski) diff --git a/xscreensaver.spec b/xscreensaver.spec index 5c53dd0f..d51bffad 100644 --- a/xscreensaver.spec +++ b/xscreensaver.spec @@ -1,5 +1,5 @@ %define name xscreensaver -%define version 4.13 +%define version 4.14 %define release 1 %define serial 1 %define x11_prefix /usr/X11R6 @@ -34,7 +34,7 @@ Buildroot: %{_tmppath}/%{name}-%{version}-root A modular screen saver and locker for the X Window System. Highly customizable: allows the use of any program that can draw on the root window as a display mode. -More than 160 display modes are included in this package. +More than 175 display modes are included in this package. %{?USE_GL:See also the xscreensaver-gl package, which} %{?USE_GL:includes optional OpenGL display modes.} @@ -42,7 +42,7 @@ More than 160 display modes are included in this package. Un économiseur d'écran et verrouillage modulaire pour X-Window. Hautement configurable: permet l'utilisation de n'importe quel programme qui peut dessiner dans la fenêtre root. -Plus de 160 modes d'affichage sont inclus dans ce paquet. +Plus de 175 modes d'affichage sont inclus dans ce paquet. %{?USE_GL:Voir aussi le paquet xscreensaver-gl, qui inclut} %{?USE_GL:des modules optionnels OpenGL.} @@ -189,7 +189,3 @@ if [ -d $RPM_BUILD_ROOT-gl ]; then rm -r $RPM_BUILD_ROOT-gl ; fi # %{?USE_GL:%files -f exes-gl gl} %{?USE_GL:%defattr(-,root,root)} - -%changelog -* Wed Mar 05 2003 Eric Lassauge -- Updated for xscreensaver-4.08 with french translations -- 2.30.2