From 38886de0c3e9ea5729ef23e4c653fa2822f52e8f Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 6 Apr 2021 14:43:39 +0200 Subject: xscreensaver 6.00 --- driver/Makefile.in | 1119 ++++---- driver/XScreenSaver-Xm.ad | 2 +- driver/XScreenSaver.ad.in | 556 ++-- driver/XScreenSaver_Xm_ad.h | 108 + driver/XScreenSaver_ad.h | 534 ++++ driver/atoms.c | 70 + driver/atoms.h | 38 + driver/atomswm.c | 101 + driver/auth.h | 79 +- driver/blurb.c | 50 + driver/blurb.h | 11 + driver/clientmsg.c | 123 + driver/clientmsg.h | 19 + driver/demo-Gtk-conf.c | 24 +- driver/demo-Gtk.c | 535 ++-- driver/demo-Xm-widgets.c | 49 +- driver/demo-Xm.c | 166 +- driver/dialog.c | 2507 +++++++++++++++++ driver/dpms.c | 211 +- driver/exec.c | 32 +- driver/exts.c | 238 ++ driver/fade.c | 1749 ++++++++++++ driver/fade.h | 20 + driver/passwd-helper.c | 64 +- driver/passwd-kerberos.c | 45 +- driver/passwd-pam.c | 312 +-- driver/passwd-pwent.c | 110 +- driver/passwd.c | 319 +-- driver/prefs.c | 1702 +----------- driver/prefs.h | 30 +- driver/prefsw.c | 1561 +++++++++++ driver/remote.c | 288 +- driver/remote.h | 3 +- driver/screens.c | 592 +---- driver/screens.h | 37 + driver/screensaver-properties.desktop.in | 2 +- driver/setuid.c | 229 +- driver/splash.c | 917 ------- driver/subprocs.c | 1315 ++++----- driver/test-apm.c | 101 - driver/test-fade.c | 172 +- driver/test-grab.c | 146 +- driver/test-mlstring.c | 312 --- driver/test-passwd.c | 333 +-- driver/test-randr.c | 25 +- driver/test-screens.c | 31 +- driver/test-uid.c | 14 +- driver/test-vp.c | 24 +- driver/test-xdpms.c | 47 +- driver/test-xinerama.c | 26 +- driver/test-xinput.c | 306 +++ driver/test-xkb.c | 89 + driver/test-yarandom.c | 3 +- driver/timers.c | 1807 ------------- driver/types.h | 315 +-- driver/vms-getpwnam.c | 129 - driver/vms-hpwd.c | 75 - driver/vms-pwd.h | 48 - driver/vms-validate.c | 75 - driver/windows.c | 2134 ++++----------- driver/xdpyinfo.c | 112 +- driver/xinput.c | 381 +++ driver/xinput.h | 19 + driver/xscreensaver-auth.c | 340 +++ driver/xscreensaver-auth.man | 27 + driver/xscreensaver-command.c | 152 +- driver/xscreensaver-command.man | 268 +- driver/xscreensaver-getimage.c | 2000 -------------- driver/xscreensaver-gfx.c | 595 +++++ driver/xscreensaver-gfx.man | 28 + driver/xscreensaver-settings.man | 420 +++ driver/xscreensaver-systemd.c | 1090 ++++++-- driver/xscreensaver-systemd.man | 80 +- driver/xscreensaver.c | 4290 +++++++++++++++--------------- driver/xscreensaver.h | 136 +- driver/xscreensaver.man | 1180 ++++---- driver/xscreensaver.ui | 2994 +++++++++++++++++++++ driver/xset.c | 389 --- 78 files changed, 19674 insertions(+), 16906 deletions(-) create mode 100644 driver/XScreenSaver_Xm_ad.h create mode 100644 driver/XScreenSaver_ad.h create mode 100644 driver/atoms.c create mode 100644 driver/atoms.h create mode 100644 driver/atomswm.c create mode 100644 driver/blurb.c create mode 100644 driver/blurb.h create mode 100644 driver/clientmsg.c create mode 100644 driver/clientmsg.h create mode 100644 driver/dialog.c create mode 100644 driver/exts.c create mode 100644 driver/fade.c create mode 100644 driver/fade.h create mode 100644 driver/prefsw.c create mode 100644 driver/screens.h delete mode 100644 driver/splash.c delete mode 100644 driver/test-apm.c delete mode 100644 driver/test-mlstring.c create mode 100644 driver/test-xinput.c create mode 100644 driver/test-xkb.c delete mode 100644 driver/timers.c delete mode 100644 driver/vms-getpwnam.c delete mode 100644 driver/vms-hpwd.c delete mode 100644 driver/vms-pwd.h delete mode 100644 driver/vms-validate.c create mode 100644 driver/xinput.c create mode 100644 driver/xinput.h create mode 100644 driver/xscreensaver-auth.c create mode 100644 driver/xscreensaver-auth.man delete mode 100644 driver/xscreensaver-getimage.c create mode 100644 driver/xscreensaver-gfx.c create mode 100644 driver/xscreensaver-gfx.man create mode 100644 driver/xscreensaver-settings.man create mode 100644 driver/xscreensaver.ui delete mode 100644 driver/xset.c (limited to 'driver') diff --git a/driver/Makefile.in b/driver/Makefile.in index 7baf504..0e986a9 100644 --- a/driver/Makefile.in +++ b/driver/Makefile.in @@ -1,9 +1,9 @@ -# driver/Makefile.in --- xscreensaver, Copyright (c) 1997-2010 Jamie Zawinski. +# driver/Makefile.in --- xscreensaver, Copyright © 1997-2021 Jamie Zawinski. # the `../configure' script generates `driver/Makefile' from this file. @SET_MAKE@ .SUFFIXES: -.SUFFIXES: .c .m .o +.SUFFIXES: .c .m .o .desktop .desktop.in srcdir = @srcdir@ VPATH = @srcdir@ @@ -19,42 +19,36 @@ datadir = @datadir@ localedir = @PO_DATADIR@/locale mandir = @mandir@ libexecdir = @libexecdir@ -mansuffix = 1 -manNdir = $(mandir)/man$(mansuffix) +sysconfdir = @sysconfdir@ -INTLTOOL_MERGE = @INTLTOOL_MERGE@ +# A = suffix for user commands in /usr/bin/ +# B = suffix for helper programs in /usr/libexec/xscreensaver/ +mansuffixA = 1 +mansuffixB = 6 GTK_DATADIR = @GTK_DATADIR@ GTK_APPDIR = $(GTK_DATADIR)/applications GTK_ICONDIR = $(GTK_DATADIR)/pixmaps -GTK_GLADEDIR = $(GTK_DATADIR)/xscreensaver/glade +GTK_UIDIR = $(GTK_DATADIR)/xscreensaver/ui + +HACKDIR = @HACKDIR@ HACK_CONF_DIR = @HACK_CONF_DIR@ CC = @CC@ -OBJCC = @OBJCC@ CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ DEFS = @DEFS@ -INTL_DEFS = -DLOCALEDIR=\"$(localedir)\" -SUBP_DEFS = $(DEFS) -DDEFAULT_PATH_PREFIX='"@HACKDIR@"' -GTK_DEFS = $(DEFS) -DDEFAULT_ICONDIR='"$(GTK_GLADEDIR)"' -CONF_DEFS = -DHACK_CONFIGURATION_PATH='"$(HACK_CONF_DIR)"' LIBS = @LIBS@ -INTL_LIBS = @INTLLIBS@ -JPEG_LIBS = @JPEG_LIBS@ -PERL = @PERL@ DEPEND = @DEPEND@ DEPEND_FLAGS = @DEPEND_FLAGS@ DEPEND_DEFINES = @DEPEND_DEFINES@ -SHELL = /bin/sh INSTALL = @INSTALL@ SUID_FLAGS = -o root -m 4755 INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_SETUID = @INSTALL_SETUID@ +INSTALL_SETUID = $(INSTALL_PROGRAM) $(SUID_FLAGS) INSTALL_DATA = @INSTALL_DATA@ INSTALL_DIRS = @INSTALL_DIRS@ @@ -62,8 +56,11 @@ X_CFLAGS = @X_CFLAGS@ X_LIBS = @X_LIBS@ X_PRE_LIBS = @X_PRE_LIBS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ -XMU_LIBS = @XMU_LIBS@ PNG_LIBS = @PNG_LIBS@ +XFT_LIBS = @XFT_LIBS@ + +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTL_LIBS = @INTLLIBS@ # Note: # @@ -74,15 +71,18 @@ PNG_LIBS = @PNG_LIBS@ # (e.g., -lsocket, -lnsl, etc.) # # I think (but am not totally sure) that LIBS is also really "LDFLAGS". -# -# SAVER_LIBS is the link line for "xscreensaver", and -# CMD_LIBS is the link line for "xscreensaver-command". -# GETIMG_LIBS is the link line for "xscreensaver-getimage". AD_DIR = @APPDEFAULTS@ -PAM_DIR = /etc/pam.d -PAM_CONF = /etc/pam.conf + +# $(sysconfdir) is either /usr/local/etc or /usr/etc but this must be /etc. +PAM_ROOT = /etc +PAM_DIR = $(PAM_ROOT)/pam.d +PAM_CONF = $(PAM_ROOT)/pam.conf + +ICON_SRC = $(UTILS_SRC)/images +LOGO = $(ICON_SRC)/logo-50.xpm +GTK_ICONS = $(ICON_SRC)/screensaver-*.png UTILS_SRC = $(srcdir)/../utils UTILS_BIN = ../utils @@ -90,11 +90,40 @@ UTILS_BIN = ../utils INCLUDES_1 = -I. -I$(srcdir) -I$(UTILS_SRC) -I.. INCLUDES = $(INCLUDES_1) @INCLUDES@ -MOTIF_SRCS = demo-Xm.c demo-Xm-widgets.c -MOTIF_OBJS = demo-Xm.o demo-Xm-widgets.o +LIBS_PRE = $(LIBS) $(X_LIBS) $(X_PRE_LIBS) +LIBS_POST = $(X_EXTRA_LIBS) -GTK_SRCS = demo-Gtk.c demo-Gtk-conf.c -GTK_OBJS = demo-Gtk.o demo-Gtk-conf.o @GTK_EXTRA_OBJS@ +XDPMS_LIBS = @XDPMS_LIBS@ +XINERAMA_LIBS = @XINERAMA_LIBS@ $(FIXME) +XINPUT_LIBS = -lXi +XML_LIBS = @XML_LIBS@ + +DAEMON_DEFS = -DDEFAULT_PATH_PREFIX='"@HACKDIR@"' -DAD_DIR='"$(AD_DIR)"' +DAEMON_SRCS = xscreensaver.c blurb.c atoms.c clientmsg.c xinput.c prefs.c +DAEMON_OBJS = xscreensaver.o blurb.o atoms.o clientmsg.o xinput.o prefs.o \ + $(UTILS_BIN)/xmu.o +DAEMON_LIBS = $(LIBS_PRE) $(XINPUT_LIBS) -lX11 $(LIBS_POST) + +GFX_DEFS = -DLOCALEDIR=\"$(localedir)\" +GFX_SRCS = xscreensaver-gfx.c screens.c windows.c subprocs.c \ + exec.c prefsw.c dpms.c fade.c exts.c atomswm.c +GFX_OBJS = xscreensaver-gfx.o screens.o windows.o subprocs.o \ + exec.o prefsw.o dpms.o fade.o exts.o atomswm.o \ + prefs.o blurb.o atoms.o clientmsg.o xinput.o \ + $(UTILS_BIN)/xmu.o \ + $(UTILS_BIN)/yarandom.o \ + $(UTILS_BIN)/resources.o \ + $(UTILS_BIN)/visual.o \ + $(UTILS_BIN)/usleep.o \ + $(UTILS_BIN)/font-retry.o \ + $(UTILS_BIN)/logo.o \ + $(UTILS_BIN)/minixpm.o \ + $(UTILS_BIN)/xft.o \ + $(UTILS_BIN)/utf8wc.o \ + $(UTILS_BIN)/xshm.o \ + $(UTILS_BIN)/aligned_malloc.o +GFX_LIBS = $(LIBS_PRE) $(XFT_LIBS) $(XDPMS_LIBS) $(XINERAMA_LIBS) \ + @SAVER_LIBS@ -lXt -lX11 -lXext -lXi $(LIBS_POST) $(INTL_LIBS) PWENT_SRCS = passwd-pwent.c PWENT_OBJS = passwd-pwent.o @@ -108,209 +137,186 @@ PAM_OBJS = passwd-pam.o PWHELPER_SRCS = passwd-helper.c PWHELPER_OBJS = passwd-helper.o -LOCK_SRCS_1 = lock.c passwd.c -LOCK_OBJS_1 = lock.o passwd.o -NOLOCK_SRCS_1 = lock.c -NOLOCK_OBJS_1 = lock.o - -SYSTEMD_SRCS = xscreensaver-systemd.c -SYSTEMD_OBJS = xscreensaver-systemd.o -SYSTEMD_LIBS = -lsystemd - -TEST_SRCS = test-passwd.c test-uid.c test-xdpms.c test-grab.c \ - test-apm.c test-fade.c test-xinerama.c test-vp.c \ - test-randr.c xdpyinfo.c test-mlstring.c test-screens.c \ - test-yarandom.c -TEST_EXES = test-passwd test-uid test-xdpms test-grab \ - test-apm test-fade test-xinerama test-vp \ - test-randr xdpyinfo test-mlstring test-screens \ - test-yarandom - -MOTIF_LIBS = @MOTIF_LIBS@ @PNG_LIBS@ $(XMU_LIBS) -GTK_LIBS = @GTK_LIBS@ $(XMU_LIBS) -XML_LIBS = @XML_LIBS@ - -XDPMS_LIBS = @XDPMS_LIBS@ -XINERAMA_LIBS = @XINERAMA_LIBS@ -XINPUT_LIBS = @XINPUT_LIBS@ - PASSWD_SRCS = @PASSWD_SRCS@ PASSWD_OBJS = @PASSWD_OBJS@ -PASSWD_LIBS = @PASSWD_LIBS@ LOCK_SRCS = @LOCK_SRCS@ LOCK_OBJS = @LOCK_OBJS@ -XMU_SRCS = @XMU_SRCS@ -XMU_OBJS = @XMU_OBJS@ - -GL_SRCS = @SAVER_GL_SRCS@ -GL_OBJS = @SAVER_GL_OBJS@ -GL_LIBS = @SAVER_GL_LIBS@ - -ICON_SRC = $(UTILS_SRC)/images -LOGO = $(ICON_SRC)/logo-50.xpm -GTK_ICONS = $(ICON_SRC)/screensaver-*.png -DEMO_UTIL_SRCS = $(UTILS_SRC)/resources.c $(UTILS_SRC)/usleep.c \ - $(UTILS_SRC)/visual.c $(XMU_SRCS) -DEMO_UTIL_OBJS = $(UTILS_BIN)/resources.o $(UTILS_BIN)/usleep.o \ - $(UTILS_BIN)/visual.o $(XMU_OBJS) - -SAVER_UTIL_SRCS = $(UTILS_SRC)/fade.c $(UTILS_SRC)/overlay.c \ - $(UTILS_SRC)/logo.c $(UTILS_SRC)/yarandom.c \ - $(UTILS_SRC)/minixpm.c $(UTILS_SRC)/font-retry.c \ - $(DEMO_UTIL_SRCS) -SAVER_UTIL_OBJS = $(UTILS_BIN)/fade.o $(UTILS_BIN)/overlay.o \ - $(UTILS_BIN)/logo.o $(UTILS_BIN)/yarandom.o \ - $(UTILS_BIN)/minixpm.o $(UTILS_BIN)/font-retry.o \ - $(DEMO_UTIL_OBJS) - -GETIMG_SRCS_1 = xscreensaver-getimage.c -GETIMG_OBJS_1 = xscreensaver-getimage.o - -GETIMG_SRCS = $(GETIMG_SRCS_1) \ - $(UTILS_BIN)/colorbars.o $(UTILS_BIN)/resources.o \ - $(UTILS_BIN)/yarandom.o $(UTILS_BIN)/visual.o \ - $(UTILS_BIN)/usleep.o $(UTILS_BIN)/hsv.o \ - $(UTILS_BIN)/colors.o $(UTILS_BIN)/grabscreen.o \ - $(UTILS_BIN)/logo.o $(UTILS_BIN)/minixpm.o prefs.o \ - $(XMU_SRCS) - -GETIMG_OBJS = $(GETIMG_OBJS_1) \ - $(UTILS_BIN)/colorbars.o $(UTILS_BIN)/resources.o \ - $(UTILS_BIN)/yarandom.o $(UTILS_BIN)/visual.o \ - $(UTILS_BIN)/usleep.o $(UTILS_BIN)/hsv.o \ - $(UTILS_BIN)/colors.o $(UTILS_BIN)/grabscreen.o \ - $(UTILS_BIN)/logo.o $(UTILS_BIN)/minixpm.o prefs.o \ - $(XMU_OBJS) - -SAVER_SRCS_1 = xscreensaver.c windows.c screens.c timers.c subprocs.c \ - exec.c xset.c splash.c setuid.c stderr.c mlstring.c -SAVER_OBJS_1 = xscreensaver.o windows.o screens.o timers.o subprocs.o \ - exec.o xset.o splash.o setuid.o stderr.o mlstring.o - -SAVER_SRCS = $(SAVER_SRCS_1) prefs.c dpms.c $(LOCK_SRCS) \ - $(SAVER_UTIL_SRCS) $(GL_SRCS) -SAVER_OBJS = $(SAVER_OBJS_1) prefs.o dpms.o $(LOCK_OBJS) \ - $(SAVER_UTIL_OBJS) $(GL_OBJS) +AUTH_DEFS = -DLOCALEDIR=\"$(localedir)\" -DAD_DIR='"$(AD_DIR)"' +AUTH_SRCS = xscreensaver-auth.c dialog.c passwd.c setuid.c +AUTH_OBJS = xscreensaver-auth.o $(AUTH_OBJS_1) +AUTH_OBJS_1 = dialog.o passwd.o setuid.o \ + @PASSWD_OBJS@ \ + blurb.o screens.o xinput.o prefs.o atoms.o atomswm.o \ + $(UTILS_BIN)/xft.o \ + $(UTILS_BIN)/xftwrap.o \ + $(UTILS_BIN)/utf8wc.o \ + $(UTILS_BIN)/font-retry.o \ + $(UTILS_BIN)/yarandom.o \ + $(UTILS_BIN)/usleep.o \ + $(UTILS_BIN)/resources.o \ + $(UTILS_BIN)/logo.o \ + $(UTILS_BIN)/minixpm.o +AUTH_LIBS = $(LIBS_PRE) $(XFT_LIBS) $(XINPUT_LIBS) $(XINERAMA_LIBS) \ + @SAVER_LIBS@ -lXt -lX11 -lXext -lXi \ + @PASSWD_LIBS@ $(LIBS_POST) $(INTL_LIBS) + +SYSTEMD_DEFS = +SYSTEMD_SRCS = xscreensaver-systemd.c +SYSTEMD_OBJS = xscreensaver-systemd.o blurb.o $(UTILS_BIN)/yarandom.o +SYSTEMD_LIBS = $(LIBS_PRE) @SYSTEMD_LIBS@ -lX11 $(LIBS_POST) +CMD_DEFS = CMD_SRCS = remote.c xscreensaver-command.c -CMD_OBJS = remote.o xscreensaver-command.o - -DEMO_SRCS_1 = prefs.c dpms.c -DEMO_OBJS_1 = prefs.o dpms.o - -DEMO_SRCS = $(DEMO_SRCS_1) remote.c exec.c $(DEMO_UTIL_SRCS) -DEMO_OBJS = $(DEMO_OBJS_1) 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) $(XINERAMA_LIBS) $(GL_LIBS) $(X_PRE_LIBS) \ - -lXt -lX11 -lXext $(X_EXTRA_LIBS) \ - $(PASSWD_LIBS) $(INTL_LIBS) - -CMD_LIBS = $(LIBS) $(X_LIBS) \ - $(X_PRE_LIBS) -lX11 -lXext $(X_EXTRA_LIBS) - -GETIMG_LIBS = $(LIBS) $(X_LIBS) $(PNG_LIBS) $(JPEG_LIBS) \ - $(X_PRE_LIBS) -lXt -lX11 $(XMU_LIBS) -lXext $(X_EXTRA_LIBS) - -EXES = xscreensaver xscreensaver-command xscreensaver-demo \ - xscreensaver-getimage @EXES_OSX@ @EXES_SYSTEMD@ -EXES2 = @ALL_DEMO_PROGRAMS@ -EXES_OSX = pdf2jpeg +CMD_OBJS = remote.o xscreensaver-command.o blurb.o atoms.o clientmsg.o +CMD_LIBS = $(LIBS_PRE) $(XINPUT_LIBS) -lX11 -lXext $(LIBS_POST) + +GTK_DEFS = -DHACK_CONFIGURATION_PATH='"$(HACK_CONF_DIR)"' \ + -DDEFAULT_PATH_PREFIX='"@HACKDIR@"' \ + -DDEFAULT_ICONDIR='"$(GTK_UIDIR)"' \ + -DLOCALEDIR=\"$(localedir)\" \ + -I$(ICON_SRC) +GTK_SRCS = demo-Gtk.c demo-Gtk-conf.c +GTK_OBJS = demo-Gtk.o demo-Gtk-conf.o \ + blurb.o exec.o prefs.o prefsw.o dpms.o remote.o \ + clientmsg.o atoms.o \ + $(UTILS_BIN)/xmu.o \ + $(UTILS_BIN)/resources.o \ + $(UTILS_BIN)/visual.o \ + $(UTILS_BIN)/usleep.o +GTK_LIBS = $(LIBS_PRE) $(INTL_LIBS) $(XDPMS_LIBS) \ + $(XINERAMA_LIBS) $(XML_LIBS) @GTK_LIBS@ \ + -lXt -lX11 -lXext -lXi $(LIBS_POST) + +MOTIF_DEFS = -DHACK_CONFIGURATION_PATH='"$(HACK_CONF_DIR)"' \ + -DDEFAULT_PATH_PREFIX='"@HACKDIR@"' +MOTIF_SRCS = demo-Xm.c demo-Xm-widgets.c +MOTIF_OBJS = demo-Xm.o demo-Xm-widgets.o \ + blurb.o exec.o prefs.o prefsw.o dpms.o remote.o \ + clientmsg.o atoms.o \ + $(UTILS_BIN)/xmu.o \ + $(UTILS_BIN)/resources.o \ + $(UTILS_BIN)/visual.o \ + $(UTILS_BIN)/usleep.o +MOTIF_LIBS = $(LIBS_PRE) $(XDPMS_LIBS) $(XDPMS_LIBS) @MOTIF_LIBS@ \ + @PNG_LIBS@ -lXt -lX11 -lXext -lXi $(LIBS_POST) + +TEST_SRCS = test-passwd.c test-uid.c test-xdpms.c test-grab.c \ + test-fade.c test-xinerama.c test-vp.c test-randr.c \ + xdpyinfo.c test-screens.c test-yarandom.c test-xinput.c \ + test-xkb.c +TEST_EXES = test-passwd test-uid test-xdpms test-grab \ + test-fade test-xinerama test-vp test-randr \ + xdpyinfo test-screens test-yarandom test-xinput \ + test-xkb + +EXES = xscreensaver xscreensaver-command xscreensaver-settings +UTIL_EXES = xscreensaver-gfx @EXES_SYSTEMD@ +SETUID_EXES = xscreensaver-auth +DEMO_EXES = @ALL_DEMO_PROGRAMS@ EXES_SYSTEMD = xscreensaver-systemd -SCRIPTS_1 = xscreensaver-getimage-file xscreensaver-getimage-video \ - xscreensaver-text -SCRIPTS_OSX = xscreensaver-getimage-desktop -SCRIPTS = $(SCRIPTS_1) @SCRIPTS_OSX@ - HDRS = XScreenSaver_ad.h XScreenSaver_Xm_ad.h \ xscreensaver.h prefs.h remote.h exec.h \ - demo-Gtk-conf.h auth.h mlstring.h types.h -MEN_1 = xscreensaver.man xscreensaver-demo.man \ - xscreensaver-command.man \ - xscreensaver-text.man \ - xscreensaver-getimage.man \ - xscreensaver-getimage-file.man \ - xscreensaver-getimage-video.man \ - xscreensaver-systemd.man -MEN_OSX = xscreensaver-getimage-desktop.man pdf2jpeg.man -MEN = $(MEN_1) @MEN_OSX@ + demo-Gtk-conf.h auth.h types.h blurb.h atoms.h clientmsg.h \ + screens.h xinput.h fade.h +MENA = xscreensaver.man xscreensaver-settings.man \ + xscreensaver-command.man +MENB = xscreensaver-gfx.man xscreensaver-auth.man \ + xscreensaver-command.man xscreensaver-systemd.man EXTRAS = README Makefile.in \ XScreenSaver.ad.in XScreenSaver-Xm.ad xscreensaver.pam.in \ - xscreensaver-demo.glade2.in xscreensaver-demo.glade2p \ - screensaver-properties.desktop.in \ + xscreensaver.ui screensaver-properties.desktop.in \ .gdbinit -VMSFILES = compile_axp.com compile_decc.com link_axp.com link_decc.com \ - vms-getpwnam.c vms-pwd.h vms-hpwd.c vms-validate.c \ - vms_axp.opt vms_axp_12.opt vms_decc.opt vms_decc_12.opt -TARFILES = $(EXTRAS) $(VMSFILES) $(SAVER_SRCS_1) $(SYSTEMD_SRCS) \ - $(MOTIF_SRCS) $(GTK_SRCS) $(PWENT_SRCS) $(PWHELPER_SRCS) \ - $(KERBEROS_SRCS) $(PAM_SRCS) $(LOCK_SRCS_1) $(DEMO_SRCS_1) \ - $(CMD_SRCS) $(GETIMG_SRCS_1) $(PDF2JPEG_SRCS) $(HDRS) \ - $(SCRIPTS_1) $(SCRIPTS_OSX) $(MEN_1) $(MEN_OSX) \ - $(TEST_SRCS) +TARFILES = $(DAEMON_SRCS) $(GFX_SRCS) $(AUTH_SRCS) $(SYSTEMD_SRCS) \ + $(CMD_SRCS) $(GTK_SRCS) $(MOTIF_SRCS) $(PWENT_SRCS) \ + $(PWHELPER_SRCS) $(KERBEROS_SRCS) $(PAM_SRCS) \ + $(HDRS) $(MENA) $(MENB) $(TEST_SRCS) $(EXTRAS) +default: $(EXES) $(UTIL_EXES) $(SETUID_EXES) +all: $(EXES) $(UTIL_EXES) $(SETUID_EXES) $(DEMO_EXES) +tests: $(TEST_EXES) -default: $(EXES) -all: $(EXES) $(EXES2) -tests: $(TEST_EXES) -install: install-program install-ad install-scripts \ - install-gnome install-man install-xml install-pam -uninstall: uninstall-program uninstall-ad \ - uninstall-gnome uninstall-man uninstall-xml +############################################################################## +# +# Installation +# +############################################################################## -install-strip: - $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' \ - install +install: install-program install-ad install-man install-xml \ + install-pam install-gnome +uninstall: uninstall-program uninstall-ad uninstall-man uninstall-xml \ + uninstall-gnome -install-program: $(EXES) - @if [ ! -d $(install_prefix)$(bindir) ]; then \ - $(INSTALL_DIRS) $(install_prefix)$(bindir) ; \ +install-strip: + $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install + +install-program:: $(EXES) + @if [ ! -d $(install_prefix)$(bindir) ]; then \ + $(INSTALL_DIRS) $(install_prefix)$(bindir) ; \ + fi ; \ + if [ ! -d $(install_prefix)$(HACKDIR) ]; then \ + $(INSTALL_DIRS) $(install_prefix)$(HACKDIR) ; \ fi - @inst="$(INSTALL_PROGRAM)" ; \ - if [ @NEED_SETUID@ = yes ]; then \ - me=`PATH="$$PATH:/usr/ucb" whoami` ; \ - if [ "$$me" = root ]; then \ - inst="$(INSTALL_SETUID)" ; \ - else \ - e=echo ; \ - $$e "" ;\ - $$e " ####################################################################";\ - $$e " Warning: xscreensaver has been compiled with support for shadow" ;\ - $$e " passwords. If your system actually uses shadow passwords," ;\ - $$e " then xscreensaver must be installed as a setuid root" ;\ - $$e " program in order for locking to work. To do this, you" ;\ - $$e " must run 'make install' as 'root', not as '$$me'." ;\ - $$e "" ;\ - $$e " For now, xscreensaver will be installed non-setuid, which" ;\ - $$e " means that locking might not work. (Try it and see.)" ;\ - $$e " ####################################################################";\ - $$e "" ;\ - fi ; \ - fi ; \ - echo $$inst xscreensaver $(install_prefix)$(bindir)/xscreensaver ; \ - $$inst xscreensaver $(install_prefix)$(bindir)/xscreensaver - @for exe in xscreensaver-command xscreensaver-demo \ - xscreensaver-getimage @EXES_OSX@ @EXES_SYSTEMD@ ; do \ - echo $(INSTALL_PROGRAM) $$exe $(install_prefix)$(bindir)/$$exe ; \ - $(INSTALL_PROGRAM) $$exe $(install_prefix)$(bindir)/$$exe ; \ + +install-program:: $(EXES) + @inst="$(INSTALL_PROGRAM)" ; \ + for exe in $(EXES); do \ + echo $$inst $$exe $(install_prefix)$(bindir)/$$exe ; \ + $$inst $$exe $(install_prefix)$(bindir)/$$exe ; \ + done + +install-program:: $(UTIL_EXES) + @inst="$(INSTALL_PROGRAM)" ; \ + for exe in $(UTIL_EXES); do \ + echo $$inst $$exe $(install_prefix)$(HACKDIR)/$$exe ; \ + $$inst $$exe $(install_prefix)$(HACKDIR)/$$exe ; \ done +install-program:: $(SETUID_EXES) + @inst="$(INSTALL_PROGRAM)" ; \ + idir="$(install_prefix)$(HACKDIR)" ; \ + if [ @SETUID_AUTH@ = yes ]; then \ + inst="$(INSTALL_SETUID)" ; \ + else \ + inst="$(INSTALL_PROGRAM)" ; \ + fi ; \ + for exe in $(SETUID_EXES); do \ + echo $$inst $$exe $$idir/$$exe ; \ + if $$inst $$exe $$idir/$$exe ; then \ + true ; \ + elif [ @SETUID_AUTH@ = yes ]; then \ + echo $(INSTALL_PROGRAM) $$exe $$idir/$$exe ; \ + if $(INSTALL_PROGRAM) $$exe $$idir/$$exe ; then \ + echo "" ; \ + echo "WARNING: unable to install $$exe setuid." ; \ + echo "WARNING: authentication may not work!" ; \ + echo "" ; \ + else \ + exit 1 ; \ + fi ; \ + else \ + exit 1 ; \ + fi ; \ + done + + +# Symlink from xscreensaver-demo -> xscreensaver-settings in /usr/bin/ +install-program:: + @D=$(install_prefix)$(bindir) ; \ + echo ln -sf xscreensaver-settings $$D/xscreensaver-demo ; \ + ln -sf xscreensaver-settings $$D/xscreensaver-demo + install-ad: XScreenSaver.ad @if [ ! -d $(install_prefix)$(AD_DIR) ]; then \ $(INSTALL_DIRS) $(install_prefix)$(AD_DIR) ; \ - fi - @-echo $(INSTALL_DATA) XScreenSaver.ad \ + fi ; \ + echo $(INSTALL_DATA) XScreenSaver.ad \ $(install_prefix)$(AD_DIR)/XScreenSaver ; \ if $(INSTALL_DATA) XScreenSaver.ad \ $(install_prefix)$(AD_DIR)/XScreenSaver ; then \ @@ -331,101 +337,106 @@ install-ad: XScreenSaver.ad $$e "" ;\ $$e " ####################################################################";\ $$e " Warning: unable to install $(install_prefix)$(AD_DIR)/XScreenSaver" ;\ - $$e " The directory is unwritable. This is probably ok;" ;\ - $$e " xscreensaver should work without that file." ;\ + $$e " This is probably ok; it should work without that file." ;\ $$e " ####################################################################";\ $$e "" ;\ exit 0 ; \ fi \ fi -install-scripts: $(SCRIPTS) munge-scripts - @for program in $(SCRIPTS); do \ - if [ -r $$program ] ; then \ - p=$$program ; \ - else \ - p=$(srcdir)/$$program ; \ - fi ; \ - echo $(INSTALL_SCRIPT) $$p \ - $(install_prefix)$(bindir)/$$program ; \ - $(INSTALL_SCRIPT) $$p \ - $(install_prefix)$(bindir)/$$program ; \ - done - -munge-scripts: $(SCRIPTS) - @tmp=/tmp/mf.$$$$ ; \ - perl="${PERL}" ; \ - rm -f $$tmp ; \ - for program in $(SCRIPTS); do \ - sed "s@^\(#!\)\(/[^ ]*/perl[^ ]*\)\(.*\)\$$@\1$$perl\3@" \ - < $(srcdir)/$$program > $$tmp ; \ - if cmp -s $(srcdir)/$$program $$tmp ; then \ - true ; \ - else \ - echo "$$program: setting interpreter to $$perl" >&2 ; \ - cat $$tmp > ./$$program ; \ - fi ; \ - done ; \ - rm -f $$tmp - # When installing man pages, we install "foo.man" as "foo.N" and update # the .TH line in the installed file with one like # # .TH XScreenSaver N "V.VV (DD-MMM-YYYY)" "X Version 11" # -# where N is the manual section suffix. +# where N is the manual section suffix (usually 1 or 6, depending). # -install-man: $(MEN) - @men="$(MEN)" ; \ +install-man:: $(MENA) $(MENB) + @ \ U=$(UTILS_SRC)/version.h ; \ - V=`sed -n 's/.*xscreensaver \([0-9]\.[^)]*)\).*/\1/p' < $$U` ; \ - T=/tmp/xs$$$$.$(mansuffix) ; \ - TH=".TH XScreenSaver $(mansuffix) \"$$V\" \"X Version 11\"" ; \ - echo "installing man pages: $$TH" ; \ + V=`sed -n 's/.*xscreensaver \([0-9]\.[^)]*)\).*/\1/p' < $$U | \ + head -1` ; \ + T=/tmp/xs$$$$.man ; \ + SUFA=$(mansuffixA) ; \ + SUFB=$(mansuffixB) ; \ \ - if [ ! -d $(install_prefix)$(manNdir) ]; then \ - $(INSTALL_DIRS) $(install_prefix)$(manNdir) ; \ - fi ; \ + INST() { \ + TH=".TH XScreenSaver $$SUF \"$$V\" \"X Version 11\"" ; \ + DIR="$(install_prefix)$(mandir)/man$$SUF" ; \ + if [ ! -d $$DIR ]; then \ + echo $(INSTALL_DIRS) $$DIR ; \ + $(INSTALL_DIRS) $$DIR ; \ + fi ; \ \ - for man in $$men; do \ - instname=`echo $$man | sed 's/\.man$$/\.$(mansuffix)/'` ; \ - manbase=`echo $$man | sed 's/\.man$$//'` ; \ - TH=".TH $$manbase $(mansuffix) \"$$V\" \"X Version 11\" \"XScreenSaver manual\"" ; \ sed -e "s/^\.TH.*/$$TH/" \ - -e 's/^\(\.BR xscr.*(\)[^()]\(.*\)/\1$(mansuffix)\2/' \ - -e 's@(MANSUFFIX)@($(mansuffix))@g' \ + -e "s/^\(\.BR xscreens[^ ]* (\)[0-9]\(.*\)/\1$$SUFA\2/" \ + -e "s@(MANSUFFIX)@($$SUFB)@g" \ < $(srcdir)/$$man > $$T ; \ - echo $(INSTALL_DATA) $(srcdir)/$$man \ - $(install_prefix)$(manNdir)/$$instname ; \ - $(INSTALL_DATA) $$T \ - $(install_prefix)$(manNdir)/$$instname ; \ - done ; \ + manbase=`echo $$man | sed 's/\.man$$//'` ; \ + echo $(INSTALL_DATA) $$man $$DIR/$$manbase.$$SUF ; \ + $(INSTALL_DATA) $$T $$DIR/$$manbase.$$SUF ; \ + } ; \ + \ + SUF=$$SUFA ; for man in $(MENA); do INST ; done ; \ + SUF=$$SUFB ; for man in $(MENB); do INST ; done ; \ rm -f $$T -uninstall-program: - @for program in $(EXES) $(SCRIPTS); do \ +# Symlink from xscreensaver-demo.1 -> xscreensaver-settings.1 in /usr/man/man1/ +install-man:: + @D=$(install_prefix)$(mandir)/man$(mansuffixA) ; \ + F1=xscreensaver-settings.$(mansuffixA) ; \ + F2=xscreensaver-demo.$(mansuffixA) ; \ + echo ln -sf $$F1 $$D/$$F2 ; \ + ln -sf $$F1 $$D/$$F2 + +# These used to be in driver/ and installed into $(bindir) +# Now they are in hacks/ and are installed into ${libexecdir}/xscreensaver/ +OLD_EXES = xscreensaver-getimage xscreensaver-getimage-file \ + xscreensaver-getimage-video xscreensaver-text \ + xscreensaver-systemd +OLD_MEN = xscreensaver-getimage.man xscreensaver-getimage-file.man \ + xscreensaver-getimage-video.man xscreensaver-text.man \ + xscreensaver-demo.man +uninstall-program:: + @for program in $(EXES) $(OLD_EXES); do \ echo rm -f $(install_prefix)$(bindir)/$$program ; \ rm -f $(install_prefix)$(bindir)/$$program ; \ done +uninstall-program:: + @for program in $(UTIL_EXES) $(SETUID_EXES); do \ + echo rm -f $(install_prefix)$(HACKDIR)/$$program ; \ + rm -f $(install_prefix)$(HACKDIR)/$$program ; \ + done + uninstall-ad: rm -f $(install_prefix)$(AD_DIR)/XScreenSaver uninstall-man: - @men="$(MEN)" ; \ + @men="$(MEN) $(OLD_MEN)" ; \ for man in $$men; do \ instname=`echo $$man | sed 's/\.man$$/\.$(mansuffix)/'` ; \ - echo rm -f $(install_prefix)$(manNdir)/$$instname* ; \ - rm -f $(install_prefix)$(manNdir)/$$instname* ; \ + echo rm -f $(install_prefix)$(manAdir)/$$instname* ; \ + rm -f $(install_prefix)$(manAdir)/$$instname* ; \ done install-pam: xscreensaver.pam - @src="xscreensaver.pam" ; \ - dest=`sed -n 's/.*PAM_SERVICE_NAME[ ]*"\([^"]*\)".*$$/\1/p' \ + @src="xscreensaver.pam" ; \ + name=`sed -n 's/.*PAM_SERVICE_NAME[ ]*"\([^"]*\)".*$$/\1/p' \ < ../config.h` ; \ dir="$(install_prefix)$(PAM_DIR)" ; \ conf="$(PAM_CONF)" ; \ \ + if [ -z "$$name" ]; then \ + echo "PAM not configured, not installing" >&2 ; \ + exit 0 ; \ + fi ; \ + \ + if [ ! -d $(install_prefix)$(PAM_ROOT) ]; then \ + echo $(INSTALL_DIRS) $(install_prefix)$(PAM_ROOT) ; \ + $(INSTALL_DIRS) $(install_prefix)$(PAM_ROOT) ; \ + fi ; \ + \ if [ -d $$dir ] ; then \ \ if [ -f $$dir/xdm ]; then \ @@ -435,52 +446,50 @@ install-pam: xscreensaver.pam fi ; \ \ if [ -z "$$src2" ]; then \ - echo $(INSTALL_DATA) $$src $$dir/$$dest ; \ - $(INSTALL_DATA) $$src $$dir/$$dest ; \ + echo $(INSTALL_DATA) $$src $$dir/$$name ; \ + $(INSTALL_DATA) $$src $$dir/$$name ; \ else \ - src="xscreensaver.pam.$$$$" ; \ - echo "grep '^#%\|^auth\|^@include' $$src2 > $$src" ; \ - grep '^#%\|^auth\|^@include' $$src2 > $$src ; \ - echo $(INSTALL_DATA) $$src $$dir/$$dest ; \ - $(INSTALL_DATA) $$src $$dir/$$dest ; \ - echo rm -f $$src ; \ - rm -f $$src ; \ + tmp="xscreensaver.pam.$$$$" ; \ + grep '^#%\|^auth\|^@include' $$src2 > $$tmp ; \ + if cmp -s $$tmp $$dir/$$name ; then \ + echo "$$dir/$$name unchanged" ; \ + else \ + echo "Updating contents of $$dir/$$name from $$src2" ; \ + $(INSTALL_DATA) $$tmp $$dir/$$name ; \ + fi ; \ + rm -f $$tmp ; \ fi ; \ \ - if [ ! -f $$dir/$$dest ]; then \ + if [ ! -f $$dir/$$name ]; then \ e=echo ; \ $$e "" ;\ $$e " ####################################################################";\ $$e " Warning: xscreensaver has been compiled with support for Pluggable" ;\ $$e " Authentication Modules (PAM). However, we were unable to" ;\ - $$e " install the file $$dir/$$dest. PAM is unlikely" ;\ - $$e " to work without this file (and old-style password" ;\ - $$e " authentication will be used instead, which may or may not" ;\ - $$e " work.)" ;\ + $$e " install the file \"$$dir/$$name\". XScreenSaver is" ;\ + $$e " unlikely to work without this file." ;\ $$e " ####################################################################";\ $$e "" ;\ fi ; \ - elif [ -f $$conf -a "x$$dest" != "x" ]; then \ - if ( grep $$dest $$conf >/dev/null ); then \ - echo "$$conf unchanged: already has an entry for $$dest" ; \ + elif [ -f $$conf -a "x$$name" != "x" ]; then \ + if ( grep $$name $$conf >/dev/null ); then \ + echo "$$conf unchanged: already has an entry for $$name" ; \ else \ - src="pam.conf.$$$$" ; \ - echo "grep -v $$dest $$conf > $$src" ; \ - grep -v $$dest $$conf > $$src ; \ - extras=`sed -n "s/^login\(.*auth.*\)$$/$$dest\1/p" $$conf`; \ - echo "$$extras" >> $$src ; \ + tmp="pam.conf.$$$$" ; \ + grep -v $$name $$conf > $$tmp ; \ + extras=`sed -n "s/^login\(.*auth.*\)$$/$$name\1/p" $$conf`; \ + echo "$$extras" >> $$tmp ; \ if [ "x$$extras" = "x" ]; then \ echo "Error: no login rules in $$conf?" >&2 ; \ else \ - echo "adding $$dest rules to $$src:" ; \ + echo "adding $$name rules to $$conf:" ; \ + echo "" ; \ echo "$$extras" | sed 's/^/ /' ; \ fi ; \ - echo $(INSTALL_DATA) $$src $$conf ; \ - $(INSTALL_DATA) $$src $$conf ; \ - echo rm -f $$src ; \ - rm -f $$src ; \ + $(INSTALL_DATA) $$tmp $$conf ; \ + rm -f $$tmp ; \ fi ; \ - if ( grep $$dest $$conf >/dev/null ); then \ + if ( grep $$name $$conf >/dev/null ); then \ echo ; \ else \ e=echo ; \ @@ -489,12 +498,17 @@ install-pam: xscreensaver.pam $$e " Warning: xscreensaver has been compiled with support for Pluggable" ;\ $$e " Authentication Modules (PAM). However, we were unable to" ;\ $$e " install xscreensaver rules in the file $$conf." ;\ - $$e " PAM is unlikely to work without this (and old-style" ;\ - $$e " password authentication will be used instead, which may" ;\ - $$e " or may not work.)" ;\ + $$e " XScreenSaver is unlikely to work without this." ;\ $$e " ####################################################################";\ $$e "" ;\ fi ; \ + else \ + e=echo ; \ + $$e "" ;\ + $$e " ####################################################################";\ + $$e " Warning: $$dir/ does not exist, not installing PAM config." ;\ + $$e " ####################################################################";\ + $$e "" ;\ fi # screensaver-properties.desktop @@ -529,43 +543,43 @@ install-gnome:: $(LOGO) fi # ../utils/images/screensaver-*.png -# into /usr/share/xscreensaver/glade/ +# into /usr/share/xscreensaver/ui/ install-gnome:: - @if [ "$(GTK_DATADIR)" != "" ]; then \ - if [ ! -d "$(install_prefix)$(GTK_GLADEDIR)" ]; then \ - echo $(INSTALL_DIRS) "$(install_prefix)$(GTK_GLADEDIR)" ;\ - $(INSTALL_DIRS) "$(install_prefix)$(GTK_GLADEDIR)" ;\ + @if [ "$(GTK_DATADIR)" != "" ]; then \ + if [ ! -d "$(install_prefix)$(GTK_UIDIR)" ]; then \ + echo $(INSTALL_DIRS) "$(install_prefix)$(GTK_UIDIR)" ;\ + $(INSTALL_DIRS) "$(install_prefix)$(GTK_UIDIR)" ;\ fi ;\ for target in $(GTK_ICONS) ; do \ dest=`echo $$target | sed 's@^.*/@@'` ;\ echo $(INSTALL_DATA) $$target \ - $(install_prefix)$(GTK_GLADEDIR)/$$dest ;\ + $(install_prefix)$(GTK_UIDIR)/$$dest ;\ $(INSTALL_DATA) $$target \ - $(install_prefix)$(GTK_GLADEDIR)/$$dest ;\ + $(install_prefix)$(GTK_UIDIR)/$$dest ;\ done ;\ fi -# xscreensaver-demo.glade2 -# into /usr/share/xscreensaver/glade/ -install-gnome:: xscreensaver-demo.glade2 +# xscreensaver.ui +# into /usr/share/xscreensaver/ui/ +install-gnome:: xscreensaver.ui @if [ "$(GTK_DATADIR)" != "" ]; then \ - if [ ! -d "$(install_prefix)$(GTK_GLADEDIR)" ]; then \ - echo $(INSTALL_DIRS) "$(install_prefix)$(GTK_GLADEDIR)" ;\ - $(INSTALL_DIRS) "$(install_prefix)$(GTK_GLADEDIR)" ;\ + if [ ! -d "$(install_prefix)$(GTK_UIDIR)" ]; then \ + echo $(INSTALL_DIRS) "$(install_prefix)$(GTK_UIDIR)" ;\ + $(INSTALL_DIRS) "$(install_prefix)$(GTK_UIDIR)" ;\ fi ;\ - target=xscreensaver-demo.glade2 ;\ - echo $(INSTALL_DATA) $$target \ - $(install_prefix)$(GTK_GLADEDIR)/$$target ;\ - if $(INSTALL_DATA) $$target \ - $(install_prefix)$(GTK_GLADEDIR)/$$target ;\ + target=xscreensaver.ui ;\ + echo $(INSTALL_DATA) $(srcdir)/$$target \ + $(install_prefix)$(GTK_UIDIR)/$$target ;\ + if $(INSTALL_DATA) $(srcdir)/$$target \ + $(install_prefix)$(GTK_UIDIR)/$$target ;\ then true ;\ else \ e=echo ; \ $$e "" ;\ $$e " ####################################################################";\ $$e " Warning: unable to install $$target into" ;\ - $$e " $(install_prefix)$(GTK_GLADEDIR)/." ;\ - $$e " Without this file, xscreensaver-demo will not" ;\ + $$e " $(install_prefix)$(GTK_UIDIR)/." ;\ + $$e " Without this file, xscreensaver-settings will not" ;\ $$e " be able to run properly." ;\ $$e " ####################################################################";\ $$e "" ;\ @@ -593,25 +607,34 @@ uninstall-gnome:: fi # ../utils/images/screensaver-*.png -# into /usr/share/xscreensaver/glade/ +# into /usr/share/xscreensaver/ui/ uninstall-gnome:: @if [ "$(GTK_DATADIR)" != "" ]; then \ for target in $(GTK_ICONS) ; do \ dest=`echo $$target | sed 's@^.*/@@'` ;\ - echo rm -f $(install_prefix)$(GTK_GLADEDIR)/$$dest ;\ - rm -f $(install_prefix)$(GTK_GLADEDIR)/$$dest ;\ + echo rm -f $(install_prefix)$(GTK_UIDIR)/$$dest ;\ + rm -f $(install_prefix)$(GTK_UIDIR)/$$dest ;\ done ;\ fi -# xscreensaver-demo.glade2 -# into /usr/share/xscreensaver/glade/ -uninstall-gnome:: xscreensaver-demo.glade2 +# xscreensaver.ui +# into /usr/share/xscreensaver/ui/ +uninstall-gnome:: @if [ "$(GTK_DATADIR)" != "" ]; then \ - target=xscreensaver-demo.glade2 ;\ - echo rm -f $(install_prefix)$(GTK_GLADEDIR)/$$target ;\ - rm -f $(install_prefix)$(GTK_GLADEDIR)/$$target ;\ + for target in xscreensaver.ui xscreensaver-demo.ui ; do \ + echo rm -f $(install_prefix)$(GTK_UIDIR)/$$target ;\ + rm -f $(install_prefix)$(GTK_UIDIR)/$$target ;\ + done ;\ + rmdir "$(GTK_UIDIR)" ;\ + rmdir "$(GTK_DATADIR)/xscreensaver" ;\ + exit 0 ;\ fi +# /usr/share/xscreensaver/glade/ no longer used +uninstall-gnome:: + -rm -rf $(GTK_DATADIR)/xscreensaver/glade + + # /usr/share/xscreensaver/config/README install-xml: @dest=$(install_prefix)$(HACK_CONF_DIR) ; \ @@ -627,14 +650,24 @@ install-xml: uninstall-xml: rm -f $(install_prefix)$(HACK_CONF_DIR)/README + +############################################################################## +# +# Clean and dependencies +# +############################################################################## + clean: - -rm -f *.o a.out core $(EXES) $(EXES2) $(TEST_EXES) \ + -rm -f *.o a.out core $(EXES) $(UTIL_EXES) $(SETUID_EXES) \ + $(DEMO_EXES) $(TEST_EXES) \ XScreenSaver_ad.h XScreenSaver_Xm_ad.h distclean: clean - -rm -f Makefile XScreenSaver.ad \ - TAGS *~ "#"* screensaver-properties.desktop \ - xscreensaver-demo.glade2 \ + -rm -f \ + TAGS *~ "#"* *.rej *.orig \ + Makefile \ + XScreenSaver.ad \ + screensaver-properties.desktop \ xscreensaver.pam # Adds all current dependencies to Makefile @@ -642,7 +675,7 @@ depend: XScreenSaver_ad.h XScreenSaver_Xm_ad.h $(DEPEND) -s '# DO NOT DELETE: updated by make depend' \ $(DEPEND_FLAGS) -- \ $(INCLUDES_1) $(DEFS) $(DEPEND_DEFINES) $(CFLAGS) $(X_CFLAGS) -- \ - $(SAVER_SRCS) $(CMD_SRCS) $(GETIMG_SRCS_1) + $(SAVER_SRCS) $(CMD_SRCS) # Adds some dependencies to Makefile.in -- not totally accurate, but pretty # close. This excludes dependencies on files in /usr/include, etc. It tries @@ -655,8 +688,8 @@ distdepend: check_men update_ad_version XScreenSaver_ad.h XScreenSaver_Xm_ad.h $(INCLUDES_1) $(DEFS) $(DEPEND_DEFINES) $(CFLAGS) $(X_CFLAGS) -- \ $(SAVER_SRCS_1) $(SYSTEMD_SRCS) $(MOTIF_SRCS) $(GTK_SRCS) \ $(PWENT_SRCS) $(PWHELPER_SRCS) $(KERBEROS_SRCS) $(PAM_SRCS) \ - $(LOCK_SRCS_1) $(DEMO_SRCS_1) $(CMD_SRCS) $(GETIMG_SRCS_1) \ - $(PDF2JPEG_SRCS) $(TEST_SRCS) 2>/dev/null | \ + $(LOCK_SRCS_1) $(DEMO_SRCS_1) $(CMD_SRCS) \ + $(TEST_SRCS) 2>/dev/null | \ sort -d | \ ( \ awk '/^# .*Makefile.in ---/,/^# DO .*distdepend/' < Makefile.in ; \ @@ -679,12 +712,12 @@ update_ad_version:: @ \ files="XScreenSaver.ad.in ../hacks/config/README ../OSX/bindist.rtf" ; \ U=$(UTILS_SRC)/version.h ; \ - V=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^. ]*\).*/\1/p' < $$U` ; \ + V=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^ ]*\).*/\1/p' < $$U | head -1` ; \ Y=`date '+%Y'` ; \ D=`date '+%d-%b-%Y'` ; \ for S in $$files ; do \ T=/tmp/xs.$$$$ ; \ - sed -e "s/\(.*version \)[0-9][0-9]*\.[0-9]*[ab]*[0-9]*\(.*\)/\1$$V\2/" \ + sed -e "s/\(.*version \)[0-9][0-9]*\.[0-9.]*[ab]*[0-9]*\(.*\)/\1$$V\2/" \ -e "s/\([0-9][0-9]-[A-Z][a-z][a-z]-[0-9][0-9][0-9]*\)/$$D/" \ -e "s/\( [0-9][0-9][0-9][0-9]-\)[0-9][0-9][0-9][0-9] /\1$$Y /" \ < $$S > $$T ; \ @@ -701,9 +734,9 @@ TAGS: tags tags: find $(srcdir) -name '*.[chly]' -print | xargs etags -a -echo_tarfiles: +list_tarfiles: @$(MAKE) XScreenSaver_ad.h XScreenSaver_Xm_ad.h 2>&1 >/dev/null - @echo $(TARFILES) + @find $(TARFILES) -type f -print | sort check_men: @badmen="" ; \ @@ -723,11 +756,12 @@ check_men: fi -# Rules for noticing when the objects from the utils directory are out of -# date with respect to their sources, and going and building them according -# to the rules in their own Makefile... +############################################################################## +# +# Dependencies on utils/ # -$(UTILS_BIN)/fade.o: $(UTILS_SRC)/fade.c +############################################################################## + $(UTILS_BIN)/overlay.o: $(UTILS_SRC)/overlay.c $(UTILS_BIN)/resources.o: $(UTILS_SRC)/resources.c $(UTILS_BIN)/usleep.o: $(UTILS_SRC)/usleep.c @@ -740,157 +774,219 @@ $(UTILS_BIN)/colorbars.o: $(UTILS_SRC)/colorbars.c $(UTILS_BIN)/hsv.o: $(UTILS_SRC)/hsv.c $(UTILS_BIN)/colors.o: $(UTILS_SRC)/colors.c $(UTILS_BIN)/grabscreen.o: $(UTILS_SRC)/grabscreen.c +$(UTILS_BIN)/utf8wc.o: $(UTILS_SRC)/utf8wc.c +$(UTILS_BIN)/xftwrap.o: $(UTILS_SRC)/xftwrap.c $(UTILS_BIN)/font-retry.o: $(UTILS_SRC)/font-retry.c - -UTIL_OBJS = $(SAVER_UTIL_OBJS) $(UTILS_BIN)/colorbars.o \ - $(UTILS_BIN)/hsv.o $(UTILS_BIN)/colors.o \ - $(UTILS_BIN)/grabscreen.o +$(UTILS_BIN)/xshm.o: $(UTILS_SRC)/xshm.c +$(UTILS_BIN)/aligned_malloc.o: $(UTILS_SRC)/aligned_malloc.c + + +UTIL_OBJS = $(UTILS_BIN)/overlay.o \ + $(UTILS_BIN)/resources.o \ + $(UTILS_BIN)/usleep.o \ + $(UTILS_BIN)/visual.o \ + $(UTILS_BIN)/xmu.o \ + $(UTILS_BIN)/logo.o \ + $(UTILS_BIN)/minixpm.o \ + $(UTILS_BIN)/yarandom.o \ + $(UTILS_BIN)/colorbars.o \ + $(UTILS_BIN)/hsv.o \ + $(UTILS_BIN)/colors.o \ + $(UTILS_BIN)/grabscreen.o \ + $(UTILS_BIN)/xft.o \ + $(UTILS_BIN)/xftwrap.o \ + $(UTILS_BIN)/utf8wc.o \ + $(UTILS_BIN)/font-retry.o \ + $(UTILS_BIN)/xshm.o \ + $(UTILS_BIN)/aligned_malloc.o $(UTIL_OBJS): cd $(UTILS_BIN) ; \ $(MAKE) $(@F) CC="$(CC)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" + +############################################################################## +# +# Compiling the daemon: xscreensaver, xscreensaver-gfx, xscreensaver-auth, +# xscreensaver-systemd and xscreensaver-comand. +# +############################################################################## + # How we build object files in this directory. +CC_ALL=$(INCLUDES) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) .c.o: - $(CC) -c $(INCLUDES) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) $< - -.m.o: - $(OBJCC) -c $(INCLUDES) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) $< + $(CC) -c $(CC_ALL) $< -# subprocs takes an extra -D option. -subprocs.o: subprocs.c - $(CC) -c $(INCLUDES) $(SUBP_DEFS) $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) \ - $(srcdir)/subprocs.c +# # subprocs takes an extra -D option. +# subprocs.o: subprocs.c +# $(CC) -c $(CC_ALL) $(SUBP_DEFS) $< # xscreensaver takes an extra -D option. xscreensaver.o: xscreensaver.c - $(CC) -c $(INCLUDES) $(DEFS) $(INTL_DEFS) $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) \ - $(srcdir)/xscreensaver.c + $(CC) -c $(CC_ALL) $(DAEMON_DEFS) $< -# demo-Gtk takes extra -D options, and an extra -I option. -demo-Gtk.o: demo-Gtk.c - $(CC) -c $(INCLUDES) $(SUBP_DEFS) -I$(ICON_SRC) \ - $(GTK_DEFS) $(INTL_DEFS) $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) \ - $(srcdir)/demo-Gtk.c +xscreensaver-auth.o: XScreenSaver_ad.h +xscreensaver-auth.o: xscreensaver-auth.c + $(CC) -c $(CC_ALL) $(AUTH_DEFS) $< -# demo-Gtk-conf takes an extra -D option. -demo-Gtk-conf.o: demo-Gtk-conf.c - $(CC) -c $(INCLUDES) $(CONF_DEFS) $(GTK_DEFS) $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) \ - $(srcdir)/demo-Gtk-conf.c +xscreensaver: $(DAEMON_OBJS) + $(CC) $(LDFLAGS) -o $@ $(DAEMON_OBJS) $(DAEMON_LIBS) +xscreensaver-gfx.o: XScreenSaver_ad.h +xscreensaver-gfx.o: xscreensaver-gfx.c + $(CC) -c $(CC_ALL) $(GFX_DEFS) $< +xscreensaver-gfx: $(GFX_OBJS) + $(CC) $(LDFLAGS) -o $@ $(GFX_OBJS) $(GFX_LIBS) -# How we build the default app-defaults file into the program. -# -XScreenSaver_ad.h: XScreenSaver.ad - $(SHELL) $(UTILS_SRC)/ad2c XScreenSaver.ad > XScreenSaver_ad.h +dialog.o: dialog.c + $(CC) -c $(CC_ALL) $(AUTH_DEFS) $< +xscreensaver-auth: $(AUTH_OBJS) + $(CC) $(LDFLAGS) -o $@ $(AUTH_OBJS) $(AUTH_LIBS) -XScreenSaver_Xm_ad.h: XScreenSaver-Xm.ad - $(SHELL) $(UTILS_SRC)/ad2c XScreenSaver-Xm.ad > XScreenSaver_Xm_ad.h +xscreensaver-systemd: $(SYSTEMD_OBJS) + $(CC) $(LDFLAGS) -o $@ $(SYSTEMD_OBJS) $(SYSTEMD_LIBS) -lm + +xscreensaver-command: $(CMD_OBJS) + $(CC) $(LDFLAGS) -o $@ $(CMD_OBJS) $(CMD_LIBS) -@INTLTOOL_DESKTOP_RULE@ -# The executables linked in this directory. +############################################################################## +# +# Compiling the GUI, xscreensaver-settings # -xscreensaver: $(SAVER_OBJS) - $(CC) $(LDFLAGS) -o $@ $(SAVER_OBJS) $(SAVER_LIBS) $(INTL_LIBS) +############################################################################## -xscreensaver-command: $(CMD_OBJS) - $(CC) $(LDFLAGS) -o $@ $(CMD_OBJS) $(CMD_LIBS) +demo-Gtk.o: XScreenSaver_ad.h +demo-Gtk.o: demo-Gtk.c + $(CC) -c $(CC_ALL) $(GTK_DEFS) $< +demo-Gtk-conf.o: demo-Gtk-conf.c + $(CC) -c $(CC_ALL) $(GTK_DEFS) $< + +xscreensaver-settings-Gtk: $(GTK_OBJS) + $(CC) $(LDFLAGS) -o $@ $(GTK_OBJS) $(GTK_LIBS) -xscreensaver-demo: @PREFERRED_DEMO_PROGRAM@ +demo-Xm.o: XScreenSaver_ad.h +demo-Xm.o: demo-Xm.c + $(CC) -c $(CC_ALL) $(GTK_DEFS) $< +demo-Xm-widgets.o: demo-Xm-widgets.c + $(CC) -c $(CC_ALL) $(GTK_DEFS) $< + +xscreensaver-settings-Xm: $(MOTIF_OBJS) + $(CC) $(LDFLAGS) -o $@ $(MOTIF_OBJS) $(MOTIF_LIBS) + +xscreensaver-settings: @PREFERRED_DEMO_PROGRAM@ @if [ "@PREFERRED_DEMO_PROGRAM@" = "" ]; then \ echo "WARNING: neither GTK nor Motif are available," \ - "therefore no xscreensaver-demo!" ; \ + "therefore no xscreensaver-settings!" ; \ rm -f $@@EXEEXT@ ; \ else \ echo cp -p @PREFERRED_DEMO_PROGRAM@@EXEEXT@ $@@EXEEXT@ ; \ cp -p @PREFERRED_DEMO_PROGRAM@@EXEEXT@ $@@EXEEXT@ ; \ fi -xscreensaver-demo-Xm: $(DEMO_OBJS) $(MOTIF_OBJS) - $(CC) $(LDFLAGS) -o $@ $(DEMO_OBJS) $(MOTIF_OBJS) $(LIBS) $(X_LIBS) \ - $(MOTIF_LIBS) $(INTL_LIBS) $(X_PRE_LIBS) -lXt -lX11 \ - $(XDPMS_LIBS) $(XINERAMA_LIBS) -lXext $(X_EXTRA_LIBS) -xscreensaver-demo-Gtk: $(DEMO_OBJS) $(GTK_OBJS) - $(CC) $(LDFLAGS) -o $@ $(DEMO_OBJS) $(GTK_OBJS) $(LIBS) $(X_LIBS) \ - $(GTK_LIBS) $(XML_LIBS) $(INTL_LIBS) $(X_PRE_LIBS) \ - -lXt -lX11 $(XDPMS_LIBS) $(XINERAMA_LIBS) -lXext $(X_EXTRA_LIBS) +# How we build the default app-defaults file into the program. +# +XScreenSaver_ad.h:: + @TMP=/tmp/xs$$$$.h ; \ + IN="XScreenSaver.ad" ; \ + OUT=XScreenSaver_ad.h ; \ + $(UTILS_SRC)/ad2c $$IN > $$TMP ; \ + if cmp -s $$TMP $$OUT ; then \ + rm -f "$$TMP" ; \ + else \ + echo $(UTILS_SRC)/ad2c $$IN \> $$OUT ; \ + mv $$TMP $$OUT ; \ + fi -demo-Gtk.o: XScreenSaver_ad.h -demo-Xm.o: XScreenSaver_Xm_ad.h -xscreensaver.o: XScreenSaver_ad.h -xscreensaver-getimage.o: XScreenSaver_ad.h +XScreenSaver_Xm_ad.h:: + @TMP=/tmp/xs$$$$.h ; \ + IN="XScreenSaver-Xm.ad" ; \ + OUT=XScreenSaver_Xm_ad.h ; \ + $(UTILS_SRC)/ad2c $$IN > $$TMP ; \ + if cmp -s $$TMP $$OUT ; then \ + rm -f "$$TMP" ; \ + else \ + echo $(UTILS_SRC)/ad2c $$IN \> $$OUT ; \ + mv $$TMP $$OUT ; \ + fi -xscreensaver-getimage: $(GETIMG_OBJS) - $(CC) $(LDFLAGS) -o $@ $(GETIMG_OBJS) $(GETIMG_LIBS) -lm -pdf2jpeg: $(PDF2JPEG_OBJS) - $(OBJCC) $(LDFLAGS) -o $@ $(PDF2JPEG_OBJS) $(PDF2JPEG_LIBS) -lm +# Replace this with @INTLTOOL_DESKTOP_RULE@ once +# https://bugs.launchpad.net/intltool/+bug/1749904 is fixed. +.desktop.in.desktop: $(INTLTOOL_MERGE) $(top_srcdir)/po/*.po + $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) \ + $(INTLTOOL_V_MERGE_OPTIONS) -d -u \ + -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@ -xscreensaver-systemd: $(SYSTEMD_OBJS) - $(CC) $(LDFLAGS) -o $@ $(SYSTEMD_OBJS) $(SYSTEMD_LIBS) -lm -TEST_PASSWD_OBJS = test-passwd.o $(LOCK_OBJS_1) $(PASSWD_OBJS) \ - subprocs.o setuid.o splash.o prefs.o mlstring.o exec.o \ - $(SAVER_UTIL_OBJS) -test-passwd.o: XScreenSaver_ad.h +############################################################################## +# +# Debugging utilities, not built by default +# +############################################################################## -test-passwd: $(TEST_PASSWD_OBJS) XScreenSaver_ad.h - $(CC) $(LDFLAGS) -o $@ $(TEST_PASSWD_OBJS) $(SAVER_LIBS) +TESTPASS_OBJS = test-passwd.o test-passwd-b.o $(AUTH_OBJS_1) +TESTPATH_DEFS = -Dxscreensaver_auth_conv=test_auth_conv $(AUTH_DEFS) +test-passwd-b.o: XScreenSaver_ad.h +test-passwd-b.o: $(srcdir)/xscreensaver-auth.c + $(CC) -c $(CC_ALL) $(TESTPATH_DEFS) $< -o $@ +test-passwd: $(TESTPASS_OBJS) + $(CC) $(LDFLAGS) -o $@ $(TESTPASS_OBJS) $(AUTH_LIBS) test-uid: test-uid.o $(CC) $(LDFLAGS) -o $@ test-uid.o -test-xdpms: test-xdpms.o - $(CC) $(LDFLAGS) -o $@ test-xdpms.o $(LIBS) $(X_LIBS) $(XDPMS_LIBS) \ - $(X_PRE_LIBS) -lXt -lX11 -lXext $(X_EXTRA_LIBS) +TESTDPMS_LIBS = $(LIBS_PRE) $(XDPMS_LIBS) -lXt -lX11 -lXext $(LIBS_POST) +test-xdpms: test-xdpms.o blurb.o + $(CC) $(LDFLAGS) -o $@ test-xdpms.o blurb.o $(TESTDPMS_LIBS) -test-xinerama: test-xinerama.o - $(CC) $(LDFLAGS) -o $@ test-xinerama.o $(LIBS) $(X_LIBS) $(SAVER_LIBS) \ - $(X_PRE_LIBS) $(XINERAMA_LIBS) -lXt -lX11 -lXext $(X_EXTRA_LIBS) +TESTXINPUT_OBJS = test-xinput.o blurb.o xinput.o +TESTXINPUT_LIBS = $(LIBS_PRE) $(XDPMS_LIBS) -lXi -lXt -lX11 -lXext $(LIBS_POST) +test-xinput: $(TESTXINPUT_OBJS) + $(CC) $(LDFLAGS) -o $@ $(TESTXINPUT_OBJS) $(TESTXINPUT_LIBS) -test-vp: test-vp.o - $(CC) $(LDFLAGS) -o $@ test-vp.o $(LIBS) $(X_LIBS) $(SAVER_LIBS) \ - $(X_PRE_LIBS) -lXt -lX11 -lXext $(X_EXTRA_LIBS) +TESTXIN_LIBS = $(LIBS_PRE) $(XINERAMA_LIBS) -lXi -lXt -lX11 -lXext $(LIBS_POST) +test-xinerama: test-xinerama.o blurb.o + $(CC) $(LDFLAGS) -o $@ test-xinerama.o blurb.o $(TESTXIN_LIBS) -test-randr: test-randr.o - $(CC) $(LDFLAGS) -o $@ test-randr.o $(LIBS) $(X_LIBS) $(SAVER_LIBS) \ - $(X_PRE_LIBS) -lXt -lX11 -lXext $(X_EXTRA_LIBS) +TESTXKB_OBJS = test-xkb.o blurb.o +TESTXKB_LIBS = $(LIBS_PRE) $(XDPMS_LIBS) -lXi -lXt -lX11 -lXext $(LIBS_POST) +test-xkb: $(TESTXKB_OBJS) + $(CC) $(LDFLAGS) -o $@ $(TESTXKB_OBJS) $(TESTXKB_LIBS) -test-grab: test-grab.o - $(CC) $(LDFLAGS) -o $@ test-grab.o $(SAVER_LIBS) +test-vp: test-vp.o blurb.o + $(CC) $(LDFLAGS) -o $@ test-vp.o blurb.o $(GFX_LIBS) -test-apm: test-apm.o - $(CC) $(LDFLAGS) -o $@ test-apm.o $(SAVER_LIBS) -lapm +test-randr: test-randr.o blurb.o + $(CC) $(LDFLAGS) -o $@ test-randr.o blurb.o $(GFX_LIBS) -test-mlstring.o: mlstring.c -test-mlstring: test-mlstring.o - $(CC) -DTEST $(LDFLAGS) -o $@ test-mlstring.o $(SAVER_LIBS) +TESTGRAB_LIBS = $(LIBS_PRE) -lXt -lX11 -lXext $(LIBS_POST) +test-grab: test-grab.o blurb.o + $(CC) $(LDFLAGS) -o $@ test-grab.o blurb.o $(TESTGRAB_LIBS) -TEST_FADE_OBJS = test-fade.o $(UTILS_BIN)/fade.o $(DEMO_UTIL_OBJS) -test-fade: test-fade.o $(UTILS_BIN)/fade.o - $(CC) $(LDFLAGS) -o $@ $(TEST_FADE_OBJS) $(SAVER_LIBS) +TEST_FADE_OBJS = test-fade.o fade.o blurb.o atoms.o clientmsg.o xinput.o \ + $(UTILS_BIN)/visual.o $(UTILS_BIN)/resources.o $(UTILS_BIN)/usleep.o \ + $(UTILS_BIN)/logo.o $(UTILS_BIN)/minixpm.o $(UTILS_BIN)/xshm.o \ + $(UTILS_BIN)/xmu.o $(UTILS_BIN)/aligned_malloc.o +test-fade: $(TEST_FADE_OBJS) + $(CC) $(LDFLAGS) -o $@ $(TEST_FADE_OBJS) $(GFX_LIBS) -TEST_SCREENS_OBJS = test-screens.o $(DEMO_UTIL_OBJS) -test-screens.o: screens.c -test-screens: test-screens.o - $(CC) $(LDFLAGS) -o $@ $(TEST_SCREENS_OBJS) $(SAVER_LIBS) +TEST_SCREENS_OBJS = test-screens.o screens.o blurb.o +test-screens: $(TEST_SCREENS_OBJS) + $(CC) $(LDFLAGS) -o $@ $(TEST_SCREENS_OBJS) $(GFX_LIBS) -test-yarandom: test-yarandom.o - $(CC) -DTEST $(LDFLAGS) -o $@ test-yarandom.o $(UTILS_BIN)/yarandom.o +test-yarandom: test-yarandom.o blurb.o + $(CC) -DTEST $(LDFLAGS) -o $@ test-yarandom.o blurb.o $(UTILS_BIN)/yarandom.o +XDPY_DEFS = -DHAVE_GLX $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) +XDPY_LIBS = $(LIBS_PRE) -lGL -lX11 -lXext $(LIBS_POST) xdpyinfo.o: xdpyinfo.c - $(CC) -c $(INCLUDES) -DHAVE_GLX $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) \ - $(srcdir)/xdpyinfo.c - + $(CC) -c $(INCLUDES) $(XDPY_DEFS) $(srcdir)/xdpyinfo.c xdpyinfo: xdpyinfo.o - $(CC) $(LDFLAGS) -o $@ xdpyinfo.o \ - $(LIBS) $(X_LIBS) @GL_LIBS@ \ - $(X_PRE_LIBS) -lX11 -lXext $(X_EXTRA_LIBS) -lm - + $(CC) $(LDFLAGS) -o $@ xdpyinfo.o $(XDPY_LIBS) ############################################################################## # @@ -900,150 +996,89 @@ demo-Gtk-conf.o: ../config.h demo-Gtk-conf.o: $(srcdir)/demo-Gtk-conf.h demo-Gtk-conf.o: $(UTILS_SRC)/xscreensaver-intl.h demo-Gtk.o: XScreenSaver_ad.h +demo-Gtk.o: $(srcdir)/atoms.h +demo-Gtk.o: $(srcdir)/blurb.h demo-Gtk.o: ../config.h demo-Gtk.o: $(srcdir)/demo-Gtk-conf.h -demo-Gtk.o: $(srcdir)/prefs.h demo-Gtk.o: $(srcdir)/remote.h demo-Gtk.o: $(srcdir)/types.h demo-Gtk.o: $(UTILS_SRC)/resources.h demo-Gtk.o: $(UTILS_SRC)/usleep.h demo-Gtk.o: $(UTILS_SRC)/version.h demo-Gtk.o: $(UTILS_SRC)/visual.h +demo-Gtk.o: $(UTILS_SRC)/xmu.h demo-Gtk.o: $(UTILS_SRC)/xscreensaver-intl.h +demo-Xm.o: XScreenSaver_Xm_ad.h +demo-Xm.o: XScreenSaver_ad.h +demo-Xm.o: $(srcdir)/atoms.h +demo-Xm.o: $(srcdir)/blurb.h demo-Xm.o: ../config.h +demo-Xm.o: $(srcdir)/remote.h +demo-Xm.o: $(srcdir)/types.h +demo-Xm.o: $(UTILS_SRC)/resources.h +demo-Xm.o: $(UTILS_SRC)/version.h +demo-Xm.o: $(UTILS_SRC)/visual.h +demo-Xm.o: $(UTILS_SRC)/xmu.h demo-Xm-widgets.o: ../config.h -dpms.o: ../config.h -dpms.o: $(srcdir)/prefs.h -dpms.o: $(srcdir)/types.h -dpms.o: $(srcdir)/xscreensaver.h -exec.o: ../config.h -exec.o: $(srcdir)/exec.h -lock.o: $(srcdir)/auth.h -lock.o: ../config.h -lock.o: $(srcdir)/mlstring.h -lock.o: $(srcdir)/prefs.h -lock.o: $(srcdir)/types.h -lock.o: $(UTILS_SRC)/resources.h -lock.o: $(srcdir)/xscreensaver.h -mlstring.o: $(srcdir)/mlstring.h +passwd-helper.o: $(srcdir)/auth.h +passwd-helper.o: $(srcdir)/blurb.h passwd-helper.o: ../config.h -passwd-helper.o: $(srcdir)/prefs.h -passwd-helper.o: $(srcdir)/types.h -passwd-helper.o: $(srcdir)/xscreensaver.h +passwd-kerberos.o: $(srcdir)/auth.h +passwd-kerberos.o: $(srcdir)/blurb.h passwd-kerberos.o: ../config.h -passwd.o: $(srcdir)/auth.h -passwd.o: ../config.h -passwd.o: $(srcdir)/prefs.h -passwd.o: $(srcdir)/types.h -passwd.o: $(srcdir)/xscreensaver.h passwd-pam.o: $(srcdir)/auth.h +passwd-pam.o: $(srcdir)/blurb.h passwd-pam.o: ../config.h -passwd-pam.o: $(srcdir)/types.h +passwd-pwent.o: $(srcdir)/auth.h +passwd-pwent.o: $(srcdir)/blurb.h passwd-pwent.o: ../config.h -prefs.o: ../config.h -prefs.o: $(srcdir)/prefs.h -prefs.o: $(srcdir)/types.h -prefs.o: $(UTILS_SRC)/resources.h -prefs.o: $(UTILS_SRC)/version.h +remote.o: $(srcdir)/atoms.h +remote.o: $(srcdir)/blurb.h +remote.o: $(srcdir)/clientmsg.h remote.o: ../config.h remote.o: $(srcdir)/remote.h -screens.o: ../config.h -screens.o: $(srcdir)/prefs.h -screens.o: $(srcdir)/types.h -screens.o: $(UTILS_SRC)/visual.h -screens.o: $(srcdir)/xscreensaver.h -setuid.o: ../config.h -setuid.o: $(srcdir)/prefs.h -setuid.o: $(srcdir)/types.h -setuid.o: $(srcdir)/xscreensaver.h -splash.o: ../config.h -splash.o: $(srcdir)/prefs.h -splash.o: $(srcdir)/types.h -splash.o: $(UTILS_SRC)/font-retry.h -splash.o: $(UTILS_SRC)/resources.h -splash.o: $(srcdir)/xscreensaver.h -stderr.o: ../config.h -stderr.o: $(srcdir)/prefs.h -stderr.o: $(srcdir)/types.h -stderr.o: $(UTILS_SRC)/resources.h -stderr.o: $(UTILS_SRC)/visual.h -stderr.o: $(srcdir)/xscreensaver.h -subprocs.o: ../config.h -subprocs.o: $(srcdir)/exec.h -subprocs.o: $(srcdir)/prefs.h -subprocs.o: $(srcdir)/types.h -subprocs.o: $(UTILS_SRC)/visual.h -subprocs.o: $(UTILS_SRC)/yarandom.h -subprocs.o: $(srcdir)/xscreensaver.h -test-apm.o: ../config.h +test-fade.o: $(srcdir)/atoms.h +test-fade.o: $(srcdir)/blurb.h test-fade.o: ../config.h -test-fade.o: $(srcdir)/prefs.h +test-fade.o: $(srcdir)/fade.h +test-fade.o: $(srcdir)/screens.h test-fade.o: $(srcdir)/types.h -test-fade.o: $(UTILS_SRC)/fade.h +test-fade.o: $(UTILS_SRC)/resources.h test-fade.o: $(srcdir)/xscreensaver.h +test-grab.o: $(srcdir)/blurb.h test-grab.o: ../config.h -test-mlstring.o: $(srcdir)/mlstring.c -test-mlstring.o: $(srcdir)/mlstring.h -test-passwd.o: XScreenSaver_ad.h test-passwd.o: $(srcdir)/auth.h +test-passwd.o: $(srcdir)/blurb.h test-passwd.o: ../config.h -test-passwd.o: $(srcdir)/prefs.h -test-passwd.o: $(srcdir)/types.h -test-passwd.o: $(UTILS_SRC)/resources.h -test-passwd.o: $(UTILS_SRC)/version.h -test-passwd.o: $(UTILS_SRC)/visual.h -test-passwd.o: $(srcdir)/xscreensaver.h +test-randr.o: $(srcdir)/blurb.h test-randr.o: ../config.h +test-screens.o: $(srcdir)/blurb.h test-screens.o: ../config.h -test-screens.o: $(srcdir)/prefs.h -test-screens.o: $(srcdir)/screens.c -test-screens.o: $(srcdir)/types.h +test-screens.o: $(srcdir)/screens.h test-screens.o: $(UTILS_SRC)/visual.h -test-screens.o: $(srcdir)/xscreensaver.h test-uid.o: ../config.h +test-vp.o: $(srcdir)/blurb.h test-vp.o: ../config.h +test-xdpms.o: $(srcdir)/blurb.h test-xdpms.o: ../config.h +test-xinerama.o: $(srcdir)/blurb.h test-xinerama.o: ../config.h +test-xinput.o: $(srcdir)/blurb.h +test-xinput.o: ../config.h +test-xinput.o: $(srcdir)/xinput.h +test-xkb.o: $(srcdir)/blurb.h +test-xkb.o: ../config.h +test-yarandom.o: $(srcdir)/blurb.h test-yarandom.o: ../config.h test-yarandom.o: $(UTILS_SRC)/yarandom.h -timers.o: ../config.h -timers.o: $(srcdir)/prefs.h -timers.o: $(srcdir)/types.h -timers.o: $(srcdir)/xscreensaver.h -windows.o: ../config.h -windows.o: $(srcdir)/prefs.h -windows.o: $(srcdir)/types.h -windows.o: $(UTILS_SRC)/fade.h -windows.o: $(UTILS_SRC)/visual.h -windows.o: $(srcdir)/xscreensaver.h +xscreensaver-command.o: $(srcdir)/atoms.h +xscreensaver-command.o: $(srcdir)/blurb.h xscreensaver-command.o: ../config.h xscreensaver-command.o: $(srcdir)/remote.h xscreensaver-command.o: $(UTILS_SRC)/version.h -xscreensaver-getimage.o: ../config.h -xscreensaver-getimage.o: XScreenSaver_ad.h -xscreensaver-getimage.o: $(srcdir)/prefs.h -xscreensaver-getimage.o: $(srcdir)/types.h -xscreensaver-getimage.o: $(UTILS_SRC)/colorbars.h -xscreensaver-getimage.o: $(UTILS_SRC)/grabscreen.h -xscreensaver-getimage.o: $(UTILS_SRC)/resources.h -xscreensaver-getimage.o: $(UTILS_SRC)/utils.h -xscreensaver-getimage.o: $(UTILS_SRC)/version.h -xscreensaver-getimage.o: $(UTILS_SRC)/visual.h -xscreensaver-getimage.o: $(UTILS_SRC)/vroot.h -xscreensaver-getimage.o: $(UTILS_SRC)/yarandom.h -xscreensaver.o: XScreenSaver_ad.h -xscreensaver.o: $(srcdir)/auth.h -xscreensaver.o: ../config.h -xscreensaver.o: $(srcdir)/prefs.h -xscreensaver.o: $(srcdir)/types.h -xscreensaver.o: $(UTILS_SRC)/resources.h -xscreensaver.o: $(UTILS_SRC)/usleep.h -xscreensaver.o: $(UTILS_SRC)/version.h -xscreensaver.o: $(UTILS_SRC)/visual.h -xscreensaver.o: $(UTILS_SRC)/yarandom.h -xscreensaver.o: $(srcdir)/xscreensaver.h -xset.o: ../config.h -xset.o: $(srcdir)/prefs.h -xset.o: $(srcdir)/types.h -xset.o: $(srcdir)/xscreensaver.h +xscreensaver-systemd.o: $(srcdir)/blurb.h +xscreensaver-systemd.o: ../config.h +xscreensaver-systemd.o: $(UTILS_SRC)/queue.h +xscreensaver-systemd.o: $(UTILS_SRC)/version.h +xscreensaver-systemd.o: $(UTILS_SRC)/yarandom.h diff --git a/driver/XScreenSaver-Xm.ad b/driver/XScreenSaver-Xm.ad index 6b04ae9..560c48e 100644 --- a/driver/XScreenSaver-Xm.ad +++ b/driver/XScreenSaver-Xm.ad @@ -1,4 +1,4 @@ -! Resources for the Motif dialog boxes of the "xscreensaver-demo" program. +! Resources for the Motif dialog boxes of the "xscreensaver-settings" program. ! *fontList: *-helvetica-medium-r-*-*-*-120-*-*-*-iso8859-1 *demoDialog*label1.fontList: *-helvetica-medium-r-*-*-*-140-*-*-*-iso8859-1 diff --git a/driver/XScreenSaver.ad.in b/driver/XScreenSaver.ad.in index 0b51f0c..bfc6414 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 5.44 -! 20-Mar-2020 +! version 6.00 +! 01-Apr-2021 ! ! See "man xscreensaver" for more info. The latest version is always ! available at https://www.jwz.org/xscreensaver/ @@ -38,7 +38,6 @@ *dpmsStandby: 2:00:00 *dpmsSuspend: 2:00:00 *dpmsOff: 4:00:00 -*dpmsFullThrottle: False *grabDesktopImages: True *grabVideoFrames: False *chooseRandomImages: @DEFAULT_IMAGES_P@ @@ -48,9 +47,8 @@ *memoryLimit: 0 *lock: False *verbose: False -*timestamp: True *fade: True -*unfade: False +*unfade: True *fadeSeconds: 0:00:03 *fadeTicks: 20 *splash: True @@ -66,32 +64,11 @@ *textProgram: fortune *textURL: https://en.wikipedia.org/w/index.php?title=Special:NewPages&feed=rss -! When a saver writes an error message to stdout/stderr, it can be printed -! on the screen. -! -*captureStderr: True -*overlayStderr: True -*overlayTextForeground: #FFFF00 -*overlayTextBackground: #000000 -*font: *-medium-r-*-140-*-m-* - -! The default is to use these server extensions if available (as noted.) -*sgiSaverExtension: True -*xidleExtension: True -*procInterrupts: True - -! Turning this on makes pointerHysteresis not work. -*xinputExtensionDev: False - -! Set this to True if you are experiencing longstanding XFree86 bug #421 -! (xscreensaver not covering the whole screen) -*GetViewPortIsFullOfLies: False - ! This is what the "Settings" button on the splash screen runs. -*demoCommand: xscreensaver-demo +*demoCommand: xscreensaver-settings ! This is the URL loaded by the "Help" button on the splash screen, -! and by the "Documentation" menu item in xscreensaver-demo. +! and by the "Documentation" menu item in xscreensaver-settings. *helpURL: https://www.jwz.org/xscreensaver/man.html ! loadURL -- how the "Help" buttons load the helpURL (/bin/sh syntax.) @@ -99,11 +76,13 @@ ! ! And there are so very many options to choose from! ! -! Gnome 2.4, 2.6: (yelp can't display man pages, as of 2.6.3) +! Gnome 2.4, 2.6: ! @GNOME24@*loadURL: @WITH_BROWSER@ '%s' -@GNOME24@*manualCommand: gnome-terminal --title '%s manual' \ -@GNOME24@ --command '/bin/sh -c "man %s; read foo"' +@GNOME24@*manualCommand: yelp man:%s || \ +@GNOME24@ x-terminal-emulator -t '%s manual' \ +@GNOME24@ -e /bin/sh -c "man %s; read foo" + ! ! Gnome 2.2: ! @@ -118,21 +97,21 @@ ! ! non-Gnome systems: ! -@NOGNOME@*loadURL: firefox '%s' || mozilla '%s' || netscape '%s' +@NOGNOME@*loadURL: x-www-browser '%s' || firefox '%s' || chromium-browser '%s' @NOGNOME@*manualCommand: xterm -sb -fg black -bg gray75 -T '%s manual' \ @NOGNOME@ -e /bin/sh -c 'man "%s" ; read foo' -! The format used for printing the date and time in the password dialog box -! (see the strftime(3) manual page for details.) -*dateFormat: %d-%b-%y (%a); %I:%M %p -! For day month date: -! *dateFormat: %a %b %d, %I:%M %p -! To show the time only: +! The strftime(3) format string for printing the time on the password dialog. +*dateFormat: %I:%M %p, %a %b %e +! DD MMM: +! *dateFormat: %I:%M %p, %e %b (%a) +! 12 hour time only: ! *dateFormat: %I:%M %p -! For 24 hour time: +! 24 hour time only: ! *dateFormat: %H:%M - +! ISO 8601: +! *dateFormat: %Y-%m-%d %H:%M:%S ! This command is executed by the "New Login" button on the lock dialog. ! (That button does not appear on the dialog if this program does not exist.) @@ -142,22 +121,11 @@ ! @NEW_LOGIN_COMMAND_P@*newLoginCommand: @NEW_LOGIN_COMMAND@ - -! External command used to help xscreensaver aquire the mouse/keyboard -! grab. (I.e. some script that makes VMware release it). -! This is called before xscreensaver tries to aquire the grab with -! "pre" as parameter, and again with "post" after xscreensaver is done -! (trying to) aquire the grab. -! -*externalUngrabCommand: - - -! Change these at your peril: -! -XScreenSaver.pointerPollTime: 0:00:05 +! Mouse motions less than this many pixels per second are ignored. +! This helps with cats, trucks and earthquakes. XScreenSaver.pointerHysteresis: 10 -XScreenSaver.initialDelay: 0:00:00 -XScreenSaver.windowCreationTimeout: 0:00:30 + +! Change this at your peril: XScreenSaver.bourneShell: /bin/sh @@ -167,176 +135,179 @@ XScreenSaver.bourneShell: /bin/sh ! !============================================================================= -! Note, the daemon uses Xlib XLoadFont, not Xft. If these fonts don't -! exist, arcane heuristics are applied until we find something similar. -! -*Dialog.headingFont: -*-helvetica-bold-r-*-*-*-180-*-*-*-*-iso8859-1 -*Dialog.bodyFont: -*-helvetica-bold-r-*-*-*-140-*-*-*-*-iso8859-1 -*Dialog.labelFont: -*-helvetica-bold-r-*-*-*-140-*-*-*-*-iso8859-1 -*Dialog.unameFont: -*-helvetica-bold-r-*-*-*-120-*-*-*-*-iso8859-1 -*Dialog.buttonFont: -*-helvetica-bold-r-*-*-*-140-*-*-*-*-iso8859-1 -*Dialog.dateFont: -*-helvetica-medium-r-*-*-*-80-*-*-*-*-iso8859-1 - -! Helvetica asterisks look terrible. -*passwd.passwdFont: -*-courier-bold-r-*-*-*-140-*-*-*-iso8859-1 +! Which of the following color schemes is in use for the unlock dialog. +*dialogTheme: default -! Whether to display the local host name in the unlock dialog. -*passwd.uname: True +! Resources for theme names are downcased with spaces stripped. +*themeNames: Default, Borderless, Dark Gray, Borderless Black, \ + Green Black, White, Blue, Aqua Black, Wine -! Whether typed passwords should echo as asterisks (true) or nothing (false) -*passwd.asterisks: True - - -! The default color scheme for the unlock and splash dialogs. -! This looks pretty close to the default Gtk theme. -! -*Dialog.foreground: #000000 -*Dialog.background: #E6E6E6 -*Dialog.Button.foreground: #000000 -*Dialog.Button.background: #F5F5F5 -*Dialog.text.foreground: #000000 -*Dialog.text.background: #FFFFFF -*Dialog.topShadowColor: #FFFFFF -*Dialog.bottomShadowColor: #CECECE -*Dialog.logo.width: 210 -*Dialog.logo.height: 210 -*Dialog.internalBorderWidth: 24 -*Dialog.borderWidth: 1 -*Dialog.shadowThickness: 2 -*passwd.thermometer.foreground: #4464AC -*passwd.thermometer.background: #FFFFFF -*passwd.thermometer.width: 8 - - -! A few other example color schemes. -! -! A convenient way to debug these is to build and run "driver/test-passwd". -! Remember that configure overwrites XScreenSaver.ad from XScreenSaver.ad.in. +*Dialog.headingFont: sans-serif bold 16 +*Dialog.bodyFont: sans-serif 14 +*Dialog.errorFont: sans-serif bold 14 +*Dialog.labelFont: sans-serif bold 14 +*Dialog.unameFont: sans-serif 12 +*Dialog.buttonFont: sans-serif bold 14 +*Dialog.dateFont: sans-serif 9 +! Whether to display the local host name in the unlock dialog. +*passwd.uname: True + +! Whether typed passwords should echo as asterisks, or as nothing. +*passwd.asterisks: True + +! The default theme is similar to the Gtk defaults. +! +*default.Dialog.foreground: #000000 +*default.Dialog.background: #E6E6E6 +*default.Dialog.button.foreground: #000000 +*default.Dialog.button.background: #F5F5F5 +*default.Dialog.logo.background: #BBBBBB +*default.Dialog.text.foreground: #000000 +*default.Dialog.error.foreground: #FF0000 +*default.Dialog.text.background: #FFFFFF +*default.Dialog.topShadowColor: #FFFFFF +*default.Dialog.bottomShadowColor: #CECECE +*default.Dialog.shadowWidth: 2 +*default.Dialog.logo.width: 210 +*default.Dialog.logo.height: 210 +*default.Dialog.thermometer.foreground: #4464AC +*default.Dialog.thermometer.background: #FFFFFF +*default.Dialog.thermometer.width: 8 +*default.Dialog.borderColor: #CECECE +*default.Dialog.borderWidth: 0 +*default.Dialog.internalPadding: 24 ! Borderless theme: ! -! *Dialog.topShadowColor: #E6E6E6 -! *Dialog.bottomShadowColor: #E6E6E6 -! *passwd.thermometer.width: 6 - +*borderless.Dialog.topShadowColor: #E6E6E6 +*borderless.Dialog.button.background: #FFFFFF +*borderless.Dialog.bottomShadowColor: #E6E6E6 +*borderless.Dialog.logo.background: #E6E6E6 +*borderless.Dialog.borderColor: #888888 +*borderless.Dialog.thermometer.width: 6 +*borderless.Dialog.borderWidth: 1 ! Dark gray theme: ! -! *Dialog.foreground: #CCCCCC -! *Dialog.background: #333333 -! *Dialog.topShadowColor: #444444 -! *Dialog.bottomShadowColor: #111111 -! *Dialog.text.foreground: #DDDDDD -! *Dialog.text.background: #666666 -! *Dialog.Button.foreground: #CCCCCC -! *Dialog.Button.background: #666666 -! *passwd.thermometer.foreground: #4464AC -! *passwd.thermometer.background: #666666 - - -! Black borderless theme: -! -! *Dialog.foreground: #CCCCCC -! *Dialog.background: #000000 -! *Dialog.topShadowColor: #000000 -! *Dialog.bottomShadowColor: #000000 -! *Dialog.text.foreground: #CCCCCC -! *Dialog.text.background: #000000 -! *Dialog.Button.foreground: #CCCCCC -! *Dialog.Button.background: #333333 -! *passwd.thermometer.foreground: #CCCCCC -! *passwd.thermometer.background: #333333 -! *passwd.thermometer.width: 3 - +*darkgray.Dialog.foreground: #CCCCCC +*darkgray.Dialog.background: #333333 +*darkgray.Dialog.topShadowColor: #444444 +*darkgray.Dialog.bottomShadowColor: #111111 +*darkgray.Dialog.borderColor: #111111 +*darkgray.Dialog.text.foreground: #DDDDDD +*darkgray.Dialog.text.background: #666666 +*darkgray.Dialog.button.foreground: #CCCCCC +*darkgray.Dialog.button.background: #666666 +*darkgray.Dialog.logo.background: #444444 +*darkgray.Dialog.thermometer.foreground: #4464AC +*darkgray.Dialog.thermometer.background: #666666 +*darkgray.Dialog.borderWidth: 0 + +! Borderless black theme: +! +*borderlessblack.Dialog.foreground: #CCCCCC +*borderlessblack.Dialog.background: #000000 +*borderlessblack.Dialog.topShadowColor: #000000 +*borderlessblack.Dialog.bottomShadowColor: #000000 +*borderlessblack.Dialog.text.foreground: #CCCCCC +*borderlessblack.Dialog.text.background: #000000 +*borderlessblack.Dialog.button.foreground: #CCCCCC +*borderlessblack.Dialog.button.background: #333333 +*borderlessblack.Dialog.logo.background: #000000 +*borderlessblack.Dialog.thermometer.foreground: #CCCCCC +*borderlessblack.Dialog.thermometer.background: #333333 +*borderlessblack.Dialog.thermometer.width: 3 +*borderlessblack.Dialog.borderColor: #333333 +*borderlessblack.Dialog.borderWidth: 1 ! Green on black theme: ! -! *Dialog.foreground: #00FF00 -! *Dialog.background: #000000 -! *Dialog.topShadowColor: #000000 -! *Dialog.bottomShadowColor: #000000 -! *Dialog.shadowThickness: 1 -! *Dialog.text.foreground: #00FF00 -! *Dialog.text.background: #006600 -! *Dialog.Button.foreground: #00FF00 -! *Dialog.Button.background: #006600 -! *passwd.thermometer.foreground: #00CC00 -! *passwd.thermometer.background: #006600 - +*greenblack.Dialog.foreground: #00FF00 +*greenblack.Dialog.background: #000000 +*greenblack.Dialog.topShadowColor: #000000 +*greenblack.Dialog.bottomShadowColor: #000000 +*greenblack.Dialog.shadowWidth: 1 +*greenblack.Dialog.text.foreground: #00FF00 +*greenblack.Dialog.text.background: #006600 +*greenblack.Dialog.button.foreground: #00FF00 +*greenblack.Dialog.button.background: #006600 +*greenblack.Dialog.logo.background: #000000 +*greenblack.Dialog.thermometer.foreground: #00CC00 +*greenblack.Dialog.thermometer.background: #006600 +*greenblack.Dialog.borderColor: #006600 +*greenblack.Dialog.borderWidth: 1 ! White theme: ! -! *Dialog.foreground: #000000 -! *Dialog.background: #FFFFFF -! *Dialog.topShadowColor: #CCCCCC -! *Dialog.bottomShadowColor: #CCCCCC -! *Dialog.shadowThickness: 1 -! *Dialog.text.foreground: #000000 -! *Dialog.text.background: #FFFFFF -! *Dialog.Button.foreground: #000000 -! *Dialog.Button.background: #FFFFFF - +*white.Dialog.foreground: #000000 +*white.Dialog.background: #FFFFFF +*white.Dialog.topShadowColor: #CCCCCC +*white.Dialog.bottomShadowColor: #CCCCCC +*white.Dialog.shadowWidth: 1 +*white.Dialog.borderColor: #CCCCCC +*white.Dialog.text.foreground: #000000 +*white.Dialog.text.background: #FFFFFF +*white.Dialog.button.foreground: #000000 +*white.Dialog.button.background: #FFFFFF +*white.Dialog.logo.background: #FFFFFF +*white.Dialog.borderWidth: 0 ! Blue theme: ! -! *Dialog.foreground: #000000 -! *Dialog.background: #BBCCDD -! *Dialog.topShadowColor: #CCDDEE -! *Dialog.bottomShadowColor: #AABBCC -! *Dialog.text.foreground: #000000 -! *Dialog.text.background: #DDEEFF -! *Dialog.Button.foreground: #000000 -! *Dialog.Button.background: #DDEEFF -! *passwd.thermometer.foreground: #5566AA -! *passwd.thermometer.background: #BBCCDD - +*blue.Dialog.foreground: #000000 +*blue.Dialog.background: #BBCCDD +*blue.Dialog.topShadowColor: #CCDDEE +*blue.Dialog.bottomShadowColor: #AABBCC +*blue.Dialog.borderColor: #AABBCC +*blue.Dialog.text.foreground: #000000 +*blue.Dialog.text.background: #DDEEFF +*blue.Dialog.button.foreground: #000000 +*blue.Dialog.button.background: #DDEEFF +*blue.Dialog.logo.background: #BBCCDD +*blue.Dialog.thermometer.foreground: #5566AA +*blue.Dialog.thermometer.background: #BBCCDD +*blue.Dialog.borderWidth: 0 ! Aqua on black borderless theme: ! -! *Dialog.foreground: #00EFEF -! *Dialog.background: #000000 -! *Dialog.topShadowColor: #000000 -! *Dialog.bottomShadowColor: #000000 -! *Dialog.Button.foreground: #000000 -! *Dialog.Button.background: #2244EE -! *Dialog.text.foreground: #2244EE -! *Dialog.text.background: #EEEEEE -! *Dialog.internalBorderWidth: 36 -! *Dialog.borderWidth: 4 -! *Dialog.shadowThickness: 2 -! *passwd.thermometer.foreground: #2244EE -! *passwd.thermometer.background: #000088 - +*aquablack.Dialog.foreground: #00EFEF +*aquablack.Dialog.background: #000000 +*aquablack.Dialog.topShadowColor: #000000 +*aquablack.Dialog.bottomShadowColor: #000000 +*aquablack.Dialog.shadowWidth: 2 +*aquablack.Dialog.button.foreground: #000000 +*aquablack.Dialog.button.background: #2244EE +*aquablack.Dialog.logo.background: #000000 +*aquablack.Dialog.text.foreground: #2244EE +*aquablack.Dialog.text.background: #EEEEEE +*aquablack.Dialog.thermometer.foreground: #2244EE +*aquablack.Dialog.thermometer.background: #000088 +*aquablack.Dialog.borderColor: #000066 +*aquablack.Dialog.borderWidth: 1 +*aquablack.Dialog.internalPadding: 36 ! Wine theme, similar to the login screen of "Ubuntu 18.04 Community". ! -! *Dialog.foreground: #AD8FA6 -! *Dialog.background: #2C041E -! *Dialog.topShadowColor: #2C041E -! *Dialog.bottomShadowColor: #2C041E -! *Dialog.text.foreground: #706B70 -! *Dialog.text.background: #F9F9F8 -! *Dialog.Button.foreground: #CFC8CB -! *Dialog.Button.background: #4D2946 -! *passwd.thermometer.foreground: #AD8FA6 -! *passwd.thermometer.background: #4D2946 -! *passwd.thermometer.width: 6 +*wine.Dialog.foreground: #AD8FA6 +*wine.Dialog.background: #2C041E +*wine.Dialog.topShadowColor: #2C041E +*wine.Dialog.bottomShadowColor: #2C041E +*wine.Dialog.text.foreground: #706B70 +*wine.Dialog.text.background: #F9F9F8 +*wine.Dialog.button.foreground: #5F585B +*wine.Dialog.logo.background: #2C041E +*wine.Dialog.thermometer.foreground: #AD8FA6 +*wine.Dialog.thermometer.background: #4D2946 +*wine.Dialog.borderColor: #4D2946 +*wine.Dialog.thermometer.width: 6 +*wine.Dialog.borderWidth: 1 -! Static text in the dialog boxes: +! For displaying error messages about crashed screen savers. ! -*passwd.heading.label: XScreenSaver %s -*passwd.body.label: This screen is locked. -*passwd.unlock.label: OK -*passwd.login.label: New Login -*passwd.user.label: Username: - -*splash.heading.label: XScreenSaver %s -*splash.body.label: Copyright \251 1991-2020 by -*splash.body2.label: Jamie Zawinski -*splash.demo.label: Settings -*splash.help.label: Help +*errorFont: sans-serif bold 18 +*errorColor: #FF0000 !============================================================================= @@ -347,7 +318,7 @@ XScreenSaver.bourneShell: /bin/sh ! If you want to disable a screensaver, DO NOT remove it from this list: ! instead, mark it as inactive by placing a "-" at the beginning of the line. ! -! You can use the `xscreensaver-demo' program to edit the current list of +! You can use the `xscreensaver-settings' program to edit the current list of ! screen savers interactively. ! !============================================================================= @@ -589,111 +560,132 @@ XScreenSaver.bourneShell: /bin/sh vfeedback -root \n\ @GL_KLUDGE@ GL: deepstars -root \n\ @GL_KLUDGE@ GL: gravitywell -root \n\ +@GL_KLUDGE@ GL: beats -root \n\ +@GL_KLUDGE@ GL: covid19 -root \n\ @GL_KLUDGE@ GL: etruscanvenus -root \n\ -@GL_KLUDGE@ GL: gibson -root \n +@GL_KLUDGE@ GL: gibson -root \n\ +@GL_KLUDGE@ GL: headroom -root \n\ +@GL_KLUDGE@ GL: sphereeversion -root \n !============================================================================= ! ! Pretty names for the hacks that have unusual capitalization. -! Used by xscreensaver-demo. +! Used by xscreensaver-settings. ! !============================================================================= -*hacks.antinspect.name: AntInspect -*hacks.antmaze.name: AntMaze -*hacks.antspotlight.name: AntSpotlight -*hacks.binaryring.name: BinaryRing -*hacks.blinkbox.name: BlinkBox -*hacks.blitspin.name: BlitSpin -*hacks.blocktube.name: BlockTube -*hacks.bouncingcow.name: BouncingCow -*hacks.boxfit.name: BoxFit +*hacks.antinspect.name: Ant Inspect +*hacks.antmaze.name: Ant Maze +*hacks.antspotlight.name: Ant Spotlight +*hacks.apple2.name: Apple ][ +*hacks.binaryring.name: Binary Ring +*hacks.blinkbox.name: Blink Box +*hacks.blitspin.name: Blit Spin +*hacks.blocktube.name: Block Tube +*hacks.bouncingcow.name: Bouncing Cow +*hacks.boxfit.name: Box Fit *hacks.bsod.name: BSOD -*hacks.bubble3d.name: Bubble3D -*hacks.ccurve.name: CCurve -*hacks.cloudlife.name: CloudLife -*hacks.companioncube.name: CompanionCube -*hacks.cubestack.name: CubeStack -*hacks.cubestorm.name: CubeStorm -*hacks.cubetwist.name: CubeTwist -*hacks.cubicgrid.name: CubicGrid -*hacks.cwaves.name: CWaves -*hacks.dangerball.name: DangerBall -*hacks.decayscreen.name: DecayScreen -*hacks.deepstars.name: DeepStars +*hacks.bubble3d.name: Bubble 3D +*hacks.ccurve.name: C Curve +*hacks.cityflow.name: City Flow +*hacks.cloudlife.name: Cloud Life +*hacks.companioncube.name: Companion Cube +*hacks.covid19.name: COVID19 +*hacks.cube21.name: Cube 21 +*hacks.cubestack.name: Cube Stack +*hacks.cubestorm.name: Cube Storm +*hacks.cubetwist.name: Cube Twist +*hacks.cubicgrid.name: Cubic Grid +*hacks.cwaves.name: C Waves +*hacks.dangerball.name: Danger Ball +*hacks.decayscreen.name: Decay Screen +*hacks.deepstars.name: Deep Stars *hacks.dnalogo.name: DNA Logo -*hacks.dymaxionmap.name: DymaxionMap -*hacks.energystream.name: EnergyStream -*hacks.etruscanvenus.name: EtruscanVenus -*hacks.euler2d.name: Euler2D -*hacks.fadeplot.name: FadePlot -*hacks.filmleader.name: FilmLeader -*hacks.flipflop.name: FlipFlop -*hacks.flipscreen3d.name: FlipScreen3D -*hacks.fliptext.name: FlipText -*hacks.fluidballs.name: FluidBalls -*hacks.flyingtoasters.name: FlyingToasters -*hacks.fontglide.name: FontGlide -*hacks.fuzzyflakes.name: FuzzyFlakes -*hacks.geodesicgears.name: GeodesicGears +*hacks.dymaxionmap.name: Dymaxion Map +*hacks.energystream.name: Energy Stream +*hacks.etruscanvenus.name: Etruscan Venus +*hacks.euler2d.name: Euler 2D +*hacks.fadeplot.name: Fade Plot +*hacks.fiberlamp.name: Fiber Lamp +*hacks.filmleader.name: Film Leader +*hacks.flipflop.name: Flip Flop +*hacks.flipscreen3d.name: Flip Screen 3D +*hacks.fliptext.name: Flip Text +*hacks.fluidballs.name: Fluid Balls +*hacks.flyingtoasters.name: Flying Toasters +*hacks.fontglide.name: Font Glide +*hacks.fuzzyflakes.name: Fuzzy Flakes +*hacks.geodesicgears.name: Geodesic Gears *hacks.gflux.name: GFlux +*hacks.glblur.name: GL Blur +*hacks.glcells.name: GL Cells *hacks.gleidescope.name: Gleidescope -*hacks.glforestfire.name: GLForestFire +*hacks.glforestfire.name: GL Forest Fire +*hacks.glhanoi.name: GL Hanoi *hacks.glitchpeg.name: GlitchPEG -*hacks.gravitywell.name: GravityWell -*hacks.hyperball.name: HyperBall -*hacks.hypercube.name: HyperCube +*hacks.glknots.name: GL Knots +*hacks.glmatrix.name: GL Matrix +*hacks.glplanet.name: GL Planet +*hacks.glschool.name: GL School +*hacks.glslideshow.name: GL Slideshow +*hacks.glsnake.name: GL Snake +*hacks.gltext.name: GL Text +*hacks.gravitywell.name: Gravity Well +*hacks.hexstrut.name: Hex Strut *hacks.ifs.name: IFS -*hacks.imsmap.name: IMSMap -*hacks.jigglypuff.name: JigglyPuff -*hacks.juggler3d.name: Juggler3D -*hacks.lcdscrub.name: LCDscrub +*hacks.imsmap.name: IMS Map +*hacks.jigglypuff.name: Jiggly Puff +*hacks.juggler3d.name: Juggler 3D +*hacks.lcdscrub.name: LCD Scrub *hacks.lmorph.name: LMorph *hacks.m6502.name: m6502 -*hacks.maze3d.name: Maze3D -*hacks.memscroller.name: MemScroller -*hacks.metaballs.name: MetaBalls -*hacks.mirrorblob.name: MirrorBlob -*hacks.moebiusgears.name: MoebiusGears -*hacks.morph3d.name: Morph3D -*hacks.nerverot.name: NerveRot -*hacks.noseguy.name: NoseGuy -*hacks.popsquares.name: PopSquares -*hacks.projectiveplane.name:ProjectivePlane -*hacks.quasicrystal.name: QuasiCrystal -*hacks.raverhoop.name: RaverHoop -*hacks.razzledazzle.name: RazzleDazzle -*hacks.rd-bomb.name: RDbomb -*hacks.rdbomb.name: RDbomb -*hacks.romanboy.name: RomanBoy -*hacks.rotzoomer.name: RotZoomer -*hacks.rubikblocks.name: RubikBlocks +*hacks.maze3d.name: Maze 3D +*hacks.memscroller.name: Mem Scroller +*hacks.metaballs.name: Meta Balls +*hacks.mirrorblob.name: Mirror Blob +*hacks.moebius.name: Möbius +*hacks.moebiusgears.name: Möbius Gears +*hacks.moire.name: Moiré +*hacks.moire2.name: Moiré 2 +*hacks.morph3d.name: Morph 3D +*hacks.nerverot.name: Nerve Rot +*hacks.noseguy.name: Nose Guy +*hacks.pacman.name: Pac-Man +*hacks.photopile.name: Photo Pile +*hacks.popsquares.name: Pop Squares +*hacks.projectiveplane.name:Projective Plane +*hacks.quasicrystal.name: Quasi-Crystal +*hacks.raverhoop.name: Raver Hoop +*hacks.razzledazzle.name: Razzle Dazzle +*hacks.rd-bomb.name: RD-Bomb +*hacks.rd-bomb.name: RD-Bomb +*hacks.romanboy.name: Roman Boy +*hacks.rotzoomer.name: Rot Zoomer +*hacks.rubikblocks.name: Rubik Blocks *hacks.sballs.name: SBalls -*hacks.shadebobs.name: ShadeBobs -*hacks.sierpinski3d.name: Sierpinski3D -*hacks.skytentacles.name: SkyTentacles -*hacks.slidescreen.name: SlideScreen -*hacks.speedmine.name: SpeedMine -*hacks.splitflap.name: SplitFlap -*hacks.starwars.name: StarWars -*hacks.stonerview.name: StonerView +*hacks.shadebobs.name: Shade Bobs +*hacks.sierpinski3d.name: Sierpinski 3D +*hacks.skytentacles.name: Sky Tentacles +*hacks.slidescreen.name: Slide Screen +*hacks.speedmine.name: Speed Mine +*hacks.sphereeversion.name: Sphere Eversion +*hacks.splitflap.name: Split-Flap +*hacks.starwars.name: Star Wars +*hacks.stonerview.name: Stoner View *hacks.t3d.name: T3D -*hacks.testx11.name: TestX11 -*hacks.timetunnel.name: TimeTunnel -*hacks.topblock.name: TopBlock -*hacks.tronbit.name: TronBit -*hacks.unknownpleasures.name:UnknownPleasures +*hacks.testx11.name: Test X11 +*hacks.timetunnel.name: Time Tunnel +*hacks.topblock.name: Top Block +*hacks.tronbit.name: Tron Bit +*hacks.unknownpleasures.name:Unknown Pleasures *hacks.vfeedback.name: VFeedback -*hacks.vidwhacker.name: VidWhacker -*hacks.webcollage.name: WebCollage -*hacks.whirlwindwarp.name: WhirlWindWarp -*hacks.winduprobot.name: WindupRobot +*hacks.vidwhacker.name: Vid Whacker +*hacks.webcollage.name: Web Collage +*hacks.whirlwindwarp.name: Whirlwind Warp +*hacks.winduprobot.name: Windup Robot *hacks.xanalogtv.name: XAnalogTV *hacks.xrayswarm.name: XRaySwarm -! obsolete, but still used by xscreensaver-demo-Xm. -*hacks.documentation.isInstalled: True - ! (xrdb prevention kludge: whole file) */ diff --git a/driver/XScreenSaver_Xm_ad.h b/driver/XScreenSaver_Xm_ad.h new file mode 100644 index 0000000..371e0a2 --- /dev/null +++ b/driver/XScreenSaver_Xm_ad.h @@ -0,0 +1,108 @@ +"*fontList: *-helvetica-medium-r-*-*-*-120-*-*-*-iso8859-1", +"*demoDialog*label1.fontList: *-helvetica-medium-r-*-*-*-140-*-*-*-iso8859-1", +"*cmdText.fontList: *-courier-medium-r-*-*-*-120-*-*-*-iso8859-1", +"*label0.fontList: *-helvetica-bold-r-*-*-*-140-*-*-*-iso8859-1", +"XScreenSaver*doc.fontList: *-helvetica-medium-r-*-*-*-100-*-*-*-iso8859-1", +"*foreground: #000000", +"*background: #C0C0C0", +"*XmTextField.foreground: #000000", +"*XmTextField.background: #FFFFFF", +"*list.foreground: #000000", +"*list.background: #FFFFFF", +"*ApplicationShell.title: XScreenSaver", +"*warning.title: XScreenSaver", +"*warning_popup.title: XScreenSaver", +"*allowShellResize: True", +"*autoUnmanage: False", +"*menubar*file.labelString: File", +"*menubar*file.mnemonic: F", +"*file.blank.labelString: Blank Screen Now", +"*file.blank.mnemonic: B", +"*file.lock.labelString: Lock Screen Now", +"*file.lock.mnemonic: L", +"*file.kill.labelString: Kill Daemon", +"*file.kill.mnemonic: K", +"*file.restart.labelString: Restart Daemon", +"*file.restart.mnemonic: R", +"*file.exit.labelString: Exit", +"*file.exit.mnemonic: E", +"*menubar*edit.labelString: Edit", +"*menubar*edit.mnemonic: E", +"*edit.cut.labelString: Cut", +"*edit.cut.mnemonic: u", +"*edit.copy.labelString: Copy", +"*edit.copy.mnemonic: C", +"*edit.paste.labelString: Paste", +"*edit.paste.mnemonic: P", +"*menubar*help.labelString: Help", +"*menubar*help.mnemonic: H", +"*help.about.labelString: About...", +"*help.about.mnemonic: A", +"*help.docMenu.labelString: Documentation...", +"*help.docMenu.mnemonic: D", +"*demoTab.marginWidth: 10", +"*optionsTab.marginWidth: 10", +"*XmScrolledWindow.topOffset: 10", +"*XmScrolledWindow.leftOffset: 10", +"*demoTab.topOffset: 4", +"*form1.bottomOffset: 10", +"*form3.leftOffset: 10", +"*form3.rightOffset: 10", +"*frame.topOffset: 10", +"*frame.bottomOffset: 10", +"*enabled.topOffset: 10", +"*visLabel.topOffset: 10", +"*combo.topOffset: 10", +"*form4.bottomOffset: 4", +"*hr.bottomOffset: 4", +"*XmComboBox.marginWidth: 0", +"*XmComboBox.marginHeight: 0", +"*demo.marginWidth: 30", +"*demo.marginHeight: 4", +"*man.marginWidth: 10", +"*man.marginHeight: 4", +"*down.leftOffset: 40", +"*down.marginWidth: 4", +"*down.marginHeight: 4", +"*up.marginWidth: 4", +"*up.marginHeight: 4", +"*frame.traversalOn: False", +"*list.automaticSelection: True", +"*list.visibleItemCount: 20", +"*doc.columns: 60", +"*combo.columns: 11", +"*demoTab.labelString: Graphics Demos", +"*optionsTab.labelString: Screensaver Options", +"*down.labelString: \\\\/ ", +"*up.labelString: /\\\\ ", +"*frameLabel.labelString: ", +"*cmdLabel.labelString: Command Line:", +"*cmdLabel.alignment: ALIGNMENT_BEGINNING", +"*enabled.labelString: Enabled", +"*visLabel.labelString: Visual:", +"*visLabel.alignment: ALIGNMENT_END", +"*visLabel.leftOffset: 20", +"*demo.labelString: Demo", +"*man.labelString: Documentation...", +"*done.labelString: Quit", +"*preferencesLabel.labelString: XScreenSaver Parameters", +"*timeoutLabel.labelString: Saver Timeout", +"*cycleLabel.labelString: Cycle Timeout", +"*fadeSecondsLabel.labelString: Fade Duration", +"*fadeTicksLabel.labelString: Fade Ticks", +"*lockLabel.labelString: Lock Timeout", +"*passwdLabel.labelString: Password Timeout", +"*preferencesForm*XmTextField.columns: 8", +"*verboseToggle.labelString: Verbose", +"*cmapToggle.labelString: Install Colormap", +"*fadeToggle.labelString: Fade Colormap", +"*unfadeToggle.labelString: Unfade Colormap", +"*lockToggle.labelString: Require Password", +"*OK.marginWidth: 30", +"*OK.marginHeight: 4", +"*OK.leftOffset: 10", +"*OK.bottomOffset: 10", +"*Cancel.marginWidth: 30", +"*Cancel.marginHeight: 4", +"*Cancel.rightOffset: 10", +"*Cancel.bottomOffset: 10", diff --git a/driver/XScreenSaver_ad.h b/driver/XScreenSaver_ad.h new file mode 100644 index 0000000..e5c4790 --- /dev/null +++ b/driver/XScreenSaver_ad.h @@ -0,0 +1,534 @@ +"#error Do not run app-defaults files through xrdb!", +"#error That does not do what you might expect.", +"#error Put this file in /usr/lib/X11/app-defaults/XScreenSaver instead.", +"*mode: random", +"*timeout: 0:10:00", +"*cycle: 0:10:00", +"*lockTimeout: 0:00:00", +"*passwdTimeout: 0:00:30", +"*dpmsEnabled: False", +"*dpmsQuickoffEnabled: False", +"*dpmsStandby: 2:00:00", +"*dpmsSuspend: 2:00:00", +"*dpmsOff: 4:00:00", +"*grabDesktopImages: True", +"*grabVideoFrames: False", +"*chooseRandomImages: True", +"*imageDirectory: /usr/share/wallpapers/", +"*nice: 10", +"*memoryLimit: 0", +"*lock: False", +"*verbose: False", +"*fade: True", +"*unfade: True", +"*fadeSeconds: 0:00:03", +"*fadeTicks: 20", +"*splash: True", +"*splashDuration: 0:00:05", +"*visualID: default", +"*installColormap: True", +"*ignoreUninstalledPrograms: False", +"*authWarningSlack: 20", +"*textMode: file", +"*textLiteral: XScreenSaver", +"*textFile: /usr/share/doc/xserver-common/copyright", +"*textProgram: fortune", +"*textURL: https://en.wikipedia.org/w/index.php?title=Special:NewPages&feed=rss", +"*demoCommand: xscreensaver-settings", +"*helpURL: https://www.jwz.org/xscreensaver/man.html", +"*loadURL: gnome-open '%s'", +"*manualCommand: yelp man:%s || \ + x-terminal-emulator -t '%s manual' \ + -e /bin/sh -c \"man %s; read foo\"", +"*dateFormat: %I:%M %p, %a %b %e", +"*newLoginCommand: dm-tool switch-to-greeter", +"XScreenSaver.pointerHysteresis: 10", +"XScreenSaver.bourneShell: /bin/sh", +"*dialogTheme: default", +"*themeNames: Default, Borderless, Dark Gray, Borderless Black, \ + Green Black, White, Blue, Aqua Black, Wine", +"*Dialog.headingFont: sans-serif bold 16", +"*Dialog.bodyFont: sans-serif 14", +"*Dialog.errorFont: sans-serif bold 14", +"*Dialog.labelFont: sans-serif bold 14", +"*Dialog.unameFont: sans-serif 12", +"*Dialog.buttonFont: sans-serif bold 14", +"*Dialog.dateFont: sans-serif 9", +"*passwd.uname: True", +"*passwd.asterisks: True", +"*default.Dialog.foreground: #000000", +"*default.Dialog.background: #E6E6E6", +"*default.Dialog.button.foreground: #000000", +"*default.Dialog.button.background: #F5F5F5", +"*default.Dialog.logo.background: #BBBBBB", +"*default.Dialog.text.foreground: #000000", +"*default.Dialog.error.foreground: #FF0000", +"*default.Dialog.text.background: #FFFFFF", +"*default.Dialog.topShadowColor: #FFFFFF", +"*default.Dialog.bottomShadowColor: #CECECE", +"*default.Dialog.shadowWidth: 2", +"*default.Dialog.logo.width: 210", +"*default.Dialog.logo.height: 210", +"*default.Dialog.thermometer.foreground: #4464AC", +"*default.Dialog.thermometer.background: #FFFFFF", +"*default.Dialog.thermometer.width: 8", +"*default.Dialog.borderColor: #CECECE", +"*default.Dialog.borderWidth: 0", +"*default.Dialog.internalPadding: 24", +"*borderless.Dialog.topShadowColor: #E6E6E6", +"*borderless.Dialog.button.background: #FFFFFF", +"*borderless.Dialog.bottomShadowColor: #E6E6E6", +"*borderless.Dialog.logo.background: #E6E6E6", +"*borderless.Dialog.borderColor: #888888", +"*borderless.Dialog.thermometer.width: 6", +"*borderless.Dialog.borderWidth: 1", +"*darkgray.Dialog.foreground: #CCCCCC", +"*darkgray.Dialog.background: #333333", +"*darkgray.Dialog.topShadowColor: #444444", +"*darkgray.Dialog.bottomShadowColor: #111111", +"*darkgray.Dialog.borderColor: #111111", +"*darkgray.Dialog.text.foreground: #DDDDDD", +"*darkgray.Dialog.text.background: #666666", +"*darkgray.Dialog.button.foreground: #CCCCCC", +"*darkgray.Dialog.button.background: #666666", +"*darkgray.Dialog.logo.background: #444444", +"*darkgray.Dialog.thermometer.foreground: #4464AC", +"*darkgray.Dialog.thermometer.background: #666666", +"*darkgray.Dialog.borderWidth: 0", +"*borderlessblack.Dialog.foreground: #CCCCCC", +"*borderlessblack.Dialog.background: #000000", +"*borderlessblack.Dialog.topShadowColor: #000000", +"*borderlessblack.Dialog.bottomShadowColor: #000000", +"*borderlessblack.Dialog.text.foreground: #CCCCCC", +"*borderlessblack.Dialog.text.background: #000000", +"*borderlessblack.Dialog.button.foreground: #CCCCCC", +"*borderlessblack.Dialog.button.background: #333333", +"*borderlessblack.Dialog.logo.background: #000000", +"*borderlessblack.Dialog.thermometer.foreground: #CCCCCC", +"*borderlessblack.Dialog.thermometer.background: #333333", +"*borderlessblack.Dialog.thermometer.width: 3", +"*borderlessblack.Dialog.borderColor: #333333", +"*borderlessblack.Dialog.borderWidth: 1", +"*greenblack.Dialog.foreground: #00FF00", +"*greenblack.Dialog.background: #000000", +"*greenblack.Dialog.topShadowColor: #000000", +"*greenblack.Dialog.bottomShadowColor: #000000", +"*greenblack.Dialog.shadowWidth: 1", +"*greenblack.Dialog.text.foreground: #00FF00", +"*greenblack.Dialog.text.background: #006600", +"*greenblack.Dialog.button.foreground: #00FF00", +"*greenblack.Dialog.button.background: #006600", +"*greenblack.Dialog.logo.background: #000000", +"*greenblack.Dialog.thermometer.foreground: #00CC00", +"*greenblack.Dialog.thermometer.background: #006600", +"*greenblack.Dialog.borderColor: #006600", +"*greenblack.Dialog.borderWidth: 1", +"*white.Dialog.foreground: #000000", +"*white.Dialog.background: #FFFFFF", +"*white.Dialog.topShadowColor: #CCCCCC", +"*white.Dialog.bottomShadowColor: #CCCCCC", +"*white.Dialog.shadowWidth: 1", +"*white.Dialog.borderColor: #CCCCCC", +"*white.Dialog.text.foreground: #000000", +"*white.Dialog.text.background: #FFFFFF", +"*white.Dialog.button.foreground: #000000", +"*white.Dialog.button.background: #FFFFFF", +"*white.Dialog.logo.background: #FFFFFF", +"*white.Dialog.borderWidth: 0", +"*blue.Dialog.foreground: #000000", +"*blue.Dialog.background: #BBCCDD", +"*blue.Dialog.topShadowColor: #CCDDEE", +"*blue.Dialog.bottomShadowColor: #AABBCC", +"*blue.Dialog.borderColor: #AABBCC", +"*blue.Dialog.text.foreground: #000000", +"*blue.Dialog.text.background: #DDEEFF", +"*blue.Dialog.button.foreground: #000000", +"*blue.Dialog.button.background: #DDEEFF", +"*blue.Dialog.logo.background: #BBCCDD", +"*blue.Dialog.thermometer.foreground: #5566AA", +"*blue.Dialog.thermometer.background: #BBCCDD", +"*blue.Dialog.borderWidth: 0", +"*aquablack.Dialog.foreground: #00EFEF", +"*aquablack.Dialog.background: #000000", +"*aquablack.Dialog.topShadowColor: #000000", +"*aquablack.Dialog.bottomShadowColor: #000000", +"*aquablack.Dialog.shadowWidth: 2", +"*aquablack.Dialog.button.foreground: #000000", +"*aquablack.Dialog.button.background: #2244EE", +"*aquablack.Dialog.logo.background: #000000", +"*aquablack.Dialog.text.foreground: #2244EE", +"*aquablack.Dialog.text.background: #EEEEEE", +"*aquablack.Dialog.thermometer.foreground: #2244EE", +"*aquablack.Dialog.thermometer.background: #000088", +"*aquablack.Dialog.borderColor: #000066", +"*aquablack.Dialog.borderWidth: 1", +"*aquablack.Dialog.internalPadding: 36", +"*wine.Dialog.foreground: #AD8FA6", +"*wine.Dialog.background: #2C041E", +"*wine.Dialog.topShadowColor: #2C041E", +"*wine.Dialog.bottomShadowColor: #2C041E", +"*wine.Dialog.text.foreground: #706B70", +"*wine.Dialog.text.background: #F9F9F8", +"*wine.Dialog.button.foreground: #5F585B", +"*wine.Dialog.logo.background: #2C041E", +"*wine.Dialog.thermometer.foreground: #AD8FA6", +"*wine.Dialog.thermometer.background: #4D2946", +"*wine.Dialog.borderColor: #4D2946", +"*wine.Dialog.thermometer.width: 6", +"*wine.Dialog.borderWidth: 1", +"*errorFont: sans-serif bold 18", +"*errorColor: #FF0000", +"*programs: \ + maze -root \\n\ + GL: superquadrics -root \\n\ + attraction -root \\n\ + blitspin -root \\n\ + greynetic -root \\n\ + helix -root \\n\ + hopalong -root \\n\ + imsmap -root \\n\ +- noseguy -root \\n\ +- pyro -root \\n\ + qix -root \\n\ +- rocks -root \\n\ + rorschach -root \\n\ + decayscreen -root \\n\ + flame -root \\n\ + halo -root \\n\ + slidescreen -root \\n\ + pedal -root \\n\ + bouboule -root \\n\ +- braid -root \\n\ + coral -root \\n\ + deco -root \\n\ + drift -root \\n\ +- fadeplot -root \\n\ + galaxy -root \\n\ + goop -root \\n\ + grav -root \\n\ + ifs -root \\n\ + GL: jigsaw -root \\n\ + julia -root \\n\ +- kaleidescope -root \\n\ + GL: moebius -root \\n\ + moire -root \\n\ + GL: morph3d -root \\n\ + mountain -root \\n\ + munch -root \\n\ + penrose -root \\n\ + GL: pipes -root \\n\ + rd-bomb -root \\n\ + GL: rubik -root \\n\ +- sierpinski -root \\n\ + slip -root \\n\ + GL: sproingies -root \\n\ + starfish -root \\n\ + strange -root \\n\ + swirl -root \\n\ + triangle -root \\n\ + xjack -root \\n\ + xlyap -root \\n\ + GL: atlantis -root \\n\ + bsod -root \\n\ + GL: bubble3d -root \\n\ + GL: cage -root \\n\ +- crystal -root \\n\ + cynosure -root \\n\ + discrete -root \\n\ + distort -root \\n\ + epicycle -root \\n\ + flow -root \\n\ + GL: glplanet -root \\n\ + interference -root \\n\ + kumppa -root \\n\ + GL: lament -root \\n\ + moire2 -root \\n\ + GL: sonar -root \\n\ + GL: stairs -root \\n\ + truchet -root \\n\ +- vidwhacker -root \\n\ + blaster -root \\n\ + bumps -root \\n\ + ccurve -root \\n\ + compass -root \\n\ + deluxe -root \\n\ +- demon -root \\n\ +- GL: extrusion -root \\n\ +- loop -root \\n\ + penetrate -root \\n\ + petri -root \\n\ + phosphor -root \\n\ + GL: pulsar -root \\n\ + ripples -root \\n\ + shadebobs -root \\n\ + GL: sierpinski3d -root \\n\ + spotlight -root \\n\ + squiral -root \\n\ + wander -root \\n\ +- webcollage -root \\n\ + xflame -root \\n\ + xmatrix -root \\n\ + GL: gflux -root \\n\ +- nerverot -root \\n\ + xrayswarm -root \\n\ + xspirograph -root \\n\ + GL: circuit -root \\n\ + GL: dangerball -root \\n\ +- GL: dnalogo -root \\n\ + GL: engine -root \\n\ + GL: flipscreen3d -root \\n\ + GL: gltext -root \\n\ + GL: menger -root \\n\ + GL: molecule -root \\n\ + rotzoomer -root \\n\ + scooter -root \\n\ + speedmine -root \\n\ + GL: starwars -root \\n\ + GL: stonerview -root \\n\ + vermiculate -root \\n\ + whirlwindwarp -root \\n\ + zoom -root \\n\ + anemone -root \\n\ + apollonian -root \\n\ + GL: boxed -root \\n\ + GL: cubenetic -root \\n\ + GL: endgame -root \\n\ + euler2d -root \\n\ + fluidballs -root \\n\ + GL: flurry -root \\n\ +- GL: glblur -root \\n\ + GL: glsnake -root \\n\ + halftone -root \\n\ + GL: juggler3d -root \\n\ + GL: lavalite -root \\n\ +- polyominoes -root \\n\ + GL: queens -root \\n\ +- GL: sballs -root \\n\ + GL: spheremonics -root \\n\ +- thornbird -root \\n\ + twang -root \\n\ +- GL: antspotlight -root \\n\ + apple2 -root \\n\ + GL: atunnel -root \\n\ + barcode -root \\n\ + GL: blinkbox -root \\n\ + GL: blocktube -root \\n\ + GL: bouncingcow -root \\n\ + cloudlife -root \\n\ + GL: cubestorm -root \\n\ + eruption -root \\n\ + GL: flipflop -root \\n\ + GL: flyingtoasters -root \\n\ + fontglide -root \\n\ + GL: gleidescope -root \\n\ + GL: glknots -root \\n\ + GL: glmatrix -root \\n\ +- GL: glslideshow -root \\n\ + GL: hypertorus -root \\n\ +- GL: jigglypuff -root \\n\ + metaballs -root \\n\ + GL: mirrorblob -root \\n\ + piecewise -root \\n\ + GL: polytopes -root \\n\ + pong -root \\n\ + popsquares -root \\n\ + GL: surfaces -root \\n\ + xanalogtv -root \\n\ + abstractile -root \\n\ + anemotaxis -root \\n\ +- GL: antinspect -root \\n\ + fireworkx -root \\n\ + fuzzyflakes -root \\n\ + interaggregate -root \\n\ + intermomentary -root \\n\ + memscroller -root \\n\ + GL: noof -root \\n\ + pacman -root \\n\ + GL: pinion -root \\n\ + GL: polyhedra -root \\n\ +- GL: providence -root \\n\ + substrate -root \\n\ + wormhole -root \\n\ +- GL: antmaze -root \\n\ + GL: boing -root \\n\ + boxfit -root \\n\ + GL: carousel -root \\n\ + celtic -root \\n\ + GL: crackberg -root \\n\ + GL: cube21 -root \\n\ + fiberlamp -root \\n\ + GL: fliptext -root \\n\ + GL: glhanoi -root \\n\ + GL: tangram -root \\n\ + GL: timetunnel -root \\n\ + GL: glschool -root \\n\ + GL: topblock -root \\n\ + GL: cubicgrid -root \\n\ + cwaves -root \\n\ + GL: gears -root \\n\ + GL: glcells -root \\n\ + GL: lockward -root \\n\ + m6502 -root \\n\ + GL: moebiusgears -root \\n\ + GL: voronoi -root \\n\ + GL: hypnowheel -root \\n\ + GL: klein -root \\n\ +- lcdscrub -root \\n\ + GL: photopile -root \\n\ + GL: skytentacles -root \\n\ + GL: rubikblocks -root \\n\ + GL: companioncube -root \\n\ + GL: hilbert -root \\n\ + GL: tronbit -root \\n\ + GL: geodesic -root \\n\ + hexadrop -root \\n\ + GL: kaleidocycle -root \\n\ + GL: quasicrystal -root \\n\ + GL: unknownpleasures -root \\n\ + binaryring -root \\n\ + GL: cityflow -root \\n\ + GL: geodesicgears -root \\n\ + GL: projectiveplane -root \\n\ + GL: romanboy -root \\n\ + tessellimage -root \\n\ + GL: winduprobot -root \\n\ + GL: splitflap -root \\n\ + GL: cubestack -root \\n\ + GL: cubetwist -root \\n\ + GL: discoball -root \\n\ + GL: dymaxionmap -root \\n\ + GL: energystream -root \\n\ + GL: hexstrut -root \\n\ + GL: hydrostat -root \\n\ + GL: raverhoop -root \\n\ + GL: splodesic -root \\n\ + GL: unicrud -root \\n\ + GL: esper -root \\n\ + GL: vigilance -root \\n\ + GL: crumbler -root \\n\ + filmleader -root \\n\ + glitchpeg -root \\n\ + GL: handsy -root \\n\ + GL: maze3d -root \\n\ + GL: peepers -root \\n\ + GL: razzledazzle -root \\n\ + vfeedback -root \\n\ + GL: deepstars -root \\n\ + GL: gravitywell -root \\n\ + GL: beats -root \\n\ + GL: covid19 -root \\n\ + GL: etruscanvenus -root \\n\ + GL: gibson -root \\n\ + GL: headroom -root \\n\ + GL: sphereeversion -root \\n", +"*hacks.antinspect.name: Ant Inspect", +"*hacks.antmaze.name: Ant Maze", +"*hacks.antspotlight.name: Ant Spotlight", +"*hacks.apple2.name: Apple ][", +"*hacks.binaryring.name: Binary Ring", +"*hacks.blinkbox.name: Blink Box", +"*hacks.blitspin.name: Blit Spin", +"*hacks.blocktube.name: Block Tube", +"*hacks.bouncingcow.name: Bouncing Cow", +"*hacks.boxfit.name: Box Fit", +"*hacks.bsod.name: BSOD", +"*hacks.bubble3d.name: Bubble 3D", +"*hacks.ccurve.name: C Curve", +"*hacks.cityflow.name: City Flow", +"*hacks.cloudlife.name: Cloud Life", +"*hacks.companioncube.name: Companion Cube", +"*hacks.covid19.name: COVID19", +"*hacks.cube21.name: Cube 21", +"*hacks.cubestack.name: Cube Stack", +"*hacks.cubestorm.name: Cube Storm", +"*hacks.cubetwist.name: Cube Twist", +"*hacks.cubicgrid.name: Cubic Grid", +"*hacks.cwaves.name: C Waves", +"*hacks.dangerball.name: Danger Ball", +"*hacks.decayscreen.name: Decay Screen", +"*hacks.deepstars.name: Deep Stars", +"*hacks.dnalogo.name: DNA Logo", +"*hacks.dymaxionmap.name: Dymaxion Map", +"*hacks.energystream.name: Energy Stream", +"*hacks.etruscanvenus.name: Etruscan Venus", +"*hacks.euler2d.name: Euler 2D", +"*hacks.fadeplot.name: Fade Plot", +"*hacks.fiberlamp.name: Fiber Lamp", +"*hacks.filmleader.name: Film Leader", +"*hacks.flipflop.name: Flip Flop", +"*hacks.flipscreen3d.name: Flip Screen 3D", +"*hacks.fliptext.name: Flip Text", +"*hacks.fluidballs.name: Fluid Balls", +"*hacks.flyingtoasters.name: Flying Toasters", +"*hacks.fontglide.name: Font Glide", +"*hacks.fuzzyflakes.name: Fuzzy Flakes", +"*hacks.geodesicgears.name: Geodesic Gears", +"*hacks.gflux.name: GFlux", +"*hacks.glblur.name: GL Blur", +"*hacks.glcells.name: GL Cells", +"*hacks.gleidescope.name: Gleidescope", +"*hacks.glforestfire.name: GL Forest Fire", +"*hacks.glhanoi.name: GL Hanoi", +"*hacks.glitchpeg.name: GlitchPEG", +"*hacks.glknots.name: GL Knots", +"*hacks.glmatrix.name: GL Matrix", +"*hacks.glplanet.name: GL Planet", +"*hacks.glschool.name: GL School", +"*hacks.glslideshow.name: GL Slideshow", +"*hacks.glsnake.name: GL Snake", +"*hacks.gltext.name: GL Text", +"*hacks.gravitywell.name: Gravity Well", +"*hacks.hexstrut.name: Hex Strut", +"*hacks.ifs.name: IFS", +"*hacks.imsmap.name: IMS Map", +"*hacks.jigglypuff.name: Jiggly Puff", +"*hacks.juggler3d.name: Juggler 3D", +"*hacks.lcdscrub.name: LCD Scrub", +"*hacks.lmorph.name: LMorph", +"*hacks.m6502.name: m6502", +"*hacks.maze3d.name: Maze 3D", +"*hacks.memscroller.name: Mem Scroller", +"*hacks.metaballs.name: Meta Balls", +"*hacks.mirrorblob.name: Mirror Blob", +"*hacks.moebius.name: Möbius", +"*hacks.moebiusgears.name: Möbius Gears", +"*hacks.moire.name: Moiré", +"*hacks.moire2.name: Moiré 2", +"*hacks.morph3d.name: Morph 3D", +"*hacks.nerverot.name: Nerve Rot", +"*hacks.noseguy.name: Nose Guy", +"*hacks.pacman.name: Pac-Man", +"*hacks.photopile.name: Photo Pile", +"*hacks.popsquares.name: Pop Squares", +"*hacks.projectiveplane.name:Projective Plane", +"*hacks.quasicrystal.name: Quasi-Crystal", +"*hacks.raverhoop.name: Raver Hoop", +"*hacks.razzledazzle.name: Razzle Dazzle", +"*hacks.rd-bomb.name: RD-Bomb", +"*hacks.rd-bomb.name: RD-Bomb", +"*hacks.romanboy.name: Roman Boy", +"*hacks.rotzoomer.name: Rot Zoomer", +"*hacks.rubikblocks.name: Rubik Blocks", +"*hacks.sballs.name: SBalls", +"*hacks.shadebobs.name: Shade Bobs", +"*hacks.sierpinski3d.name: Sierpinski 3D", +"*hacks.skytentacles.name: Sky Tentacles", +"*hacks.slidescreen.name: Slide Screen", +"*hacks.speedmine.name: Speed Mine", +"*hacks.sphereeversion.name: Sphere Eversion", +"*hacks.splitflap.name: Split-Flap", +"*hacks.starwars.name: Star Wars", +"*hacks.stonerview.name: Stoner View", +"*hacks.t3d.name: T3D", +"*hacks.testx11.name: Test X11", +"*hacks.timetunnel.name: Time Tunnel", +"*hacks.topblock.name: Top Block", +"*hacks.tronbit.name: Tron Bit", +"*hacks.unknownpleasures.name:Unknown Pleasures", +"*hacks.vfeedback.name: VFeedback", +"*hacks.vidwhacker.name: Vid Whacker", +"*hacks.webcollage.name: Web Collage", +"*hacks.whirlwindwarp.name: Whirlwind Warp", +"*hacks.winduprobot.name: Windup Robot", +"*hacks.xanalogtv.name: XAnalogTV", +"*hacks.xrayswarm.name: XRaySwarm", diff --git a/driver/atoms.c b/driver/atoms.c new file mode 100644 index 0000000..e8b6add --- /dev/null +++ b/driver/atoms.c @@ -0,0 +1,70 @@ +/* xscreensaver-command, Copyright © 1991-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include /* for CARD32 */ +#include +#include + +#include "atoms.h" + +Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_RESPONSE, + XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO, XA_EXIT, + XA_BLANK, XA_LOCK, XA_ACTIVATE, XA_SUSPEND, XA_NEXT, XA_PREV, + XA_DEACTIVATE, XA_CYCLE, XA_RESTART, XA_PREFS, + XA_NET_WM_PID, XA_NET_WM_STATE, XA_NET_WM_STATE_ABOVE, + XA_NET_WM_STATE_FULLSCREEN, XA_NET_WM_BYPASS_COMPOSITOR, + XA_NET_WM_WINDOW_TYPE, XA_NET_WM_WINDOW_TYPE_SPLASH, + XA_NET_WM_WINDOW_TYPE_DIALOG, XA_NET_WM_WINDOW_TYPE_NOTIFICATION, + XA_NET_WM_WINDOW_TYPE_NORMAL; + +void +init_xscreensaver_atoms (Display *dpy) +{ +# define A(N) XInternAtom (dpy, (N), False) + XA_SCREENSAVER = A("SCREENSAVER"); + XA_SCREENSAVER_ID = A("_SCREENSAVER_ID"); + XA_SCREENSAVER_VERSION = A("_SCREENSAVER_VERSION"); + XA_SCREENSAVER_STATUS = A("_SCREENSAVER_STATUS"); + XA_SCREENSAVER_RESPONSE = A("_SCREENSAVER_RESPONSE"); + + XA_ACTIVATE = A("ACTIVATE"); + XA_DEACTIVATE = A("DEACTIVATE"); + XA_SUSPEND = A("SUSPEND"); + XA_RESTART = A("RESTART"); + XA_CYCLE = A("CYCLE"); + XA_NEXT = A("NEXT"); + XA_PREV = A("PREV"); + XA_SELECT = A("SELECT"); + XA_EXIT = A("EXIT"); + XA_DEMO = A("DEMO"); + XA_PREFS = A("PREFS"); + XA_LOCK = A("LOCK"); + XA_BLANK = A("BLANK"); + + XA_NET_WM_PID = A("_NET_WM_PID"); + XA_NET_WM_STATE = A("_NET_WM_STATE"); + XA_NET_WM_STATE_ABOVE = A("_NET_WM_STATE_ABOVE"); + XA_NET_WM_STATE_FULLSCREEN = A("_NET_WM_STATE_FULLSCREEN"); + XA_NET_WM_BYPASS_COMPOSITOR = A("_NET_WM_BYPASS_COMPOSITOR"); + XA_NET_WM_WINDOW_TYPE = A("_NET_WM_WINDOW_TYPE"); + XA_NET_WM_WINDOW_TYPE_SPLASH = A("_NET_WM_WINDOW_TYPE_SPLASH"); + XA_NET_WM_WINDOW_TYPE_DIALOG = A("_NET_WM_WINDOW_TYPE_DIALOG"); + XA_NET_WM_WINDOW_TYPE_NOTIFICATION = A("_NET_WM_WINDOW_TYPE_NOTIFICATION"); + XA_NET_WM_WINDOW_TYPE_NORMAL = A("_NET_WM_WINDOW_TYPE_NORMAL"); +# undef A +} diff --git a/driver/atoms.h b/driver/atoms.h new file mode 100644 index 0000000..b5a7b7c --- /dev/null +++ b/driver/atoms.h @@ -0,0 +1,38 @@ +/* xscreensaver-command, Copyright © 1991-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifndef _XSCREENSAVER_ATOMS_H_ +#define _XSCREENSAVER_ATOMS_H_ + +extern Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_RESPONSE, + XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO, XA_EXIT, + XA_BLANK, XA_LOCK, XA_ACTIVATE, XA_SUSPEND, XA_NEXT, XA_PREV, + XA_DEACTIVATE, XA_CYCLE, XA_RESTART, XA_PREFS, + XA_NET_WM_PID, XA_NET_WM_STATE, XA_NET_WM_STATE_ABOVE, + XA_NET_WM_STATE_FULLSCREEN, XA_NET_WM_BYPASS_COMPOSITOR, + XA_NET_WM_WINDOW_TYPE, XA_NET_WM_WINDOW_TYPE_SPLASH, + XA_NET_WM_WINDOW_TYPE_DIALOG, XA_NET_WM_WINDOW_TYPE_NOTIFICATION, + XA_NET_WM_WINDOW_TYPE_NORMAL; + +extern void init_xscreensaver_atoms (Display *dpy); +extern void xscreensaver_set_wm_atoms (Display *, Window, + int width, int height, + Window for_window); + +/* You might think that to store an array of 32-bit quantities onto a + server-side property, you would pass an array of 32-bit data quantities + into XChangeProperty(). You would be wrong. You have to use an array + of longs, even if long is 64 bits (using 32 of each 64.) + */ +typedef long PROP32; + + +#endif /* _XSCREENSAVER_ATOMS_H_ */ diff --git a/driver/atomswm.c b/driver/atomswm.c new file mode 100644 index 0000000..887dc2c --- /dev/null +++ b/driver/atomswm.c @@ -0,0 +1,101 @@ +/* xscreensaver-command, Copyright © 1991-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include /* for CARD32 */ +#include +#include +#include + +#include "atoms.h" + +#ifdef HAVE_UNAME +# include /* for uname() */ +#endif /* HAVE_UNAME */ + + +/* Set some properties to hopefully tell the window manager to leave us alone. + This is used by xscreensaver-gfx and xscreensaver-auth but not xscreensaver. + */ +void +xscreensaver_set_wm_atoms (Display *dpy, Window window, int width, int height, + Window for_window) +{ + XClassHint class_hints; + XSizeHints size_hints; +# ifdef HAVE_UNAME + struct utsname uts; +# endif + Atom va[10]; + long vl[10]; + class_hints.res_name = "xscreensaver"; /* not progname */ + class_hints.res_class = "XScreenSaver"; + size_hints.flags = PMinSize | PMaxSize; + size_hints.min_width = size_hints.max_width = width; /* non-resizable */ + size_hints.min_height = size_hints.max_height = height; + XStoreName (dpy, window, "XScreenSaver"); + XSetClassHint (dpy, window, &class_hints); + XSetWMNormalHints (dpy, window, &size_hints); + + /* XA_WM_COMMAND and _NET_WM_PID are later updated by spawn_screenhack. */ + XChangeProperty (dpy, window, XA_WM_COMMAND, + XA_STRING, 8, PropModeReplace, + (unsigned char *) class_hints.res_name, + strlen (class_hints.res_name)); + +# ifdef HAVE_UNAME + if (! uname (&uts)) + XChangeProperty (dpy, window, + XA_WM_CLIENT_MACHINE, XA_STRING, 8, + PropModeReplace, (unsigned char *) uts.nodename, + strlen (uts.nodename)); +# endif + + /* In the olden days, OverrideRedirect meant that the window manager did + not see or touch our window, but these days, compositing WMs like to + get up to all sorts of shenanigans. I don't know whether setting these + properties has any effect, but they *might* tell the WM to keep its + grubby paws off of our windows. + */ + + vl[0] = 1; /* _NET_WM_BYPASS_COMPOSITOR = 1 */ + XChangeProperty (dpy, window, XA_NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) vl, 1); + + /* _NET_WM_STATE = [ _NET_WM_STATE_ABOVE, _NET_WM_STATE_FULLSCREEN ] */ + va[0] = XA_NET_WM_STATE_ABOVE; + va[1] = XA_NET_WM_STATE_FULLSCREEN; + XChangeProperty (dpy, window, XA_NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char *) va, 2); + + /* As there is no _NET_WM_WINDOW_TYPE_SCREENSAVER, which property is + most likely to effectively communicate "on top always" to the WM? + _NET_WM_WINDOW_TYPE = NORMAL, SPLASH, DIALOG or NOTIFICATION? */ + va[0] = XA_NET_WM_WINDOW_TYPE_NOTIFICATION; + XChangeProperty (dpy, window, XA_NET_WM_WINDOW_TYPE, XA_ATOM, 32, + PropModeReplace, (unsigned char *) va, 1); + + if (for_window) /* This is the error dialog for a saver window */ + { + va[0] = for_window; + /* _WM_TRANSIENT_FOR = screensaver_window */ + XChangeProperty (dpy, window, + XA_WM_TRANSIENT_FOR, XA_WINDOW, 32, + PropModeReplace, (unsigned char *) va, 1); + } +} diff --git a/driver/auth.h b/driver/auth.h index 65e00f3..40594ea 100644 --- a/driver/auth.h +++ b/driver/auth.h @@ -1,9 +1,7 @@ /* auth.h --- Providing authentication mechanisms. - * + * Copyright © 1993-2021 Jamie Zawinski * (c) 2007, Quest Software, Inc. All rights reserved. - * - * This file is part of XScreenSaver, - * Copyright (c) 1993-2004 Jamie Zawinski + * This file is part of XScreenSaver. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -13,10 +11,8 @@ * software for any purpose. It is provided "as is" without express or * implied warranty. */ -#ifndef XSS_AUTH_H -#define XSS_AUTH_H - -#include "types.h" +#ifndef __XSCREENSAVER_AUTH_H__ +#define __XSCREENSAVER_AUTH_H__ #undef Bool #undef True @@ -25,7 +21,9 @@ #define True 1 #define False 0 -struct auth_message { +extern Bool verbose_p; + +typedef struct { enum { AUTH_MSGTYPE_INFO, AUTH_MSGTYPE_ERROR, @@ -33,22 +31,61 @@ struct auth_message { AUTH_MSGTYPE_PROMPT_ECHO } type; const char *msg; -}; +} auth_message; -struct auth_response { +typedef struct { char *response; -}; +} auth_response; -int -gui_auth_conv(int num_msg, - const struct auth_message auth_msgs[], - struct auth_response **resp, - saver_info *si); -void -xss_authenticate(saver_info *si, Bool verbose_p); +/* To run all authentication methods. + */ +extern void disavow_privileges (void); +extern Bool lock_priv_init (void); +extern Bool lock_init (void); + +/* Returns true if authenticated. */ +extern Bool xscreensaver_auth (void *closure, + Bool (*conv_fn) (void *closure, + int nmsgs, + const auth_message *msg, + auth_response **resp), + void (*finished_fn) (void *closure, + Bool authenticated_p)); -void -auth_finished_cb (saver_info *si); +/* The implementations, called by xscreensaver_auth. + */ +#ifdef HAVE_KERBEROS +extern Bool kerberos_lock_init (void); +extern Bool kerberos_passwd_valid_p (void *closure, const char *plaintext); #endif + +#ifdef HAVE_PAM +extern Bool pam_priv_init (void); +extern Bool pam_try_unlock (void *closure, + Bool (*conv_fn) (void *closure, + int nmsgs, + const auth_message *msg, + auth_response **resp)); +#endif + +#ifdef PASSWD_HELPER_PROGRAM +extern Bool ext_priv_init (void); +extern Bool ext_passwd_valid_p (void *closure, const char *plaintext); +#endif + +extern Bool pwent_lock_init (void); +extern Bool pwent_priv_init (void); +extern Bool pwent_passwd_valid_p (void *closure, const char *plaintext); + +/* GUI conversation function to pass to xscreensaver_auth. */ +extern Bool xscreensaver_auth_conv (void *closure, + int num_msg, + const auth_message *msg, + auth_response **resp); +extern void xscreensaver_auth_finished (void *closure, Bool authenticated_p); +extern void xscreensaver_splash (void *root_widget); + +#endif /* __XSCREENSAVER_AUTH_H__ */ + diff --git a/driver/blurb.c b/driver/blurb.c new file mode 100644 index 0000000..d732a9b --- /dev/null +++ b/driver/blurb.c @@ -0,0 +1,50 @@ +/* xscreensaver, Copyright © 1991-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "blurb.h" + +#include +#include + +const char *progname = ""; +int verbose_p = 0; + +const char * +blurb (void) +{ + static char buf[255] = { 0 }; + struct tm tm; + time_t now; + int i; + + now = time ((time_t *) 0); + localtime_r (&now, &tm); + i = strlen (progname); + if (i > 40) i = 40; + memcpy (buf, progname, i); + buf[i++] = ':'; + buf[i++] = ' '; + buf[i++] = '0' + (tm.tm_hour >= 10 ? tm.tm_hour/10 : 0); + buf[i++] = '0' + (tm.tm_hour % 10); + buf[i++] = ':'; + buf[i++] = '0' + (tm.tm_min >= 10 ? tm.tm_min/10 : 0); + buf[i++] = '0' + (tm.tm_min % 10); + buf[i++] = ':'; + buf[i++] = '0' + (tm.tm_sec >= 10 ? tm.tm_sec/10 : 0); + buf[i++] = '0' + (tm.tm_sec % 10); + buf[i] = 0; + return buf; +} + diff --git a/driver/blurb.h b/driver/blurb.h new file mode 100644 index 0000000..6e33c76 --- /dev/null +++ b/driver/blurb.h @@ -0,0 +1,11 @@ +/* progname plus timestamp */ + +#ifndef __BLURB_H__ +#define __BLURB_H__ + +extern const char *progname; +extern int verbose_p; +extern const char *blurb (void); + +#endif /* __BLURB_H__ */ + diff --git a/driver/clientmsg.c b/driver/clientmsg.c new file mode 100644 index 0000000..f3d9d71 --- /dev/null +++ b/driver/clientmsg.c @@ -0,0 +1,123 @@ +/* xscreensaver, Copyright © 1991-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "blurb.h" +#include "atoms.h" +#include "clientmsg.h" + +extern Bool verbose_p; + +static int +error_handler (Display *dpy, XErrorEvent *error) +{ + return 0; +} + + +Window +find_screensaver_window (Display *dpy, char **version) +{ + int i; + Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy)); + Window root2, parent, *kids; + unsigned int nkids; + XErrorHandler old_handler; + Window ret = 0; + + XSync (dpy, False); + old_handler = XSetErrorHandler (error_handler); + + if (version) *version = 0; + + if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) + abort (); + if (root != root2) + abort (); + if (parent) + abort (); + if (! (kids && nkids)) + goto DONE; + for (i = 0; i < nkids; i++) + { + Atom type; + int format; + unsigned long nitems, bytesafter; + unsigned char *v = 0; + int status; + + /* We're walking the list of root-level windows and trying to find + the one that has a particular property on it. We need to trap + BadWindows errors while doing this, because it's possible that + some random window might get deleted in the meantime. (That + window won't have been the one we're looking for.) + */ + status = XGetWindowProperty (dpy, kids[i], + XA_SCREENSAVER_VERSION, + 0, 200, False, XA_STRING, + &type, &format, &nitems, &bytesafter, + &v); + if (status == Success && type != None) + { + ret = kids[i]; + if (version) + *version = (char *) v; + else + XFree (v); + goto DONE; + } + if (v) XFree (v); + } + + DONE: + if (kids) XFree (kids); + XSetErrorHandler (old_handler); + return ret; +} + + +void +clientmessage_response (Display *dpy, XEvent *xev, Bool ok, const char *msg) +{ + char *proto; + int L = 0; + + if (verbose_p || !ok) + { + Atom cmd = xev->xclient.data.l[0]; + char *name = XGetAtomName (dpy, cmd); + fprintf (stderr, "%s: ClientMessage %s: %s\n", blurb(), + (name ? name : "???"), msg); + } + + L = strlen (msg); + proto = (char *) malloc (L + 2); + if (!proto) return; + proto[0] = (ok ? '+' : '-'); + memcpy (proto+1, msg, L); + L++; + proto[L] = 0; + + XChangeProperty (dpy, xev->xclient.window, + XA_SCREENSAVER_RESPONSE, XA_STRING, 8, + PropModeReplace, (unsigned char *) proto, L); + free (proto); +} diff --git a/driver/clientmsg.h b/driver/clientmsg.h new file mode 100644 index 0000000..cd836d8 --- /dev/null +++ b/driver/clientmsg.h @@ -0,0 +1,19 @@ +/* xscreensaver, Copyright © 1991-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifndef __CLIENTMSG_H__ +#define __CLIENTMSG_H__ + +extern Window find_screensaver_window (Display *, char **version); +extern void clientmessage_response (Display *, XEvent *, Bool ok, + const char *msg); + +#endif /* __CLIENTMSG_H__ */ diff --git a/driver/demo-Gtk-conf.c b/driver/demo-Gtk-conf.c index bac6ecc..6d04215 100644 --- a/driver/demo-Gtk-conf.c +++ b/driver/demo-Gtk-conf.c @@ -1,5 +1,5 @@ /* demo-Gtk-conf.c --- implements the dynamic configuration dialogs. - * xscreensaver, Copyright (c) 2001-2014 Jamie Zawinski + * xscreensaver, Copyright (c) 2001-2020 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 @@ -59,8 +59,21 @@ # endif /* LIBXML_VERSION */ #endif /* xmlChildrenNode */ +#if (__GNUC__ >= 4) /* Ignore useless warnings generated by gtk.h */ +# undef inline +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +# pragma GCC diagnostic ignored "-Wlong-long" +# pragma GCC diagnostic ignored "-Wvariadic-macros" +# pragma GCC diagnostic ignored "-Wpedantic" +#endif + #include +#if (__GNUC__ >= 4) +# pragma GCC diagnostic pop +#endif + #include "demo-Gtk-conf.h" /* Deal with deprecation of direct access to struct fields on the way to GTK3 @@ -1320,7 +1333,7 @@ parameters_to_cmd_line (GList *parms, gboolean default_p) { int L = g_list_length (parms); int LL = 0; - char **strs = (char **) calloc (sizeof (*parms), L); + char **strs = (char **) calloc (sizeof (*strs), L); char *result; char *out; int i, j; @@ -1428,7 +1441,7 @@ parse_command_line_into_parameters (const char *filename, char *option = rest->data; rest->data = 0; - if (option[0] != '-' && option[0] != '+') + if (option && option[0] != '-' && option[0] != '+') { if (debug_p) fprintf (stderr, "%s: WARNING: %s: not a switch: \"%s\"\n", @@ -1737,7 +1750,8 @@ get_description (GList *parms, gboolean verbose_p) s++; else if (s[1] == ' ' || s[1] == '\t') s++; /* next line is indented: leave newline */ - else if (!strncmp(s+1, "http:", 5)) + else if (!strncmp(s+1, "http:", 5) || + !strncmp(s+1, "https:", 5)) s++; /* next line begins a URL: leave newline */ else s[0] = ' '; /* delete newline to un-fold this line */ @@ -1770,7 +1784,7 @@ get_description (GList *parms, gboolean verbose_p) } #if 0 - if (verbose_p) + /*if (verbose_p)*/ { fprintf (stderr, "%s: text read is \"%s\"\n", blurb(),doc->string); fprintf (stderr, "%s: description is \"%s\"\n", blurb(), d); diff --git a/driver/demo-Gtk.c b/driver/demo-Gtk.c index da98c53..b5e82e2 100644 --- a/driver/demo-Gtk.c +++ b/driver/demo-Gtk.c @@ -1,5 +1,5 @@ /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs. - * xscreensaver, Copyright (c) 1993-2020 Jamie Zawinski + * xscreensaver, Copyright © 1993-2021 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 @@ -16,6 +16,8 @@ #ifdef HAVE_GTK /* whole file */ +#include "blurb.h" + #include #include @@ -35,18 +37,13 @@ # include #endif /* ENABLE_NLS */ -#ifndef VMS -# include /* for getpwuid() */ -#else /* VMS */ -# include "vms-pwd.h" -#endif /* VMS */ - #ifdef HAVE_UNAME # include /* for uname() */ #endif /* HAVE_UNAME */ #include #include +#include /* for getpwuid() */ #include #include @@ -68,20 +65,19 @@ #include #include -#ifdef HAVE_XMU -# ifndef VMS -# include -# else /* VMS */ -# include -# endif -#else -# include "xmu.h" -#endif - #ifdef HAVE_XINERAMA # include #endif /* HAVE_XINERAMA */ +#if (__GNUC__ >= 4) /* Ignore useless warnings generated by gtk.h */ +# undef inline +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +# pragma GCC diagnostic ignored "-Wlong-long" +# pragma GCC diagnostic ignored "-Wvariadic-macros" +# pragma GCC diagnostic ignored "-Wpedantic" +#endif + #include #ifdef HAVE_CRAPPLET @@ -92,38 +88,36 @@ #include #ifdef HAVE_GTK2 -# include # include #else /* !HAVE_GTK2 */ # define G_MODULE_EXPORT /**/ #endif /* !HAVE_GTK2 */ -#if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR) -# define GLADE_DIR DEFAULT_ICONDIR -#endif -#if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR) -# define DEFAULT_ICONDIR GLADE_DIR -#endif - #ifndef HAVE_XML /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML. It is unused otherwise, so in that case, stub it out. */ static const char *hack_configuration_path = 0; #endif +#if (__GNUC__ >= 4) +# pragma GCC diagnostic pop +#endif #include "version.h" -#include "prefs.h" +#include "types.h" #include "resources.h" /* for parse_time() */ -#include "visual.h" /* for has_writable_cells() */ #include "remote.h" /* for xscreensaver_command() */ +#include "visual.h" +#include "atoms.h" #include "usleep.h" +#include "xmu.h" #include "logo-50.xpm" #include "logo-180.xpm" #include "demo-Gtk-conf.h" +#include "atoms.h" #include #include @@ -182,14 +176,6 @@ static void hack_subproc_environment (Window preview_window_id, Bool debug_p); #define countof(x) (sizeof((x))/sizeof((*x))) -/* You might think that to read an array of 32-bit quantities out of a - server-side property, you would pass an array of 32-bit data quantities - into XGetWindowProperty(). You would be wrong. You have to use an array - of longs, even if long is 64 bits (using 32 of each 64.) - */ -typedef long PROP32; - -char *progname = 0; char *progclass = "XScreenSaver"; XrmDatabase db; @@ -208,7 +194,7 @@ typedef struct { conf_data *cdata; /* private data for per-hack configuration */ #ifdef HAVE_GTK2 - GladeXML *glade_ui; /* Glade UI file */ + GtkBuilder *gtk_ui; /* UI file */ #endif /* HAVE_GTK2 */ Bool debug_p; /* whether to print diagnostics */ @@ -247,13 +233,6 @@ typedef struct { a closure object of our own down into the various widget callbacks. */ static state *global_state_kludge; -Atom XA_VROOT; -Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION; -Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO; -Atom XA_ACTIVATE, XA_SUSPEND, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT; -Atom XA_NEXT, XA_PREV; - - static void populate_demo_window (state *, int list_elt); static void populate_prefs_page (state *); static void populate_popup_window (state *); @@ -272,16 +251,15 @@ static void kill_preview_subproc (state *, Bool reset_p); static void schedule_preview_check (state *); -/* Prototypes of functions used by the Glade-generated code, - to avoid warnings. +/* Prototypes of functions used by the Gtk-generated code, to avoid warnings. */ -void exit_menu_cb (GtkMenuItem *, gpointer user_data); -void about_menu_cb (GtkMenuItem *, gpointer user_data); -void doc_menu_cb (GtkMenuItem *, gpointer user_data); -void file_menu_cb (GtkMenuItem *, gpointer user_data); -void activate_menu_cb (GtkMenuItem *, gpointer user_data); -void lock_menu_cb (GtkMenuItem *, gpointer user_data); -void kill_menu_cb (GtkMenuItem *, gpointer user_data); +void exit_menu_cb (GtkAction *, gpointer user_data); +void about_menu_cb (GtkAction *, gpointer user_data); +void doc_menu_cb (GtkAction *, gpointer user_data); +void file_menu_cb (GtkAction *, gpointer user_data); +void activate_menu_cb (GtkAction *, gpointer user_data); +void lock_menu_cb (GtkAction *, gpointer user_data); +void kill_menu_cb (GtkAction *, gpointer user_data); void restart_menu_cb (GtkWidget *, gpointer user_data); void run_this_cb (GtkButton *, gpointer user_data); void manual_cb (GtkButton *, gpointer user_data); @@ -303,33 +281,14 @@ void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *, gint page_num, gpointer user_data); void settings_cancel_cb (GtkButton *, gpointer user_data); void settings_ok_cb (GtkButton *, gpointer user_data); +void preview_theme_cb (GtkWidget *, gpointer user_data); static void kill_gnome_screensaver (void); static void kill_kde_screensaver (void); - /* Some random utility functions */ -const char *blurb (void); - -const char * -blurb (void) -{ - time_t now = time ((time_t *) 0); - char *ct = (char *) ctime (&now); - static char buf[255]; - int n = strlen(progname); - if (n > 100) n = 99; - strncpy(buf, progname, n); - buf[n++] = ':'; - buf[n++] = ' '; - strncpy(buf+n, ct+11, 8); - strcpy(buf+n+9, ": "); - return buf; -} - - static GtkWidget * name_to_widget (state *s, const char *name) { @@ -339,38 +298,49 @@ name_to_widget (state *s, const char *name) if (!*name) abort(); #ifdef HAVE_GTK2 - if (!s->glade_ui) + if (!s->gtk_ui) { - /* First try to load the Glade file from the current directory; + /* First try to load the UI file from the current directory; if there isn't one there, check the installed directory. */ -# define GLADE_FILE_NAME "xscreensaver-demo.glade2" - const char * const files[] = { GLADE_FILE_NAME, - GLADE_DIR "/" GLADE_FILE_NAME }; +# define UI_FILE "xscreensaver.ui" + const char * const files[] = { UI_FILE, + DEFAULT_ICONDIR "/" UI_FILE }; int i; + + s->gtk_ui = gtk_builder_new (); + for (i = 0; i < countof (files); i++) { struct stat st; if (!stat (files[i], &st)) { - s->glade_ui = glade_xml_new (files[i], NULL, NULL); - break; + GError* error = NULL; + + if (gtk_builder_add_from_file (s->gtk_ui, files[i], &error)) + break; + else + { + g_warning ("Couldn't load builder file %s: %s", + files[i], error->message); + g_error_free (error); + } } } - if (!s->glade_ui) + if (i >= countof (files)) { fprintf (stderr, - "%s: could not load \"" GLADE_FILE_NAME "\"\n" - "\tfrom " GLADE_DIR "/ or current directory.\n", + "%s: could not load \"" UI_FILE "\"\n" + "\tfrom " DEFAULT_ICONDIR "/ or current directory.\n", blurb()); exit (-1); } -# undef GLADE_FILE_NAME +# undef UI_FILE - glade_xml_signal_autoconnect (s->glade_ui); + gtk_builder_connect_signals (s->gtk_ui, NULL); } - w = glade_xml_get_widget (s->glade_ui, name); + w = GTK_WIDGET (gtk_builder_get_object (s->gtk_ui, name)); #else /* !HAVE_GTK2 */ @@ -382,7 +352,7 @@ name_to_widget (state *s, const char *name) #endif /* HAVE_GTK2 */ if (w) return w; - fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n", + fprintf (stderr, "%s: no widget \"%s\" (wrong UI file?)\n", blurb(), name); abort(); } @@ -774,12 +744,12 @@ run_hack (state *s, int list_elt, Bool report_errors_p) /* Button callbacks According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make - libglade work on Cygwin; apparently all Glade callbacks need this magic - extra declaration. I do not pretend to understand. + GTK work on Cygwin; apparently all GTK callbacks need this magic extra + declaration. I do not pretend to understand. */ G_MODULE_EXPORT void -exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data) +exit_menu_cb (GtkAction *menu_action, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ flush_dialog_changes_and_save (s); @@ -798,28 +768,27 @@ wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data) G_MODULE_EXPORT void -about_menu_cb (GtkMenuItem *menuitem, gpointer user_data) +about_menu_cb (GtkAction *menu_action, gpointer user_data) { +#if 1 + /* Let's just pop up the splash dialog instead. */ + preview_theme_cb (NULL, user_data); +#else char msg [2048]; - char *vers = strdup (screensaver_id + 4); - char *s, *s2; char copy[1024]; - char year[5]; char *desc = _("For updates, check https://www.jwz.org/xscreensaver/"); - s = strchr (vers, ','); + char *version = strdup (screensaver_id + 17); + char *year = strchr (version, '-'); + char *s = strchr (version, ' '); + *s = 0; + year = strchr (year+1, '-') + 1; + s = strchr (year, ')'); *s = 0; - s += 2; - - s2 = vers; - s2 = strrchr (vers, '-'); - s2++; - strncpy (year, s2, 4); - year[4] = 0; /* Ole Laursen says "don't use _() here because non-ASCII characters aren't allowed in localizable string keys." - (I don't want to just use (c) instead of © because that doesn't + (I don't want to just use (c) instead of © because that doesn't look as good in the plain-old default Latin1 "C" locale.) */ #ifdef HAVE_GTK2 @@ -837,7 +806,7 @@ about_menu_cb (GtkMenuItem *menuitem, gpointer user_data) #ifdef HAVE_CRAPPLET { const gchar *auth[] = { 0 }; - GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc, + GtkWidget *about = gnome_about_new (progclass, version, "", auth, desc, "xscreensaver.xpm"); gtk_widget_show (about); } @@ -851,7 +820,8 @@ about_menu_cb (GtkMenuItem *menuitem, gpointer user_data) GtkWidget *dialog = gtk_dialog_new (); GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok; - GtkWidget *parent = GTK_WIDGET (menuitem); + GSList *proxies = gtk_action_get_proxies (menu_action); + GtkWidget *parent = GTK_WIDGET (proxies->data); while (GET_PARENT (parent)) parent = GET_PARENT (parent); @@ -871,7 +841,7 @@ about_menu_cb (GtkMenuItem *menuitem, gpointer user_data) vbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); - label1 = gtk_label_new (vers); + label1 = gtk_label_new (version); gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0); gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75); @@ -928,11 +898,12 @@ about_menu_cb (GtkMenuItem *menuitem, gpointer user_data) gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog))); gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog))); } +#endif /* 0 */ } G_MODULE_EXPORT void -doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data) +doc_menu_cb (GtkAction *menu_action, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ saver_preferences *p = &s->prefs; @@ -947,11 +918,11 @@ doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data) } help_command = (char *) malloc (strlen (p->load_url_command) + - (strlen (p->help_url) * 4) + 20); + (strlen (p->help_url) * 5) + 20); strcpy (help_command, "( "); sprintf (help_command + strlen(help_command), p->load_url_command, - p->help_url, p->help_url, p->help_url, p->help_url); + p->help_url, p->help_url, p->help_url, p->help_url, p->help_url); strcat (help_command, " ) &"); if (system (help_command) < 0) fprintf (stderr, "%s: fork error\n", blurb()); @@ -960,7 +931,7 @@ doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data) G_MODULE_EXPORT void -file_menu_cb (GtkMenuItem *menuitem, gpointer user_data) +file_menu_cb (GtkAction *menu_action, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ sensitize_menu_items (s, False); @@ -968,7 +939,7 @@ file_menu_cb (GtkMenuItem *menuitem, gpointer user_data) G_MODULE_EXPORT void -activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data) +activate_menu_cb (GtkAction *menu_action, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ run_cmd (s, XA_ACTIVATE, 0); @@ -976,7 +947,7 @@ activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data) G_MODULE_EXPORT void -lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data) +lock_menu_cb (GtkAction *menu_action, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ run_cmd (s, XA_LOCK, 0); @@ -984,7 +955,7 @@ lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data) G_MODULE_EXPORT void -kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data) +kill_menu_cb (GtkAction *menu_action, gpointer user_data) { state *s = global_state_kludge; /* I hate C so much... */ run_cmd (s, XA_EXIT, 0); @@ -998,7 +969,7 @@ restart_menu_cb (GtkWidget *widget, gpointer user_data) flush_dialog_changes_and_save (s); xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL); sleep (1); - if (system ("xscreensaver -nosplash &") < 0) + if (system ("xscreensaver -splash &") < 0) fprintf (stderr, "%s: fork error\n", blurb()); await_xscreensaver (s); @@ -1084,12 +1055,6 @@ demo_write_init_file (state *s, saver_preferences *p) { Display *dpy = GDK_DISPLAY(); -#if 0 - /* #### try to figure out why shit keeps getting reordered... */ - if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow")) - abort(); -#endif - if (!write_init_file (dpy, p, s->short_version, False)) { if (s->debug_p) @@ -1191,7 +1156,7 @@ force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p) if (!was) gtk_widget_set_sensitive (parent, True); #ifdef HAVE_GTK2 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list)); - g_assert (model); + if (!model) abort(); if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt)) { selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); @@ -1485,6 +1450,25 @@ flush_checkbox (GtkTreeModel *model, #endif /* HAVE_GTK2 */ + +static char * +theme_name_strip (const char *s) +{ + const char *in = s; + char *s2 = strdup(s); + char *out = s2; + for (; *in; in++) + if (*in >= 'A' && *in <= 'Z') + *out++ = *in + ('a'-'A'); + else if (*in == ' ' || *in == '\t') + ; + else + *out++ = *in; + *out = 0; + return s2; +} + + /* Flush out any changes made in the main dialog window (where changes take place immediately: clicking on a checkbox causes the init file to be written right away.) @@ -1492,6 +1476,7 @@ flush_checkbox (GtkTreeModel *model, static Bool flush_dialog_changes_and_save (state *s) { + Display *dpy = GDK_DISPLAY(); saver_preferences *p = &s->prefs; saver_preferences P2, *p2 = &P2; #ifdef HAVE_GTK2 @@ -1578,7 +1563,6 @@ flush_dialog_changes_and_save (state *s) #if 0 CHECKBOX (p2->verbose_p, "verbose_button"); - CHECKBOX (p2->capture_stderr_p, "capture_button"); CHECKBOX (p2->splash_p, "splash_button"); #endif @@ -1596,7 +1580,6 @@ flush_dialog_changes_and_save (state *s) TEXT (p2->text_url, "text_url_entry"); } - CHECKBOX (p2->install_cmap_p, "install_button"); CHECKBOX (p2->fade_p, "fade_button"); CHECKBOX (p2->unfade_p, "unfade_button"); SECONDS (&p2->fade_seconds, "fade_spinbutton"); @@ -1610,12 +1593,13 @@ flush_dialog_changes_and_save (state *s) /* Warn if the image directory doesn't exist, when: - not being warned before - image directory is changed and the directory doesn't exist - - image directory does not begin with http:// + - image directory is not a URL */ if (p2->image_directory && *p2->image_directory && !directory_p (p2->image_directory) && - strncmp(p2->image_directory, "http://", 6) && + strncmp(p2->image_directory, "http://", 7) && + strncmp(p2->image_directory, "https://", 8) && ( !already_warned_about_missing_image_directory || ( p->image_directory && *p->image_directory && @@ -1634,11 +1618,8 @@ flush_dialog_changes_and_save (state *s) /* Map the mode menu to `saver_mode' enum values. */ { - GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu")); - GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt)); - GtkWidget *selected = gtk_menu_get_active (menu); - GList *kids = gtk_container_children (GTK_CONTAINER (menu)); - int menu_elt = g_list_index (kids, (gpointer) selected); + GtkComboBox *opt = GTK_COMBO_BOX (name_to_widget (s, "mode_menu")); + int menu_elt = gtk_combo_box_get_active (opt); if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort(); p2->mode = mode_menu_order[menu_elt]; } @@ -1651,11 +1632,33 @@ flush_dialog_changes_and_save (state *s) : -1); } + /* Theme menu. */ + { + GtkComboBox *cbox = GTK_COMBO_BOX (name_to_widget (s, "theme_menu")); + char *themes = get_string_resource (dpy, "themeNames", "ThemeNames"); + int menu_index = gtk_combo_box_get_active (cbox); + char *token = themes; + char *name, *last; + int i = 0; + while ((name = strtok_r (token, ",", &last))) + { + token = 0; + if (i == menu_index) + { + char *name2 = theme_name_strip (name); + if (p->dialog_theme) free (p->dialog_theme); + p2->dialog_theme = name2; + } + i++; + } + } + # define COPY(field, name) \ if (p->field != p2->field) { \ changed = True; \ if (s->debug_p) \ - fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \ + fprintf (stderr, "%s: %s => %ld\n", blurb(), \ + name, (unsigned long) p2->field); \ } \ p->field = p2->field @@ -1675,7 +1678,6 @@ flush_dialog_changes_and_save (state *s) #if 0 COPY(verbose_p, "verbose_p"); - COPY(capture_stderr_p, "capture_stderr_p"); COPY(splash_p, "splash_p"); #endif @@ -1690,6 +1692,7 @@ flush_dialog_changes_and_save (state *s) COPY(grab_video_p, "grab_video_p"); COPY(random_image_p, "random_image_p"); + COPY(dialog_theme, "dialog_theme"); # undef COPY # define COPYSTR(FIELD,NAME) \ @@ -1717,14 +1720,7 @@ flush_dialog_changes_and_save (state *s) if (changed) { - Display *dpy = GDK_DISPLAY(); - Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK); - sync_server_dpms_settings (dpy, enabled_p, p->dpms_quickoff_p, - p->dpms_standby / 1000, - p->dpms_suspend / 1000, - p->dpms_off / 1000, - False); - + sync_server_dpms_settings (GDK_DISPLAY(), p); changed = demo_write_init_file (s, p); } @@ -1744,9 +1740,10 @@ flush_popup_changes_and_save (state *s) int list_elt = selected_list_element (s); GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text")); - GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo")); + GtkComboBoxEntry *vis = GTK_COMBO_BOX_ENTRY (name_to_widget (s, "visual_combo")); + GtkEntry *visent = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (vis))); - const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry)); + const char *visual = gtk_entry_get_text (visent); const char *command = gtk_entry_get_text (cmd); char c; @@ -1791,7 +1788,7 @@ flush_popup_changes_and_save (state *s) { gdk_beep (); /* unparsable */ visual = ""; - gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any")); + gtk_entry_set_text (visent, _("Any")); } changed = flush_changes (s, list_elt, -1, command, visual); @@ -1839,21 +1836,8 @@ mode_menu_item_cb (GtkWidget *widget, gpointer user_data) GtkWidget *list = name_to_widget (s, "list"); int list_elt; - GList *menu_items = - gtk_container_children (GTK_CONTAINER (GET_PARENT (widget))); - int menu_index = 0; - saver_mode new_mode; - - while (menu_items) - { - if (menu_items->data == widget) - break; - menu_index++; - menu_items = menu_items->next; - } - if (!menu_items) abort(); - - new_mode = mode_menu_order[menu_index]; + int menu_index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget)); + saver_mode new_mode = mode_menu_order[menu_index]; /* Keep the same list element displayed as before; except if we're switching *to* "one screensaver" mode from any other mode, set @@ -1901,7 +1885,7 @@ list_activated_cb (GtkTreeView *list, char *str; int list_elt; - g_return_if_fail (!gdk_pointer_is_grabbed ()); + if (gdk_pointer_is_grabbed()) return; str = gtk_tree_path_to_string (path); list_elt = strtol (str, NULL, 10); @@ -2305,7 +2289,12 @@ browse_text_program_cb (GtkButton *button, gpointer user_data) } - +G_MODULE_EXPORT void +preview_theme_cb (GtkWidget *w, gpointer user_data) +{ + if (system ("xscreensaver-auth --splash &") < 0) + fprintf (stderr, "%s: splash exec failed\n", blurb()); +} G_MODULE_EXPORT void @@ -2761,6 +2750,7 @@ update_list_sensitivity (state *s) static void populate_prefs_page (state *s) { + Display *dpy = GDK_DISPLAY(); saver_preferences *p = &s->prefs; Bool can_lock_p = True; @@ -2813,7 +2803,6 @@ populate_prefs_page (state *s) TOGGLE_ACTIVE ("lock_button", p->lock_p); #if 0 TOGGLE_ACTIVE ("verbose_button", p->verbose_p); - TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p); TOGGLE_ACTIVE ("splash_button", p->splash_p); #endif TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p); @@ -2821,7 +2810,6 @@ populate_prefs_page (state *s) TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p); TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p); TOGGLE_ACTIVE ("grab_image_button", p->random_image_p); - TOGGLE_ACTIVE ("install_button", p->install_cmap_p); TOGGLE_ACTIVE ("fade_button", p->fade_p); TOGGLE_ACTIVE ("unfade_button", p->unfade_p); @@ -2866,40 +2854,73 @@ populate_prefs_page (state *s) p->tmode == TEXT_URL); + /* Theme menu */ + { + GtkComboBox *cbox = GTK_COMBO_BOX (name_to_widget (s, "theme_menu")); + + /* Without this, pref_changed_cb gets called an exponentially-increasing + number of times on the themes menu, despite the call to + gtk_list_store_clear(). */ + static Bool done_once = False; + + if (cbox && !done_once) + { + char *themes = get_string_resource (dpy, "themeNames", "ThemeNames"); + char *token = themes; + char *name, *name2, *last; + GtkListStore *model; + GtkTreeIter iter; + int i = 0; + done_once = True; + + g_object_get (G_OBJECT (cbox), "model", &model, NULL); + if (!model) abort(); + gtk_list_store_clear (model); + + gtk_signal_connect (GTK_OBJECT (cbox), "changed", + GTK_SIGNAL_FUNC (pref_changed_cb), (gpointer) s); + + while ((name = strtok_r (token, ",", &last))) + { + int L; + token = 0; + + /* Strip leading and trailing whitespace */ + while (*name == ' ' || *name == '\t' || *name == '\n') + name++; + L = strlen(name); + while (L && (name[L-1] == ' ' || name[L-1] == '\t' || + name[L-1] == '\n')) + name[--L] = 0; + + gtk_list_store_append (model, &iter); + gtk_list_store_set (model, &iter, 0, name, -1); + + name2 = theme_name_strip (name); + if (!strcmp (p->dialog_theme, name2)) + gtk_combo_box_set_active (cbox, i); + free (name2); + i++; + } + } + } + + /* Map the `saver_mode' enum to mode menu to values. */ { - GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu")); + GtkComboBox *opt = GTK_COMBO_BOX (name_to_widget (s, "mode_menu")); int i; for (i = 0; i < countof(mode_menu_order); i++) if (mode_menu_order[i] == p->mode) break; - gtk_option_menu_set_history (opt, i); + gtk_combo_box_set_active (opt, i); update_list_sensitivity (s); } { - Bool found_any_writable_cells = False; - Bool fading_possible = False; Bool dpms_supported = False; - Display *dpy = GDK_DISPLAY(); - int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */ - int i; - for (i = 0; i < nscreens; i++) - { - Screen *s = ScreenOfDisplay (dpy, i); - if (has_writable_cells (s, DefaultVisualOfScreen (s))) - { - found_any_writable_cells = True; - break; - } - } - - fading_possible = found_any_writable_cells; -#ifdef HAVE_XF86VMODE_GAMMA - fading_possible = True; -#endif #ifdef HAVE_DPMS_EXTENSION { @@ -2923,7 +2944,6 @@ populate_prefs_page (state *s) */ SENSITIZE ("dpms_frame", dpms_supported); SENSITIZE ("dpms_button", dpms_supported); - SENSITIZE ("dpms_quickoff_button", dpms_supported); SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p); @@ -2934,24 +2954,26 @@ populate_prefs_page (state *s) SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p); SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p); + SENSITIZE ("dpms_quickoff_button", dpms_supported); - /* Colormaps - */ - SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible); - SENSITIZE ("install_button", found_any_writable_cells); - SENSITIZE ("fade_button", fading_possible); - SENSITIZE ("unfade_button", fading_possible); - - SENSITIZE ("fade_label", (fading_possible && - (p->fade_p || p->unfade_p))); - SENSITIZE ("fade_spinbutton", (fading_possible && - (p->fade_p || p->unfade_p))); + SENSITIZE ("fade_label", (p->fade_p || p->unfade_p)); + SENSITIZE ("fade_spinbutton", (p->fade_p || p->unfade_p)); # undef SENSITIZE } } +/* Allow the documentation label to re-flow when the text is changed. + http://blog.borovsak.si/2009/05/wrapping-adn-resizing-gtklabel.html + */ +static void +cb_allocate (GtkWidget *label, GtkAllocation *allocation, gpointer data) +{ + gtk_widget_set_size_request (label, allocation->width - 8, -1); +} + + static void populate_popup_window (state *s) { @@ -2962,6 +2984,9 @@ populate_popup_window (state *s) gtk_label_set_selectable (doc); */ + g_signal_connect (G_OBJECT (doc), "size-allocate", + G_CALLBACK (cb_allocate), NULL); + # ifdef HAVE_XML if (s->cdata) { @@ -2998,6 +3023,14 @@ populate_popup_window (state *s) gtk_label_set_text (doc, (doc_string ? _(doc_string) : _("No description available."))); + + { + GtkWidget *w = name_to_widget (s, "dialog_vbox"); + gtk_widget_hide (w); + gtk_widget_unrealize (w); + gtk_widget_realize (w); + gtk_widget_show (w); + } } @@ -3022,7 +3055,7 @@ sensitize_menu_items (state *s, Bool force_p) static Bool running_p = False; static time_t last_checked = 0; time_t now = time ((time_t *) 0); - const char *names[] = { "activate_menu", "lock_menu", "kill_menu", + const char *names[] = { "activate_action", "lock_action", "kill_action", /* "demo" */ }; int i; @@ -3034,8 +3067,8 @@ sensitize_menu_items (state *s, Bool force_p) for (i = 0; i < countof(names); i++) { - GtkWidget *w = name_to_widget (s, names[i]); - gtk_widget_set_sensitive (GTK_WIDGET(w), running_p); + GtkAction *a = GTK_ACTION (gtk_builder_get_object (s->gtk_ui, names[i])); + gtk_action_set_sensitive (a, running_p); } } @@ -3107,7 +3140,7 @@ fix_text_entry_sizes (state *s) /* Now fix the width of the combo box. */ w = GTK_WIDGET (name_to_widget (s, "visual_combo")); - w = GTK_COMBO (w)->entry; + w = GTK_COMBO_BOX_ENTRY (w)->entry; width = gdk_string_width (w->style->font, "PseudoColor___"); gtk_widget_set_usize (w, width, -2); @@ -3313,7 +3346,7 @@ populate_demo_window (state *s, int list_elt) GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame")); GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame")); GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text")); - GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo")); + GtkComboBoxEntry *vis = GTK_COMBO_BOX_ENTRY (name_to_widget (s, "visual_combo")); GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list")); if (p->mode == BLANK_ONLY) @@ -3363,7 +3396,7 @@ populate_demo_window (state *s, int list_elt) gtk_window_set_title (GTK_WINDOW (s->popup_widget), title); } - gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), + gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (vis))), (hack ? (hack->visual && *hack->visual ? hack->visual @@ -3471,10 +3504,11 @@ initialize_sort_map (state *s) char *name = (hack->name && *hack->name ? strdup (hack->name) : make_hack_name (dpy, hack->command)); - char *str; - for (str = name; *str; str++) - *str = tolower(*str); - sort_hack_cmp_names_kludge[i] = name; + gchar *s2 = g_str_to_ascii (name, 0); /* Sort "Möbius" properly */ + gchar *s3 = g_ascii_strdown (s2, -1); + free (name); + free (s2); + sort_hack_cmp_names_kludge[i] = s3; } /* Sort list->hack map alphabetically @@ -3782,7 +3816,7 @@ get_best_gl_visual (state *s) char *av[10]; int ac = 0; - av[ac++] = "xscreensaver-gl-helper"; + av[ac++] = "xscreensaver-gl-visual"; av[ac] = 0; if (pipe (fds)) @@ -4395,7 +4429,9 @@ gnome_screensaver_window (Screen *screen) &bytesafter, &name) == Success && type != None - && !strcmp ((char *) name, "gnome-screensaver")) + && (!strcmp ((char *) name, "gnome-screensaver") || + !strcmp ((char *) name, "mate-screensaver") || + !strcmp ((char *) name, "cinnamon-screensaver"))) { gnome_window = kids[i]; break; @@ -4464,11 +4500,9 @@ the_network_is_not_the_computer (state *s) lhost = ""; else lhost = uts.nodename; -# elif defined(VMS) - strcpy (lhost, getenv("SYS$NODE")); -# else /* !HAVE_UNAME && !VMS */ +# else /* !HAVE_UNAME */ strcat (lhost, ""); -# endif /* !HAVE_UNAME && !VMS */ +# endif /* !HAVE_UNAME */ if (p && p->pw_name) luser = p->pw_name; @@ -4791,10 +4825,7 @@ main (int argc, char **argv) progname = real_progname; - s->short_version = (char *) malloc (5); - memcpy (s->short_version, screensaver_id + 17, 4); - s->short_version [4] = 0; - + s->short_version = XSCREENSAVER_VERSION; /* Register our error message logger for every ``log domain'' known. There's no way to do this globally, so I grepped the Gtk/Gdk sources @@ -5007,7 +5038,7 @@ main (int argc, char **argv) dpy = XtDisplay (toplevel_shell); db = XtDatabase (dpy); - XtGetApplicationNameAndClass (dpy, &progname, &progclass); + XtGetApplicationNameAndClass (dpy, (char **) &progname, &progclass); XSetErrorHandler (demo_ehandler); /* Let's just ignore these. They seem to confuse Irix Gtk... */ @@ -5046,6 +5077,7 @@ main (int argc, char **argv) p->db = db; s->nscreens = screen_count (dpy); + init_xscreensaver_atoms (dpy); hack_environment (s); /* must be before initialize_sort_map() */ load_init_file (dpy, p); @@ -5069,26 +5101,7 @@ main (int argc, char **argv) } #endif - - /* Intern the atoms that xscreensaver_command() needs. - */ - XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False); - XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False); - XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False); - XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False); - XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False); - XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False); - XA_SELECT = XInternAtom (dpy, "SELECT", False); - XA_DEMO = XInternAtom (dpy, "DEMO", False); - XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False); - XA_SUSPEND = XInternAtom (dpy, "SUSPEND", False); - XA_BLANK = XInternAtom (dpy, "BLANK", False); - XA_LOCK = XInternAtom (dpy, "LOCK", False); - XA_NEXT = XInternAtom (dpy, "NEXT", False); - XA_PREV = XInternAtom (dpy, "PREV", False); - XA_EXIT = XInternAtom (dpy, "EXIT", False); - XA_RESTART = XInternAtom (dpy, "RESTART", False); - + init_xscreensaver_atoms (dpy); /* Create the window and all its widgets. */ @@ -5171,31 +5184,32 @@ main (int argc, char **argv) #endif /* !HAVE_GTK2 */ /* Hook up callbacks to the items on the mode menu. */ - { - GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu")); - GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt)); - GList *kids = gtk_container_children (GTK_CONTAINER (menu)); - int i; - for (i = 0; kids; kids = kids->next, i++) - { - gtk_signal_connect (GTK_OBJECT (kids->data), "activate", - GTK_SIGNAL_FUNC (mode_menu_item_cb), - (gpointer) s); - - /* The "random-same" mode menu item does not appear unless - there are multple screens. - */ - if (s->nscreens <= 1 && - mode_menu_order[i] == RANDOM_HACKS_SAME) - gtk_widget_hide (GTK_WIDGET (kids->data)); - } + gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "mode_menu")), + "changed", GTK_SIGNAL_FUNC (mode_menu_item_cb), + (gpointer) s); + if (s->nscreens <= 1) + { + GtkComboBox *opt = GTK_COMBO_BOX (name_to_widget (s, "mode_menu")); + GtkTreeModel *list = gtk_combo_box_get_model (opt); + unsigned int i; + for (i = 0; i < countof(mode_menu_order); i++) + { + /* The "random-same" mode menu item does not appear unless + there are multiple screens. + */ + if (mode_menu_order[i] == RANDOM_HACKS_SAME) + { + GtkTreeIter iter; + gtk_tree_model_iter_nth_child (list, &iter, NULL, i); + gtk_list_store_remove (GTK_LIST_STORE (list), &iter); + break; + } + } - if (s->nscreens <= 1) /* recompute option-menu size */ - { - gtk_widget_unrealize (GTK_WIDGET (menu)); - gtk_widget_realize (GTK_WIDGET (menu)); - } - } + /* recompute option-menu size */ + gtk_widget_unrealize (GTK_WIDGET (opt)); + gtk_widget_realize (GTK_WIDGET (opt)); + } /* Handle the -prefs command-line argument. */ @@ -5299,7 +5313,7 @@ main (int argc, char **argv) the_network_is_not_the_computer (s); - if (senesculent_p()) + if (time ((time_t *) 0) - XSCREENSAVER_RELEASED > 60*60*24*30*17) warning_dialog (s->toplevel_widget, _("Warning:\n\n" "This version of xscreensaver is VERY OLD!\n" @@ -5312,7 +5326,6 @@ main (int argc, char **argv) ), D_NONE, 7); - /* Run the Gtk event loop, and not the Xt event loop. This means that if there were Xt timers or fds registered, they would never get serviced, and if there were any Xt widgets, they would never have events delivered. diff --git a/driver/demo-Xm-widgets.c b/driver/demo-Xm-widgets.c index cbe3393..d47259b 100644 --- a/driver/demo-Xm-widgets.c +++ b/driver/demo-Xm-widgets.c @@ -79,6 +79,8 @@ tab_cb (Widget button, XtPointer client_data, XtPointer ignored) } +extern Widget create_xscreensaver_demo (Widget parent); + Widget create_xscreensaver_demo (Widget parent) { @@ -539,14 +541,12 @@ create_options_page (Widget parent) timeoutLabel cycleLabel fadeSecondsLabel - fadeTicksLabel lockLabel passwdLabel timeoutText cycleText fadeSecondsText - fadeTicksText lockText passwdText @@ -566,11 +566,11 @@ create_options_page (Widget parent) Arg av[64]; int ac = 0; Widget children[100]; - Widget timeout_label, cycle_label, fade_seconds_label, fade_ticks_label; + Widget timeout_label, cycle_label, fade_seconds_label; Widget lock_label, passwd_label, hr; Widget preferences_form; - Widget timeout_text, cycle_text, fade_text, fade_ticks_text; + Widget timeout_text, cycle_text, fade_text; Widget lock_timeout_text, passwd_timeout_text, verbose_toggle; Widget install_cmap_toggle, fade_toggle, unfade_toggle; Widget lock_toggle, prefs_done, prefs_cancel; @@ -601,10 +601,6 @@ create_options_page (Widget parent) "fadeSecondsLabel", av, ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; - fade_ticks_label = XmCreateLabelGadget (preferences_form, "fadeTicksLabel", - av, ac); - ac = 0; - XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; lock_label = XmCreateLabelGadget (preferences_form, "lockLabel", av, ac); ac = 0; XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; @@ -613,8 +609,6 @@ create_options_page (Widget parent) timeout_text = XmCreateTextField (preferences_form, "timeoutText", av, ac); cycle_text = XmCreateTextField (preferences_form, "cycleText", av, ac); fade_text = XmCreateTextField (preferences_form, "fadeSecondsText", av, ac); - fade_ticks_text = XmCreateTextField (preferences_form, "fadeTicksText", - av, ac); lock_timeout_text = XmCreateTextField (preferences_form, "lockText", av, ac); passwd_timeout_text = XmCreateTextField (preferences_form, "passwdText", @@ -684,20 +678,6 @@ create_options_page (Widget parent) XmNrightWidget, fade_text, NULL); - XtVaSetValues (fade_ticks_label, - XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, - XmNtopOffset, 0, - XmNtopWidget, fade_ticks_text, - XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, - XmNbottomOffset, 0, - XmNbottomWidget, fade_ticks_text, - XmNleftAttachment, XmATTACH_FORM, - XmNleftOffset, 20, - XmNrightAttachment, XmATTACH_WIDGET, - XmNrightOffset, 4, - XmNrightWidget, fade_ticks_text, - NULL); - XtVaSetValues (lock_label, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, @@ -751,7 +731,7 @@ create_options_page (Widget parent) XmNleftWidget, cycle_text, NULL); - XtVaSetValues (fade_ticks_text, + XtVaSetValues (lock_timeout_text, XmNtopAttachment, XmATTACH_WIDGET, XmNtopOffset, 2, XmNtopWidget, fade_text, @@ -760,15 +740,6 @@ create_options_page (Widget parent) XmNleftWidget, fade_text, NULL); - XtVaSetValues (lock_timeout_text, - XmNtopAttachment, XmATTACH_WIDGET, - XmNtopOffset, 2, - XmNtopWidget, fade_ticks_text, - XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, - XmNleftOffset, 0, - XmNleftWidget, fade_ticks_text, - NULL); - XtVaSetValues (passwd_timeout_text, XmNtopAttachment, XmATTACH_WIDGET, XmNtopOffset, 4, @@ -822,10 +793,10 @@ create_options_page (Widget parent) XtVaSetValues (unfade_toggle, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, - XmNtopWidget, fade_ticks_text, + XmNtopWidget, lock_timeout_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, - XmNbottomWidget, fade_ticks_text, + XmNbottomWidget, lock_timeout_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, fade_toggle, @@ -836,10 +807,10 @@ create_options_page (Widget parent) XtVaSetValues (lock_toggle, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopOffset, 0, - XmNtopWidget, lock_timeout_text, + XmNtopWidget, passwd_timeout_text, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomOffset, 0, - XmNbottomWidget, lock_timeout_text, + XmNbottomWidget, passwd_timeout_text, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, unfade_toggle, @@ -872,13 +843,11 @@ create_options_page (Widget parent) children[ac++] = timeout_label; children[ac++] = cycle_label; children[ac++] = fade_seconds_label; - children[ac++] = fade_ticks_label; children[ac++] = lock_label; children[ac++] = passwd_label; children[ac++] = timeout_text; children[ac++] = cycle_text; children[ac++] = fade_text; - children[ac++] = fade_ticks_text; children[ac++] = lock_timeout_text; children[ac++] = passwd_timeout_text; children[ac++] = verbose_toggle; diff --git a/driver/demo-Xm.c b/driver/demo-Xm.c index 3497641..547bbe9 100644 --- a/driver/demo-Xm.c +++ b/driver/demo-Xm.c @@ -1,5 +1,5 @@ /* demo-Xm.c --- implements the interactive demo-mode and options dialogs. - * xscreensaver, Copyright (c) 1993-2003, 2005 Jamie Zawinski + * xscreensaver, Copyright © 1993-2021 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 @@ -16,23 +16,20 @@ #ifdef HAVE_MOTIF /* whole file */ +#include "blurb.h" + #include #ifdef HAVE_UNISTD_H # include #endif -#ifndef VMS -# include /* for getpwuid() */ -#else /* VMS */ -# include "vms-pwd.h" -#endif /* VMS */ - #ifdef HAVE_UNAME # include /* for uname() */ #endif /* HAVE_UNAME */ #include +#include /* for getpwuid() */ #include /* for CARD32 */ #include /* for XA_INTEGER */ @@ -48,18 +45,6 @@ # include #endif /* HAVE_XPM */ -#ifdef HAVE_XMU -# ifndef VMS -# include -# else /* VMS */ -# include -# endif -#else -# include "xmu.h" -#endif - - - #include #include #include @@ -78,11 +63,12 @@ #endif /* HAVE_XMCOMBOBOX */ #include "version.h" -#include "prefs.h" +#include "types.h" #include "resources.h" /* for parse_time() */ -#include "visual.h" /* for has_writable_cells() */ #include "remote.h" /* for xscreensaver_command() */ -#include "usleep.h" +#include "visual.h" +#include "atoms.h" +#include "xmu.h" #include #include @@ -92,7 +78,6 @@ #define countof(x) (sizeof((x))/sizeof((*x))) -char *progname = 0; char *progclass = "XScreenSaver"; XrmDatabase db; @@ -102,20 +87,12 @@ typedef struct { static void *global_prefs_pair; /* I hate C so much... */ -char *blurb (void) { return progname; } - extern Widget create_xscreensaver_demo (Widget parent); extern const char *visual_menu[]; static char *short_version = 0; -Atom XA_VROOT; -Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION; -Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO; -Atom XA_ACTIVATE, XA_SUSPEND, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT; - - static void populate_demo_window (Widget toplevel, int which, prefs_pair *pair); static void populate_prefs_page (Widget top, prefs_pair *pair); @@ -297,7 +274,7 @@ run_hack (Widget widget, int which, Bool report_errors_p) /* Button callbacks */ -void +static void exit_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { apply_changes_and_save (XtParent (button)); @@ -313,7 +290,7 @@ wm_close_cb (Widget widget, GdkEvent *event, XtPointer data) } #endif -void +static void cut_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { /* #### */ @@ -323,7 +300,7 @@ cut_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) } -void +static void copy_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { /* #### */ @@ -333,7 +310,7 @@ copy_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) } -void +static void paste_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { /* #### */ @@ -343,7 +320,7 @@ paste_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) } -void +static void about_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { char buf [2048]; @@ -356,8 +333,8 @@ about_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) sprintf (buf, "%s\n%s\n" "\n" - "This is the Motif version of \"xscreensaver-demo\". The Motif\n" - "version is no longer maintained. Please use the GTK version\n" + "This is the Motif version of \"xscreensaver-settings\".\n" + "It is no longer maintained. Please use the GTK version\n" "instead, which has many more features.\n" "\n" "For xscreensaver updates, check https://www.jwz.org/xscreensaver/", @@ -368,7 +345,7 @@ about_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) } -void +static void doc_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; @@ -385,39 +362,39 @@ doc_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) } help_command = (char *) malloc (strlen (p->load_url_command) + - (strlen (p->help_url) * 4) + 20); + (strlen (p->help_url) * 5) + 20); strcpy (help_command, "( "); sprintf (help_command + strlen(help_command), p->load_url_command, - p->help_url, p->help_url, p->help_url, p->help_url); + p->help_url, p->help_url, p->help_url, p->help_url, p->help_url); strcat (help_command, " ) &"); system (help_command); free (help_command); } -void +static void activate_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { run_cmd (XtParent (button), XA_ACTIVATE, 0); } -void +static void lock_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { run_cmd (XtParent (button), XA_LOCK, 0); } -void +static void kill_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { run_cmd (XtParent (button), XA_EXIT, 0); } -void +static void restart_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) { #if 0 @@ -427,7 +404,7 @@ restart_menu_cb (Widget button, XtPointer client_data, XtPointer ignored) apply_changes_and_save (button); xscreensaver_command (XtDisplay (button), XA_EXIT, 0, False, NULL); sleep (1); - system ("xscreensaver -nosplash &"); + system ("xscreensaver -splash &"); #endif await_xscreensaver (button); @@ -641,7 +618,7 @@ apply_changes_and_save (Widget widget) return 0; } -void +static void run_this_cb (Widget button, XtPointer client_data, XtPointer ignored) { int which = selected_hack_number (XtParent (button)); @@ -651,7 +628,7 @@ run_this_cb (Widget button, XtPointer client_data, XtPointer ignored) } -void +static void manual_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; @@ -695,7 +672,7 @@ manual_cb (Widget button, XtPointer client_data, XtPointer ignored) } -void +static void run_next_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; @@ -725,7 +702,7 @@ run_next_cb (Widget button, XtPointer client_data, XtPointer ignored) } -void +static void run_prev_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; @@ -781,7 +758,7 @@ hack_time_text (Widget button, const char *line, Time *store, Bool sec_p) } -void +static void prefs_ok_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; @@ -825,7 +802,6 @@ prefs_ok_cb (Widget button, XtPointer client_data, XtPointer ignored) MINUTES (&p2->timeout, "timeoutText"); MINUTES (&p2->cycle, "cycleText"); SECONDS (&p2->fade_seconds, "fadeSecondsText"); - INTEGER (&p2->fade_ticks, "fadeTicksText"); MINUTES (&p2->lock_timeout, "lockText"); SECONDS (&p2->passwd_timeout, "passwdText"); CHECKBOX (p2->verbose_p, "verboseToggle"); @@ -848,7 +824,6 @@ prefs_ok_cb (Widget button, XtPointer client_data, XtPointer ignored) COPY(lock_timeout); COPY(passwd_timeout); COPY(fade_seconds); - COPY(fade_ticks); COPY(verbose_p); COPY(install_cmap_p); COPY(fade_p); @@ -863,7 +838,7 @@ prefs_ok_cb (Widget button, XtPointer client_data, XtPointer ignored) } -void +static void prefs_cancel_cb (Widget button, XtPointer client_data, XtPointer ignored) { prefs_pair *pair = (prefs_pair *) client_data; @@ -996,8 +971,6 @@ populate_prefs_page (Widget top, prefs_pair *pair) XtVaSetValues (name_to_widget (top, "passwdText"), XmNvalue, s, NULL); format_time (s, p->fade_seconds); XtVaSetValues (name_to_widget (top, "fadeSecondsText"), XmNvalue, s, NULL); - sprintf (s, "%u", p->fade_ticks); - XtVaSetValues (name_to_widget (top, "fadeTicksText"), XmNvalue, s, NULL); XtVaSetValues (name_to_widget (top, "verboseToggle"), XmNset, p->verbose_p, NULL); @@ -1009,42 +982,6 @@ populate_prefs_page (Widget top, prefs_pair *pair) XmNset, p->unfade_p, NULL); XtVaSetValues (name_to_widget (top, "lockToggle"), XmNset, p->lock_p, NULL); - - - { - Bool found_any_writable_cells = False; - Display *dpy = XtDisplay (top); - int nscreens = ScreenCount(dpy); - int i; - for (i = 0; i < nscreens; i++) - { - Screen *s = ScreenOfDisplay (dpy, i); - if (has_writable_cells (s, DefaultVisualOfScreen (s))) - { - found_any_writable_cells = True; - break; - } - } - -#ifdef HAVE_XF86VMODE_GAMMA - found_any_writable_cells = True; /* if we can gamma fade, go for it */ -#endif - - XtVaSetValues (name_to_widget (top, "fadeSecondsLabel"), XtNsensitive, - found_any_writable_cells, NULL); - XtVaSetValues (name_to_widget (top, "fadeTicksLabel"), XtNsensitive, - found_any_writable_cells, NULL); - XtVaSetValues (name_to_widget (top, "fadeSecondsText"), XtNsensitive, - found_any_writable_cells, NULL); - XtVaSetValues (name_to_widget (top, "fadeTicksText"), XtNsensitive, - found_any_writable_cells, NULL); - XtVaSetValues (name_to_widget (top, "cmapToggle"), XtNsensitive, - found_any_writable_cells, NULL); - XtVaSetValues (name_to_widget (top, "fadeToggle"), XtNsensitive, - found_any_writable_cells, NULL); - XtVaSetValues (name_to_widget (top, "unfadeToggle"), XtNsensitive, - found_any_writable_cells, NULL); - } } @@ -1054,7 +991,7 @@ sensitize_demo_widgets (Widget toplevel, Bool sensitive_p) const char *names[] = { "cmdLabel", "cmdText", "enabled", "visLabel", "combo", "demo", "man" }; int i; - for (i = 0; i < sizeof(names)/countof(*names); i++) + for (i = 0; i < countof(names); i++) { Widget w = name_to_widget (toplevel, names[i]); XtVaSetValues (w, XtNsensitive, sensitive_p, NULL); @@ -1063,7 +1000,7 @@ sensitize_demo_widgets (Widget toplevel, Bool sensitive_p) /* I don't know how to handle these yet... */ { const char *names2[] = { "cut", "copy", "paste" }; - for (i = 0; i < sizeof(names2)/countof(*names2); i++) + for (i = 0; i < countof(names2); i++) { Widget w = name_to_widget (toplevel, names2[i]); XtVaSetValues (w, XtNsensitive, FALSE, NULL); @@ -1208,7 +1145,7 @@ pixmapify_buttons (Widget toplevel) -char * +static char * get_hack_blurb (Display *dpy, screenhack *hack) { char *doc_string; @@ -1301,8 +1238,8 @@ get_hack_blurb (Display *dpy, screenhack *hack) # endif /* 0 */ doc_string = strdup ( "\n" - "This is the Motif version of \"xscreensaver-demo\". The Motif " - "version is no longer maintained. Please use the GTK version " + "This is the Motif version of \"xscreensaver-settings\"." + "It is no longer maintained. Please use the GTK version " "instead, which has many more features." "\n\n" "If you were running the GTK version, there would be a preview " @@ -1475,10 +1412,10 @@ sanity_check_resources (Widget toplevel) { const char *names[] = { "demoTab", "optionsTab", "cmdLabel", "visLabel", "enabled", "demo", "man", "timeoutLabel", - "cycleLabel", "fadeSecondsLabel", "fadeTicksLabel", + "cycleLabel", "fadeSecondsLabel", "lockLabel", "passwdLabel" }; int i; - for (i = 0; i < sizeof(names)/countof(*names); i++) + for (i = 0; i < countof(names); i++) { Widget w = name_to_widget (toplevel, names[i]); const char *name = XtName(w); @@ -1566,11 +1503,9 @@ the_network_is_not_the_computer (Widget parent) lhost = ""; else lhost = uts.nodename; -# elif defined(VMS) - strcpy (lhost, getenv("SYS$NODE")); -# else /* !HAVE_UNAME && !VMS */ +# else /* !HAVE_UNAME */ strcat (lhost, ""); -# endif /* !HAVE_UNAME && !VMS */ +# endif /* !HAVE_UNAME */ if (p && p->pw_name) luser = p->pw_name; @@ -1738,7 +1673,7 @@ main (int argc, char **argv) dpy = XtDisplay (toplevel_shell); db = XtDatabase (dpy); - XtGetApplicationNameAndClass (dpy, &progname, &progclass); + XtGetApplicationNameAndClass (dpy, (char **) &progname, &progclass); XSetErrorHandler (demo_ehandler); /* Complain about unrecognized command-line arguments. @@ -1758,9 +1693,9 @@ main (int argc, char **argv) } } - short_version = (char *) malloc (5); - memcpy (short_version, screensaver_id + 17, 4); - short_version [4] = 0; + short_version = strdup (screensaver_id + 17); + s = strchr (short_version, ' '); + *s = 0; /* Load the init file, which may end up consulting the X resource database and the site-wide app-defaults file. Note that at this point, it's @@ -1789,22 +1724,7 @@ main (int argc, char **argv) #endif - /* Intern the atoms that xscreensaver_command() needs. - */ - XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False); - XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False); - XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False); - XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False); - XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False); - XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False); - XA_SELECT = XInternAtom (dpy, "SELECT", False); - XA_DEMO = XInternAtom (dpy, "DEMO", False); - XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False); - XA_SUSPEND = XInternAtom (dpy, "SUSPEND", False); - XA_BLANK = XInternAtom (dpy, "BLANK", False); - XA_LOCK = XInternAtom (dpy, "LOCK", False); - XA_EXIT = XInternAtom (dpy, "EXIT", False); - XA_RESTART = XInternAtom (dpy, "RESTART", False); + init_xscreensaver_atoms (dpy); /* Create the window and all its widgets. */ diff --git a/driver/dialog.c b/driver/dialog.c new file mode 100644 index 0000000..fce74c4 --- /dev/null +++ b/driver/dialog.c @@ -0,0 +1,2507 @@ +/* dialog.c --- the password dialog and splash screen. + * xscreensaver, Copyright © 1993-2021 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 file renders the unlock dialog and splash screen, using Xlib and Xft. + * One significant complication is that it must read raw XInput2 events to + * get keyboard and mouse input, as the "xscreensaver" process has the mouse + * and keyboard grabbed while this is running. + * + * It might be possible to implement this file using Gtk instead of Xlib, + * but the grab situation might make that tricky: those events would have to + * be re-sent to the toolkit widgets in a way that it would understand them. + * Also, toolkits tend to assume that a window manager exists, and this + * window must be an OverrideRedirect window with no focus management. + * + * Crashes here are interpreted as "unauthorized" and do not unlock the + * screen. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_UNAME +# include +#endif /* HAVE_UNAME */ +#include +#include + +#include /* for CARD32 */ +#include +#include +#include +#include + +#ifdef ENABLE_NLS +# include +# include +# define _(S) gettext(S) +#else +# define _(S) (S) +#endif + +#ifdef HAVE_XKB +# include +# include +#endif + +#include "version.h" +#include "blurb.h" +#include "auth.h" +#include "atoms.h" +#include "screens.h" +#include "xft.h" +#include "xftwrap.h" +#include "xinput.h" +#include "resources.h" +#include "visual.h" +#include "font-retry.h" +#include "prefs.h" +#include "usleep.h" + +extern Bool debug_p; + +#undef DEBUG_METRICS +#undef DEBUG_STACKING + +#define LOCK_FAILURE_ATOM "_XSCREENSAVER_AUTH_FAILURES" + +#undef MAX +#undef MIN +#define MAX(a,b) ((a)>(b)?(a):(b)) +#define MIN(a,b) ((a)<(b)?(a):(b)) + +#define MAX_BYTES_PER_CHAR 8 /* UTF-8 uses up to 6 bytes */ +#define MAX_PASSWD_CHARS 280 /* Longest possible passphrase */ + +typedef struct window_state window_state; + + +typedef enum { + AUTH_READ, /* reading input or ready to do so */ + AUTH_SUCCESS, /* auth success, unlock */ + AUTH_FAIL, /* auth fail */ + AUTH_CANCEL, /* user canceled, or typed blank password */ + AUTH_TIME, /* timed out */ + AUTH_FINISHED, /* user pressed enter */ + AUTH_NOTIFY /* displaying message after finished */ +} auth_state; + + +/* A mini-toolkit for rendering text labels, input fields, and buttons. + */ +typedef enum { CENTER, LEFT, RIGHT } line_align; + +typedef struct { + Bool down_p; + XRectangle rect; + char *cmd; + void (*fn) (window_state *ws); + Bool disabled_p; +} line_button_state; + + +typedef struct { + char *text; + XftFont *font; + XftColor fg; + Pixel bg; + enum { LABEL, BUTTON, TEXT, TEXT_RO } type; + line_align align; + Bool float_p; + Bool i_beam; + line_button_state *button; +} dialog_line; + + +/* Global state. + */ +struct window_state { + XtAppContext app; + Display *dpy; + Screen *screen; + Position cx, cy, x, y; + Dimension min_height; + Window window; + Colormap cmap; + + Bool splash_p; + auth_state auth_state; + int xi_opcode; + int xkb_opcode; + + /* Variant strings + */ + char *version; + char *user; + int nmsgs; + const auth_message *msgs; + + /* "Characters" in the password may be a variable number of bytes long. + plaintext_passwd contains the raw bytes. + plaintext_passwd_char_size indicates the size in bytes of each character, + so that we can make backspace work. + censored_passwd is the asterisk version. + + Maybe it would be more sensible to use uint32_t and utils/utf8wc.c here, + but the multi-byte string returned by XLookupString might not be UTF-8 + (see comment in handle_keypress). + */ + char plaintext_passwd [MAX_PASSWD_CHARS * MAX_BYTES_PER_CHAR]; + char censored_passwd [MAX_PASSWD_CHARS * MAX_BYTES_PER_CHAR]; + char plaintext_passwd_char_size [MAX_PASSWD_CHARS]; + + XComposeStatus compose_status; + + XtIntervalId timer; + + XtIntervalId cursor_timer; /* Blink the I-beam */ + int i_beam; + + double start_time, end_time; + + Bool show_stars_p; /* "I regret that I have but one asterisk for my country." + -- Nathan Hale, 1776. */ + Bool caps_p; /* Whether we saw a keypress with caps-lock on */ + + char *dialog_theme; + char *heading_label; + char *body_label; + char *hostname_label; + char *date_format; + char *kbd_layout_label; + char *newlogin_cmd; + + /* Resources for fonts and colors */ + XftDraw *xftdraw; + XftFont *heading_font; + XftFont *body_font; + XftFont *error_font; + XftFont *label_font; + XftFont *date_font; + XftFont *button_font; + XftFont *hostname_font; + + Pixel foreground; + Pixel background; + XftColor xft_foreground; + XftColor xft_text_foreground; + XftColor xft_button_foreground; + XftColor xft_error_foreground; + Pixel passwd_background; + Pixel thermo_foreground; + Pixel thermo_background; + Pixel shadow_top; + Pixel shadow_bottom; + Pixel border_color; + Pixel button_background; + Pixel logo_background; + + Dimension preferred_logo_width; + Dimension preferred_logo_height; + Dimension thermo_width; + Dimension internal_padding; + Dimension shadow_width; + Dimension border_width; + + Pixmap logo_pixmap; + Pixmap logo_clipmask; + unsigned int logo_width, logo_height; + int logo_npixels; + unsigned long *logo_pixels; + + line_button_state newlogin_button_state; + line_button_state unlock_button_state; + line_button_state demo_button_state; + line_button_state help_button_state; +}; + + +static void +draw_shaded_rectangle (Display *dpy, Window window, + int x, int y, + int width, int height, + int thickness, + unsigned long top_color, + unsigned long bottom_color) +{ + XPoint points[4]; + XGCValues gcv; + GC gc1, gc2; + if (thickness == 0) return; + + gcv.foreground = top_color; + gc1 = XCreateGC (dpy, window, GCForeground, &gcv); + gcv.foreground = bottom_color; + gc2 = XCreateGC (dpy, window, GCForeground, &gcv); + + points [0].x = x; + points [0].y = y; + points [1].x = x + width; + points [1].y = y; + points [2].x = x + width - thickness; + points [2].y = y + thickness; + points [3].x = x; + points [3].y = y + thickness; + XFillPolygon (dpy, window, gc1, points, 4, Convex, CoordModeOrigin); + + points [0].x = x; + points [0].y = y + thickness; + points [1].x = x; + points [1].y = y + height; + points [2].x = x + thickness; + points [2].y = y + height - thickness; + points [3].x = x + thickness; + points [3].y = y + thickness; + XFillPolygon (dpy, window, gc1, points, 4, Convex, CoordModeOrigin); + + points [0].x = x + width; + points [0].y = y; + points [1].x = x + width - thickness; + points [1].y = y + thickness; + points [2].x = x + width - thickness; + points [2].y = y + height - thickness; + points [3].x = x + width; + points [3].y = y + height - thickness; + XFillPolygon (dpy, window, gc2, points, 4, Convex, CoordModeOrigin); + + points [0].x = x; + points [0].y = y + height; + points [1].x = x + width; + points [1].y = y + height; + points [2].x = x + width; + points [2].y = y + height - thickness; + points [3].x = x + thickness; + points [3].y = y + height - thickness; + XFillPolygon (dpy, window, gc2, points, 4, Convex, CoordModeOrigin); + + XFreeGC (dpy, gc1); + XFreeGC (dpy, gc2); +} + +#define IBEAM_WIDTH 2 + +static void +draw_i_beam (Display *dpy, Drawable d, Pixel color, int x, int y, int height) +{ + XGCValues gcv; + GC gc; + gcv.foreground = color; + gcv.line_width = IBEAM_WIDTH; + gc = XCreateGC (dpy, d, GCForeground | GCLineWidth, &gcv); + XDrawLine (dpy, d, gc, x, y, x, y + height); /* Ceci n'est pas une pipe */ + XFreeGC (dpy, gc); +} + + +static int +draw_dialog_line (window_state *ws, Drawable d, dialog_line *line, + int left, int right, int y, Bool clear_p) +{ + int w = right - left; + int h; + int xpad = 0, ypad = 0; + XGlyphInfo overall; + line_align align = line->align; + int oleft = left; + int tleft = left; + int oright = right; + int clip_w = 0; + int gutter = 0; + XRectangle rect; + int xoff2 = 0; + int yoff2 = 0; + char *text2 = 0; + char *text = line->text; + int nlines = 1; + + /* Adjust left/right margins based on the type of the line. + */ + switch (line->type) { + case LABEL: + if (line->float_p && line->align == LEFT) + { + /* Add 1px to leave a little padding between the top border of the + label and the ascenders. */ + ypad = ws->shadow_width + 1; + right = left + w/2 - ws->shadow_width * 2 - line->font->ascent / 2; + align = RIGHT; + } + + if (*line->text) + text = text2 = xft_word_wrap (ws->dpy, line->font, line->text, + right - left); + break; + + case BUTTON: /* box is fixed width at 1/3, text centered */ + align = CENTER; + xpad = 0; + /* make the buttons a little taller than everything else */ + /* Add 1px as above */ + ypad = ws->shadow_width + line->font->ascent / 2 + 1; + gutter = ws->shadow_width; + clear_p = True; + + switch (line->align) { + case LEFT: + right = left + w/3 - xpad; + break; + case CENTER: + xpad = ws->shadow_width * 2; + left += w/3 + xpad; + right -= w/3 + xpad; + break; + case RIGHT: + left = right - w/3 + xpad; + break; + } + oright = right; + xpad = 0; + break; + + case TEXT: /* box is fixed width at 1/2, text left */ + case TEXT_RO: + align = LEFT; + oleft = left + xoff2; + clear_p = True; + xpad = ws->shadow_width + line->font->ascent / 4; + /* Add 1px as above */ + ypad = ws->shadow_width + 1; + gutter = ws->shadow_width; + if (gutter < 2) gutter = 2; + + switch (line->align) { + case LEFT: + right = left + w/2; + break; + case RIGHT: + left = right - w/2; + break; + case CENTER: + abort(); + break; + } + + /* If the text is longer than the field, scroll horizontally to show + the end of the text instead of the beginning. + */ + XftTextExtentsUtf8_multi (ws->dpy, line->font, (FcChar8 *) text, + strlen(text), &overall); + if (overall.width >= w/2 - ws->shadow_width * 2 - IBEAM_WIDTH) + { + align = RIGHT; + left = right - w/2; + } + break; + + default: abort(); break; + } + + /* Clear out the area we're about to overwrite. + */ + h = nlines * (line->font->ascent + line->font->descent) + ypad*2; + if (clear_p) + { + GC gc; + XGCValues gcv; + gcv.foreground = line->bg; + gc = XCreateGC (ws->dpy, d, GCForeground, &gcv); + XFillRectangle (ws->dpy, d, gc, left, y, oright-left, h); + XFreeGC (ws->dpy, gc); + } + + /* Draw borders if necessary. + */ + switch (line->type) { + case LABEL: break; + case BUTTON: case TEXT: case TEXT_RO: + { + Bool in_p = (line->type != BUTTON); + if (line->button) + { + line->button->rect.x = left; + line->button->rect.y = y; + line->button->rect.width = right-left; + line->button->rect.height = h; + in_p = line->button->down_p || line->button->disabled_p; + } + tleft = left; + draw_shaded_rectangle (ws->dpy, d, + left, y, right-left, h, + ws->shadow_width, + (in_p ? ws->shadow_bottom : ws->shadow_top), + (in_p ? ws->shadow_top : ws->shadow_bottom)); + clip_w = ws->shadow_width; + } + break; + default: abort(); break; + } + + /* Draw the text inside our box. + */ + nlines = XftTextExtentsUtf8_multi (ws->dpy, line->font, (FcChar8 *) text, + strlen(text), &overall); + w = overall.width - overall.x; + switch (align) { + case LEFT: left = left + xpad; break; + case RIGHT: left = right - w - xpad; break; + case CENTER: + oleft = left; + left = left + xpad + (right - left - w) / 2; + if (left < oleft) left = oleft; + break; + default: abort(); break; + } + + rect.x = MAX (oleft, MAX (left, tleft + clip_w)); + rect.width = MIN (oright, right) - rect.x - clip_w; + rect.y = y + ypad - overall.y + line->font->ascent; + rect.height = overall.height; + + XftDrawSetClipRectangles (ws->xftdraw, 0, 0, &rect, 1); + + if (line->type == BUTTON && + line->button && + (line->button->down_p || line->button->disabled_p)) + xoff2 = yoff2 = MIN (ws->shadow_width, line->font->ascent/2); + + XftDrawStringUtf8_multi (ws->xftdraw, &line->fg, line->font, + left + xoff2, + y + ypad + yoff2 + line->font->ascent, + (FcChar8 *) text, strlen (text), + (align == LEFT ? 1 : align == CENTER ? 0 : -1)); +# ifdef DEBUG_METRICS + { + GC gc; + XGCValues gcv; + int yy = y + ypad + yoff2 + line->font->ascent; + gcv.foreground = line->fg.pixel; + gc = XCreateGC (ws->dpy, d, GCForeground, &gcv); + /* draw a line on the baseline of the text */ + XDrawLine (ws->dpy, d, gc, 0, yy, right, yy); + yy -= line->font->ascent; + /* a line above the ascenders */ + XDrawLine (ws->dpy, d, gc, left, yy, right, yy); + yy += line->font->ascent + line->font->descent; + /* and below the descenders */ + XDrawLine (ws->dpy, d, gc, left, yy, right, yy); + XFreeGC (ws->dpy, gc); + } +# endif + + if (line->i_beam) + draw_i_beam (ws->dpy, d, + ws->foreground, + left + xoff2 + overall.width, + y + ypad + yoff2, + line->font->ascent + line->font->descent); + + XftDrawSetClip (ws->xftdraw, 0); + + if (text2) free (text2); + + y += ypad*2 + (nlines * (line->font->ascent + line->font->descent)) + gutter; + return y; +} + + +static int +draw_dialog_lines (window_state *sp, Drawable d, dialog_line *lines, + int left, int right, int top) +{ + int i; + int maxy = 0; + for (i = 0; lines[i].text; i++) + { + Bool clear_p = (i > 0 && lines[i-1].float_p ? False : True); + int y = draw_dialog_line (sp, d, &lines[i], left, right, top, clear_p); + if (y > maxy) maxy = y; + if (! lines[i].float_p) + top = maxy; + } + return top; +} + + +static pid_t +fork_and_exec (Display *dpy, int argc, char **argv) +{ + char buf [255]; + pid_t forked = fork(); + switch ((int) forked) { + case -1: + sprintf (buf, "%s: couldn't fork", blurb()); + perror (buf); + break; + + case 0: + close (ConnectionNumber (dpy)); /* close display fd */ + execvp (argv[0], argv); /* shouldn't return. */ + + sprintf (buf, "%s: pid %lu: couldn't exec %s", blurb(), + (unsigned long) getpid(), argv[0]); + perror (buf); + exit (1); /* exits child fork */ + break; + + default: /* parent fork */ + if (verbose_p) + { + int i; + fprintf (stderr, "%s: pid %lu: launched", + blurb(), (unsigned long) forked); + for (i = 0; i < argc; i++) + fprintf (stderr, " %s", argv[i]); + fprintf (stderr, "\n"); + } + break; + } + + return forked; +} + + +/* Loading resources + */ +static void +resource_keys (window_state *ws, const char **name, const char **rclass) +{ + const char *theme = ws->dialog_theme; + const char *name2 = (ws->splash_p ? "splash" : "passwd"); + const char *class2 = "Dialog"; + static char res[200], rclass2[200]; + char *s; + + /* First try $THEME."Dialog.value" */ + sprintf (res, "%s.%s.%s", theme, name2, *name); + sprintf (rclass2, "%s.%s.%s", theme, class2, *rclass); + s = get_string_resource (ws->dpy, res, rclass2); + if (s && *s) goto DONE; + + /* Next try "default.Dialog.value" */ + if (s) free (s); + theme = "default"; + sprintf (res, "%s.%s.%s", theme, name2, *name); + sprintf (rclass2, "%s.%s.%s", theme, class2, *rclass); + s = get_string_resource (ws->dpy, res, rclass2); + if (s && *s) goto DONE; + + /* Next try "Dialog.value" */ + if (s) free (s); + sprintf (res, "%s.%s", theme, *name); + sprintf (rclass2, "%s.%s", theme, *rclass); + s = get_string_resource (ws->dpy, res, rclass2); + if (s && *s) goto DONE; + + DONE: + *name = res; + *rclass = rclass2; + if (s) free (s); +} + + +static char * +get_str (window_state *ws, const char *name, const char *rclass) +{ + resource_keys (ws, &name, &rclass); + return get_string_resource (ws->dpy, (char *) name, (char *) rclass); +} + + +static XftFont * +get_font (window_state *ws, const char *name) +{ + const char *rclass = "Font"; + XftFont *f; + char *s; + resource_keys (ws, &name, &rclass); + s = get_string_resource (ws->dpy, (char *) name, (char *) rclass); + if (!s || !*s) + s = "sans-serif 14"; + f = load_xft_font_retry (ws->dpy, DefaultScreen(ws->dpy), s); + if (!f) abort(); + return f; +} + +static unsigned long +get_color (window_state *ws, const char *name, const char *rclass) +{ + resource_keys (ws, &name, &rclass); + return get_pixel_resource (ws->dpy, DefaultColormapOfScreen (ws->screen), + (char *) name, (char *) rclass); +} + +static void +get_xft_color (window_state *ws, XftColor *ret, + const char *name, const char *rclass) +{ + char *s; + resource_keys (ws, &name, &rclass); + s = get_string_resource (ws->dpy, (char *) name, (char *) rclass); + if (!s || !*s) s = "black"; + XftColorAllocName (ws->dpy, + DefaultVisualOfScreen(ws->screen), + DefaultColormapOfScreen (ws->screen), + s, ret); +} + +static int +get_int (window_state *ws, const char *name, const char *rclass) +{ + resource_keys (ws, &name, &rclass); + return get_integer_resource (ws->dpy, (char *) name, (char *) rclass); +} + + +/* Decide where on the X11 screen to place the dialog. + This is complicated because, in the face of RANDR and Xinerama, we want + to center it on a *monitor*, not on what X calls a 'Screen'. So get the + monitor state, then figure out which one of those the mouse is in. + */ +static void +splash_pick_window_position (Display *dpy, Position *xP, Position *yP) +{ + Window pointer_root, pointer_child; + int root_x = 0, root_y = 0, win_x, win_y; + unsigned int mask; + monitor **monitors; + monitor *m = 0; + int i; + + XQueryPointer (dpy, RootWindow (dpy, 0), + &pointer_root, &pointer_child, + &root_x, &root_y, &win_x, &win_y, &mask); + + monitors = scan_monitors (dpy); + if (!monitors || !*monitors) abort(); + + for (i = 0; monitors[i]; i++) + { + monitor *m0 = monitors[i]; + if (m0->sanity == S_SANE && + root_x >= m0->x && + root_y >= m0->y && + root_x < m0->x + m0->width && + root_y < m0->y + m0->height) + { + m = m0; + break; + } + } + + if (!m) + { + if (verbose_p) + fprintf (stderr, "%s: mouse is not on any monitor?\n", blurb()); + m = monitors[0]; + } + else if (verbose_p) + fprintf (stderr, + "%s: mouse is at %d,%d on monitor %d %dx%d+%d+%d \"%s\"\n", + blurb(), root_x, root_y, m->id, + m->width, m->height, m->x, m->y, + (m->desc ? m->desc : "")); + + *xP = m->x + m->width/2; + *yP = m->y + m->height/2; + + free_monitors (monitors); +} + + +static void unlock_cb (window_state *ws); + + +/* This program only needs one option from the init file, so it + just reads the .ad file and the .xscreensaver file directly rather + than going through Xt and Xrm. + */ +static void init_line_handler (int lineno, + const char *key, const char *val, + void *closure) +{ + window_state *ws = (window_state *) closure; + if (val && *val && !strcmp (key, "dialogTheme")) + { + if (ws->dialog_theme) free (ws->dialog_theme); + ws->dialog_theme = strdup (val); + } +} + +static void +read_init_file_simple (window_state *ws) +{ + const char *home = getenv("HOME"); + const char *fn1 = AD_DIR "/XScreenSaver"; + char *fn2; + if (!home || !*home) return; + fn2 = (char *) malloc (strlen(home) + 40); + sprintf (fn2, "%s/.xscreensaver", home); + + if (debug_p) + fprintf (stderr, "%s: reading %s\n", blurb(), fn1); + parse_init_file (fn1, init_line_handler, ws); + + if (debug_p) + fprintf (stderr, "%s: reading %s\n", blurb(), fn2); + parse_init_file (fn2, init_line_handler, ws); + + if (verbose_p) + fprintf (stderr, "%s: theme: %s\n", blurb(), + (ws->dialog_theme ? ws->dialog_theme : "none")); +} + + +static void +grab_keyboard_and_mouse (window_state *ws) +{ + /* If we have been launched by xscreensaver, these grabs won't succeed, + and that is expected. But if we are being run manually for debugging, + they are necessary to avoid having events seen by two apps at once. + (We don't bother to ungrab, that happens when we exit.) + */ + Display *dpy = ws->dpy; + Window root = RootWindowOfScreen (ws->screen); + XGrabKeyboard (dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime); + XGrabPointer (dpy, root, True, + (ButtonPressMask | ButtonReleaseMask | + EnterWindowMask | LeaveWindowMask | + PointerMotionMask | PointerMotionHintMask | + Button1MotionMask | Button2MotionMask | + Button3MotionMask | Button4MotionMask | + Button5MotionMask | ButtonMotionMask), + GrabModeAsync, GrabModeAsync, root, + None, CurrentTime); +} + + +static void +get_keyboard_layout (window_state *ws) +{ +# ifdef HAVE_XKB + XkbStateRec state; + XkbDescPtr desc = 0; + Atom name = 0; + char *namestr = 0; + + if (! ws->xkb_opcode) + { + if (! XkbQueryExtension (ws->dpy, 0, &ws->xkb_opcode, 0, 0, 0)) + { + ws->xkb_opcode = -1; /* Only try once */ + if (verbose_p) + fprintf (stderr, "%s: XkbQueryExtension failed\n", blurb()); + return; + } + + if (! XkbSelectEvents (ws->dpy, XkbUseCoreKbd, + XkbMapNotifyMask | XkbStateNotifyMask, + XkbMapNotifyMask | XkbStateNotifyMask)) + { + if (verbose_p) + fprintf (stderr, "%s: XkbSelectEvents failed\n", blurb()); + } + } + + if (XkbGetState (ws->dpy, XkbUseCoreKbd, &state)) + { + if (verbose_p) + fprintf (stderr, "%s: XkbGetState failed\n", blurb()); + return; + } + desc = XkbGetKeyboard (ws->dpy, XkbAllComponentsMask, XkbUseCoreKbd); + if (!desc || !desc->names) + { + if (verbose_p) + fprintf (stderr, "%s: XkbGetKeyboard failed\n", blurb()); + goto DONE; + } + name = desc->names->groups[state.group]; + namestr = (name ? XGetAtomName (ws->dpy, name) : 0); + if (!namestr) + { + if (verbose_p) + fprintf (stderr, "%s: XkbGetKeyboard returned null layout\n", blurb()); + goto DONE; + } + + if (ws->kbd_layout_label) + free (ws->kbd_layout_label); + ws->kbd_layout_label = namestr; + + if (verbose_p) + fprintf (stderr, "%s: kbd layout: %s\n", blurb(), + namestr ? namestr : "null"); + + DONE: + if (desc) XFree (desc); +# endif /* HAVE_XKB */ +} + + +static double +double_time (void) +{ + struct timeval now; +# ifdef GETTIMEOFDAY_TWO_ARGS + struct timezone tzp; + gettimeofday(&now, &tzp); +# else + gettimeofday(&now); +# endif + + return (now.tv_sec + ((double) now.tv_usec * 0.000001)); +} + + +static void +create_window (window_state *ws, int w, int h) +{ + XSetWindowAttributes attrs; + unsigned long attrmask; + Window ow = ws->window; + + attrmask = CWOverrideRedirect | CWEventMask; + attrs.override_redirect = True; + attrs.event_mask = ExposureMask | VisibilityChangeMask; + ws->window = XCreateWindow (ws->dpy, + RootWindowOfScreen(ws->screen), + ws->x, ws->y, w, h, 0, + DefaultDepthOfScreen (ws->screen), + InputOutput, + DefaultVisualOfScreen(ws->screen), + attrmask, &attrs); + XSetWindowBackground (ws->dpy, ws->window, ws->background); + XSetWindowColormap (ws->dpy, ws->window, ws->cmap); + xscreensaver_set_wm_atoms (ws->dpy, ws->window, w, h, 0); + + if (ow) + { + XMapRaised (ws->dpy, ws->window); + XDestroyWindow (ws->dpy, ow); + } +} + + +/* Loads resources and creates and returns the global window state. + */ +static window_state * +window_init (Widget root_widget, Bool splash_p) +{ + Display *dpy = XtDisplay (root_widget); + Screen *screen = XtScreen (root_widget); + window_state *ws; + Bool resource_error_p = False; + + ws = (window_state *) calloc (1, sizeof(*ws)); + if (!ws) abort(); + + ws->splash_p = splash_p; + ws->dpy = dpy; + ws->screen = screen; + ws->app = XtWidgetToApplicationContext (root_widget); + + /* Read default theme from resources before the init file. */ + ws->dialog_theme = + get_string_resource (ws->dpy, "dialogTheme", "DialogTheme"); + if (!ws->dialog_theme || !*ws->dialog_theme) + ws->dialog_theme = strdup ("default"); + + /* Read theme from init file before any other resources. */ + read_init_file_simple (ws); + + { + struct passwd *p = getpwuid (getuid()); + if (!p || !p->pw_name || !*p->pw_name) abort(); + ws->user = p->pw_name; + } + + ws->cmap = XCreateColormap (dpy, RootWindowOfScreen (screen), /* Old skool */ + DefaultVisualOfScreen (screen), + AllocNone); + + ws->newlogin_cmd = get_str (ws, "newLoginCommand", "NewLoginCommand"); + ws->date_format = get_str (ws, "dateFormat", "DateFormat"); + ws->show_stars_p = + get_boolean_resource (ws->dpy, "passwd.asterisks", "Passwd.Boolean"); + + /* Put the version number in the label. */ + { + char *version = strdup (screensaver_id + 17); + char *year = strchr (version, '-'); + char *s = strchr (version, ' '); + *s = 0; + year = strchr (year+1, '-') + 1; + s = strchr (year, ')'); + *s = 0; + ws->heading_label = (char *) malloc (100); + ws->version = strdup(version); + sprintf (ws->heading_label, "XScreenSaver %.4s, v%.10s", year, version); + + if (splash_p) + { + ws->body_label = (char *) malloc (100); + sprintf (ws->body_label, + _("Copyright \xC2\xA9 1991-%.4s by\nJamie Zawinski "), + year); + } + } + + ws->heading_font = get_font (ws, "headingFont"); + ws->button_font = get_font (ws, "buttonFont"); + ws->body_font = get_font (ws, "bodyFont"); + ws->error_font = get_font (ws, "errorFont"); + ws->label_font = get_font (ws, "labelFont"); + ws->date_font = get_font (ws, "dateFont"); + ws->hostname_font = get_font (ws, "unameFont"); + + ws->foreground = get_color (ws, "foreground", "Foreground"); + ws->background = get_color (ws, "background", "Background"); + + get_xft_color (ws, &ws->xft_foreground, "foreground", "Foreground"); + get_xft_color (ws, &ws->xft_text_foreground, + "text.foreground", "Text.Foreground"); + get_xft_color (ws, &ws->xft_error_foreground, + "error.foreground", "Error.Foreground"); + get_xft_color (ws, &ws->xft_button_foreground, + "button.foreground", "Button.Foreground"); + + ws->shadow_top = get_color (ws, "topShadowColor", "Foreground"); + ws->shadow_bottom = get_color (ws, "bottomShadowColor", "Background"); + ws->border_color = get_color (ws, "borderColor", "BorderColor"); + ws->passwd_background = get_color (ws, "text.background", "Text.Background"); + ws->button_background = + get_color (ws, "button.background", "Button.Background"); + ws->thermo_foreground = + get_color (ws, "thermometer.foreground", "Thermometer.Foreground"); + ws->thermo_background = + get_color ( ws, "thermometer.background", "Thermometer.Background"); + ws->logo_background = get_color ( ws, "logo.background", "Logo.Background"); + + if (resource_error_p) + { + /* Make sure the error messages show up. */ + ws->foreground = BlackPixelOfScreen (screen); + ws->background = WhitePixelOfScreen (screen); + } + + ws->preferred_logo_width = get_int (ws, "logo.width", "Logo.Width"); + ws->preferred_logo_height = get_int (ws, "logo.height", "Logo.Height"); + ws->thermo_width = get_int (ws, "thermometer.width", "Thermometer.Width"); + ws->shadow_width = get_int (ws, "shadowWidth", "ShadowWidth"); + ws->border_width = get_int (ws, "borderWidth", "BorderWidth"); + ws->internal_padding = + get_int (ws, "internalPadding", "InternalPadding"); + + if (ws->preferred_logo_width == 0) ws->preferred_logo_width = 150; + if (ws->preferred_logo_height == 0) ws->preferred_logo_height = 150; + if (ws->internal_padding == 0) ws->internal_padding = 15; + if (ws->thermo_width == 0) ws->thermo_width = ws->shadow_width; + + if (ws->splash_p) ws->thermo_width = 0; + +# ifdef HAVE_UNAME + if (!splash_p && + get_boolean_resource (ws->dpy, "passwd.uname", "Passwd.Boolean")) + { + struct utsname uts; + if (!uname (&uts) && *uts.nodename) + ws->hostname_label = strdup (uts.nodename); + } +# endif + + get_keyboard_layout (ws); + + /* Load the logo pixmap, based on font size */ + { + int x, y; + unsigned int bw, d; + Window root = RootWindowOfScreen(ws->screen); + Visual *visual = DefaultVisualOfScreen (ws->screen); + int logo_size = (ws->heading_font->ascent > 24 ? 2 : 1); + ws->logo_pixmap = xscreensaver_logo (screen, visual, root, ws->cmap, + ws->background, + &ws->logo_pixels, &ws->logo_npixels, + &ws->logo_clipmask, logo_size); + if (!ws->logo_pixmap) abort(); + XGetGeometry (dpy, ws->logo_pixmap, &root, &x, &y, + &ws->logo_width, &ws->logo_height, &bw, &d); + } + + splash_pick_window_position (ws->dpy, &ws->cx, &ws->cy); + + ws->x = ws->y = 0; + create_window (ws, 1, 1); + + /* Select SubstructureNotifyMask on the root window so that we know + when another process has mapped a window, so that we can make our + window always be on top. */ + { + Window root = RootWindowOfScreen (ws->screen); + XWindowAttributes xgwa; + XGetWindowAttributes (ws->dpy, root, &xgwa); + XSelectInput (ws->dpy, root, + xgwa.your_event_mask | SubstructureNotifyMask); + } + + + ws->newlogin_button_state.cmd = ws->newlogin_cmd; + ws->demo_button_state.cmd = + get_string_resource (ws->dpy, "demoCommand", "Command"); + { + char *load = get_string_resource (ws->dpy, "loadURL", "Command"); + char *url = get_string_resource (ws->dpy, "helpURL", "URL"); + if (load && *load && url && *url) + { + char *cmd = (char *) malloc (strlen(load) + (strlen(url) * 5) + 10); + sprintf (cmd, load, url, url, url, url, url); + ws->help_button_state.cmd = cmd; + } + } + + ws->unlock_button_state.fn = unlock_cb; + + grab_keyboard_and_mouse (ws); + + return ws; +} + + +#ifdef DEBUG_STACKING +static void +describe_window (Display *dpy, Window w) +{ + XClassHint ch; + char *name = 0; + if (XGetClassHint (dpy, w, &ch)) + { + fprintf (stderr, "0x%lx \"%s\", \"%s\"\n", (unsigned long) w, + ch.res_class, ch.res_name); + XFree (ch.res_class); + XFree (ch.res_name); + } + else if (XFetchName (dpy, w, &name) && name) + { + fprintf (stderr, "0x%lx \"%s\"\n", (unsigned long) w, name); + XFree (name); + } + else + { + fprintf (stderr, "0x%lx (untitled)\n", (unsigned long) w); + } +} +#endif /* DEBUG_STACKING */ + + +/* Returns true if some other window is on top of this one. + */ +static Bool +window_occluded_p (Display *dpy, Window window) +{ + int screen; + +# ifdef DEBUG_STACKING + fprintf (stderr, "\n"); +# endif + + for (screen = 0; screen < ScreenCount (dpy); screen++) + { + int i; + Window root = RootWindow (dpy, screen); + Window root2 = 0, parent = 0, *kids = 0; + unsigned int nkids = 0; + Bool saw_our_window_p = False; + Bool saw_later_window_p = False; + if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) + { +# ifdef DEBUG_STACKING + fprintf (stderr, "%s: XQueryTree failed\n", blurb()); +# endif + continue; + } + + for (i = 0; i < nkids; i++) + { + if (kids[i] == window) + { + saw_our_window_p = True; +# ifdef DEBUG_STACKING + fprintf (stderr, "%s: our window: ", blurb()); + describe_window (dpy, kids[i]); +# endif + } + else if (saw_our_window_p) + { + saw_later_window_p = True; +# ifdef DEBUG_STACKING + fprintf (stderr, "%s: higher window: ", blurb()); + describe_window (dpy, kids[i]); +# endif + break; + } + else + { +# ifdef DEBUG_STACKING + fprintf (stderr, "%s: lower window: ", blurb()); + describe_window (dpy, kids[i]); +# endif + } + } + + if (kids) + XFree ((char *) kids); + + if (saw_later_window_p) + return True; + else if (saw_our_window_p) + return False; + /* else our window is not on this screen; keep going, try the next. */ + } + + /* Window doesn't exist? */ +# ifdef DEBUG_STACKING + fprintf (stderr, "%s: our window isn't on the screen\n", blurb()); +# endif + return False; +} + + +/* Strip leading and trailing whitespace. */ +static char * +trim (const char *s) +{ + char *s2; + int L; + if (!s) return 0; + while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') + s++; + s2 = strdup (s); + L = strlen (s2); + while (L > 0 && + (s2[L-1] == ' ' || s2[L-1] == '\t' || + s2[L-1] == '\r' || s2[L-1] == '\n')) + s2[--L] = 0; + return s2; +} + + +/* Repaint the entire window. + */ +static void +window_draw (window_state *ws) +{ + Display *dpy = ws->dpy; + Screen *screen = DefaultScreenOfDisplay (dpy); + Window root = RootWindowOfScreen (screen); + Visual *visual = DefaultVisualOfScreen(screen); + int depth = DefaultDepthOfScreen (screen); + XWindowAttributes xgwa; + +# define MIN_COLUMNS 22 /* Set window width based on headingFont ascent. */ + + int ext_border = (ws->internal_padding / 2 + + ws->shadow_width + ws->border_width); + + Pixmap dbuf; + unsigned int logo_frame_width, logo_frame_height; + unsigned int window_width, window_height; + unsigned int text_left, text_right; + unsigned int thermo_x; + unsigned int x, y; + GC gc; + XGCValues gcv; + char date_text[100]; + time_t now = time ((time_t *) 0); + struct tm *tm = localtime (&now); + double ratio = 1 - ((double_time() - ws->start_time) / + (ws->end_time - ws->start_time)); + dialog_line *lines = + (dialog_line *) calloc (ws->nmsgs + 40, sizeof(*lines)); + Bool emitted_user_p = False; + int i = 0, j; + + XGetWindowAttributes (ws->dpy, ws->window, &xgwa); + + if (!lines) abort(); + + strftime (date_text, sizeof(date_text)-2, ws->date_format, tm); + + logo_frame_width = (ws->logo_width + ws->internal_padding * 2 + + ws->shadow_width * 2); + logo_frame_height = logo_frame_width; + if (logo_frame_width < ws->preferred_logo_width) + logo_frame_width = ws->preferred_logo_width; + if (logo_frame_height < ws->preferred_logo_height) + logo_frame_height = ws->preferred_logo_height; + + thermo_x = ext_border * 1.5 + logo_frame_width + ws->shadow_width; + text_left = (thermo_x + ws->internal_padding + + (ws->thermo_width + ? ws->thermo_width + ws->shadow_width * 3 + : 0)); + text_right = text_left + ws->heading_font->ascent * MIN_COLUMNS; + window_width = text_right + ws->internal_padding + ext_border; + window_height = window_width * 3; /* reduced later */ + + dbuf = XCreatePixmap (dpy, root, window_width, window_height, depth); + gc = XCreateGC (dpy, dbuf, 0, &gcv); + XSetForeground (dpy, gc, ws->background); + XFillRectangle (dpy, dbuf, gc, 0, 0, window_width, window_height); + + if (ws->xftdraw) + XftDrawDestroy (ws->xftdraw); + ws->xftdraw = XftDrawCreate (dpy, dbuf, visual, xgwa.colormap); + + lines[i].text = ws->heading_label; /* XScreenSaver */ + lines[i].font = ws->heading_font; + lines[i].fg = ws->xft_foreground; + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = CENTER; + i++; + + if (time ((time_t *) 0) - XSCREENSAVER_RELEASED > 60*60*24*30*17) + { + lines[i].text = _("Update available!\nThis version is very old.\n"); + lines[i].font = ws->error_font; + lines[i].fg = ws->xft_error_foreground; + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = CENTER; + i++; + } + else if (strstr (ws->version, "a") || + strstr (ws->version, "b")) + { + lines[i].text = _("PRE-RELEASE VERSION"); + lines[i].font = ws->error_font; + lines[i].fg = ws->xft_error_foreground; + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = CENTER; + i++; + } + + if (ws->hostname_label && *ws->hostname_label) + { + lines[i].text = ws->hostname_label; + lines[i].font = ws->hostname_font; + lines[i].fg = ws->xft_foreground; + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = CENTER; + i++; + } + +# define BLANK_LINE \ + lines[i].text = ""; \ + lines[i].font = ws->body_font; \ + lines[i].fg = ws->xft_foreground; \ + lines[i].bg = ws->background; \ + lines[i].type = LABEL; \ + lines[i].align = CENTER; \ + i++ + + BLANK_LINE; + + if (debug_p && !ws->splash_p) + { + lines[i].text = + _("DEBUG MODE:\nAll keystrokes are being logged to stderr.\n"); + lines[i].font = ws->error_font; + lines[i].fg = ws->xft_error_foreground; + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = CENTER; + i++; + } + + if (ws->body_label && *ws->body_label) + { + lines[i].text = ws->body_label; /* Copyright or error message */ + lines[i].font = ws->body_font; + lines[i].fg = ws->xft_foreground; + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = CENTER; + i++; + + BLANK_LINE; + } + + for (j = 0; j < ws->nmsgs; j++) /* PAM msgs */ + { + switch (ws->msgs[j].type) { + case AUTH_MSGTYPE_INFO: + case AUTH_MSGTYPE_ERROR: + lines[i].text = trim (ws->msgs[j].msg); + lines[i].font = (ws->msgs[j].type == AUTH_MSGTYPE_ERROR + ? ws->error_font + : ws->body_font); + lines[i].fg = (ws->msgs[j].type == AUTH_MSGTYPE_ERROR + ? ws->xft_error_foreground + : ws->xft_foreground); + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = CENTER; + i++; + break; + + case AUTH_MSGTYPE_PROMPT_NOECHO: + case AUTH_MSGTYPE_PROMPT_ECHO: + + /* Show the logged in user before the first password field. */ + if (!emitted_user_p) + { + lines[i].text = _("Username:"); + lines[i].font = ws->label_font; + lines[i].fg = ws->xft_foreground; + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = LEFT; + lines[i].float_p = True; + i++; + + lines[i].text = ws->user; /* $USER */ + lines[i].font = ws->label_font; + lines[i].fg = ws->xft_text_foreground; + lines[i].bg = ws->passwd_background; + lines[i].type = TEXT_RO; + lines[i].align = RIGHT; + i++; + } + + lines[i].text = trim (ws->msgs[j].msg); /* PAM prompt text */ + lines[i].font = ws->label_font; + lines[i].fg = ws->xft_foreground; + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = LEFT; + lines[i].float_p = True; + i++; + + lines[i].text = (ws->auth_state == AUTH_FINISHED + ? _("Checking...") : + ws->msgs[j].type == AUTH_MSGTYPE_PROMPT_ECHO + ? ws->plaintext_passwd /* Hopefully UTF-8 */ + : ws->show_stars_p + ? ws->censored_passwd + : ""); + lines[i].font = ws->label_font; + lines[i].fg = ws->xft_text_foreground; + lines[i].bg = ws->passwd_background; + lines[i].type = TEXT; + lines[i].align = RIGHT; + lines[i].i_beam = (ws->i_beam && ws->auth_state != AUTH_FINISHED); + i++; + + /* Show the current time below the first password field only. */ + if (*date_text && !emitted_user_p) + { + lines[i].text = date_text; + lines[i].font = ws->date_font; + lines[i].fg = ws->xft_foreground; + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = RIGHT; + i++; + } + + /* Show the current keyboard layout below that. */ + if (ws->kbd_layout_label && *ws->kbd_layout_label && !emitted_user_p) + { + lines[i].text = ws->kbd_layout_label; + lines[i].font = ws->date_font; + lines[i].fg = ws->xft_foreground; + lines[i].bg = ws->background; + lines[i].type = LABEL; + lines[i].align = RIGHT; + i++; + } + + emitted_user_p = True; + break; + + default: + abort(); + break; + } + } + + lines[i].text = 0; + y = draw_dialog_lines (ws, dbuf, lines, + text_left, text_right, + ws->border_width + ws->internal_padding + + ws->shadow_width); + window_height = y; + window_height += (ws->button_font->ascent * 4); + window_height += (ws->internal_padding + ws->shadow_width * 2 + + ws->border_width); + + + /* Keep logo area square or taller */ + if (window_height < logo_frame_height + ws->shadow_width * 4) + window_height = logo_frame_height + ws->shadow_width * 4; + + /* Fitt's Law: It is distracting to reduce the height of the window + after creation. */ + if (window_height < ws->min_height) + window_height = ws->min_height; + ws->min_height = window_height; + + + + /* Now do a second set of lines for the buttons at the bottom. */ + + memset (lines, 0, sizeof(*lines)); + i = 0; + + if (ws->splash_p) + { + lines[i].text = _("Settings"); + lines[i].font = ws->button_font; + lines[i].fg = ws->xft_button_foreground; + lines[i].bg = ws->button_background; + lines[i].type = BUTTON; + lines[i].align = LEFT; + lines[i].float_p = True; + lines[i].button = &ws->demo_button_state; + i++; + + lines[i].text = _("Help"); + lines[i].font = ws->button_font; + lines[i].fg = ws->xft_button_foreground; + lines[i].bg = ws->button_background; + lines[i].type = BUTTON; + lines[i].align = RIGHT; + lines[i].button = &ws->help_button_state; + i++; + } + else + { + if (ws->newlogin_cmd && *ws->newlogin_cmd) + { + lines[i].text = _("New Login"); + lines[i].font = ws->button_font; + lines[i].fg = ws->xft_button_foreground; + lines[i].bg = ws->button_background; + lines[i].type = BUTTON; + lines[i].align = LEFT; + lines[i].float_p = True; + lines[i].button = &ws->newlogin_button_state; + i++; + } + + lines[i].text = _("OK"); + lines[i].font = ws->button_font; + lines[i].fg = ws->xft_button_foreground; + lines[i].bg = ws->button_background; + lines[i].type = BUTTON; + lines[i].align = RIGHT; + lines[i].button = &ws->unlock_button_state; + i++; + } + + lines[i].text = 0; + y = draw_dialog_lines (ws, dbuf, lines, + text_left, text_right, + window_height - ws->internal_padding - + ext_border - + ws->shadow_width - + (ws->button_font->ascent * 2)); + + /* The thermometer */ + if (ws->thermo_width) + { + if (ws->auth_state != AUTH_NOTIFY) + { + int thermo_w = ws->thermo_width; + int thermo_h = window_height - ext_border * 2; + int thermo_h2 = thermo_h - ws->shadow_width * 2; + int thermo_h3 = thermo_h2 * (1.0 - ratio); + + XSetForeground (dpy, gc, ws->thermo_foreground); + XFillRectangle (dpy, dbuf, gc, + thermo_x + ws->shadow_width, + ext_border + ws->shadow_width, + thermo_w, thermo_h2); + if (thermo_h3 > 0) + { + XSetForeground (dpy, gc, ws->thermo_background); + XFillRectangle (dpy, dbuf, gc, + thermo_x + ws->shadow_width, + ext_border + ws->shadow_width, + thermo_w, thermo_h3); + } + } + + draw_shaded_rectangle (dpy, dbuf, + thermo_x, ext_border, + ws->thermo_width + ws->shadow_width * 2, + window_height - ext_border * 2, + ws->shadow_width, + ws->shadow_bottom, ws->shadow_top); + } + + /* The logo, centered vertically. + */ + { + int bot = window_height - ext_border * 2; + int xoff = (logo_frame_width - ws->logo_width) / 2; + int yoff = (bot - ws->logo_height) / 2; + x = ext_border; + y = ext_border; + XSetForeground (dpy, gc, ws->logo_background); + XFillRectangle (dpy, dbuf, gc, x, y, logo_frame_width, bot); + XSetForeground (dpy, gc, ws->foreground); + XSetBackground (dpy, gc, ws->background); + XSetClipMask (dpy, gc, ws->logo_clipmask); + XSetClipOrigin (dpy, gc, x + xoff, y + yoff); + XCopyArea (dpy, ws->logo_pixmap, dbuf, gc, 0, 0, + ws->logo_width, ws->logo_height, + x + xoff, y + yoff); + XSetClipMask (dpy, gc, 0); + draw_shaded_rectangle (dpy, dbuf, + x, y, + logo_frame_width, + bot, + ws->shadow_width, + ws->shadow_bottom, ws->shadow_top); + } + + /* The window's shadow */ + draw_shaded_rectangle (dpy, dbuf, + ws->border_width, ws->border_width, + window_width - ws->border_width * 2, + window_height - ws->border_width * 2, + ws->shadow_width, + ws->shadow_top, ws->shadow_bottom); + + /* The window's border */ + draw_shaded_rectangle (dpy, dbuf, + 0, 0, window_width, window_height, + ws->border_width, + ws->border_color, ws->border_color); + + + /* Now that everything has been rendered into the pixmap, reshape the window + and copy the pixmap to it. This double-buffering is to prevent flicker. + + You'd think we could just reshape the window and then XMapRaised, but no. + With the XCompose extension enabled and the "Mutter" window manager, the + dialog window was sometimes not appearing on the screen, despite the fact + that XQueryTree reported it as the topmost window. The mouse pointer also + reflected it being there, but it wasn't visible. This is probably related + to the "XCompositeGetOverlayWindow" window in some way, which is a magic, + invisible window that is secretly excluded from the list returned by + XQueryTree, but I can't figure out what was really going on, except that + XMapRaised did not make my OverrideRedirect window appear topmost on the + screen. + + However! Though XMapRaised was not working, it turns out that destroying + and re-creating the window *does* make it appear. So we do that, any time + the window's shape has changed, or some other window has raised above it. + + Calling XQueryTree at 30fps could conceivably be a performance problem, + if there are thousands of windows on the screen. But here we are. + */ + { + Bool size_changed_p, occluded_p; + + /* It's distracting to move or shrink the window after creating it. */ + if (xgwa.height > 100 && xgwa.height > window_height) + window_height = xgwa.height; + if (! ws->x) + { + ws->x = ws->cx - (window_width / 2); + ws->y = ws->cy - (window_height / 2); + } + + /* If there is any change to the window's size, or if the window is + not on top, destroy and re-create the window. */ + size_changed_p = !(xgwa.x == ws->x && + xgwa.y == ws->y && + xgwa.width == window_width && + xgwa.height == window_height); + occluded_p = (!size_changed_p && + window_occluded_p (ws->dpy, ws->window)); + + if (size_changed_p || occluded_p) + { +# if 0 /* Window sometimes disappears under Mutter 3.30.2, Feb 2021. */ + XWindowChanges wc; + wc.x = ws->x; + wc.y = ws->y; + wc.width = window_width; + wc.height = window_height; + if (verbose_p) + fprintf (stderr, "%s: reshaping window %dx%d+%d+%d\n", blurb(), + wc.width, wc.height, wc.x, wc.y); + XConfigureWindow (ws->dpy, ws->window, CWX|CWY|CWWidth|CWHeight, &wc); +# else + if (verbose_p) + fprintf (stderr, "%s: re-creating window: %s\n", blurb(), + size_changed_p ? "size changed" : "occluded"); + create_window (ws, window_width, window_height); +# endif + XMapRaised (ws->dpy, ws->window); + XInstallColormap (ws->dpy, ws->cmap); + } + } + + XFreeGC (dpy, gc); + gc = XCreateGC (dpy, ws->window, 0, &gcv); + XCopyArea (dpy, dbuf, ws->window, gc, 0, 0, + window_width, window_height, 0, 0); + XSync (dpy, False); + XFreeGC (dpy, gc); + XFreePixmap (dpy, dbuf); + free (lines); + + if (verbose_p > 1) + { + static time_t last = 0; + static int count = 0; + count++; + if (now > last) + { + double fps = count / (double) (now - last); + fprintf (stderr, "%s: FPS: %0.1f\n", blurb(), fps); + count = 0; + last = now; + } + } +} + + +/* Unmaps the window and frees window_state. + */ +static void +destroy_window (window_state *ws) +{ + XEvent event; + + memset (ws->plaintext_passwd, 0, sizeof(ws->plaintext_passwd)); + memset (ws->plaintext_passwd_char_size, 0, + sizeof(ws->plaintext_passwd_char_size)); + memset (ws->censored_passwd, 0, sizeof(ws->censored_passwd)); + + if (ws->timer) + { + XtRemoveTimeOut (ws->timer); + ws->timer = 0; + } + + if (ws->cursor_timer) + { + XtRemoveTimeOut (ws->cursor_timer); + ws->cursor_timer = 0; + } + + while (XCheckMaskEvent (ws->dpy, PointerMotionMask, &event)) + if (verbose_p) + fprintf (stderr, "%s: discarding MotionNotify event\n", blurb()); + + if (ws->window) + { + XDestroyWindow (ws->dpy, ws->window); + ws->window = 0; + } + + if (ws->heading_label) free (ws->heading_label); + if (ws->date_format) free (ws->date_format); + if (ws->hostname_label) free (ws->hostname_label); + if (ws->kbd_layout_label) free (ws->kbd_layout_label); + + if (ws->heading_font) XftFontClose (ws->dpy, ws->heading_font); + if (ws->body_font) XftFontClose (ws->dpy, ws->body_font); + if (ws->label_font) XftFontClose (ws->dpy, ws->label_font); + if (ws->date_font) XftFontClose (ws->dpy, ws->date_font); + if (ws->button_font) XftFontClose (ws->dpy, ws->button_font); + if (ws->hostname_font) XftFontClose (ws->dpy, ws->hostname_font); + + XftColorFree (ws->dpy, DefaultVisualOfScreen (ws->screen), + DefaultColormapOfScreen (ws->screen), + &ws->xft_foreground); + XftColorFree (ws->dpy, DefaultVisualOfScreen (ws->screen), + DefaultColormapOfScreen (ws->screen), + &ws->xft_button_foreground); + XftColorFree (ws->dpy, DefaultVisualOfScreen (ws->screen), + DefaultColormapOfScreen (ws->screen), + &ws->xft_text_foreground); + XftColorFree (ws->dpy, DefaultVisualOfScreen (ws->screen), + DefaultColormapOfScreen (ws->screen), + &ws->xft_error_foreground); + XftDrawDestroy (ws->xftdraw); + +# if 0 /* screw this, we're exiting anyway */ + if (ws->foreground != black && ws->foreground != white) + XFreeColors (ws->dpy, ws->cmap, &ws->foreground, 1, 0L); + if (ws->background != black && ws->background != white) + XFreeColors (ws->dpy, ws->cmap, &ws->background, 1, 0L); + if (ws->button_background != black && ws->button_background != white) + XFreeColors (ws->dpy, ws->cmap, &ws->button_background, 1, 0L); + if (ws->passwd_background != black && ws->passwd_background != white) + XFreeColors (ws->dpy, ws->cmap, &ws->passwd_background, 1, 0L); + if (ws->thermo_foreground != black && ws->thermo_foreground != white) + XFreeColors (ws->dpy, ws->cmap, &ws->thermo_foreground, 1, 0L); + if (ws->thermo_background != black && ws->thermo_background != white) + XFreeColors (ws->dpy, ws->cmap, &ws->thermo_background, 1, 0L); + if (ws->logo_background != black && ws->logo_background != white) + XFreeColors (ws->dpy, ws->cmap, &ws->logo_background, 1, 0L); + if (ws->shadow_top != black && ws->shadow_top != white) + XFreeColors (ws->dpy, ws->cmap, &ws->shadow_top, 1, 0L); + if (ws->shadow_bottom != black && ws->shadow_bottom != white) + XFreeColors (ws->dpy, ws->cmap, &ws->shadow_bottom, 1, 0L); +# endif + + if (ws->logo_pixmap) + XFreePixmap (ws->dpy, ws->logo_pixmap); + if (ws-> logo_clipmask) + XFreePixmap (ws->dpy, ws->logo_clipmask); + if (ws->logo_pixels) + { + if (ws->logo_npixels) + XFreeColors (ws->dpy, ws->cmap, ws->logo_pixels, ws->logo_npixels, 0L); + free (ws->logo_pixels); + ws->logo_pixels = 0; + ws->logo_npixels = 0; + } + + XSync (ws->dpy, False); + memset (ws, 0, sizeof(*ws)); + free (ws); + +} + + +static void +unlock_cb (window_state *ws) +{ + if (ws->auth_state == AUTH_READ) + ws->auth_state = AUTH_FINISHED; +} + + +/* We store the count and last time of authorization failures on a property + on the root window, so that on subsequent runs of this program that + succeed, we can warn the user that someone tried to log in and failed. + */ +static void +persistent_auth_status_failure (window_state *ws, + Bool increment_p, Bool clear_p, + int *count_ret, + time_t *time_ret) +{ + Display *dpy = ws->dpy; + Window w = RootWindow (dpy, 0); /* always screen 0 */ + Atom prop = XInternAtom (ws->dpy, LOCK_FAILURE_ATOM, False); + int count = 0; + time_t tt = 0; + + Atom type; + unsigned char *dataP = 0; + int format; + unsigned long nitems, bytesafter; + + if (increment_p && clear_p) abort(); + + /* Read the old property so that we can increment it. */ + if (XGetWindowProperty (dpy, w, prop, + 0, 999, False, XA_INTEGER, + &type, &format, &nitems, &bytesafter, + &dataP) + == Success + && type == XA_INTEGER + && nitems >= 2 + && dataP) + { + count = ((PROP32 *) dataP) [0]; + tt = ((PROP32 *) dataP) [1]; /* Y2038 bug: unsigned 32 bit time_t */ + if (verbose_p) + fprintf (stderr, "%s: previous auth failures: %d @ %lu\n", + blurb(), count, (unsigned long) tt); + } + + if (dataP) + XFree (dataP); + + if (clear_p) + { + XDeleteProperty (dpy, w, prop); + if (verbose_p) + fprintf (stderr, "%s: deleted auth failure property\n", blurb()); + } + else if (increment_p) + { + PROP32 vv[2]; + count++; + + /* Remember the time of the *oldest* failed login. A failed login + 5 seconds ago does not mean we should skip warning about a failed + login yesterday. + */ + if (tt <= 0) tt = time ((time_t *) 0); + + vv[0] = (PROP32) count; + vv[1] = (PROP32) tt; + XChangeProperty (dpy, w, prop, XA_INTEGER, 32, + PropModeReplace, (unsigned char *) vv, 2); + if (verbose_p) + fprintf (stderr, "%s: saved auth failure: %d @ %lu\n", + blurb(), count, (unsigned long) tt); + } + + if (count_ret) *count_ret = count; + if (time_ret) *time_ret = tt; +} + + + +static void +handle_keypress (window_state *ws, XKeyEvent *event) +{ + unsigned char decoded [MAX_BYTES_PER_CHAR * 10]; /* leave some slack */ + KeySym keysym = 0; + + /* XLookupString may return more than one character via XRebindKeysym; + and on some systems it returns multi-byte UTF-8 characters (contrary + to its documentation, which says it returns only Latin1.) + + It seems to only do so, however, if setlocale() has been called. + See the code inside ENABLE_NLS in xscreensaver-auth.c. + + The X Keyboard Extension X11R6.4 documentation says: "When Xkb is + present, XLookupString is allowed, but not required, to return strings + in character sets other than ISO Latin-1, depending on the current + locale." So I guess that means that multi-byte strings returned by + XLookupString might not be UTF-8, and thus might not be compatible + with XftDrawStringUtf8. + */ + int decoded_size = XLookupString (event, (char *)decoded, sizeof(decoded), + &keysym, &ws->compose_status); + + if (decoded_size > MAX_BYTES_PER_CHAR) + { + /* The multi-byte character returned is too large. */ + XBell (ws->dpy, 0); + return; + } + + decoded[decoded_size] = 0; + + /* Add 10% to the time remaining every time a key is pressed, but don't + go past the max duration. */ + { + time_t now = time ((time_t *) 0); + int max = get_seconds_resource (ws->dpy, "passwdTimeout", "Time"); + int remain = ws->end_time - now; + remain *= 1.1; + if (remain > max) remain = max; + if (remain < 3) remain = 3; + ws->end_time = now + remain; + } + + + if (decoded_size == 1) /* Handle single-char commands */ + { + switch (*decoded) + { + case '\010': case '\177': /* Backspace */ + { + /* kludgey way to get the number of "logical" characters. */ + int nchars = strlen (ws->plaintext_passwd_char_size); + int nbytes = strlen (ws->plaintext_passwd); + if (nbytes <= 0) + XBell (ws->dpy, 0); + else + { + int i; + for (i = ws->plaintext_passwd_char_size[nchars-1]; i >= 0; i--) + { + if (nbytes < 0) abort(); + ws->plaintext_passwd[nbytes--] = 0; + } + ws->plaintext_passwd_char_size[nchars-1] = 0; + } + } + break; + + case '\012': case '\015': /* Enter */ + unlock_cb (ws); + break; + + case '\033': /* Escape */ + ws->auth_state = AUTH_CANCEL; + break; + + case '\025': case '\030': /* Erase line */ + memset (ws->plaintext_passwd, 0, sizeof (ws->plaintext_passwd)); + memset (ws->plaintext_passwd_char_size, 0, + sizeof (ws->plaintext_passwd_char_size)); + break; + + default: + if (*decoded < ' ' && *decoded != '\t') /* Other ctrl char */ + XBell (ws->dpy, 0); + else + goto SELF_INSERT; + break; + } + } + else + { + int nbytes, nchars; + SELF_INSERT: + nbytes = strlen (ws->plaintext_passwd); + nchars = strlen (ws->plaintext_passwd_char_size); + if (nchars + 1 >= sizeof (ws->plaintext_passwd_char_size)-1 || + nbytes + decoded_size >= sizeof (ws->plaintext_passwd)-1) + XBell (ws->dpy, 0); /* overflow */ + else if (decoded_size == 0) + ; /* Non-inserting keysym (Shift, Ctrl) */ + else + { + ws->plaintext_passwd_char_size[nchars] = decoded_size; + ws->plaintext_passwd_char_size[nchars+1] = 0; + memcpy (ws->plaintext_passwd + nbytes, decoded, decoded_size); + ws->plaintext_passwd[nbytes + decoded_size] = 0; + } + } + + /* Construct the string of asterisks. */ + { + char *out = ws->censored_passwd; + int i; + *out = 0; + for (i = 0; i < MAX_PASSWD_CHARS && ws->plaintext_passwd_char_size[i]; i++) + { + const char *b = /* "\xE2\x80\xA2"; */ /* U+2022 Bullet */ + "\xe2\x97\x8f"; /* U+25CF Black Circle */ + strcat (out, b); + out += strlen(out); + } + } +} + + +static Bool +handle_button (window_state *ws, XEvent *xev, line_button_state *bs) +{ + Bool mouse_in_box = + (xev->xbutton.x_root >= (ws->x + bs->rect.x) && + xev->xbutton.x_root < (ws->x + bs->rect.x + bs->rect.width) && + xev->xbutton.y_root >= (ws->y + bs->rect.y) && + xev->xbutton.y_root < (ws->y + bs->rect.y + bs->rect.height)); + + bs->down_p = (!bs->disabled_p && + mouse_in_box && + xev->xany.type != ButtonRelease); + + if (xev->xany.type == ButtonRelease && mouse_in_box && !bs->disabled_p) + { + bs->disabled_p = True; /* Only allow them to press the button once. */ + if (bs->fn) + bs->fn (ws); + else if (bs->cmd) + { + int ac = 0; + char *av[10]; + av[ac++] = "/bin/sh"; + av[ac++] = "-c"; + av[ac++] = bs->cmd; + av[ac] = 0; + fork_and_exec (ws->dpy, ac, av); + } + else + XBell (ws->dpy, 0); + } + return mouse_in_box; +} + + +static Bool +handle_event (window_state *ws, XEvent *xev) +{ + Bool refresh_p = False; + switch (xev->xany.type) { + case KeyPress: + if (ws->splash_p) + ws->auth_state = AUTH_CANCEL; + else + { + handle_keypress (ws, &xev->xkey); + ws->caps_p = (xev->xkey.state & LockMask); + if (ws->auth_state == AUTH_NOTIFY) + ws->auth_state = AUTH_CANCEL; + } + refresh_p = True; + break; + + case ButtonPress: + case ButtonRelease: + { + if (! (handle_button (ws, xev, &ws->newlogin_button_state) || + handle_button (ws, xev, &ws->unlock_button_state) || + handle_button (ws, xev, &ws->demo_button_state) || + handle_button (ws, xev, &ws->help_button_state))) + if (ws->splash_p && xev->xany.type == ButtonRelease) + ws->auth_state = AUTH_CANCEL; + refresh_p = True; + } + default: + break; + } + return refresh_p; +} + + +/* Blink the I-beam cursor. */ +static void +cursor_timer (XtPointer closure, XtIntervalId *id) +{ + window_state *ws = (window_state *) closure; + int timeout = 0.7 * 1000 * (ws->i_beam ? 0.25 : 0.75); + if (ws->cursor_timer) + XtRemoveTimeOut (ws->cursor_timer); + ws->cursor_timer = + XtAppAddTimeOut (ws->app, timeout, cursor_timer, (XtPointer) ws); + ws->i_beam = !ws->i_beam; +} + + +/* Redraw the window for the thermometer, and exit if the time has expired. + */ +static void +thermo_timer (XtPointer closure, XtIntervalId *id) +{ + window_state *ws = (window_state *) closure; + int timeout = 1000/30; /* FPS */ + double now = double_time(); + if (now >= ws->end_time) + ws->auth_state = AUTH_TIME; + if (ws->timer) XtRemoveTimeOut (ws->timer); + ws->timer = XtAppAddTimeOut (ws->app, timeout, thermo_timer, (XtPointer) ws); +} + + +static void +gui_main_loop (window_state *ws, Bool splash_p, Bool notification_p) +{ + int timeout; + Bool refresh_p = True; + + if (splash_p) + { + timeout = get_seconds_resource (ws->dpy, "splashDuration", "Time"); + if (timeout <= 1) timeout = 1; + } + else if (ws->auth_state == AUTH_NOTIFY) + timeout = 5; + else + { + timeout = get_seconds_resource (ws->dpy, "passwdTimeout", "Time"); + if (timeout <= 5) timeout = 5; + cursor_timer (ws, 0); + } + + ws->start_time = double_time(); + ws->end_time = ws->start_time + timeout; + + /* Since the "xscreensaver" process holds the mouse and keyboard grabbed + while "xscreensaver-auth" is running, we don't receive normal KeyPress + events. That means that the XInput2 extension is required in order to + snoop on the keyboard in a way that bypasses grabs. + */ + if (! ws->xi_opcode) + { + Bool ov = verbose_p; + verbose_p = False; + init_xinput (ws->dpy, &ws->xi_opcode); + verbose_p = ov; + } + + thermo_timer (ws, 0); + window_draw (ws); + + while (ws->auth_state == AUTH_READ || + ws->auth_state == AUTH_NOTIFY) + { + XEvent xev; + XtInputMask m = XtAppPending (ws->app); + + if (m & XtIMXEvent) + /* Process timers then block on an X event (which we know is there) */ + XtAppNextEvent (ws->app, &xev); + else + { + if (m) + /* Process timers only, don't block */ + XtAppProcessEvent (ws->app, m); + else + { + if (refresh_p) + { + /* Redraw when outstanding events have been processed. */ + window_draw (ws); + refresh_p = False; + } + + /* No way to say "block until timer *or* X pending". + Without this, the timer that changes auth_state will fire but + then we will still be blocked until the next X event. */ + usleep (1000000/30); + } + continue; + } + + if ((m & ~XtIMXEvent) && !ws->splash_p) + refresh_p = True; /* In auth mode, all timers refresh */ + + if (verbose_p || debug_p) + print_xinput_event (ws->dpy, &xev, ""); + + /* Convert XInput events to Xlib events, for simplicity and familiarity. + */ + if (xev.xcookie.type == GenericEvent && + xev.xcookie.extension == ws->xi_opcode && + (xev.xcookie.data || XGetEventData (ws->dpy, &xev.xcookie))) + { + XEvent ev2; + Bool ok = + xinput_event_to_xlib (xev.xcookie.evtype, xev.xcookie.data, &ev2); + XFreeEventData (ws->dpy, &xev.xcookie); + if (ok) + xev = ev2; + } + + if (handle_event (ws, &xev)) + refresh_p = True; + + XtDispatchEvent (&xev); + + switch (xev.xany.type) { + + /* I don't think we ever receive these, but if we do, redraw. */ + case Expose: case GraphicsExpose: + refresh_p = True; + break; + + /* Likewise, receiving this event would be ideal, but we don't. */ + case VisibilityNotify: + refresh_p = True; + if (verbose_p > 1) + fprintf (stderr, "%s: VisibilityNotify\n", blurb()); + break; + + /* When another override-redirect window is raised above us, + we receive several ConfigureNotify events on the root window. */ + case ConfigureNotify: + if (verbose_p > 1) + fprintf (stderr, "%s: ConfigureNotify\n", blurb()); + break; + + case MappingNotify: + /* This event is supposed to be sent when the keymap is changed. + You would think that typing XK_ISO_Next_Group to change the + keyboard layout would count as such. It does not. */ + if (verbose_p) + fprintf (stderr, "%s: MappingNotify\n", blurb()); + get_keyboard_layout (ws); + refresh_p = True; + break; + + default: + break; + } + + /* Since MappingNotify doesn't work, we have to do this crap instead. */ + if (xev.xany.type == ws->xkb_opcode) + { + XkbEvent *xkb = (XkbEvent *) &xev; + if (verbose_p) + fprintf (stderr, "%s: XKB event %d\n", blurb(), xkb->any.xkb_type); + get_keyboard_layout (ws); + refresh_p = True; + } + } + + /* Re-raw the window one last time, since it might sit here for a while + while PAM does it's thing. */ + window_draw (ws); + XSync (ws->dpy, False); + + if (verbose_p) { + const char *kind = (splash_p ? "splash" : + notification_p ? "notification" : "authentication"); + switch (ws->auth_state) { + case AUTH_FINISHED: + fprintf (stderr, "%s: %s input finished\n", blurb(), kind); break; + case AUTH_CANCEL: + fprintf (stderr, "%s: %s canceled\n", blurb(), kind); break; + case AUTH_TIME: + fprintf (stderr, "%s: %s timed out\n", blurb(), kind); break; + default: break; + } + } +} + + +/* Pops up a dialog and waits for the user to complete it. + Returns 0 on successful completion. + Updates 'resp' with any entered response. + */ +static Bool +dialog_session (window_state *ws, + int nmsgs, + const auth_message *msgs, + auth_response *resp) +{ + int i; + + ws->auth_state = AUTH_READ; + ws->nmsgs = nmsgs; + ws->msgs = msgs; + + memset (ws->plaintext_passwd, 0, sizeof(ws->plaintext_passwd)); + memset (ws->plaintext_passwd_char_size, 0, + sizeof(ws->plaintext_passwd_char_size)); + memset (ws->censored_passwd, 0, sizeof(ws->censored_passwd)); + ws->unlock_button_state.disabled_p = False; + + gui_main_loop (ws, False, False); + + if (ws->auth_state != AUTH_FINISHED) + return True; /* Timed out or canceled */ + + /* Find the (at most one) input field in the previous batch and return + the entered plaintext to it. */ + for (i = 0; i < nmsgs; i++) + { + if (msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO || + msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO) + { + if (resp[i].response) abort(); + resp[i].response = strdup(ws->plaintext_passwd); + } + } + + ws->nmsgs = 0; + ws->msgs = 0; + + return False; +} + + +/* To retain this across multiple calls from PAM to xscreensaver_auth_conv. */ +window_state *global_ws = 0; + + +/* The authentication conversation function. + + Like a PAM conversation function, this accepts multiple messages in a + single round. We can only do one text entry field in the dialog at a + time, so if there is more than one entry, multiple dialogs will be used. + + PAM might call this multiple times before authenticating. We are unable + to combine multiple messages onto a single dialog if PAM splits them + between calls to this function. + + Returns True on success. If the user timed out or cancelled, we just exit. + */ +Bool +xscreensaver_auth_conv (void *closure, + int nmsgs, + const auth_message *msgs, + auth_response **resp) +{ + Widget root_widget = (Widget) closure; + int i; + int prev_msg = 0; + int field_count = 0; + auth_response *responses; + window_state *ws = global_ws; + + if (!ws) + ws = global_ws = window_init (root_widget, False); + + responses = calloc (nmsgs, sizeof(*responses)); + if (!responses) abort(); + + for (i = 0; i < nmsgs; i++) + { + if (msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO || + msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO) + { + /* A text input field. */ + + if (field_count > 0) + { + /* This is the second one -- we must run the dialog on + the field collected so far. */ + if (dialog_session (ws, + i - prev_msg, + msgs + prev_msg, + responses + prev_msg)) + goto END; + prev_msg = i; + field_count = 0; + } + + field_count++; + } + } + + if (prev_msg < i || nmsgs == 0) + /* Run the dialog on the stuff that's left. This happens if there was + more than one text field. */ + dialog_session (ws, + i - prev_msg, + msgs + prev_msg, + responses + prev_msg); + + END: + + switch (ws->auth_state) { + case AUTH_CANCEL: + case AUTH_TIME: + /* No need to return to PAM or clean up. We're outta here! + Exit with 0 to distinguish it from our "success" or "failure" + exit codes. */ + destroy_window (ws); + exit (0); + break; + case AUTH_FINISHED: + *resp = responses; + return True; + default: + abort(); + break; + } +} + + +/* Called after authentication is complete so that we can present a "nope" + dialog if it failed, or snitch on previous failed login attempts. + */ +void +xscreensaver_auth_finished (void *closure, Bool authenticated_p) +{ + Widget root_widget = (Widget) closure; + window_state *ws = global_ws; + char msg[1024]; + int unlock_failures = 0; + time_t failure_time = 0; + Bool prompted_p = !!ws; + + /* If this was called without xscreensaver_auth_conv() ever having been + called, then either PAM decided that the user is authenticated without + a prompt (e.g. a bluetooth fob); or there was an error initializing + passwords (e.g., shadow passwords but not setuid.) + */ + if (!ws) + ws = global_ws = window_init (root_widget, False); + + if (authenticated_p) + { + /* Read the old failure count, and delete it. */ + persistent_auth_status_failure (ws, False, True, + &unlock_failures, &failure_time); + } + else + { + /* Increment the failure count. */ + persistent_auth_status_failure (ws, True, False, + &unlock_failures, &failure_time); + } + + /* If we have something to say, put the dialog back up for a few seconds + to display it. Otherwise, don't bother. + */ + if (!authenticated_p && !prompted_p) + strcpy (msg, _("Password initialization failed")); + else if (!authenticated_p && ws && ws->caps_p) + strcpy (msg, _("Authentication failed (Caps Lock?)")); + else if (!authenticated_p) + strcpy (msg, _("Authentication failed!")); + else if (authenticated_p && unlock_failures > 0) + { + time_t now = time ((time_t *) 0); + int sec = now - failure_time; + int min = (sec + 30) / 60; + int hours = (min + 30) / 60; + int days = (hours + 12) / 24; + char ago[100]; + int warning_slack = + get_integer_resource (ws->dpy, "authWarningSlack", "AuthWarningSlack"); + + if (sec < warning_slack) + { + if (verbose_p) + fprintf (stderr, "%s: ignoring recent unlock failures:" + " %d within %d sec\n", + blurb(), unlock_failures, warning_slack); + goto END; + } + else if (days > 1) sprintf (ago, _("%d days ago"), days); + else if (hours > 1) sprintf (ago, _("%d hours ago"), hours); + else if (min > 1) sprintf (ago, _("%d minutes ago"), min); + else sprintf (ago, _("just now")); + + if (unlock_failures == 1) + sprintf (msg, _("There has been 1 failed login attempt, %s."), ago); + else + sprintf (msg, + _("There have been %d failed login attempts, oldest %s."), + unlock_failures, ago); + } + else + { + /* No need to pop up a window. Authenticated, and there are no previous + failures to report. + */ + goto END; + } + + if (!*msg) abort(); + ws->body_label = strdup (msg); + + ws->auth_state = AUTH_NOTIFY; + gui_main_loop (ws, False, True); + + END: + destroy_window (global_ws); +} + + +void +xscreensaver_splash (void *closure) +{ + Widget root_widget = (Widget) closure; + window_state *ws = window_init (root_widget, True); + ws->auth_state = AUTH_READ; + gui_main_loop (ws, True, False); + destroy_window (ws); + exit (0); +} diff --git a/driver/dpms.c b/driver/dpms.c index 3a8803c..15721ea 100644 --- a/driver/dpms.c +++ b/driver/dpms.c @@ -1,5 +1,5 @@ -/* dpms.c --- syncing the X Display Power Management values - * xscreensaver, Copyright (c) 2001-2017 Jamie Zawinski +/* dpms.c --- syncing the X Display Power Management System values + * xscreensaver, Copyright © 2001-2021 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 @@ -10,88 +10,36 @@ * implied warranty. */ -/* Display Power Management System (DPMS.) - - On XFree86 systems, "man xset" reports: - - -dpms The -dpms option disables DPMS (Energy Star) features. - +dpms The +dpms option enables DPMS (Energy Star) features. - - dpms flags... - The dpms option allows the DPMS (Energy Star) - parameters to be set. The option can take up to three - numerical values, or the `force' flag followed by a - DPMS state. The `force' flags forces the server to - immediately switch to the DPMS state specified. The - DPMS state can be one of `standby', `suspend', or - `off'. When numerical values are given, they set the - inactivity period before the three modes are activated. - The first value given is for the `standby' mode, the - second is for the `suspend' mode, and the third is for - the `off' mode. Setting these values implicitly - enables the DPMS features. A value of zero disables a - particular mode. - - However, note that the implementation is more than a little bogus, - in that there is code in /usr/X11R6/lib/libXdpms.a to implement all - the usual server-extension-querying utilities -- but there are no - prototypes in any header file! Thus, the prototypes here. (The - stuff in X11/extensions/dpms.h and X11/extensions/dpmsstr.h define - the raw X protcol, they don't define the API to libXdpms.a.) - - Some documentation: - Library: ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMSLib.ms - Protocol: ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMS.ms - */ - #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include +#include -#ifdef HAVE_DPMS_EXTENSION /* almost the whole file */ +#include "xscreensaver.h" -# include -# include -/*# include */ +#ifndef HAVE_DPMS_EXTENSION /* almost the whole file */ - /* Why this crap is not in a header file somewhere, I have no idea. Losers! - */ - extern Bool DPMSQueryExtension (Display *, int *event_ret, int *err_ret); - extern Status DPMSGetVersion (Display *, int *major_ret, int *minor_ret); - extern Bool DPMSCapable (Display *); - extern Status DPMSInfo (Display *, CARD16 *power_level, BOOL *state); - extern Status DPMSEnable (Display *dpy); - extern Status DPMSDisable (Display *dpy); - extern Status DPMSForceLevel (Display *, CARD16 level); - extern Status DPMSSetTimeouts (Display *, CARD16 standby, CARD16 suspend, - CARD16 off); - extern Bool DPMSGetTimeouts (Display *, CARD16 *standby, - CARD16 *suspend, CARD16 *off); - -#endif /* HAVE_DPMS_EXTENSION */ - - -/* This file doesn't need the Xt headers, so stub these types out... */ -#undef XtPointer -#define XtAppContext void* -#define XrmDatabase void* -#define XtIntervalId void* -#define XtPointer void* -#define Widget void* +void +sync_server_dpms_settings (Display *dpy, struct saver_preferences *p) +{ + if (p->verbose_p) + fprintf (stderr, "%s: DPMS not supported at compile time\n", blurb()); +} -#include "xscreensaver.h" +Bool monitor_powered_on_p (Display *dpy) { return True; } +void monitor_power_on (saver_info *si, Bool on_p) { return; } + +#else /* HAVE_DPMS_EXTENSION -- whole file */ -#ifdef HAVE_DPMS_EXTENSION +# include +# include -#define CLAMP_DOWN(var,val,mi) do { int old = (var); (var) -= (val); if ((var) < (mi) || (var) > old) (var) = (mi); } while (0) static Bool error_handler_hit_p = False; -static int idle_offset = 0; - static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) { @@ -99,17 +47,9 @@ ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) return 0; } -void -store_dpms_offset(int offset) -{ - idle_offset = offset < 15 ? 0 : offset; -} - void -sync_server_dpms_settings (Display *dpy, Bool enabled_p, Bool dpms_quickoff_p, - int standby_secs, int suspend_secs, int off_secs, - Bool verbose_p) +sync_server_dpms_settings (Display *dpy, struct saver_preferences *p) { int event = 0, error = 0; BOOL o_enabled = False; @@ -117,11 +57,29 @@ sync_server_dpms_settings (Display *dpy, Bool enabled_p, Bool dpms_quickoff_p, CARD16 o_standby = 0, o_suspend = 0, o_off = 0; Bool bogus_p = False; + Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK); + Bool dpms_quickoff_p = p->dpms_quickoff_p; + int standby_secs = p->dpms_standby / 1000; + int suspend_secs = p->dpms_suspend / 1000; + int off_secs = p->dpms_off / 1000; + Bool verbose_p = p->verbose_p; + static Bool warned_p = False; + + /* If the monitor is currently powered off, defer any changes until + we are next called while it is powered on. */ + if (! monitor_powered_on_p (dpy)) + return; + + /* Why did I do this? It makes DPMS never happen. + XSetScreenSaver (dpy, 0, 0, 0, 0); + XForceScreenSaver (dpy, ScreenSaverReset); + */ + if (dpms_quickoff_p && !off_secs) - { - /* To do this, we might need to temporarily re-enable DPMS first. */ - off_secs = 0xFFFF; - } + { + /* To do this, we might need to temporarily re-enable DPMS first. */ + off_secs = 0xFFFF; + } if (standby_secs == 0 && suspend_secs == 0 && off_secs == 0) /* all zero implies "DPMS disabled" */ @@ -135,13 +93,6 @@ sync_server_dpms_settings (Display *dpy, Bool enabled_p, Bool dpms_quickoff_p, if (bogus_p) enabled_p = False; - if (idle_offset != 0) - { - CLAMP_DOWN (standby_secs, idle_offset, 10); - CLAMP_DOWN (suspend_secs, idle_offset, 10); - CLAMP_DOWN (off_secs, idle_offset, 10); - } - /* X protocol sends these values in a CARD16, so truncate them to 16 bits. This means that the maximum timeout is 18:12:15. */ @@ -151,22 +102,25 @@ sync_server_dpms_settings (Display *dpy, Bool enabled_p, Bool dpms_quickoff_p, if (! DPMSQueryExtension (dpy, &event, &error)) { - if (verbose_p) - fprintf (stderr, "%s: XDPMS extension not supported.\n", blurb()); + if (verbose_p && !warned_p) + fprintf (stderr, "%s: XDPMS extension not supported\n", blurb()); + warned_p = True; return; } if (! DPMSCapable (dpy)) { - if (verbose_p) - fprintf (stderr, "%s: DPMS not supported.\n", blurb()); + if (verbose_p && !warned_p) + fprintf (stderr, "%s: DPMS not supported\n", blurb()); + warned_p = True; return; } if (! DPMSInfo (dpy, &o_power, &o_enabled)) { - if (verbose_p) - fprintf (stderr, "%s: unable to get DPMS state.\n", blurb()); + if (verbose_p && !warned_p) + fprintf (stderr, "%s: unable to get DPMS state\n", blurb()); + warned_p = True; return; } @@ -174,19 +128,20 @@ sync_server_dpms_settings (Display *dpy, Bool enabled_p, Bool dpms_quickoff_p, { if (! (enabled_p ? DPMSEnable (dpy) : DPMSDisable (dpy))) { - if (verbose_p) - fprintf (stderr, "%s: unable to set DPMS state.\n", blurb()); + if (verbose_p && !warned_p) + fprintf (stderr, "%s: unable to set DPMS state\n", blurb()); + warned_p = True; return; } else if (verbose_p) - fprintf (stderr, "%s: turned DPMS %s.\n", blurb(), + fprintf (stderr, "%s: turned DPMS %s\n", blurb(), enabled_p ? "on" : "off"); } if (bogus_p) { if (verbose_p) - fprintf (stderr, "%s: not setting bogus DPMS timeouts: %d %d %d.\n", + fprintf (stderr, "%s: not setting bogus DPMS timeouts: %d %d %d\n", blurb(), standby_secs, suspend_secs, off_secs); return; } @@ -194,7 +149,7 @@ sync_server_dpms_settings (Display *dpy, Bool enabled_p, Bool dpms_quickoff_p, if (!DPMSGetTimeouts (dpy, &o_standby, &o_suspend, &o_off)) { if (verbose_p) - fprintf (stderr, "%s: unable to get DPMS timeouts.\n", blurb()); + fprintf (stderr, "%s: unable to get DPMS timeouts\n", blurb()); return; } @@ -205,34 +160,34 @@ sync_server_dpms_settings (Display *dpy, Bool enabled_p, Bool dpms_quickoff_p, if (!DPMSSetTimeouts (dpy, standby_secs, suspend_secs, off_secs)) { if (verbose_p) - fprintf (stderr, "%s: unable to set DPMS timeouts.\n", blurb()); + fprintf (stderr, "%s: unable to set DPMS timeouts\n", blurb()); return; } else if (verbose_p) - fprintf (stderr, "%s: set DPMS timeouts: %d %d %d.\n", blurb(), + fprintf (stderr, "%s: set DPMS timeouts: %d %d %d\n", blurb(), standby_secs, suspend_secs, off_secs); } } Bool -monitor_powered_on_p (saver_info *si) +monitor_powered_on_p (Display *dpy) { Bool result; int event_number, error_number; BOOL onoff = False; CARD16 state; - if (!DPMSQueryExtension(si->dpy, &event_number, &error_number)) + if (!DPMSQueryExtension(dpy, &event_number, &error_number)) /* Server doesn't know -- assume the monitor is on. */ result = True; - else if (!DPMSCapable(si->dpy)) + else if (!DPMSCapable(dpy)) /* Server says the monitor doesn't do power management -- so it's on. */ result = True; else { - DPMSInfo(si->dpy, &state, &onoff); + DPMSInfo(dpy, &state, &onoff); if (!onoff) /* Server says DPMS is disabled -- so the monitor is on. */ result = True; @@ -252,17 +207,19 @@ monitor_powered_on_p (saver_info *si) void monitor_power_on (saver_info *si, Bool on_p) { - if ((!!on_p) != monitor_powered_on_p (si)) + if ((!!on_p) != monitor_powered_on_p (si->dpy)) { XErrorHandler old_handler; int event_number, error_number; + static Bool warned_p = False; if (!DPMSQueryExtension(si->dpy, &event_number, &error_number) || !DPMSCapable(si->dpy)) { - if (si->prefs.verbose_p) + if (si->prefs.verbose_p && !warned_p) fprintf (stderr, - "%s: unable to power %s monitor: no DPMS extension.\n", + "%s: unable to power %s monitor: no DPMS extension\n", blurb(), (on_p ? "on" : "off")); + warned_p = True; return; } @@ -286,42 +243,12 @@ monitor_power_on (saver_info *si, Bool on_p) XSetErrorHandler (old_handler); /* Ignore error_handler_hit_p, just probe monitor instead */ - if ((!!on_p) != monitor_powered_on_p (si)) /* double-check */ + if ((!!on_p) != monitor_powered_on_p (si->dpy)) /* double-check */ fprintf (stderr, - "%s: DPMSForceLevel(dpy, %s) did not change monitor power state.\n", + "%s: DPMSForceLevel(dpy, %s) did not change monitor power state\n", blurb(), (on_p ? "DPMSModeOn" : "DPMSModeOff")); } } -#else /* !HAVE_DPMS_EXTENSION */ - -void -store_dpms_offset(Display *dpy, Bool reset_p) -{ -} - - -void -sync_server_dpms_settings (Display *dpy, Bool enabled_p, - Bool dpms_quickoff_p, - int standby_secs, int suspend_secs, int off_secs, - Bool verbose_p) -{ - if (verbose_p) - fprintf (stderr, "%s: DPMS support not compiled in.\n", blurb()); -} - -Bool -monitor_powered_on_p (saver_info *si) -{ - return True; -} - -void -monitor_power_on (saver_info *si, Bool on_p) -{ - return; -} - -#endif /* !HAVE_DPMS_EXTENSION */ +#endif /* HAVE_DPMS_EXTENSION -- whole file */ diff --git a/driver/exec.c b/driver/exec.c index 38ca88a..8d1c45a 100644 --- a/driver/exec.c +++ b/driver/exec.c @@ -1,5 +1,5 @@ /* exec.c --- executes a program in *this* pid, without an intervening process. - * xscreensaver, Copyright (c) 1991-2013 Jamie Zawinski + * xscreensaver, Copyright © 1991-2021 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 @@ -69,14 +69,6 @@ /* and also setrlimit() and RLIMIT_AS */ #endif -#ifdef VMS -# include -# include /* for close */ -# include /* for getpid */ -# define pid_t int -# define fork vfork -#endif /* VMS */ - #include "exec.h" extern const char *blurb (void); @@ -84,8 +76,6 @@ extern const char *blurb (void); static void nice_process (int nice_level); -#ifndef VMS - static void exec_simple_command (const char *command) { @@ -161,26 +151,12 @@ exec_complex_command (const char *shell, const char *command) execvp (av[0], av); /* shouldn't return. */ } -#else /* VMS */ - -static void -exec_vms_command (const char *command) -{ - system (command); - fflush (stderr); - fflush (stdout); - exit (1); /* Note that this only exits a child fork. */ -} - -#endif /* !VMS */ - void exec_command (const char *shell, const char *command, int nice_level) { int hairy_p; -#ifndef VMS nice_process (nice_level); hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"="); @@ -204,10 +180,6 @@ exec_command (const char *shell, const char *command, int nice_level) else /* Otherwise, we can just exec the program directly. */ exec_simple_command (command); - -#else /* VMS */ - exec_vms_command (command); -#endif /* VMS */ } @@ -242,7 +214,7 @@ nice_process (int nice_level) } #else fprintf (stderr, - "%s: don't know how to change process priority on this system.\n", + "%s: don't know how to change process priority on this system\n", blurb()); #endif diff --git a/driver/exts.c b/driver/exts.c new file mode 100644 index 0000000..641325d --- /dev/null +++ b/driver/exts.c @@ -0,0 +1,238 @@ +/* xscreensaver, Copyright © 1991-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +/* Some random diagnostics printed in -verbose mode about what extensions + are available on the server, and their version numbers. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include + +#ifdef HAVE_XSHM_EXTENSION +# include +#endif /* HAVE_XSHM_EXTENSION */ + +#ifdef HAVE_DPMS_EXTENSION +# include +#endif /* HAVE_DPMS_EXTENSION */ + + +#ifdef HAVE_DOUBLE_BUFFER_EXTENSION +# include +#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ + +#ifdef HAVE_XF86VMODE +# include +#endif /* HAVE_XF86VMODE */ + +#ifdef HAVE_XINERAMA +# include +#endif /* HAVE_XINERAMA */ + +#ifdef HAVE_RANDR +# include +#endif /* HAVE_RANDR */ + +#ifdef HAVE_XCOMPOSITE_EXTENSION +# include +#endif + +#ifdef HAVE_XKB +# include +#endif + +#include "xscreensaver.h" + + +void +print_available_extensions (saver_info *si) +{ + int i, j; + static struct { + const char *name; const char *desc; + Bool useful_p; + Status (*version_fn) (Display *, int *majP, int *minP); + } exts[] = { + +# if 0 + { "MIT-SCREEN-SAVER", "MIT Screen-Saver", + False, 0 + }, +# endif + { "MIT-SHM", "Shared Memory", +# ifdef HAVE_XSHM_EXTENSION + True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */ +# else + False, 0 +# endif + }, { "DOUBLE-BUFFER", "Double-Buffering", +# ifdef HAVE_DOUBLE_BUFFER_EXTENSION + True, XdbeQueryExtension +# else + False, 0 +# endif + }, { "DPMS", "Power Management", +# ifdef HAVE_DPMS_EXTENSION + True, DPMSGetVersion +# else + False, 0 +# endif + }, { "GLX", "GLX", +# ifdef HAVE_GL + True, 0 +# else + False, 0 +# endif + }, { "XFree86-VidModeExtension", "XF86 Video-Mode", +# ifdef HAVE_XF86VMODE + True, XF86VidModeQueryVersion +# else + False, 0 +# endif + }, { "XC-VidModeExtension", "XC Video-Mode", +# ifdef HAVE_XF86VMODE + True, XF86VidModeQueryVersion +# else + False, 0 +# endif + }, { "XINERAMA", "Xinerama", +# ifdef HAVE_XINERAMA + True, XineramaQueryVersion +# else + False, 0 +# endif + }, { "RANDR", "Resize-and-Rotate", +# ifdef HAVE_RANDR + True, XRRQueryVersion +# else + False, 0 +# endif + }, { "Composite", "Composite", +# ifdef HAVE_XCOMPOSITE_EXTENSION + True, XCompositeQueryVersion +# else + True, 0 +# endif + }, { "XKEYBOARD", "XKeyboard", +# ifdef HAVE_XKB + True, 0, +# else + False, 0 +# endif + }, { "DRI", "DRI", True, 0 + }, { "NV-CONTROL", "NVidia", True, 0 + }, { "NV-GLX", "NVidia GLX", True, 0 + }, { "Apple-DRI", "Apple-DRI", True, 0 + }, { "Apple-WM", "Apple-WM", True, 0 + }, { "XInputExtension", "XInput", True, 0 + }, + }; + + fprintf (stderr, "%s: running on display \"%s\"\n", blurb(), + DisplayString(si->dpy)); + fprintf (stderr, "%s: vendor is %s, %d\n", blurb(), + ServerVendor(si->dpy), VendorRelease(si->dpy)); + + fprintf (stderr, "%s: useful extensions:\n", blurb()); + for (i = 0; i < countof(exts); i++) + { + int op = 0, event = 0, error = 0; + char buf [255]; + int maj = 0, min = 0; + int dummy1, dummy2, dummy3; + + /* Most of the extension version functions take 3 args, + writing results into args 2 and 3, but some take more. + We only ever care about the first two results, but we + pass in three extra pointers just in case. + */ + Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) = + (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn; + + if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error)) + continue; + sprintf (buf, "%s: ", blurb()); + strcat (buf, exts[i].desc); + + if (!strcmp (exts[i].desc, "XInput")) + { + int maj = 999, min = 999; /* Desired */ + XIQueryVersion (si->dpy, &maj, &min); /* Actual */ + sprintf (buf+strlen(buf), " (%d.%d)", maj, min); + } + else if (!version_fn_2) + ; + else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3)) + sprintf (buf+strlen(buf), " (%d.%d)", maj, min); + else + strcat (buf, " (unavailable)"); + + if (!exts[i].useful_p) + strcat (buf, " (disabled at compile time)"); + fprintf (stderr, "%s\n", buf); + } + +# ifdef HAVE_LIBSYSTEMD + fprintf (stderr, "%s: libsystemd\n", blurb()); +# else + fprintf (stderr, "%s: libsystemd (disabled at compile time)\n", blurb()); +# endif + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + unsigned long colormapped_depths = 0; + unsigned long non_mapped_depths = 0; + XVisualInfo vi_in, *vi_out; + int out_count; + + if (!ssi->real_screen_p) continue; + + vi_in.screen = ssi->real_screen_number; + vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count); + if (!vi_out) continue; + for (j = 0; j < out_count; j++) { + if (vi_out[j].depth >= 32) continue; + if (vi_out[j].class == PseudoColor) + colormapped_depths |= (1 << vi_out[j].depth); + else + non_mapped_depths |= (1 << vi_out[j].depth); + } + XFree ((char *) vi_out); + + if (colormapped_depths) + { + fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), + ssi->real_screen_number); + for (j = 0; j < 32; j++) + if (colormapped_depths & (1 << j)) + fprintf (stderr, " %d", j); + fprintf (stderr, "\n"); + } + if (non_mapped_depths) + { + fprintf (stderr, "%s: screen %d non-colormapped depths:", + blurb(), ssi->real_screen_number); + for (j = 0; j < 32; j++) + if (non_mapped_depths & (1 << j)) + fprintf (stderr, " %d", j); + fprintf (stderr, "\n"); + } + } +} diff --git a/driver/fade.c b/driver/fade.c new file mode 100644 index 0000000..9451582 --- /dev/null +++ b/driver/fade.c @@ -0,0 +1,1749 @@ +/* xscreensaver, Copyright © 1992-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +/* There are several different mechanisms here for fading the desktop to + black, and then fading it back in again. + + - Colormaps: This only works on 8-bit displays, which basically haven't + existed since the 90s. It takes the current colormap, makes a writable + copy of it, and then animates the color cells to fade and unfade. + + - XF86 Gamma or RANDR Gamma: These do the fade by altering the brightness + settings of the screen. This works on any system that has the "XF86 + Video-Mode" extension (which is every modern system) AND ALSO has gamma + support in the video driver. But it turns out that as of 2021, the + Raspberry Pi HDMI video driver still does not support gamma. And there's + no way to determine that the video driver lacks gamma support even though + the extension exists. Since the Pi is probably the single most popular + platform for running X11 on the desktop these days, that makes this + method pretty much useless now. + + - SGI VC: Same as the above, but only works on SGI. + + - XSHM: This works by taking a screenshot and hacking the bits by hand. + It's slow. Also, in order to fade in from black to the desktop (possibly + hours after it faded out) it has to retain that first screenshot of the + desktop to fade back to. But if the desktop had changed in the meantime, + there will be a glitch at the end as it snaps from the screenshot to the + new current reality. + + In summary, everything is terrible because X11 doesn't support alpha. + + + The fade process goes like this: + + Screen saver activates: + - Fade out: + - Desktop is visible + - Save screenshot for later + - Map invisible temp windows + - Fade from desktop to black + - Erase saver windows to black and raise them + - Destroy temp windows + + Screen saver deactivates: + - Fade out: + - Saver graphics are visible + - Map invisible temp windows + - Do not save a screenshot + - Fade from graphics to black + - Erase saver windows to black and raise them + - Destroy temp windows + + - Fade in: + - Screen is black + - Map invisible temp windows + - Do not save a screenshot + - Unmap saver windows + - Fade from black to saved screenshot + - Destroy temp windows + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#ifdef HAVE_JWXYZ +# include "jwxyz.h" +#else /* real X11 */ +# include +# include +# include +# include +#endif /* !HAVE_JWXYZ */ + +#include "blurb.h" +#include "visual.h" +#include "usleep.h" +#include "fade.h" +#include "xshm.h" +#include "atoms.h" +#include "clientmsg.h" +#include "xmu.h" + +/* Since gamma fading doesn't work on the Raspberry Pi, probably the single + most popular desktop Linux system these days, let's not use this fade + method even if the extension exists (which it does). + */ +#undef HAVE_XF86VMODE_GAMMA + +/* I'm not sure that the RANDR fade method brings anything to the party + that the XF86 method does See below. + */ +#undef HAVE_RANDR_12 + +#define HAVE_XINPUT2 1 /* Mandatory */ + + +#ifdef HAVE_XINPUT2 +# include +# include "xinput.h" +#endif + + +typedef struct { + int nscreens; + Pixmap *screenshots; +} fade_state; + + +/* #### There's a bunch of duplicated code in the back half of the + four _fade and _whack functions that could probably be combined. + */ +#ifdef HAVE_SGI_VC_EXTENSION +static int sgi_gamma_fade (XtAppContext, Display *, Window *wins, int count, + double secs, Bool out_p); +#endif +#ifdef HAVE_XF86VMODE_GAMMA +static int xf86_gamma_fade (XtAppContext, Display *, Window *wins, int count, + double secs, Bool out_p); +#endif +#ifdef HAVE_RANDR_12 +static int randr_gamma_fade (XtAppContext, Display *, Window *wins, int count, + double secs, Bool out_p); +#endif +static int colormap_fade (XtAppContext, Display *, Window *wins, int count, + double secs, Bool out_p, Bool from_desktop_p); +static int xshm_fade (XtAppContext, Display *, + Window *wins, int count, double secs, + Bool out_p, Bool from_desktop_p, fade_state *); + + +static double +double_time (void) +{ + struct timeval now; +# ifdef GETTIMEOFDAY_TWO_ARGS + struct timezone tzp; + gettimeofday(&now, &tzp); +# else + gettimeofday(&now); +# endif + + return (now.tv_sec + ((double) now.tv_usec * 0.000001)); +} + + +#ifdef HAVE_XINPUT2 +static int xi_opcode = -1; +#endif + +/* Closure arg points to a Bool saying whether motion events count. + Motion aborts fade-out, but only clicks and keys abort fade-in. + */ +static Bool +user_event_p (Display *dpy, XEvent *event, XPointer arg) +{ + Bool motion_p = *((Bool *) arg); + + switch (event->xany.type) { + case KeyPress: case ButtonPress: + return True; + break; + case MotionNotify: + if (motion_p) return True; + break; +# ifdef HAVE_XINPUT2 + case GenericEvent: + { + XIRawEvent *re; + if (event->xcookie.extension != xi_opcode) + return False; + if (! event->xcookie.data) + XGetEventData (dpy, &event->xcookie); + if (! event->xcookie.data) + return False; + re = event->xcookie.data; + + if (re->evtype == XI_RawKeyPress || + re->evtype == XI_RawButtonPress) + return True; + else if (motion_p && re->evtype == XI_RawMotion) + return True; + + /* Calling XFreeEventData here is bad news */ + } + break; +# endif /* HAVE_XINPUT2 */ + default: break; + } + + return False; +} + + +static Bool +user_active_p (XtAppContext app, Display *dpy, Bool fade_out_p) +{ + XEvent event; + XtInputMask m; + Bool motion_p = fade_out_p; /* Motion aborts fade-out, not fade-in. */ + motion_p = False; /* Naah, never abort on motion only */ + +# ifdef HAVE_XINPUT2 + if (xi_opcode == -1) + { + Bool ov = verbose_p; + xi_opcode = 0; /* only init once */ + verbose_p = False; /* xscreensaver already printed this */ + init_xinput (dpy, &xi_opcode); + verbose_p = ov; + } +# endif + + m = XtAppPending (app); + if (m & ~XtIMXEvent) + { + /* Process timers and signals only, don't block. */ + if (verbose_p > 1) + fprintf (stderr, "%s: Xt pending %ld\n", blurb(), m); + XtAppProcessEvent (app, m); + } + + /* If there is user activity, bug out. (Bug out on keypresses or + mouse presses, but not motion, and not release events. Bugging + out on motion made the unfade hack be totally useless, I think.) + */ + if (XCheckIfEvent (dpy, &event, &user_event_p, (XPointer) &motion_p)) + { + if (verbose_p > 1) + { + XIRawEvent *re = 0; + if (event.xany.type == GenericEvent && !event.xcookie.data) + { + XGetEventData (dpy, &event.xcookie); + re = event.xcookie.data; + } + fprintf (stderr, "%s: user input %d %d\n", blurb(), + event.xany.type, + (re ? re->evtype : -1)); + } + XPutBackEvent (dpy, &event); + return True; + } + + return False; +} + + +static void +flush_user_input (Display *dpy) +{ + XEvent event; + Bool motion_p = True; + while (XCheckIfEvent (dpy, &event, &user_event_p, (XPointer) &motion_p)) + if (verbose_p > 1) + { + XIRawEvent *re = 0; + if (event.xany.type == GenericEvent && !event.xcookie.data) + { + XGetEventData (dpy, &event.xcookie); + re = event.xcookie.data; + } + fprintf (stderr, "%s: flushed user event %d %d\n", blurb(), + event.xany.type, + (re ? re->evtype : -1)); + } +} + + +/* This bullshit is needed because the VidMode and SHM extensions don't work + on remote displays. */ +static Bool error_handler_hit_p = False; + +static int +ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) +{ + if (verbose_p > 1) + XmuPrintDefaultErrorMessage (dpy, error, stderr); + error_handler_hit_p = True; + return 0; +} + + +/* Returns true if canceled by user activity. */ +Bool +fade_screens (XtAppContext app, Display *dpy, + Window *saver_windows, int nwindows, + double seconds, Bool out_p, Bool from_desktop_p, + void **closureP) +{ + int status = False; + fade_state *state = 0; + + if (nwindows <= 0) abort(); + if (!saver_windows) abort(); + + if (!closureP) abort(); + state = (fade_state *) *closureP; + if (!state) + { + state = (fade_state *) calloc (1, sizeof (*state)); + *closureP = state; + } + + if (from_desktop_p && !out_p) + abort(); /* Fading in from desktop makes no sense */ + + if (out_p) + flush_user_input (dpy); /* Flush at start of cycle */ + +# ifdef HAVE_SGI_VC_EXTENSION + /* First try to do it by fading the gamma in an SGI-specific way... */ + status = sgi_gamma_fade (app, dpy, saver_windows, nwindows, seconds, out_p); + if (status == 0 || status == 1) + return status; /* faded, possibly canceled */ +# endif + +# ifdef HAVE_RANDR_12 + /* Then try to do it by fading the gamma in an RANDR-specific way... */ + status = randr_gamma_fade (app, dpy, saver_windows, nwindows, seconds, out_p); + if (status == 0 || status == 1) + return status; /* faded, possibly canceled */ +# endif + +# ifdef HAVE_XF86VMODE_GAMMA + /* Then try to do it by fading the gamma in an XFree86-specific way... */ + status = xf86_gamma_fade(app, dpy, saver_windows, nwindows, seconds, out_p); + if (status == 0 || status == 1) + return status; /* faded, possibly canceled */ +# endif + + if (has_writable_cells (DefaultScreenOfDisplay (dpy), + DefaultVisual (dpy, 0))) + { + /* Do it the old-fashioned way, which only really worked on + 8-bit displays. */ + status = colormap_fade (app, dpy, saver_windows, nwindows, seconds, + out_p, from_desktop_p); + if (status == 0 || status == 1) + return status; /* faded, possibly canceled */ + } + + /* Else do it the hard way, by hacking a screenshot. */ + status = xshm_fade (app, dpy, saver_windows, nwindows, seconds, out_p, + from_desktop_p, state); + status = (status ? True : False); + + return status; +} + +/**************************************************************************** + + Colormap fading + + ****************************************************************************/ + + +/* The business with `cmaps_per_screen' is to fake out the SGI 8-bit video + hardware, which is capable of installing multiple (4) colormaps + simultaneously. We have to install multiple copies of the same set of + colors in order to fill up all the available slots in the hardware color + lookup table, so we install an extra N colormaps per screen to make sure + that all screens really go black. + + I'm told that this trick also works with XInside's AcceleratedX when using + the Matrox Millennium card (which also allows multiple PseudoColor and + TrueColor visuals to co-exist and display properly at the same time.) + + This trick works ok on the 24-bit Indy video hardware, but doesn't work at + all on the O2 24-bit hardware. I guess the higher-end hardware is too + "good" for this to work (dammit.) So... I figured out the "right" way to + do this on SGIs, which is to ramp the monitor's gamma down to 0. That's + what is implemented in sgi_gamma_fade(), so we use that if we can. + + Returns: + 0: faded normally + 1: canceled by user activity + -1: unable to fade because the extension isn't supported. + */ +static int +colormap_fade (XtAppContext app, Display *dpy, + Window *saver_windows, int nwindows, + double seconds, Bool out_p, Bool from_desktop_p) +{ + int status = -1; + Colormap *window_cmaps = 0; + int i, j, k; + int cmaps_per_screen = 5; + int nscreens = ScreenCount(dpy); + int ncmaps = nscreens * cmaps_per_screen; + Colormap *fade_cmaps = 0; + Bool installed = False; + int total_ncolors; + XColor *orig_colors, *current_colors, *screen_colors, *orig_screen_colors; + int screen; + + window_cmaps = (Colormap *) calloc(sizeof(Colormap), nwindows); + if (!window_cmaps) abort(); + for (screen = 0; screen < nwindows; screen++) + { + XWindowAttributes xgwa; + XGetWindowAttributes (dpy, saver_windows[screen], &xgwa); + window_cmaps[screen] = xgwa.colormap; + } + + error_handler_hit_p = False; + + if (verbose_p > 1) + fprintf (stderr, "%s: colormap fade %s\n", + blurb(), (out_p ? "out" : "in")); + + total_ncolors = 0; + for (i = 0; i < nscreens; i++) + total_ncolors += CellsOfScreen (ScreenOfDisplay(dpy, i)); + + orig_colors = (XColor *) calloc(sizeof(XColor), total_ncolors); + current_colors = (XColor *) calloc(sizeof(XColor), total_ncolors); + + /* Get the contents of the colormap we are fading from or to. */ + screen_colors = orig_colors; + for (i = 0; i < nscreens; i++) + { + Screen *sc = ScreenOfDisplay (dpy, i); + int ncolors = CellsOfScreen (sc); + Colormap cmap = (from_desktop_p || !out_p + ? DefaultColormapOfScreen(sc) + : window_cmaps[i]); + for (j = 0; j < ncolors; j++) + screen_colors[j].pixel = j; + XQueryColors (dpy, cmap, screen_colors, ncolors); + + screen_colors += ncolors; + } + + memcpy (current_colors, orig_colors, total_ncolors * sizeof (XColor)); + + + /* Make the writable colormaps (we keep these around and reuse them.) */ + if (!fade_cmaps) + { + fade_cmaps = (Colormap *) calloc(sizeof(Colormap), ncmaps); + for (i = 0; i < nscreens; i++) + { + Visual *v = DefaultVisual(dpy, i); + Screen *s = ScreenOfDisplay(dpy, i); + if (has_writable_cells (s, v)) + for (j = 0; j < cmaps_per_screen; j++) + fade_cmaps[(i * cmaps_per_screen) + j] = + XCreateColormap (dpy, RootWindowOfScreen (s), v, AllocAll); + } + } + + /* Run the animation at the maximum frame rate in the time allotted. */ + { + double start_time = double_time(); + double end_time = start_time + seconds; + double prev = 0; + double now; + int frames = 0; + double max = 1/60.0; /* max FPS */ + while ((now = double_time()) < end_time) + { + double ratio = (end_time - now) / seconds; + if (!out_p) ratio = 1-ratio; + + /* For each screen, compute the current value of each color... + */ + orig_screen_colors = orig_colors; + screen_colors = current_colors; + for (j = 0; j < nscreens; j++) + { + int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j)); + for (k = 0; k < ncolors; k++) + { + /* This doesn't take into account the relative luminance of the + RGB components (0.299, 0.587, and 0.114 at gamma 2.2) but + the difference is imperceptible for this application... */ + screen_colors[k].red = orig_screen_colors[k].red * ratio; + screen_colors[k].green = orig_screen_colors[k].green * ratio; + screen_colors[k].blue = orig_screen_colors[k].blue * ratio; + } + screen_colors += ncolors; + orig_screen_colors += ncolors; + } + + /* Put the colors into the maps... + */ + screen_colors = current_colors; + for (j = 0; j < nscreens; j++) + { + int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j)); + for (k = 0; k < cmaps_per_screen; k++) + { + Colormap c = fade_cmaps[j * cmaps_per_screen + k]; + if (c) + XStoreColors (dpy, c, screen_colors, ncolors); + } + screen_colors += ncolors; + } + + /* Put the maps on the screens, and then take the windows off the + screen. (only need to do this the first time through the loop.) + */ + if (!installed) + { + for (j = 0; j < ncmaps; j++) + if (fade_cmaps[j]) + XInstallColormap (dpy, fade_cmaps[j]); + installed = True; + + if (!out_p) + for (j = 0; j < nwindows; j++) + { + XUnmapWindow (dpy, saver_windows[j]); + XClearWindow (dpy, saver_windows[j]); + } + } + + XSync (dpy, False); + + if (error_handler_hit_p) + goto DONE; + if (user_active_p (app, dpy, out_p)) + { + status = 1; /* user activity status code */ + goto DONE; + } + frames++; + + if (now < prev + max) + usleep (1000000 * (prev + max - now)); + prev = now; + } + + if (verbose_p > 1) + fprintf (stderr, "%s: %.0f FPS\n", blurb(), frames / (now - start_time)); + } + + status = 0; /* completed fade with no user activity */ + + DONE: + + if (orig_colors) free (orig_colors); + if (current_colors) free (current_colors); + + /* If we've been given windows to raise after blackout, raise them before + releasing the colormaps. + */ + if (out_p) + { + for (i = 0; i < nwindows; i++) + { + XClearWindow (dpy, saver_windows[i]); + XMapRaised (dpy, saver_windows[i]); + } + XSync(dpy, False); + } + + /* Now put the target maps back. + If we're fading out, use the given cmap (or the default cmap, if none.) + If we're fading in, always use the default cmap. + */ + for (i = 0; i < nscreens; i++) + { + Colormap cmap = window_cmaps[i]; + if (!cmap || !out_p) + cmap = DefaultColormap(dpy, i); + XInstallColormap (dpy, cmap); + } + + /* The fade (in or out) is complete, so we don't need the black maps on + stage any more. + */ + for (i = 0; i < ncmaps; i++) + if (fade_cmaps[i]) + { + XUninstallColormap(dpy, fade_cmaps[i]); + XFreeColormap(dpy, fade_cmaps[i]); + fade_cmaps[i] = 0; + } + free (window_cmaps); + free(fade_cmaps); + fade_cmaps = 0; + + if (error_handler_hit_p) status = -1; + return status; +} + + +/**************************************************************************** + + SGI gamma fading + + ****************************************************************************/ + +#ifdef HAVE_SGI_VC_EXTENSION + +# include + +struct screen_sgi_gamma_info { + int gamma_map; /* ??? always using 0 */ + int nred, ngreen, nblue; + unsigned short *red1, *green1, *blue1; + unsigned short *red2, *green2, *blue2; + int gamma_size; + int gamma_precision; + Bool alpha_p; +}; + + +static void sgi_whack_gamma(Display *dpy, int screen, + struct screen_sgi_gamma_info *info, float ratio); + +/* Returns: + 0: faded normally + 1: canceled by user activity + -1: unable to fade because the extension isn't supported. + */ +static int +sgi_gamma_fade (XtAppContext app, Display *dpy, + Window *saver_windows, int nwindows, + double seconds, Bool out_p) +{ + int nscreens = ScreenCount(dpy); + struct timeval then, now; + int i, screen; + int status = -1; + struct screen_sgi_gamma_info *info = (struct screen_sgi_gamma_info *) + calloc(nscreens, sizeof(*info)); + + if (verbose_p > 1) + fprintf (stderr, "%s: sgi fade %s\n", + blurb(), (out_p ? "out" : "in")); + + /* Get the current gamma maps for all screens. + Bug out and return -1 if we can't get them for some screen. + */ + for (screen = 0; screen < nscreens; screen++) + { + if (!XSGIvcQueryGammaMap(dpy, screen, info[screen].gamma_map, + &info[screen].gamma_size, + &info[screen].gamma_precision, + &info[screen].alpha_p)) + goto FAIL; + + if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map, + XSGIVC_COMPONENT_RED, + &info[screen].nred, &info[screen].red1)) + goto FAIL; + if (! XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map, + XSGIVC_COMPONENT_GREEN, + &info[screen].ngreen, &info[screen].green1)) + goto FAIL; + if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map, + XSGIVC_COMPONENT_BLUE, + &info[screen].nblue, &info[screen].blue1)) + goto FAIL; + + if (info[screen].gamma_precision == 8) /* Scale it up to 16 bits. */ + { + int j; + for(j = 0; j < info[screen].nred; j++) + info[screen].red1[j] = + ((info[screen].red1[j] << 8) | info[screen].red1[j]); + for(j = 0; j < info[screen].ngreen; j++) + info[screen].green1[j] = + ((info[screen].green1[j] << 8) | info[screen].green1[j]); + for(j = 0; j < info[screen].nblue; j++) + info[screen].blue1[j] = + ((info[screen].blue1[j] << 8) | info[screen].blue1[j]); + } + + info[screen].red2 = (unsigned short *) + malloc(sizeof(*info[screen].red2) * (info[screen].nred+1)); + info[screen].green2 = (unsigned short *) + malloc(sizeof(*info[screen].green2) * (info[screen].ngreen+1)); + info[screen].blue2 = (unsigned short *) + malloc(sizeof(*info[screen].blue2) * (info[screen].nblue+1)); + } + +#ifdef GETTIMEOFDAY_TWO_ARGS + gettimeofday(&then, &tzp); +#else + gettimeofday(&then); +#endif + + /* If we're fading in (from black), then first crank the gamma all the + way down to 0, then take the windows off the screen. + */ + if (!out_p) + { + for (screen = 0; screen < nscreens; screen++) + sgi_whack_gamma(dpy, screen, &info[screen], 0.0); + + for (screen = 0; screen < nwindows; screen++) + { + XUnmapWindow (dpy, saver_windows[screen]); + XClearWindow (dpy, saver_windows[screen]); + XSync(dpy, False); + } + } + + /* Run the animation at the maximum frame rate in the time allotted. */ + { + double start_time = double_time(); + double end_time = start_time + seconds; + double prev = 0; + double now; + int frames = 0; + double max = 1/60.0; /* max FPS */ + while ((now = double_time()) < end_time) + { + double ratio = (end_time - now) / seconds; + if (!out_p) ratio = 1-ratio; + + for (screen = 0; screen < nwindows; screen++) + sgi_whack_gamma (dpy, screen, &info[screen], ratio); + + if (error_handler_hit_p) + goto FAIL; + if (user_active_p (app, dpy, out_p)) + { + status = 1; /* user activity status code */ + goto DONE; + } + frames++; + + if (now < prev + max) + usleep (1000000 * (prev + max - now)); + prev = now; + } + + if (verbose_p > 1) + fprintf (stderr, "%s: %.0f FPS\n", blurb(), frames / (now - start_time)); + } + + status = 0; /* completed fade with no user activity */ + + DONE: + + if (out_p) + { + for (screen = 0; screen < nwindows; screen++) + { + XClearWindow (dpy, saver_windows[screen]); + XMapRaised (dpy, saver_windows[screen]); + } + XSync(dpy, False); + } + + /* I can't explain this; without this delay, we get a flicker. + I suppose there's some lossage with stale bits being in the + hardware frame buffer or something, and this delay gives it + time to flush out. This sucks! */ + usleep(100000); /* 1/10th second */ + + for (screen = 0; screen < nscreens; screen++) + sgi_whack_gamma(dpy, screen, &info[screen], 1.0); + XSync(dpy, False); + + FAIL: + for (screen = 0; screen < nscreens; screen++) + { + if (info[screen].red1) free (info[screen].red1); + if (info[screen].green1) free (info[screen].green1); + if (info[screen].blue1) free (info[screen].blue1); + if (info[screen].red2) free (info[screen].red2); + if (info[screen].green2) free (info[screen].green2); + if (info[screen].blue2) free (info[screen].blue2); + } + free(info); + + if (verbose_p > 1 && status) + fprintf (stderr, "%s: SGI fade %s failed\n", + blurb(), (out_p ? "out" : "in")); + + if (error_handler_hit_p) status = -1; + return status; +} + +static void +sgi_whack_gamma (Display *dpy, int screen, struct screen_sgi_gamma_info *info, + float ratio) +{ + int k; + + if (ratio < 0) ratio = 0; + if (ratio > 1) ratio = 1; + for (k = 0; k < info->gamma_size; k++) + { + info->red2[k] = info->red1[k] * ratio; + info->green2[k] = info->green1[k] * ratio; + info->blue2[k] = info->blue1[k] * ratio; + } + + XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nred, + XSGIVC_MComponentRed, info->red2); + XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->ngreen, + XSGIVC_MComponentGreen, info->green2); + XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nblue, + XSGIVC_MComponentBlue, info->blue2); + XSync(dpy, False); +} + +#endif /* HAVE_SGI_VC_EXTENSION */ + + +/**************************************************************************** + + XFree86 gamma fading + + ****************************************************************************/ + +#ifdef HAVE_XF86VMODE_GAMMA + +#include + +typedef struct { + XF86VidModeGamma vmg; + int size; + unsigned short *r, *g, *b; +} xf86_gamma_info; + +static int xf86_check_gamma_extension (Display *dpy); +static Bool xf86_whack_gamma (Display *dpy, int screen, + xf86_gamma_info *ginfo, float ratio); + +/* Returns: + 0: faded normally + 1: canceled by user activity + -1: unable to fade because the extension isn't supported. + */ +static int +xf86_gamma_fade (XtAppContext app, Display *dpy, + Window *saver_windows, int nwindows, + double seconds, Bool out_p) +{ + int nscreens = ScreenCount(dpy); + int screen; + int status = -1; + xf86_gamma_info *info = 0; + + static int ext_ok = -1; + + if (verbose_p > 1) + fprintf (stderr, "%s: xf86 fade %s\n", + blurb(), (out_p ? "out" : "in")); + + /* Only probe the extension once: the answer isn't going to change. */ + if (ext_ok == -1) + ext_ok = xf86_check_gamma_extension (dpy); + + /* If this server doesn't have the gamma extension, bug out. */ + if (ext_ok == 0) + goto FAIL; + +# ifndef HAVE_XF86VMODE_GAMMA_RAMP + if (ext_ok == 2) ext_ok = 1; /* server is newer than client! */ +# endif + + info = (xf86_gamma_info *) calloc(nscreens, sizeof(*info)); + + /* Get the current gamma maps for all screens. + Bug out and return -1 if we can't get them for some screen. + */ + for (screen = 0; screen < nscreens; screen++) + { + if (ext_ok == 1) /* only have gamma parameter, not ramps. */ + { + if (!XF86VidModeGetGamma(dpy, screen, &info[screen].vmg)) + goto FAIL; + } +# ifdef HAVE_XF86VMODE_GAMMA_RAMP + else if (ext_ok == 2) /* have ramps */ + { + if (!XF86VidModeGetGammaRampSize(dpy, screen, &info[screen].size)) + goto FAIL; + if (info[screen].size <= 0) + goto FAIL; + + info[screen].r = (unsigned short *) + calloc(info[screen].size, sizeof(unsigned short)); + info[screen].g = (unsigned short *) + calloc(info[screen].size, sizeof(unsigned short)); + info[screen].b = (unsigned short *) + calloc(info[screen].size, sizeof(unsigned short)); + + if (!(info[screen].r && info[screen].g && info[screen].b)) + goto FAIL; + +# if 0 + if (verbose_p > 1 && out_p) + { + int i; + fprintf (stderr, "%s: initial gamma ramps, size %d:\n", + blurb(), info[screen].size); + fprintf (stderr, "%s: R:", blurb()); + for (i = 0; i < info[screen].size; i++) + fprintf (stderr, " %d", info[screen].r[i]); + fprintf (stderr, "\n%s: G:", blurb()); + for (i = 0; i < info[screen].size; i++) + fprintf (stderr, " %d", info[screen].g[i]); + fprintf (stderr, "\n%s: B:", blurb()); + for (i = 0; i < info[screen].size; i++) + fprintf (stderr, " %d", info[screen].b[i]); + fprintf (stderr, "\n"); + } +# endif /* 0 */ + + if (!XF86VidModeGetGammaRamp(dpy, screen, info[screen].size, + info[screen].r, + info[screen].g, + info[screen].b)) + goto FAIL; + } +# endif /* HAVE_XF86VMODE_GAMMA_RAMP */ + else + abort(); + } + + /* If we're fading in (from black), then first crank the gamma all the + way down to 0, then take the windows off the screen. + */ + if (!out_p) + { + for (screen = 0; screen < nscreens; screen++) + xf86_whack_gamma(dpy, screen, &info[screen], 0.0); + for (screen = 0; screen < nwindows; screen++) + { + XUnmapWindow (dpy, saver_windows[screen]); + XClearWindow (dpy, saver_windows[screen]); + XSync(dpy, False); + } + } + + /* Run the animation at the maximum frame rate in the time allotted. */ + { + double start_time = double_time(); + double end_time = start_time + seconds; + double prev = 0; + double now; + int frames = 0; + double max = 1/60.0; /* max FPS */ + while ((now = double_time()) < end_time) + { + double ratio = (end_time - now) / seconds; + if (!out_p) ratio = 1-ratio; + + for (screen = 0; screen < nscreens; screen++) + xf86_whack_gamma (dpy, screen, &info[screen], ratio); + + if (error_handler_hit_p) + goto FAIL; + if (user_active_p (app, dpy, out_p)) + { + status = 1; /* user activity status code */ + goto DONE; + } + frames++; + + if (now < prev + max) + usleep (1000000 * (prev + max - now)); + prev = now; + } + + if (verbose_p > 1) + fprintf (stderr, "%s: %.0f FPS\n", blurb(), frames / (now - start_time)); + } + + status = 0; /* completed fade with no user activity */ + + DONE: + + if (out_p) + { + for (screen = 0; screen < nwindows; screen++) + { + XClearWindow (dpy, saver_windows[screen]); + XMapRaised (dpy, saver_windows[screen]); + } + XSync(dpy, False); + } + + /* I can't explain this; without this delay, we get a flicker. + I suppose there's some lossage with stale bits being in the + hardware frame buffer or something, and this delay gives it + time to flush out. This sucks! */ + usleep(100000); /* 1/10th second */ + + for (screen = 0; screen < nscreens; screen++) + xf86_whack_gamma(dpy, screen, &info[screen], 1.0); + XSync(dpy, False); + + FAIL: + if (info) + { + for (screen = 0; screen < nscreens; screen++) + { + if (info[screen].r) free(info[screen].r); + if (info[screen].g) free(info[screen].g); + if (info[screen].b) free(info[screen].b); + } + free(info); + } + + if (verbose_p > 1 && status) + fprintf (stderr, "%s: xf86 fade %s failed\n", + blurb(), (out_p ? "out" : "in")); + + if (error_handler_hit_p) status = -1; + return status; +} + + +static Bool +safe_XF86VidModeQueryVersion (Display *dpy, int *majP, int *minP) +{ + Bool result; + XErrorHandler old_handler; + XSync (dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + + result = XF86VidModeQueryVersion (dpy, majP, minP); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + return (error_handler_hit_p + ? False + : result); +} + + + +/* VidModeExtension version 2.0 or better is needed to do gamma. + 2.0 added gamma values; 2.1 added gamma ramps. + */ +# define XF86_VIDMODE_GAMMA_MIN_MAJOR 2 +# define XF86_VIDMODE_GAMMA_MIN_MINOR 0 +# define XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR 2 +# define XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR 1 + + + +/* Returns 0 if gamma fading not available; 1 if only gamma value setting + is available; 2 if gamma ramps are available. + */ +static int +xf86_check_gamma_extension (Display *dpy) +{ + int event, error, major, minor; + + if (!XF86VidModeQueryExtension (dpy, &event, &error)) + return 0; /* display doesn't have the extension. */ + + if (!safe_XF86VidModeQueryVersion (dpy, &major, &minor)) + return 0; /* unable to get version number? */ + + if (major < XF86_VIDMODE_GAMMA_MIN_MAJOR || + (major == XF86_VIDMODE_GAMMA_MIN_MAJOR && + minor < XF86_VIDMODE_GAMMA_MIN_MINOR)) + return 0; /* extension is too old for gamma. */ + + if (major < XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR || + (major == XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR && + minor < XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR)) + return 1; /* extension is too old for gamma ramps. */ + + /* Copacetic */ + return 2; +} + + +/* XFree doesn't let you set gamma to a value smaller than this. + Apparently they didn't anticipate the trick I'm doing here... + */ +#define XF86_MIN_GAMMA 0.1 + + +static Bool +xf86_whack_gamma(Display *dpy, int screen, xf86_gamma_info *info, + float ratio) +{ + XErrorHandler old_handler; + XSync (dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + + if (ratio < 0) ratio = 0; + if (ratio > 1) ratio = 1; + + if (info->size == 0) /* we only have a gamma number, not a ramp. */ + { + XF86VidModeGamma g2; + + g2.red = info->vmg.red * ratio; + g2.green = info->vmg.green * ratio; + g2.blue = info->vmg.blue * ratio; + +# ifdef XF86_MIN_GAMMA + if (g2.red < XF86_MIN_GAMMA) g2.red = XF86_MIN_GAMMA; + if (g2.green < XF86_MIN_GAMMA) g2.green = XF86_MIN_GAMMA; + if (g2.blue < XF86_MIN_GAMMA) g2.blue = XF86_MIN_GAMMA; +# endif + + if (! XF86VidModeSetGamma (dpy, screen, &g2)) + return -1; + } + else + { +# ifdef HAVE_XF86VMODE_GAMMA_RAMP + + unsigned short *r, *g, *b; + int i; + r = (unsigned short *) malloc(info->size * sizeof(unsigned short)); + g = (unsigned short *) malloc(info->size * sizeof(unsigned short)); + b = (unsigned short *) malloc(info->size * sizeof(unsigned short)); + + for (i = 0; i < info->size; i++) + { + r[i] = info->r[i] * ratio; + g[i] = info->g[i] * ratio; + b[i] = info->b[i] * ratio; + } + + if (! XF86VidModeSetGammaRamp(dpy, screen, info->size, r, g, b)) + return -1; + + free (r); + free (g); + free (b); + +# else /* !HAVE_XF86VMODE_GAMMA_RAMP */ + abort(); +# endif /* !HAVE_XF86VMODE_GAMMA_RAMP */ + } + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + return status; +} + +#endif /* HAVE_XF86VMODE_GAMMA */ + + +/**************************************************************************** + + RANDR gamma fading + + **************************************************************************** + + + Dec 2020: I noticed that gamma fading was not working on a Raspberry Pi + with Raspbian 10.6, and wrote this under the hypothesis that the XF86 + gamma fade code was failing and maybe the RANDR version would work better. + Then I discovered that gamma simply isn't supported by the Raspberry Pi + HDMI driver: + + https://github.com/raspberrypi/firmware/issues/1274 + + I should have tried this first and seen it not work: + + xrandr --output HDMI-1 --brightness .1 + + Since I still don't have an answer to the question of whether the XF86 + gamma fading method works on modern Linux systems that also have RANDR, + I'm leaving this new code turned off for now, as it is largely untested. + The new code would be useful if: + + A) The XF86 way doesn't work but the RANDR way does, or + B) There exist systems that have RANDR but do not have XF86. + + But until Raspberry Pi supports gamma, both gamma methods fail to work + for far too many users for them to be used in XScreenSaver. + */ +#ifdef HAVE_RANDR_12 + +# include + +typedef struct { + RRCrtc crtc; + Bool enabled_p; + XRRCrtcGamma *gamma; +} randr_gamma_info; + + +static int +randr_check_gamma_extension (Display *dpy) +{ + int event, error, major, minor; + if (! XRRQueryExtension (dpy, &event, &error)) + return 0; + + if (! XRRQueryVersion (dpy, &major, &minor)) { + if (verbose_p > 1) fprintf (stderr, "%s: no randr ext\n", blurb()); + return 0; + } + + /* Reject if < 1.5. It's possible that 1.2 - 1.4 work, but untested. */ + if (major < 1 || (major == 1 && minor < 5)) { + if (verbose_p > 1) fprintf (stderr, "%s: randr ext only version %d.%d\n", + blurb(), major, minor); + return 0; + } + + return 1; +} + + +static void randr_whack_gamma (Display *dpy, int screen, + randr_gamma_info *ginfo, float ratio); + +/* Returns: + 0: faded normally + 1: canceled by user activity + -1: unable to fade because the extension isn't supported. + */ +static int +randr_gamma_fade (XtAppContext app, Display *dpy, + Window *saver_windows, int nwindows, + double seconds, Bool out_p) +{ + int xsc = ScreenCount (dpy); + int nscreens = 0; + int j, screen; + int status = -1; + randr_gamma_info *info = 0; + + static int ext_ok = -1; + + if (verbose_p > 1) + fprintf (stderr, "%s: randr fade %s\n", + blurb(), (out_p ? "out" : "in")); + + /* Only probe the extension once: the answer isn't going to change. */ + if (ext_ok == -1) + ext_ok = randr_check_gamma_extension (dpy); + + /* If this server doesn't have the RANDR extension, bug out. */ + if (ext_ok == 0) + goto FAIL; + + /* Add up the virtual screens on each X screen. */ + for (screen = 0; screen < xsc; screen++) + { + XRRScreenResources *res = + XRRGetScreenResources (dpy, RootWindow (dpy, screen)); + nscreens += res->noutput; + XRRFreeScreenResources (res); + } + + if (nscreens <= 0) + goto FAIL; + + info = (randr_gamma_info *) calloc(nscreens, sizeof(*info)); + + /* Get the current gamma maps for all screens. + Bug out and return -1 if we can't get them for some screen. + */ + for (screen = 0, j = 0; screen < xsc; screen++) + { + XRRScreenResources *res = + XRRGetScreenResources (dpy, RootWindow (dpy, screen)); + int k; + for (k = 0; k < res->noutput; k++, j++) + { + XRROutputInfo *rroi = XRRGetOutputInfo (dpy, res, res->outputs[j]); + RRCrtc crtc = (rroi->crtc ? rroi->crtc : + rroi->ncrtc ? rroi->crtcs[0] : 0); + + info[j].crtc = crtc; + info[j].gamma = XRRGetCrtcGamma (dpy, crtc); + + /* #### is this test sufficient? */ + info[j].enabled_p = (rroi->connection != RR_Disconnected); + +# if 0 + if (verbose_p > 1 && out_p) + { + int m; + fprintf (stderr, "%s: initial gamma ramps, size %d:\n", + blurb(), info[j].gamma->size); + fprintf (stderr, "%s: R:", blurb()); + for (m = 0; m < info[j].gamma->size; m++) + fprintf (stderr, " %d", info[j].gamma->red[m]); + fprintf (stderr, "\n%s: G:", blurb()); + for (m = 0; m < info[j].gamma->size; m++) + fprintf (stderr, " %d", info[j].gamma->green[m]); + fprintf (stderr, "\n%s: B:", blurb()); + for (m = 0; m < info[j].gamma->size; m++) + fprintf (stderr, " %d", info[j].gamma->blue[m]); + fprintf (stderr, "\n"); + } +# endif /* 0 */ + + XRRFreeOutputInfo (rroi); + } + XRRFreeScreenResources (res); + } + + /* If we're fading in (from black), then first crank the gamma all the + way down to 0, then take the windows off the screen. + */ + if (!out_p) + { + for (screen = 0; screen < nscreens; screen++) + randr_whack_gamma(dpy, screen, &info[screen], 0.0); + for (screen = 0; screen < nwindows; screen++) + { + XUnmapWindow (dpy, saver_windows[screen]); + XClearWindow (dpy, saver_windows[screen]); + XSync(dpy, False); + } + } + + /* Run the animation at the maximum frame rate in the time allotted. */ + { + double start_time = double_time(); + double end_time = start_time + seconds; + double prev = 0; + double now; + int frames = 0; + double max = 1/60.0; /* max FPS */ + while ((now = double_time()) < end_time) + { + double ratio = (end_time - now) / seconds; + if (!out_p) ratio = 1-ratio; + + for (screen = 0; screen < nwindows; screen++) + { + if (!info[screen].enabled_p) + continue; + + randr_whack_gamma (dpy, screen, &info[screen], ratio); + } + + if (error_handler_hit_p) + goto FAIL; + if (user_active_p (app, dpy, out_p)) + { + status = 1; /* user activity status code */ + goto DONE; + } + frames++; + + if (now < prev + max) + usleep (1000000 * (prev + max - now)); + prev = now; + } + + if (verbose_p > 1) + fprintf (stderr, "%s: %.0f FPS\n", blurb(), frames / (now - start_time)); + } + + status = 0; /* completed fade with no user activity */ + + DONE: + + if (out_p) + { + for (screen = 0; screen < nwindows; screen++) + { + XClearWindow (dpy, saver_windows[screen]); + XMapRaised (dpy, saver_windows[screen]); + } + XSync(dpy, False); + } + + /* I can't explain this; without this delay, we get a flicker. + I suppose there's some lossage with stale bits being in the + hardware frame buffer or something, and this delay gives it + time to flush out. This sucks! */ + /* #### That comment was about XF86, not verified with randr. */ + usleep(100000); /* 1/10th second */ + + for (screen = 0; screen < nscreens; screen++) + randr_whack_gamma (dpy, screen, &info[screen], 1.0); + XSync(dpy, False); + + FAIL: + if (info) + { + for (screen = 0; screen < nscreens; screen++) + { + if (info[screen].gamma) XRRFreeGamma (info[screen].gamma); + } + free(info); + } + + if (verbose_p > 1 && status) + fprintf (stderr, "%s: randr fade %s failed\n", + blurb(), (out_p ? "out" : "in")); + + return status; +} + + +static void +randr_whack_gamma (Display *dpy, int screen, randr_gamma_info *info, + float ratio) +{ + XErrorHandler old_handler; + XRRCrtcGamma *g2; + int i; + + XSync (dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + + if (ratio < 0) ratio = 0; + if (ratio > 1) ratio = 1; + + g2 = XRRAllocGamma (info->gamma->size); + for (i = 0; i < info->gamma->size; i++) + { + g2->red[i] = ratio * info->gamma->red[i]; + g2->green[i] = ratio * info->gamma->green[i]; + g2->blue[i] = ratio * info->gamma->blue[i]; + } + + XRRSetCrtcGamma (dpy, info->crtc, g2); + XRRFreeGamma (g2); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + return 0; +} + +#endif /* HAVE_RANDR_12 */ + + +/**************************************************************************** + + XSHM screen-shot fading + + ****************************************************************************/ + +typedef struct { + GC gc; + Window window; + Pixmap screenshot; + XImage *src, *intermediate; +} xshm_fade_info; + + +static int xshm_whack (Display *, XShmSegmentInfo *, + xshm_fade_info *, float ratio); + +/* Returns: + 0: faded normally + 1: canceled by user activity + -1: unknown error + */ +static int +xshm_fade (XtAppContext app, Display *dpy, + Window *saver_windows, int nwindows, double seconds, + Bool out_p, Bool from_desktop_p, fade_state *state) +{ + int screen; + int status = -1; + xshm_fade_info *info = 0; + XShmSegmentInfo shm_info; + Window saver_window = 0; + XErrorHandler old_handler = 0; + + XSync (dpy, False); + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + error_handler_hit_p = False; + + if (verbose_p > 1) + fprintf (stderr, "%s: SHM fade %s\n", + blurb(), (out_p ? "out" : "in")); + + info = (xshm_fade_info *) calloc(nwindows, sizeof(*info)); + if (!info) goto FAIL; + + saver_window = find_screensaver_window (dpy, 0); + if (!saver_window) goto FAIL; + + /* Retrieve a screenshot of the area covered by each window. + Windows might not be mapped. + Bug out and return -1 if we can't get one for some screen. + */ + + for (screen = 0; screen < nwindows; screen++) + { + XWindowAttributes xgwa; + Window root; + XGCValues gcv; + GC gc; + unsigned long attrmask = 0; + XSetWindowAttributes attrs; + + XGetWindowAttributes (dpy, saver_windows[screen], &xgwa); + root = RootWindowOfScreen (xgwa.screen); + + info[screen].src = + create_xshm_image (dpy, xgwa.visual, xgwa.depth, + ZPixmap, &shm_info, xgwa.width, xgwa.height); + if (!info[screen].src) goto FAIL; + + info[screen].intermediate = + create_xshm_image (dpy, xgwa.visual, xgwa.depth, + ZPixmap, &shm_info, xgwa.width, xgwa.height); + if (!info[screen].intermediate) goto FAIL; + + if (!out_p) + { + /* If we are fading in, retrieve the saved screenshot from + before we faded out. */ + if (state->nscreens <= screen) goto FAIL; + info[screen].screenshot = state->screenshots[screen]; + } + else + { + /* Create a pixmap and grab a screenshot into it. */ + info[screen].screenshot = + XCreatePixmap (dpy, root, xgwa.width, xgwa.height, xgwa.depth); + if (!info[screen].screenshot) goto FAIL; + + gcv.function = GXcopy; + gcv.subwindow_mode = IncludeInferiors; + gc = XCreateGC (dpy, root, GCFunction | GCSubwindowMode, &gcv); + XCopyArea (dpy, root, info[screen].screenshot, gc, + xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0); + XFreeGC (dpy, gc); + } + + /* Create the fader window for the animation. */ + attrmask = CWOverrideRedirect; + attrs.override_redirect = True; + info[screen].window = + XCreateWindow (dpy, root, xgwa.x, xgwa.y, + xgwa.width, xgwa.height, xgwa.border_width, xgwa.depth, + InputOutput, xgwa.visual, + attrmask, &attrs); + if (!info[screen].window) goto FAIL; + /* XSelectInput (dpy, info[screen].window, + KeyPressMask | ButtonPressMask); */ + + /* Copy the screenshot pixmap to the source image */ + if (! get_xshm_image (dpy, info[screen].screenshot, info[screen].src, + 0, 0, ~0L, &shm_info)) + goto FAIL; + + gcv.function = GXcopy; + info[screen].gc = XCreateGC (dpy, info[screen].window, GCFunction, &gcv); + } + + /* If we're fading out from the desktop, save our screen shots for later use. + But not if we're fading out from the savers to black. In that case we + don't want to overwrite the desktop screenshot with the current screenshot + which is of the final frames of the just-killed graphics hacks. */ + if (from_desktop_p) + { + if (!out_p) abort(); + for (screen = 0; screen < state->nscreens; screen++) + if (state->screenshots[screen]) + XFreePixmap (dpy, state->screenshots[screen]); + if (state->screenshots) + free (state->screenshots); + state->nscreens = nwindows; + state->screenshots = calloc (nwindows, sizeof(*state->screenshots)); + if (!state->screenshots) + state->nscreens = 0; + for (screen = 0; screen < state->nscreens; screen++) + state->screenshots[screen] = info[screen].screenshot; + } + + for (screen = 0; screen < nwindows; screen++) + { + if (out_p) + /* Copy the screenshot to the fader window */ + XSetWindowBackgroundPixmap (dpy, info[screen].window, + info[screen].screenshot); + else + { + XSetWindowBackgroundPixmap (dpy, info[screen].window, None); + XSetWindowBackground (dpy, info[screen].window, BlackPixel (dpy, 0)); + } + + XMapRaised (dpy, info[screen].window); + + /* Now that we have mapped the screenshot on the fader windows, + take the saver windows off the screen. */ + if (out_p) + { + XUnmapWindow (dpy, saver_windows[screen]); + XClearWindow (dpy, saver_windows[screen]); + } + } + + /* Run the animation at the maximum frame rate in the time allotted. */ + { + double start_time = double_time(); + double end_time = start_time + seconds; + double prev = 0; + double now; + int frames = 0; + double max = 1/60.0; /* max FPS */ + while ((now = double_time()) < end_time) + { + double ratio = (end_time - now) / seconds; + if (!out_p) ratio = 1-ratio; + + for (screen = 0; screen < nwindows; screen++) + if (xshm_whack (dpy, &shm_info, &info[screen], ratio)) + goto FAIL; + + if (error_handler_hit_p) + goto FAIL; + if (user_active_p (app, dpy, out_p)) + { + status = 1; /* user activity status code */ + goto DONE; + } + frames++; + + if (now < prev + max) + usleep (1000000 * (prev + max - now)); + prev = now; + } + + if (verbose_p > 1) + fprintf (stderr, "%s: %.0f FPS\n", blurb(), frames / (now - start_time)); + } + + status = 0; /* completed fade with no user activity */ + + DONE: + + /* If we're fading out, we have completed the transition from what was + on the screen to black, using our fader windows. Now raise the saver + windows and take the fader windows off the screen. Since they're both + black, that will be imperceptible. + */ + if (out_p) + { + for (screen = 0; screen < nwindows; screen++) + { + XClearWindow (dpy, saver_windows[screen]); + XMapRaised (dpy, saver_windows[screen]); + if (info[screen].window) + XUnmapWindow (dpy, info[screen].window); + } + } + + XSync (dpy, False); + + FAIL: + + /* After fading in, take the saver windows off the screen before + destroying the occluding screenshot windows. */ + if (!out_p) + { + for (screen = 0; screen < nwindows; screen++) + { + XUnmapWindow (dpy, saver_windows[screen]); + XClearWindow (dpy, saver_windows[screen]); + } + } + + if (info) + { + for (screen = 0; screen < nwindows; screen++) + { + if (info[screen].src) + destroy_xshm_image (dpy, info[screen].src, &shm_info); + if (info[screen].intermediate) + destroy_xshm_image (dpy, info[screen].intermediate, &shm_info); + if (info[screen].window) + XDestroyWindow (dpy, info[screen].window); + if (info[screen].gc) + XFreeGC (dpy, info[screen].gc); + } + free (info); + } + + /* If fading in, delete the screenshot pixmaps, and the list of them. */ + if (!out_p && saver_window) + { + for (screen = 0; screen < state->nscreens; screen++) + if (state->screenshots[screen]) + XFreePixmap (dpy, state->screenshots[screen]); + if (state->screenshots) + free (state->screenshots); + state->nscreens = 0; + state->screenshots = 0; + } + + XSync (dpy, False); + XSetErrorHandler (old_handler); + + if (error_handler_hit_p) status = -1; + if (verbose_p > 1 && status) + fprintf (stderr, "%s: SHM fade %s failed\n", + blurb(), (out_p ? "out" : "in")); + + return status; +} + + +static int +xshm_whack (Display *dpy, XShmSegmentInfo *shm_info, + xshm_fade_info *info, float ratio) +{ + unsigned char *inbits = (unsigned char *) info->src->data; + unsigned char *outbits = (unsigned char *) info->intermediate->data; + unsigned char *end = (outbits + + info->intermediate->bytes_per_line * + info->intermediate->height); + unsigned char ramp[256]; + int i; + + XSync (dpy, False); + + if (ratio < 0) ratio = 0; + if (ratio > 1) ratio = 1; + + for (i = 0; i < sizeof(ramp); i++) + ramp[i] = i * ratio; + while (outbits < end) + *outbits++ = ramp[*inbits++]; + + put_xshm_image (dpy, info->window, info->gc, info->intermediate, 0, 0, 0, 0, + info->intermediate->width, info->intermediate->height, + shm_info); + XSync (dpy, False); + return 0; +} diff --git a/driver/fade.h b/driver/fade.h new file mode 100644 index 0000000..56725b5 --- /dev/null +++ b/driver/fade.h @@ -0,0 +1,20 @@ +/* xscreensaver, Copyright © 1992-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifndef __FADE_H__ +#define __FADE_H__ + +/* Returns true if canceled by user activity. */ +extern Bool fade_screens (XtAppContext app, Display *dpy, + Window *black_windows, int nwindows, + double seconds, Bool out_p, Bool from_desktop_p, + void **closureP); +#endif /* __FADE_H__ */ diff --git a/driver/passwd-helper.c b/driver/passwd-helper.c index a3a6b92..4b17c63 100644 --- a/driver/passwd-helper.c +++ b/driver/passwd-helper.c @@ -1,6 +1,6 @@ /* passwd-helper.c --- verifying typed passwords with external helper program + * xscreensaver, Copyright © 1993-2021 Jamie Zawinski * written by Olaf Kirch - * xscreensaver, Copyright (c) 1993-2005 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -11,6 +11,18 @@ * implied warranty. */ + +/***************************************************************************** + + I strongly suspect that this code has not been used in decades, and I + am considering removing it. These details should be hidden behind PAM. + If you are using this code, email me and tell me why. -- jwz, Feb 2021 + + *****************************************************************************/ + +#error "email jwz@jwz.org about passwd-helper.c" + + /* The idea here is to be able to run xscreensaver without any setuid bits. * Password verification happens through an external program that you feed * your password to on stdin. The external command is invoked with a user @@ -35,18 +47,6 @@ #ifndef NO_LOCKING /* whole file */ -#include /* not used for much... */ - -/* This file doesn't need the Xt headers, so stub these types out... */ -#undef XtPointer -#define XtAppContext void* -#define XrmDatabase void* -#define XtIntervalId void* -#define XtPointer void* -#define Widget void* - -#include "xscreensaver.h" - #include #ifdef HAVE_UNISTD_H # include @@ -57,11 +57,14 @@ #include #include #include - #include +#include "blurb.h" +#include "auth.h" + + static int -ext_run (const char *user, const char *typed_passwd, int verbose_p) +ext_run (const char *user, const char *typed_passwd) { int pfd[2], status; pid_t pid; @@ -70,10 +73,7 @@ ext_run (const char *user, const char *typed_passwd, int verbose_p) return 0; if (verbose_p) - fprintf (stderr, "%s: ext_run (%s, %s)\n", - blurb(), PASSWD_HELPER_PROGRAM, user); - - block_sigchld(); + fprintf (stderr, "%s: EXT: %s\n", blurb(), PASSWD_HELPER_PROGRAM); if ((pid = fork()) < 0) { close(pfd[0]); @@ -89,7 +89,7 @@ ext_run (const char *user, const char *typed_passwd, int verbose_p) /* Helper is invoked as helper service-name [user] */ execlp(PASSWD_HELPER_PROGRAM, PASSWD_HELPER_PROGRAM, "xscreensaver", user, NULL); if (verbose_p) - fprintf(stderr, "%s: %s\n", PASSWD_HELPER_PROGRAM, + fprintf(stderr, "%s: EXT: %s\n", PASSWD_HELPER_PROGRAM, strerror(errno)); exit(1); } @@ -106,14 +106,11 @@ ext_run (const char *user, const char *typed_passwd, int verbose_p) if (errno == EINTR) continue; if (verbose_p) - fprintf(stderr, "%s: ext_run: waitpid failed: %s\n", + fprintf(stderr, "%s: EXT: waitpid failed: %s\n", blurb(), strerror(errno)); - unblock_sigchld(); return 0; } - unblock_sigchld(); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return 0; return 1; @@ -126,37 +123,36 @@ ext_run (const char *user, const char *typed_passwd, int verbose_p) to root. */ int -ext_passwd_valid_p (const char *typed_passwd, int verbose_p) +ext_passwd_valid_p (void *closure, const char *typed_passwd) { struct passwd *pw; int res = 0; if ((pw = getpwuid(getuid())) != NULL) - res = ext_run (pw->pw_name, typed_passwd, verbose_p); + res = ext_run (pw->pw_name, typed_passwd); endpwent(); #ifdef ALLOW_ROOT_PASSWD if (!res) - res = ext_run ("root", typed_passwd, verbose_p); + res = ext_run ("root", typed_passwd); #endif /* ALLOW_ROOT_PASSWD */ return res; } -int -ext_priv_init (int argc, char **argv, int verbose_p) +Bool +ext_priv_init (void) { /* Make sure the passwd helper exists */ if (access(PASSWD_HELPER_PROGRAM, X_OK) < 0) { fprintf(stderr, - "%s: warning: %s does not exist.\n" - "%s: password authentication via " - "external helper will not work.\n", + "%s: EXT: warning: %s does not exist.\n" + "%s: EXT password authentication will not work.\n", blurb(), PASSWD_HELPER_PROGRAM, blurb()); - return 0; + return False; } - return 1; + return True; } #endif /* NO_LOCKING -- whole file */ diff --git a/driver/passwd-kerberos.c b/driver/passwd-kerberos.c index 202e0eb..7d94ebd 100644 --- a/driver/passwd-kerberos.c +++ b/driver/passwd-kerberos.c @@ -1,6 +1,6 @@ /* kpasswd.c --- verify kerberos passwords. - * written by Nat Lanza (magus@cs.cmu.edu) for - * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski + * xscreensaver, Copyright © 1993-2021 Jamie Zawinski + * written by Nat Lanza (magus@cs.cmu.edu) * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -11,6 +11,17 @@ * implied warranty. */ +/***************************************************************************** + + I strongly suspect that this code has not been used in decades, and I + am considering removing it. These details should be hidden behind PAM. + If you are using this code, email me and tell me why. -- jwz, Feb 2021 + + *****************************************************************************/ + +#error "email jwz@jwz.org about passwd-kerberos.c" + + #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -48,9 +59,7 @@ # include #endif /* !HAVE_KERBEROS5 */ -#if !defined(VMS) && !defined(HAVE_ADJUNCT_PASSWD) -# include -#endif +#include #ifdef __bsdi__ @@ -60,13 +69,9 @@ # endif #endif /* __bsdi__ */ -/* blargh */ -#undef Bool -#undef True -#undef False -#define Bool int -#define True 1 -#define False 0 +#include "blurb.h" +#include "auth.h" + /* The user information we need to store */ #ifdef HAVE_DARWIN @@ -78,10 +83,6 @@ static const char *tk_file; #endif /* !HAVE_DARWIN */ -/* warning suppression: duplicated in passwd.c */ -extern Bool kerberos_lock_init (int argc, char **argv, Bool verbose_p); -extern Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p); - /* Called at startup to grab user, instance, and realm information from the user's ticketfile (remember, name.inst@realm). Since we're @@ -102,7 +103,7 @@ extern Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p); We don't use the arguments we're given, though. */ Bool -kerberos_lock_init (int argc, char **argv, Bool verbose_p) +kerberos_lock_init (void) { # ifdef HAVE_DARWIN @@ -182,7 +183,7 @@ key_to_key(char *user, char *instance, char *realm, char *passwd, C_Block key) some sites. So, we do a quick, painful hack with a tmpfile. */ Bool -kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p) +kerberos_passwd_valid_p (void *closure, const char *typed_passwd) { # ifdef HAVE_DARWIN return (klNoErr == @@ -201,10 +202,10 @@ kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p) /* temporarily switch to a new ticketfile. I'm not using tmpnam() because it isn't entirely portable. this could probably be fixed with autoconf. */ - newtkfile = malloc(80 * sizeof(char)); - memset(newtkfile, 0, sizeof(newtkfile)); - - sprintf(newtkfile, "/tmp/xscrn-%i.XXXXXX", getpid()); + char *tmpdir = getenv("TMPDIR"); + if (!tmpdir || !*tmpdir) tmpdir = "/tmp"; + newtkfile = malloc (strlen(tmpdir) + 40); + sprintf (newtkfile, "%s/xscreensaver.XXXXXX", tmpdir); if( (fh = mkstemp(newtkfile)) < 0) { diff --git a/driver/passwd-pam.c b/driver/passwd-pam.c index d463bc2..87942ab 100644 --- a/driver/passwd-pam.c +++ b/driver/passwd-pam.c @@ -1,7 +1,6 @@ /* passwd-pam.c --- verifying typed passwords with PAM - * (Pluggable Authentication Modules.) - * written by Bill Nottingham (and jwz) for - * xscreensaver, Copyright (c) 1993-2017 Jamie Zawinski + * xscreensaver, Copyright © 1993-2021 Jamie Zawinski + * By Bill Nottingham and jwz. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -10,26 +9,37 @@ * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. - * - * Some PAM resources: - * - * PAM home page: - * http://www.us.kernel.org/pub/linux/libs/pam/ - * - * PAM FAQ: - * http://www.us.kernel.org/pub/linux/libs/pam/FAQ - * - * PAM Application Developers' Guide: - * http://www.us.kernel.org/pub/linux/libs/pam/Linux-PAM-html/Linux-PAM_ADG.html - * - * PAM Mailing list archives: - * http://www.linuxhq.com/lnxlists/linux-pam/ - * - * Compatibility notes, especially between Linux and Solaris: - * http://www.contrib.andrew.cmu.edu/u/shadow/pam.html - * - * The Open Group's PAM API documentation: - * http://www.opengroup.org/onlinepubs/8329799/pam_start.htm + */ + +/* PAM sucks in that there is no way to tell whether a particular service is + configured at all. That is, there is no way to tell the difference between + "authentication of the FOO service is not allowed" and "the user typed the + wrong password." + + On RedHat 5.1 systems, if a service name is not known, it defaults to being + not allowed (because the fallback service, /etc/pam.d/other, is set to + `pam_deny'.) + + On Solaris 2.6 systems, unknown services default to authenticating normally. + + So, we require that an "xscreensaver" PAM service exist. This has a bad + failure mode, however: if that service doesn't exist, then XScreenSaver + will lock the screen, but is unable to unlock. (With the non-PAM password + code, XScreenSaver can refuse to lock, because it is able to determine up + front that it was unable to retrieve the password info.) + + At startup, we check for the existence of /etc/pam.d/xscreensaver or + /etc/pam.conf, and if those don't exist, we print a warning that PAM is + probably not configured properly. This isn't *necessarily* correct, since + those files are not a part of PAM's C API, but it's how real-world systems + actually work. + + Also note that FreeBSD's implementation of PAM requires the calling process + to be running as root during the entire interactive PAM conversation: it + can't ever disavow privileges. Linux's PAM implementation uses a setuid + helper so that a non-root process can still authenticate, as is right and + proper. Consequently, XScreenSaver does not support PAM on FreeBSD. + Dear FreeBSD, get your shit together. */ #ifdef HAVE_CONFIG_H @@ -43,36 +53,17 @@ # include #endif -extern char *blurb(void); - - #include #include #include +#include #include #include #include -#include -#include -#include - -#include +#include "blurb.h" #include "auth.h" -extern sigset_t block_sigchld (void); -extern void unblock_sigchld (void); - -/* blargh */ -#undef Bool -#undef True -#undef False -#define Bool int -#define True 1 -#define False 0 - -#undef countof -#define countof(x) (sizeof((x))/sizeof(*(x))) /* Some time between Red Hat 4.2 and 7.0, the words were transposed in the various PAM_x_CRED macro names. Yay! @@ -89,10 +80,14 @@ static int pam_conversation (int nmsgs, struct pam_response **resp, void *closure); -void pam_try_unlock(saver_info *si, Bool verbose_p, - Bool (*valid_p)(const char *typed_passwd, Bool verbose_p)); +typedef struct { + Bool (*conv_fn) (void *closure, + int nmsgs, + const auth_message *msg, + auth_response **resp); + void *conv_fn_closure; +} pam_conv_closure; -Bool pam_priv_init (int argc, char **argv, Bool verbose_p); #ifdef HAVE_PAM_FAIL_DELAY /* We handle delays ourself.*/ @@ -116,52 +111,6 @@ Bool pam_priv_init (int argc, char **argv, Bool verbose_p); #endif /* !PAM_STRERROR_TWO_ARGS */ -/* PAM sucks in that there is no way to tell whether a particular service - is configured at all. That is, there is no way to tell the difference - between "authentication of the FOO service is not allowed" and "the - user typed the wrong password." - - On RedHat 5.1 systems, if a service name is not known, it defaults to - being not allowed (because the fallback service, /etc/pam.d/other, is - set to `pam_deny'.) - - On Solaris 2.6 systems, unknown services default to authenticating normally. - - So, we could simply require that the person who installs xscreensaver - set up an "xscreensaver" PAM service. However, if we went that route, - it would have a really awful failure mode: the failure mode would be that - xscreensaver was willing to *lock* the screen, but would be unwilling to - *unlock* the screen. (With the non-PAM password code, the analagous - situation -- security not being configured properly, for example do to the - executable not being installed as setuid root -- the failure mode is much - more palettable, in that xscreensaver will refuse to *lock* the screen, - because it can know up front that there is no password that will work.) - - Another route would be to have the service name to consult be computed at - compile-time (perhaps with a configure option.) However, that doesn't - really solve the problem, because it means that the same executable might - work fine on one machine, but refuse to unlock when run on another - machine. - - Another alternative would be to look in /etc/pam.conf or /etc/pam.d/ at - runtime to see what services actually exist. But I think that's no good, - because who is to say that the PAM info is actually specified in those - files? Opening and reading those files is not a part of the PAM client - API, so it's not guarenteed to work on any given system. - - An alternative I tried was to specify a list of services to try, and to - try them all in turn ("xscreensaver", "xlock", "xdm", and "login"). - This worked, but it was slow (and I also had to do some contortions to - work around bugs in Linux PAM 0.64-3.) - - So what we do today is, try PAM once, and if that fails, try the usual - getpwent() method. So if PAM doesn't work, it will at least make an - attempt at looking up passwords in /etc/passwd or /etc/shadow instead. - - This all kind of blows. I'm not sure what else to do. - */ - - /* On SunOS 5.6, the `pam_conv.appdata_ptr' slot seems to be ignored, and the `closure' argument to pc.conv always comes in as random garbage. So we get around this by using a global variable instead. Shoot me! @@ -172,97 +121,70 @@ Bool pam_priv_init (int argc, char **argv, Bool verbose_p); static void *suns_pam_implementation_blows = 0; -/** - * This function is the PAM conversation driver. It conducts a full - * authentication round by invoking the GUI with various prompts. +/* This function invokes the PAM conversation. It conducts a full + authentication round by presenting the GUI with various prompts. */ -void -pam_try_unlock(saver_info *si, Bool verbose_p, - Bool (*valid_p)(const char *typed_passwd, Bool verbose_p)) +Bool +pam_try_unlock (void *closure, + Bool (*conv_fn) (void *closure, + int nmsgs, + const auth_message *msg, + auth_response **resp)) { const char *service = PAM_SERVICE_NAME; pam_handle_t *pamh = 0; int status = -1; struct pam_conv pc; -# ifdef HAVE_SIGTIMEDWAIT - sigset_t set; - struct timespec timeout; -# endif /* HAVE_SIGTIMEDWAIT */ + struct passwd *p = getpwuid (getuid()); + const char *user = (p && p->pw_name && *p->pw_name ? p->pw_name : 0); + pam_conv_closure conv_closure; + + if (!user) + { + fprintf (stderr, "%s: PAM: unable to get current user\n", blurb()); + return False; + } + conv_closure.conv_fn = conv_fn; + conv_closure.conv_fn_closure = closure; + pc.appdata_ptr = &conv_closure; pc.conv = &pam_conversation; - pc.appdata_ptr = (void *) si; /* On SunOS 5.6, the `appdata_ptr' slot seems to be ignored, and the `closure' argument to pc.conv always comes in as random garbage. */ - suns_pam_implementation_blows = (void *) si; + suns_pam_implementation_blows = pc.appdata_ptr; /* Initialize PAM. */ - status = pam_start (service, si->user, &pc, &pamh); + status = pam_start (service, user, &pc, &pamh); if (verbose_p) - fprintf (stderr, "%s: pam_start (\"%s\", \"%s\", ...) ==> %d (%s)\n", - blurb(), service, si->user, + fprintf (stderr, "%s: PAM: pam_start (\"%s\", \"%s\", ...) ==> %d (%s)\n", + blurb(), service, user, status, PAM_STRERROR (pamh, status)); if (status != PAM_SUCCESS) goto DONE; - /* #### We should set PAM_TTY to the display we're using, but we - don't have that handy from here. So set it to :0.0, which is a - good guess (and has the bonus of counting as a "secure tty" as - far as PAM is concerned...) - */ { - char *tty = strdup (":0.0"); + char *tty = getenv ("DISPLAY"); + if (!tty || !*tty) tty = ":0.0"; status = pam_set_item (pamh, PAM_TTY, tty); if (verbose_p) fprintf (stderr, "%s: pam_set_item (p, PAM_TTY, \"%s\") ==> %d (%s)\n", blurb(), tty, status, PAM_STRERROR(pamh, status)); - free (tty); } - /* Try to authenticate as the current user. - We must turn off our SIGCHLD handler for the duration of the call to - pam_authenticate(), because in some cases, the underlying PAM code - will do this: - - 1: fork a setuid subprocess to do some dirty work; - 2: read a response from that subprocess; - 3: waitpid(pid, ...) on that subprocess. - - If we (the ignorant parent process) have a SIGCHLD handler, then there's - a race condition between steps 2 and 3: if the subprocess exits before - waitpid() was called, then our SIGCHLD handler fires, and gets notified - of the subprocess death; then PAM's call to waitpid() fails, because the - process has already been reaped. - - I consider this a bug in PAM, since the caller should be able to have - whatever signal handlers it wants -- the PAM documentation doesn't say - "oh by the way, if you use PAM, you can't use SIGCHLD." - */ - PAM_NO_DELAY(pamh); if (verbose_p) fprintf (stderr, "%s: pam_authenticate (...) ...\n", blurb()); -# ifdef HAVE_SIGTIMEDWAIT - timeout.tv_sec = 0; - timeout.tv_nsec = 1; - set = -# endif /* HAVE_SIGTIMEDWAIT */ - block_sigchld(); status = pam_authenticate (pamh, 0); -# ifdef HAVE_SIGTIMEDWAIT - sigtimedwait (&set, NULL, &timeout); - /* #### What is the portable thing to do if we don't have it? */ -# endif /* HAVE_SIGTIMEDWAIT */ - unblock_sigchld(); if (verbose_p) fprintf (stderr, "%s: pam_authenticate (...) ==> %d (%s)\n", blurb(), status, PAM_STRERROR(pamh, status)); - if (status == PAM_SUCCESS) /* Win! */ + if (status == PAM_SUCCESS) /* So far so good... */ { int status2; @@ -341,22 +263,16 @@ pam_try_unlock(saver_info *si, Bool verbose_p, (status2 == PAM_SUCCESS ? "Success" : "Failure")); } - if (status == PAM_SUCCESS) - si->unlock_state = ul_success; /* yay */ - else if (si->unlock_state == ul_cancel || - si->unlock_state == ul_time) - ; /* more specific failures ok */ - else - si->unlock_state = ul_fail; /* generic failure */ + return (status == PAM_SUCCESS); } Bool -pam_priv_init (int argc, char **argv, Bool verbose_p) +pam_priv_init (void) { /* We have nothing to do at init-time. However, we might as well do some error checking. - If "/etc/pam.d" exists and is a directory, but "/etc/pam.d/xlock" + If "/etc/pam.d" exists and is a directory, but "/etc/pam.d/xscreensaver" does not exist, warn that PAM probably isn't going to work. This is a priv-init instead of a non-priv init in case the directory @@ -375,8 +291,8 @@ pam_priv_init (int argc, char **argv, Bool verbose_p) { if (stat (file, &st) != 0) fprintf (stderr, - "%s: warning: %s does not exist.\n" - "%s: password authentication via PAM is unlikely to work.\n", + "%s: PAM: warning: %s does not exist.\n" + "%s: PAM: password authentication is unlikely to work.\n", blurb(), file, blurb()); } else if (stat (file2, &st) == 0) @@ -396,8 +312,8 @@ pam_priv_init (int argc, char **argv, Bool verbose_p) if (!ok) { fprintf (stderr, - "%s: warning: %s does not list the `%s' service.\n" - "%s: password authentication via PAM is unlikely to work.\n", + "%s: PAM: warning: %s does not list the `%s' service.\n" + "%s: PAM: password authentication is unlikely to work.\n", blurb(), file2, PAM_SERVICE_NAME, blurb()); } } @@ -406,8 +322,8 @@ pam_priv_init (int argc, char **argv, Bool verbose_p) else { fprintf (stderr, - "%s: warning: neither %s nor %s exist.\n" - "%s: password authentication via PAM is unlikely to work.\n", + "%s: PAM: warning: neither %s nor %s exist.\n" + "%s: PAM: password authentication is unlikely to work.\n", blurb(), file2, file, blurb()); } @@ -416,23 +332,22 @@ pam_priv_init (int argc, char **argv, Bool verbose_p) } +/* This is pam_conv->conv */ static int pam_conversation (int nmsgs, const struct pam_message **msg, struct pam_response **resp, - void *vsaver_info) + void *closure) { - int i, ret = -1; - struct auth_message *messages = 0; - struct auth_response *authresp = 0; + int i; + auth_message *messages = 0; + auth_response *authresp = 0; struct pam_response *pam_responses; - saver_info *si = (saver_info *) vsaver_info; - Bool verbose_p; + pam_conv_closure *conv_closure; /* On SunOS 5.6, the `closure' argument always comes in as random garbage. */ - si = (saver_info *) suns_pam_implementation_blows; - - verbose_p = si->prefs.verbose_p; + closure = suns_pam_implementation_blows; + conv_closure = closure; /* Converting the PAM prompts into the XScreenSaver native format. * It was a design goal to collapse (INFO,PROMPT) pairs from PAM @@ -443,11 +358,9 @@ pam_conversation (int nmsgs, * pass along whatever was passed in here. */ - messages = calloc(nmsgs, sizeof(struct auth_message)); - pam_responses = calloc(nmsgs, sizeof(*pam_responses)); - - if (!pam_responses || !messages) - goto end; + messages = calloc (nmsgs, sizeof(*messages)); + pam_responses = calloc (nmsgs, sizeof(*pam_responses)); + if (!pam_responses || !messages) abort(); if (verbose_p) fprintf (stderr, "%s: pam_conversation (", blurb()); @@ -483,44 +396,25 @@ pam_conversation (int nmsgs, if (verbose_p) fprintf (stderr, ") ...\n"); - ret = si->unlock_cb(nmsgs, messages, &authresp, si); - - /* #### If the user times out, or hits ESC or Cancel, we return PAM_CONV_ERR, - and PAM logs this as an authentication failure. It would be nice if - there was some way to indicate that this was a "cancel" rather than - a "fail", so that it wouldn't show up in syslog, but I think the - only options are PAM_SUCCESS and PAM_CONV_ERR. (I think that - PAM_ABORT means "internal error", not "cancel".) Bleh. - */ - - if (ret == 0) - { - for (i = 0; i < nmsgs; ++i) - pam_responses[i].resp = authresp[i].response; - } + /* This opens the dialog box and runs the X11 event loop. + It only returns if the user entered a password. + If they hit cancel, or timed out, it exited. + */ + conv_closure->conv_fn (conv_closure->conv_fn_closure, nmsgs, messages, + &authresp); -end: - if (messages) - free(messages); + for (i = 0; i < nmsgs; ++i) + pam_responses[i].resp = authresp[i].response; - if (authresp) - free(authresp); + if (messages) free (messages); + if (authresp) free (authresp); if (verbose_p) - fprintf (stderr, "%s: pam_conversation (...) ==> %s\n", blurb(), - (ret == 0 ? "PAM_SUCCESS" : "PAM_CONV_ERR")); - - if (ret == 0) - { - *resp = pam_responses; - return PAM_SUCCESS; - } - - /* Failure only */ - if (pam_responses) - free(pam_responses); + fprintf (stderr, "%s: pam_conversation (...) ==> PAM_SUCCESS\n", + blurb()); - return PAM_CONV_ERR; + *resp = pam_responses; + return PAM_SUCCESS; } #endif /* NO_LOCKING -- whole file */ diff --git a/driver/passwd-pwent.c b/driver/passwd-pwent.c index bb0edfc..b6c74de 100644 --- a/driver/passwd-pwent.c +++ b/driver/passwd-pwent.c @@ -1,5 +1,5 @@ /* passwd-pwent.c --- verifying typed passwords with the OS. - * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski + * xscreensaver, Copyright © 1993-2021 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 @@ -28,13 +28,8 @@ #include #include #include -#ifndef VMS -# include -# include -#else /* VMS */ -# include "vms-pwd.h" -#endif /* VMS */ - +#include +#include #ifdef __bsdi__ # include @@ -47,6 +42,7 @@ #if defined(HAVE_SHADOW_PASSWD) /* passwds live in /etc/shadow */ # include +# define PWNAME "spnam" # define PWTYPE struct spwd * # define PWPSLOT sp_pwdp # define GETPW getspnam @@ -56,6 +52,7 @@ # include # include +# define PWNAME "prpwnam" # define PWTYPE struct pr_passwd * # define PWPSLOT ufld.fd_encrypt # define GETPW getprpwnam @@ -66,6 +63,7 @@ # include # include +# define PWNAME "pwanam" # define PWTYPE struct passwd_adjunct * # define PWPSLOT pwa_passwd # define GETPW getpwanam @@ -75,43 +73,33 @@ # include # include +# define PWNAME "spwnam" # define PWTYPE struct s_passwd * # define PWPSLOT pw_passwd # define GETPW getspwnam # define HAVE_BIGCRYPT -#endif +#elif defined(HAVE_PWNAM_SHADOW_PASSWD) +# define PWNAME "pwnam_shadow" +# define PWTYPE struct passwd * +# define PWPSLOT pw_passwd +# define GETPW getpwnam_shadow -/* blargh */ -#undef Bool -#undef True -#undef False -#define Bool int -#define True 1 -#define False 0 +#endif +#include "blurb.h" +#include "auth.h" -extern const char *blurb(void); +#ifdef ALLOW_ROOT_PASSWD static char *encrypted_root_passwd = 0; -static char *encrypted_user_passwd = 0; - -#ifdef VMS -# define ROOT "SYSTEM" -#else -# define ROOT "root" -#endif - -#ifndef VMS -Bool pwent_priv_init (int argc, char **argv, Bool verbose_p); -Bool pwent_lock_init (int argc, char **argv, Bool verbose_p); -Bool pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p); #endif +static char *encrypted_user_passwd = 0; +#define ROOT "root" -#ifndef VMS static char * user_name (void) @@ -140,17 +128,6 @@ user_name (void) return (u ? strdup(u) : 0); } -#else /* VMS */ - -static char * -user_name (void) -{ - char *u = getenv("USER"); - return (u ? strdup(u) : 0); -} - -#endif /* VMS */ - static Bool passwd_known_p (const char *pw) @@ -162,24 +139,28 @@ passwd_known_p (const char *pw) static char * -get_encrypted_passwd(const char *user) +get_encrypted_passwd (const char *user) { char *result = 0; + const char *pwtype = "pwnam"; -#ifdef PWTYPE +# ifdef PWTYPE if (user && *user && !result) { /* First check the shadow passwords. */ PWTYPE p = GETPW((char *) user); if (p && passwd_known_p (p->PWPSLOT)) - result = strdup(p->PWPSLOT); + { + result = strdup(p->PWPSLOT); + pwtype = PWNAME; + } } -#endif /* PWTYPE */ +# endif /* PWTYPE */ if (user && *user && !result) { /* Check non-shadow passwords too. */ struct passwd *p = getpwnam(user); if (p && passwd_known_p (p->pw_passwd)) - result = strdup(p->pw_passwd); + result = strdup(p->pw_passwd); } /* The manual for passwd(4) on HPUX 10.10 says: @@ -200,15 +181,20 @@ get_encrypted_passwd(const char *user) *s = 0; } -#ifndef HAVE_PAM - /* We only issue this warning if not compiled with support for PAM. - If we're using PAM, it's not unheard of that normal pwent passwords - would be unavailable. */ + /* We only issue this warning in non-verbose mode if not compiled with + support for PAM. If we're using PAM, it's common for pwent passwords + to be unavailable. */ - if (!result) - fprintf (stderr, "%s: couldn't get password of \"%s\"\n", - blurb(), (user ? user : "(null)")); -#endif /* !HAVE_PAM */ + if (!result && + (verbose_p +# ifdef HAVE_PAM + || 0 +# else + || 1 +# endif + )) + fprintf (stderr, "%s: %s: couldn't get password of \"%s\"\n", + blurb(), pwtype, (user ? user : "(null)")); return result; } @@ -221,10 +207,8 @@ get_encrypted_passwd(const char *user) locking isn't possible. (It will also have written to stderr.) */ -#ifndef VMS - Bool -pwent_priv_init (int argc, char **argv, Bool verbose_p) +pwent_priv_init (void) { char *u; @@ -235,7 +219,9 @@ pwent_priv_init (int argc, char **argv, Bool verbose_p) u = user_name(); encrypted_user_passwd = get_encrypted_passwd(u); +#ifdef ALLOW_ROOT_PASSWD encrypted_root_passwd = get_encrypted_passwd(ROOT); +#endif if (u) free (u); if (encrypted_user_passwd) @@ -246,7 +232,7 @@ pwent_priv_init (int argc, char **argv, Bool verbose_p) Bool -pwent_lock_init (int argc, char **argv, Bool verbose_p) +pwent_lock_init (void) { if (encrypted_user_passwd) return True; @@ -287,7 +273,7 @@ passwds_match_p (const char *cleartext, const char *ciphertext) to root. */ Bool -pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p) +pwent_passwd_valid_p (void *closure, const char *typed_passwd) { if (encrypted_user_passwd && passwds_match_p (typed_passwd, encrypted_user_passwd)) @@ -305,8 +291,6 @@ pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p) return False; } -#else /* VMS */ -Bool pwent_lock_init (int argc, char **argv, Bool verbose_p) { return True; } -#endif /* VMS */ - +#else /* NO_LOCKING */ +int _ignore_; #endif /* NO_LOCKING -- whole file */ diff --git a/driver/passwd.c b/driver/passwd.c index 68e1a0b..11ca134 100644 --- a/driver/passwd.c +++ b/driver/passwd.c @@ -1,5 +1,5 @@ /* passwd.c --- verifying typed passwords with the OS. - * xscreensaver, Copyright (c) 1993-2019 Jamie Zawinski + * xscreensaver, Copyright © 1993-2021 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 @@ -14,8 +14,6 @@ # include "config.h" #endif -#ifndef NO_LOCKING /* whole file */ - #include #include #include @@ -26,11 +24,7 @@ #include #include -#ifndef VMS -# include /* for getpwuid() */ -#else /* VMS */ -# include "vms-pwd.h" -#endif /* VMS */ +#include /* for getpwuid() */ #ifdef HAVE_SYSLOG # include @@ -41,8 +35,24 @@ #include "xscreensaver.h" #include "auth.h" + +#ifdef NO_LOCKING + +Bool lock_init (void) { return 0; } +Bool lock_priv_init (void) { return 0; } +Bool xscreensaver_auth (void *closure, + Bool (*conv_fn) (void *closure, + int nmsgs, + const auth_message *msg, + auth_response **resp), + void (*finished_fn) (void *closure, Bool status)) +{ + return False; +} + +#else /* NO_LOCKING -- whole file */ + extern const char *blurb(void); -extern void check_for_leaks (const char *where); /* blargh */ @@ -58,100 +68,39 @@ extern void check_for_leaks (const char *where); struct auth_methods { const char *name; - Bool (*init) (int argc, char **argv, Bool verbose_p); - Bool (*priv_init) (int argc, char **argv, Bool verbose_p); - Bool (*valid_p) (const char *typed_passwd, Bool verbose_p); - void (*try_unlock) (saver_info *si, Bool verbose_p, - Bool (*valid_p)(const char *typed_passwd, Bool verbose_p)); + Bool (*init) (void); + Bool (*priv_init) (void); + Bool (*valid_p) (void *closure, const char *plaintext); + Bool (*try_unlock) (void *closure, + Bool (*conv_fn) (void *closure, + int nmsgs, + const auth_message *msg, + auth_response **resp)); Bool initted_p; Bool priv_initted_p; }; -#ifdef HAVE_KERBEROS -extern Bool kerberos_lock_init (int argc, char **argv, Bool verbose_p); -extern Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p); -#endif -#ifdef HAVE_PAM -extern Bool pam_priv_init (int argc, char **argv, Bool verbose_p); -extern void pam_try_unlock (saver_info *si, Bool verbose_p, - Bool (*valid_p)(const char *typed_passwd, Bool verbose_p)); -#endif -#ifdef PASSWD_HELPER_PROGRAM -extern Bool ext_priv_init (int argc, char **argv, Bool verbose_p); -extern Bool ext_passwd_valid_p (const char *typed_passwd, Bool verbose_p); -#endif -extern Bool pwent_lock_init (int argc, char **argv, Bool verbose_p); -extern Bool pwent_priv_init (int argc, char **argv, Bool verbose_p); -extern Bool pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p); - -Bool lock_priv_init (int argc, char **argv, Bool verbose_p); -Bool lock_init (int argc, char **argv, Bool verbose_p); -Bool passwd_valid_p (const char *typed_passwd, Bool verbose_p); - -/* The authorization methods to try, in order. - Note that the last one (the pwent version) is actually two auth methods, - since that code tries shadow passwords, and then non-shadow passwords. - (It's all in the same file since the APIs are randomly nearly-identical.) + +/* The authorization methods to try, in order of preference. + The first that initializes successfully is used and others are ignored. */ struct auth_methods methods[] = { # ifdef HAVE_PAM - { "PAM", 0, pam_priv_init, 0, pam_try_unlock, - False, False }, + { "PAM", 0, pam_priv_init, 0, pam_try_unlock, 0, }, # endif # ifdef HAVE_KERBEROS - { "Kerberos", kerberos_lock_init, 0, kerberos_passwd_valid_p, 0, - False, False }, + { "KRB", kerberos_lock_init, 0, kerberos_passwd_valid_p, 0, }, # endif # ifdef PASSWD_HELPER_PROGRAM - { "external", 0, ext_priv_init, ext_passwd_valid_p, 0, - False, False }, + { "EXT", 0, ext_priv_init, ext_passwd_valid_p, 0, }, # endif - { "normal", pwent_lock_init, pwent_priv_init, pwent_passwd_valid_p, 0, - False, False } + { "pwnam", pwent_lock_init, pwent_priv_init, pwent_passwd_valid_p, 0, } }; -# ifdef HAVE_PROC_OOM -/* On some recent Linux systems you can tell the kernel's OOM-killer to - consider the possibility of maybe sometimes not killing you in low-memory - situations. Because that would unlock the screen. And that would be bad. - - Linux >= 2.6.11: echo -17 > /proc/$$/oom_adj <-- ignoring this. - Linux >= 2.6.37: echo -1000 > /proc/$$/oom_score_adj <-- trying this. - */ -static void -oom_assassin_immunity (Bool verbose_p) -{ - char fn[1024]; - struct stat st; - FILE *out; - sprintf (fn, "/proc/%d/oom_score_adj", getpid()); - if (stat(fn, &st) != 0) - { - if (verbose_p) - fprintf (stderr, "%s: OOM: %s does not exist\n", blurb(), fn); - return; - } - out = fopen (fn, "w"); - if (!out) - { - if (verbose_p) - { - char b[2048]; - sprintf (b, "%s: OOM: unable to write %s\n", blurb(), fn); - perror(b); - } - return; - } - fputs ("-1000\n", out); - fclose (out); -} -# endif /* HAVE_PROC_OOM */ - - Bool -lock_priv_init (int argc, char **argv, Bool verbose_p) +lock_priv_init (void) { int i; Bool any_ok = False; @@ -160,26 +109,17 @@ lock_priv_init (int argc, char **argv, Bool verbose_p) if (!methods[i].priv_init) methods[i].priv_initted_p = True; else - methods[i].priv_initted_p = methods[i].priv_init (argc, argv, - verbose_p); + methods[i].priv_initted_p = methods[i].priv_init(); if (methods[i].priv_initted_p) any_ok = True; - else if (verbose_p) - fprintf (stderr, "%s: initialization of %s passwords failed.\n", - blurb(), methods[i].name); } - -# ifdef HAVE_PROC_OOM - oom_assassin_immunity (verbose_p); -# endif - return any_ok; } Bool -lock_init (int argc, char **argv, Bool verbose_p) +lock_init (void) { int i; Bool any_ok = False; @@ -191,70 +131,75 @@ lock_init (int argc, char **argv, Bool verbose_p) if (!methods[i].init) methods[i].initted_p = True; else - methods[i].initted_p = methods[i].init (argc, argv, verbose_p); + methods[i].initted_p = methods[i].init(); if (methods[i].initted_p) any_ok = True; else if (verbose_p) - fprintf (stderr, "%s: initialization of %s passwords failed.\n", + fprintf (stderr, "%s: %s: passwords initialization failed\n", blurb(), methods[i].name); } return any_ok; } -/* A basic auth driver that simply prompts for a password then runs it through - * valid_p to determine whether the password is correct. +/* For those auth methods that have a 'valid_p' function instead of a + 'try_unlock' function, this does a PAM-like conversation that first + prompts for a password and then tests it with the 'valid_p' function. */ -static void -try_unlock_password(saver_info *si, - Bool verbose_p, - Bool (*valid_p)(const char *typed_passwd, Bool verbose_p)) +static Bool +try_valid_p (void *closure, + const char *name, + Bool (*valid_p) (void *closure, const char *typed_passwd), + Bool (*conv_fn) (void *closure, + int nmsgs, + const auth_message *msg, + auth_response **resp)) { - struct auth_message message; - struct auth_response *response = NULL; + auth_message message; + auth_response *response = NULL; + Bool ok = False; - memset(&message, 0, sizeof(message)); + memset (&message, 0, sizeof(message)); if (verbose_p) - fprintf(stderr, "%s: non-PAM password auth.\n", blurb()); + fprintf (stderr, "%s: %s: non-PAM password auth\n", blurb(), name); - /* Call the auth_conv function with "Password:", then feed - * the result into valid_p() - */ + /* Call the auth_conv function with "Password:", then feed the result + into valid_p() */ message.type = AUTH_MSGTYPE_PROMPT_NOECHO; message.msg = "Password:"; - si->unlock_cb(1, &message, &response, si); + ok = conv_fn (closure, 1, &message, &response); + if (!response || !response->response) + ok = False; - if (!response) - return; + if (ok) + ok = valid_p (closure, response->response); - if (valid_p (response->response, verbose_p)) - si->unlock_state = ul_success; /* yay */ - else if (si->unlock_state == ul_cancel || - si->unlock_state == ul_time) - ; /* more specific failures ok */ - else - si->unlock_state = ul_fail; /* generic failure */ + if (response) + { + if (response->response) + free (response->response); + free (response); + } - if (response->response) - free(response->response); - free(response); + return ok; } /* Write a password failure to the system log. */ static void -do_syslog (saver_info *si, Bool verbose_p) +do_syslog (void) { # ifdef HAVE_SYSLOG struct passwd *pw = getpwuid (getuid ()); - char *d = (si->dpy ? DisplayString (si->dpy) : 0); + char *d = getenv ("DISPLAY"); char *u = (pw && pw->pw_name ? pw->pw_name : "???"); int opt = 0; int fac = 0; + int pri = LOG_NOTICE; # ifdef LOG_PID opt = LOG_PID; @@ -270,115 +215,57 @@ do_syslog (saver_info *si, Bool verbose_p) if (!d) d = ""; -# undef FMT -# define FMT "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"" - - if (verbose_p) - fprintf (stderr, "%s: syslog: " FMT "\n", blurb(), - si->unlock_failures, d, u); - openlog (progname, opt, fac); - syslog (LOG_NOTICE, FMT, si->unlock_failures, d, u); + syslog (pri, "Failed login on display \"%s\" for \"%s\"", d, u); closelog (); # endif /* HAVE_SYSLOG */ } - -/** - * Runs through each authentication driver calling its try_unlock function. - * Called xss_authenticate() because AIX beat us to the name authenticate(). +/* Runs through each authentication driver calling its try_unlock function. */ -void -xss_authenticate(saver_info *si, Bool verbose_p) +Bool +xscreensaver_auth (void *closure, + Bool (*conv_fn) (void *closure, + int nmsgs, + const auth_message *msg, + auth_response **resp), + void (*finished_fn) (void *closure, Bool status)) { - int i, j; - - si->unlock_state = ul_read; + int i; + Bool ok = False; for (i = 0; i < countof(methods); i++) { if (!methods[i].initted_p) continue; - if (si->cached_passwd != NULL && methods[i].valid_p) - si->unlock_state = (methods[i].valid_p(si->cached_passwd, verbose_p) == True) - ? ul_success : ul_fail; - else if (methods[i].try_unlock != NULL) - methods[i].try_unlock(si, verbose_p, methods[i].valid_p); + if (methods[i].try_unlock) + ok = methods[i].try_unlock (closure, conv_fn); else if (methods[i].valid_p) - try_unlock_password(si, verbose_p, methods[i].valid_p); - else /* Ze goggles, zey do nozing! */ - fprintf(stderr, "%s: authentication method %s does nothing.\n", - blurb(), methods[i].name); - - check_for_leaks (methods[i].name); - - /* If password authentication failed, but the password was NULL - (meaning the user just hit RET) then treat that as "cancel". - This means that if the password is literally NULL, it will - work; but if not, then NULL passwords are treated as cancel. - */ - if (si->unlock_state == ul_fail && - si->cached_passwd && - !*si->cached_passwd) - { - if (verbose_p) - fprintf (stderr, "%s: assuming null password means cancel.\n", - blurb()); - si->unlock_state = ul_cancel; - } - - if (si->unlock_state == ul_success) - { - /* If we successfully authenticated by method N, but attempting - to authenticate by method N-1 failed, mention that (since if - an earlier authentication method fails and a later one succeeds, - something screwy is probably going on.) - */ - if (verbose_p && i > 0) - { - for (j = 0; j < i; j++) - if (methods[j].initted_p) - fprintf (stderr, - "%s: authentication via %s failed.\n", - blurb(), methods[j].name); - fprintf (stderr, - "%s: authentication via %s succeeded.\n", - blurb(), methods[i].name); - } - goto DONE; /* Successfully authenticated! */ - } - else if (si->unlock_state == ul_cancel || - si->unlock_state == ul_time) - { - /* If any auth method gets a cancel or timeout, don't try the - next auth method! We're done! */ - if (verbose_p) - fprintf (stderr, "%s: authentication via %s %s.\n", - blurb(), methods[i].name, - (si->unlock_state == ul_cancel - ? "cancelled" : "timed out")); - goto DONE; - } + ok = try_valid_p (closure, methods[i].name, methods[i].valid_p, + conv_fn); + else + abort(); /* method must have one or the other function */ + + /* Only try the first method that initialized properly. That means that + if PAM initialized correctly, we will never try pwent or Kerberos. + If we did, then typing an incorrect password at PAM would result in a + second password prompt that would only go to pwent. There's no + sensible way to re-use the password typed the first time, if there + even was one. With fingerprint readers or OTP fobs, there might have + been 0, 2, or more passwords entered. */ + break; } - if (verbose_p) - fprintf(stderr, "%s: All authentication mechanisms failed.\n", blurb()); + if (!ok) + do_syslog (); - if (si->unlock_state == ul_fail) - { - /* Note the time of the first failure */ - if (si->unlock_failures == 0) - si->unlock_failure_time = time((time_t *) 0); - si->unlock_failures++; - do_syslog (si, verbose_p); - } + if (finished_fn) + finished_fn (closure, ok); -DONE: - if (si->auth_finished_cb) - si->auth_finished_cb (si); + return ok; } #endif /* NO_LOCKING -- whole file */ diff --git a/driver/prefs.c b/driver/prefs.c index b608738..a65b679 100644 --- a/driver/prefs.c +++ b/driver/prefs.c @@ -1,5 +1,5 @@ -/* dotfile.c --- management of the ~/.xscreensaver file. - * xscreensaver, Copyright (c) 1998-2020 Jamie Zawinski +/* prefs.c --- reading and writing the ~/.xscreensaver file. + * xscreensaver, Copyright © 1998-2021 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 @@ -14,311 +14,20 @@ # include "config.h" #endif -#include - -#ifdef HAVE_UNISTD_H -# include -#endif - #include -#include +#include #include #include #include #include -#include /* for PATH_MAX */ -#include -#include - -#ifndef VMS -# include -#else /* VMS */ -# include "vms-pwd.h" -#endif /* VMS */ - - -/* This file doesn't need the Xt headers, so stub these types out... */ -#undef XtPointer -#define XtAppContext void* -#define XtIntervalId void* -#define XtPointer void* -#define Widget void* - - -/* Just in case there's something pathological about stat.h... */ -#ifndef S_IRUSR -# define S_IRUSR 00400 -#endif -#ifndef S_IWUSR -# define S_IWUSR 00200 -#endif -#ifndef S_IXUSR -# define S_IXUSR 00100 -#endif -#ifndef S_IXGRP -# define S_IXGRP 00010 -#endif -#ifndef S_IXOTH -# define S_IXOTH 00001 +#ifdef HAVE_UNISTD_H +# include #endif - -#include "version.h" +#include "blurb.h" #include "prefs.h" -#include "resources.h" - -/* don't use realpath() on fedora system */ -#ifdef _FORTIFY_SOURCE -#undef HAVE_REALPATH -#endif - - -extern char *progname; -extern char *progclass; -extern const char *blurb (void); - - - -static void get_screenhacks (Display *, saver_preferences *); -static char *format_command (const char *cmd, Bool wrap_p); -static void merge_system_screenhacks (Display *, saver_preferences *, - screenhack **system_list, int count); -static void stop_the_insanity (saver_preferences *p); - - -static char * -chase_symlinks (const char *file) -{ -# ifdef HAVE_REALPATH - if (file) - { -# ifndef PATH_MAX -# ifdef MAXPATHLEN -# define PATH_MAX MAXPATHLEN -# else -# define PATH_MAX 2048 -# endif -# endif - char buf[PATH_MAX]; - if (realpath (file, buf)) - return strdup (buf); - -/* sprintf (buf, "%.100s: realpath %.200s", blurb(), file); - perror(buf);*/ - } -# endif /* HAVE_REALPATH */ - return 0; -} - - -static Bool -i_am_a_nobody (uid_t uid) -{ - struct passwd *p; - - p = getpwnam ("nobody"); - if (! p) p = getpwnam ("noaccess"); - if (! p) p = getpwnam ("daemon"); - - if (! p) /* There is no nobody? */ - return False; - - return (uid == p->pw_uid); -} - - -const char * -init_file_name (void) -{ - static char *file = 0; - - if (!file) - { - uid_t uid = getuid (); - const char *home = getenv("HOME"); - - if (i_am_a_nobody (uid) || !home || !*home) - { - /* If we're running as nobody, then use root's .xscreensaver file - (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely - to be different -- if we didn't do this, then xscreensaver-demo - would appear to have no effect when the luser is running as root.) - */ - struct passwd *p = getpwuid (uid); - uid = 0; - if (!p || !p->pw_name || !*p->pw_name) - { - fprintf (stderr, "%s: couldn't get user info of uid %d\n", - blurb(), getuid ()); - } - else if (!p->pw_dir || !*p->pw_dir) - { - fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n", - blurb(), (p->pw_name ? p->pw_name : "???")); - } - else - { - home = p->pw_dir; - } - } - if (home && *home) - { - const char *name = ".xscreensaver"; - file = (char *) malloc(strlen(home) + strlen(name) + 2); - strcpy(file, home); - if (!*home || home[strlen(home)-1] != '/') - strcat(file, "/"); - strcat(file, name); - } - else - { - file = ""; - } - } - - if (file && *file) - return file; - else - return 0; -} - - -static const char * -init_file_tmp_name (void) -{ - static char *file = 0; - if (!file) - { - const char *name = init_file_name(); - const char *suffix = ".tmp"; - - char *n2 = chase_symlinks (name); - if (n2) name = n2; - if (!name || !*name) - file = ""; - else - { - file = (char *) malloc(strlen(name) + strlen(suffix) + 2); - strcpy(file, name); - strcat(file, suffix); - } - - if (n2) free (n2); - } - - if (file && *file) - return file; - else - return 0; -} - -static int -get_byte_resource (Display *dpy, char *name, char *class) -{ - char *s = get_string_resource (dpy, name, class); - char *s2 = s; - int n = 0; - if (!s) return 0; - - while (isspace(*s2)) s2++; - while (*s2 >= '0' && *s2 <= '9') - { - n = (n * 10) + (*s2 - '0'); - s2++; - } - while (isspace(*s2)) s2++; - if (*s2 == 'k' || *s2 == 'K') n <<= 10; - else if (*s2 == 'm' || *s2 == 'M') n <<= 20; - else if (*s2 == 'g' || *s2 == 'G') n <<= 30; - else if (*s2) - { - LOSE: - fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n", - progname, name, s); - free (s); - return 0; - } - s2++; - if (*s2 == 'b' || *s2 == 'B') s2++; - while (isspace(*s2)) s2++; - if (*s2) goto LOSE; - - free (s); - return n; -} - - -static const char * const prefs[] = { - "timeout", - "cycle", - "lock", - "lockVTs", /* not saved */ - "lockTimeout", - "passwdTimeout", - "visualID", - "installColormap", - "verbose", - "timestamp", - "splash", - "splashDuration", - "quad", - "demoCommand", - "prefsCommand", - "newLoginCommand", - "helpURL", /* not saved */ - "loadURL", /* not saved */ - "newLoginCommand", /* not saved */ - "externalUngrabCommand", /* not saved */ - "nice", - "memoryLimit", - "fade", - "unfade", - "fadeSeconds", - "fadeTicks", - "captureStderr", - "captureStdout", /* not saved -- obsolete */ - "logFile", /* not saved */ - "ignoreUninstalledPrograms", - "font", - "dpmsEnabled", - "dpmsQuickOff", - "dpmsStandby", - "dpmsSuspend", - "dpmsOff", - "dpmsFullThrottle", - "grabDesktopImages", - "grabVideoFrames", - "chooseRandomImages", - "imageDirectory", - "mode", - "selected", - "textMode", - "textLiteral", - "textFile", - "textProgram", - "textURL", - "", - "programs", - "", - "pointerPollTime", - "pointerHysteresis", - "windowCreationTimeout", - "initialDelay", - "sgiSaverExtension", /* not saved -- obsolete */ - "mitSaverExtension", /* not saved -- obsolete */ - "xidleExtension", /* not saved -- obsolete */ - "GetViewPortIsFullOfLies", - "procInterrupts", - "xinputExtensionDev", - "overlayStderr", - "overlayTextBackground", /* not saved -- X resources only */ - "overlayTextForeground", /* not saved -- X resources only */ - "bourneShell", /* not saved -- X resources only */ - "authWarningSlack", - 0 -}; static char * strip (char *s) @@ -336,85 +45,49 @@ strip (char *s) return s; } - -/* Reading - */ - -static int -handle_entry (XrmDatabase *db, const char *key, const char *value, - const char *filename, int line) -{ - int i; - for (i = 0; prefs[i]; i++) - if (*prefs[i] && !strcasecmp(key, prefs[i])) - { - char *val = strdup(value); - char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10); - strcpy(spec, progclass); - strcat(spec, "."); - strcat(spec, prefs[i]); - - XrmPutStringResource (db, spec, val); - - free(spec); - free(val); - return 0; - } - - fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n", - blurb(), filename, line, key); - return 1; -} - -static int -parse_init_file (saver_preferences *p) +/* Parse the .xscreensaver or XScreenSaver.ad file and run the callback + for each key-value pair. +*/ +int +parse_init_file (const char *name, + void (*handler) (int lineno, + const char *key, const char *val, + void *closure), + void *closure) { - time_t write_date = 0; - const char *name = init_file_name(); int line = 0; struct stat st; FILE *in; int buf_size = 1024; - char *buf; + char *buf = 0; if (!name) return 0; + if (stat (name, &st) != 0) goto FAIL; - if (stat(name, &st) != 0) - { - p->init_file_date = 0; - return 0; - } + buf = (char *) malloc (buf_size); + if (!buf) goto FAIL; in = fopen(name, "r"); if (!in) { - char *buf = (char *) malloc(1024 + strlen(name)); - sprintf(buf, "%s: error reading \"%s\"", blurb(), name); - perror(buf); - free(buf); - return -1; + sprintf (buf, "%s: error reading \"%.100s\"", blurb(), name); + perror (buf); + goto FAIL; } - if (fstat (fileno(in), &st) == 0) + if (fstat (fileno (in), &st) != 0) { - write_date = st.st_mtime; - } - else - { - char *buf = (char *) malloc(1024 + strlen(name)); - sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name); - perror(buf); - free(buf); + sprintf (buf, "%s: couldn't re-stat \"%.100s\"", blurb(), name); + perror (buf); + free (buf); return -1; } - buf = (char *) malloc(buf_size); - while (fgets (buf, buf_size-1, in)) { char *key, *value; - int L = strlen(buf); + int L = strlen (buf); line++; while (L > 2 && @@ -427,13 +100,13 @@ parse_init_file (saver_preferences *p) L -= 2; } buf_size += 1024; - buf = (char *) realloc(buf, buf_size); - if (!buf) exit(1); + buf = (char *) realloc (buf, buf_size); + if (!buf) goto FAIL; line++; if (!fgets (buf + L, buf_size-L-1, in)) break; - L = strlen(buf); + L = strlen (buf); } /* Now handle other backslash escapes. */ @@ -455,7 +128,7 @@ parse_init_file (saver_preferences *p) } } - key = strip(buf); + key = strip (buf); if (*key == '#' || *key == '!' || *key == ';' || *key == '\n' || *key == 0) @@ -464,1320 +137,23 @@ parse_init_file (saver_preferences *p) value = strchr (key, ':'); if (!value) { - fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(), - name, line, key); + fprintf (stderr, "%s: %s:%d: unparsable line: %s\n", blurb(), + name, line, key); continue; } else { *value++ = 0; - value = strip(value); + value = strip (value); } - if (!p->db) abort(); - handle_entry (&p->db, key, value, name, line); + handler (line, key, value, closure); } fclose (in); - free(buf); - - p->init_file_date = write_date; + free (buf); return 0; -} - - -Bool -init_file_changed_p (saver_preferences *p) -{ - const char *name = init_file_name(); - struct stat st; - - if (!name) return False; - - if (stat(name, &st) != 0) - return False; - - if (p->init_file_date == st.st_mtime) - return False; - - return True; -} - - -/* Writing - */ - -static int -tab_to (FILE *out, int from, int to) -{ - int tab_width = 8; - int to_mod = (to / tab_width) * tab_width; - while (from < to_mod) - { - fprintf(out, "\t"); - from = (((from / tab_width) + 1) * tab_width); - } - while (from < to) - { - fprintf(out, " "); - from++; - } - return from; -} - -static char * -stab_to (char *out, int from, int to) -{ - int tab_width = 8; - int to_mod = (to / tab_width) * tab_width; - while (from < to_mod) - { - *out++ = '\t'; - from = (((from / tab_width) + 1) * tab_width); - } - while (from < to) - { - *out++ = ' '; - from++; - } - return out; -} - -static int -string_columns (const char *string, int length, int start) -{ - int tab_width = 8; - int col = start; - const char *end = string + length; - while (string < end) - { - if (*string == '\n') - col = 0; - else if (*string == '\t') - col = (((col / tab_width) + 1) * tab_width); - else - col++; - string++; - } - return col; -} - - -static void -write_entry (FILE *out, const char *key, const char *value) -{ - char *v = strdup(value ? value : ""); - char *v2 = v; - char *nl = 0; - int col; - Bool programs_p = (!strcmp(key, "programs")); - int tab = (programs_p ? 32 : 16); - Bool first = True; - - fprintf(out, "%s:", key); - col = strlen(key) + 1; - - if (strlen(key) > 14) - col = tab_to (out, col, 20); - - while (1) - { - if (!programs_p) - v2 = strip(v2); - nl = strchr(v2, '\n'); - if (nl) - *nl = 0; - - if (first && programs_p) - { - col = tab_to (out, col, 77); - fprintf (out, " \\\n"); - col = 0; - } - - if (first) - first = False; - else - { - col = tab_to (out, col, 75); - fprintf (out, " \\n\\\n"); - col = 0; - } - - if (!programs_p) - col = tab_to (out, col, tab); - - if (programs_p && - string_columns(v2, strlen (v2), col) + col > 75) - { - int L = strlen (v2); - int start = 0; - int end = start; - while (start < L) - { - while (v2[end] == ' ' || v2[end] == '\t') - end++; - while (v2[end] != ' ' && v2[end] != '\t' && - v2[end] != '\n' && v2[end] != 0) - end++; - if (string_columns (v2 + start, (end - start), col) >= 74) - { - col = tab_to (out, col, 75); - fprintf(out, " \\\n"); - col = tab_to (out, 0, tab + 2); - while (v2[start] == ' ' || v2[start] == '\t') - start++; - } - - col = string_columns (v2 + start, (end - start), col); - while (start < end) - fputc(v2[start++], out); - } - } - else - { - fprintf (out, "%s", v2); - col += string_columns(v2, strlen (v2), col); - } - - if (nl) - v2 = nl + 1; - else - break; - } - - fprintf(out, "\n"); - free(v); -} - -int -write_init_file (Display *dpy, - saver_preferences *p, const char *version_string, - Bool verbose_p) -{ - int status = -1; - const char *name = init_file_name(); - const char *tmp_name = init_file_tmp_name(); - char *n2 = chase_symlinks (name); - struct stat st; - int i, j; - - /* Kludge, since these aren't in the saver_preferences struct as strings... - */ - char *visual_name; - char *programs; - Bool overlay_stderr_p; - char *stderr_font; - FILE *out; - - if (!name) goto END; - - if (n2) name = n2; - - /* Throttle the various timeouts to reasonable values before writing - the file to disk. */ - stop_the_insanity (p); - - - if (verbose_p) - fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name); - - unlink (tmp_name); - out = fopen(tmp_name, "w"); - if (!out) - { - char *buf = (char *) malloc(1024 + strlen(name)); - sprintf(buf, "%s: error writing \"%s\"", blurb(), name); - perror(buf); - free(buf); - goto END; - } - - /* Give the new .xscreensaver file the same permissions as the old one; - except ensure that it is readable and writable by owner, and not - executable. Extra hack: if we're running as root, make the file - be world-readable (so that the daemon, running as "nobody", will - still be able to read it.) - */ - if (stat(name, &st) == 0) - { - mode_t mode = st.st_mode; - mode |= S_IRUSR | S_IWUSR; /* read/write by user */ - mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */ - - if (getuid() == (uid_t) 0) /* read by group/other */ - mode |= S_IRGRP | S_IROTH; - - if (fchmod (fileno(out), mode) != 0) - { - char *buf = (char *) malloc(1024 + strlen(name)); - sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(), - tmp_name, (unsigned int) mode); - perror(buf); - free(buf); - goto END; - } - } - - /* Kludge, since these aren't in the saver_preferences struct... */ - visual_name = get_string_resource (dpy, "visualID", "VisualID"); - programs = 0; - overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean"); - stderr_font = get_string_resource (dpy, "font", "Font"); - - i = 0; - { - char *ss; - char **hack_strings = (char **) - calloc (p->screenhacks_count, sizeof(char *)); - - for (j = 0; j < p->screenhacks_count; j++) - { - hack_strings[j] = format_hack (dpy, p->screenhacks[j], True); - i += strlen (hack_strings[j]); - i += 2; - } - - ss = programs = (char *) malloc(i + 10); - *ss = 0; - for (j = 0; j < p->screenhacks_count; j++) - { - strcat (ss, hack_strings[j]); - free (hack_strings[j]); - ss += strlen(ss); - *ss++ = '\n'; - *ss = 0; - } - free (hack_strings); - } - - { - struct passwd *pw = getpwuid (getuid ()); - char *whoami = (pw && pw->pw_name && *pw->pw_name - ? pw->pw_name - : ""); - time_t now = time ((time_t *) 0); - char *timestr = (char *) ctime (&now); - char *nl = (char *) strchr (timestr, '\n'); - if (nl) *nl = 0; - fprintf (out, - "# %s Preferences File\n" - "# Written by %s %s for %s on %s.\n" - "# https://www.jwz.org/xscreensaver/\n" - "\n", - progclass, progname, version_string, whoami, timestr); - } - - for (j = 0; prefs[j]; j++) - { - char buf[255]; - const char *pr = prefs[j]; - enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time - } type = pref_str; - const char *s = 0; - int i = 0; - Bool b = False; - Time t = 0; - - if (pr && !*pr) - { - fprintf(out, "\n"); - continue; - } - -# undef CHECK -# define CHECK(X) else if (!strcmp(pr, X)) - if (!pr || !*pr) ; - CHECK("timeout") type = pref_time, t = p->timeout; - CHECK("cycle") type = pref_time, t = p->cycle; - CHECK("lock") type = pref_bool, b = p->lock_p; - CHECK("lockVTs") continue; /* don't save, unused */ - CHECK("lockTimeout") type = pref_time, t = p->lock_timeout; - CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout; - CHECK("visualID") type = pref_str, s = visual_name; - CHECK("installColormap") type = pref_bool, b = p->install_cmap_p; - CHECK("verbose") type = pref_bool, b = p->verbose_p; - CHECK("timestamp") type = pref_bool, b = p->timestamp_p; - CHECK("splash") type = pref_bool, b = p->splash_p; - CHECK("splashDuration") type = pref_time, t = p->splash_duration; -# ifdef QUAD_MODE - CHECK("quad") type = pref_bool, b = p->quad_p; -# else /* !QUAD_MODE */ - CHECK("quad") continue; /* don't save */ -# endif /* !QUAD_MODE */ - CHECK("demoCommand") type = pref_str, s = p->demo_command; - CHECK("prefsCommand") type = pref_str, s = p->prefs_command; -/* CHECK("helpURL") type = pref_str, s = p->help_url; */ - CHECK("helpURL") continue; /* don't save */ -/* CHECK("loadURL") type = pref_str, s = p->load_url_command; */ - CHECK("loadURL") continue; /* don't save */ -/* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */ - CHECK("newLoginCommand") continue; /* don't save */ - CHECK("externalUngrabCommand") continue; /* don't save */ - CHECK("nice") type = pref_int, i = p->nice_inferior; - CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit; - CHECK("fade") type = pref_bool, b = p->fade_p; - CHECK("unfade") type = pref_bool, b = p->unfade_p; - CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds; - CHECK("fadeTicks") type = pref_int, i = p->fade_ticks; - CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p; - CHECK("captureStdout") continue; /* don't save */ - CHECK("logFile") continue; /* don't save */ - CHECK("ignoreUninstalledPrograms") - type = pref_bool, b = p->ignore_uninstalled_p; - - CHECK("font") type = pref_str, s = stderr_font; - - CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p; - CHECK("dpmsQuickOff") type = pref_bool, b = p->dpms_quickoff_p; - CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby; - CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend; - CHECK("dpmsOff") type = pref_time, t = p->dpms_off; - CHECK("dpmsFullThrottle") continue; /* don't save */ - - CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p; - CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p; - CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p; - CHECK("imageDirectory") type =pref_str, s = p->image_directory; - - CHECK("mode") type = pref_str, - s = (p->mode == ONE_HACK ? "one" : - p->mode == BLANK_ONLY ? "blank" : - p->mode == DONT_BLANK ? "off" : - p->mode == RANDOM_HACKS_SAME - ? "random-same" - : "random"); - CHECK("selected") type = pref_int, i = p->selected_hack; - - CHECK("textMode") type = pref_str, - s = (p->tmode == TEXT_URL ? "url" : - p->tmode == TEXT_LITERAL ? "literal" : - p->tmode == TEXT_FILE ? "file" : - p->tmode == TEXT_PROGRAM ? "program" : - "date"); - CHECK("textLiteral") type = pref_str, s = p->text_literal; - CHECK("textFile") type = pref_str, s = p->text_file; - CHECK("textProgram") type = pref_str, s = p->text_program; - CHECK("textURL") type = pref_str, s = p->text_url; - - CHECK("programs") type = pref_str, s = programs; - CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout; - CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis; - CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout; - CHECK("initialDelay") type = pref_time, t = p->initial_delay; - CHECK("sgiSaverExtension") continue; /* don't save */ - CHECK("mitSaverExtension") continue; /* don't save */ - CHECK("xidleExtension") continue; /* don't save */ - CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts; - CHECK("xinputExtensionDev") type = pref_bool, b = p->use_xinput_extension; - CHECK("GetViewPortIsFullOfLies") type = pref_bool, - b = p->getviewport_full_of_lies_p; - CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p; - CHECK("overlayTextBackground") continue; /* don't save */ - CHECK("overlayTextForeground") continue; /* don't save */ - CHECK("bourneShell") continue; /* don't save */ - CHECK("authWarningSlack") type = pref_int, i = p->auth_warning_slack; - else abort(); -# undef CHECK - - switch (type) - { - case pref_str: - break; - case pref_int: - sprintf(buf, "%d", i); - s = buf; - break; - case pref_bool: - s = b ? "True" : "False"; - break; - case pref_time: - { - unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000); - if (sec >= 60) - { - min += (sec / 60); - sec %= 60; - } - if (min >= 60) - { - hour += (min / 60); - min %= 60; - } - sprintf (buf, "%u:%02u:%02u", hour, min, sec); - s = buf; - } - break; - case pref_byte: - { - if (i >= (1<<30) && i == ((i >> 30) << 30)) - sprintf(buf, "%dG", i >> 30); - else if (i >= (1<<20) && i == ((i >> 20) << 20)) - sprintf(buf, "%dM", i >> 20); - else if (i >= (1<<10) && i == ((i >> 10) << 10)) - sprintf(buf, "%dK", i >> 10); - else - sprintf(buf, "%d", i); - s = buf; - } - break; - default: - abort(); - break; - } - - if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode"))) - fprintf(out, "\n"); - - write_entry (out, pr, s); - } - - fprintf(out, "\n"); - - if (visual_name) free(visual_name); - if (stderr_font) free(stderr_font); - if (programs) free(programs); - - if (fclose(out) == 0) - { - time_t write_date = 0; - - if (stat(tmp_name, &st) == 0) - { - write_date = st.st_mtime; - } - else - { - char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name)); - sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name); - perror(buf); - unlink (tmp_name); - free(buf); - goto END; - } - - if (rename (tmp_name, name) != 0) - { - char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name)); - sprintf(buf, "%s: error renaming \"%s\" to \"%s\"", - blurb(), tmp_name, name); - perror(buf); - unlink (tmp_name); - free(buf); - goto END; - } - else - { - p->init_file_date = write_date; - - /* Since the .xscreensaver file is used for IPC, let's try and make - sure that the bits actually land on the disk right away. */ - sync (); - - status = 0; /* wrote and renamed successfully! */ - } - } - else - { - char *buf = (char *) malloc(1024 + strlen(name)); - sprintf(buf, "%s: error closing \"%s\"", blurb(), name); - perror(buf); - free(buf); - unlink (tmp_name); - goto END; - } - - END: - if (n2) free (n2); - return status; -} - - -/* Parsing the resource database - */ - -void -free_screenhack (screenhack *hack) -{ - if (hack->visual) free (hack->visual); - if (hack->name) free (hack->name); - free (hack->command); - memset (hack, 0, sizeof(*hack)); - free (hack); -} - -static void -free_screenhack_list (screenhack **list, int count) -{ - int i; - if (!list) return; - for (i = 0; i < count; i++) - if (list[i]) - free_screenhack (list[i]); - free (list); -} - - - -/* Populate `saver_preferences' with the contents of the resource database. - Note that this may be called multiple times -- it is re-run each time - the ~/.xscreensaver file is reloaded. - - This function can be very noisy, since it issues resource syntax errors - and so on. - */ -void -load_init_file (Display *dpy, saver_preferences *p) -{ - static Bool first_time = True; - - screenhack **system_default_screenhacks = 0; - int system_default_screenhack_count = 0; - - if (first_time) - { - /* Get the programs resource before the .xscreensaver file has been - parsed and merged into the resource database for the first time: - this is the value of *programs from the app-defaults file. - Then clear it out so that it will be parsed again later, after - the init file has been read. - */ - get_screenhacks (dpy, p); - system_default_screenhacks = p->screenhacks; - system_default_screenhack_count = p->screenhacks_count; - p->screenhacks = 0; - p->screenhacks_count = 0; - } - - if (parse_init_file (p) != 0) /* file might have gone away */ - if (!first_time) return; - - first_time = False; - - p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous"); - p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean"); - p->timestamp_p = get_boolean_resource (dpy, "timestamp", "Boolean"); - p->lock_p = get_boolean_resource (dpy, "lock", "Boolean"); - p->fade_p = get_boolean_resource (dpy, "fade", "Boolean"); - p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean"); - p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time"); - p->fade_ticks = get_integer_resource (dpy, "fadeTicks", "Integer"); - p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean"); - p->nice_inferior = get_integer_resource (dpy, "nice", "Nice"); - p->inferior_memory_limit = get_byte_resource (dpy, "memoryLimit", - "MemoryLimit"); - p->splash_p = get_boolean_resource (dpy, "splash", "Boolean"); -# ifdef QUAD_MODE - p->quad_p = get_boolean_resource (dpy, "quad", "Boolean"); -# endif - p->capture_stderr_p = get_boolean_resource (dpy, "captureStderr", "Boolean"); - p->ignore_uninstalled_p = get_boolean_resource (dpy, - "ignoreUninstalledPrograms", - "Boolean"); - - p->initial_delay = 1000 * get_seconds_resource (dpy, "initialDelay", "Time"); - p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time"); - p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time"); - p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time"); - p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time"); - p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time"); - p->pointer_timeout = 1000 * get_seconds_resource (dpy, "pointerPollTime", "Time"); - p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer"); - p->notice_events_timeout = 1000*get_seconds_resource(dpy, - "windowCreationTimeout", - "Time"); - - p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean"); - p->dpms_quickoff_p = get_boolean_resource (dpy, "dpmsQuickOff", "Boolean"); - p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time"); - p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time"); - p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time"); - p->dpms_full_throttle_p = get_boolean_resource (dpy, "dpmsFullThrottle", "Boolean"); - - p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean"); - p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean"); - p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean"); - p->image_directory = get_string_resource (dpy, - "imageDirectory", - "ImageDirectory"); - - p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral"); - p->text_file = get_string_resource (dpy, "textFile", "TextFile"); - p->text_program = get_string_resource (dpy, "textProgram", "TextProgram"); - p->text_url = get_string_resource (dpy, "textURL", "TextURL"); - - p->shell = get_string_resource (dpy, "bourneShell", "BourneShell"); - - p->demo_command = get_string_resource(dpy, "demoCommand", "URL"); - p->prefs_command = get_string_resource(dpy, "prefsCommand", "URL"); - p->help_url = get_string_resource(dpy, "helpURL", "URL"); - p->load_url_command = get_string_resource(dpy, "loadURL", "LoadURL"); - p->new_login_command = get_string_resource(dpy, - "newLoginCommand", - "NewLoginCommand"); - p->external_ungrab_command = get_string_resource(dpy, - "externalUngrabCommand", - "ExternalUngrabCommand"); - p->auth_warning_slack = get_integer_resource(dpy, "authWarningSlack", - "Integer"); - - /* If "*splash" is unset, default to true. */ - { - char *s = get_string_resource (dpy, "splash", "Boolean"); - if (s) - free (s); - else - p->splash_p = True; - } - - /* If "*grabDesktopImages" is unset, default to true. */ - { - char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean"); - if (s) - free (s); - else - p->grab_desktop_p = True; - } - - p->use_xidle_extension = get_boolean_resource (dpy, "xidleExtension","Boolean"); -#if 0 /* obsolete. */ - p->use_sgi_saver_extension = get_boolean_resource (dpy, - "sgiSaverExtension", - "Boolean"); -#endif -#ifdef HAVE_XINPUT - p->use_xinput_extension = get_boolean_resource (dpy, "xinputExtensionDev", - "Boolean"); -#endif -#if 0 /* broken and evil. */ - p->use_mit_saver_extension = get_boolean_resource (dpy, - "mitSaverExtension", - "Boolean"); -#endif - - p->use_proc_interrupts = get_boolean_resource (dpy, - "procInterrupts", "Boolean"); - - p->getviewport_full_of_lies_p = - get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean"); - - get_screenhacks (dpy, p); /* Parse the "programs" resource. */ - - { - char *s = get_string_resource (dpy, "selected", "Integer"); - if (!s || !*s) - p->selected_hack = -1; - else - p->selected_hack = get_integer_resource (dpy, "selected", "Integer"); - if (s) free (s); - if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count) - p->selected_hack = -1; - } - - { - char *s = get_string_resource (dpy, "mode", "Mode"); - if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK; - else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY; - else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK; - else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME; - else p->mode = RANDOM_HACKS; - if (s) free (s); - } - - { - char *s = get_string_resource (dpy, "textMode", "TextMode"); - if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL; - else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL; - else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE; - else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM; - else p->tmode = TEXT_DATE; - if (s) free (s); - } - - if (system_default_screenhack_count) /* note: first_time is also true */ - { - merge_system_screenhacks (dpy, p, system_default_screenhacks, - system_default_screenhack_count); - free_screenhack_list (system_default_screenhacks, - system_default_screenhack_count); - system_default_screenhacks = 0; - system_default_screenhack_count = 0; - } - - if (p->debug_p) - { - p->xsync_p = True; - p->verbose_p = True; - p->timestamp_p = True; - p->initial_delay = 0; - } - - /* Throttle the various timeouts to reasonable values after reading the - disk file. */ - stop_the_insanity (p); -} - - -/* If there are any hacks in the system-wide defaults that are not in - the ~/.xscreensaver file, add the new ones to the end of the list. - This does *not* actually save the file. - */ -static void -merge_system_screenhacks (Display *dpy, saver_preferences *p, - screenhack **system_list, int system_count) -{ - /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy, - so fuck it. */ - - int made_space = 0; - int i; - for (i = 0; i < system_count; i++) - { - int j; - Bool matched_p = False; - - for (j = 0; j < p->screenhacks_count; j++) - { - char *name; - if (!system_list[i]->name) - system_list[i]->name = make_hack_name (dpy, - system_list[i]->command); - - name = p->screenhacks[j]->name; - if (!name) - name = make_hack_name (dpy, p->screenhacks[j]->command); - - matched_p = !strcasecmp (name, system_list[i]->name); - - if (name != p->screenhacks[j]->name) - free (name); - - if (matched_p) - break; - } - - if (!matched_p) - { - /* We have an entry in the system-wide list that is not in the - user's .xscreensaver file. Add it to the end. - Note that p->screenhacks is a single malloc block, not a - linked list, so we have to realloc it. - */ - screenhack *oh = system_list[i]; - screenhack *nh = (screenhack *) malloc (sizeof(screenhack)); - - if (made_space == 0) - { - made_space = 10; - p->screenhacks = (screenhack **) - realloc (p->screenhacks, - (p->screenhacks_count + made_space + 1) - * sizeof(screenhack)); - if (!p->screenhacks) abort(); - } - - nh->enabled_p = oh->enabled_p; - nh->visual = oh->visual ? strdup(oh->visual) : 0; - nh->name = oh->name ? strdup(oh->name) : 0; - nh->command = oh->command ? strdup(oh->command) : 0; - - p->screenhacks[p->screenhacks_count++] = nh; - p->screenhacks[p->screenhacks_count] = 0; - made_space--; - -#if 0 - fprintf (stderr, "%s: noticed new hack: %s\n", blurb(), - (nh->name ? nh->name : make_hack_name (dpy, nh->command))); -#endif - } - } -} - - - -/* Parsing the programs resource. - */ - -screenhack * -parse_screenhack (const char *line) -{ - screenhack *h = (screenhack *) calloc (1, sizeof(*h)); - const char *s; - - h->enabled_p = True; - - while (isspace(*line)) line++; /* skip whitespace */ - if (*line == '-') /* handle "-" */ - { - h->enabled_p = False; - line++; - while (isspace(*line)) line++; /* skip whitespace */ - } - - s = line; /* handle "visual:" */ - while (*line && *line != ':' && *line != '"' && !isspace(*line)) - line++; - if (*line != ':') - line = s; - else - { - h->visual = (char *) malloc (line-s+1); - strncpy (h->visual, s, line-s); - h->visual[line-s] = 0; - if (*line == ':') line++; /* skip ":" */ - while (isspace(*line)) line++; /* skip whitespace */ - } - - if (*line == '"') /* handle "name" */ - { - line++; - s = line; - while (*line && *line != '"') - line++; - h->name = (char *) malloc (line-s+1); - strncpy (h->name, s, line-s); - h->name[line-s] = 0; - if (*line == '"') line++; /* skip "\"" */ - while (isspace(*line)) line++; /* skip whitespace */ - } - - h->command = format_command (line, False); /* handle command */ - return h; -} - - -static char * -format_command (const char *cmd, Bool wrap_p) -{ - int tab = 30; - int col = tab; - char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1)); - const char *in = cmd; - char *out = cmd2; - while (*in) - { - /* shrink all whitespace to one space, for the benefit of the "demo" - mode display. We only do this when we can easily tell that the - whitespace is not significant (no shell metachars). - */ - switch (*in) - { - case '\'': case '"': case '`': case '\\': - /* Metachars are scary. Copy the rest of the line unchanged. */ - while (*in) - *out++ = *in++, col++; - break; - - case ' ': case '\t': - /* Squeeze all other whitespace down to one space. */ - while (*in == ' ' || *in == '\t') - in++; - *out++ = ' ', col++; - break; - - default: - /* Copy other chars unchanged. */ - *out++ = *in++, col++; - break; - } - } - - *out = 0; - - /* Strip trailing whitespace */ - while (out > cmd2 && isspace (out[-1])) - *(--out) = 0; - - return cmd2; -} - - -/* Returns a new string describing the shell command. - This may be just the name of the program, capitalized. - It also may be something from the resource database (gotten - by looking for "hacks.XYZ.name", where XYZ is the program.) - */ -char * -make_hack_name (Display *dpy, const char *shell_command) -{ - char *s = strdup (shell_command); - char *s2; - char res_name[255]; - - for (s2 = s; *s2; s2++) /* truncate at first whitespace */ - if (isspace (*s2)) - { - *s2 = 0; - break; - } - - s2 = strrchr (s, '/'); /* if pathname, take last component */ - if (s2) - { - s2 = strdup (s2+1); - free (s); - s = s2; - } - - if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */ - s[50] = 0; - - sprintf (res_name, "hacks.%s.name", s); /* resource? */ - s2 = get_string_resource (dpy, res_name, res_name); - if (s2) - { - free (s); - return s2; - } - - for (s2 = s; *s2; s2++) /* if it has any capitals, return it */ - if (*s2 >= 'A' && *s2 <= 'Z') - return s; - - if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */ - s[0] -= 'a'-'A'; - if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */ - s[1] -= 'a'-'A'; - if (s[0] == 'G' && s[1] == 'l' && - s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */ - s[1] -= 'a'-'A', - s[2] -= 'a'-'A'; - return s; -} - - -char * -format_hack (Display *dpy, screenhack *hack, Bool wrap_p) -{ - int tab = 32; - int size; - char *h2, *out, *s; - int col = 0; - - char *def_name = make_hack_name (dpy, hack->command); - - /* Don't ever write out a name for a hack if it's the same as the default. - */ - if (hack->name && !strcmp (hack->name, def_name)) - { - free (hack->name); - hack->name = 0; - } - free (def_name); - - size = (2 * (strlen(hack->command) + - (hack->visual ? strlen(hack->visual) : 0) + - (hack->name ? strlen(hack->name) : 0) + - tab)); - h2 = (char *) malloc (size); - out = h2; - - if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */ - - if (hack->visual && *hack->visual) /* write visual name */ - { - if (hack->enabled_p) *out++ = ' '; - *out++ = ' '; - strcpy (out, hack->visual); - out += strlen (hack->visual); - *out++ = ':'; - *out++ = ' '; - } - - *out = 0; - col = string_columns (h2, strlen (h2), 0); - - if (hack->name && *hack->name) /* write pretty name */ - { - int L = (strlen (hack->name) + 2); - if (L + col < tab) - out = stab_to (out, col, tab - L - 2); - else - *out++ = ' '; - *out++ = '"'; - strcpy (out, hack->name); - out += strlen (hack->name); - *out++ = '"'; - *out = 0; - - col = string_columns (h2, strlen (h2), 0); - if (wrap_p && col >= tab) - out = stab_to (out, col, 77); - else - *out++ = ' '; - - if (out >= h2+size) abort(); - } - - *out = 0; - col = string_columns (h2, strlen (h2), 0); - out = stab_to (out, col, tab); /* indent */ - - if (out >= h2+size) abort(); - s = format_command (hack->command, wrap_p); - strcpy (out, s); - out += strlen (s); - free (s); - *out = 0; - - return h2; -} - - -static void -get_screenhacks (Display *dpy, saver_preferences *p) -{ - int i, j; - int start = 0; - int end = 0; - int size; - char *d; - - d = get_string_resource (dpy, "monoPrograms", "MonoPrograms"); - if (d && !*d) { free(d); d = 0; } - if (!d) - d = get_string_resource (dpy, "colorPrograms", "ColorPrograms"); - if (d && !*d) { free(d); d = 0; } - - if (d) - { - fprintf (stderr, - "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\ - see the manual for details.\n", blurb()); - free(d); - } - - d = get_string_resource (dpy, "programs", "Programs"); - - free_screenhack_list (p->screenhacks, p->screenhacks_count); - p->screenhacks = 0; - p->screenhacks_count = 0; - - if (!d || !*d) - return; - - size = strlen (d); - - - /* Count up the number of newlines (which will be equal to or larger than - one less than the number of hacks.) - */ - for (i = j = 0; d[i]; i++) - if (d[i] == '\n') - j++; - j++; - - p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *)); - - /* Iterate over the lines in `d' (the string with newlines) - and make new strings to stuff into the `screenhacks' array. - */ - p->screenhacks_count = 0; - while (start < size) - { - /* skip forward over whitespace. */ - while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n') - start++; - - /* skip forward to newline or end of string. */ - end = start; - while (d[end] != 0 && d[end] != '\n') - end++; - - /* null terminate. */ - d[end] = 0; - - p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start); - if (p->screenhacks_count >= i) - abort(); - - start = end+1; - } - - free (d); - - if (p->screenhacks_count == 0) - { - free (p->screenhacks); - p->screenhacks = 0; - } -} - - -/* Make sure all the values in the preferences struct are sane. - */ -static void -stop_the_insanity (saver_preferences *p) -{ - if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */ - if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */ - if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */ - if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */ - if (p->notice_events_timeout <= 0) - p->notice_events_timeout = 10000; /* 10 secs */ - if (p->fade_seconds <= 0 || p->fade_ticks <= 0) - p->fade_p = False; - if (! p->fade_p) p->unfade_p = False; - - /* The DPMS settings may have the value 0. - But if they are negative, or are a range less than 10 seconds, - reset them to sensible defaults. (Since that must be a mistake.) - */ - if (p->dpms_standby != 0 && - p->dpms_standby < 10 * 1000) - p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */ - if (p->dpms_suspend != 0 && - p->dpms_suspend < 10 * 1000) - p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */ - if (p->dpms_off != 0 && - p->dpms_off < 10 * 1000) - p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */ - - /* suspend may not be greater than off, unless off is 0. - standby may not be greater than suspend, unless suspend is 0. - */ - if (p->dpms_off != 0 && - p->dpms_suspend > p->dpms_off) - p->dpms_suspend = p->dpms_off; - if (p->dpms_suspend != 0 && - p->dpms_standby > p->dpms_suspend) - p->dpms_standby = p->dpms_suspend; - - /* These fixes above ignores the case - suspend = 0 and standby > off ... - */ - if (p->dpms_off != 0 && - p->dpms_standby > p->dpms_off) - p->dpms_standby = p->dpms_off; - - - if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */ - p->dpms_suspend == 0 && - p->dpms_off == 0 && - !(p->dpms_quickoff_p) /* ... but we want to do DPMS quick off */ - ) - p->dpms_enabled_p = False; - - - /* Set watchdog timeout to about half of the cycle timeout, but - don't let it be faster than 1/2 minute or slower than 1 minute. - */ - p->watchdog_timeout = p->cycle * 0.6; - if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */ - if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */ - - if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0; -/* if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100; */ - - if (p->auth_warning_slack < 0) p->auth_warning_slack = 0; - if (p->auth_warning_slack > 300) p->auth_warning_slack = 300; -} - - -Bool -senesculent_p (void) -{ - /* If you are in here because you're planning on disabling this warning - before redistributing my software, please don't. - - I sincerely request that you do one of the following: - - 1: leave this code intact and this warning in place, -OR- - - 2: Remove xscreensaver from your distribution. - - I would seriously prefer that you not distribute my software at all - than that you distribute one version and then never update it for - years. - - I am *constantly* getting email from users reporting bugs that have - been fixed for literally years who have no idea that the software - they are running is years out of date. Yes, it would be great if we - lived in the ideal world where people checked that they were running - the latest release before they report a bug, but we don't. To most - people, "running the latest release" is synonymous with "running the - latest release that my distro packages for me." - - When they even bother to tell me what version they're running, I - say, "That version is three years old!", and they say "But this is - the latest version my distro ships". Then I say, "your distro - sucks", and they say "but I don't know how to compile from source, - herp derp I eat paste", and *everybody* goes away unhappy. - - It wastes an enormous amount of my time, but worse than that, it - does a grave disservice to the users, who are stuck experiencing - bugs that are already fixed! These users think they are running the - latest release, and they are not. They would like to be running the - actual latest release, but they don't know how, because their distro - makes that very difficult for them. It's terrible for everyone, and - kind of makes me regret ever having released this software in the - first place. - - So seriously. I ask that if you're planning on disabling this - obsolescence warning, that you instead just remove xscreensaver from - your distro entirely. Everybody will be happier that way. Check - out gnome-screensaver instead, I understand it's really nice. - - Of course, my license allows you to ignore me and do whatever the - fuck you want, but as the author, I hope you will have the common - courtesy of complying with my request. - - Thank you! - - jwz, 2014, 2016, 2018. - - PS: In particular, since Debian refuses to upgrade software on any - kind of rational timeline, I have asked that they stop shipping - xscreensaver at all. They have refused. Instead of upgrading the - software, they simply patched out this warning. - - If you want to witness the sad state of the open source peanut - gallery, look no farther than the comments on my blog: - http://jwz.org/b/yiYo - - Many of these people fall back on their go-to argument of, "If it is - legal, it must be right." If you believe in that rhetorical device - then you are a terrible person, and possibly a sociopath. - - There are also the armchair lawyers who say "Well, instead of - *asking* people to do the right thing out of common courtesy, you - should just change your license to prohibit them from acting - amorally." Again, this is the answer of a sociopath, but that aside, - if you devote even a second's thought to this you will realize that - the end result of this would be for distros like Debian to just keep - shipping the last version with the old license and then never - upgrading it again -- which would be the worst possible outcome for - everyone involved, most especially the users. - */ - time_t now = time ((time_t *) 0); /* d */ - struct tm *tm = localtime (&now); /* o */ - const char *s = screensaver_id; /* n */ - char mon[4], year[5]; /* ' */ - int m, y, mrnths; /* t */ - s = strchr (s, ' '); if (!s) abort(); s++; /* */ - s = strchr (s, '('); if (!s) abort(); s++; /* d */ - s = strchr (s, '-'); if (!s) abort(); s++; /* o */ - strncpy (mon, s, 3); /* o */ - mon[3] = 0; /* */ - s = strchr (s, '-'); if (!s) abort(); s++; /* e */ - strncpy (year, s, 4); /* e */ - year[4] = 0; /* t */ - y = atoi (year); /* , */ - if (!strcmp(mon, "Jan")) m = 0; /* */ - else if (!strcmp(mon, "Feb")) m = 1; /* s */ - else if (!strcmp(mon, "Mar")) m = 2; /* t */ - else if (!strcmp(mon, "Apr")) m = 3; /* o */ - else if (!strcmp(mon, "May")) m = 4; /* p */ - else if (!strcmp(mon, "Jun")) m = 5; /* , */ - else if (!strcmp(mon, "Jul")) m = 6; /* */ - else if (!strcmp(mon, "Aug")) m = 7; /* s */ - else if (!strcmp(mon, "Sep")) m = 8; /* t */ - else if (!strcmp(mon, "Oct")) m = 9; /* a */ - else if (!strcmp(mon, "Nov")) m = 10; /* a */ - else if (!strcmp(mon, "Dec")) m = 11; /* a */ - else abort(); /* h */ - mrnths = ((((tm->tm_year + 1900) * 12) + tm->tm_mon) - /* h */ - (y * 12 + m)); /* h */ - /* p */ - return (mrnths >= 34); /* . */ + FAIL: + if (buf) free (buf); + return -1; } diff --git a/driver/prefs.h b/driver/prefs.h index c494eaf..14d5542 100644 --- a/driver/prefs.h +++ b/driver/prefs.h @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1993-2018 Jamie Zawinski +/* xscreensaver, Copyright © 1993-2021 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 @@ -12,28 +12,10 @@ #ifndef __XSCREENSAVER_PREFS_H__ #define __XSCREENSAVER_PREFS_H__ -#include "types.h" - -extern void load_init_file (Display *, saver_preferences *); -extern Bool init_file_changed_p (saver_preferences *); -extern int write_init_file (Display *, - saver_preferences *, const char *version_string, - Bool verbose_p); -const char *init_file_name (void); -extern Bool senesculent_p (void); - -extern screenhack *parse_screenhack (const char *line); -extern void free_screenhack (screenhack *); -extern char *format_hack (Display *, screenhack *, Bool wrap_p); -char *make_hack_name (Display *, const char *shell_command); - -/* From dpms.c */ -extern void sync_server_dpms_settings (Display *, Bool enabled_p, - Bool dpms_quickoff_p, - int standby_secs, int suspend_secs, - int off_secs, - Bool verbose_p); - -extern void store_dpms_offset(int offset); +extern int parse_init_file (const char *name, + void (*handler) (int lineno, + const char *key, const char *val, + void *closure), + void *closure); #endif /* __XSCREENSAVER_PREFS_H__ */ diff --git a/driver/prefsw.c b/driver/prefsw.c new file mode 100644 index 0000000..654016c --- /dev/null +++ b/driver/prefsw.c @@ -0,0 +1,1561 @@ +/* prefs.c --- reading and writing the ~/.xscreensaver file. + * xscreensaver, Copyright © 1998-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include /* for PATH_MAX */ +#include + +#include +#include +#include + +#include "version.h" +#include "types.h" +#include "prefs.h" +#include "resources.h" +#include "blurb.h" + +/* don't use realpath() on fedora system */ +#ifdef _FORTIFY_SOURCE +# undef HAVE_REALPATH +#endif + + +extern char *progclass; + +static void get_screenhacks (Display *, saver_preferences *); +static char *format_command (const char *cmd, Bool wrap_p); +static void merge_system_screenhacks (Display *, saver_preferences *, + screenhack **system_list, int count); +static void stop_the_insanity (saver_preferences *p); + +static char *format_hack (Display *, screenhack *, Bool wrap_p); + +static char * +chase_symlinks (const char *file) +{ +# ifdef HAVE_REALPATH + if (file) + { +# ifndef PATH_MAX +# ifdef MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# else +# define PATH_MAX 2048 +# endif +# endif + char buf[PATH_MAX]; + if (realpath (file, buf)) + return strdup (buf); + +/* sprintf (buf, "%.100s: realpath %.200s", blurb(), file); + perror(buf);*/ + } +# endif /* HAVE_REALPATH */ + return 0; +} + + +static Bool +i_am_a_nobody (uid_t uid) +{ + struct passwd *p; + + p = getpwnam ("nobody"); + if (! p) p = getpwnam ("noaccess"); + if (! p) p = getpwnam ("daemon"); + + if (! p) /* There is no nobody? */ + return False; + + return (uid == p->pw_uid); +} + + +const char * +init_file_name (void) +{ + static char *file = 0; + + if (!file) + { + uid_t uid = getuid (); + const char *home = getenv("HOME"); + + if (i_am_a_nobody (uid) || !home || !*home) + { + /* If we're running as nobody, then use root's .xscreensaver file + (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely + to be different -- without this, xscreensaver-settings would + appear to have no effect when the luser is running as root.) + */ + struct passwd *p = getpwuid (uid); + if (!p || !p->pw_name || !*p->pw_name) + { + fprintf (stderr, "%s: couldn't get user info of uid %d\n", + blurb(), getuid ()); + } + else if (!p->pw_dir || !*p->pw_dir) + { + fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n", + blurb(), (p->pw_name ? p->pw_name : "???")); + } + else + { + home = p->pw_dir; + } + } + if (home && *home) + { + const char *name = ".xscreensaver"; + file = (char *) malloc(strlen(home) + strlen(name) + 2); + strcpy(file, home); + if (!*home || home[strlen(home)-1] != '/') + strcat(file, "/"); + strcat(file, name); + } + else + { + file = ""; + } + } + + if (file && *file) + return file; + else + return 0; +} + + +static const char * +init_file_tmp_name (void) +{ + static char *file = 0; + if (!file) + { + const char *name = init_file_name(); + const char *suffix = ".tmp"; + + char *n2 = chase_symlinks (name); + if (n2) name = n2; + + if (!name || !*name) + file = ""; + else + { + file = (char *) malloc(strlen(name) + strlen(suffix) + 2); + strcpy(file, name); + strcat(file, suffix); + } + + if (n2) free (n2); + } + + if (file && *file) + return file; + else + return 0; +} + +static const char * const prefs[] = { + "timeout", + "cycle", + "lock", + "lockVTs", /* not saved */ + "lockTimeout", + "passwdTimeout", + "visualID", + "installColormap", + "verbose", + "splash", + "splashDuration", + "quad", + "demoCommand", + "prefsCommand", + "newLoginCommand", + "helpURL", /* not saved */ + "loadURL", /* not saved */ + "newLoginCommand", /* not saved */ + "externalUngrabCommand", /* not saved */ + "nice", + "memoryLimit", /* not saved */ + "fade", + "unfade", + "fadeSeconds", + "fadeTicks", /* not saved */ + "logFile", /* not saved */ + "ignoreUninstalledPrograms", + "font", + "dpmsEnabled", + "dpmsQuickOff", + "dpmsStandby", + "dpmsSuspend", + "dpmsOff", + "grabDesktopImages", + "grabVideoFrames", + "chooseRandomImages", + "imageDirectory", + "mode", + "selected", + "textMode", + "textLiteral", + "textFile", + "textProgram", + "textURL", + "dialogTheme", + "", + "programs", + "", + "pointerHysteresis", + "bourneShell", /* not saved -- X resources only */ + "authWarningSlack", + 0 +}; + +static char * +strip (char *s) +{ + char *s2; + while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n') + s++; + for (s2 = s; *s2; s2++) + ; + for (s2--; s2 >= s; s2--) + if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n') + *s2 = 0; + else + break; + return s; +} + + +/* Reading + */ + +static int +handle_entry (XrmDatabase *db, const char *key, const char *value, + const char *filename, int line) +{ + int i; + for (i = 0; prefs[i]; i++) + if (*prefs[i] && !strcasecmp(key, prefs[i])) + { + char *val = strdup(value); + char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10); + strcpy(spec, progclass); + strcat(spec, "."); + strcat(spec, prefs[i]); + + XrmPutStringResource (db, spec, val); + + free(spec); + free(val); + return 0; + } + + /* fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n", + blurb(), filename, line, key); */ + return 1; +} + + +struct parser_closure { + const char *file; + saver_preferences *prefs; +}; + +static void line_handler (int lineno, + const char *key, const char *val, + void *closure) +{ + struct parser_closure *c = (struct parser_closure *) closure; + saver_preferences *p = c->prefs; + if (!p->db) abort(); + handle_entry (&p->db, key, val, c->file, lineno); +} + +static int +parse_init_file_1 (saver_preferences *p) +{ + time_t write_date = 0; + const char *name = init_file_name(); + struct parser_closure C; + struct stat st; + int status; + + if (!name) return 0; + + C.file = name; + C.prefs = p; + + if (stat(name, &st) == 0) + write_date = st.st_mtime; + else + { + p->init_file_date = 0; + return 0; + } + + status = parse_init_file (name, line_handler, &C); + + p->init_file_date = write_date; + return status; +} + + +Bool +init_file_changed_p (saver_preferences *p) +{ + const char *name = init_file_name(); + struct stat st; + + if (!name) return False; + + if (stat(name, &st) != 0) + return False; + + if (p->init_file_date == st.st_mtime) + return False; + + return True; +} + + +/* Writing + */ + +static int +tab_to (FILE *out, int from, int to) +{ + int tab_width = 8; + int to_mod = (to / tab_width) * tab_width; + while (from < to_mod) + { + fprintf(out, "\t"); + from = (((from / tab_width) + 1) * tab_width); + } + while (from < to) + { + fprintf(out, " "); + from++; + } + return from; +} + +static char * +stab_to (char *out, int from, int to) +{ + int tab_width = 8; + int to_mod = (to / tab_width) * tab_width; + while (from < to_mod) + { + *out++ = '\t'; + from = (((from / tab_width) + 1) * tab_width); + } + while (from < to) + { + *out++ = ' '; + from++; + } + return out; +} + +static int +string_columns (const char *string, int length, int start) +{ + int tab_width = 8; + int col = start; + const char *end = string + length; + while (string < end) + { + if (*string == '\n') + col = 0; + else if (*string == '\t') + col = (((col / tab_width) + 1) * tab_width); + else + col++; + string++; + } + return col; +} + + +static void +write_entry (FILE *out, const char *key, const char *value) +{ + char *v = strdup(value ? value : ""); + char *v2 = v; + char *nl = 0; + int col; + Bool programs_p = (!strcmp(key, "programs")); + int tab = (programs_p ? 32 : 16); + Bool first = True; + + fprintf(out, "%s:", key); + col = strlen(key) + 1; + + if (strlen(key) > 14) + col = tab_to (out, col, 20); + + while (1) + { + if (!programs_p) + v2 = strip(v2); + nl = strchr(v2, '\n'); + if (nl) + *nl = 0; + + if (first && programs_p) + { + col = tab_to (out, col, 77); + fprintf (out, " \\\n"); + col = 0; + } + + if (first) + first = False; + else + { + col = tab_to (out, col, 75); + fprintf (out, " \\n\\\n"); + col = 0; + } + + if (!programs_p) + col = tab_to (out, col, tab); + + if (programs_p && + string_columns(v2, strlen (v2), col) + col > 75) + { + int L = strlen (v2); + int start = 0; + int end = start; + while (start < L) + { + while (v2[end] == ' ' || v2[end] == '\t') + end++; + while (v2[end] != ' ' && v2[end] != '\t' && + v2[end] != '\n' && v2[end] != 0) + end++; + if (string_columns (v2 + start, (end - start), col) >= 74) + { + col = tab_to (out, col, 75); + fprintf(out, " \\\n"); + col = tab_to (out, 0, tab + 2); + while (v2[start] == ' ' || v2[start] == '\t') + start++; + } + + col = string_columns (v2 + start, (end - start), col); + while (start < end) + fputc(v2[start++], out); + } + } + else + { + fprintf (out, "%s", v2); + col += string_columns(v2, strlen (v2), col); + } + + if (nl) + v2 = nl + 1; + else + break; + } + + fprintf(out, "\n"); + free(v); +} + +int +write_init_file (Display *dpy, + saver_preferences *p, const char *version_string, + Bool verbose_p) +{ + int status = -1; + const char *name = init_file_name(); + const char *tmp_name = init_file_tmp_name(); + char *n2 = chase_symlinks (name); + struct stat st; + int i, j; + + /* Kludge, since these aren't in the saver_preferences struct as strings... + */ + char *visual_name; + char *programs; + Bool overlay_stderr_p; + char *stderr_font; + FILE *out; + + if (!name) goto END; + + if (n2) name = n2; + + /* Throttle the various timeouts to reasonable values before writing + the file to disk. */ + stop_the_insanity (p); + + + if (verbose_p) + fprintf (stderr, "%s: writing \"%s\"\n", blurb(), name); + + unlink (tmp_name); + out = fopen(tmp_name, "w"); + if (!out) + { + char *buf = (char *) malloc(1024 + strlen(name)); + sprintf(buf, "%s: error writing \"%s\"", blurb(), name); + perror(buf); + free(buf); + goto END; + } + + /* Give the new .xscreensaver file the same permissions as the old one; + except ensure that it is readable and writable by owner, and not + executable. Extra hack: if we're running as root, make the file + be world-readable (so that the daemon, running as "nobody", will + still be able to read it.) + */ + if (stat(name, &st) == 0) + { + mode_t mode = st.st_mode; + mode |= S_IRUSR | S_IWUSR; /* read/write by user */ + mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */ + + if (getuid() == (uid_t) 0) /* read by group/other */ + mode |= S_IRGRP | S_IROTH; + + if (fchmod (fileno(out), mode) != 0) + { + char *buf = (char *) malloc(1024 + strlen(name)); + sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(), + tmp_name, (unsigned int) mode); + perror(buf); + free(buf); + goto END; + } + } + + /* Kludge, since these aren't in the saver_preferences struct... */ + visual_name = get_string_resource (dpy, "visualID", "VisualID"); + programs = 0; + overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean"); + stderr_font = get_string_resource (dpy, "font", "Font"); + + i = 0; + { + char *ss; + char **hack_strings = (char **) + calloc (p->screenhacks_count, sizeof(char *)); + + for (j = 0; j < p->screenhacks_count; j++) + { + hack_strings[j] = format_hack (dpy, p->screenhacks[j], True); + i += strlen (hack_strings[j]); + i += 2; + } + + ss = programs = (char *) malloc(i + 10); + *ss = 0; + for (j = 0; j < p->screenhacks_count; j++) + { + strcat (ss, hack_strings[j]); + free (hack_strings[j]); + ss += strlen(ss); + *ss++ = '\n'; + *ss = 0; + } + free (hack_strings); + } + + { + struct passwd *pw = getpwuid (getuid ()); + char *whoami = (pw && pw->pw_name && *pw->pw_name + ? pw->pw_name + : ""); + time_t now = time ((time_t *) 0); + char *timestr = (char *) ctime (&now); + char *nl = (char *) strchr (timestr, '\n'); + if (nl) *nl = 0; + fprintf (out, + "# %s Preferences File\n" + "# Written by %s %s for %s on %s.\n" + "# https://www.jwz.org/xscreensaver/\n" + "\n", + progclass, progname, version_string, whoami, timestr); + } + + for (j = 0; prefs[j]; j++) + { + char buf[255]; + const char *pr = prefs[j]; + enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time + } type = pref_str; + const char *s = 0; + int i = 0; + Bool b = False; + Time t = 0; + + if (pr && !*pr) + { + fprintf(out, "\n"); + continue; + } + +# undef CHECK +# define CHECK(X) else if (!strcmp(pr, X)) + if (!pr || !*pr) ; + CHECK("timeout") type = pref_time, t = p->timeout; + CHECK("cycle") type = pref_time, t = p->cycle; + CHECK("lock") type = pref_bool, b = p->lock_p; + CHECK("lockVTs") continue; /* don't save, unused */ + CHECK("lockTimeout") type = pref_time, t = p->lock_timeout; + CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout; + CHECK("visualID") type = pref_str, s = visual_name; + CHECK("installColormap") type = pref_bool, b = p->install_cmap_p; + CHECK("verbose") type = pref_bool, b = p->verbose_p; + CHECK("splash") type = pref_bool, b = p->splash_p; + CHECK("splashDuration") type = pref_time, t = p->splash_duration; + CHECK("quad") continue; /* don't save */ + CHECK("demoCommand") type = pref_str, s = p->demo_command; + CHECK("prefsCommand") continue; /* don't save, unused */ + CHECK("helpURL") continue; /* don't save */ + CHECK("loadURL") continue; /* don't save */ + CHECK("newLoginCommand") continue; /* don't save */ + CHECK("externalUngrabCommand") continue; /* don't save */ + CHECK("dialogTheme") type = pref_str, s = p->dialog_theme; + CHECK("nice") type = pref_int, i = p->nice_inferior; + CHECK("memoryLimit") continue; /* don't save */ + CHECK("fade") type = pref_bool, b = p->fade_p; + CHECK("unfade") type = pref_bool, b = p->unfade_p; + CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds; + CHECK("fadeTicks") continue; /* don't save */ + CHECK("captureStdout") continue; /* don't save */ + CHECK("logFile") continue; /* don't save */ + CHECK("ignoreUninstalledPrograms") + type = pref_bool, b = p->ignore_uninstalled_p; + + CHECK("font") type = pref_str, s = stderr_font; + + CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p; + CHECK("dpmsQuickOff") type = pref_bool, b = p->dpms_quickoff_p; + CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby; + CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend; + CHECK("dpmsOff") type = pref_time, t = p->dpms_off; + + CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p; + CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p; + CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p; + CHECK("imageDirectory") type =pref_str, s = p->image_directory; + + CHECK("mode") type = pref_str, + s = (p->mode == ONE_HACK ? "one" : + p->mode == BLANK_ONLY ? "blank" : + p->mode == DONT_BLANK ? "off" : + p->mode == RANDOM_HACKS_SAME + ? "random-same" + : "random"); + CHECK("selected") type = pref_int, i = p->selected_hack; + + CHECK("textMode") type = pref_str, + s = (p->tmode == TEXT_URL ? "url" : + p->tmode == TEXT_LITERAL ? "literal" : + p->tmode == TEXT_FILE ? "file" : + p->tmode == TEXT_PROGRAM ? "program" : + "date"); + CHECK("textLiteral") type = pref_str, s = p->text_literal; + CHECK("textFile") type = pref_str, s = p->text_file; + CHECK("textProgram") type = pref_str, s = p->text_program; + CHECK("textURL") type = pref_str, s = p->text_url; + + CHECK("programs") type = pref_str, s = programs; + CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis; + CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p; + CHECK("overlayTextBackground") continue; /* don't save */ + CHECK("overlayTextForeground") continue; /* don't save */ + CHECK("bourneShell") continue; /* don't save */ + CHECK("authWarningSlack") type = pref_int, i = p->auth_warning_slack; + else + { + fprintf (stderr, "%s: internal error: key %s\n", blurb(), pr); + abort(); + } +# undef CHECK + + switch (type) + { + case pref_str: + break; + case pref_int: + sprintf(buf, "%d", i); + s = buf; + break; + case pref_bool: + s = b ? "True" : "False"; + break; + case pref_time: + { + unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000); + if (sec >= 60) + { + min += (sec / 60); + sec %= 60; + } + if (min >= 60) + { + hour += (min / 60); + min %= 60; + } + sprintf (buf, "%u:%02u:%02u", hour, min, sec); + s = buf; + } + break; + case pref_byte: + { + if (i >= (1<<30) && i == ((i >> 30) << 30)) + sprintf(buf, "%dG", i >> 30); + else if (i >= (1<<20) && i == ((i >> 20) << 20)) + sprintf(buf, "%dM", i >> 20); + else if (i >= (1<<10) && i == ((i >> 10) << 10)) + sprintf(buf, "%dK", i >> 10); + else + sprintf(buf, "%d", i); + s = buf; + } + break; + default: + abort(); + break; + } + + if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode"))) + fprintf(out, "\n"); + + write_entry (out, pr, s); + } + + fprintf(out, "\n"); + + if (visual_name) free(visual_name); + if (stderr_font) free(stderr_font); + if (programs) free(programs); + + if (fclose(out) == 0) + { + time_t write_date = 0; + + if (stat(tmp_name, &st) == 0) + { + write_date = st.st_mtime; + } + else + { + char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name)); + sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name); + perror(buf); + unlink (tmp_name); + free(buf); + goto END; + } + + if (rename (tmp_name, name) != 0) + { + char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name)); + sprintf(buf, "%s: error renaming \"%s\" to \"%s\"", + blurb(), tmp_name, name); + perror(buf); + unlink (tmp_name); + free(buf); + goto END; + } + else + { + p->init_file_date = write_date; + + /* Since the .xscreensaver file is used for IPC, let's try and make + sure that the bits actually land on the disk right away. */ + /* Update 2020: Apparently here in the future, this sometimes takes + 3+ seconds, so let's not. */ + /* sync(); */ + + status = 0; /* wrote and renamed successfully! */ + } + } + else + { + char *buf = (char *) malloc(1024 + strlen(name)); + sprintf(buf, "%s: error closing \"%s\"", blurb(), name); + perror(buf); + free(buf); + unlink (tmp_name); + goto END; + } + + END: + if (n2) free (n2); + return status; +} + + +/* Parsing the resource database + */ + +static void +free_screenhack (screenhack *hack) +{ + if (hack->visual) free (hack->visual); + if (hack->name) free (hack->name); + free (hack->command); + memset (hack, 0, sizeof(*hack)); + free (hack); +} + +static void +free_screenhack_list (screenhack **list, int count) +{ + int i; + if (!list) return; + for (i = 0; i < count; i++) + if (list[i]) + free_screenhack (list[i]); + free (list); +} + + + +/* Populate `saver_preferences' with the contents of the resource database. + Note that this may be called multiple times -- it is re-run each time + the ~/.xscreensaver file is reloaded. + + This function can be very noisy, since it issues resource syntax errors + and so on. + */ +void +load_init_file (Display *dpy, saver_preferences *p) +{ + static Bool first_time = True; + + screenhack **system_default_screenhacks = 0; + int system_default_screenhack_count = 0; + + if (first_time) + { + /* Get the programs resource before the .xscreensaver file has been + parsed and merged into the resource database for the first time: + this is the value of *programs from the app-defaults file. + Then clear it out so that it will be parsed again later, after + the init file has been read. + */ + get_screenhacks (dpy, p); + system_default_screenhacks = p->screenhacks; + system_default_screenhack_count = p->screenhacks_count; + p->screenhacks = 0; + p->screenhacks_count = 0; + } + + if (parse_init_file_1 (p) != 0) /* file might have gone away */ + if (!first_time) return; + + first_time = False; + + p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous"); + p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean"); + p->lock_p = get_boolean_resource (dpy, "lock", "Boolean"); + p->fade_p = get_boolean_resource (dpy, "fade", "Boolean"); + p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean"); + p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time"); + p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean"); + p->nice_inferior = get_integer_resource (dpy, "nice", "Nice"); + p->splash_p = get_boolean_resource (dpy, "splash", "Boolean"); +# ifdef QUAD_MODE + p->quad_p = get_boolean_resource (dpy, "quad", "Boolean"); +# endif + p->ignore_uninstalled_p = get_boolean_resource (dpy, + "ignoreUninstalledPrograms", + "Boolean"); + + p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time"); + p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time"); + p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time"); + p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time"); + p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time"); + p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer"); + + p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean"); + p->dpms_quickoff_p = get_boolean_resource (dpy, "dpmsQuickOff", "Boolean"); + p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time"); + p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time"); + p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time"); + + p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean"); + p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean"); + p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean"); + p->image_directory = get_string_resource (dpy, + "imageDirectory", + "ImageDirectory"); + + p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral"); + p->text_file = get_string_resource (dpy, "textFile", "TextFile"); + p->text_program = get_string_resource (dpy, "textProgram", "TextProgram"); + p->text_url = get_string_resource (dpy, "textURL", "TextURL"); + + p->shell = get_string_resource (dpy, "bourneShell", "BourneShell"); + + p->demo_command = get_string_resource(dpy, "demoCommand", "Command"); + p->help_url = get_string_resource(dpy, "helpURL", "URL"); + p->load_url_command = get_string_resource(dpy, "loadURL", "Command"); + p->new_login_command = get_string_resource(dpy, "newLoginCommand", + "Command"); + p->external_ungrab_command = get_string_resource(dpy, + "externalUngrabCommand", + "ExternalUngrabCommand"); + + p->dialog_theme = get_string_resource(dpy, "dialogTheme", "String"); + p->auth_warning_slack = get_integer_resource(dpy, "authWarningSlack", + "Integer"); + + /* If "*splash" is unset, default to true. */ + { + char *s = get_string_resource (dpy, "splash", "Boolean"); + if (s) + free (s); + else + p->splash_p = True; + } + + /* If "*grabDesktopImages" is unset, default to true. */ + { + char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean"); + if (s) + free (s); + else + p->grab_desktop_p = True; + } + + get_screenhacks (dpy, p); /* Parse the "programs" resource. */ + + { + char *s = get_string_resource (dpy, "selected", "Integer"); + if (!s || !*s) + p->selected_hack = -1; + else + p->selected_hack = get_integer_resource (dpy, "selected", "Integer"); + if (s) free (s); + if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count) + p->selected_hack = -1; + } + + { + char *s = get_string_resource (dpy, "mode", "Mode"); + if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK; + else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY; + else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK; + else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME; + else p->mode = RANDOM_HACKS; + if (s) free (s); + } + + { + char *s = get_string_resource (dpy, "textMode", "TextMode"); + if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL; + else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL; + else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE; + else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM; + else p->tmode = TEXT_DATE; + if (s) free (s); + } + + if (system_default_screenhack_count) /* note: first_time is also true */ + { + merge_system_screenhacks (dpy, p, system_default_screenhacks, + system_default_screenhack_count); + free_screenhack_list (system_default_screenhacks, + system_default_screenhack_count); + system_default_screenhacks = 0; + system_default_screenhack_count = 0; + } + + if (p->debug_p) + { + p->xsync_p = True; + p->verbose_p = True; + } + + /* Throttle the various timeouts to reasonable values after reading the + disk file. */ + stop_the_insanity (p); +} + + +/* If there are any hacks in the system-wide defaults that are not in + the ~/.xscreensaver file, add the new ones to the end of the list. + This does *not* actually save the file. + */ +static void +merge_system_screenhacks (Display *dpy, saver_preferences *p, + screenhack **system_list, int system_count) +{ + /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy, + so fuck it. */ + + int made_space = 0; + int i; + for (i = 0; i < system_count; i++) + { + int j; + Bool matched_p = False; + + for (j = 0; j < p->screenhacks_count; j++) + { + char *name; + if (!system_list[i]->name) + system_list[i]->name = make_hack_name (dpy, + system_list[i]->command); + + name = p->screenhacks[j]->name; + if (!name) + name = make_hack_name (dpy, p->screenhacks[j]->command); + + matched_p = !strcasecmp (name, system_list[i]->name); + + if (name != p->screenhacks[j]->name) + free (name); + + if (matched_p) + break; + } + + if (!matched_p) + { + /* We have an entry in the system-wide list that is not in the + user's .xscreensaver file. Add it to the end. + Note that p->screenhacks is a single malloc block, not a + linked list, so we have to realloc it. + */ + screenhack *oh = system_list[i]; + screenhack *nh = (screenhack *) malloc (sizeof(screenhack)); + + if (made_space == 0) + { + made_space = 10; + p->screenhacks = (screenhack **) + realloc (p->screenhacks, + (p->screenhacks_count + made_space + 1) + * sizeof(*p->screenhacks)); + if (!p->screenhacks) abort(); + } + + nh->enabled_p = oh->enabled_p; + nh->visual = oh->visual ? strdup(oh->visual) : 0; + nh->name = oh->name ? strdup(oh->name) : 0; + nh->command = oh->command ? strdup(oh->command) : 0; + + p->screenhacks[p->screenhacks_count++] = nh; + p->screenhacks[p->screenhacks_count] = 0; + made_space--; + +#if 0 + fprintf (stderr, "%s: noticed new hack: %s\n", blurb(), + (nh->name ? nh->name : make_hack_name (dpy, nh->command))); +#endif + } + } +} + + + +/* Parsing the programs resource. + */ + +static screenhack * +parse_screenhack (const char *line) +{ + screenhack *h = (screenhack *) calloc (1, sizeof(*h)); + const char *s; + + h->enabled_p = True; + + while (isspace(*line)) line++; /* skip whitespace */ + if (*line == '-') /* handle "-" */ + { + h->enabled_p = False; + line++; + while (isspace(*line)) line++; /* skip whitespace */ + } + + s = line; /* handle "visual:" */ + while (*line && *line != ':' && *line != '"' && !isspace(*line)) + line++; + if (*line != ':') + line = s; + else + { + h->visual = (char *) malloc (line-s+1); + strncpy (h->visual, s, line-s); + h->visual[line-s] = 0; + if (*line == ':') line++; /* skip ":" */ + while (isspace(*line)) line++; /* skip whitespace */ + } + + if (*line == '"') /* handle "name" */ + { + line++; + s = line; + while (*line && *line != '"') + line++; + h->name = (char *) malloc (line-s+1); + strncpy (h->name, s, line-s); + h->name[line-s] = 0; + if (*line == '"') line++; /* skip "\"" */ + while (isspace(*line)) line++; /* skip whitespace */ + } + + h->command = format_command (line, False); /* handle command */ + return h; +} + + +static char * +format_command (const char *cmd, Bool wrap_p) +{ + int tab = 30; + int col = tab; + char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1)); + const char *in = cmd; + char *out = cmd2; + while (*in) + { + /* shrink all whitespace to one space, for the benefit of the "demo" + mode display. We only do this when we can easily tell that the + whitespace is not significant (no shell metachars). + */ + switch (*in) + { + case '\'': case '"': case '`': case '\\': + /* Metachars are scary. Copy the rest of the line unchanged. */ + while (*in) + *out++ = *in++, col++; + break; + + case ' ': case '\t': + /* Squeeze all other whitespace down to one space. */ + while (*in == ' ' || *in == '\t') + in++; + *out++ = ' ', col++; + break; + + default: + /* Copy other chars unchanged. */ + *out++ = *in++, col++; + break; + } + } + + *out = 0; + + /* Strip trailing whitespace */ + while (out > cmd2 && isspace (out[-1])) + *(--out) = 0; + + return cmd2; +} + + +/* Returns a new string describing the shell command. + This may be just the name of the program, capitalized. + It also may be something from the resource database (gotten + by looking for "hacks.XYZ.name", where XYZ is the program.) + */ +char * +make_hack_name (Display *dpy, const char *shell_command) +{ + char *s = strdup (shell_command); + char *s2; + char res_name[255]; + + for (s2 = s; *s2; s2++) /* truncate at first whitespace */ + if (isspace (*s2)) + { + *s2 = 0; + break; + } + + s2 = strrchr (s, '/'); /* if pathname, take last component */ + if (s2) + { + s2 = strdup (s2+1); + free (s); + s = s2; + } + + if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */ + s[50] = 0; + + sprintf (res_name, "hacks.%s.name", s); /* resource? */ + s2 = get_string_resource (dpy, res_name, res_name); + if (s2) + { + free (s); + return s2; + } + + for (s2 = s; *s2; s2++) /* if it has any capitals, return it */ + if (*s2 >= 'A' && *s2 <= 'Z') + return s; + + if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */ + s[0] -= 'a'-'A'; + if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */ + s[1] -= 'a'-'A'; + if (s[0] == 'G' && s[1] == 'l' && + s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */ + s[1] -= 'a'-'A', + s[2] -= 'a'-'A'; + return s; +} + + +static char * +format_hack (Display *dpy, screenhack *hack, Bool wrap_p) +{ + int tab = 32; + int size; + char *h2, *out, *s; + int col = 0; + + char *def_name = make_hack_name (dpy, hack->command); + + /* Don't ever write out a name for a hack if it's the same as the default. + */ + if (hack->name && !strcmp (hack->name, def_name)) + { + free (hack->name); + hack->name = 0; + } + free (def_name); + + size = (2 * (strlen(hack->command) + + (hack->visual ? strlen(hack->visual) : 0) + + (hack->name ? strlen(hack->name) : 0) + + tab)); + h2 = (char *) malloc (size); + out = h2; + + if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */ + + if (hack->visual && *hack->visual) /* write visual name */ + { + if (hack->enabled_p) *out++ = ' '; + *out++ = ' '; + strcpy (out, hack->visual); + out += strlen (hack->visual); + *out++ = ':'; + *out++ = ' '; + } + + *out = 0; + col = string_columns (h2, strlen (h2), 0); + + if (hack->name && *hack->name) /* write pretty name */ + { + int L = (strlen (hack->name) + 2); + if (L + col < tab) + out = stab_to (out, col, tab - L - 2); + else + *out++ = ' '; + *out++ = '"'; + strcpy (out, hack->name); + out += strlen (hack->name); + *out++ = '"'; + *out = 0; + + col = string_columns (h2, strlen (h2), 0); + if (wrap_p && col >= tab) + out = stab_to (out, col, 77); + else + *out++ = ' '; + + if (out >= h2+size) abort(); + } + + *out = 0; + col = string_columns (h2, strlen (h2), 0); + out = stab_to (out, col, tab); /* indent */ + + if (out >= h2+size) abort(); + s = format_command (hack->command, wrap_p); + strcpy (out, s); + out += strlen (s); + free (s); + *out = 0; + + return h2; +} + + +static void +get_screenhacks (Display *dpy, saver_preferences *p) +{ + int i, j; + int start = 0; + int end = 0; + int size; + char *d = get_string_resource (dpy, "programs", "Programs"); + + free_screenhack_list (p->screenhacks, p->screenhacks_count); + p->screenhacks = 0; + p->screenhacks_count = 0; + + if (!d || !*d) + return; + + size = strlen (d); + + + /* Count up the number of newlines (which will be equal to or larger than + one less than the number of hacks.) + */ + for (i = j = 0; d[i]; i++) + if (d[i] == '\n') + j++; + j++; + + p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *)); + + /* Iterate over the lines in `d' (the string with newlines) + and make new strings to stuff into the `screenhacks' array. + */ + p->screenhacks_count = 0; + while (start < size) + { + /* skip forward over whitespace. */ + while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n') + start++; + + /* skip forward to newline or end of string. */ + end = start; + while (d[end] != 0 && d[end] != '\n') + end++; + + /* null terminate. */ + d[end] = 0; + + p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start); + if (p->screenhacks_count >= i) + abort(); + + start = end+1; + } + + free (d); + + if (p->screenhacks_count == 0) + { + free (p->screenhacks); + p->screenhacks = 0; + } +} + + +/* Make sure all the values in the preferences struct are sane. + */ +static void +stop_the_insanity (saver_preferences *p) +{ + if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */ + if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */ + if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */ + if (p->fade_seconds <= 0) + p->fade_p = False; + if (! p->fade_p) p->unfade_p = False; + + /* The DPMS settings may have the value 0. + But if they are negative, or are a range less than 10 seconds, + reset them to sensible defaults. (Since that must be a mistake.) + */ + if (p->dpms_standby != 0 && + p->dpms_standby < 10 * 1000) + p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */ + if (p->dpms_suspend != 0 && + p->dpms_suspend < 10 * 1000) + p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */ + if (p->dpms_off != 0 && + p->dpms_off < 10 * 1000) + p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */ + + /* suspend may not be greater than off, unless off is 0. + standby may not be greater than suspend, unless suspend is 0. + */ + if (p->dpms_off != 0 && + p->dpms_suspend > p->dpms_off) + p->dpms_suspend = p->dpms_off; + if (p->dpms_suspend != 0 && + p->dpms_standby > p->dpms_suspend) + p->dpms_standby = p->dpms_suspend; + + /* These fixes above ignores the case + suspend = 0 and standby > off ... + */ + if (p->dpms_off != 0 && + p->dpms_standby > p->dpms_off) + p->dpms_standby = p->dpms_off; + + if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */ + p->dpms_suspend == 0 && + p->dpms_off == 0 && + !p->dpms_quickoff_p) /* ... but we want to do DPMS quick off */ + p->dpms_enabled_p = False; + + + /* Set watchdog timeout to about half of the cycle timeout, but + don't let it be faster than 1/2 minute or slower than 1 minute. + */ + p->watchdog_timeout = p->cycle * 0.6; + if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */ + if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */ + + if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0; + + if (p->auth_warning_slack < 0) p->auth_warning_slack = 0; + if (p->auth_warning_slack > 300) p->auth_warning_slack = 300; +} + + +Bool +senescent_p (void) +{ + /* If you are in here because you're planning on disabling this warning + before redistributing my software, please don't. + + I sincerely request that you do one of the following: + + 1: leave this code intact and this warning in place, -OR- + + 2: Remove xscreensaver from your distribution. + + I would seriously prefer that you not distribute my software at all + than that you distribute one version and then never update it for + years. + + I am *constantly* getting email from users reporting bugs that have + been fixed for literally years who have no idea that the software + they are running is years out of date. Yes, it would be great if we + lived in the ideal world where people checked that they were running + the latest release before they report a bug, but we don't. To most + people, "running the latest release" is synonymous with "running the + latest release that my distro packages for me." + + When they even bother to tell me what version they're running, I + say, "That version is three years old!", and they say "But this is + the latest version my distro ships". Then I say, "your distro + sucks", and they say "but I don't know how to compile from source, + herp derp I eat paste", and *everybody* goes away unhappy. + + It wastes an enormous amount of my time, but worse than that, it + does a grave disservice to the users, who are stuck experiencing + bugs that are already fixed! These users think they are running the + latest release, and they are not. They would like to be running the + actual latest release, but they don't know how, because their distro + makes that very difficult for them. It's terrible for everyone, and + kind of makes me regret ever having released this software in the + first place. + + So seriously. I ask that if you're planning on disabling this + obsolescence warning, that you instead just remove xscreensaver from + your distro entirely. Everybody will be happier that way. Check + out gnome-screensaver instead, I understand it's really nice. + + Of course, my license allows you to ignore me and do whatever the + fuck you want, but as the author, I hope you will have the common + courtesy of complying with my request. + + Thank you! + + jwz, 2014, 2016, 2018, 2021. + + PS: In particular, since Debian refuses to upgrade software on any + kind of rational timeline, I have asked that they stop shipping + xscreensaver at all. They have refused. Instead of upgrading the + software, they simply patched out this warning. + + If you want to witness the sad state of the open source peanut + gallery, look no farther than the comments on my blog: + http://jwz.org/b/yiYo + + Many of these people fall back on their go-to argument of, "If it is + legal, it must be right." If you believe in that rhetorical device + then you are a terrible person, and possibly a sociopath. + + There are also the armchair lawyers who say "Well, instead of + *asking* people to do the right thing out of common courtesy, you + should just change your license to prohibit them from acting + amorally." Again, this is the answer of a sociopath, but that aside, + if you devote even a second's thought to this you will realize that + the end result of this would be for distros like Debian to just keep + shipping the last version with the old license and then never + upgrading it again -- which would be the worst possible outcome for + everyone involved, most especially the users. + + Also, some have incorrectly characterized this as a "time bomb". + It is a software update notification, nothing more. A "time bomb" + makes software stop working. This merely alerts the user that the + security-critical software that they are running is dangerously out + of date. + */ + + time_t now = time ((time_t *) 0); /* d */ + struct tm *tm = localtime (&now); /* o */ + const char *s = screensaver_id; /* n */ + char mon[4], year[5]; /* ' */ + int m, y, mrnths; /* t */ + s = strchr (s, ' '); if (!s) abort(); s++; /* */ + s = strchr (s, '('); if (!s) abort(); s++; /* d */ + s = strchr (s, '-'); if (!s) abort(); s++; /* o */ + strncpy (mon, s, 3); /* o */ + mon[3] = 0; /* */ + s = strchr (s, '-'); if (!s) abort(); s++; /* e */ + strncpy (year, s, 4); /* e */ + year[4] = 0; /* t */ + y = atoi (year); /* , */ + if (!strcmp(mon, "Jan")) m = 0; /* */ + else if (!strcmp(mon, "Feb")) m = 1; /* s */ + else if (!strcmp(mon, "Mar")) m = 2; /* t */ + else if (!strcmp(mon, "Apr")) m = 3; /* o */ + else if (!strcmp(mon, "May")) m = 4; /* p */ + else if (!strcmp(mon, "Jun")) m = 5; /* , */ + else if (!strcmp(mon, "Jul")) m = 6; /* */ + else if (!strcmp(mon, "Aug")) m = 7; /* s */ + else if (!strcmp(mon, "Sep")) m = 8; /* t */ + else if (!strcmp(mon, "Oct")) m = 9; /* a */ + else if (!strcmp(mon, "Nov")) m = 10; /* a */ + else if (!strcmp(mon, "Dec")) m = 11; /* a */ + else abort(); /* h */ + mrnths = ((((tm->tm_year + 1900) * 12) + tm->tm_mon) - /* h */ + (y * 12 + m)); /* h */ + /* p */ + return (mrnths >= 34); /* . */ +} diff --git a/driver/remote.c b/driver/remote.c index 83254e0..123bc28 100644 --- a/driver/remote.c +++ b/driver/remote.c @@ -1,4 +1,4 @@ -/* xscreensaver-command, Copyright (c) 1991-2019 Jamie Zawinski +/* xscreensaver-command, Copyright © 1991-2021 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 @@ -33,102 +33,61 @@ #include /* for XGetClassHint() */ #include +#ifdef HAVE_DPMS_EXTENSION +# include +#endif + +#include "blurb.h" +#include "atoms.h" #include "remote.h" +#include "clientmsg.h" #ifdef _VROOT_H_ ERROR! you must not include vroot.h in this file #endif -extern char *progname; -extern Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_RESPONSE; -extern Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_EXIT; -extern Atom XA_VROOT, XA_SELECT, XA_DEMO, XA_BLANK, XA_LOCK; -extern Atom XA_ACTIVATE, XA_SUSPEND, XA_NEXT, XA_PREV, XA_EXIT; - - -static XErrorHandler old_handler = 0; -static Bool got_badwindow = False; +static Bool error_handler_hit_p = False; static int -BadWindow_ehandler (Display *dpy, XErrorEvent *error) +ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) { - if (error->error_code == BadWindow) - { - got_badwindow = True; - return 0; - } - else - { - fprintf (stderr, "%s: ", progname); - if (!old_handler) abort(); - return (*old_handler) (dpy, error); - } + error_handler_hit_p = True; + return 0; } - -static Window -find_screensaver_window (Display *dpy, char **version) +/* See comment in xscreensaver.c for why this is here instead of there. + */ +static void +reset_dpms_timer (Display *dpy) { - int i; - Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy)); - Window root2, parent, *kids; - unsigned int nkids; - - if (version) *version = 0; - - if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) - abort (); - if (root != root2) - abort (); - if (parent) - abort (); - if (! (kids && nkids)) - return 0; - for (i = 0; i < nkids; i++) - { - Atom type; - int format; - unsigned long nitems, bytesafter; - unsigned char *v; - int status; - - /* We're walking the list of root-level windows and trying to find - the one that has a particular property on it. We need to trap - BadWindows errors while doing this, because it's possible that - some random window might get deleted in the meantime. (That - window won't have been the one we're looking for.) - */ - XSync (dpy, False); - if (old_handler) abort(); - got_badwindow = False; - old_handler = XSetErrorHandler (BadWindow_ehandler); - status = XGetWindowProperty (dpy, kids[i], - XA_SCREENSAVER_VERSION, - 0, 200, False, XA_STRING, - &type, &format, &nitems, &bytesafter, - &v); - XSync (dpy, False); - XSetErrorHandler (old_handler); - old_handler = 0; +# ifdef HAVE_DPMS_EXTENSION - if (got_badwindow) - { - status = BadWindow; - got_badwindow = False; - } + XErrorHandler old_handler; + int event_number, error_number; + BOOL enabled = False; + CARD16 power = 0; - if (status == Success && type != None) - { - Window ret = kids[i]; - if (version) - *version = (char *) v; - XFree (kids); - return ret; - } - } + XSync (dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); - if (kids) XFree (kids); - return 0; + if (! DPMSQueryExtension (dpy, &event_number, &error_number)) + goto DONE; + if (! DPMSCapable (dpy)) + goto DONE; + if (! DPMSInfo (dpy, &power, &enabled)) + goto DONE; + if (!enabled) + goto DONE; + + /* Do this even if power == DPMSModeOn to reset the timer */ + DPMSForceLevel (dpy, DPMSModeOn); + + DONE: + XSync (dpy, False); + XSetErrorHandler (old_handler); + +# endif /* HAVE_DPMS_EXTENSION */ } @@ -239,7 +198,7 @@ send_xscreensaver_command (Display *dpy, Atom command, long arg, if (data) free (data); fprintf (stdout, "\n"); fflush (stdout); - fprintf (stderr, "bad status format on root window.\n"); + fprintf (stderr, "bad status format on root window\n"); status = -1; goto DONE; } @@ -301,7 +260,7 @@ send_xscreensaver_command (Display *dpy, Atom command, long arg, if (dataP) XFree (dataP); fprintf (stdout, "\n"); fflush (stdout); - fprintf (stderr, "no saver status on root window.\n"); + fprintf (stderr, "no saver status on root window\n"); status = -1; goto DONE; } @@ -316,7 +275,6 @@ send_xscreensaver_command (Display *dpy, Atom command, long arg, XEvent event; long arg1 = arg; long arg2 = 0; - if (arg < 0) abort(); else if (arg == 0 && command == XA_SELECT) @@ -327,6 +285,9 @@ send_xscreensaver_command (Display *dpy, Atom command, long arg, arg2 = arg; /* since it didn't use to take an argument. */ } + if (command == XA_DEACTIVATE) + reset_dpms_timer (dpy); + event.xany.type = ClientMessage; event.xclient.display = dpy; event.xclient.window = window; @@ -336,10 +297,11 @@ send_xscreensaver_command (Display *dpy, Atom command, long arg, event.xclient.data.l[0] = (long) command; event.xclient.data.l[1] = arg1; event.xclient.data.l[2] = arg2; - if (! XSendEvent (dpy, window, False, 0L, &event)) + + if (! XSendEvent (dpy, window, False, PropertyChangeMask, &event)) { - sprintf (err, "XSendEvent(dpy, 0x%x ...) failed.\n", - (unsigned int) window); + sprintf (err, "XSendEvent(dpy, 0x%x ...) failed\n", + (unsigned int) window); if (error_ret) *error_ret = strdup (err); else @@ -416,10 +378,11 @@ xscreensaver_command_response (Display *dpy, Window window, int format; unsigned long nitems, bytesafter; unsigned char *msg = 0; + XErrorHandler old_handler; XSync (dpy, False); - if (old_handler) abort(); - old_handler = XSetErrorHandler (BadWindow_ehandler); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); st2 = XGetWindowProperty (dpy, window, XA_SCREENSAVER_RESPONSE, 0, 1024, True, @@ -428,9 +391,8 @@ xscreensaver_command_response (Display *dpy, Window window, &msg); XSync (dpy, False); XSetErrorHandler (old_handler); - old_handler = 0; - if (got_badwindow) + if (error_handler_hit_p) { if (exiting_p) return 0; @@ -491,109 +453,87 @@ xscreensaver_command_response (Display *dpy, Window window, } -/* Wait until the window has been mapped, blanking the screen. +/* Wait until xscreensaver says the screen is blanked. Catches errors, times out after a few seconds. */ static int -xscreensaver_command_wait_for_blank (Display *dpy, Window window, +xscreensaver_command_wait_for_blank (Display *dpy, Bool verbose_p, char **error_ret) { + Window w = RootWindow (dpy, 0); /* always screen 0 */ time_t start = time((time_t*)0); int max = 10; char err[2048]; - Status status = -1; - while (1) { - XWindowAttributes xgwa; - xgwa.map_state = IsUnmapped; + Atom type; + int format; + unsigned long nitems, bytesafter; + unsigned char *dataP = 0; + time_t now; + struct timeval tv; - if (!window) - got_badwindow = True; - else + /* Wait until the status property on the root window changes to + BLANK or LOCKED. */ + if (XGetWindowProperty (dpy, w, + XA_SCREENSAVER_STATUS, + 0, 999, False, XA_INTEGER, + &type, &format, &nitems, &bytesafter, + &dataP) + == Success + && type == XA_INTEGER + && nitems >= 3 + && dataP) { - XSync (dpy, False); - if (old_handler) abort(); - got_badwindow = False; - old_handler = XSetErrorHandler (BadWindow_ehandler); - status = XGetWindowAttributes (dpy, window, &xgwa); - XSync (dpy, False); - XSetErrorHandler (old_handler); - old_handler = 0; - } + Atom state = ((Atom *) dataP)[0]; - if (got_badwindow) - { - /* If we got a BadWindow, it might be that in the course of - activating, xscreensaver had to destroy and re-create the - window to get one with the proper Visual. So wait for a - new window to come into existence. - */ - if (window && verbose_p > 1) - fprintf (stderr, - "%s: BadWindow 0x%08x waiting for screen to blank\n", - progname, (unsigned int) window); - window = find_screensaver_window (dpy, 0); - if (window && verbose_p > 1) - fprintf (stderr, "%s: new window is 0x%08x.\n", - progname, (unsigned int) window); - got_badwindow = False; + if (verbose_p > 1) + { + PROP32 *status = (PROP32 *) dataP; + int i; + fprintf (stderr, "%s: read status property: 0x%lx: %s", progname, + (unsigned long) w, + (status[0] == XA_LOCK ? "LOCK" : + status[0] == XA_BLANK ? "BLANK" : + status[0] == 0 ? "0" : "???")); + for (i = 1; i < nitems; i++) + fprintf (stderr, ", %lu", status[i]); + fprintf (stderr, "\n"); + } + + if (state == XA_BLANK || state == XA_LOCK) + { + if (verbose_p > 1) + fprintf (stderr, "%s: screen blanked\n", progname); + break; + } } - else if (status == 0) + + now = time ((time_t *) 0); + if (now >= start + max) { - sprintf (err, "error on 0x%08x waiting for screen to blank", - (unsigned int) window); + strcpy (err, "Timed out waiting for screen to blank"); if (error_ret) *error_ret = strdup (err); else fprintf (stderr, "%s: %s\n", progname, err); return -1; } - else if (xgwa.map_state == IsViewable) + else if (verbose_p == 1 && now > start + 3) { - if (verbose_p) - fprintf (stderr, "%s: window 0x%08x mapped.\n", - progname, (unsigned int) window); - return 0; + fprintf (stderr, "%s: waiting for status change\n", progname); + verbose_p++; } - else - { - time_t now = time((time_t*)0); - if (now >= start + max) - { - sprintf (err, "Timed out waiting for screen to blank on 0x%08x", - (unsigned int) window); - if (error_ret) - *error_ret = strdup (err); - else - fprintf (stderr, "%s: %s\n", progname, err); - return -1; - } - else if (verbose_p && now > start+1) - { - fprintf (stderr, "%s: waiting for window 0x%08x to map\n", - progname, (unsigned int) window); - } - } - -# if defined(HAVE_SELECT) - { - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 1000000L / 10; - select (0, 0, 0, 0, &tv); - } -# else - sleep (1); -# endif + tv.tv_sec = 0; + tv.tv_usec = 1000000L / 10; + select (0, 0, 0, 0, &tv); } return 0; } - int xscreensaver_command (Display *dpy, Atom command, long arg, Bool verbose_p, char **error_ret) @@ -614,8 +554,7 @@ xscreensaver_command (Display *dpy, Atom command, long arg, Bool verbose_p, command == XA_NEXT || command == XA_PREV || command == XA_SELECT)) - status = xscreensaver_command_wait_for_blank (dpy, w, verbose_p, - error_ret); + status = xscreensaver_command_wait_for_blank (dpy, verbose_p, error_ret); fflush (stdout); fflush (stderr); @@ -648,7 +587,7 @@ server_xscreensaver_version (Display *dpy, if (version_ret) { unsigned char *v = 0; - XGetWindowProperty (dpy, window, XA_SCREENSAVER_VERSION, 0, 1, + XGetWindowProperty (dpy, window, XA_SCREENSAVER_VERSION, 0, 100, False, XA_STRING, &type, &format, &nitems, &bytesafter, &v); if (v) @@ -681,11 +620,12 @@ server_xscreensaver_version (Display *dpy, { char *o = 0, *p = 0, *c = 0; o = strchr ((char *) id, '('); - if (o) p = strchr (o, '@'); + if (o) p = strrchr (o, '@'); if (p) c = strchr (p, ')'); if (c) { - /* found ID of the form "1234 (user@host)". */ + /* found ID of the form "1234 (user@host)" + or the weirder "1234 (user@crap@host)". */ user = o+1; host = p+1; *p = 0; @@ -695,12 +635,16 @@ server_xscreensaver_version (Display *dpy, } - if (user && *user && *user != '?') + if (!user_ret) + ; + else if (user && *user && *user != '?') *user_ret = strdup (user); else *user_ret = 0; - if (host && *host && *host != '?') + if (!host_ret) + ; + else if (host && *host && *host != '?') *host_ret = strdup (host); else *host_ret = 0; diff --git a/driver/remote.h b/driver/remote.h index e1db351..6b1763f 100644 --- a/driver/remote.h +++ b/driver/remote.h @@ -1,5 +1,4 @@ -/* xscreensaver-command, Copyright (c) 1991-1998 - * by Jamie Zawinski +/* xscreensaver-command, Copyright © 1991-2021 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 diff --git a/driver/screens.c b/driver/screens.c index f319c96..dc87e51 100644 --- a/driver/screens.c +++ b/driver/screens.c @@ -1,5 +1,5 @@ /* screens.c --- dealing with RANDR, Xinerama, and VidMode Viewports. - * xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski + * xscreensaver, Copyright © 1991-2021 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 @@ -11,115 +11,114 @@ */ /* There are a bunch of different mechanisms for multiple monitors - * available in X. XScreenSaver needs to care about this for two - * reasons: first, to ensure that all visible areas go black; and - * second, so that the windows of screen savers exactly fill the - * glass of each monitor (instead of one saver spanning multiple - * monitors, or a monitor displaying only a sub-rectangle of the - * screen saver.) + * available in X. XScreenSaver needs to care about this for two reasons: + * first, to ensure that all visible areas go black; and second, so that + * the windows of screen savers exactly fill the glass of each monitor + * (instead of one saver spanning multiple monitors, or a monitor + * displaying only a sub-rectangle of the screen saver.) * * 1) Multi-screen: * - * This is the original way. Each monitor gets its own display - * number. :0.0 is the first one, :0.1 is the next, etc. The - * value of $DISPLAY determines which screen windows open on by - * default. A single app can open windows on multiple screens - * with the same display connection, but windows cannot be moved - * from one screen to another. The mouse can be moved from one - * screen to another, though. Screens may be different depths - * (e.g., one can be TrueColor and one can be PseudoColor.) - * Screens cannot be resized or moved without restarting X. + * This is the original way. Each monitor gets its own display number. + * ":0.0" is the first one, ":0.1" is the next, and so on. The value + * of $DISPLAY determines which screen windows open on by default. A + * single app can open windows on multiple screens with the same + * display connection, but windows cannot be moved from one screen to + * another. The mouse can be moved from one screen to another, though. + * Screens may be different depths (e.g., one can be TrueColor and one + * can be PseudoColor.) Screens cannot be resized or moved without + * restarting X. * - * Everyone hates this way of doing things because of the - * inability to move a window from one screen to another without - * restarting the application. + * Everyone hates this way of doing things because of the inability to + * move a window from one screen to another without restarting the + * application. * * 2) Xinerama: * - * There is a single giant root window that spans all the - * monitors. All monitors are the same depth, and windows can be - * moved around. Applications can learn which rectangles are - * actually visible on monitors by querying the Xinerama server - * extension. (If you don't do that, you end up with dialog - * boxes that try to appear in the middle of the screen actually - * spanning the gap between two monitors.) + * There is a single giant root window that spans all the monitors. + * All monitors are the same depth, and windows can be moved around. + * Applications can learn which rectangles are actually visible on + * monitors by querying the Xinerama server extension. (If you don't + * do that, you end up with dialog boxes that try to appear in the + * middle of the screen actually spanning the gap between two + * monitors.) * - * Xinerama doesn't work with DRI, which means that if you use - * it, you lose hardware acceleration on OpenGL programs. Also, - * screens can't be resized or moved without restarting X. + * Xinerama didn't? work with DRI, which means that Xinerama precluded + * hardware acceleration in OpenGL programs. Also, screens couldn't + * be resized or moved without restarting X. * * 3) Vidmode Viewports: * + * No longer supported as of XScreenSaver 6. + * * With this extension, the root window can be bigger than the - * monitor. Moving the mouse near the edges of the screen - * scrolls around, like a pan-and-scan movie. There can also be - * a hot key for changing the monitor's resolution (zooming - * in/out). + * monitor. Moving the mouse near the edges of the screen scrolls + * around, like a pan-and-scan movie. There was also a hot-key for + * changing the monitor's resolution (zooming in/out). * - * Trying to combine this with Xinerama crashes the server, so - * you can only use this if you have only a single screen, or are - * in old-multi-screen mode. + * Trying to combine this with Xinerama crashes the server, so you + * could only use this if you had only a single screen, or were in old + * multi-screen mode. * - * Also, half the time it doesn't work at all: it tends to lie - * about the size of the rectangle in use. + * Also, half the time it didn't work at all: it tended to lie about + * the size of the rectangle in use. * * 4) RANDR 1.0: * * The first version of the "Resize and Rotate" extension let you * change the resolution of a screen on the fly. The root window * would actually resize. However, it was also incompatible with - * Xinerama (did it crash, or just do nothing? I can't remember) - * so you needed to be in single-screen or old multi-screen mode. - * I believe RANDR could co-exist with Vidmode Viewports, but I'm - * not sure. + * Xinerama (did it crash, or just do nothing? I can't remember) so + * you needed to be in single-screen or old multi-screen mode. I + * believe RANDR could co-exist with Vidmode Viewports, but I'm not + * sure. * * 5) RANDR 1.2: * * Finally, RANDR added the functionality of Xinerama, plus some. * Each X screen (in the sense of #1, "multi-screen") can have a - * number of sub-rectangles that are displayed on monitors, and - * each of those sub-rectangles can be displayed on more than one - * monitor. So it's possible (I think) to have a hybrid of - * multi-screen and Xinerama (e.g., to have two monitors running - * in one depth, and three monitors running in another?) - * Typically though, there will be a single X screen, with - * Xinerama-like division of that large root window onto multiple - * monitors. Also everything's dynamic: monitors can be added, - * removed, and resized at runtime. - * - * I believe that as of RANDR 1.2, the Xinerama extension still - * exists but only as a compatiblity layer: it's actually - * returning data from the RANDR extension. - * - * Though RANDR 1.2 allows the same image to be cloned onto more - * than one monitor, and also allows one monitor to show a - * subsection of something on another monitor (e.g., the - * rectangles can be enclosed or overlap). Since there's no way - * to put seperate savers on those duplicated-or-overlapping - * monitors, xscreensaver just ignores them (which allows them to - * display duplicates or overlaps). + * number of sub-rectangles that are displayed on monitors, and each + * of those sub-rectangles can be displayed on more than one monitor. + * So it's possible (I think) to have a hybrid of multi-screen and + * Xinerama (e.g., to have two monitors running in one depth, and + * three monitors running in another?) Typically though, there will + * be a single X screen with one giant root window underlying the + * rectangles of multiple monitors. Also everything is dynamic: + * monitors can be added, removed, and resized at runtime, with + * notification events. * - * 5a) Nvidia fucks it up: + * RANDR rectangles can overlap, meaning one monitor can mirror + * another, or show a sub-rectangle of another, or just overlap in + * strange ways. The proper way to respond to weird layouts is... not + * always obvious. * - * Nvidia drivers as of Aug 2008 running in "TwinView" mode - * apparently report correct screen geometry via Xinerama, but - * report one giant screen via RANDR. The response from the - * nvidia developers is, "we don't support RANDR, use Xinerama - * instead." Which is a seriously lame answer. So, xscreensaver - * has to query *both* extensions, and make a guess as to which - * is to be believed. + * Also sometimes RANDR says stupid shit like, "You have one screen, + * and it has no available sizes or orientations." * - * 5b) Also sometimes RANDR says stupid shit like, "You have one - * screen, and it has no available orientations or sizes." + * Sometimes RANDR and Xinerama report the same info, and sometimes + * not, so we look at both and see which looks most plausible. * + * Also, Nvidia fucked it up: their drivers that were popular in 2008, + * when running in "TwinView" mode, reported correct sizes via + * Xinerama, but reported one giant screen via RANDR. Nvidia's + * response was, "We don't support RANDR, use Xinerama instead", which + * is another reason that XScreenSaver historically had to query both + * extensions and make a guess. Maybe this is no longer necessary. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include +#include +#include #include +#ifndef HAVE_RANDR_12 +# undef HAVE_RANDR /* RANDR 1.1 is no longer supported */ +#endif + #ifdef HAVE_RANDR # include #endif /* HAVE_RANDR */ @@ -132,37 +131,14 @@ # include #endif /* HAVE_XF86VMODE */ -/* This file doesn't need the Xt headers, so stub these types out... */ -#undef XtPointer -#define XtAppContext void* -#define XrmDatabase void* -#define XtIntervalId void* -#define XtPointer void* -#define Widget void* - -#include "xscreensaver.h" -#include "visual.h" - - -typedef enum { S_SANE, S_ENCLOSED, S_DUPLICATE, S_OVERLAP, - S_OFFSCREEN, S_DISABLED } monitor_sanity; - -/* 'typedef monitor' is in types.h */ -struct _monitor { - int id; - char *desc; - Screen *screen; - int x, y, width, height; - monitor_sanity sanity; /* I'm not crazy you're the one who's crazy */ - int enemy; /* which monitor it overlaps or duplicates */ - char *err; /* msg to print at appropriate later time; - exists only on monitor #0. */ -}; +#include "blurb.h" +#include "screens.h" -static Bool layouts_differ_p (monitor **a, monitor **b); +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) -static void +void free_monitors (monitor **monitors) { monitor **m2 = monitors; @@ -231,122 +207,6 @@ xinerama_scan_monitors (Display *dpy, char **errP) #endif /* HAVE_XINERAMA */ -#ifdef HAVE_XF86VMODE - -static monitor ** -vidmode_scan_monitors (Display *dpy, char **errP) -{ - int event, error, nscreens, i; - monitor **monitors; - - /* Note that XF86VidModeGetViewPort() tends to be full of lies on laptops - that have a docking station or external monitor that runs in a different - resolution than the laptop's screen: - - http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=81593 - http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=208417 - http://bugs.xfree86.org/show_bug.cgi?id=421 - - Presumably this is fixed by using RANDR instead of VidMode. - */ - -# ifdef HAVE_XINERAMA - /* Attempts to use the VidMode extension when the Xinerama extension is - active can result in a server crash! Yay! */ - if (XQueryExtension (dpy, "XINERAMA", &error, &event, &error)) - return 0; -# endif /* !HAVE_XINERAMA */ - - if (! XF86VidModeQueryExtension (dpy, &event, &error)) - return 0; - - nscreens = ScreenCount (dpy); - monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors)); - if (!monitors) return 0; - - for (i = 0; i < nscreens; i++) - { - monitor *m = (monitor *) calloc (1, sizeof (monitor)); - XF86VidModeModeLine ml; - int dot; - Screen *screen = ScreenOfDisplay (dpy, i); - - monitors[i] = m; - m->id = i; - m->screen = screen; - - if (! safe_XF86VidModeGetViewPort (dpy, i, &m->x, &m->y)) - m->x = m->y = -1; - - if (XF86VidModeGetModeLine (dpy, i, &dot, &ml)) - { - m->width = ml.hdisplay; - m->height = ml.vdisplay; - } - - /* On a system that has VidMode but does not have RANDR, and that has - "Option Rotate" set, WidthOfScreen/HeightOfScreen are the rotated - size, but XF86VidModeModeLine contains the unrotated size. - Maybe there's something in 'flags' that indicates this? - Or, we can just notice that the aspect ratios are inverted: - */ - if (m->width > 0 && - m->height > 0 && - ((m->width > m->height) != - (WidthOfScreen(screen) > HeightOfScreen(screen)))) - { - int swap = m->width; - m->width = m->height; - m->height = swap; - } - - - /* Apparently, though the server stores the X position in increments of - 1 pixel, it will only make changes to the *display* in some other - increment. With XF86_SVGA on a Thinkpad, the display only updates - in multiples of 8 pixels when in 8-bit mode, and in multiples of 4 - pixels in 16-bit mode. I don't know what it does in 24- and 32-bit - mode, because I don't have enough video memory to find out. - - I consider it a bug that XF86VidModeGetViewPort() is telling me the - server's *target* scroll position rather than the server's *actual* - scroll position. David Dawes agrees, and says they may fix this in - XFree86 4.0, but it's nontrivial. - - He also confirms that this behavior is server-dependent, so the - actual scroll position cannot be reliably determined by the client. - So... that means the only solution is to provide a ``sandbox'' - around the blackout window -- we make the window be up to N pixels - larger than the viewport on both the left and right sides. That - means some part of the outer edges of each hack might not be - visible, but screw it. - - I'm going to guess that 16 pixels is enough, and that the Y dimension - doesn't have this problem. - - The drawback of doing this, of course, is that some of the screenhacks - will still look pretty stupid -- for example, "slidescreen" will cut - off the left and right edges of the grid, etc. - */ -# define FUDGE 16 - if (m->x > 0 && m->x < m->width - ml.hdisplay) - { - /* Not at left edge or right edge: - Round X position down to next lower multiple of FUDGE. - Increase width by 2*FUDGE in case some server rounds up. - */ - m->x = ((m->x - 1) / FUDGE) * FUDGE; - m->width += (FUDGE * 2); - } -# undef FUDGE - } - - return monitors; -} - -#endif /* HAVE_XF86VMODE */ - - #ifdef HAVE_RANDR static monitor ** @@ -354,7 +214,6 @@ randr_scan_monitors (Display *dpy, char **errP) { int event, error, major, minor, nscreens, i, j; monitor **monitors; - Bool new_randr_p = False; if (! XRRQueryExtension (dpy, &event, &error)) return 0; @@ -362,30 +221,17 @@ randr_scan_monitors (Display *dpy, char **errP) if (! XRRQueryVersion (dpy, &major, &minor)) return 0; - if (major <= 0) /* Protocol was still in flux back then -- fuck it. */ - return 0; - -# ifdef HAVE_RANDR_12 - new_randr_p = (major > 1 || (major == 1 && minor >= 2)); -# endif + if (! (major > 1 || (major == 1 && minor >= 2))) + return 0; /* 1.2 ir newer is required */ - if (! new_randr_p) - /* RANDR 1.0 -- no Xinerama-like virtual screens. */ - nscreens = ScreenCount (dpy); - else /* RANDR 1.2 or newer -- built-in Xinerama */ + /* Add up the virtual screens on each X screen. */ + nscreens = 0; + for (i = 0; i < ScreenCount (dpy); i++) { -# ifdef HAVE_RANDR_12 - int xsc = ScreenCount (dpy); - nscreens = 0; - /* Add up the virtual screens on each X screen. */ - for (i = 0; i < xsc; i++) - { - XRRScreenResources *res = - XRRGetScreenResources (dpy, RootWindow (dpy, i)); - nscreens += res->noutput; - XRRFreeScreenResources (res); - } -# endif /* HAVE_RANDR_12 */ + XRRScreenResources *res = + XRRGetScreenResources (dpy, RootWindow (dpy, i)); + nscreens += res->noutput; + XRRFreeScreenResources (res); } if (nscreens <= 0) @@ -401,88 +247,43 @@ randr_scan_monitors (Display *dpy, char **errP) for (i = 0, j = 0; i < ScreenCount (dpy); i++) { Screen *screen = ScreenOfDisplay (dpy, i); - - if (! new_randr_p) /* RANDR 1.0 */ + int k; + XRRScreenResources *res = + XRRGetScreenResources (dpy, RootWindowOfScreen (screen)); + for (k = 0; k < res->noutput; k++, j++) { - XRRScreenConfiguration *rrc; monitor *m = (monitor *) calloc (1, sizeof (monitor)); - monitors[i] = m; + XRROutputInfo *rroi = XRRGetOutputInfo (dpy, res, + res->outputs[k]); + RRCrtc crtc = (rroi->crtc ? rroi->crtc : + rroi->ncrtc ? rroi->crtcs[0] : 0); + XRRCrtcInfo *crtci = (crtc ? XRRGetCrtcInfo(dpy, res, crtc) : 0); + + monitors[j] = m; m->screen = screen; - m->id = i; + m->id = (i * 1000) + j; + m->desc = (rroi->name ? strdup (rroi->name) : 0); - rrc = XRRGetScreenInfo (dpy, RootWindowOfScreen (screen)); - if (rrc) + if (crtci) { - SizeID size = -1; - Rotation rot = ~0; - XRRScreenSize *rrsizes; - int nsizes = 0; - - size = XRRConfigCurrentConfiguration (rrc, &rot); - rrsizes = XRRConfigSizes (rrc, &nsizes); - - if (nsizes <= 0) /* WTF? Shouldn't happen but does. */ - { - m->width = DisplayWidth (dpy, i); - m->height = DisplayHeight (dpy, i); - } - else if (rot & (RR_Rotate_90|RR_Rotate_270)) - { - m->width = rrsizes[size].height; - m->height = rrsizes[size].width; - } - else - { - m->width = rrsizes[size].width; - m->height = rrsizes[size].height; - } - - /* don't free 'rrsizes' */ - XRRFreeScreenConfigInfo (rrc); + /* Note: if the screen is rotated, XRRConfigSizes contains + the unrotated WxH, but XRRCrtcInfo contains rotated HxW. + */ + m->x = crtci->x; + m->y = crtci->y; + m->width = crtci->width; + m->height = crtci->height; } - } - else /* RANDR 1.2 or newer */ - { -# ifdef HAVE_RANDR_12 - int k; - XRRScreenResources *res = - XRRGetScreenResources (dpy, RootWindowOfScreen (screen)); - for (k = 0; k < res->noutput; k++, j++) - { - monitor *m = (monitor *) calloc (1, sizeof (monitor)); - XRROutputInfo *rroi = XRRGetOutputInfo (dpy, res, - res->outputs[k]); - RRCrtc crtc = (rroi->crtc ? rroi->crtc : - rroi->ncrtc ? rroi->crtcs[0] : 0); - XRRCrtcInfo *crtci = (crtc ? XRRGetCrtcInfo(dpy, res, crtc) : 0); - - monitors[j] = m; - m->screen = screen; - m->id = (i * 1000) + j; - m->desc = (rroi->name ? strdup (rroi->name) : 0); - - if (crtci) - { - /* Note: if the screen is rotated, XRRConfigSizes contains - the unrotated WxH, but XRRCrtcInfo contains rotated HxW. - */ - m->x = crtci->x; - m->y = crtci->y; - m->width = crtci->width; - m->height = crtci->height; - } - if (rroi->connection == RR_Disconnected) - m->sanity = S_DISABLED; - /* #### do the same for RR_UnknownConnection? */ + if (rroi->connection == RR_Disconnected) + m->sanity = S_DISABLED; + /* #### do the same for RR_UnknownConnection? */ - if (crtci) - XRRFreeCrtcInfo (crtci); - XRRFreeOutputInfo (rroi); - } - XRRFreeScreenResources (res); -# endif /* HAVE_RANDR_12 */ + if (crtci) + XRRFreeCrtcInfo (crtci); + XRRFreeOutputInfo (rroi); } + XRRFreeScreenResources (res); } /* Work around more fucking brain damage. */ @@ -566,7 +367,7 @@ randr_versus_xinerama_fight (Display *dpy, monitor **randr_monitors, if (!xinerama_monitors) return randr_monitors; - if (! layouts_differ_p (randr_monitors, xinerama_monitors)) + if (! monitor_layouts_differ_p (randr_monitors, xinerama_monitors)) { free_monitors (xinerama_monitors); return randr_monitors; @@ -582,9 +383,8 @@ randr_versus_xinerama_fight (Display *dpy, monitor **randr_monitors, } else { - *errP = append (*errP, - "WARNING: RANDR and Xinerama report different\n" - " screen layouts! Believing RANDR."); + *errP = append (*errP, /* This is "normal" now, I guess. */ + "RANDR and Xinerama report different screen layouts"); free_monitors (xinerama_monitors); return randr_monitors; } @@ -642,7 +442,6 @@ debug_scan_monitors (Display *dpy, char **errP) index = (index+1) % countof(geoms); return monitors; } - #endif /* DEBUG_MULTISCREEN */ @@ -683,42 +482,36 @@ quadruple (monitor **monitors, Bool debug_p, char **errP) #endif /* QUAD_MODE */ -static monitor ** -scan_monitors (saver_info *si) +monitor ** +scan_monitors (Display *dpy) { - saver_preferences *p = &si->prefs; monitor **monitors = 0; char *err = 0; # ifdef DEBUG_MULTISCREEN - if (! monitors) monitors = debug_scan_monitors (si->dpy, &err); + if (! monitors) monitors = debug_scan_monitors (dpy, &err); # endif # ifdef HAVE_RANDR - if (! p->getviewport_full_of_lies_p) - if (! monitors) monitors = randr_scan_monitors (si->dpy, &err); + if (! monitors) monitors = randr_scan_monitors (dpy, &err); # ifdef HAVE_XINERAMA - monitors = randr_versus_xinerama_fight (si->dpy, monitors, &err); + monitors = randr_versus_xinerama_fight (dpy, monitors, &err); # endif # endif /* HAVE_RANDR */ -# ifdef HAVE_XF86VMODE - if (! monitors) monitors = vidmode_scan_monitors (si->dpy, &err); -# endif - # ifdef HAVE_XINERAMA - if (! monitors) monitors = xinerama_scan_monitors (si->dpy, &err); + if (! monitors) monitors = xinerama_scan_monitors (dpy, &err); # endif - if (! monitors) monitors = basic_scan_monitors (si->dpy, &err); + if (! monitors) monitors = basic_scan_monitors (dpy, &err); # ifdef QUAD_MODE if (p->quad_p) monitors = quadruple (monitors, p->debug_p, &err); # endif - if (monitors && err) monitors[0]->err = err; + if (monitors && *monitors && err) monitors[0]->err = err; return monitors; } @@ -778,7 +571,7 @@ plausible_aspect_ratio_p (monitor **monitors) /* Mark the ones that overlap, etc. */ -static void +void check_monitor_sanity (monitor **monitors) { int i, j, count = 0; @@ -862,8 +655,8 @@ check_monitor_sanity (monitor **monitors) } -static Bool -layouts_differ_p (monitor **a, monitor **b) +Bool +monitor_layouts_differ_p (monitor **a, monitor **b) { if (!a || !b) return True; while (1) @@ -886,10 +679,21 @@ layouts_differ_p (monitor **a, monitor **b) } +static int +screen_number (Screen *screen) +{ + Display *dpy = DisplayOfScreen (screen); + int i; + for (i = 0; i < ScreenCount (dpy); i++) + if (ScreenOfDisplay (dpy, i) == screen) + return i; + return 0; +} + + void -describe_monitor_layout (saver_info *si) +describe_monitor_layout (monitor **monitors) { - monitor **monitors = si->monitor_layout; int count = 0; int good_count = 0; int bad_count = 0; @@ -904,7 +708,7 @@ describe_monitor_layout (saver_info *si) count++; } - if (monitors[0]->err) /* deferred error msg */ + if (monitors && *monitors && monitors[0]->err) /* deferred error msg */ { char *token = strtok (monitors[0]->err, "\n"); while (token) @@ -980,115 +784,3 @@ describe_monitor_layout (saver_info *si) blurb()); } } - - -/* Synchronize the contents of si->ssi to the current state of the monitors. - Doesn't change anything if nothing has changed; otherwise, alters and - reuses existing saver_screen_info structs as much as possible. - Returns True if anything changed. - */ -Bool -update_screen_layout (saver_info *si) -{ - monitor **monitors = scan_monitors (si); - int count = 0; - int good_count = 0; - int i, j; - int seen_screens[100] = { 0, }; - - if (! layouts_differ_p (monitors, si->monitor_layout)) - { - free_monitors (monitors); - return False; - } - - free_monitors (si->monitor_layout); - si->monitor_layout = monitors; - check_monitor_sanity (si->monitor_layout); - - while (monitors[count]) - { - if (monitors[count]->sanity == S_SANE) - good_count++; - count++; - } - - if (si->ssi_count == 0) - { - si->ssi_count = 10; - si->screens = (saver_screen_info *) - calloc (sizeof(*si->screens), si->ssi_count); - } - - if (si->ssi_count <= good_count) - { - si->ssi_count = good_count + 10; - si->screens = (saver_screen_info *) - realloc (si->screens, sizeof(*si->screens) * si->ssi_count); - memset (si->screens + si->nscreens, 0, - sizeof(*si->screens) * (si->ssi_count - si->nscreens)); - } - - if (! si->screens) abort(); - - si->nscreens = good_count; - - /* Regenerate the list of GL visuals as needed. */ - if (si->best_gl_visuals) - free (si->best_gl_visuals); - si->best_gl_visuals = 0; - - for (i = 0, j = 0; i < count; i++) - { - monitor *m = monitors[i]; - saver_screen_info *ssi = &si->screens[j]; - Screen *old_screen = ssi->screen; - int sn; - if (monitors[i]->sanity != S_SANE) continue; - - ssi->global = si; - ssi->number = j; - - sn = screen_number (m->screen); - ssi->screen = m->screen; - ssi->real_screen_number = sn; - ssi->real_screen_p = (seen_screens[sn] == 0); - seen_screens[sn]++; - - ssi->default_visual = - get_visual_resource (ssi->screen, "visualID", "VisualID", False); - ssi->current_visual = ssi->default_visual; - ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual); - - /* If the screen changed (or if this is the first time) we need - a new toplevel shell for this screen's depth. - */ - if (ssi->screen != old_screen) - initialize_screen_root_widget (ssi); - - ssi->last_poll_mouse.root_x = -1; - ssi->last_poll_mouse.root_y = -1; - - ssi->x = m->x; - ssi->y = m->y; - ssi->width = m->width; - ssi->height = m->height; - -# ifndef DEBUG_MULTISCREEN - { - saver_preferences *p = &si->prefs; - if (p->debug_p -# ifdef QUAD_MODE - && !p->quad_p -# endif - ) - ssi->width /= 2; - } -# endif - - j++; - } - - si->default_screen = &si->screens[0]; - return True; -} diff --git a/driver/screens.h b/driver/screens.h new file mode 100644 index 0000000..1e3501c --- /dev/null +++ b/driver/screens.h @@ -0,0 +1,37 @@ +/* xscreensaver, Copyright © 1993-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifndef __XSCREENSAVER_SCREENS_H__ +#define __XSCREENSAVER_SCREENS_H__ + +typedef struct _monitor monitor; + +typedef enum { S_SANE, S_ENCLOSED, S_DUPLICATE, S_OVERLAP, + S_OFFSCREEN, S_DISABLED } monitor_sanity; + +struct _monitor { + int id; + char *desc; + Screen *screen; + int x, y, width, height; + monitor_sanity sanity; /* I'm not crazy you're the one who's crazy */ + int enemy; /* which monitor it overlaps or duplicates */ + char *err; /* msg to print at appropriate later time; + exists only on monitor #0. */ +}; + +extern monitor **scan_monitors (Display *); +extern Bool monitor_layouts_differ_p (monitor **a, monitor **b); +extern void free_monitors (monitor **monitors); +extern void describe_monitor_layout (monitor **monitors); +extern void check_monitor_sanity (monitor **monitors); + +#endif /* __XSCREENSAVER_SCREENS_H__ */ diff --git a/driver/screensaver-properties.desktop.in b/driver/screensaver-properties.desktop.in index de42527..9cc17e6 100644 --- a/driver/screensaver-properties.desktop.in +++ b/driver/screensaver-properties.desktop.in @@ -1,5 +1,5 @@ [Desktop Entry] -Exec=xscreensaver-demo +Exec=xscreensaver-settings Icon=xscreensaver Terminal=false _Name=Screensaver diff --git a/driver/setuid.c b/driver/setuid.c index 3ac78e4..164576f 100644 --- a/driver/setuid.c +++ b/driver/setuid.c @@ -1,5 +1,5 @@ /* setuid.c --- management of runtime privileges. - * xscreensaver, Copyright (c) 1993-1998, 2005 Jamie Zawinski + * xscreensaver, Copyright © 1993-2021 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 @@ -14,24 +14,20 @@ # include "config.h" #endif -#include /* not used for much... */ - -/* This file doesn't need the Xt headers, so stub these types out... */ -#undef XtPointer -#define XtAppContext void* -#define XrmDatabase void* -#define XtIntervalId void* -#define XtPointer void* -#define Widget void* - -#include "xscreensaver.h" - -#ifndef EPERM -#include +#include +#include +#ifdef HAVE_UNISTD_H +# include #endif +#include #include /* for getpwnam() and struct passwd */ #include /* for getgrgid() and struct group */ +#include + +#include "blurb.h" +#include "auth.h" + static const char * uid_gid_string (uid_t uid, gid_t gid) @@ -49,31 +45,6 @@ uid_gid_string (uid_t uid, gid_t gid) } -void -describe_uids (saver_info *si, FILE *out) -{ - uid_t uid = getuid(); - gid_t gid = getgid(); - uid_t euid = geteuid(); - gid_t egid = getegid(); - char *s1 = strdup (uid_gid_string (uid, gid)); - char *s2 = strdup (uid_gid_string (euid, egid)); - - if (si->orig_uid && *si->orig_uid && - (!!strcmp (si->orig_uid, s1) || - !!strcmp (si->orig_uid, s2))) - fprintf (out, "%s: initial effective uid/gid was %s\n", blurb(), - si->orig_uid); - - fprintf (out, "%s: running as %s", blurb(), s1); - if (uid != euid || gid != egid) - fprintf (out, "; effectively %s", s2); - fprintf(out, "\n"); - free(s1); - free(s2); -} - - /* Returns true if we need to call setgroups(). Without calling setgroups(), the process will retain any supplementary @@ -113,7 +84,7 @@ setgroups_needed_p (uid_t target_group) static int -set_ids_by_number (uid_t uid, gid_t gid, char **message_ret) +set_ids_by_number (uid_t uid, gid_t gid) { int uid_errno = 0; int gid_errno = 0; @@ -121,9 +92,6 @@ set_ids_by_number (uid_t uid, gid_t gid, char **message_ret) struct passwd *p = getpwuid (uid); struct group *g = getgrgid (gid); - if (message_ret) - *message_ret = 0; - /* Rumor has it that some implementations of of setuid() do nothing when called with -1; therefore, if the "nobody" user has a uid of -1, then that would be Really Bad. Rumor further has it that such @@ -150,13 +118,12 @@ set_ids_by_number (uid_t uid, gid_t gid, char **message_ret) if (uid_errno == 0 && gid_errno == 0 && sgs_errno == 0) { - static char buf [1024]; - sprintf (buf, "changed uid/gid to %.100s/%.100s (%ld/%ld).", - (p && p->pw_name ? p->pw_name : "???"), - (g && g->gr_name ? g->gr_name : "???"), - (long) uid, (long) gid); - if (message_ret) - *message_ret = buf; + if (verbose_p) + fprintf (stderr, "%s: changed uid/gid to %.100s/%.100s (%ld/%ld)\n", + blurb(), + (p && p->pw_name ? p->pw_name : "???"), + (g && g->gr_name ? g->gr_name : "???"), + (long) uid, (long) gid); return 0; } else @@ -176,7 +143,7 @@ set_ids_by_number (uid_t uid, gid_t gid, char **message_ret) else { errno = sgs_errno; - perror(buf); + perror (buf); } fprintf (stderr, "%s: effective group list: ", blurb()); @@ -206,11 +173,11 @@ set_ids_by_number (uid_t uid, gid_t gid, char **message_ret) (g && g->gr_name ? g->gr_name : "???"), (long) gid); if (gid_errno == -1) - fprintf(stderr, "%s: unknown error\n", buf); + fprintf (stderr, "%s: unknown error\n", buf); else { errno = gid_errno; - perror(buf); + perror (buf); } } @@ -241,121 +208,63 @@ set_ids_by_number (uid_t uid, gid_t gid, char **message_ret) lock-mode.) *** WARNING: DO NOT DISABLE ANY OF THE FOLLOWING CODE! - If you do so, you will open a security hole. See the sections - of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", - and "USING XDM". + If you do so, you will open a security hole. + Do not log in to an X11 desktop as root. + Log in as a normal users and 'sudo' as needed. */ void -hack_uid (saver_info *si) +disavow_privileges (void) { + uid_t euid = geteuid(); + gid_t egid = getegid(); + uid_t uid = getuid(); + gid_t gid = getgid(); + struct passwd *p; - /* Discard privileges, and set the effective user/group ids to the - real user/group ids. That is, give up our "chmod +s" rights. - */ - { - uid_t euid = geteuid(); - gid_t egid = getegid(); - uid_t uid = getuid(); - gid_t gid = getgid(); - - si->orig_uid = strdup (uid_gid_string (euid, egid)); - - if (uid != euid || gid != egid) - if (set_ids_by_number (uid, gid, &si->uid_message) != 0) - saver_exit (si, 1, 0); - } - - - /* Locking can't work when running as root, because we have no way of - knowing what the user id of the logged in user is (so we don't know - whose password to prompt for.) - - *** WARNING: DO NOT DISABLE THIS CODE! - If you do so, you will open a security hole. See the sections - of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", - and "USING XDM". - */ - if (getuid() == (uid_t) 0) + if (uid != euid || gid != egid) { - si->locking_disabled_p = True; - si->nolock_reason = "running as root"; + if (verbose_p) + fprintf (stderr, "%s: initial effective uid/gid was %s\n", blurb(), + uid_gid_string (euid, egid)); + if (set_ids_by_number (uid, gid) != 0) + exit (1); /* already printed error */ } - - /* If we're running as root, switch to a safer user. This is above and - beyond the fact that we've disabling locking, above -- the theory is - that running graphics demos as root is just always a stupid thing - to do, since they have probably never been security reviewed and are - more likely to be buggy than just about any other kind of program. - (And that assumes non-malicious code. There are also attacks here.) - - *** WARNING: DO NOT DISABLE THIS CODE! - If you do so, you will open a security hole. See the sections - of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", - and "USING XDM". + /* If we're still running as root, or if the user we are running at seems + to be in any way hinky, exit and do not allow password authentication + to continue. */ - if (getuid() == (uid_t) 0) - { - struct passwd *p; - - p = getpwnam ("nobody"); - if (! p) p = getpwnam ("noaccess"); - if (! p) p = getpwnam ("daemon"); - if (! p) - { - fprintf (stderr, - "%s: running as root, and couldn't find a safer uid.\n", - blurb()); - saver_exit(si, 1, 0); - } + uid = getuid (); /* get it again */ + p = getpwuid (uid); - if (set_ids_by_number (p->pw_uid, p->pw_gid, &si->uid_message) != 0) - saver_exit (si, -1, 0); + if (!p || + uid == (uid_t) 0 || + uid == (uid_t) -1 || + uid == (uid_t) -2 || + p->pw_uid == (uid_t) 0 || + p->pw_uid == (uid_t) -1 || + p->pw_uid == (uid_t) -2 || + !p->pw_name || + !*p->pw_name || + !strcmp (p->pw_name, "root") || + !strcmp (p->pw_name, "nobody") || + !strcmp (p->pw_name, "noaccess") || + !strcmp (p->pw_name, "operator") || + !strcmp (p->pw_name, "daemon") || + !strcmp (p->pw_name, "bin") || + !strcmp (p->pw_name, "adm") || + !strcmp (p->pw_name, "sys") || + !strcmp (p->pw_name, "games")) + { + fprintf (stderr, + "%s: running as user \"%s\" -- authentication disallowed\n", + blurb(), + (p && p->pw_name && *p->pw_name + ? p->pw_name + : "")); + exit (1); } - - /* If there's anything even remotely funny looking about the passwd struct, - or if we're running as some other user from the list below (a - non-comprehensive selection of users known to be privileged in some way, - and not normal end-users) then disable locking. If it was possible, - switching to "nobody" would be the thing to do, but only root itself has - the privs to do that. - - *** WARNING: DO NOT DISABLE THIS CODE! - If you do so, you will open a security hole. See the sections - of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", - and "USING XDM". - */ - { - uid_t uid = getuid (); /* get it again */ - struct passwd *p = getpwuid (uid); /* get it again */ - - if (!p || - uid == (uid_t) 0 || - uid == (uid_t) -1 || - uid == (uid_t) -2 || - p->pw_uid == (uid_t) 0 || - p->pw_uid == (uid_t) -1 || - p->pw_uid == (uid_t) -2 || - !p->pw_name || - !*p->pw_name || - !strcmp (p->pw_name, "root") || - !strcmp (p->pw_name, "nobody") || - !strcmp (p->pw_name, "noaccess") || - !strcmp (p->pw_name, "operator") || - !strcmp (p->pw_name, "daemon") || - !strcmp (p->pw_name, "bin") || - !strcmp (p->pw_name, "adm") || - !strcmp (p->pw_name, "sys") || - !strcmp (p->pw_name, "games")) - { - static char buf [1024]; - sprintf (buf, "running as %.100s", - (p && p->pw_name && *p->pw_name - ? p->pw_name : "")); - si->nolock_reason = buf; - si->locking_disabled_p = True; - si->dangerous_uid_p = True; - } - } + if (verbose_p) + fprintf (stderr, "%s: running as user \"%s\"\n", blurb(), p->pw_name); } diff --git a/driver/splash.c b/driver/splash.c deleted file mode 100644 index b971ade..0000000 --- a/driver/splash.c +++ /dev/null @@ -1,917 +0,0 @@ -/* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include "xscreensaver.h" -#include "resources.h" -#include "font-retry.h" - -#undef MAX -#define MAX(a,b) ((a)>(b)?(a):(b)) - -void -draw_shaded_rectangle (Display *dpy, Window window, - int x, int y, - int width, int height, - int thickness, - unsigned long top_color, - unsigned long bottom_color) -{ - XPoint points[4]; - XGCValues gcv; - GC gc1, gc2; - if (thickness == 0) return; - - gcv.foreground = top_color; - gc1 = XCreateGC (dpy, window, GCForeground, &gcv); - gcv.foreground = bottom_color; - gc2 = XCreateGC (dpy, window, GCForeground, &gcv); - - points [0].x = x; - points [0].y = y; - points [1].x = x + width; - points [1].y = y; - points [2].x = x + width - thickness; - points [2].y = y + thickness; - points [3].x = x; - points [3].y = y + thickness; - XFillPolygon (dpy, window, gc1, points, 4, Convex, CoordModeOrigin); - - points [0].x = x; - points [0].y = y + thickness; - points [1].x = x; - points [1].y = y + height; - points [2].x = x + thickness; - points [2].y = y + height - thickness; - points [3].x = x + thickness; - points [3].y = y + thickness; - XFillPolygon (dpy, window, gc1, points, 4, Convex, CoordModeOrigin); - - points [0].x = x + width; - points [0].y = y; - points [1].x = x + width - thickness; - points [1].y = y + thickness; - points [2].x = x + width - thickness; - points [2].y = y + height - thickness; - points [3].x = x + width; - points [3].y = y + height - thickness; - XFillPolygon (dpy, window, gc2, points, 4, Convex, CoordModeOrigin); - - points [0].x = x; - points [0].y = y + height; - points [1].x = x + width; - points [1].y = y + height; - points [2].x = x + width; - points [2].y = y + height - thickness; - points [3].x = x + thickness; - points [3].y = y + height - thickness; - XFillPolygon (dpy, window, gc2, points, 4, Convex, CoordModeOrigin); - - XFreeGC (dpy, gc1); - XFreeGC (dpy, gc2); -} - - -int -string_width (XFontStruct *font, char *s) -{ - return XTextWidth(font, s, strlen(s)); -} - - -static void update_splash_window (saver_info *si); -static void draw_splash_window (saver_info *si); -static void destroy_splash_window (saver_info *si); -static void unsplash_timer (XtPointer closure, XtIntervalId *id); - -static void do_demo (saver_screen_info *ssi); -#ifdef PREFS_BUTTON -static void do_prefs (saver_screen_info *ssi); -#endif /* PREFS_BUTTON */ -static void do_help (saver_screen_info *ssi); - - -XFontStruct * -splash_load_font (Display *dpy, char *name, char *class) -{ - char *s = get_string_resource (dpy, name, class); - XFontStruct *f; - if (!s || !*s) - s = "-*-helvetica-bold-r-*-*-*-140-*-*-*-*-*-*"; - f = load_font_retry (dpy, s); - if (!f) abort(); - return f; -} - - -struct splash_dialog_data { - - saver_screen_info *prompt_screen; - XtIntervalId timer; - - Dimension width; - Dimension height; - - char *heading_label; - char *body_label; - char *body2_label; - char *body3_label; - char *body4_label; - char *demo_label; -#ifdef PREFS_BUTTON - char *prefs_label; -#endif /* PREFS_BUTTON */ - char *help_label; - - XFontStruct *heading_font; - XFontStruct *body_font; - XFontStruct *button_font; - - Pixel foreground; - Pixel background; - Pixel border; - Pixel button_foreground; - Pixel button_background; - Pixel shadow_top; - Pixel shadow_bottom; - - Dimension logo_width; - Dimension logo_height; - Dimension internal_border; - Dimension shadow_width; - - Dimension button_width, button_height; - Dimension demo_button_x, demo_button_y; -#ifdef PREFS_BUTTON - Dimension prefs_button_x, prefs_button_y; -#endif /* PREFS_BUTTON */ - Dimension help_button_x, help_button_y; - - Pixmap logo_pixmap; - Pixmap logo_clipmask; - int logo_npixels; - unsigned long *logo_pixels; - - int pressed; -}; - - -void -make_splash_dialog (saver_info *si) -{ - saver_preferences *p = &si->prefs; - int x, y, bw; - XSetWindowAttributes attrs; - unsigned long attrmask = 0; - splash_dialog_data *sp; - saver_screen_info *ssi; - Colormap cmap; - - Bool whyne = senesculent_p (); - - if (whyne) - { - /* If locking is not enabled, make sure they see the message. */ - if (!p->lock_p) - { - si->prefs.splash_p = True; - if (si->prefs.splash_duration < 5000) - si->prefs.splash_duration = 5000; - } - si->prefs.splash_duration += 3000; - } - - if (si->sp_data) - return; - if (!si->prefs.splash_p || - si->prefs.splash_duration <= 0) - return; - - ssi = &si->screens[mouse_screen (si)]; - - if (!ssi || !ssi->screen) - return; /* WTF? Trying to splash while no screens connected? */ - - cmap = DefaultColormapOfScreen (ssi->screen); - - sp = (splash_dialog_data *) calloc (1, sizeof(*sp)); - sp->prompt_screen = ssi; - - sp->heading_label = get_string_resource (si->dpy, - "splash.heading.label", - "Dialog.Label.Label"); - sp->body_label = get_string_resource (si->dpy, - "splash.body.label", - "Dialog.Label.Label"); - sp->body2_label = get_string_resource (si->dpy, - "splash.body2.label", - "Dialog.Label.Label"); - sp->demo_label = get_string_resource (si->dpy, - "splash.demo.label", - "Dialog.Button.Label"); -#ifdef PREFS_BUTTON - sp->prefs_label = get_string_resource (si->dpy, - "splash.prefs.label", - "Dialog.Button.Label"); -#endif /* PREFS_BUTTON */ - sp->help_label = get_string_resource (si->dpy, - "splash.help.label", - "Dialog.Button.Label"); - - - - if (whyne) - { - sp->body3_label = strdup("WARNING: This version is very old!"); - sp->body4_label = strdup("Please upgrade!"); - } - - if (!sp->heading_label) - sp->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY"); - if (!sp->body_label) - sp->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY"); - if (!sp->body2_label) - sp->body2_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY"); - if (!sp->demo_label) sp->demo_label = strdup("ERROR"); -#ifdef PREFS_BUTTON - if (!sp->prefs_label) sp->prefs_label = strdup("ERROR"); -#endif /* PREFS_BUTTON */ - if (!sp->help_label) sp->help_label = strdup("ERROR"); - - /* Put the version number in the label. */ - { - char *s = (char *) malloc (strlen(sp->heading_label) + 20); - sprintf(s, sp->heading_label, si->version); - free (sp->heading_label); - sp->heading_label = s; - } - - sp->heading_font = - splash_load_font (si->dpy, "splash.headingFont", "Dialog.Font"); - sp->body_font = - splash_load_font (si->dpy, "splash.bodyFont", "Dialog.Font"); - sp->button_font = - splash_load_font (si->dpy, "splash.buttonFont", "Dialog.Font"); - - sp->foreground = get_pixel_resource (si->dpy, cmap, - "splash.foreground", - "Dialog.Foreground"); - sp->background = get_pixel_resource (si->dpy, cmap, - "splash.background", - "Dialog.Background"); - sp->border = get_pixel_resource (si->dpy, cmap, - "splash.borderColor", - "Dialog.borderColor"); - - if (sp->foreground == sp->background) - { - /* Make sure the error messages show up. */ - sp->foreground = BlackPixelOfScreen (ssi->screen); - sp->background = WhitePixelOfScreen (ssi->screen); - } - - sp->button_foreground = get_pixel_resource (si->dpy, cmap, - "splash.Button.foreground", - "Dialog.Button.Foreground"); - sp->button_background = get_pixel_resource (si->dpy, cmap, - "splash.Button.background", - "Dialog.Button.Background"); - sp->shadow_top = get_pixel_resource (si->dpy, cmap, - "splash.topShadowColor", - "Dialog.Foreground"); - sp->shadow_bottom = get_pixel_resource (si->dpy, cmap, - "splash.bottomShadowColor", - "Dialog.Background"); - - sp->logo_width = get_integer_resource (si->dpy, - "splash.logo.width", - "Dialog.Logo.Width"); - sp->logo_height = get_integer_resource (si->dpy, - "splash.logo.height", - "Dialog.Logo.Height"); - sp->internal_border = get_integer_resource (si->dpy, - "splash.internalBorderWidth", - "Dialog.InternalBorderWidth"); - sp->shadow_width = get_integer_resource (si->dpy, - "splash.shadowThickness", - "Dialog.ShadowThickness"); - - if (sp->logo_width == 0) sp->logo_width = 150; - if (sp->logo_height == 0) sp->logo_height = 150; - if (sp->internal_border == 0) sp->internal_border = 15; - if (sp->shadow_width == 0) sp->shadow_width = 4; - - { - int direction, ascent, descent; - XCharStruct overall; - - sp->width = 0; - sp->height = 0; - - /* Measure the heading_label. */ - XTextExtents (sp->heading_font, - sp->heading_label, strlen(sp->heading_label), - &direction, &ascent, &descent, &overall); - if (overall.width > sp->width) sp->width = overall.width; - sp->height += ascent + descent; - - /* Measure the body_label. */ - XTextExtents (sp->body_font, - sp->body_label, strlen(sp->body_label), - &direction, &ascent, &descent, &overall); - if (overall.width > sp->width) sp->width = overall.width; - sp->height += ascent + descent; - - /* Measure the body2_label. */ - XTextExtents (sp->body_font, - sp->body2_label, strlen(sp->body2_label), - &direction, &ascent, &descent, &overall); - if (overall.width > sp->width) sp->width = overall.width; - sp->height += ascent + descent; - - /* Measure the optional body3_label. */ - if (sp->body3_label) - { - XTextExtents (sp->heading_font, - sp->body3_label, strlen(sp->body3_label), - &direction, &ascent, &descent, &overall); - if (overall.width > sp->width) sp->width = overall.width; - XTextExtents (sp->heading_font, - sp->body4_label, strlen(sp->body4_label), - &direction, &ascent, &descent, &overall); - if (overall.width > sp->width) sp->width = overall.width; - sp->height += (ascent + descent) * 5; - } - - { - Dimension w2 = 0, w3 = 0, w4 = 0; - Dimension h2 = 0, h3 = 0, h4 = 0; - - /* Measure the Demo button. */ - XTextExtents (sp->button_font, - sp->demo_label, strlen(sp->demo_label), - &direction, &ascent, &descent, &overall); - w2 = overall.width; - h2 = ascent + descent; - -#ifdef PREFS_BUTTON - /* Measure the Prefs button. */ - XTextExtents (sp->button_font, - sp->prefs_label, strlen(sp->prefs_label), - &direction, &ascent, &descent, &overall); - w3 = overall.width; - h3 = ascent + descent; -#else /* !PREFS_BUTTON */ - w3 = 0; - h3 = 0; -#endif /* !PREFS_BUTTON */ - - /* Measure the Help button. */ - XTextExtents (sp->button_font, - sp->help_label, strlen(sp->help_label), - &direction, &ascent, &descent, &overall); - w4 = overall.width; - h4 = ascent + descent; - - w2 = MAX(w2, w3); w2 = MAX(w2, w4); - h2 = MAX(h2, h3); h2 = MAX(h2, h4); - - /* Add some horizontal padding inside the buttons. */ - w2 += ascent; - - w2 += ((ascent + descent) / 2) + (sp->shadow_width * 2); - h2 += ((ascent + descent) / 2) + (sp->shadow_width * 2); - - sp->button_width = w2; - sp->button_height = h2; - -#ifdef PREFS_BUTTON - w2 *= 3; -#else /* !PREFS_BUTTON */ - w2 *= 2; -#endif /* !PREFS_BUTTON */ - - w2 += ((ascent + descent) * 2); /* for space between buttons */ - - if (w2 > sp->width) sp->width = w2; - sp->height += h2; - } - - sp->width += (sp->internal_border * 2); - sp->height += (sp->internal_border * 3); - - if (sp->logo_height > sp->height) - sp->height = sp->logo_height; - else if (sp->height > sp->logo_height) - sp->logo_height = sp->height; - - sp->logo_width = sp->logo_height; - - sp->width += sp->logo_width; - } - - attrmask |= CWOverrideRedirect; attrs.override_redirect = True; - attrmask |= CWEventMask; - attrs.event_mask = (ExposureMask | ButtonPressMask | ButtonReleaseMask); - - { - int sx = 0, sy = 0, w, h; - - x = ssi->x; - y = ssi->y; - w = ssi->width; - h = ssi->height; - if (si->prefs.debug_p) w /= 2; - x = sx + (((w + sp->width) / 2) - sp->width); - y = sy + (((h + sp->height) / 2) - sp->height); - if (x < sx) x = sx; - if (y < sy) y = sy; - } - - bw = get_integer_resource (si->dpy, - "splash.borderWidth", - "Dialog.BorderWidth"); - - si->splash_dialog = - XCreateWindow (si->dpy, - RootWindowOfScreen(ssi->screen), - x, y, sp->width, sp->height, bw, - DefaultDepthOfScreen (ssi->screen), InputOutput, - DefaultVisualOfScreen(ssi->screen), - attrmask, &attrs); - XSetWindowBackground (si->dpy, si->splash_dialog, sp->background); - XSetWindowBorder (si->dpy, si->splash_dialog, sp->border); - - - sp->logo_pixmap = xscreensaver_logo (ssi->screen, - /* same visual as si->splash_dialog */ - DefaultVisualOfScreen (ssi->screen), - si->splash_dialog, cmap, - sp->background, - &sp->logo_pixels, &sp->logo_npixels, - &sp->logo_clipmask, True); - - XMapRaised (si->dpy, si->splash_dialog); - XSync (si->dpy, False); - - si->sp_data = sp; - - sp->timer = XtAppAddTimeOut (si->app, si->prefs.splash_duration, - unsplash_timer, (XtPointer) si); - - draw_splash_window (si); - XSync (si->dpy, False); -} - - -static void -draw_splash_window (saver_info *si) -{ - splash_dialog_data *sp = si->sp_data; - XGCValues gcv; - GC gc1, gc2; - int vspacing, height; - int x1, x2, x3, y1, y2; - int sw; - -#ifdef PREFS_BUTTON - int hspacing; - int nbuttons = 3; -#endif /* !PREFS_BUTTON */ - - height = (sp->heading_font->ascent + sp->heading_font->descent + - sp->body_font->ascent + sp->body_font->descent + - sp->body_font->ascent + sp->body_font->descent + - sp->button_font->ascent + sp->button_font->descent); - vspacing = ((sp->height - - (4 * sp->shadow_width) - - (2 * sp->internal_border) - - height) / 5); - if (vspacing < 0) vspacing = 0; - if (vspacing > (sp->heading_font->ascent * 2)) - vspacing = (sp->heading_font->ascent * 2); - - gcv.foreground = sp->foreground; - gc1 = XCreateGC (si->dpy, si->splash_dialog, GCForeground, &gcv); - gc2 = XCreateGC (si->dpy, si->splash_dialog, GCForeground, &gcv); - x1 = sp->logo_width; - x3 = sp->width - (sp->shadow_width * 2); - y1 = sp->internal_border; - - /* top heading - */ - XSetFont (si->dpy, gc1, sp->heading_font->fid); - sw = string_width (sp->heading_font, sp->heading_label); - x2 = (x1 + ((x3 - x1 - sw) / 2)); - y1 += sp->heading_font->ascent; - XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1, - sp->heading_label, strlen(sp->heading_label)); - y1 += sp->heading_font->descent; - - /* text below top heading - */ - XSetFont (si->dpy, gc1, sp->body_font->fid); - y1 += vspacing + sp->body_font->ascent; - sw = string_width (sp->body_font, sp->body_label); - x2 = (x1 + ((x3 - x1 - sw) / 2)); - XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1, - sp->body_label, strlen(sp->body_label)); - y1 += sp->body_font->descent; - - y1 += sp->body_font->ascent; - sw = string_width (sp->body_font, sp->body2_label); - x2 = (x1 + ((x3 - x1 - sw) / 2)); - XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1, - sp->body2_label, strlen(sp->body2_label)); - y1 += sp->body_font->descent; - - if (sp->body3_label) - { - XSetFont (si->dpy, gc1, sp->heading_font->fid); - y1 += sp->heading_font->ascent + sp->heading_font->descent; - y1 += sp->heading_font->ascent; - sw = string_width (sp->heading_font, sp->body3_label); - x2 = (x1 + ((x3 - x1 - sw) / 2)); - XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1, - sp->body3_label, strlen(sp->body3_label)); - y1 += sp->heading_font->descent + sp->heading_font->ascent; - sw = string_width (sp->heading_font, sp->body4_label); - x2 = (x1 + ((x3 - x1 - sw) / 2)); - XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1, - sp->body4_label, strlen(sp->body4_label)); - y1 += sp->heading_font->descent; - XSetFont (si->dpy, gc1, sp->body_font->fid); - } - - /* The buttons - */ - XSetForeground (si->dpy, gc1, sp->button_foreground); - XSetForeground (si->dpy, gc2, sp->button_background); - -/* y1 += (vspacing * 2);*/ - y1 = sp->height - sp->internal_border - sp->button_height; - - x1 += sp->internal_border; - y2 = (y1 + ((sp->button_height - - (sp->button_font->ascent + sp->button_font->descent)) - / 2) - + sp->button_font->ascent); -#ifdef PREFS_BUTTON - hspacing = ((sp->width - x1 - (sp->shadow_width * 2) - - sp->internal_border - (sp->button_width * nbuttons)) - / 2); -#endif - - x2 = x1 + ((sp->button_width - string_width(sp->button_font, sp->demo_label)) - / 2); - XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1, - sp->button_width, sp->button_height); - XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2, - sp->demo_label, strlen(sp->demo_label)); - sp->demo_button_x = x1; - sp->demo_button_y = y1; - -#ifdef PREFS_BUTTON - x1 += hspacing + sp->button_width; - x2 = x1 + ((sp->button_width - string_width(sp->button_font,sp->prefs_label)) - / 2); - XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1, - sp->button_width, sp->button_height); - XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2, - sp->prefs_label, strlen(sp->prefs_label)); - sp->prefs_button_x = x1; - sp->prefs_button_y = y1; -#endif /* PREFS_BUTTON */ - -#ifdef PREFS_BUTTON - x1 += hspacing + sp->button_width; -#else /* !PREFS_BUTTON */ - x1 = (sp->width - sp->button_width - - sp->internal_border - (sp->shadow_width * 2)); -#endif /* !PREFS_BUTTON */ - - x2 = x1 + ((sp->button_width - string_width(sp->button_font,sp->help_label)) - / 2); - XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1, - sp->button_width, sp->button_height); - XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2, - sp->help_label, strlen(sp->help_label)); - sp->help_button_x = x1; - sp->help_button_y = y1; - - - /* The logo - */ - x1 = sp->shadow_width * 6; - y1 = sp->shadow_width * 6; - x2 = sp->logo_width - (sp->shadow_width * 12); - y2 = sp->logo_height - (sp->shadow_width * 12); - - if (sp->logo_pixmap) - { - Window root; - int x, y; - unsigned int w, h, bw, d; - XGetGeometry (si->dpy, sp->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d); - XSetForeground (si->dpy, gc1, sp->foreground); - XSetBackground (si->dpy, gc1, sp->background); - XSetClipMask (si->dpy, gc1, sp->logo_clipmask); - XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) /2), y1 + ((y2 - (int)h) / 2)); - if (d == 1) - XCopyPlane (si->dpy, sp->logo_pixmap, si->splash_dialog, gc1, - 0, 0, w, h, - x1 + ((x2 - (int)w) / 2), - y1 + ((y2 - (int)h) / 2), - 1); - else - XCopyArea (si->dpy, sp->logo_pixmap, si->splash_dialog, gc1, - 0, 0, w, h, - x1 + ((x2 - (int)w) / 2), - y1 + ((y2 - (int)h) / 2)); - } - - /* Solid border inside the logo box. */ -#if 0 - XSetForeground (si->dpy, gc1, sp->foreground); - XDrawRectangle (si->dpy, si->splash_dialog, gc1, x1, y1, x2-1, y2-1); -#endif - - /* The shadow around the logo - */ - draw_shaded_rectangle (si->dpy, si->splash_dialog, - sp->shadow_width * 4, - sp->shadow_width * 4, - sp->logo_width - (sp->shadow_width * 8), - sp->logo_height - (sp->shadow_width * 8), - sp->shadow_width, - sp->shadow_bottom, sp->shadow_top); - - /* The shadow around the whole window - */ - draw_shaded_rectangle (si->dpy, si->splash_dialog, - 0, 0, sp->width, sp->height, sp->shadow_width, - sp->shadow_top, sp->shadow_bottom); - - XFreeGC (si->dpy, gc1); - XFreeGC (si->dpy, gc2); - - update_splash_window (si); -} - - -static void -update_splash_window (saver_info *si) -{ - splash_dialog_data *sp = si->sp_data; - int pressed; - if (!sp) return; - pressed = sp->pressed; - - /* The shadows around the buttons - */ - draw_shaded_rectangle (si->dpy, si->splash_dialog, - sp->demo_button_x, sp->demo_button_y, - sp->button_width, sp->button_height, sp->shadow_width, - (pressed == 1 ? sp->shadow_bottom : sp->shadow_top), - (pressed == 1 ? sp->shadow_top : sp->shadow_bottom)); -#ifdef PREFS_BUTTON - draw_shaded_rectangle (si->dpy, si->splash_dialog, - sp->prefs_button_x, sp->prefs_button_y, - sp->button_width, sp->button_height, sp->shadow_width, - (pressed == 2 ? sp->shadow_bottom : sp->shadow_top), - (pressed == 2 ? sp->shadow_top : sp->shadow_bottom)); -#endif /* PREFS_BUTTON */ - draw_shaded_rectangle (si->dpy, si->splash_dialog, - sp->help_button_x, sp->help_button_y, - sp->button_width, sp->button_height, sp->shadow_width, - (pressed == 3 ? sp->shadow_bottom : sp->shadow_top), - (pressed == 3 ? sp->shadow_top : sp->shadow_bottom)); -} - -static void -destroy_splash_window (saver_info *si) -{ - splash_dialog_data *sp = si->sp_data; - saver_screen_info *ssi = sp->prompt_screen; - Colormap cmap = DefaultColormapOfScreen (ssi->screen); - Pixel black = BlackPixelOfScreen (ssi->screen); - Pixel white = WhitePixelOfScreen (ssi->screen); - - if (sp->timer) - XtRemoveTimeOut (sp->timer); - - if (si->splash_dialog) - { - XDestroyWindow (si->dpy, si->splash_dialog); - si->splash_dialog = 0; - } - - if (sp->heading_label) free (sp->heading_label); - if (sp->body_label) free (sp->body_label); - if (sp->body2_label) free (sp->body2_label); - if (sp->body3_label) free (sp->body3_label); - if (sp->body4_label) free (sp->body4_label); - if (sp->demo_label) free (sp->demo_label); -#ifdef PREFS_BUTTON - if (sp->prefs_label) free (sp->prefs_label); -#endif /* PREFS_BUTTON */ - if (sp->help_label) free (sp->help_label); - - if (sp->heading_font) XFreeFont (si->dpy, sp->heading_font); - if (sp->body_font) XFreeFont (si->dpy, sp->body_font); - if (sp->button_font) XFreeFont (si->dpy, sp->button_font); - - if (sp->foreground != black && sp->foreground != white) - XFreeColors (si->dpy, cmap, &sp->foreground, 1, 0L); - if (sp->background != black && sp->background != white) - XFreeColors (si->dpy, cmap, &sp->background, 1, 0L); - if (sp->button_foreground != black && sp->button_foreground != white) - XFreeColors (si->dpy, cmap, &sp->button_foreground, 1, 0L); - if (sp->button_background != black && sp->button_background != white) - XFreeColors (si->dpy, cmap, &sp->button_background, 1, 0L); - if (sp->shadow_top != black && sp->shadow_top != white) - XFreeColors (si->dpy, cmap, &sp->shadow_top, 1, 0L); - if (sp->shadow_bottom != black && sp->shadow_bottom != white) - XFreeColors (si->dpy, cmap, &sp->shadow_bottom, 1, 0L); - - if (sp->logo_pixmap) - XFreePixmap (si->dpy, sp->logo_pixmap); - if (sp->logo_clipmask) - XFreePixmap (si->dpy, sp->logo_clipmask); - if (sp->logo_pixels) - { - if (sp->logo_npixels) - XFreeColors (si->dpy, cmap, sp->logo_pixels, sp->logo_npixels, 0L); - free (sp->logo_pixels); - sp->logo_pixels = 0; - sp->logo_npixels = 0; - } - - memset (sp, 0, sizeof(*sp)); - free (sp); - si->sp_data = 0; -} - -void -handle_splash_event (saver_info *si, XEvent *event) -{ - splash_dialog_data *sp = si->sp_data; - saver_screen_info *ssi; - int which = 0; - if (!sp) return; - ssi = sp->prompt_screen; - - switch (event->xany.type) - { - case Expose: - draw_splash_window (si); - break; - - case ButtonPress: case ButtonRelease: - - if (event->xbutton.x >= sp->demo_button_x && - event->xbutton.x < sp->demo_button_x + sp->button_width && - event->xbutton.y >= sp->demo_button_y && - event->xbutton.y < sp->demo_button_y + sp->button_height) - which = 1; - -#ifdef PREFS_BUTTON - else if (event->xbutton.x >= sp->prefs_button_x && - event->xbutton.x < sp->prefs_button_x + sp->button_width && - event->xbutton.y >= sp->prefs_button_y && - event->xbutton.y < sp->prefs_button_y + sp->button_height) - which = 2; -#endif /* PREFS_BUTTON */ - - else if (event->xbutton.x >= sp->help_button_x && - event->xbutton.x < sp->help_button_x + sp->button_width && - event->xbutton.y >= sp->help_button_y && - event->xbutton.y < sp->help_button_y + sp->button_height) - which = 3; - - if (event->xany.type == ButtonPress) - { - sp->pressed = which; - update_splash_window (si); - if (which == 0) - XBell (si->dpy, False); - } - else if (event->xany.type == ButtonRelease) - { - if (which && sp->pressed == which) - { - destroy_splash_window (si); - sp = si->sp_data; - switch (which) - { - case 1: do_demo (ssi); break; -#ifdef PREFS_BUTTON - case 2: do_prefs (ssi); break; -#endif /* PREFS_BUTTON */ - case 3: do_help (ssi); break; - default: abort(); - } - } - else if (which == 0 && sp->pressed == 0) - { - /* click and release on the window but not in a button: - treat that as "dismiss the splash dialog." */ - destroy_splash_window (si); - sp = si->sp_data; - } - if (sp) sp->pressed = 0; - update_splash_window (si); - } - break; - - default: - break; - } -} - -static void -unsplash_timer (XtPointer closure, XtIntervalId *id) -{ - saver_info *si = (saver_info *) closure; - if (si && si->sp_data) - destroy_splash_window (si); -} - - -/* Button callbacks */ - -#ifdef VMS -# define pid_t int -# define fork vfork -#endif /* VMS */ - - -static void -do_demo (saver_screen_info *ssi) -{ - saver_info *si = ssi->global; - saver_preferences *p = &si->prefs; - const char *cmd = p->demo_command; - - if (cmd && *cmd) - fork_and_exec (ssi, cmd); - else - fprintf (stderr, "%s: no demo-mode command has been specified.\n", - blurb()); -} - -#ifdef PREFS_BUTTON -static void -do_prefs (saver_screen_info *ssi) -{ - saver_info *si = ssi->global; - saver_preferences *p = &si->prefs; - const char *cmd = p->prefs_command; - - if (cmd && *cmd) - fork_and_exec (ssi, cmd); - else - fprintf (stderr, "%s: no preferences command has been specified.\n", - blurb()); -} -#endif /* PREFS_BUTTON */ - -static void -do_help (saver_screen_info *ssi) -{ - saver_info *si = ssi->global; - saver_preferences *p = &si->prefs; - char *help_command = 0; - - if (!p->load_url_command || !*p->load_url_command) - { - fprintf (stderr, "%s: no URL command has been specified.\n", blurb()); - return; - } - if (!p->help_url || !*p->help_url) - { - fprintf (stderr, "%s: no Help URL has been specified.\n", blurb()); - return; - } - - help_command = (char *) malloc (strlen (p->load_url_command) + - (strlen (p->help_url) * 4) + 10); - sprintf (help_command, p->load_url_command, - p->help_url, p->help_url, p->help_url, p->help_url); - - fork_and_exec (ssi, help_command); - free (help_command); -} diff --git a/driver/subprocs.c b/driver/subprocs.c index 6cb96d3..2b3453d 100644 --- a/driver/subprocs.c +++ b/driver/subprocs.c @@ -1,5 +1,5 @@ /* subprocs.c --- choosing, spawning, and killing screenhacks. - * xscreensaver, Copyright (c) 1991-2019 Jamie Zawinski + * xscreensaver, Copyright © 1991-2021 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 @@ -19,6 +19,8 @@ #include #include /* not used for much... */ +#include +#include /* For XtAppAddSignal */ #ifndef ESRCH # include @@ -35,169 +37,23 @@ # include /* for setrlimit() and RLIMIT_AS */ #endif -#ifdef VMS -# include -# include /* for close */ -# include /* for getpid */ -# define pid_t int -# define fork vfork -#endif /* VMS */ - #include /* for the signal names */ #include -#if !defined(SIGCHLD) && defined(SIGCLD) -# define SIGCHLD SIGCLD -#endif - -#if 0 /* putenv() is declared in stdlib.h on modern linux systems. */ -#ifdef HAVE_PUTENV -extern int putenv (/* const char * */); /* getenv() is in stdlib.h... */ -#endif +#ifdef ENABLE_NLS +# include +# include +# define _(S) gettext(S) +#else +# define _(S) (S) #endif -extern int kill (pid_t, int); /* signal() is in sys/signal.h... */ - -/* This file doesn't need the Xt headers, so stub these types out... */ -#undef XtPointer -#define XtAppContext void* -#define XrmDatabase void* -#define XtIntervalId void* -#define XtPointer void* -#define Widget void* - #include "xscreensaver.h" #include "exec.h" #include "yarandom.h" -#include "visual.h" /* for id_to_visual() */ - -extern saver_info *global_si_kludge; /* I hate C so much... */ - - -/* Used when printing error/debugging messages from signal handlers. - */ -static const char * -no_malloc_number_to_string (long num) -{ - static char string[128] = ""; - int num_digits; - Bool negative_p = False; - - num_digits = 0; - - if (num == 0) - return "0"; - - if (num < 0) - { - negative_p = True; - num = -num; - } - - while ((num > 0) && (num_digits < sizeof(string) - 1)) - { - int digit; - digit = (int) num % 10; - num_digits++; - string[sizeof(string) - 1 - num_digits] = digit + '0'; - num /= 10; - } - - if (negative_p) - { - num_digits++; - string[sizeof(string) - 1 - num_digits] = '-'; - } - - return string + sizeof(string) - 1 - num_digits; -} - -/* Like write(), but runs strlen() on the arg to get the length. */ -static int -write_string (int fd, const char *str) -{ - return write (fd, str, strlen (str)); -} - -static int -write_long (int fd, long n) -{ - const char *str = no_malloc_number_to_string (n); - return write_string (fd, str); -} - - -/* RLIMIT_AS (called RLIMIT_VMEM on some systems) controls the maximum size - of a process's address space, i.e., the maximal brk(2) and mmap(2) values. - Setting this lets you put a cap on how much memory a process can allocate. - - Except the "and mmap()" part kinda makes this useless, since many GL - implementations end up using mmap() to pull the whole frame buffer into - memory (or something along those lines) making it appear processes are - using hundreds of megabytes when in fact they're using very little, and - we end up capping their mallocs prematurely. YAY! - */ -#if defined(RLIMIT_VMEM) && !defined(RLIMIT_AS) -# define RLIMIT_AS RLIMIT_VMEM -#endif - -static void -limit_subproc_memory (int address_space_limit, Bool verbose_p) -{ - -/* This has caused way more problems than it has solved... - Let's just completely ignore the "memoryLimit" option now. - */ -#undef HAVE_SETRLIMIT - -#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_AS) - struct rlimit r; - - if (address_space_limit < 10 * 1024) /* let's not be crazy */ - return; - - if (getrlimit (RLIMIT_AS, &r) != 0) - { - char buf [512]; - sprintf (buf, "%s: getrlimit(RLIMIT_AS) failed", blurb()); - perror (buf); - return; - } - - r.rlim_cur = address_space_limit; - - if (setrlimit (RLIMIT_AS, &r) != 0) - { - char buf [512]; - sprintf (buf, "%s: setrlimit(RLIMIT_AS, {%lu, %lu}) failed", - blurb(), r.rlim_cur, r.rlim_max); - perror (buf); - return; - } - - if (verbose_p) - { - int i = address_space_limit; - char buf[100]; - if (i >= (1<<30) && i == ((i >> 30) << 30)) - sprintf(buf, "%dG", i >> 30); - else if (i >= (1<<20) && i == ((i >> 20) << 20)) - sprintf(buf, "%dM", i >> 20); - else if (i >= (1<<10) && i == ((i >> 10) << 10)) - sprintf(buf, "%dK", i >> 10); - else - sprintf(buf, "%d bytes", i); - - fprintf (stderr, "%s: limited pid %lu address space to %s.\n", - blurb(), (unsigned long) getpid (), buf); - } - -#endif /* HAVE_SETRLIMIT && RLIMIT_AS */ -} +#include "visual.h" /* for id_to_visual() */ +#include "atoms.h" - -/* Management of child processes, and de-zombification. - */ enum job_status { job_running, /* the process is still alive */ @@ -220,10 +76,107 @@ struct screenhack_job { static struct screenhack_job *jobs = 0; -/* for debugging -- nothing calls this, but it's useful to invoke from gdb. +static void clean_job_list (void); +static void await_dying_children (saver_info *si); +static void describe_dead_child (saver_info *, pid_t, int wait_status, + struct rusage); + +static XtSignalId xt_sigterm_id = 0; +static int sigterm_received = 0; + +static XtSignalId xt_sigchld_id = 0; +static int sigchld_received = 0; + + +const char * +signal_name (int signal) +{ + /* sys_signame[], sys_siglist[], strsignal() and sigabbrev_np() + are an unportable mess. */ + switch (signal) { + case SIGHUP: return "SIGHUP"; + case SIGINT: return "SIGINT"; + case SIGQUIT: return "SIGQUIT"; + case SIGILL: return "SIGILL"; + case SIGTRAP: return "SIGTRAP"; +#ifdef SIGABRT + case SIGABRT: return "SIGABRT"; +#endif + case SIGFPE: return "SIGFPE"; + case SIGKILL: return "SIGKILL"; + case SIGBUS: return "SIGBUS"; + case SIGSEGV: return "SIGSEGV"; + case SIGPIPE: return "SIGPIPE"; + case SIGALRM: return "SIGALRM"; + case SIGTERM: return "SIGTERM"; +#ifdef SIGSTOP + case SIGSTOP: return "SIGSTOP"; +#endif +#ifdef SIGCONT + case SIGCONT: return "SIGCONT"; +#endif +#ifdef SIGUSR1 + case SIGUSR1: return "SIGUSR1"; +#endif +#ifdef SIGUSR2 + case SIGUSR2: return "SIGUSR2"; +#endif +#ifdef SIGEMT + case SIGEMT: return "SIGEMT"; +#endif +#ifdef SIGSYS + case SIGSYS: return "SIGSYS"; +#endif + case SIGCHLD: return "SIGCHLD"; +#ifdef SIGPWR + case SIGPWR: return "SIGPWR"; +#endif +#ifdef SIGWINCH + case SIGWINCH: return "SIGWINCH"; +#endif +#ifdef SIGURG + case SIGURG: return "SIGURG"; +#endif +#ifdef SIGIO + case SIGIO: return "SIGIO"; +#endif +#ifdef SIGVTALRM + case SIGVTALRM: return "SIGVTALRM"; +#endif +#ifdef SIGXCPU + case SIGXCPU: return "SIGXCPU"; +#endif +#ifdef SIGXFSZ + case SIGXFSZ: return "SIGXFSZ"; +#endif +#ifdef SIGDANGER + case SIGDANGER: return "SIGDANGER"; +#endif + default: + { + static char buf[50]; + sprintf(buf, "signal %d\n", signal); + return buf; + } + } +} + + +/* Management of child processes, and de-zombification. */ -void show_job_list (void); +static char * +timestring (time_t when) +{ + static char buf[255] = { 0 }; + struct tm tm; + localtime_r (&when, &tm); + sprintf (buf, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec); + return buf; +} + +#ifdef DEBUG +void show_job_list (void); void show_job_list (void) { @@ -231,27 +184,24 @@ show_job_list (void) fprintf(stderr, "%s: job list:\n", blurb()); for (job = jobs; job; job = job->next) { - char b[] = " ??:??:?? "; - char *t = (job->killed ? timestring (job->killed) : - job->launched ? timestring (job->launched) : b); - t += 11; - t[8] = 0; - fprintf (stderr, " %5ld: %2d: (%s) %s %s\n", - (long) job->pid, - job->screen, - (job->status == job_running ? "running" : - job->status == job_stopped ? "stopped" : - job->status == job_killed ? " killed" : - job->status == job_dead ? " dead" : " ???"), - t, job->name); + fprintf (stderr, " %5ld: %2d: (%s) %s %s\n", + (long) job->pid, + job->screen, + (job->status == job_running ? "running" : + job->status == job_stopped ? "stopped" : + job->status == job_killed ? " killed" : + job->status == job_dead ? " dead" : " ???"), + (job->killed ? timestring (job->killed) : + job->launched ? timestring (job->launched) : + "??:??:??"), + job->name); } fprintf (stderr, "\n"); } +#endif /* DEBUG */ -static void clean_job_list (void); - -static struct screenhack_job * +static void make_job (pid_t pid, int screen, const char *cmd) { struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job)); @@ -288,8 +238,6 @@ make_job (pid_t pid, int screen, const char *cmd) job->killed = 0; job->next = jobs; jobs = job; - - return jobs; } @@ -317,8 +265,7 @@ free_job (struct screenhack_job *job) } -/* Cleans out dead jobs from the jobs list -- this must only be called - from the main thread, not from a signal handler. +/* Cleans out dead jobs from the jobs list. */ static void clean_job_list (void) @@ -367,79 +314,8 @@ find_job (pid_t pid) return 0; } -static void await_dying_children (saver_info *si); -#ifndef VMS -static void describe_dead_child (saver_info *, pid_t, int wait_status); -#endif - - -/* Semaphore to temporarily turn the SIGCHLD handler into a no-op. - Don't alter this directly -- use block_sigchld() / unblock_sigchld(). - */ -static int block_sigchld_handler = 0; - - -#ifdef HAVE_SIGACTION - sigset_t -#else /* !HAVE_SIGACTION */ - int -#endif /* !HAVE_SIGACTION */ -block_sigchld (void) -{ -#ifdef HAVE_SIGACTION - struct sigaction sa; - sigset_t child_set; - - memset (&sa, 0, sizeof (sa)); - sa.sa_handler = SIG_IGN; - sigaction (SIGPIPE, &sa, NULL); - - sigemptyset (&child_set); - sigaddset (&child_set, SIGCHLD); - sigprocmask (SIG_BLOCK, &child_set, 0); - -#else /* !HAVE_SIGACTION */ - signal (SIGPIPE, SIG_IGN); -#endif /* !HAVE_SIGACTION */ - - block_sigchld_handler++; - -#ifdef HAVE_SIGACTION - return child_set; -#else /* !HAVE_SIGACTION */ - return 0; -#endif /* !HAVE_SIGACTION */ -} - -void -unblock_sigchld (void) -{ - if (block_sigchld_handler <= 0) - abort(); - - if (block_sigchld_handler <= 1) /* only unblock if count going to 0 */ - { -#ifdef HAVE_SIGACTION - struct sigaction sa; - sigset_t child_set; - - memset(&sa, 0, sizeof (sa)); - sa.sa_handler = SIG_DFL; - sigaction(SIGPIPE, &sa, NULL); - - sigemptyset(&child_set); - sigaddset(&child_set, SIGCHLD); - sigprocmask(SIG_UNBLOCK, &child_set, 0); - -#else /* !HAVE_SIGACTION */ - signal(SIGPIPE, SIG_DFL); -#endif /* !HAVE_SIGACTION */ - } - - block_sigchld_handler--; -} -int +static int kill_job (saver_info *si, pid_t pid, int signal) { saver_preferences *p = &si->prefs; @@ -448,12 +324,6 @@ kill_job (saver_info *si, pid_t pid, int signal) clean_job_list(); - if (in_signal_handler_p) - /* This function should not be called from the signal handler. */ - abort(); - - block_sigchld(); /* we control the horizontal... */ - job = find_job (pid); if (!job || !job->pid || @@ -471,7 +341,6 @@ kill_job (saver_info *si, pid_t pid, int signal) job->killed = time ((time_t *) 0); break; #ifdef SIGSTOP - /* #### there must be a way to do this on VMS... */ case SIGSTOP: job->status = job_stopped; break; case SIGCONT: job->status = job_running; break; #endif /* SIGSTOP */ @@ -492,7 +361,7 @@ kill_job (saver_info *si, pid_t pid, int signal) { if (errno == ESRCH) fprintf (stderr, - "%s: %d: child process %lu (%s) was already dead.\n", + "%s: %d: child process %lu (%s) was already dead\n", blurb(), job->screen, (unsigned long) job->pid, job->name); else { @@ -506,53 +375,148 @@ kill_job (saver_info *si, pid_t pid, int signal) await_dying_children (si); DONE: - unblock_sigchld(); - if (block_sigchld_handler < 0) - abort(); - clean_job_list(); return status; } -#ifdef SIGCHLD -static RETSIGTYPE -sigchld_handler (int sig) +/* We use Xt-style signal handling. A Unix signal fires, and we inform Xt of + that. Then after we return to the top-level command loop on the main + stack, Xt runs our callback function for that signal. Just like Xt timers. + */ +static void +catch_signal (int sig, RETSIGTYPE (*handler) (int)) { - saver_info *si = global_si_kludge; /* I hate C so much... */ - in_signal_handler_p++; - - if (si->prefs.debug_p) +# ifdef HAVE_SIGACTION + struct sigaction a; + a.sa_handler = handler; + sigemptyset (&a.sa_mask); + a.sa_flags = 0; + if (sigaction (sig, &a, 0) < 0) +# else /* !HAVE_SIGACTION */ + if (((long) signal (sig, handler)) == -1L) +# endif /* !HAVE_SIGACTION */ { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(), - (block_sigchld_handler ? " (blocked)" : "")); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": got SIGCHLD"); - - if (block_sigchld_handler) - write_string (STDERR_FILENO, " (blocked)\n"); - else - write_string (STDERR_FILENO, "\n"); + char buf [255]; + sprintf (buf, "%s: couldn't catch signal %s", blurb(), + signal_name (sig)); + perror (buf); + abort(); } +} + + +/* Exiting gracefully. + + When xscreensaver sends a SIGTERM signal to xscreensaver-gfx, rather + than exiting immediately, we want it to do two things: + + - Send a SIGTERM to each running screenhack. They would *probably* + die of a BadWindow X error once their window was deleted, but this + is cleaner and more immediate. + + - Fade the screens in from black. This might take several seconds. + + Should another signal come in while that is ongoing, we should just + die immediately. + */ - if (block_sigchld_handler < 0) - abort(); - else if (block_sigchld_handler == 0) +static RETSIGTYPE +saver_sigterm_handler (int sig) +{ + /* This is the actual signal handler, running on the signal stack. + After firing once, set this signal back to the default behavior. */ + sigterm_received = sig; + catch_signal (sig, SIG_DFL); + + /* The first time a signal fires, inform Xt of that so that it will run + xt_signal_handler(). "XtNoticeSignal is the only Intrinsics function + that can safely be called from a signal handler". */ + if (xt_sigterm_id) + XtNoticeSignal (xt_sigterm_id); +} + + +static void +xt_sigterm_handler (XtPointer data, XtSignalId *id) +{ + /* This runs from the Xt event loop on the main stack, some time after + the signal fired. */ + saver_info *si = (saver_info *) data; + saver_preferences *p = &si->prefs; + static Bool hit_p = False; + int i; + + if (xt_sigterm_id) + XtRemoveSignal (xt_sigterm_id); + xt_sigterm_id = 0; + + if (hit_p) + fprintf (stderr, "%s: second signal: %s: exiting\n", blurb(), + signal_name (sigterm_received)); + else { - block_sigchld(); - await_dying_children (si); - unblock_sigchld(); + hit_p = True; + if (p->verbose_p) + fprintf (stderr, "%s: %s: unblanking\n", blurb(), + signal_name (sigterm_received)); + + /* Kill before unblanking, to stop drawing as soon as possible. */ + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (ssi->cycle_id) + { + XtRemoveTimeOut (ssi->cycle_id); + ssi->cycle_id = 0; + ssi->cycle_at = 0; + } + kill_screenhack (ssi); + } + unblank_screen (si); + + if (p->verbose_p) + fprintf (stderr, "%s: %s: exiting\n", blurb(), + signal_name (sigterm_received)); } - init_sigchld(); - in_signal_handler_p--; + /* Exit with the original signal received. */ + kill (getpid(), sigterm_received); + abort(); +} + + +/* SIGCHLD handling. Basically the same deal as SIGTERM. + */ + +static RETSIGTYPE +sigchld_handler (int sig) +{ + /* This is the actual signal handler, running on the signal stack. + After firing once, set this signal to fire again. */ + sigchld_received = sig; +# ifndef HAVE_SIGACTION + catch_signal (SIGCHLD, sigchld_handler); +# endif + + if (xt_sigchld_id) + XtNoticeSignal (xt_sigchld_id); } -#endif /* SIGCHLD */ -#ifndef VMS +static void +xt_sigchld_handler (XtPointer data, XtSignalId *id) +{ + /* This runs from the Xt event loop on the main stack, some time after + the signal fired. */ + saver_info *si = (saver_info *) data; + + if (si->prefs.debug_p) + fprintf(stderr, "%s: got SIGCHLD\n", blurb()); + + await_dying_children (si); /* Their first album was better */ +} + static void await_dying_children (saver_info *si) @@ -561,36 +525,19 @@ await_dying_children (saver_info *si) { int wait_status = 0; pid_t kid; + struct rusage rus; errno = 0; - kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED); + kid = wait4 (-1, &wait_status, WNOHANG|WUNTRACED, &rus); if (si->prefs.debug_p) { if (kid < 0 && errno) - { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(), - (long) kid, errno); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": waitpid(-1) ==> "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_long (STDERR_FILENO, (long) errno); - write_string (STDERR_FILENO, ")\n"); - } + fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(), + (long) kid, errno); else - { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(), - (long) kid); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": waitpid(-1) ==> "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, "\n"); - } + fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(), + (long) kid); } /* 0 means no more children to reap. @@ -600,272 +547,193 @@ await_dying_children (saver_info *si) (kid < 0 && errno != EINTR)) break; - describe_dead_child (si, kid, wait_status); + describe_dead_child (si, kid, wait_status, rus); } } static void -describe_dead_child (saver_info *si, pid_t kid, int wait_status) +describe_dead_child (saver_info *si, pid_t kid, int wait_status, + struct rusage rus) { int i; saver_preferences *p = &si->prefs; struct screenhack_job *job = find_job (kid); const char *name = job ? job->name : ""; int screen_no = job ? job->screen : 0; + char msg[1024]; + *msg = 0; if (WIFEXITED (wait_status)) { int exit_status = WEXITSTATUS (wait_status); - /* Treat exit code as a signed 8-bit quantity. */ if (exit_status & 0x80) exit_status |= ~0xFF; - /* One might assume that exiting with non-0 means something went wrong. - But that loser xswarm exits with the code that it was killed with, so - it *always* exits abnormally. Treat abnormal exits as "normal" (don't - mention them) if we've just killed the subprocess. But mention them - if they happen on their own. - */ - if (!job || - (exit_status != 0 && - (p->verbose_p || job->status != job_killed))) - { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, - "%s: %d: child pid %lu (%s) exited abnormally (code %d).\n", - blurb(), screen_no, (unsigned long) kid, name, exit_status); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": "); - write_long (STDERR_FILENO, (long) screen_no); - write_string (STDERR_FILENO, ": child pid "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_string (STDERR_FILENO, name); - write_string (STDERR_FILENO, ") exited abnormally (code "); - write_long (STDERR_FILENO, (long) exit_status); - write_string (STDERR_FILENO, ").\n"); - } - else if (p->verbose_p) - { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, "%s: %d: child pid %lu (%s) exited normally.\n", - blurb(), screen_no, (unsigned long) kid, name); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": "); - write_long (STDERR_FILENO, (long) screen_no); - write_string (STDERR_FILENO, ": child pid "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_string (STDERR_FILENO, name); - write_string (STDERR_FILENO, ") exited normally.\n"); - } - + sprintf (msg, _("crashed with status %d"), exit_status); + if (p->verbose_p) + fprintf (stderr, + "%s: %d: child pid %lu (%s) exited abnormally" + " with status %d\n", + blurb(), screen_no, (unsigned long) kid, name, exit_status); if (job) job->status = job_dead; } else if (WIFSIGNALED (wait_status)) { - if (p->verbose_p || - !job || - job->status != job_killed || - WTERMSIG (wait_status) != SIGTERM) + const char *sig = signal_name (WTERMSIG (wait_status)); + if (job && + job->status == job_killed && + WTERMSIG (wait_status) == SIGTERM) { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, "%s: %d: child pid %lu (%s) terminated with %s.\n", - blurb(), screen_no, (unsigned long) kid, name, - signal_name (WTERMSIG(wait_status))); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": "); - write_long (STDERR_FILENO, (long) screen_no); - write_string (STDERR_FILENO, ": child pid "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_string (STDERR_FILENO, name); - write_string (STDERR_FILENO, ") terminated with signal "); - write_long (STDERR_FILENO, WTERMSIG(wait_status)); - write_string (STDERR_FILENO, ".\n"); + /* Expected notification after we killed it. */ + sprintf (msg, _("exited normally with %.100s"), sig); + if (p->verbose_p) + fprintf (stderr, "%s: %d: child pid %lu (%s)" + " exited normally with %s\n", + blurb(), screen_no, (unsigned long) kid, name, sig); + } + else + { + /* Unexpected signal. */ + sprintf (msg, _("crashed with %.100s"), sig); + if (p->verbose_p) + fprintf (stderr, "%s: %d: child pid %lu (%s)" + " unexpectedly terminated with %s\n", + blurb(), screen_no, (unsigned long) kid, name, sig); } - if (job) job->status = job_dead; } else if (WIFSTOPPED (wait_status)) { if (p->verbose_p) - { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n", - blurb(), (unsigned long) kid, name, - signal_name (WSTOPSIG (wait_status))); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": "); - write_long (STDERR_FILENO, (long) screen_no); - write_string (STDERR_FILENO, ": child pid "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_string (STDERR_FILENO, name); - write_string (STDERR_FILENO, ") stopped with signal "); - write_long (STDERR_FILENO, WSTOPSIG(wait_status)); - write_string (STDERR_FILENO, ".\n"); - } - + fprintf (stderr, "%s: child pid %lu (%s) stopped with %s\n", + blurb(), (unsigned long) kid, name, + signal_name (WSTOPSIG (wait_status))); if (job) job->status = job_stopped; } else { - /* Don't call fprintf() from signal handlers, as it might malloc. + /* Didn't exit, signal or stop; is this even possible? */ + sprintf (msg, _("crashed mysteriously")); fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!", blurb(), (unsigned long) kid, name); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": "); - write_long (STDERR_FILENO, (long) screen_no); - write_string (STDERR_FILENO, ": child pid "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_string (STDERR_FILENO, name); - write_string (STDERR_FILENO, ") died in a mysterious way!"); if (job) job->status = job_dead; } - /* Clear out the pid so that screenhack_running_p() knows it's dead. +# ifdef LOG_CPU_TIME + if (p->verbose_p && job && job->status == job_dead) + { + long u = rus.ru_utime.tv_usec / 1000 + rus.ru_utime.tv_sec * 1000; + long s = rus.ru_stime.tv_usec / 1000 + rus.ru_stime.tv_sec * 1000; + fprintf (stderr, "%s: %d: CPU used: %.1fu, %.1fs\n", + blurb(), screen_no, u / 1000.0, s / 1000.0); + } +# endif /* LOG_CPU_TIME */ + + /* Clear out the pid so that any_screenhacks_running_p() knows it's dead. */ if (!job || job->status == job_dead) { - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (kid == ssi->pid) - ssi->pid = 0; - } -# ifdef HAVE_LIBSYSTEMD - if (kid == si->systemd_pid) - si->systemd_pid = 0; -# endif + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (kid == ssi->pid) + { + ssi->pid = 0; + if (*msg) + screenhack_obituary (ssi, name, msg); + } + } } } -#else /* VMS */ -static void await_dying_children (saver_info *si) { return; } -#endif /* VMS */ - void -init_sigchld (void) +init_sigchld (saver_info *si) { -#ifdef SIGCHLD - -# ifdef HAVE_SIGACTION /* Thanks to Tom Kelly */ - - static Bool sigchld_initialized_p = 0; - if (!sigchld_initialized_p) - { - struct sigaction action, old; - - action.sa_handler = sigchld_handler; - sigemptyset(&action.sa_mask); - action.sa_flags = 0; - - if (sigaction(SIGCHLD, &action, &old) < 0) - { - char buf [255]; - sprintf (buf, "%s: couldn't catch SIGCHLD", blurb()); - perror (buf); - } - sigchld_initialized_p = True; - } + static Bool signals_initialized_p = 0; + if (signals_initialized_p) return; + signals_initialized_p = True; -# else /* !HAVE_SIGACTION */ + catch_signal (SIGTERM, saver_sigterm_handler); /* kill */ + catch_signal (SIGINT, saver_sigterm_handler); /* shell ^C */ + catch_signal (SIGQUIT, saver_sigterm_handler); /* shell ^| */ + catch_signal (SIGCHLD, sigchld_handler); - if (((long) signal (SIGCHLD, sigchld_handler)) == -1L) - { - char buf [255]; - sprintf (buf, "%s: couldn't catch SIGCHLD", blurb()); - perror (buf); - } -# endif /* !HAVE_SIGACTION */ -#endif /* SIGCHLD */ + xt_sigchld_id = XtAppAddSignal (si->app, xt_sigchld_handler, si); + xt_sigterm_id = XtAppAddSignal (si->app, xt_sigterm_handler, si); } +static void +hack_subproc_environment (Screen *screen, Window saver_window) +{ + /* Store $DISPLAY into the environment, so that the $DISPLAY variable that + the spawned processes inherit is correct. First, it must be on the same + host and display as the value of -display passed in on our command line + (which is not necessarily the same as what our $DISPLAY variable is.) + Second, the screen number in the $DISPLAY passed to the subprocess should + be the screen on which this particular hack is running -- not the display + specification which the driver itself is using, since the driver ignores + its screen number and manages all existing screens. - + Likewise, store a window ID in $XSCREENSAVER_WINDOW -- this is necessary + in a Xinerama or RANDR world where a single X11 'Screen' spans multiple + monitors, and we want to run a hack on each piece of glass, not spanning + them. In that case, multiple hacks have the same $DISPLAY, screen and + root window. + */ + Display *dpy = DisplayOfScreen (screen); + const char *odpy = DisplayString (dpy); + char *ndpy = (char *) malloc (strlen(odpy) + 20); + char *nssw = (char *) malloc (40); + char *s, *c; -static Bool -select_visual_of_hack (saver_screen_info *ssi, screenhack *hack) -{ - saver_info *si = ssi->global; - saver_preferences *p = &si->prefs; - Bool selected; + strcpy (ndpy, "DISPLAY="); + s = ndpy + strlen(ndpy); + strcpy (s, odpy); - if (hack->visual && *hack->visual) - selected = select_visual(ssi, hack->visual); - else - selected = select_visual(ssi, 0); + /* We have to find the last colon since it is the boundary between + hostname & screen - IPv6 numeric format addresses may have many + colons before that point, and DECnet addresses always have two colons */ + c = strrchr(s,':'); /* skip to last colon */ + if (c != NULL) s = c+1; + while (isdigit(*s)) s++; /* skip over dpy number */ + while (*s == '.') s++; /* skip over dot */ + if (s[-1] != '.') *s++ = '.'; /* put on a dot */ + sprintf(s, "%d", screen_number (screen)); /* put on screen number */ - if (!selected && (p->verbose_p || si->demoing_p)) - fprintf (stderr, - (si->demoing_p - ? "%s: warning, no \"%s\" visual for \"%s\".\n" - : "%s: no \"%s\" visual; skipping \"%s\".\n"), - blurb(), - (hack->visual && *hack->visual ? hack->visual : "???"), - hack->command); + sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX", (unsigned long) saver_window); - return selected; + if (putenv (ndpy)) + abort (); + if (putenv (nssw)) + abort (); + + /* don't free ndpy/nssw -- some implementations of putenv (BSD 4.4, + glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) + do not. So we must leak it (and/or the previous setting). Yay. + */ } +#ifdef ABORT_TESTER /* Shoot down processes after a bit, for debugging */ static void -print_path_error (const char *program) +abort_debug_timer (XtPointer closure, XtIntervalId *id) { - char buf [512]; - char *cmd = strdup (program); - char *token = strchr (cmd, ' '); - - if (token) *token = 0; - sprintf (buf, "%s: could not execute \"%.100s\"", blurb(), cmd); - free (cmd); - perror (buf); - - if (errno == ENOENT && - (token = getenv("PATH"))) + saver_screen_info *ssi = (saver_screen_info *) closure; + if (ssi->pid) { -# ifndef PATH_MAX -# ifdef MAXPATHLEN -# define PATH_MAX MAXPATHLEN -# else -# define PATH_MAX 2048 -# endif -# endif - char path[PATH_MAX]; - fprintf (stderr, "\n"); - *path = 0; -# if defined(HAVE_GETCWD) - if (! getcwd (path, sizeof(path))) - *path = 0; -# elif defined(HAVE_GETWD) - getwd (path); -# endif - if (*path) - fprintf (stderr, " Current directory is: %s\n", path); - fprintf (stderr, " PATH is:\n"); - token = strtok (strdup(token), ":"); - while (token) - { - fprintf (stderr, " %s\n", token); - token = strtok(0, ":"); - } - fprintf (stderr, "\n"); + fprintf (stderr, "%s: %d: %ld: born to ill\n", blurb(), ssi->number, + (unsigned long) ssi->pid); + kill (ssi->pid, SIGILL); } } +#endif /* ABORT_TESTER */ /* Executes the command in another process. @@ -875,15 +743,10 @@ print_path_error (const char *program) Otherwise, -1 is returned and an error may have been printed to stderr. */ -pid_t +static pid_t fork_and_exec (saver_screen_info *ssi, const char *command) { - return fork_and_exec_1 (ssi->global, ssi, command); -} - -pid_t -fork_and_exec_1 (saver_info *si, saver_screen_info *ssi, const char *command) -{ + saver_info *si = ssi->global; saver_preferences *p = &si->prefs; pid_t forked; @@ -899,92 +762,57 @@ fork_and_exec_1 (saver_info *si, saver_screen_info *ssi, const char *command) case 0: close (ConnectionNumber (si->dpy)); /* close display fd */ - limit_subproc_memory (p->inferior_memory_limit, p->verbose_p); if (ssi) hack_subproc_environment (ssi->screen, ssi->screensaver_window); - if (p->verbose_p) - fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n", - blurb(), (ssi ? ssi->number : 0), command, - (unsigned long) getpid ()); - exec_command (p->shell, command, p->nice_inferior); - - /* If that returned, we were unable to exec the subprocess. - Print an error message, if desired. - */ - if (! p->ignore_uninstalled_p) - print_path_error (command); - + /* If that returned, we were unable to exec the subprocess. */ exit (1); /* exits child fork */ break; default: /* parent */ - (void) make_job (forked, (ssi ? ssi->number : 0), command); + make_job (forked, (ssi ? ssi->number : 0), command); + if (p->verbose_p) + fprintf (stderr, "%s: %d: forked \"%s\" in pid %lu" + " on window 0x%lx\n", + blurb(), (ssi ? ssi->number : 0), command, + (unsigned long) forked, + (unsigned long) ssi->screensaver_window); break; } +# ifdef ABORT_TESTER + if (forked) + XtAppAddTimeOut (si->app, 1000 * (5 + (5 * ssi->number)), + abort_debug_timer, (XtPointer) ssi); +# endif + return forked; } -/* Execute command in another process and wait for it to - * finish. Return exit code of process, or -1 on error - * with fork() or exec(). - */ -int -exec_and_wait (saver_info *si, const char *command) +static Bool +select_visual_of_hack (saver_screen_info *ssi, screenhack *hack) { - pid_t forked; + saver_info *si = ssi->global; saver_preferences *p = &si->prefs; + Bool selected; - switch ((int) (forked = fork ())) - { - case -1: - { - char buf [255]; - sprintf (buf, "%s: couldn't fork", blurb()); - perror (buf); - return -1; - } - - case 0: - close (ConnectionNumber (si->dpy)); /* close display fd */ - limit_subproc_memory (p->inferior_memory_limit, p->verbose_p); - - if (p->verbose_p) - fprintf (stderr, "%s: spawning \"%s\" in pid %lu.\n", - blurb(), command, - (unsigned long) getpid ()); - - exec_command (p->shell, command, 0); - - /* If that returned, we were unable to exec the subprocess. - Print an error message, if desired. - */ - print_path_error (command); + if (hack->visual && *hack->visual) + selected = select_visual(ssi, hack->visual); + else + selected = select_visual(ssi, 0); - exit (-1); /* exits child fork */ - break; + if (!selected && (p->verbose_p || si->demoing_p)) + fprintf (stderr, + (si->demoing_p + ? "%s: warning, no \"%s\" visual for \"%s\"\n" + : "%s: no \"%s\" visual; skipping \"%s\"\n"), + blurb(), + (hack->visual && *hack->visual ? hack->visual : "???"), + hack->command); - default: /* parent */ - { - pid_t retpid; - int wstatus; - while ((retpid = waitpid (forked, &wstatus, 0)) == -1) { - if (errno == EINTR) - continue; - perror ("Could not waitpid for child."); - return -1; - } - if (WIFEXITED(wstatus)) - return WEXITSTATUS(wstatus); - if (WIFSIGNALED(wstatus)) - return WTERMSIG(wstatus) + 128; - return -1; - } - } - return -1; + return selected; } @@ -995,13 +823,21 @@ spawn_screenhack (saver_screen_info *ssi) saver_preferences *p = &si->prefs; XFlush (si->dpy); - if (!p->dpms_full_throttle_p && !monitor_powered_on_p (si)) + if (!monitor_powered_on_p (si->dpy)) { if (si->prefs.verbose_p) fprintf (stderr, "%s: %d: X says monitor has powered down; " - "not launching a hack.\n", blurb(), ssi->number); - return; + "not launching a hack\n", blurb(), ssi->number); + ssi->current_hack = -1; + + /* Hooray, this doesn't actually clear the window if it was OpenGL. */ + XClearWindow (si->dpy, ssi->screensaver_window); + + /* Even though we aren't launching a hack, do launch the cycle timer, + in case the monitor powers back up at some point without us having + un-blanked. */ + goto DONE; } if (p->screenhacks_count) @@ -1075,9 +911,7 @@ spawn_screenhack (saver_screen_info *ssi) if (new_hack < 0) /* don't run a hack */ { ssi->current_hack = -1; - if (si->selection_mode < 0) - si->selection_mode = 0; - return; + goto DONE; } ssi->current_hack = new_hack; @@ -1105,7 +939,7 @@ spawn_screenhack (saver_screen_info *ssi) */ if (p->verbose_p) fprintf(stderr, - "%s: %d: no programs enabled, or no suitable visuals.\n", + "%s: %d: no programs enabled, or no suitable visuals\n", blurb(), ssi->number); return; } @@ -1113,12 +947,6 @@ spawn_screenhack (saver_screen_info *ssi) goto AGAIN; } - /* Turn off "next" and "prev" modes now, but "demo" mode is only - turned off by explicit action. - */ - if (si->selection_mode < 0) - si->selection_mode = 0; - forked = fork_and_exec (ssi, hack->command); switch ((int) forked) { @@ -1126,17 +954,95 @@ spawn_screenhack (saver_screen_info *ssi) case 0: /* child fork (can't happen) */ sprintf (buf, "%s: couldn't fork", blurb()); perror (buf); - restore_real_vroot (si); - saver_exit (si, 1, "couldn't fork"); + exit (1); break; default: ssi->pid = forked; break; } + + XChangeProperty (si->dpy, ssi->screensaver_window, XA_WM_COMMAND, + XA_STRING, 8, PropModeReplace, + (unsigned char *) hack->command, + strlen (hack->command)); + XChangeProperty (si->dpy, ssi->screensaver_window, XA_NET_WM_PID, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &ssi->pid, 1); } - store_saver_status (si); /* store current hack number */ + DONE: + + if (ssi->current_hack < 0) + XDeleteProperty (si->dpy, ssi->screensaver_window, XA_WM_COMMAND); + + store_saver_status (si); /* store current hack numbers */ + + /* Now that the hack has launched, queue a timer to cycle it. */ + if (!si->demoing_p && p->cycle) + { + time_t now = time ((time_t *) 0); + Time how_long = p->cycle; + + /* If we're in "SELECT n" mode, the cycle timer going off will just + restart this same hack again. There's not much point in doing this + every 5 or 10 minutes, but on the other hand, leaving one hack + running for days is probably not a great idea, since they tend to + leak and/or crash. So, restart the thing once an hour. + */ + if (si->selection_mode > 0 && ssi->pid) + how_long = 1000 * 60 * 60; + + /* If there are multiple screens, stagger the restart time of subsequent + screens: they will all change every N minutes, but not at the same + time. But don't let that offset be more than about 5 minutes. + + I originally did this by just adding an offset to the very first + cycle only, but after a few days, the cycles would synchronize again! + Are Xt timers implemented with Huygens pendulums?? So compare this + screen's target time against the previous screen's, and offset it as + needed. + */ + if (ssi->number > 0 && + p->mode != RANDOM_HACKS_SAME) + { + saver_screen_info *prev = &si->screens[ssi->number-1]; + time_t cycle_at = now + how_long / 1000; + time_t prev_at = prev->cycle_at; + + Time max = 1000 * 60 * 60 * 10; + Time off = (how_long > max ? max : how_long) / si->nscreens; + + if (cycle_at < prev_at + off / 1000) + { + time_t old = cycle_at; + cycle_at = prev_at + off / 1000; + how_long = 1000 * (cycle_at - now); + + if (p->verbose_p && cycle_at - old > 2) + fprintf (stderr, "%s: %d: offsetting cycle time by %ld sec\n", + blurb(), ssi->number, + cycle_at - old); + } + } + + if (p->debug_p) + fprintf (stderr, "%s: %d: starting cycle_timer (%ld)\n", + blurb(), ssi->number, how_long); + + if (ssi->cycle_id) + XtRemoveTimeOut (ssi->cycle_id); + ssi->cycle_id = + XtAppAddTimeOut (si->app, how_long, cycle_timer, (XtPointer) ssi); + ssi->cycle_at = now + how_long / 1000; + + if (p->verbose_p) + { + time_t t = time((time_t *) 0) + how_long/1000; + fprintf (stderr, "%s: %d: next cycle in %lu sec at %s\n", + blurb(), ssi->number, how_long/1000, timestring(t)); + } + } } @@ -1147,42 +1053,14 @@ kill_screenhack (saver_screen_info *ssi) if (ssi->pid) kill_job (si, ssi->pid, SIGTERM); ssi->pid = 0; -} - -void -suspend_screenhack (saver_screen_info *ssi, Bool suspend_p) -{ -#ifdef SIGSTOP /* older VMS doesn't have it... */ - saver_info *si = ssi->global; - if (ssi->pid) - kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT)); -#endif /* SIGSTOP */ + /* Hooray, this doesn't actually clear the window if it was OpenGL. */ + XClearWindow (si->dpy, ssi->screensaver_window); } -/* Called when we're exiting abnormally, to kill off the subproc. */ -void -emergency_kill_subproc (saver_info *si) -{ - int i; -#ifdef SIGCHLD - signal (SIGCHLD, SIG_IGN); -#endif /* SIGCHLD */ - - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (ssi->pid) - { - kill_job (si, ssi->pid, SIGTERM); - ssi->pid = 0; - } - } -} - Bool -screenhack_running_p (saver_info *si) +any_screenhacks_running_p (saver_info *si) { Bool any_running_p = False; int i; @@ -1190,104 +1068,21 @@ screenhack_running_p (saver_info *si) { saver_screen_info *ssi = &si->screens[i]; if (ssi->pid) any_running_p = True; + /* Consider it running if an error dialog is posted, so that we + don't prematurely clear the window. */ + if (ssi->error_dialog) any_running_p = True; } return any_running_p; } - -/* Environment variables. */ - -/* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX - is defined, the xscreensaver daemon will search that directory for hacks. +/* Fork "xscreensaver-gl-visual" and wait for it to print the IDs of + the GL visual that should be used on this screen. */ -void -hack_environment (saver_info *si) -{ -#if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX) - static const char *def_path = DEFAULT_PATH_PREFIX; - if (def_path && *def_path) - { - const char *opath = getenv("PATH"); - char *npath; - if (! opath) opath = "/bin:/usr/bin"; /* WTF */ - npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20); - strcpy (npath, "PATH="); - strcat (npath, def_path); - strcat (npath, ":"); - strcat (npath, opath); - - if (putenv (npath)) - abort (); - - /* don't free (npath) -- some implementations of putenv (BSD 4.4, - glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) - do not. So we must leak it (and/or the previous setting). Yay. - */ - } -#endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */ -} - - -void -hack_subproc_environment (Screen *screen, Window saver_window) -{ - /* Store $DISPLAY into the environment, so that the $DISPLAY variable that - the spawned processes inherit is correct. First, it must be on the same - host and display as the value of -display passed in on our command line - (which is not necessarily the same as what our $DISPLAY variable is.) - Second, the screen number in the $DISPLAY passed to the subprocess should - be the screen on which this particular hack is running -- not the display - specification which the driver itself is using, since the driver ignores - its screen number and manages all existing screens. - - Likewise, store a window ID in $XSCREENSAVER_WINDOW -- this will allow - us to (eventually) run multiple hacks in Xinerama mode, where each hack - has the same $DISPLAY but a different piece of glass. - */ - Display *dpy = DisplayOfScreen (screen); - const char *odpy = DisplayString (dpy); - char *ndpy = (char *) malloc (strlen(odpy) + 20); - char *nssw = (char *) malloc (40); - char *s, *c; - - strcpy (ndpy, "DISPLAY="); - s = ndpy + strlen(ndpy); - strcpy (s, odpy); - - /* We have to find the last colon since it is the boundary between - hostname & screen - IPv6 numeric format addresses may have many - colons before that point, and DECnet addresses always have two colons */ - c = strrchr(s,':'); /* skip to last colon */ - if (c != NULL) s = c+1; - while (isdigit(*s)) s++; /* skip over dpy number */ - while (*s == '.') s++; /* skip over dot */ - if (s[-1] != '.') *s++ = '.'; /* put on a dot */ - sprintf(s, "%d", screen_number (screen)); /* put on screen number */ - - sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX", (unsigned long) saver_window); - - /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems - any more, right? It's not Posix, but everyone seems to have it. */ -#ifdef HAVE_PUTENV - if (putenv (ndpy)) - abort (); - if (putenv (nssw)) - abort (); - - /* don't free ndpy/nssw -- some implementations of putenv (BSD 4.4, - glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) - do not. So we must leak it (and/or the previous setting). Yay. - */ -#endif /* HAVE_PUTENV */ -} - - -/* GL crap */ - Visual * get_best_gl_visual (saver_info *si, Screen *screen) { + saver_preferences *p = &si->prefs; pid_t forked; int fds [2]; int in, out; @@ -1298,7 +1093,7 @@ get_best_gl_visual (saver_info *si, Screen *screen) char *av[10]; int ac = 0; - av[ac++] = "xscreensaver-gl-helper"; + av[ac++] = "xscreensaver-gl-visual"; av[ac] = 0; if (pipe (fds)) @@ -1322,18 +1117,13 @@ get_best_gl_visual (saver_info *si, Screen *screen) errout = errfds [1]; } - block_sigchld(); /* This blocks it in the parent and child, to avoid - racing. It is never unblocked in the child before - the child exits, but that doesn't matter. - */ - switch ((int) (forked = fork ())) { case -1: { sprintf (buf, "%s: couldn't fork", blurb()); perror (buf); - saver_exit (si, 1, 0); + exit (1); } case 0: { @@ -1375,11 +1165,17 @@ get_best_gl_visual (saver_info *si, Screen *screen) int result = 0; int wait_status = 0; pid_t pid = -1; - - FILE *f = fdopen (in, "r"); + FILE *f; unsigned long v = 0; char c; + make_job (forked, 0, av[0]); /* Bookkeeping for SIGCHLD */ + + if (p->verbose_p) + fprintf (stderr, "%s: %d: forked \"%s\" in pid %lu\n", + blurb(), 0, av[0], (unsigned long) forked); + + f = fdopen (in, "r"); close (out); /* don't need this one */ *buf = 0; @@ -1396,16 +1192,8 @@ get_best_gl_visual (saver_info *si, Screen *screen) /* Wait for the child to die - wait for this pid only, not others. */ pid = waitpid (forked, &wait_status, 0); if (si->prefs.debug_p) - { - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": waitpid("); - write_long (STDERR_FILENO, (long) forked); - write_string (STDERR_FILENO, ") ==> "); - write_long (STDERR_FILENO, (long) pid); - write_string (STDERR_FILENO, "\n"); - } - - unblock_sigchld(); /* child is dead and waited, unblock now. */ + fprintf (stderr, "%s: waitpid(%ld) => %ld\n", blurb(), + (long) forked, (long) pid); if (1 == sscanf (buf, "0x%lx %c", &v, &c)) result = (int) v; @@ -1430,7 +1218,7 @@ get_best_gl_visual (saver_info *si, Screen *screen) { Visual *v = id_to_visual (screen, result); if (si->prefs.verbose_p) - fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s.\n", + fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s\n", blurb(), screen_number (screen), av[0], result, (v == DefaultVisualOfScreen (screen) @@ -1442,54 +1230,3 @@ get_best_gl_visual (saver_info *si, Screen *screen) abort(); } - - - -/* Restarting the xscreensaver process from scratch. */ - -static char **saved_argv; - -void -save_argv (int argc, char **argv) -{ - saved_argv = (char **) calloc (argc+2, sizeof (char *)); - saved_argv [argc] = 0; - while (argc--) - { - int i = strlen (argv [argc]) + 1; - saved_argv [argc] = (char *) malloc (i); - memcpy (saved_argv [argc], argv [argc], i); - } -} - - -/* Re-execs the process with the arguments in saved_argv. Does not return. - */ -void -restart_process (saver_info *si) -{ - fflush (stdout); - fflush (stderr); - shutdown_stderr (si); - if (si->prefs.verbose_p) - { - int i; - fprintf (stderr, "%s: re-executing", blurb()); - for (i = 0; saved_argv[i]; i++) - fprintf (stderr, " %s", saved_argv[i]); - fprintf (stderr, "\n"); - } - describe_uids (si, stderr); - fprintf (stderr, "\n"); - - fflush (stdout); - fflush (stderr); - execvp (saved_argv [0], saved_argv); /* shouldn't return */ - { - char buf [512]; - sprintf (buf, "%s: could not restart process", blurb()); - perror(buf); - fflush(stderr); - abort(); - } -} diff --git a/driver/test-apm.c b/driver/test-apm.c deleted file mode 100644 index 6b87c7e..0000000 --- a/driver/test-apm.c +++ /dev/null @@ -1,101 +0,0 @@ -/* test-apm.c --- playing with the APM library. - * xscreensaver, Copyright (c) 1999 Jamie Zawinski - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#ifdef HAVE_UNISTD_H -# include -#endif - -#include -#include -#include - -#include -#include - -#include - -#define countof(x) (sizeof((x))/sizeof(*(x))) - - -char *progname = 0; -char *progclass = "XScreenSaver"; - -static const char * -blurb (void) -{ - static char buf[255]; - time_t now = time ((time_t *) 0); - char *ct = (char *) ctime (&now); - int n = strlen(progname); - if (n > 100) n = 99; - strncpy(buf, progname, n); - buf[n++] = ':'; - buf[n++] = ' '; - strncpy(buf+n, ct+11, 8); - strcpy(buf+n+9, ": "); - return buf; -} - -static void -apm_cb (XtPointer closure, int *fd, XtInputId *id) -{ - apm_event_t events[100]; - int n, i; - while ((n = apm_get_events (*fd, 0, events, countof(events))) - > 0) - for (i = 0; i < n; i++) - { - fprintf (stderr, "%s: APM event 0x%x: %s.\n", blurb(), - events[i], apm_event_name (events[i])); -#if 0 - switch (events[i]) - { - case APM_SYS_STANDBY: - case APM_USER_STANDBY: - case APM_SYS_SUSPEND: - case APM_USER_SUSPEND: - case APM_CRITICAL_SUSPEND: - break; - } -#endif - } -} - -int -main (int argc, char **argv) -{ - XtAppContext app; - Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, - &argc, argv, 0, 0, 0); - Display *dpy = XtDisplay (toplevel_shell); - int fd; - XtInputId id; - XtGetApplicationNameAndClass (dpy, &progname, &progclass); - - fd = apm_open (); - if (fd <= 0) - { - fprintf (stderr, "%s: couldn't initialize APM.\n", blurb()); - exit (1); - } - - id = XtAppAddInput(app, fd, - (XtPointer) (XtInputReadMask | XtInputWriteMask), - apm_cb, 0); - XtAppMainLoop (app); - exit (0); -} diff --git a/driver/test-fade.c b/driver/test-fade.c index 9db773d..31852ef 100644 --- a/driver/test-fade.c +++ b/driver/test-fade.c @@ -1,5 +1,5 @@ /* test-fade.c --- playing with colormap and/or gamma fading. - * xscreensaver, Copyright (c) 2001, 2004 Jamie Zawinski + * xscreensaver, Copyright © 2001-2021 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 @@ -21,30 +21,35 @@ #include +#include #include + #include "xscreensaver.h" +#include "resources.h" +#include "screens.h" #include "fade.h" +#include "atoms.h" -#ifdef HAVE_SGI_VC_EXTENSION -# include -#endif #ifdef HAVE_XF86VMODE_GAMMA # include #endif +#ifdef HAVE_RANDR +# include +#endif XrmDatabase db = 0; -char *progname = 0; char *progclass = "XScreenSaver"; +Bool debug_p = True; -#define SGI_VC_NAME "SGI-VIDEO-CONTROL" #define XF86_VIDMODE_NAME "XFree86-VidModeExtension" +#define RANDR_NAME "RANDR" int main (int argc, char **argv) { - int seconds = 3; - int ticks = 20; - int delay = 1; + double seconds = 3; + double ratio = 1/3.0; + int delay = 2; int op, event, error, major, minor; @@ -52,70 +57,151 @@ main (int argc, char **argv) Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); - Colormap *current_maps; - int i; - - XtGetApplicationNameAndClass (dpy, &progname, &progclass); + Screen *screen = ScreenOfDisplay (dpy, 0); + int nwindows, i; + Window windows[100]; + + int x, y; + unsigned int bw, d; + Window root = RootWindow (dpy, 0); + Visual *visual = DefaultVisual (dpy, 0); + Pixmap logo, logo_clipmask; + int logo_npixels; + unsigned long *logo_pixels; + unsigned int logo_width, logo_height; + XSetWindowAttributes attrs; + unsigned long attrmask = 0; + void *state = 0; + + verbose_p += 2; + + progname = argv[0]; + progclass = "XScreenSaver"; db = XtDatabase (dpy); - current_maps = (Colormap *) calloc(sizeof(Colormap), ScreenCount(dpy)); - for (i = 0; i < ScreenCount(dpy); i++) - current_maps[i] = DefaultColormap (dpy, i); + init_xscreensaver_atoms (dpy); + + { + const char * version_number = "test-fade"; + Window daemon_window = + XCreateWindow (dpy, RootWindow (dpy, 0), + 0, 0, 1, 1, 0, + DefaultDepth (dpy, 0), InputOutput, + DefaultVisual (dpy, 0), attrmask, &attrs); + XChangeProperty (dpy, daemon_window, XA_SCREENSAVER_VERSION, XA_STRING, + 8, PropModeReplace, (unsigned char *) version_number, + strlen (version_number)); + } - if (!XQueryExtension (dpy, SGI_VC_NAME, &op, &event, &error)) - fprintf(stderr, "%s: no " SGI_VC_NAME " extension\n", progname); - else - { -# ifdef HAVE_SGI_VC_EXTENSION - if (!XSGIvcQueryVersion (dpy, &major, &minor)) - fprintf(stderr, "%s: unable to get " SGI_VC_NAME " version\n", - progname); - else - fprintf(stderr, "%s: " SGI_VC_NAME " version %d.%d\n", - progname, major, minor); -# else /* !HAVE_SGI_VC_EXTENSION */ - fprintf(stderr, "%s: no support for display's " SGI_VC_NAME - " extension\n", progname); -# endif /* !HAVE_SGI_VC_EXTENSION */ - } if (!XQueryExtension (dpy, XF86_VIDMODE_NAME, &op, &event, &error)) - fprintf(stderr, "%s: no " XF86_VIDMODE_NAME " extension\n", progname); + fprintf(stderr, "%s: no " XF86_VIDMODE_NAME " extension\n", blurb()); else { # ifdef HAVE_XF86VMODE_GAMMA if (!XF86VidModeQueryVersion (dpy, &major, &minor)) fprintf(stderr, "%s: unable to get " XF86_VIDMODE_NAME " version\n", - progname); + blurb()); else fprintf(stderr, "%s: " XF86_VIDMODE_NAME " version %d.%d\n", - progname, major, minor); + blurb(), major, minor); # else /* !HAVE_XF86VMODE_GAMMA */ fprintf(stderr, "%s: no support for display's " XF86_VIDMODE_NAME - " extension\n", progname); + " extension\n", blurb()); # endif /* !HAVE_XF86VMODE_GAMMA */ } + if (!XQueryExtension (dpy, RANDR_NAME, &op, &event, &error)) + fprintf(stderr, "%s: no " RANDR_NAME " extension\n", blurb()); + else + { +# ifdef HAVE_RANDR + if (!XRRQueryVersion (dpy, &major, &minor)) + fprintf(stderr, "%s: unable to get " RANDR_NAME " version\n", + blurb()); + else + fprintf(stderr, "%s: " RANDR_NAME " version %d.%d\n", + blurb(), major, minor); +# else /* !HAVE_RANDR */ + fprintf(stderr, "%s: no support for display's " RANDR_NAME + " extension\n", blurb()); +# endif /* !HAVE_RANDR */ + } + + logo = xscreensaver_logo (screen, visual, root, DefaultColormap (dpy, 0), + WhitePixel (dpy, 0), + &logo_pixels, &logo_npixels, + &logo_clipmask, True); + XGetGeometry (dpy, logo, &root, &x, &y, &logo_width, &logo_height, &bw, &d); + + nwindows = 0; + { + int x, y; + for (y = 0; y < 2; y++) + for (x = 0; x < 2; x++) + { + int win_width = 250; + int win_height = 200; + + attrmask = CWOverrideRedirect; + attrs.override_redirect = True; + windows[nwindows] = + XCreateWindow (dpy, root, + 200 + x * win_width * 1.5, + 200 + y * win_height * 1.5, + win_width, win_height, 0, DefaultDepth (dpy, 0), + InputOutput, visual, + attrmask, &attrs); + XSetWindowBackground (dpy, windows[nwindows], BlackPixel (dpy, 0)); + XClearWindow (dpy, windows[nwindows]); + nwindows++; + } + } + fprintf (stderr, "%s: fading %d screen%s\n", - progname, ScreenCount(dpy), ScreenCount(dpy) == 1 ? "" : "s"); + blurb(), ScreenCount(dpy), ScreenCount(dpy) == 1 ? "" : "s"); while (1) { XSync (dpy, False); - fprintf(stderr, "%s: out...", progname); + fprintf(stderr, "%s: fading out\n\n", blurb()); fflush(stderr); - fade_screens (dpy, current_maps, 0, 0, seconds, ticks, True, False); - fprintf(stderr, "done.\n"); + fade_screens (app, dpy, windows, nwindows, seconds, + True, /* out_p */ + True, /* from_desktop_p */ + &state); + for (i = 0; i < nwindows; i++) + XMapRaised (dpy, windows[i]); + XSync (dpy, False); + fprintf(stderr, "%s: out done\n\n", blurb()); fflush(stderr); + for (i = 0; i < nwindows; i++) + { + XSetWindowBackgroundPixmap (dpy, windows[i], logo); + XClearWindow (dpy, windows[i]); + XSetWindowBackground (dpy, windows[i], BlackPixel (dpy, 0)); + } + XSync (dpy, False); + if (delay) sleep (delay); - fprintf(stderr,"%s: in...", progname); + fprintf(stderr, "%s: fading in\n\n", blurb()); fflush(stderr); - fade_screens (dpy, current_maps, 0, 0, seconds, ticks, False, False); - fprintf(stderr, "done.\n"); + fade_screens (app, dpy, windows, nwindows, + seconds * ratio, + True, /* out_p */ + False, /* from_desktop_p */ + &state); + fade_screens (app, dpy, windows, nwindows, + seconds * ratio, + False, /* out_p */ + False, /* from_desktop_p */ + &state); + XSync (dpy, False); + fprintf(stderr, "%s: in done\n\n", blurb()); fflush(stderr); if (delay) sleep (delay); diff --git a/driver/test-grab.c b/driver/test-grab.c index 03018eb..65313a1 100644 --- a/driver/test-grab.c +++ b/driver/test-grab.c @@ -1,5 +1,5 @@ /* test-uid.c --- playing with grabs. - * xscreensaver, Copyright (c) 1999, 2004 Jamie Zawinski + * xscreensaver, Copyright © 1999-2021 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 @@ -24,8 +24,9 @@ #include #include +#include -char *progname = 0; +#include "blurb.h" char *progclass = "XScreenSaver"; #define ALL_POINTER_EVENTS \ @@ -38,38 +39,89 @@ int main (int argc, char **argv) { XtAppContext app; - int kstatus, mstatus; - Cursor cursor = 0; - int delay = 60 * 15; + int kstatus = AlreadyGrabbed, mstatus = AlreadyGrabbed; + Cursor cursor1, cursor2; + int delay1 = 15; + int delay2 = 60 * 15 - delay1; Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); Window w = RootWindow (dpy, DefaultScreen(dpy)); - XtGetApplicationNameAndClass (dpy, &progname, &progclass); - - kstatus = XGrabKeyboard (dpy, w, True, - GrabModeSync, GrabModeAsync, - CurrentTime); - fprintf (stderr, "%s: grabbing keyboard on 0x%lx... %s.\n", - progname, (unsigned long) w, - (kstatus == GrabSuccess ? "GrabSuccess" : - kstatus == AlreadyGrabbed ? "AlreadyGrabbed" : - kstatus == GrabInvalidTime ? "GrabInvalidTime" : - kstatus == GrabNotViewable ? "GrabNotViewable" : - kstatus == GrabFrozen ? "GrabFrozen" : - "???")); - - mstatus = XGrabPointer (dpy, w, True, ALL_POINTER_EVENTS, - GrabModeAsync, GrabModeAsync, None, - cursor, CurrentTime); - fprintf (stderr, "%s: grabbing mouse on 0x%lx... %s.\n", - progname, (unsigned long) w, - (mstatus == GrabSuccess ? "GrabSuccess" : - mstatus == AlreadyGrabbed ? "AlreadyGrabbed" : - mstatus == GrabInvalidTime ? "GrabInvalidTime" : - mstatus == GrabNotViewable ? "GrabNotViewable" : - mstatus == GrabFrozen ? "GrabFrozen" : - "???")); + int i; + + Bool grab_kbd_p = True; + Bool grab_mouse_p = True; + Bool mouse_sync_p = True; + Bool kbd_sync_p = True; + + progname = argv[0]; + + cursor1 = XCreateFontCursor (dpy, XC_hand1); + cursor2 = XCreateFontCursor (dpy, XC_gumby); + + for (i = 1; i < argc; i++) + { + const char *oa = argv[i]; + if (argv[i][0] == '-' && argv[i][1] == '-') + argv[i]++; + if (!strcmp (argv[i], "-kbd") || !strcmp (argv[i], "-keyboard")) + grab_mouse_p = False; + else if (!strcmp (argv[i], "-mouse") || !strcmp (argv[i], "-pointer")) + grab_kbd_p = False; + else if (!strcmp (argv[i], "-kbd-sync") || + !strcmp (argv[i], "-keyboard-sync")) + kbd_sync_p = True; + else if (!strcmp (argv[i], "-kbd-async") || + !strcmp (argv[i], "-keyboard-async")) + kbd_sync_p = False; + else if (!strcmp (argv[i], "-mouse-sync") || + !strcmp (argv[i], "-pointer-sync")) + mouse_sync_p = True; + else if (!strcmp (argv[i], "-mouse-async") || + !strcmp (argv[i], "-pointer-async")) + mouse_sync_p = False; + else + { + fprintf (stderr, "%s: unknown option: %s\n", blurb(), oa); + exit (1); + } + } + + if (grab_kbd_p) + { + kstatus = XGrabKeyboard (dpy, w, True, + (mouse_sync_p ? GrabModeSync : GrabModeAsync), + (kbd_sync_p ? GrabModeSync : GrabModeAsync), + CurrentTime); + fprintf (stderr, "%s: grabbing keyboard on 0x%lx (%s, %s)... %s\n", + progname, (unsigned long) w, + (mouse_sync_p ? "sync" : "async"), + (kbd_sync_p ? "sync" : "async"), + (kstatus == GrabSuccess ? "GrabSuccess" : + kstatus == AlreadyGrabbed ? "AlreadyGrabbed" : + kstatus == GrabInvalidTime ? "GrabInvalidTime" : + kstatus == GrabNotViewable ? "GrabNotViewable" : + kstatus == GrabFrozen ? "GrabFrozen" : + "???")); + } + + if (grab_mouse_p) + { + mstatus = XGrabPointer (dpy, w, True, ALL_POINTER_EVENTS, + (mouse_sync_p ? GrabModeSync : GrabModeAsync), + (kbd_sync_p ? GrabModeSync : GrabModeAsync), + None, cursor1, CurrentTime); + fprintf (stderr, "%s: grabbing mouse on 0x%lx (%s, %s)... %s\n", + progname, (unsigned long) w, + (mouse_sync_p ? "sync" : "async"), + (kbd_sync_p ? "sync" : "async"), + (mstatus == GrabSuccess ? "GrabSuccess" : + mstatus == AlreadyGrabbed ? "AlreadyGrabbed" : + mstatus == GrabInvalidTime ? "GrabInvalidTime" : + mstatus == GrabNotViewable ? "GrabNotViewable" : + mstatus == GrabFrozen ? "GrabFrozen" : + "???")); + } XSync(dpy, False); @@ -77,12 +129,34 @@ main (int argc, char **argv) { fprintf (stderr, "%s: sleeping for %d:%02d:%02d...\n", progname, - delay / (60 * 60), - (delay % (60 * 60)) / 60, - delay % 60); - fflush(stderr); - sleep (delay); - XSync(dpy, False); + delay1 / (60 * 60), + (delay1 % (60 * 60)) / 60, + delay1 % 60); + sleep (delay1); + fprintf (stderr, "%s: changing mouse cursor...\n", progname); + + mstatus = XGrabPointer (dpy, w, True, ALL_POINTER_EVENTS, + (mouse_sync_p ? GrabModeSync : GrabModeAsync), + (kbd_sync_p ? GrabModeSync : GrabModeAsync), + None, cursor2, CurrentTime); + XSync (dpy, False); + if (mstatus != GrabSuccess) + fprintf (stderr, "%s: failed: %s\n", progname, + (mstatus == GrabSuccess ? "GrabSuccess" : + mstatus == AlreadyGrabbed ? "AlreadyGrabbed" : + mstatus == GrabInvalidTime ? "GrabInvalidTime" : + mstatus == GrabNotViewable ? "GrabNotViewable" : + mstatus == GrabFrozen ? "GrabFrozen" : + "???")); + + fprintf (stderr, "%s: sleeping for %d:%02d:%02d...\n", + progname, + delay2 / (60 * 60), + (delay2 % (60 * 60)) / 60, + delay2 % 60); + fflush (stderr); + sleep (delay2); + XSync (dpy, False); } exit (0); diff --git a/driver/test-mlstring.c b/driver/test-mlstring.c deleted file mode 100644 index e269a00..0000000 --- a/driver/test-mlstring.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * (c) 2007, Quest Software, Inc. All rights reserved. - * - * This file is part of XScreenSaver, - * Copyright (c) 1993-2004 Jamie Zawinski - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ - -#include -#include -#include - -#include "mlstring.c" /* hokey, but whatever */ - -#define WRAP_WIDTH_PX 100 - -#undef Bool -#undef True -#undef False -typedef int Bool; -#define True 1 -#define False 0 - -#define SKIPPED -1 -#define SUCCESS 0 -#define FAILURE 1 - -#define FAIL(msg, ...) \ - do { \ - ++failcount; \ - fprintf(stderr, "[FAIL] "); \ - fprintf(stderr, msg, __VA_ARGS__); \ - putc('\n', stderr); \ - return FAILURE; \ - } while (0) - -#define SUCCEED(testname) \ - do { \ - fprintf(stderr, "[SUCCESS] %s\n", (testname)); \ - } while (0) - -#define SKIP(testname) \ - do { \ - fprintf(stderr, "[SKIPPED] %s\n", (testname)); \ - } while (0) - -extern mlstring* mlstring_allocate(const char *msg); -extern void mlstring_wrap(mlstring *mstr, XFontStruct *font, Dimension width); - -static int failcount = 0; - -static char *mlstring_to_cstr(const mlstring *mlstr) { - char *cstr; - size_t cstrlen = 0, alloclen = 1024; - const struct mlstr_line *line; - - cstr = malloc(alloclen); - if (!cstr) - return NULL; - cstr[0] = '\0'; - - for (line = mlstr->lines; line; line = line->next_line) { - /* Extend the buffer if necessary. */ - if (cstrlen + strlen(line->line) + 1 > alloclen) { - cstr = realloc(cstr, alloclen *= 2); - if (!cstr) - return NULL; - } - - /* If this is not the first line */ - if (line != mlstr->lines) { - /* Append a newline character */ - cstr[cstrlen] = '\n'; - ++cstrlen; - cstr[cstrlen] = '\0'; - } - - strcat(cstr, line->line); - cstrlen += strlen(line->line); - } - return cstr; -} - -/* Pass -1 for expect_min or expect_exact to not check that value. - * expect_empty_p means an empty line is expected at some point in the string. - * Also ensures that the string was not too wide after wrapping. */ -static int mlstring_expect_lines(const mlstring *mlstr, int expect_min, int expect_exact, Bool expect_empty_p) -{ - int count; - Bool got_empty_line = False; - const struct mlstr_line *line = mlstr->lines; - - for (count = 0; line; line = line->next_line) { - if (line->line[0] == '\0') { - if (!expect_empty_p) - FAIL("Not expecting empty lines, but got one on line %d of [%s]", count + 1, mlstring_to_cstr(mlstr)); - got_empty_line = True; - } - ++count; - } - - if (expect_empty_p && !got_empty_line) - FAIL("Expecting an empty line, but none found in [%s]", mlstring_to_cstr(mlstr)); - - if (expect_exact != -1 && expect_exact != count) - FAIL("Expected %d lines, got %d", expect_exact, count); - - if (expect_min != -1 && count < expect_min) - FAIL("Expected at least %d lines, got %d", expect_min, count); - - return SUCCESS; -} - -static int mlstring_expect(const char *msg, int expect_lines, const mlstring *mlstr, Bool expect_empty_p) -{ - char *str, *str_top; - const struct mlstr_line *cur; - int linecount = 0; - - /* Duplicate msg so we can chop it up */ - str_top = strdup(msg); - if (!str_top) - return SKIPPED; - - /* Replace all newlines with NUL */ - str = str_top; - while ((str = strchr(str, '\n'))) - *str++ = '\0'; - - /* str is now used to point to the expected string */ - str = str_top; - - for (cur = mlstr->lines; cur; cur = cur->next_line) - { - ++linecount; - if (strcmp(cur->line, str)) - FAIL("lines didn't match; expected [%s], got [%s]", str, cur->line); - - str += strlen(str) + 1; /* Point to the next expected string */ - } - - free(str_top); - - return mlstring_expect_lines(mlstr, -1, expect_lines, expect_empty_p); -} - -/* Ensures that the width has been set properly after wrapping */ -static int check_width(const char *msg, const mlstring *mlstr) { - if (mlstr->overall_width == 0) - FAIL("Overall width was zero for string [%s]", msg); - - if (mlstr->overall_width > WRAP_WIDTH_PX) - FAIL("Overall width was %hu but the maximum wrap width was %d", mlstr->overall_width, WRAP_WIDTH_PX); - - return SUCCESS; -} - -/* FAIL() actually returns the wrong return codes in main, but it - * prints a message which is what we want. */ - -#define TRY_NEW(str, numl, expect_empty) \ - do { \ - mlstr = mlstring_allocate((str)); \ - if (!mlstr) \ - FAIL("%s", #str); \ - if (SUCCESS == mlstring_expect((str), (numl), mlstr, (expect_empty))) \ - SUCCEED(#str); \ - free(mlstr); \ - } while (0) - -/* Expects an XFontStruct* font, and tries to wrap to 100px */ -#define TRY_WRAP(str, minl, expect_empty) \ - do { \ - mltest = mlstring_allocate((str)); \ - if (!mltest) \ - SKIP(#str); \ - else { \ - mlstring_wrap(mltest, font, WRAP_WIDTH_PX); \ - check_width((str), mltest); \ - if (SUCCESS == mlstring_expect_lines(mltest, (minl), -1, (expect_empty))) \ - SUCCEED(#str); \ - free(mltest); \ - mltest = NULL; \ - } \ - } while (0) - - -/* Ideally this function would use stub functions rather than real Xlib. - * Then it would be possible to test for exact line counts, which would be - * more reliable. - * It also doesn't handle Xlib errors. - * - * Don't print anything based on the return value of this function, it only - * returns a value so that I can use the FAIL() macro without warning. - * - * Anyone who understands this function wins a cookie ;) - */ -static int test_wrapping(void) -{ - Display *dpy = NULL; - XFontStruct *font = NULL; - mlstring *mltest = NULL; - int ok = 0; - int chars_per_line, chars_first_word, i; - - const char *test_short = "a"; - const char *test_hardwrap = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - const char *test_withnewlines = "a\nb"; - char *test_softwrap = NULL; - - dpy = XOpenDisplay(NULL); - if (!dpy) - goto end; - - font = XLoadQueryFont(dpy, "fixed"); - if (!font) - goto end; - - TRY_WRAP(test_short, 1, False); - TRY_WRAP(test_hardwrap, 2, False); - TRY_WRAP(test_withnewlines, 2, False); - - /* See if wrapping splits on word boundaries like it should */ - chars_per_line = WRAP_WIDTH_PX / font->max_bounds.width; - if (chars_per_line < 3) - goto end; - - /* Allocate for 2 lines + \0 */ - test_softwrap = malloc(chars_per_line * 2 + 1); - if (!test_softwrap) - goto end; - - /* 2 = strlen(' a'); that is, the minimum space required to start a new word - * on the same line. */ - chars_first_word = chars_per_line - 2; - - for (i = 0; i < chars_first_word; ++i) { - test_softwrap[i] = 'a'; /* first word */ - test_softwrap[i + chars_per_line] = 'b'; /* second word */ - } - /* space between first & second words */ - test_softwrap[chars_first_word] = ' '; - /* first char of second word (last char of first line) */ - test_softwrap[chars_first_word + 1] = 'b'; - /* after second word */ - test_softwrap[chars_per_line * 2] = '\0'; - - mltest = mlstring_allocate(test_softwrap); - mlstring_wrap(mltest, font, WRAP_WIDTH_PX); - - /* reusing 'i' for a moment here to make freeing mltest easier */ - i = strlen(mltest->lines->line); - free(mltest); - - if (i != chars_first_word) - FAIL("Soft wrap failed, expected the first line to be %d chars, but it was %d.", chars_first_word, i); - SUCCEED("Soft wrap"); - - ok = 1; - -end: - if (test_softwrap) - free(test_softwrap); - - if (font) - XFreeFont(dpy, font); - - if (dpy) - XCloseDisplay(dpy); - - if (!ok) - SKIP("wrapping"); - - return ok ? SUCCESS : SKIPPED; /* Unused, actually */ -} - - -int main(int argc, char *argv[]) -{ - const char *oneline = "1Foo"; - const char *twolines = "2Foo\nBar"; - const char *threelines = "3Foo\nBar\nWhippet"; - const char *trailnewline = "4Foo\n"; - const char *trailnewlines = "5Foo\n\n"; - const char *embeddednewlines = "6Foo\n\nBar"; - mlstring *mlstr; - - TRY_NEW(oneline, 1, False); - TRY_NEW(twolines, 2, False); - TRY_NEW(threelines, 3, False); - TRY_NEW(trailnewline, 2, True); - TRY_NEW(trailnewlines, 3, True); - TRY_NEW(embeddednewlines, 3, True); - - (void) test_wrapping(); - - fprintf(stdout, "%d test failures.\n", failcount); - - return !!failcount; -} - -/* vim:ts=8:sw=2:noet - */ diff --git a/driver/test-passwd.c b/driver/test-passwd.c index 9b4f98e..2c894ac 100644 --- a/driver/test-passwd.c +++ b/driver/test-passwd.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1998-2017 Jamie Zawinski +/* xscreensaver, Copyright © 1998-2021 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 @@ -10,8 +10,6 @@ */ /* This is a kludgy test harness for debugging the password dialog box. - It's somewhat easier to debug it here than in the xscreensaver executable - itself. */ #ifdef HAVE_CONFIG_H @@ -19,288 +17,101 @@ #endif #include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "xscreensaver.h" -#include "resources.h" -#include "version.h" -#include "visual.h" +#include +#include "blurb.h" #include "auth.h" -char *progname = 0; -char *progclass = 0; -XrmDatabase db = 0; -saver_info *global_si_kludge; - -FILE *real_stderr, *real_stdout; - -void monitor_power_on (saver_info *si, Bool on_p) {} -Bool monitor_powered_on_p (saver_info *si) { return True; } -void initialize_screensaver_window (saver_info *si) {} -void raise_window (saver_info *si, Bool i, Bool b, Bool d) {} -Bool blank_screen (saver_info *si) {return False;} -void unblank_screen (saver_info *si) {} -void reset_watchdog_timer(saver_info *si, Bool on_p) {} -Bool select_visual (saver_screen_info *ssi, const char *v) { return False; } -Bool window_exists_p (Display *dpy, Window window) {return True;} -void start_notice_events_timer (saver_info *si, Window w, Bool b) {} -Bool handle_clientmessage (saver_info *si, XEvent *e, Bool u) { return False; } -int BadWindow_ehandler (Display *dpy, XErrorEvent *error) { exit(1); } -const char *signal_name(int signal) { return "???"; } -Bool restore_real_vroot (saver_info *si) { return False; } -void store_saver_status (saver_info *si) {} -void saver_exit (saver_info *si, int status, const char *core) { exit(status);} -int move_mouse_grab (saver_info *si, Window to, Cursor c, int ts) { return 0; } -int mouse_screen (saver_info *si) { return 0; } -void check_for_leaks (const char *where) { } -void shutdown_stderr (saver_info *si) { } -void resize_screensaver_window (saver_info *si) { } -void describe_monitor_layout (saver_info *si) { } -Bool update_screen_layout (saver_info *si) { return 0; } -Bool in_signal_handler_p = 0; -char *timestring (time_t when) { return ""; } - -const char *blurb(void) { return progname; } -Atom XA_SCREENSAVER, XA_DEMO, XA_PREFS; +extern Bool test_auth_conv (void *, int, auth_message *, auth_response **); -void -idle_timer (XtPointer closure, XtIntervalId *id) +Bool +test_auth_conv (void *closure, + int nmsgs, + auth_message *msg, + auth_response **resp) { - saver_info *si = (saver_info *) closure; - XEvent fake_event; - fake_event.type = 0; /* XAnyEvent type, ignored. */ - fake_event.xany.display = si->dpy; - fake_event.xany.window = 0; - XPutBackEvent (si->dpy, &fake_event); -} - -static int -text_auth_conv ( - int num_msg, - const struct auth_message *auth_msgs, - struct auth_response **resp, - saver_info *si) -{ - char *input; - char buf[255]; - struct auth_response *responses; + int page = 1; int i; + nmsgs = 0; + msg = (auth_message *) calloc (100, sizeof(*msg)); - responses = calloc(num_msg, sizeof(struct auth_response)); - if (!responses) - return -1; - - /* The unlock state won't actually be used until this function returns and - * the auth module processes the response, but set it anyway for consistency - */ - si->unlock_state = ul_read; - - for (i = 0; i < num_msg; ++i) - { - printf ("\n%s: %s", progname, auth_msgs[i].msg); - if ( auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO - || auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO) - { - input = fgets (buf, sizeof(buf)-1, stdin); - if (!input || !*input) - exit (0); - if (input[strlen(input)-1] == '\n') - input[strlen(input)-1] = 0; - - responses[i].response = strdup(input); - } - } - - *resp = responses; - - si->unlock_state = ul_finished; - - return 0; -} - - -#ifdef __GNUC__ - __extension__ /* shut up about "string length is greater than the length - ISO C89 compilers are required to support" when including - the .ad file... */ -#endif - -static char *fallback[] = { -#include "XScreenSaver_ad.h" - 0 -}; +# define DIALOG() \ + fprintf (stderr, "\n%s: page %d\n", blurb(), page++); \ + xscreensaver_auth_conv (closure, nmsgs, msg, resp); \ + if (*resp) \ + for (i = 0; i < nmsgs; i++) \ + fprintf (stderr, "%s: resp %d = \"%s\"\n", blurb(), i, \ + ((*resp)[i].response ? (*resp)[i].response : "")); \ + nmsgs = 0 -extern Bool debug_passwd_window_p; /* lock.c kludge */ -int -main (int argc, char **argv) -{ - enum { PASS, SPLASH, TTY } which; - Widget toplevel_shell = 0; - saver_screen_info ssip; - saver_info sip; - saver_info *si = &sip; - saver_preferences *p = &si->prefs; - struct passwd *pw; + msg[nmsgs].type = AUTH_MSGTYPE_INFO; + msg[nmsgs].msg = "1/4 Page One"; + nmsgs++; - memset(&sip, 0, sizeof(sip)); - memset(&ssip, 0, sizeof(ssip)); + msg[nmsgs].type = AUTH_MSGTYPE_INFO; + msg[nmsgs].msg = "2/4 All work and no play makes Jack a dull boy. " + "All work and no play makes Jack a dull boy. "; + nmsgs++; - si->nscreens = 1; - si->screens = si->default_screen = &ssip; - ssip.global = si; + msg[nmsgs].type = AUTH_MSGTYPE_ERROR; + msg[nmsgs].msg = "3/4 Red"; + nmsgs++; - global_si_kludge = si; - real_stderr = stderr; - real_stdout = stdout; + msg[nmsgs].type = AUTH_MSGTYPE_INFO; + msg[nmsgs].msg = "4/4 Greets to Crash Override."; + nmsgs++; + DIALOG(); - si->version = (char *) malloc (5); - memcpy (si->version, screensaver_id + 17, 4); - si->version[4] = 0; - progname = argv[0]; - { - char *s = strrchr(progname, '/'); - if (*s) progname = s+1; - } - if (argc != 2) goto USAGE; - else if (!strcmp (argv[1], "pass")) which = PASS; - else if (!strcmp (argv[1], "splash")) which = SPLASH; - else if (!strcmp (argv[1], "tty")) which = TTY; - else - { - USAGE: - fprintf (stderr, "usage: %s [ pass | splash | tty ]\n", progname); - exit (1); - } - -#ifdef NO_LOCKING - if (which == PASS || which == TTY) - { - fprintf (stderr, "%s: compiled with NO_LOCKING\n", progname); - exit (1); - } -#endif - -#ifndef NO_LOCKING - /* before hack_uid() for proper permissions */ - lock_priv_init (argc, argv, True); - - hack_uid (si); - - if (! lock_init (argc, argv, True)) - { - si->locking_disabled_p = True; - si->nolock_reason = "error getting password"; - } -#endif + msg[nmsgs].type = AUTH_MSGTYPE_INFO; + msg[nmsgs].msg = "1/1 Page Two"; + nmsgs++; + DIALOG(); - progclass = "XScreenSaver"; - if (!setlocale (LC_CTYPE, "")) - fprintf (stderr, "%s: warning: could not set default locale\n", - progname); + msg[nmsgs].type = AUTH_MSGTYPE_INFO; + msg[nmsgs].msg = "1/2 Page Three"; + nmsgs++; + msg[nmsgs].type = AUTH_MSGTYPE_PROMPT_NOECHO; + msg[nmsgs].msg = "2/2 Greets to Crash Override and also Joey"; + nmsgs++; - if (which != TTY) - { - toplevel_shell = XtAppInitialize (&si->app, progclass, 0, 0, - &argc, argv, fallback, - 0, 0); - si->dpy = XtDisplay (toplevel_shell); - p->db = XtDatabase (si->dpy); - si->default_screen->toplevel_shell = toplevel_shell; - si->default_screen->screen = XtScreen(toplevel_shell); - si->default_screen->default_visual = - si->default_screen->current_visual = - DefaultVisualOfScreen(si->default_screen->screen); - si->default_screen->screensaver_window = - RootWindowOfScreen(si->default_screen->screen); - si->default_screen->current_depth = - visual_depth(si->default_screen->screen, - si->default_screen->current_visual); + msg[nmsgs].type = AUTH_MSGTYPE_PROMPT_NOECHO; + msg[nmsgs].msg = "1/3 Page Four"; + nmsgs++; - ssip.width = WidthOfScreen(ssip.screen); - ssip.height = HeightOfScreen(ssip.screen); + msg[nmsgs].type = AUTH_MSGTYPE_INFO; + msg[nmsgs].msg = "2/3 Hack the planet."; + nmsgs++; - db = p->db; - XtGetApplicationNameAndClass (si->dpy, &progname, &progclass); + msg[nmsgs].type = AUTH_MSGTYPE_INFO; + msg[nmsgs].msg = "3/3 Hack the planet."; + nmsgs++; + DIALOG(); - load_init_file (si->dpy, &si->prefs); - } - p->verbose_p = True; + msg[nmsgs].type = AUTH_MSGTYPE_PROMPT_ECHO; + msg[nmsgs].msg = "1/1 Page Five visible text"; + nmsgs++; - pw = getpwuid (getuid ()); - si->user = strdup (pw->pw_name); + msg[nmsgs].type = AUTH_MSGTYPE_PROMPT_ECHO; + msg[nmsgs].msg = "1/3 Page six visible text"; + nmsgs++; -/* si->nscreens = 0; - si->screens = si->default_screen = 0; */ + msg[nmsgs].type = AUTH_MSGTYPE_INFO; + msg[nmsgs].msg = "2/3 Poop."; + nmsgs++; - while (1) - { -#ifndef NO_LOCKING - if (which == PASS) - { - si->unlock_cb = gui_auth_conv; - si->auth_finished_cb = auth_finished_cb; + msg[nmsgs].type = AUTH_MSGTYPE_INFO; + msg[nmsgs].msg = "3/3 \xF0\x9F\x92\xA9 \xF0\x9F\x92\xA9 \xF0\x9F\x92\xA9" + " \xE2\x80\x9C" + " \xE2\xAC\xA4 \xE2\x80\xA2 \xE2\xAD\x98 " + "\xE2\x80\x9D"; + nmsgs++; + DIALOG(); - debug_passwd_window_p = True; - xss_authenticate(si, True); - - if (si->unlock_state == ul_success) - fprintf (stderr, "%s: authentication succeeded\n", progname); - else - fprintf (stderr, "%s: authentication FAILED!\n", progname); - - XSync(si->dpy, False); - fprintf (stderr, "\n######################################\n\n"); - sleep (3); - } - else -#endif - if (which == SPLASH) - { - XEvent event; - make_splash_dialog (si); - XtAppAddTimeOut (si->app, p->splash_duration + 1000, - idle_timer, (XtPointer) si); - while (si->splash_dialog) - { - XtAppNextEvent (si->app, &event); - if (event.xany.window == si->splash_dialog) - handle_splash_event (si, &event); - XtDispatchEvent (&event); - } - XSync (si->dpy, False); - sleep (1); - } -#ifndef NO_LOCKING - else if (which == TTY) - { - si->unlock_cb = text_auth_conv; - - printf ("%s: Authenticating user %s\n", progname, si->user); - xss_authenticate(si, True); - - if (si->unlock_state == ul_success) - printf ("%s: Ok!\n", progname); - else - printf ("%s: Wrong!\n", progname); - } -#endif - else - abort(); - } - - free(si->user); + exit(0); } + diff --git a/driver/test-randr.c b/driver/test-randr.c index 74ead37..1f88d99 100644 --- a/driver/test-randr.c +++ b/driver/test-randr.c @@ -1,5 +1,5 @@ /* test-randr.c --- playing with the Resize And Rotate extension. - * xscreensaver, Copyright (c) 2004-2008 Jamie Zawinski + * xscreensaver, Copyright © 2004-2021 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 @@ -30,26 +30,9 @@ #include #include -char *progname = 0; +#include "blurb.h" char *progclass = "XScreenSaver"; -static const char * -blurb (void) -{ - static char buf[255]; - time_t now = time ((time_t *) 0); - char *ct = (char *) ctime (&now); - int n = strlen(progname); - if (n > 100) n = 99; - strncpy(buf, progname, n); - buf[n++] = ':'; - buf[n++] = ' '; - strncpy(buf+n, ct+11, 8); - strcpy(buf+n+9, ": "); - return buf; -} - - static Bool error_handler_hit_p = False; static int @@ -72,15 +55,15 @@ main (int argc, char **argv) Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); - XtGetApplicationNameAndClass (dpy, &progname, &progclass); + progname = argv[0]; nscreens = ScreenCount(dpy); if (!XRRQueryExtension(dpy, &event_number, &error_number)) { fprintf(stderr, "%s: XRRQueryExtension(dpy, ...) ==> False\n", blurb()); - fprintf(stderr, "%s: server does not support the RANDR extension.\n", + fprintf(stderr, "%s: server does not support the RANDR extension\n", blurb()); major = -1; } diff --git a/driver/test-screens.c b/driver/test-screens.c index 2fb3e35..141b7ad 100644 --- a/driver/test-screens.c +++ b/driver/test-screens.c @@ -1,5 +1,5 @@ /* test-screens.c --- some test cases for the "monitor sanity" checks. - * xscreensaver, Copyright (c) 2008 Jamie Zawinski + * xscreensaver, Copyright © 2008-2021 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 @@ -14,39 +14,20 @@ # include "config.h" #endif +#include +#include +#include #include -/* This file doesn't need the Xt headers, so stub these types out... */ -#undef XtPointer -#define XtAppContext void* -#define XrmDatabase void* -#define XtIntervalId void* -#define XtPointer void* -#define Widget void* - -#include "xscreensaver.h" +#include "blurb.h" #include "visual.h" +#include "screens.h" #undef WidthOfScreen #undef HeightOfScreen #define WidthOfScreen(s) 10240 #define HeightOfScreen(s) 10240 -#undef screen_number -#define screen_number(s) ((int) s) - -#include "screens.c" /* to get at static void check_monitor_sanity() */ - -char *progname = 0; -char *progclass = "XScreenSaver"; - -const char *blurb(void) { return progname; } - -Bool safe_XF86VidModeGetViewPort(Display *d, int i, int *x, int *y) { abort(); } -void initialize_screen_root_widget(saver_screen_info *ssi) { abort(); } -Visual *get_best_gl_visual (saver_info *si, Screen *sc) { abort(); } - - static const char * failstr (monitor_sanity san) { diff --git a/driver/test-uid.c b/driver/test-uid.c index 6a1f9cc..713a3fd 100644 --- a/driver/test-uid.c +++ b/driver/test-uid.c @@ -124,12 +124,12 @@ main (int argc, char **argv) { if (was_numeric) { - fprintf(stderr, "no group numbered %s.\n", group); + fprintf(stderr, "no group numbered %s\n", group); group = ""; } else { - fprintf(stderr, "no group named %s.\n", group); + fprintf(stderr, "no group named %s\n", group); goto NOGROUP; } } @@ -138,14 +138,14 @@ main (int argc, char **argv) { gid_t g2 = gid; if (setgroups(1, &g2) == 0) - fprintf(stderr, " succeeded.\n"); + fprintf(stderr, " succeeded\n"); else perror(" failed"); } fprintf(stderr, "setgid(%ld) \"%s\"", gid, group); if (setgid(gid) == 0) - fprintf(stderr, " succeeded.\n"); + fprintf(stderr, " succeeded\n"); else perror(" failed"); @@ -177,19 +177,19 @@ main (int argc, char **argv) { if (was_numeric) { - fprintf(stderr, "no user numbered \"%s\".\n", user); + fprintf(stderr, "no user numbered \"%s\"\n", user); user = ""; } else { - fprintf(stderr, "no user named %s.\n", user); + fprintf(stderr, "no user named %s\n", user); goto NOUSER; } } fprintf(stderr, "setuid(%ld) \"%s\"", uid, user); if (setuid(uid) == 0) - fprintf(stderr, " succeeded.\n"); + fprintf(stderr, " succeeded\n"); else perror(" failed"); NOUSER: ; diff --git a/driver/test-vp.c b/driver/test-vp.c index bf1a0b1..ded5aca 100644 --- a/driver/test-vp.c +++ b/driver/test-vp.c @@ -1,5 +1,5 @@ /* test-xinerama.c --- playing with XF86VidModeGetViewPort - * xscreensaver, Copyright (c) 2004 Jamie Zawinski + * xscreensaver, Copyright © 2004-2021 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 @@ -31,26 +31,9 @@ #include #include -char *progname = 0; +#include "blurb.h" char *progclass = "XScreenSaver"; -static const char * -blurb (void) -{ - static char buf[255]; - time_t now = time ((time_t *) 0); - char *ct = (char *) ctime (&now); - int n = strlen(progname); - if (n > 100) n = 99; - strncpy(buf, progname, n); - buf[n++] = ':'; - buf[n++] = ' '; - strncpy(buf+n, ct+11, 8); - strcpy(buf+n+9, ": "); - return buf; -} - - static Bool error_handler_hit_p = False; static int @@ -129,14 +112,13 @@ main (int argc, char **argv) Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); - XtGetApplicationNameAndClass (dpy, &progname, &progclass); if (!XF86VidModeQueryExtension(dpy, &event_number, &error_number)) { fprintf(stderr, "%s: XF86VidModeQueryExtension(dpy, ...) ==> False\n", blurb()); fprintf(stderr, - "%s: server does not support the XF86VidMode extension.\n", + "%s: server does not support the XF86VidMode extension\n", blurb()); exit(1); } diff --git a/driver/test-xdpms.c b/driver/test-xdpms.c index b86aed3..e837d8d 100644 --- a/driver/test-xdpms.c +++ b/driver/test-xdpms.c @@ -1,5 +1,5 @@ /* test-xdpms.c --- playing with the XDPMS extension. - * xscreensaver, Copyright (c) 1998-2011 Jamie Zawinski + * xscreensaver, Copyright © 1998-2021 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 @@ -29,42 +29,11 @@ #include #include -#include +/*#include */ -extern Bool DPMSQueryExtension (Display *dpy, int *event_ret, int *error_ret); -extern Bool DPMSCapable (Display *dpy); -extern Status DPMSForceLevel (Display *dpy, CARD16 level); -extern Status DPMSInfo (Display *dpy, CARD16 *power_level, BOOL *state); - -extern Status DPMSGetVersion (Display *dpy, int *major_ret, int *minor_ret); -extern Status DPMSSetTimeouts (Display *dpy, - CARD16 standby, CARD16 suspend, CARD16 off); -extern Bool DPMSGetTimeouts (Display *dpy, - CARD16 *standby, CARD16 *suspend, CARD16 *off); -extern Status DPMSEnable (Display *dpy); -extern Status DPMSDisable (Display *dpy); - - -char *progname = 0; +#include "blurb.h" char *progclass = "XScreenSaver"; -static const char * -blurb (void) -{ - static char buf[255]; - time_t now = time ((time_t *) 0); - char *ct = (char *) ctime (&now); - int n = strlen(progname); - if (n > 100) n = 99; - strncpy(buf, progname, n); - buf[n++] = ':'; - buf[n++] = ' '; - strncpy(buf+n, ct+11, 8); - strcpy(buf+n+9, ": "); - return buf; -} - - static Bool error_handler_hit_p = False; static int @@ -90,13 +59,12 @@ main (int argc, char **argv) Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); - XtGetApplicationNameAndClass (dpy, &progname, &progclass); if (!DPMSQueryExtension(dpy, &event_number, &error_number)) { fprintf(stderr, "%s: DPMSQueryExtension(dpy, ...) ==> False\n", blurb()); - fprintf(stderr, "%s: server does not support the XDPMS extension.\n", + fprintf(stderr, "%s: server does not support the XDPMS extension\n", blurb()); exit(1); } @@ -107,7 +75,7 @@ main (int argc, char **argv) if (!DPMSCapable(dpy)) { fprintf(stderr, "%s: DPMSCapable(dpy) ==> False\n", blurb()); - fprintf(stderr, "%s: server says hardware doesn't support DPMS.\n", + fprintf(stderr, "%s: server says hardware doesn't support DPMS\n", blurb()); exit(1); } @@ -158,13 +126,12 @@ main (int argc, char **argv) state == DPMSModeSuspend || state == DPMSModeOff) { - XErrorHandler old_handler; int st; - fprintf(stderr, "%s: monitor is off; turning it on.\n", blurb()); + fprintf(stderr, "%s: monitor is off; turning it on\n", blurb()); XSync (dpy, False); error_handler_hit_p = False; - old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + XSetErrorHandler (ignore_all_errors_ehandler); XSync (dpy, False); st = DPMSForceLevel (dpy, DPMSModeOn); XSync (dpy, False); diff --git a/driver/test-xinerama.c b/driver/test-xinerama.c index 8bafbb0..cd8add4 100644 --- a/driver/test-xinerama.c +++ b/driver/test-xinerama.c @@ -1,5 +1,5 @@ /* test-xinerama.c --- playing with the Xinerama extension. - * xscreensaver, Copyright (c) 2003 Jamie Zawinski + * xscreensaver, Copyright © 2003-2021 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 @@ -30,26 +30,9 @@ #include #include -char *progname = 0; +#include "blurb.h" char *progclass = "XScreenSaver"; -static const char * -blurb (void) -{ - static char buf[255]; - time_t now = time ((time_t *) 0); - char *ct = (char *) ctime (&now); - int n = strlen(progname); - if (n > 100) n = 99; - strncpy(buf, progname, n); - buf[n++] = ':'; - buf[n++] = ' '; - strncpy(buf+n, ct+11, 8); - strcpy(buf+n+9, ": "); - return buf; -} - - int main (int argc, char **argv) { @@ -63,13 +46,12 @@ main (int argc, char **argv) Widget toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv, 0, 0, 0); Display *dpy = XtDisplay (toplevel_shell); - XtGetApplicationNameAndClass (dpy, &progname, &progclass); if (!XineramaQueryExtension(dpy, &event_number, &error_number)) { fprintf(stderr, "%s: XineramaQueryExtension(dpy, ...) ==> False\n", blurb()); - fprintf(stderr, "%s: server does not support the Xinerama extension.\n", + fprintf(stderr, "%s: server does not support the Xinerama extension\n", blurb()); exit(1); } @@ -80,7 +62,7 @@ main (int argc, char **argv) if (!XineramaIsActive(dpy)) { fprintf(stderr, "%s: XineramaIsActive(dpy) ==> False\n", blurb()); - fprintf(stderr, "%s: server says Xinerama is turned off.\n", blurb()); + fprintf(stderr, "%s: server says Xinerama is turned off\n", blurb()); exit(1); } else diff --git a/driver/test-xinput.c b/driver/test-xinput.c new file mode 100644 index 0000000..e3a6487 --- /dev/null +++ b/driver/test-xinput.c @@ -0,0 +1,306 @@ +/* test-xinput.c --- playing with the XInput2 extension. + * xscreensaver, Copyright © 2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "blurb.h" +#include "xinput.h" + +char *progclass = "XScreenSaver"; +Bool debug_p = True; + +static void +ungrab_timer (XtPointer closure, XtIntervalId *id) +{ + Display *dpy = (Display *) closure; + fprintf (stderr, "\n%s: ungrabbing\n\n", blurb()); + XUngrabKeyboard (dpy, CurrentTime); + XUngrabPointer (dpy, CurrentTime); +} + + +static const char * +grab_string (int status) +{ + switch (status) { + case GrabSuccess: return "GrabSuccess"; + case AlreadyGrabbed: return "AlreadyGrabbed"; + case GrabInvalidTime: return "GrabInvalidTime"; + case GrabNotViewable: return "GrabNotViewable"; + case GrabFrozen: return "GrabFrozen"; + default: + { + static char buf[255]; + sprintf(buf, "unknown status: %d", status); + return buf; + } + } +} + + +int +main (int argc, char **argv) +{ + XtAppContext app; + Widget toplevel_shell; + Display *dpy; + int xi_opcode; + Bool grab_kbd_p = False; + Bool grab_mouse_p = False; + Bool mouse_sync_p = True; + Bool kbd_sync_p = True; + int i; + + progname = argv[0]; + + for (i = 1; i < argc; i++) + { + const char *oa = argv[i]; + if (argv[i][0] == '-' && argv[i][1] == '-') + argv[i]++; + if (!strcmp (argv[i], "-grab")) + grab_kbd_p = grab_mouse_p = True; + else if (!strcmp (argv[i], "-grab-kbd") || + !strcmp (argv[i], "-grab-keyboard")) + grab_kbd_p = True; + else if (!strcmp (argv[i], "-grab-mouse") || + !strcmp (argv[i], "-grab-pointer")) + grab_mouse_p = True; + else if (!strcmp (argv[i], "-kbd-sync") || + !strcmp (argv[i], "-keyboard-sync")) + kbd_sync_p = True; + else if (!strcmp (argv[i], "-kbd-async") || + !strcmp (argv[i], "-keyboard-async")) + kbd_sync_p = False; + else if (!strcmp (argv[i], "-mouse-sync") || + !strcmp (argv[i], "-pointer-sync")) + mouse_sync_p = True; + else if (!strcmp (argv[i], "-mouse-async") || + !strcmp (argv[i], "-pointer-async")) + mouse_sync_p = False; + else + { + fprintf (stderr, "%s: unknown option: %s\n", blurb(), oa); + exit (1); + } + } + + toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, + &argc, argv, 0, 0, 0); + dpy = XtDisplay (toplevel_shell); + if (!dpy) exit(1); + + if (! init_xinput (dpy, &xi_opcode)) + exit (1); + + if (grab_kbd_p || grab_mouse_p) + { + int timeout = 15; + Window w = RootWindow (dpy, 0); + int status; + XColor black = { 0, }; + Pixmap bit = XCreateBitmapFromData (dpy, w, "\000", 1, 1); + Cursor cursor = XCreatePixmapCursor (dpy, bit, bit, &black, &black, 0, 0); + + if (grab_kbd_p) + { + status = XGrabKeyboard (dpy, w, True, + (mouse_sync_p ? GrabModeSync : GrabModeAsync), + (kbd_sync_p ? GrabModeSync : GrabModeAsync), + CurrentTime); + if (status == GrabSuccess) + fprintf (stderr, "%s: grabbed keyboard (%s, %s)\n", blurb(), + (mouse_sync_p ? "sync" : "async"), + (kbd_sync_p ? "sync" : "async")); + else + { + fprintf (stderr, "%s: failed to grab keyboard (%s, %s): %s\n", + blurb(), + (mouse_sync_p ? "sync" : "async"), + (kbd_sync_p ? "sync" : "async"), + grab_string (status)); + exit(1); + } + } + + if (grab_mouse_p) + { + status = XGrabPointer (dpy, w, True, + (ButtonPressMask | ButtonReleaseMask | + EnterWindowMask | LeaveWindowMask | + PointerMotionMask | PointerMotionHintMask | + Button1MotionMask | Button2MotionMask | + Button3MotionMask | Button4MotionMask | + Button5MotionMask | ButtonMotionMask), + (mouse_sync_p ? GrabModeSync : GrabModeAsync), + (kbd_sync_p ? GrabModeSync : GrabModeAsync), + w, cursor, CurrentTime); + if (status == GrabSuccess) + fprintf (stderr, "%s: grabbed mouse (%s, %s)\n", blurb(), + (mouse_sync_p ? "sync" : "async"), + (kbd_sync_p ? "sync" : "async")); + else + { + fprintf (stderr, "%s: failed to grab mouse (%s, %s): %s\n", + blurb(), + (mouse_sync_p ? "sync" : "async"), + (kbd_sync_p ? "sync" : "async"), + grab_string (status)); + exit(1); + } + } + + fprintf (stderr, "%s: ungrabbing in %d seconds\n", blurb(), timeout); + XtAppAddTimeOut (app, 1000 * timeout, ungrab_timer, (XtPointer) dpy); + } + + while (1) + { + XEvent xev; + XIRawEvent *re; + + XtAppNextEvent (app, &xev); + XtDispatchEvent (&xev); + + switch (xev.xany.type) { + case KeyPress: + case KeyRelease: + { + static XComposeStatus compose = { 0, }; + KeySym keysym = 0; + char c[100]; + int n; + n = XLookupString (&xev.xkey, c, sizeof(c)-1, &keysym, &compose); + c[n] = 0; + fprintf (stderr, "%s: X11 Key%s %02x %02x %s \"%s\"\n", blurb(), + (xev.xkey.type == KeyPress ? "Press " : "Release"), + xev.xkey.keycode, xev.xkey.state, + XKeysymToString (keysym), c); + } + break; + case ButtonPress: + case ButtonRelease: + fprintf (stderr, "%s: X11 Button%s %d %d\n", blurb(), + (xev.xany.type == ButtonPress ? "Press " : "Release"), + xev.xbutton.button, xev.xbutton.state); + break; + case MotionNotify: + fprintf (stderr, "%s: X11 MotionNotify %4d, %-4d\n", + blurb(), xev.xmotion.x_root, xev.xmotion.y_root); + break; + case GenericEvent: + break; + case EnterNotify: + case LeaveNotify: + break; + default: + fprintf (stderr, "%s: X11 event %d on 0x%lx\n", + blurb(), xev.xany.type, xev.xany.window); + break; + } + + if (xev.xcookie.type != GenericEvent || + xev.xcookie.extension != xi_opcode) + continue; /* not an XInput event */ + if (!xev.xcookie.data) + XGetEventData (dpy, &xev.xcookie); + if (!xev.xcookie.data) + continue; /* Bogus XInput event */ + + re = xev.xcookie.data; + switch (xev.xcookie.evtype) { + case XI_RawKeyPress: + case XI_RawKeyRelease: + { + /* Fake up an XKeyEvent in order to call XKeysymToString(). */ + XEvent ev2; + Bool ok = xinput_event_to_xlib (xev.xcookie.evtype, + (XIDeviceEvent *) re, + &ev2); + if (!ok) + fprintf (stderr, "%s: unable to translate XInput2 event\n", + blurb()); + else + { + static XComposeStatus compose = { 0, }; + KeySym keysym = 0; + char c[100]; + int n; + n = XLookupString (&ev2.xkey, c, sizeof(c)-1, &keysym, &compose); + c[n] = 0; + fprintf (stderr, "%s: XI_RawKey%s %02x %02x %s \"%s\"\n", + blurb(), + (ev2.xkey.type == KeyPress ? "Press " : "Release"), + ev2.xkey.keycode, ev2.xkey.state, + XKeysymToString (keysym), c); + } + } + break; + case XI_RawButtonPress: + case XI_RawButtonRelease: + fprintf (stderr, "%s: XI_RawButton%s %d\n", blurb(), + (re->evtype == XI_RawButtonPress ? "Press " : "Release"), + re->detail); + break; + case XI_RawMotion: + { + Window root_ret, child_ret; + int root_x, root_y; + int win_x, win_y; + unsigned int mask; + XQueryPointer (dpy, DefaultRootWindow (dpy), + &root_ret, &child_ret, &root_x, &root_y, + &win_x, &win_y, &mask); + fprintf (stderr, "%s: XI_RawMotion %4d, %-4d %7.02f, %-7.02f\n", + blurb(), + root_x, root_y, + re->raw_values[0], re->raw_values[1]); + } + break; + case XI_RawTouchBegin: + fprintf (stderr, "%s: XI_RawTouchBegin\n", blurb()); + break; + case XI_RawTouchEnd: + fprintf (stderr, "%s: XI_RawTouchEnd", blurb()); + break; + case XI_RawTouchUpdate: + fprintf (stderr, "%s: XI_RawTouchUpdate", blurb()); + break; + + default: + fprintf (stderr, "%s: XInput unknown event %d\n", blurb(), + xev.xcookie.evtype); + break; + } + + XFreeEventData (dpy, &xev.xcookie); + } + + exit (0); +} diff --git a/driver/test-xkb.c b/driver/test-xkb.c new file mode 100644 index 0000000..8d26b73 --- /dev/null +++ b/driver/test-xkb.c @@ -0,0 +1,89 @@ +/* test-xkb.c --- playing with the X Keyboard extension. + * xscreensaver, Copyright © 2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "blurb.h" + +char *progclass = "XScreenSaver"; + +int +main (int argc, char **argv) +{ + XtAppContext app; + Widget toplevel_shell; + Display *dpy; + int i; + + progname = argv[0]; + + for (i = 1; i < argc; i++) + { + const char *oa = argv[i]; + if (argv[i][0] == '-' && argv[i][1] == '-') + argv[i]++; + if (0) ; + else + { + fprintf (stderr, "%s: unknown option: %s\n", blurb(), oa); + exit (1); + } + } + + toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, + &argc, argv, 0, 0, 0); + dpy = XtDisplay (toplevel_shell); + if (!dpy) exit(1); + + { + XkbStateRec state; + XkbDescPtr desc; + char *group; + Atom name; + + if (XkbGetState (dpy, XkbUseCoreKbd, &state)) + { + fprintf (stderr, "%s: XkbGetState failed\n", progname); + exit (1); + } + desc = XkbGetKeyboard (dpy, XkbAllComponentsMask, XkbUseCoreKbd); + if (!desc || !desc->names) + { + fprintf (stderr, "%s: XkbGetKeyboard failed\n", progname); + exit (1); + } + + name = desc->names->groups[state.group]; + group = (name ? XGetAtomName (dpy, name) : strdup("NULL")); + fprintf (stderr, "%s: kbd name: %s\n", progname, group); + free (group); + } + + exit (0); +} diff --git a/driver/test-yarandom.c b/driver/test-yarandom.c index 6701ff8..e434377 100644 --- a/driver/test-yarandom.c +++ b/driver/test-yarandom.c @@ -21,10 +21,9 @@ #include #include +#include "blurb.h" #include "yarandom.h" -char *progname = 0; - int main (int argc, char **argv) { diff --git a/driver/timers.c b/driver/timers.c deleted file mode 100644 index b096000..0000000 --- a/driver/timers.c +++ /dev/null @@ -1,1807 +0,0 @@ -/* timers.c --- detecting when the user is idle, and other timer-related tasks. - * xscreensaver, Copyright (c) 1991-2019 Jamie Zawinski - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_XMU -# ifndef VMS -# include -# else /* VMS */ -# include -# endif /* VMS */ -# else /* !HAVE_XMU */ -# include "xmu.h" -#endif /* !HAVE_XMU */ - -#ifdef HAVE_XIDLE_EXTENSION -#include -#endif /* HAVE_XIDLE_EXTENSION */ - -#ifdef HAVE_MIT_SAVER_EXTENSION -#include -#endif /* HAVE_MIT_SAVER_EXTENSION */ - -#ifdef HAVE_SGI_SAVER_EXTENSION -#include -#endif /* HAVE_SGI_SAVER_EXTENSION */ - -#ifdef HAVE_RANDR -#include -#endif /* HAVE_RANDR */ - -#include "xscreensaver.h" - -#undef ABS -#define ABS(x)((x)<0?-(x):(x)) - -#undef MAX -#define MAX(x,y)((x)>(y)?(x):(y)) - - -#ifdef HAVE_PROC_INTERRUPTS -static Bool proc_interrupts_activity_p (saver_info *si); -#endif /* HAVE_PROC_INTERRUPTS */ - -static void check_for_clock_skew (saver_info *si); - - -void -idle_timer (XtPointer closure, XtIntervalId *id) -{ - saver_info *si = (saver_info *) closure; - - /* What an amazingly shitty design. Not only does Xt execute timeout - events from XtAppNextEvent() instead of from XtDispatchEvent(), but - there is no way to tell Xt to block until there is an X event OR a - timeout happens. Once your timeout proc is called, XtAppNextEvent() - still won't return until a "real" X event comes in. - - So this function pushes a stupid, gratuitous, unnecessary event back - on the event queue to force XtAppNextEvent to return Right Fucking Now. - When the code in sleep_until_idle() sees an event of type XAnyEvent, - which the server never generates, it knows that a timeout has occurred. - */ - XEvent fake_event; - fake_event.type = 0; /* XAnyEvent type, ignored. */ - fake_event.xany.display = si->dpy; - fake_event.xany.window = 0; - XPutBackEvent (si->dpy, &fake_event); - - /* If we are the timer that just went off, clear the pointer to the id. */ - if (id) - { - if (si->timer_id && *id != si->timer_id) - abort(); /* oops, scheduled timer twice?? */ - si->timer_id = 0; - } -} - - -void -schedule_wakeup_event (saver_info *si, Time when, Bool verbose_p) -{ - if (si->timer_id) - { - if (verbose_p) - fprintf (stderr, "%s: idle_timer already running\n", blurb()); - return; - } - - /* Wake up periodically to ask the server if we are idle. */ - si->timer_id = XtAppAddTimeOut (si->app, when, idle_timer, - (XtPointer) si); - - if (verbose_p) - fprintf (stderr, "%s: starting idle_timer (%ld, %ld)\n", - blurb(), when, si->timer_id); -} - - -static void -notice_events (saver_info *si, Window window, Bool top_p) -{ - saver_preferences *p = &si->prefs; - XWindowAttributes attrs; - unsigned long events; - Window root, parent, *kids; - unsigned int nkids; - int screen_no; - - if (XtWindowToWidget (si->dpy, window)) - /* If it's one of ours, don't mess up its event mask. */ - return; - - if (!XQueryTree (si->dpy, window, &root, &parent, &kids, &nkids)) - return; - if (window == root) - top_p = False; - - /* Figure out which screen this window is on, for the diagnostics. */ - for (screen_no = 0; screen_no < si->nscreens; screen_no++) - if (root == RootWindowOfScreen (si->screens[screen_no].screen)) - break; - - XGetWindowAttributes (si->dpy, window, &attrs); - events = ((attrs.all_event_masks | attrs.do_not_propagate_mask) - & (KeyPressMask | PropertyChangeMask)); - - /* Select for SubstructureNotify on all windows. - Select for PropertyNotify on all windows. - Select for KeyPress on all windows that already have it selected. - - Note that we can't select for ButtonPress, because of X braindamage: - only one client at a time may select for ButtonPress on a given - window, though any number can select for KeyPress. Someone explain - *that* to me. - - So, if the user spends a while clicking the mouse without ever moving - the mouse or touching the keyboard, we won't know that they've been - active, and the screensaver will come on. That sucks, but I don't - know how to get around it. - - Since X presents mouse wheels as clicks, this applies to those, too: - scrolling through a document using only the mouse wheel doesn't - count as activity... Fortunately, /proc/interrupts helps, on - systems that have it. Oh, if it's a PS/2 mouse, not serial or USB. - This sucks! - */ - XSelectInput (si->dpy, window, - SubstructureNotifyMask | PropertyChangeMask | events); - - if (top_p && p->debug_p && (events & KeyPressMask)) - { - /* Only mention one window per tree (hack hack). */ - fprintf (stderr, "%s: %d: selected KeyPress on 0x%lX\n", - blurb(), screen_no, (unsigned long) window); - top_p = False; - } - - if (kids) - { - while (nkids) - notice_events (si, kids [--nkids], top_p); - XFree ((char *) kids); - } -} - - -int -BadWindow_ehandler (Display *dpy, XErrorEvent *error) -{ - /* When we notice a window being created, we spawn a timer that waits - 30 seconds or so, and then selects events on that window. This error - handler is used so that we can cope with the fact that the window - may have been destroyed <30 seconds after it was created. - */ - if (error->error_code == BadWindow || - error->error_code == BadMatch || - error->error_code == BadDrawable) - return 0; - else - return saver_ehandler (dpy, error); -} - - -struct notice_events_timer_arg { - saver_info *si; - Window w; -}; - -static void -notice_events_timer (XtPointer closure, XtIntervalId *id) -{ - struct notice_events_timer_arg *arg = - (struct notice_events_timer_arg *) closure; - - XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler); - - saver_info *si = arg->si; - Window window = arg->w; - - free(arg); - notice_events (si, window, True); - XSync (si->dpy, False); - XSetErrorHandler (old_handler); -} - -void -start_notice_events_timer (saver_info *si, Window w, Bool verbose_p) -{ - saver_preferences *p = &si->prefs; - struct notice_events_timer_arg *arg = - (struct notice_events_timer_arg *) malloc(sizeof(*arg)); - arg->si = si; - arg->w = w; - XtAppAddTimeOut (si->app, p->notice_events_timeout, notice_events_timer, - (XtPointer) arg); - - if (verbose_p) - fprintf (stderr, "%s: starting notice_events_timer for 0x%X (%lu)\n", - blurb(), (unsigned int) w, p->notice_events_timeout); -} - - -/* When the screensaver is active, this timer will periodically change - the running program. - */ -void -cycle_timer (XtPointer closure, XtIntervalId *id) -{ - saver_info *si = (saver_info *) closure; - saver_preferences *p = &si->prefs; - Time how_long = p->cycle; - - if (si->selection_mode > 0 && - screenhack_running_p (si)) - /* If we're in "SELECT n" mode, the cycle timer going off will just - restart this same hack again. There's not much point in doing this - every 5 or 10 minutes, but on the other hand, leaving one hack running - for days is probably not a great idea, since they tend to leak and/or - crash. So, restart the thing once an hour. */ - how_long = 1000 * 60 * 60; - - if (si->dbox_up_p) - { - if (p->verbose_p) - fprintf (stderr, "%s: dialog box up; delaying hack change.\n", - blurb()); - how_long = 30000; /* 30 secs */ - } - else - { - int i; - maybe_reload_init_file (si); - for (i = 0; i < si->nscreens; i++) - kill_screenhack (&si->screens[i]); - - raise_window (si, True, True, False); - - if (!si->throttled_p) - for (i = 0; i < si->nscreens; i++) - spawn_screenhack (&si->screens[i]); - else - { - if (p->verbose_p) - fprintf (stderr, "%s: not launching new hack (throttled.)\n", - blurb()); - } - } - - if (how_long > 0) - { - si->cycle_id = XtAppAddTimeOut (si->app, how_long, cycle_timer, - (XtPointer) si); - - if (p->debug_p) - fprintf (stderr, "%s: starting cycle_timer (%ld, %ld)\n", - blurb(), how_long, si->cycle_id); - } - else - { - if (p->debug_p) - fprintf (stderr, "%s: not starting cycle_timer: how_long == %ld\n", - blurb(), (unsigned long) how_long); - } -} - - -void -activate_lock_timer (XtPointer closure, XtIntervalId *id) -{ - saver_info *si = (saver_info *) closure; - saver_preferences *p = &si->prefs; - - if (p->verbose_p) - fprintf (stderr, "%s: timed out; activating lock.\n", blurb()); - - if (!si->locked_p) - /* So that "xscreensaver-command -watch" reports the time that the - screen was locked, instead of duplicating the blank time. */ - si->blank_time = time ((time_t *) 0); - - set_locked_p (si, True); -} - - -/* Call this when user activity (or "simulated" activity) has been noticed. - */ -void -reset_timers (saver_info *si) -{ - saver_preferences *p = &si->prefs; - if (si->using_mit_saver_extension || si->using_sgi_saver_extension) - return; - - if (si->timer_id) - { - if (p->debug_p) - fprintf (stderr, "%s: killing idle_timer (%ld, %ld)\n", - blurb(), p->timeout, si->timer_id); - XtRemoveTimeOut (si->timer_id); - si->timer_id = 0; - } - - schedule_wakeup_event (si, p->timeout, p->debug_p); /* sets si->timer_id */ - - if (si->cycle_id) abort (); /* no cycle timer when inactive */ - - si->last_activity_time = time ((time_t *) 0); - - /* This will (hopefully, supposedly) tell the server to re-set its - DPMS timer. Without this, the -deactivate clientmessage would - prevent xscreensaver from blanking, but would not prevent the - monitor from powering down. */ -#if 0 - /* #### With some servers, this causes the screen to flicker every - time a key is pressed! Ok, I surrender. I give up on ever - having DPMS work properly. - */ - XForceScreenSaver (si->dpy, ScreenSaverReset); - - /* And if the monitor is already powered off, turn it on. - You'd think the above would do that, but apparently not? */ - monitor_power_on (si, True); -#endif - -} - - -/* Returns true if a mouse has moved since the last time we checked. - Small motions (of less than "hysteresis" pixels/second) are ignored. - */ -static Bool -device_pointer_moved_p (saver_info *si, poll_mouse_data *last_poll_mouse, - poll_mouse_data *this_poll_mouse, Bool mods_p, - const char *debug_type, int debug_id) -{ - saver_preferences *p = &si->prefs; - - unsigned int distance, dps; - unsigned long seconds = 0; - Bool moved_p = False; - - distance = MAX (ABS (last_poll_mouse->root_x - this_poll_mouse->root_x), - ABS (last_poll_mouse->root_y - this_poll_mouse->root_y)); - seconds = (this_poll_mouse->time - last_poll_mouse->time); - - - /* When the screen is blanked, we get MotionNotify events, but when not - blanked, we poll only every 5 seconds, and that's not enough resolution - to do hysteresis based on a 1 second interval. So, assume that any - motion we've seen during the 5 seconds when our eyes were closed happened - in the last 1 second instead. - */ - if (seconds > 1) seconds = 1; - - dps = (seconds <= 0 ? distance : (distance / seconds)); - - /* Motion only counts if the rate is more than N pixels per second. - */ - if (dps >= p->pointer_hysteresis && - distance > 0) - moved_p = True; - - /* If the mouse is not on this screen but used to be, that's motion. - If the mouse was not on this screen, but is now, that's motion. - */ - { - Bool on_screen_p = (this_poll_mouse->root_x != -1 && - this_poll_mouse->root_y != -1); - Bool was_on_screen_p = (last_poll_mouse->root_x != -1 && - last_poll_mouse->root_y != -1); - - if (on_screen_p != was_on_screen_p) - moved_p = True; - } - - if (p->debug_p && (distance != 0 || moved_p)) - { - fprintf (stderr, "%s: %s %d: pointer %s", blurb(), debug_type, debug_id, - (moved_p ? "moved: " : "ignored:")); - if (last_poll_mouse->root_x == -1) - fprintf (stderr, "off screen"); - else - fprintf (stderr, "%d,%d", - last_poll_mouse->root_x, - last_poll_mouse->root_y); - fprintf (stderr, " -> "); - if (this_poll_mouse->root_x == -1) - fprintf (stderr, "off screen"); - else - fprintf (stderr, "%d,%d", this_poll_mouse->root_x, - this_poll_mouse->root_y); - if (last_poll_mouse->root_x != -1 && this_poll_mouse->root_x != -1) - fprintf (stderr, " (%d,%d; %d/%lu=%d)", - ABS(last_poll_mouse->root_x - this_poll_mouse->root_x), - ABS(last_poll_mouse->root_y - this_poll_mouse->root_y), - distance, seconds, dps); - - fprintf (stderr, ".\n"); - } - - if (!moved_p && - mods_p && - this_poll_mouse->mask != last_poll_mouse->mask) - { - moved_p = True; - - if (p->debug_p) - fprintf (stderr, "%s: %s %d: modifiers changed: 0x%04x -> 0x%04x.\n", - blurb(), debug_type, debug_id, - last_poll_mouse->mask, this_poll_mouse->mask); - } - - last_poll_mouse->child = this_poll_mouse->child; - last_poll_mouse->mask = this_poll_mouse->mask; - - if (moved_p || seconds > 0) - { - last_poll_mouse->time = this_poll_mouse->time; - last_poll_mouse->root_x = this_poll_mouse->root_x; - last_poll_mouse->root_y = this_poll_mouse->root_y; - } - - return moved_p; -} - -/* Returns true if core mouse pointer has moved since the last time we checked. - */ -static Bool -pointer_moved_p (saver_screen_info *ssi, Bool mods_p) -{ - saver_info *si = ssi->global; - - Window root; - poll_mouse_data this_poll_mouse; - int x, y; - - /* don't check xinerama pseudo-screens. */ - if (!ssi->real_screen_p) return False; - - this_poll_mouse.time = time ((time_t *) 0); - - if (!XQueryPointer (si->dpy, ssi->screensaver_window, &root, - &this_poll_mouse.child, - &this_poll_mouse.root_x, &this_poll_mouse.root_y, - &x, &y, &this_poll_mouse.mask)) - { - /* If XQueryPointer() returns false, the mouse is not on this screen. - */ - this_poll_mouse.root_x = -1; - this_poll_mouse.root_y = -1; - this_poll_mouse.child = 0; - this_poll_mouse.mask = 0; - } - else - si->last_activity_screen = ssi; - - return device_pointer_moved_p(si, &(ssi->last_poll_mouse), &this_poll_mouse, - mods_p, "screen", ssi->number); -} - - -/* When we aren't using a server extension, this timer is used to periodically - wake up and poll the mouse position, which is possibly more reliable than - selecting motion events on every window. - */ -static void -check_pointer_timer (XtPointer closure, XtIntervalId *id) -{ - int i; - saver_info *si = (saver_info *) closure; - saver_preferences *p = &si->prefs; - Bool active_p = False; - - if (!si->using_proc_interrupts && - (si->using_xidle_extension || - si->using_mit_saver_extension || - si->using_sgi_saver_extension)) - /* If an extension is in use, we should not be polling the mouse. - Unless we're also checking /proc/interrupts, in which case, we should. - */ - abort (); - - if (id && *id == si->check_pointer_timer_id) /* this is us - it's expired */ - si->check_pointer_timer_id = 0; - - if (si->check_pointer_timer_id) /* only queue one at a time */ - XtRemoveTimeOut (si->check_pointer_timer_id); - - si->check_pointer_timer_id = /* now re-queue */ - XtAppAddTimeOut (si->app, p->pointer_timeout, check_pointer_timer, - (XtPointer) si); - - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (pointer_moved_p (ssi, True)) - active_p = True; - } - -#ifdef HAVE_PROC_INTERRUPTS - if (!active_p && - si->using_proc_interrupts && - proc_interrupts_activity_p (si)) - { - active_p = True; - } -#endif /* HAVE_PROC_INTERRUPTS */ - - if (active_p) - reset_timers (si); - - check_for_clock_skew (si); -} - - -/* An unfortunate situation is this: the saver is not active, because the - user has been typing. The machine is a laptop. The user closes the lid - and suspends it. The CPU halts. Some hours later, the user opens the - lid. At this point, Xt's timers will fire, and xscreensaver will blank - the screen. - - So far so good -- well, not really, but it's the best that we can do, - since the OS doesn't send us a signal *before* shutdown -- but if the - user had delayed locking (lockTimeout > 0) then we should start off - in the locked state, rather than only locking N minutes from when the - lid was opened. Also, eschewing fading is probably a good idea, to - clamp down as soon as possible. - - We only do this when we'd be polling the mouse position anyway. - This amounts to an assumption that machines with APM support also - have /proc/interrupts. - - Now here's a thing that sucks about this: if the user actually changes - the time of the machine, it will either trigger or delay the triggering - of a lock. On most systems, that requires root, but I'll bet at least - some GUI configs let non-root do it. Also, NTP attacks. - - On Linux 2.6.39+ systems, there exists clock_gettime(CLOCK_BOOTTIME) - which would allow us to detect the "laptop CPU had been halted" state - independently of changes in wall-clock time. But of course that's not - portable. - - When the wall clock changes, what do Xt timers do, anyway? If I have - a timer set for 30 seconds from now, and adjust the wall clock +15 seconds, - does the timer fire 30 seconds from now or 15? I actually have no idea. - It does not appear to be specified. - */ -static void -check_for_clock_skew (saver_info *si) -{ - saver_preferences *p = &si->prefs; - time_t now = time ((time_t *) 0); - long shift = now - si->last_wall_clock_time; - - if (p->debug_p) - { - int i = (si->last_wall_clock_time == 0 ? 0 : shift); - fprintf (stderr, - "%s: checking wall clock for hibernation (%d:%02d:%02d).\n", - blurb(), - (i / (60 * 60)), ((i / 60) % 60), (i % 60)); - } - - if (si->last_wall_clock_time != 0 && - shift > (p->timeout / 1000)) - { - if (p->verbose_p) - fprintf (stderr, "%s: wall clock has jumped by %ld:%02ld:%02ld%s\n", - blurb(), - (shift / (60 * 60)), ((shift / 60) % 60), (shift % 60), - (p->mode == DONT_BLANK ? " while saver disabled" : "")); - - /* If the saver is entirely disabled, there's no need to do the - emergency-blank-and-lock thing. - */ - if (p->mode != DONT_BLANK) - { - si->emergency_lock_p = True; - idle_timer ((XtPointer) si, 0); - } - } - - si->last_wall_clock_time = now; -} - - - -static void -dispatch_event (saver_info *si, XEvent *event) -{ - /* If this is for the splash dialog, pass it along. - Note that the password dialog is handled with its own event loop, - so events for that window will never come through here. - */ - if (si->splash_dialog && event->xany.window == si->splash_dialog) - handle_splash_event (si, event); - - XtDispatchEvent (event); -} - - -static void -swallow_unlock_typeahead_events (saver_info *si, XEvent *e) -{ - XEvent event; - char buf [100]; - int i = 0; - - memset (buf, 0, sizeof(buf)); - - event = *e; - - do - { - if (event.xany.type == KeyPress) - { - char s[2]; - int size = XLookupString ((XKeyEvent *) &event, s, 1, 0, 0); - if (size != 1) continue; - switch (*s) - { - case '\010': case '\177': /* Backspace */ - if (i > 0) i--; - break; - case '\025': case '\030': /* Erase line */ - case '\012': case '\015': /* Enter */ - case '\033': /* ESC */ - i = 0; - break; - case '\040': /* Space */ - if (i == 0) - break; /* ignore space at beginning of line */ - /* else, fall through */ - default: - buf [i++] = *s; - break; - } - } - - } while (i < sizeof(buf)-1 && - XCheckMaskEvent (si->dpy, KeyPressMask, &event)); - - buf[i] = 0; - - if (si->unlock_typeahead) - { - memset (si->unlock_typeahead, 0, strlen(si->unlock_typeahead)); - free (si->unlock_typeahead); - } - - if (i > 0) - si->unlock_typeahead = strdup (buf); - else - si->unlock_typeahead = 0; - - memset (buf, 0, sizeof(buf)); -} - -void -flush_events (saver_info *si) -{ - int i; - XEvent xe; - for (i = 0; i < 10; ++i) { - while (XtAppPending (si->app) & XtIMXEvent) { - XtAppNextEvent (si->app, &xe); - dispatch_event (si, &xe); - } - usleep(5000); - } -} - -/* methods of detecting idleness: - - explicitly informed by SGI SCREEN_SAVER server event; - explicitly informed by MIT-SCREEN-SAVER server event; - poll server idle time with XIDLE extension; - select events on all windows, and note absence of recent events; - note that /proc/interrupts has not changed in a while; - activated by clientmessage. - - methods of detecting non-idleness: - - read events on the xscreensaver window; - explicitly informed by SGI SCREEN_SAVER server event; - explicitly informed by MIT-SCREEN-SAVER server event; - select events on all windows, and note events on any of them; - note that a client updated their window's _NET_WM_USER_TIME property; - note that /proc/interrupts has changed; - deactivated by clientmessage. - - I trust that explains why this function is a big hairy mess. - */ -void -sleep_until_idle (saver_info *si, Bool until_idle_p) -{ - saver_preferences *p = &si->prefs; - - /* We have to go through this union bullshit because gcc-4.4.0 has - stricter struct-aliasing rules. Without this, the optimizer - can fuck things up. - */ - union { - XEvent x_event; -# ifdef HAVE_RANDR - XRRScreenChangeNotifyEvent xrr_event; -# endif /* HAVE_RANDR */ -# ifdef HAVE_MIT_SAVER_EXTENSION - XScreenSaverNotifyEvent sevent; -# endif /* HAVE_MIT_SAVER_EXTENSION */ - } event; - - /* We need to select events on all windows if we're not using any extensions. - Otherwise, we don't need to. */ - Bool scanning_all_windows = !(si->using_xidle_extension || - si->using_mit_saver_extension || - si->using_sgi_saver_extension); - - /* We need to periodically wake up and check for idleness if we're not using - any extensions, or if we're using the XIDLE extension. The other two - extensions explicitly deliver events when we go idle/non-idle, so we - don't need to poll. */ - Bool polling_for_idleness = !(si->using_mit_saver_extension || - si->using_sgi_saver_extension); - - /* Whether we need to periodically wake up and check to see if the mouse has - moved. We only need to do this when not using any extensions. The reason - this isn't the same as `polling_for_idleness' is that the "idleness" poll - can happen (for example) 5 minutes from now, whereas the mouse-position - poll should happen with low periodicity. We don't need to poll the mouse - position with the XIDLE extension, but we do need to periodically wake up - and query the server with that extension. For our purposes, polling - /proc/interrupts is just like polling the mouse position. It has to - happen on the same kind of schedule. */ - Bool polling_mouse_position = (si->using_proc_interrupts || - !(si->using_xidle_extension || - si->using_mit_saver_extension || - si->using_sgi_saver_extension) || - si->using_xinput_extension); - - const char *why = 0; /* What caused the idle-state to change? */ - - if (until_idle_p) - { - if (polling_for_idleness) - /* This causes a no-op event to be delivered to us in a while, so that - we come back around through the event loop again. */ - schedule_wakeup_event (si, p->timeout, p->debug_p); - - if (polling_mouse_position) - /* Check to see if the mouse has moved, and set up a repeating timer - to do so periodically (typically, every 5 seconds.) */ - check_pointer_timer ((XtPointer) si, 0); - } - - while (1) - { - XtAppNextEvent (si->app, &event.x_event); - - switch (event.x_event.xany.type) { - case 0: /* our synthetic "timeout" event has been signalled */ - if (until_idle_p) - { - Time idle; - - /* We may be idle; check one last time to see if the mouse has - moved, just in case the idle-timer went off within the 5 second - window between mouse polling. If the mouse has moved, then - check_pointer_timer() will reset last_activity_time. - */ - if (polling_mouse_position) - check_pointer_timer ((XtPointer) si, 0); - -#ifdef HAVE_XIDLE_EXTENSION - if (si->using_xidle_extension) - { - /* The XIDLE extension uses the synthetic event to prod us into - re-asking the server how long the user has been idle. */ - if (! XGetIdleTime (si->dpy, &idle)) - { - fprintf (stderr, "%s: XGetIdleTime() failed.\n", blurb()); - saver_exit (si, 1, 0); - } - } - else -#endif /* HAVE_XIDLE_EXTENSION */ -#ifdef HAVE_MIT_SAVER_EXTENSION - if (si->using_mit_saver_extension) - { - /* We don't need to do anything in this case - the synthetic - event isn't necessary, as we get sent specific events - to wake us up. In fact, this event generally shouldn't - be being delivered when the MIT extension is in use. */ - idle = 0; - } - else -#endif /* HAVE_MIT_SAVER_EXTENSION */ -#ifdef HAVE_SGI_SAVER_EXTENSION - if (si->using_sgi_saver_extension) - { - /* We don't need to do anything in this case - the synthetic - event isn't necessary, as we get sent specific events - to wake us up. In fact, this event generally shouldn't - be being delivered when the SGI extension is in use. */ - idle = 0; - } - else -#endif /* HAVE_SGI_SAVER_EXTENSION */ - { - /* Otherwise, no server extension is in use. The synthetic - event was to tell us to wake up and see if the user is now - idle. Compute the amount of idle time by comparing the - `last_activity_time' to the wall clock. The l_a_t was set - by calling `reset_timers()', which is called only in only - two situations: when polling the mouse position has revealed - the the mouse has moved (user activity) or when we have read - an event (again, user activity.) - */ - idle = 1000 * (si->last_activity_time - time ((time_t *) 0)); - } - - if (idle >= p->timeout) - { - /* Look, we've been idle long enough. We're done. */ - why = "timeout"; - goto DONE; - } - else if (si->emergency_lock_p) - { - /* Oops, the wall clock has jumped far into the future, so - we need to lock down in a hurry! */ - why = "large wall clock change"; - goto DONE; - } - else - { - /* The event went off, but it turns out that the user has not - yet been idle for long enough. So re-signal the event. - Be economical: if we should blank after 5 minutes, and the - user has been idle for 2 minutes, then set this timer to - go off in 3 minutes. - */ - if (polling_for_idleness) - schedule_wakeup_event (si, p->timeout - idle, p->debug_p); - } - } - break; - - case ClientMessage: - if (handle_clientmessage (si, &event.x_event, until_idle_p)) - { - why = "ClientMessage"; - goto DONE; - } - break; - - case CreateNotify: - /* A window has been created on the screen somewhere. If we're - supposed to scan all windows for events, prepare this window. */ - if (scanning_all_windows) - { - Window w = event.x_event.xcreatewindow.window; - start_notice_events_timer (si, w, p->debug_p); - } - break; - - case KeyPress: - case ButtonPress: - /* Ignore release events so that hitting ESC at the password dialog - doesn't result in the password dialog coming right back again when - the fucking release key is seen! */ - /* case KeyRelease:*/ - /* case ButtonRelease:*/ - case MotionNotify: - - if (p->debug_p) - { - Window root=0, window=0; - int x=-1, y=-1; - const char *type = 0; - if (event.x_event.xany.type == MotionNotify) - { - /*type = "MotionNotify";*/ - root = event.x_event.xmotion.root; - window = event.x_event.xmotion.window; - x = event.x_event.xmotion.x_root; - y = event.x_event.xmotion.y_root; - } - else if (event.x_event.xany.type == KeyPress) - { - type = "KeyPress"; - root = event.x_event.xkey.root; - window = event.x_event.xkey.window; - x = y = -1; - } - else if (event.x_event.xany.type == ButtonPress) - { - type = "ButtonPress"; - root = event.x_event.xkey.root; - window = event.x_event.xkey.window; - x = event.x_event.xmotion.x_root; - y = event.x_event.xmotion.y_root; - } - - if (type) - { - int i; - for (i = 0; i < si->nscreens; i++) - if (root == RootWindowOfScreen (si->screens[i].screen)) - break; - fprintf (stderr,"%s: %d: %s on 0x%lx", - blurb(), i, type, (unsigned long) window); - - /* Be careful never to do this unless in -debug mode, as - this could expose characters from the unlock password. */ - if (p->debug_p && event.x_event.xany.type == KeyPress) - { - KeySym keysym; - char c = 0; - XLookupString (&event.x_event.xkey, &c, 1, &keysym, 0); - fprintf (stderr, " (%s%s)", - (event.x_event.xkey.send_event ? "synthetic " : ""), - XKeysymToString (keysym)); - } - - if (x == -1) - fprintf (stderr, "\n"); - else - fprintf (stderr, " at %d,%d.\n", x, y); - } - } - - /* If any widgets want to handle this event, let them. */ - dispatch_event (si, &event.x_event); - - - /* If we got a MotionNotify event, figure out what screen it - was on and poll the mouse there: if the mouse hasn't moved - far enough to count as "real" motion, then ignore this - event. - */ - if (event.x_event.xany.type == MotionNotify) - { - int i; - for (i = 0; i < si->nscreens; i++) - if (event.x_event.xmotion.root == - RootWindowOfScreen (si->screens[i].screen)) - break; - if (i < si->nscreens) - { - if (!pointer_moved_p (&si->screens[i], False)) - continue; - } - } - - - /* We got a user event. - If we're waiting for the user to become active, this is it. - If we're waiting until the user becomes idle, reset the timers - (since now we have longer to wait.) - */ - if (!until_idle_p) - { - if (si->demoing_p && - (event.x_event.xany.type == MotionNotify || - event.x_event.xany.type == KeyRelease)) - /* When we're demoing a single hack, mouse motion doesn't - cause deactivation. Only clicks and keypresses do. */ - ; - else - { - /* If we're not demoing, then any activity causes deactivation. - */ - why = (event.x_event.xany.type == MotionNotify ?"mouse motion": - event.x_event.xany.type == KeyPress?"keyboard activity": - event.x_event.xany.type == ButtonPress ? "mouse click" : - "unknown user activity"); - goto DONE; - } - } - else - reset_timers (si); - - break; - - case PropertyNotify: - - /* Starting in late 2014, GNOME programs don't actually select for - or receive KeyPress events: they do it behind the scenes through - some kind of Input Method magic, even when running in an en_US - locale. However, those applications *do* update the WM_USER_TIME - property on their own windows every time they recieve a secret - KeyPress, so we must *also* monitor that property on every - window, and treat changes to it as identical to KeyPress. - - _NET_WM_USER_TIME is documented (such as it is) here: - - http://standards.freedesktop.org/wm-spec/latest/ar01s05.html - #idm139870829932528 - - Specifically: - - "Contains the XServer time at which last user activity in this - window took place. [...] A client [...] might, for example, - use the timestamp of the last KeyPress or ButtonPress event." - - As of early 2016, KDE4 does something really stupid, though: some - hidden power management thing reduces the display brightness 150 - seconds after the screen is blanked -- and sets a WM_USER_TIME - property on a hidden "kded4" window whose time is in the distant - past (the time at which the X server launched). - - So we ignore any WM_USER_TIME whose timestamp is more than a - couple seconds old. - */ - if (event.x_event.xproperty.state == PropertyNewValue && - event.x_event.xproperty.atom == XA_NET_WM_USER_TIME) - { - int threshold = 2; /* seconds */ - Bool bogus_p = True; - Window w = event.x_event.xproperty.window; - - Atom type; - int format; - unsigned long nitems, bytesafter; - unsigned char *data = 0; - Cardinal user_time = 0; - XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler); - - if (XGetWindowProperty (si->dpy, w, - XA_NET_WM_USER_TIME, 0L, 1L, False, - XA_CARDINAL, &type, &format, &nitems, - &bytesafter, &data) - == Success && - data && - type == XA_CARDINAL && - format == 32 && - nitems == 1) - { - long diff; - user_time = ((Cardinal *) data)[0]; - diff = event.x_event.xproperty.time - user_time; - if (diff >= 0 && diff < threshold) - bogus_p = False; - } - - if (data) XFree (data); - - why = "WM_USER_TIME"; - - if (p->debug_p) - { - XWindowAttributes xgwa; - int i; - - XGetWindowAttributes (si->dpy, w, &xgwa); - for (i = 0; i < si->nscreens; i++) - if (xgwa.root == RootWindowOfScreen (si->screens[i].screen)) - break; - fprintf (stderr,"%s: %d: %s = %ld%s on 0x%lx\n", - blurb(), i, why, (unsigned long) user_time, - (bogus_p ? " (bad)" : ""), - (unsigned long) w); - } - - XSync (si->dpy, False); - XSetErrorHandler (old_handler); - - if (bogus_p) - break; - else if (until_idle_p) - reset_timers (si); - else - goto DONE; - } - break; - - default: - -#ifdef HAVE_MIT_SAVER_EXTENSION - if (event.x_event.type == si->mit_saver_ext_event_number) - { - /* This event's number is that of the MIT-SCREEN-SAVER server - extension. This extension has one event number, and the event - itself contains sub-codes that say what kind of event it was - (an "idle" or "not-idle" event.) - */ - if (event.sevent.state == ScreenSaverOn) - { - int i = 0; - if (p->verbose_p) - fprintf (stderr, "%s: MIT ScreenSaverOn event received.\n", - blurb()); - - /* Get the "real" server window(s) out of the way as soon - as possible. */ - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (ssi->server_mit_saver_window && - window_exists_p (si->dpy, - ssi->server_mit_saver_window)) - XUnmapWindow (si->dpy, ssi->server_mit_saver_window); - } - - if (event.sevent.kind != ScreenSaverExternal) - { - fprintf (stderr, - "%s: ScreenSaverOn event wasn't of type External!\n", - blurb()); - } - - if (until_idle_p) - { - why = "MIT ScreenSaverOn"; - goto DONE; - } - } - else if (event.sevent.state == ScreenSaverOff) - { - if (p->verbose_p) - fprintf (stderr, "%s: MIT ScreenSaverOff event received.\n", - blurb()); - if (!until_idle_p) - { - why = "MIT ScreenSaverOff"; - goto DONE; - } - } - else - fprintf (stderr, - "%s: unknown MIT-SCREEN-SAVER event %d received!\n", - blurb(), event.sevent.state); - } - else - -#endif /* HAVE_MIT_SAVER_EXTENSION */ - - -#ifdef HAVE_SGI_SAVER_EXTENSION - if (event.x_event.type == (si->sgi_saver_ext_event_number + ScreenSaverStart)) - { - /* The SGI SCREEN_SAVER server extension has two event numbers, - and this event matches the "idle" event. */ - if (p->verbose_p) - fprintf (stderr, "%s: SGI ScreenSaverStart event received.\n", - blurb()); - - if (until_idle_p) - { - why = "SGI ScreenSaverStart"; - goto DONE; - } - } - else if (event.x_event.type == (si->sgi_saver_ext_event_number + - ScreenSaverEnd)) - { - /* The SGI SCREEN_SAVER server extension has two event numbers, - and this event matches the "idle" event. */ - if (p->verbose_p) - fprintf (stderr, "%s: SGI ScreenSaverEnd event received.\n", - blurb()); - if (!until_idle_p) - { - why = "SGI ScreenSaverEnd"; - goto DONE; - } - } - else -#endif /* HAVE_SGI_SAVER_EXTENSION */ - -#ifdef HAVE_XINPUT - /* If we got a MotionNotify event, check to see if the mouse has - moved far enough to count as "real" motion, if not, then ignore - this event. - */ - if ((si->num_xinput_devices > 0) && - (event.x_event.type == si->xinput_DeviceMotionNotify)) - { - XDeviceMotionEvent *dme = (XDeviceMotionEvent *) &event; - poll_mouse_data *last_poll_mouse = NULL; - int d; - - for (d = 0; d < si->num_xinput_devices; d++) - { - if (si->xinput_devices[d].device->device_id == dme->deviceid) - { - last_poll_mouse = &(si->xinput_devices[d].last_poll_mouse); - break; - } - } - - if (last_poll_mouse) - { - poll_mouse_data this_poll_mouse; - this_poll_mouse.root_x = dme->x_root; - this_poll_mouse.root_y = dme->y_root; - this_poll_mouse.child = dme->subwindow; - this_poll_mouse.mask = dme->device_state; - this_poll_mouse.time = dme->time / 1000; /* milliseconds */ - - if (!device_pointer_moved_p (si, last_poll_mouse, - &this_poll_mouse, False, - "device", dme->deviceid)) - continue; - } - else if (p->debug_p) - fprintf (stderr, - "%s: received MotionNotify from unknown device %d\n", - blurb(), (int) dme->deviceid); - } - - if ((!until_idle_p) && - (si->num_xinput_devices > 0) && - (event.x_event.type == si->xinput_DeviceMotionNotify || - event.x_event.type == si->xinput_DeviceButtonPress)) - /* Ignore DeviceButtonRelease, see ButtonRelease comment above. */ - { - - dispatch_event (si, &event.x_event); - if (si->demoing_p && - event.x_event.type == si->xinput_DeviceMotionNotify) - /* When we're demoing a single hack, mouse motion doesn't - cause deactivation. Only clicks and keypresses do. */ - ; - else - /* If we're not demoing, then any activity causes deactivation. - */ - { - why = (event.x_event.type == si->xinput_DeviceMotionNotify - ? "XI mouse motion" : - event.x_event.type == si->xinput_DeviceButtonPress - ? "XI mouse click" : "unknown XINPUT event"); - goto DONE; - } - } - else -#endif /* HAVE_XINPUT */ - -#ifdef HAVE_RANDR - if (si->using_randr_extension && - (event.x_event.type == - (si->randr_event_number + RRScreenChangeNotify))) - { - /* The Resize and Rotate extension sends an event when the - size, rotation, or refresh rate of any screen has changed. - */ - if (p->verbose_p) - { - /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */ - int screen = XRRRootToScreen (si->dpy, event.xrr_event.window); - fprintf (stderr, "%s: %d: screen change event received\n", - blurb(), screen); - } - -# ifdef RRScreenChangeNotifyMask - /* Inform Xlib that it's ok to update its data structures. */ - XRRUpdateConfiguration (&event.x_event); /* Xrandr.h 1.9, 2002/09/29 */ -# endif /* RRScreenChangeNotifyMask */ - - /* Resize the existing xscreensaver windows and cached ssi data. */ - if (update_screen_layout (si)) - { - if (p->verbose_p) - { - fprintf (stderr, "%s: new layout:\n", blurb()); - describe_monitor_layout (si); - } - resize_screensaver_window (si); - } - } - else -#endif /* HAVE_RANDR */ - - /* Just some random event. Let the Widgets handle it, if desired. */ - dispatch_event (si, &event.x_event); - } - } - DONE: - - if (p->verbose_p) - { - if (! why) why = "unknown reason"; - fprintf (stderr, "%s: %s (%s)\n", blurb(), - (until_idle_p ? "user is idle" : "user is active"), - why); - } - - /* If there's a user event on the queue, swallow it. - If we're using a server extension, and the user becomes active, we - get the extension event before the user event -- so the keypress or - motion or whatever is still on the queue. This makes "unfade" not - work, because it sees that event, and bugs out. (This problem - doesn't exhibit itself without an extension, because in that case, - there's only one event generated by user activity, not two.) - */ - if (!until_idle_p && si->locked_p) - swallow_unlock_typeahead_events (si, &event.x_event); - else - while (XCheckMaskEvent (si->dpy, - (KeyPressMask|ButtonPressMask|PointerMotionMask), - &event.x_event)) - ; - - - if (si->check_pointer_timer_id) - { - XtRemoveTimeOut (si->check_pointer_timer_id); - si->check_pointer_timer_id = 0; - } - if (si->timer_id) - { - XtRemoveTimeOut (si->timer_id); - si->timer_id = 0; - } - - if (until_idle_p && si->cycle_id) /* no cycle timer when inactive */ - abort (); -} - - - -/* Some crap for dealing with /proc/interrupts. - - On Linux systems, it's possible to see the hardware interrupt count - associated with the keyboard. We can therefore use that as another method - of detecting idleness. - - Why is it a good idea to do this? Because it lets us detect keyboard - activity that is not associated with X events. For example, if the user - has switched to another virtual console, it's good for xscreensaver to not - be running graphics hacks on the (non-visible) X display. The common - complaint that checking /proc/interrupts addresses is that the user is - playing Quake on a non-X console, and the GL hacks are perceptibly slowing - the game... - - This is tricky for a number of reasons. - - * First, we must be sure to only do this when running on an X server that - is on the local machine (because otherwise, we'd be reacting to the - wrong keyboard.) The way we do this is by noting that the $DISPLAY is - pointing to display 0 on the local machine. It *could* be that display - 1 is also on the local machine (e.g., two X servers, each on a different - virtual-terminal) but it's also possible that screen 1 is an X terminal, - using this machine as the host. So we can't take that chance. - - * Second, one can only access these interrupt numbers in a completely - and utterly brain-damaged way. You would think that one would use an - ioctl for this. But no. The ONLY way to get this information is to - open the pseudo-file /proc/interrupts AS A FILE, and read the numbers - out of it TEXTUALLY. Because this is Unix, and all the world's a file, - and the only real data type is the short-line sequence of ASCII bytes. - - Now it's all well and good that the /proc/interrupts pseudo-file - exists; that's a clever idea, and a useful API for things that are - already textually oriented, like shell scripts, and users doing - interactive debugging sessions. But to make a *C PROGRAM* open a file - and parse the textual representation of integers out of it is just - insane. - - * Third, you can't just hold the file open, and fseek() back to the - beginning to get updated data! If you do that, the data never changes. - And I don't want to call open() every five seconds, because I don't want - to risk going to disk for any inodes. It turns out that if you dup() - it early, then each copy gets fresh data, so we can get around that in - this way (but for how many releases, one might wonder?) - - * Fourth, the format of the output of the /proc/interrupts file is - undocumented, and has changed several times already! In Linux 2.0.33, - even on a multiprocessor machine, it looks like this: - - 0: 309453991 timer - 1: 4771729 keyboard - - but in Linux 2.2 and 2.4 kernels with MP machines, it looks like this: - - CPU0 CPU1 - 0: 1671450 1672618 IO-APIC-edge timer - 1: 13037 13495 IO-APIC-edge keyboard - - and in Linux 2.6, it's gotten even goofier: now there are two lines - labelled "i8042". One of them is the keyboard, and one of them is - the PS/2 mouse -- and of course, you can't tell them apart, except - by wiggling the mouse and noting which one changes: - - CPU0 CPU1 - 1: 32051 30864 IO-APIC-edge i8042 - 12: 476577 479913 IO-APIC-edge i8042 - - Joy! So how are we expected to parse that? Well, this code doesn't - parse it: it saves the first line with the string "keyboard" (or - "i8042") in it, and does a string-comparison to note when it has - changed. If there are two "i8042" lines, we assume the first is - the keyboard and the second is the mouse (doesn't matter which is - which, really, as long as we don't compare them against each other.) - - Thanks to Nat Friedman for figuring out most of this crap. - - Note that if you have a serial or USB mouse, or a USB keyboard, it won't - detect it. That's because there's no way to tell the difference between a - serial mouse and a general serial port, and all USB devices look the same - from here. It would be somewhat unfortunate to have the screensaver turn - off when the modem on COM1 burped, or when a USB disk was accessed. - */ - - -#ifdef HAVE_PROC_INTERRUPTS - -#define PROC_INTERRUPTS "/proc/interrupts" - -Bool -query_proc_interrupts_available (saver_info *si, const char **why) -{ - /* We can use /proc/interrupts if $DISPLAY points to :0, and if the - "/proc/interrupts" file exists and is readable. - */ - FILE *f; - if (why) *why = 0; - - if (!display_is_on_console_p (si)) - { - if (why) *why = "not on primary console"; - return False; - } - - f = fopen (PROC_INTERRUPTS, "r"); - if (!f) - { - if (why) *why = "does not exist"; - return False; - } - - fclose (f); - return True; -} - - -static Bool -proc_interrupts_activity_p (saver_info *si) -{ - static FILE *f0 = 0; - FILE *f1 = 0; - int fd; - static char last_kbd_line[255] = { 0, }; - static char last_ptr_line[255] = { 0, }; - char new_line[sizeof(last_kbd_line)]; - Bool checked_kbd = False, kbd_changed = False; - Bool checked_ptr = False, ptr_changed = False; - int i8042_count = 0; - - if (!f0) - { - /* First time -- open the file. */ - f0 = fopen (PROC_INTERRUPTS, "r"); - if (!f0) - { - char buf[255]; - sprintf(buf, "%s: error opening %s", blurb(), PROC_INTERRUPTS); - perror (buf); - goto FAIL; - } - -# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC) - /* Close this fd upon exec instead of inheriting / leaking it. */ - if (fcntl (fileno (f0), F_SETFD, FD_CLOEXEC) != 0) - perror ("fcntl: CLOEXEC:"); -# endif - } - - if (f0 == (FILE *) -1) /* means we got an error initializing. */ - return False; - - fd = dup (fileno (f0)); - if (fd < 0) - { - char buf[255]; - sprintf(buf, "%s: could not dup() the %s fd", blurb(), PROC_INTERRUPTS); - perror (buf); - goto FAIL; - } - - f1 = fdopen (fd, "r"); - if (!f1) - { - char buf[255]; - sprintf(buf, "%s: could not fdopen() the %s fd", blurb(), - PROC_INTERRUPTS); - perror (buf); - goto FAIL; - } - - /* Actually, I'm unclear on why this fseek() is necessary, given the timing - of the dup() above, but it is. */ - if (fseek (f1, 0, SEEK_SET) != 0) - { - char buf[255]; - sprintf(buf, "%s: error rewinding %s", blurb(), PROC_INTERRUPTS); - perror (buf); - goto FAIL; - } - - /* Now read through the pseudo-file until we find the "keyboard", - "PS/2 mouse", or "i8042" lines. */ - - while (fgets (new_line, sizeof(new_line)-1, f1)) - { - Bool i8042_p = !!strstr (new_line, "i8042"); - if (i8042_p) i8042_count++; - - if (strchr (new_line, ',')) - { - /* Ignore any line that has a comma on it: this is because - a setup like this: - - 12: 930935 XT-PIC usb-uhci, PS/2 Mouse - - is really bad news. It *looks* like we can note mouse - activity from that line, but really, that interrupt gets - fired any time any USB device has activity! So we have - to ignore any shared IRQs. - */ - } - else if (!checked_kbd && - (strstr (new_line, "keyboard") || - (i8042_p && i8042_count == 1))) - { - /* Assume the keyboard interrupt is the line that says "keyboard", - or the *first* line that says "i8042". - */ - kbd_changed = (*last_kbd_line && !!strcmp (new_line, last_kbd_line)); - strcpy (last_kbd_line, new_line); - checked_kbd = True; - } - else if (!checked_ptr && - (strstr (new_line, "PS/2 Mouse") || - (i8042_p && i8042_count == 2))) - { - /* Assume the mouse interrupt is the line that says "PS/2 mouse", - or the *second* line that says "i8042". - */ - ptr_changed = (*last_ptr_line && !!strcmp (new_line, last_ptr_line)); - strcpy (last_ptr_line, new_line); - checked_ptr = True; - } - - if (checked_kbd && checked_ptr) - break; - } - - if (checked_kbd || checked_ptr) - { - fclose (f1); - - if (si->prefs.debug_p && (kbd_changed || ptr_changed)) - fprintf (stderr, "%s: /proc/interrupts activity: %s\n", - blurb(), - ((kbd_changed && ptr_changed) ? "mouse and kbd" : - kbd_changed ? "kbd" : - ptr_changed ? "mouse" : "ERR")); - - return (kbd_changed || ptr_changed); - } - - - /* If we got here, we didn't find either a "keyboard" or a "PS/2 Mouse" - line in the file at all. */ - fprintf (stderr, "%s: no keyboard or mouse data in %s?\n", - blurb(), PROC_INTERRUPTS); - - FAIL: - if (f1) - fclose (f1); - - if (f0 && f0 != (FILE *) -1) - fclose (f0); - - f0 = (FILE *) -1; - return False; -} - -#endif /* HAVE_PROC_INTERRUPTS */ - - -/* This timer goes off every few minutes, whether the user is idle or not, - to try and clean up anything that has gone wrong. - - It calls disable_builtin_screensaver() so that if xset has been used, - or some other program (like xlock) has messed with the XSetScreenSaver() - settings, they will be set back to sensible values (if a server extension - is in use, messing with xlock can cause xscreensaver to never get a wakeup - event, and could cause monitor power-saving to occur, and all manner of - heinousness.) - - If the screen is currently blanked, it raises the window, in case some - other window has been mapped on top of it. - - If the screen is currently blanked, and there is no hack running, it - clears the window, in case there is an error message printed on it (we - don't want the error message to burn in.) - */ - -static void -watchdog_timer (XtPointer closure, XtIntervalId *id) -{ - saver_info *si = (saver_info *) closure; - saver_preferences *p = &si->prefs; - - disable_builtin_screensaver (si, False); - - /* If the DPMS settings on the server have changed, change them back to - what ~/.xscreensaver says they should be. */ - sync_server_dpms_settings (si->dpy, - (p->dpms_enabled_p && - p->mode != DONT_BLANK), - p->dpms_quickoff_p, - p->dpms_standby / 1000, - p->dpms_suspend / 1000, - p->dpms_off / 1000, - False); - - if (si->screen_blanked_p) - { - Bool running_p = screenhack_running_p (si); - - if (si->dbox_up_p) - { - if (si->prefs.debug_p) - fprintf (stderr, "%s: dialog box is up: not raising screen.\n", - blurb()); - } - else - { - if (si->prefs.debug_p) - fprintf (stderr, "%s: watchdog timer raising %sscreen.\n", - blurb(), (running_p ? "" : "and clearing ")); - - raise_window (si, True, True, running_p); - } - - if (!p->dpms_full_throttle_p && screenhack_running_p (si) && - !monitor_powered_on_p (si)) - { - int i; - if (si->prefs.verbose_p) - fprintf (stderr, - "%s: X says monitor has powered down; " - "killing running hacks.\n", blurb()); - for (i = 0; i < si->nscreens; i++) - kill_screenhack (&si->screens[i]); - } - - /* Re-schedule this timer. The watchdog timer defaults to a bit less - than the hack cycle period, but is never longer than one hour. - */ - si->watchdog_id = 0; - reset_watchdog_timer (si, True); - } -} - - -void -reset_watchdog_timer (saver_info *si, Bool on_p) -{ - saver_preferences *p = &si->prefs; - - if (si->watchdog_id) - { - XtRemoveTimeOut (si->watchdog_id); - si->watchdog_id = 0; - } - - if (on_p && p->watchdog_timeout) - { - si->watchdog_id = XtAppAddTimeOut (si->app, p->watchdog_timeout, - watchdog_timer, (XtPointer) si); - - if (p->debug_p) - fprintf (stderr, "%s: restarting watchdog_timer (%ld, %ld)\n", - blurb(), p->watchdog_timeout, si->watchdog_id); - } -} - - -/* It's possible that a race condition could have led to the saver - window being unexpectedly still mapped. This can happen like so: - - - screen is blanked - - hack is launched - - that hack tries to grab a screen image (it does this by - first unmapping the saver window, then remapping it.) - - hack unmaps window - - hack waits - - user becomes active - - hack re-maps window (*) - - driver kills subprocess - - driver unmaps window (**) - - The race is that (*) might have been sent to the server before - the client process was killed, but, due to scheduling randomness, - might not have been received by the server until after (**). - In other words, (*) and (**) might happen out of order, meaning - the driver will unmap the window, and then after that, the - recently-dead client will re-map it. This leaves the user - locked out (it looks like a desktop, but it's not!) - - To avoid this: after un-blanking the screen, we launch a timer - that wakes up once a second for ten seconds, and makes damned - sure that the window is still unmapped. - */ - -void -de_race_timer (XtPointer closure, XtIntervalId *id) -{ - saver_info *si = (saver_info *) closure; - saver_preferences *p = &si->prefs; - int secs = 1; - - if (id == 0) /* if id is 0, this is the initialization call. */ - { - si->de_race_ticks = 10; - if (p->verbose_p) - fprintf (stderr, "%s: starting de-race timer (%d seconds.)\n", - blurb(), si->de_race_ticks); - } - else - { - int i; - XSync (si->dpy, False); - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - Window w = ssi->screensaver_window; - XWindowAttributes xgwa; - XGetWindowAttributes (si->dpy, w, &xgwa); - if (xgwa.map_state != IsUnmapped) - { - if (p->verbose_p) - fprintf (stderr, - "%s: %d: client race! emergency unmap 0x%lx.\n", - blurb(), i, (unsigned long) w); - XUnmapWindow (si->dpy, w); - } - else if (p->debug_p) - fprintf (stderr, "%s: %d: (de-race of 0x%lx is cool.)\n", - blurb(), i, (unsigned long) w); - } - XSync (si->dpy, False); - - si->de_race_ticks--; - } - - if (id && *id == si->de_race_id) - si->de_race_id = 0; - - if (si->de_race_id) abort(); - - if (si->de_race_ticks <= 0) - { - si->de_race_id = 0; - if (p->verbose_p) - fprintf (stderr, "%s: de-race completed.\n", blurb()); - } - else - { - si->de_race_id = XtAppAddTimeOut (si->app, secs * 1000, - de_race_timer, closure); - } -} diff --git a/driver/types.h b/driver/types.h index 705750b..2042aad 100644 --- a/driver/types.h +++ b/driver/types.h @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1993-2020 Jamie Zawinski +/* xscreensaver, Copyright © 1993-2021 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 @@ -14,15 +14,6 @@ typedef struct saver_info saver_info; -typedef enum { - ul_read, /* reading input or ready to do so */ - ul_success, /* auth success, unlock */ - ul_fail, /* auth fail */ - ul_cancel, /* user cancelled auth (pw_cancel or pw_null) */ - ul_time, /* timed out */ - ul_finished /* user pressed enter */ -} unlock_state; - typedef struct screenhack screenhack; struct screenhack { Bool enabled_p; @@ -39,44 +30,13 @@ typedef enum { TEXT_DATE, TEXT_LITERAL, TEXT_FILE, TEXT_PROGRAM, TEXT_URL } text_mode; -struct auth_message; -struct auth_response; - -typedef int (*auth_conv_cb_t) ( - int num_msg, - const struct auth_message *msg, - struct auth_response **resp, - saver_info *si); - typedef struct saver_preferences saver_preferences; typedef struct saver_screen_info saver_screen_info; -typedef struct passwd_dialog_data passwd_dialog_data; -typedef struct splash_dialog_data splash_dialog_data; -typedef struct _monitor monitor; - -typedef struct poll_mouse_data poll_mouse_data; -struct poll_mouse_data { - int root_x; - int root_y; - Window child; - unsigned int mask; - time_t time; -}; - -#ifdef HAVE_XINPUT -/* XInputExtension device support */ -#include -typedef struct xinput_dev_info xinput_dev_info; -struct xinput_dev_info { - XDevice *device; - XEventClass press, release, valuator; - poll_mouse_data last_poll_mouse; -}; -#endif /* This structure holds all the user-specified parameters, read from the command line, the resource database, or entered through a dialog box. + It is used by xscreensaver-gfx and xscreensaver-settings; */ struct saver_preferences { @@ -89,23 +49,20 @@ struct saver_preferences { wrote it. */ Bool verbose_p; /* whether to print out lots of status info */ - Bool timestamp_p; /* whether to mark messages with a timestamp */ - Bool capture_stderr_p; /* whether to redirect stdout/stderr */ Bool ignore_uninstalled_p; /* whether to avoid displaying or complaining about hacks that are not on $PATH */ Bool debug_p; /* pay no mind to the man behind the curtain */ - Bool xsync_p; /* whether XSynchronize has been called */ + Bool xsync_p; /* whether to XSynchronize */ Bool lock_p; /* whether to lock as well as save */ Bool fade_p; /* whether to fade to black, if possible */ Bool unfade_p; /* whether to fade from black, if possible */ Time fade_seconds; /* how long that should take */ - int fade_ticks; /* how many ticks should be used */ Bool splash_p; /* whether to do a splash screen at startup */ Bool install_cmap_p; /* whether we should use our own colormap - when using the screen's default visual. */ + when using the screen's default visual */ # ifdef QUAD_MODE Bool quad_p; /* whether to run four savers per monitor */ @@ -118,31 +75,26 @@ struct saver_preferences { int selected_hack; /* in one_hack mode, this is the one */ int nice_inferior; /* nice value for subprocs */ - int inferior_memory_limit; /* setrlimit(LIMIT_AS) value for subprocs */ - Time initial_delay; /* how long to sleep after launch */ Time splash_duration; /* how long the splash screen stays up */ Time timeout; /* how much idle time before activation */ Time lock_timeout; /* how long after activation locking starts */ Time cycle; /* how long each hack should run */ - Time passwd_timeout; /* how much time before pw dialog goes down */ - Time pointer_timeout; /* how often to check mouse position */ - Time notice_events_timeout; /* how long after window creation to select */ + Time passwd_timeout; /* how long before pw dialog goes down */ Time watchdog_timeout; /* how often to re-raise and re-blank screen */ int pointer_hysteresis; /* mouse motions less than N/sec are ignored */ - Bool dpms_enabled_p; /* Whether to power down the monitor */ - Bool dpms_quickoff_p; /* Whether to power down monitor immediately + Bool dpms_enabled_p; /* whether to power down the monitor */ + Bool dpms_quickoff_p; /* whether to power down monitor immediately in "Blank Only" mode */ Time dpms_standby; /* how long until monitor goes black */ Time dpms_suspend; /* how long until monitor power-saves */ Time dpms_off; /* how long until monitor powers down */ - Bool dpms_full_throttle_p; /* Never kill or suspend hack when screen is off */ Bool grab_desktop_p; /* These are not used by "xscreensaver" */ Bool grab_video_p; /* itself: they are used by the external */ Bool random_image_p; /* "xscreensaver-getimage" program, and set */ - char *image_directory; /* by the "xscreensaver-demo" configurator. */ + char *image_directory; /* by "xscreensaver-settings". */ text_mode tmode; /* How we generate text to display. */ char *text_literal; /* used when tmode is TEXT_LITERAL. */ @@ -150,26 +102,18 @@ struct saver_preferences { char *text_program; /* used when tmode is TEXT_PROGRAM. */ char *text_url; /* used when tmode is TEXT_URL. */ - Bool use_xidle_extension; /* which extension to use, if possible */ - Bool use_mit_saver_extension; - Bool use_sgi_saver_extension; - Bool use_proc_interrupts; - Bool use_xinput_extension; - - Bool getviewport_full_of_lies_p; /* XFree86 bug #421 */ - char *shell; /* where to find /bin/sh */ char *demo_command; /* How to enter demo mode. */ - char *prefs_command; /* How to edit preferences. */ char *help_url; /* Where the help document resides. */ char *load_url_command; /* How one loads URLs. */ char *new_login_command; /* Command for the "New Login" button. */ char *external_ungrab_command; /* Command that's supposed to make sure - nobody is holding the keyboard or - mouse grab. Called with params - "pre" and "post" before and after - trying to get the grab. */ + nobody is holding the keyboard or + mouse grab. Called with params + "pre" and "post" before and after + trying to get the grab. */ + char *dialog_theme; /* Color scheme on the unlock dialog */ int auth_warning_slack; /* Don't warn about login failures if they all happen within this many seconds of @@ -178,188 +122,46 @@ struct saver_preferences { /* This structure holds all the data that applies to the program as a whole, or to the non-screen-specific parts of the display connection. - - The saver_preferences structure (prefs.h) holds all the user-specified - parameters, read from the command line, the resource database, or entered - through a dialog box. + It is used only by xscreensaver-gfx. */ struct saver_info { + + XtAppContext app; + Display *dpy; + char *version; saver_preferences prefs; int nscreens; int ssi_count; saver_screen_info *screens; - saver_screen_info *default_screen; /* ...on which dialogs will appear. */ - monitor **monitor_layout; /* private to screens.c */ + struct _monitor **monitor_layout; /* private to screens.c */ Visual **best_gl_visuals; /* visuals for GL hacks on screen N */ + void *fade_state; /* fade.c private data */ - /* ======================================================================= - global connection info - ======================================================================= */ - - XtAppContext app; - Display *dpy; - - /* ======================================================================= - server extension info - ======================================================================= */ - - Bool using_xidle_extension; /* which extension is being used. */ - Bool using_mit_saver_extension; /* Note that `p->use_*' is the *request*, */ - Bool using_sgi_saver_extension; /* and `si->using_*' is the *reality*. */ - Bool using_proc_interrupts; - -# ifdef HAVE_MIT_SAVER_EXTENSION - int mit_saver_ext_event_number; - int mit_saver_ext_error_number; -# endif -# ifdef HAVE_SGI_SAVER_EXTENSION - int sgi_saver_ext_event_number; - int sgi_saver_ext_error_number; -# endif # ifdef HAVE_RANDR int randr_event_number; int randr_error_number; Bool using_randr_extension; # endif - Bool using_xinput_extension; /* Note that `p->use_*' is the *request*, */ - /* and `si->using_*' is the *reality*. */ -#ifdef HAVE_XINPUT - int xinput_ext_event_number; /* may not be used */ - int xinput_ext_error_number; - int xinput_DeviceButtonPress; /* Extension device event codes. */ - int xinput_DeviceButtonRelease; /* Assigned by server at runtime */ - int xinput_DeviceMotionNotify; - xinput_dev_info *xinput_devices; - int num_xinput_devices; -# endif - - /* ======================================================================= - blanking - ======================================================================= */ - - Bool screen_blanked_p; /* Whether the saver is currently active. */ - Window mouse_grab_window; /* Window holding our mouse grab */ - Window keyboard_grab_window; /* Window holding our keyboard grab */ - int mouse_grab_screen; /* The screen number the mouse grab is on */ - int keyboard_grab_screen; /* The screen number the keyboard grab is on */ - Bool fading_possible_p; /* Whether fading to/from black is possible. */ - Bool throttled_p; /* Whether we should temporarily just blank - the screen, not run hacks. (Deprecated: - users should use "xset dpms force off" - instead.) */ - time_t blank_time; /* The time at which the screen was blanked - (if currently blanked) or unblanked (if - not blanked.) */ - - - /* ======================================================================= - locking and runtime privileges - ======================================================================= */ - - Bool locked_p; /* Whether the screen is currently locked. */ - Bool dbox_up_p; /* Whether the demo-mode or passwd dialogs - are currently visible */ - - Bool locking_disabled_p; /* Sometimes locking is impossible. */ - char *nolock_reason; /* This is why. */ - - char *orig_uid; /* What uid/gid we had at startup, before - discarding privileges. */ - char *uid_message; /* Any diagnostics from our attempt to - discard privileges (printed only in - -verbose mode.) */ - Bool dangerous_uid_p; /* Set to true if we're running as a user id - which is known to not be a normal, non- - privileged user. */ - - Window passwd_dialog; /* The password dialog, if it's up. */ - passwd_dialog_data *pw_data; /* Other info necessary to draw it. */ - - int unlock_failures; /* Counts failed login attempts while the - screen is locked. */ - time_t unlock_failure_time; /* Time of first failed login attempt. */ - time_t unlock_dismiss_time; /* Time lock dialog most recently dismissed. */ - - char *unlock_typeahead; /* If the screen is locked, and the user types - a character, we assume that it is the first - character of the password. It's stored here - for the password dialog to use to populate - itself. */ - - char *user; /* The user whose session is locked. */ - char *cached_passwd; /* Cached password, used to avoid multiple - prompts for password-only auth mechanisms.*/ - unlock_state unlock_state; - - auth_conv_cb_t unlock_cb; /* The function used to prompt for creds. */ - void (*auth_finished_cb) (saver_info *si); - /* Called when authentication has finished, - regardless of success or failure. - May be NULL. */ - - - /* ======================================================================= - demoing - ======================================================================= */ - Bool demoing_p; /* Whether we are demoing a single hack (without UI.) */ - - Window splash_dialog; /* The splash dialog, if its up. */ - splash_dialog_data *sp_data; /* Other info necessary to draw it. */ - - - /* ======================================================================= - timers - ======================================================================= */ - - XtIntervalId lock_id; /* Timer to implement `prefs.lock_timeout' */ - XtIntervalId cycle_id; /* Timer to implement `prefs.cycle' */ - XtIntervalId timer_id; /* Timer to implement `prefs.timeout' */ + Bool emergency_p; /* Restarted because of a crash */ XtIntervalId watchdog_id; /* Timer to implement `prefs.watchdog */ - XtIntervalId check_pointer_timer_id; /* `prefs.pointer_timeout' */ - - XtIntervalId de_race_id; /* Timer to make sure screen un-blanks */ - int de_race_ticks; - - time_t last_activity_time; /* Used only when no server exts. */ - time_t last_wall_clock_time; /* Used to detect laptop suspend. */ - saver_screen_info *last_activity_screen; - - Bool emergency_lock_p; /* Set when the wall clock has jumped - (presumably due to laptop suspend) and we - need to lock down right away instead of - waiting for the lock timer to go off. */ - - - /* ======================================================================= - remote control - ======================================================================= */ int selection_mode; /* Set to -1 if the NEXT ClientMessage has just been received; set to -2 if PREV has just been received; set to N if SELECT or DEMO N has been received. (This is kind of nasty.) */ - - /* ======================================================================= - subprocs - ======================================================================= */ - - XtIntervalId stderr_popup_timer; - -# ifdef HAVE_LIBSYSTEMD - pid_t systemd_pid; -# endif }; + /* This structure holds all the data that applies to the screen-specific parts of the display connection; if the display has multiple screens, there will - be one of these for each screen. - */ + be one of these for each screen. It is used only by xscreensaver-gfx. +*/ struct saver_screen_info { saver_info *global; @@ -376,17 +178,10 @@ struct saver_screen_info { Bool real_screen_p; /* This will be true of exactly one ssi per X screen. */ - Widget toplevel_shell; - - /* ======================================================================= - blanking - ======================================================================= */ - Window screensaver_window; /* The window that will impersonate the root, - when the screensaver activates. Note that - the window stored here may change, as we - destroy and recreate it on different - visuals. */ + Window screensaver_window; /* The window on which hacks are drawn. This + window might be destroyed and re-created as + hacks cycle. */ Colormap cmap; /* The colormap that goes with the window. */ Bool install_cmap_p; /* Whether this screen should have its own colormap installed, for whichever of several @@ -398,55 +193,31 @@ struct saver_screen_info { Visual *default_visual; /* visual to use when none other specified */ - Window real_vroot; /* The original virtual-root window. */ - Window real_vroot_value; /* What was in the __SWM_VROOT property. */ - Cursor cursor; /* A blank cursor that goes with the real root window. */ unsigned long black_pixel; /* Black, allocated from `cmap'. */ - - int blank_vp_x, blank_vp_y; /* Where the virtual-scrolling viewport was - when the screen went blank. We need to - prevent the X server from letting the mouse - bump the edges to scroll while the screen - is locked, so we reset to this when it has - moved, and the lock dialog is up... */ - -# ifdef HAVE_MIT_SAVER_EXTENSION - Window server_mit_saver_window; -# endif + Window error_dialog; /* Error message about crashed savers */ - /* ======================================================================= - demoing - ======================================================================= */ - - Colormap demo_cmap; /* The colormap that goes with the dialogs: - this might be the same as `cmap' so care - must be taken not to free it while it's - still in use. */ - - /* ======================================================================= - timers - ======================================================================= */ + XtIntervalId cycle_id; /* Timer to implement `prefs.cycle' */ + time_t cycle_at; /* When cycle_id will fire */ + int current_hack; /* Index into `prefs.screenhacks' */ + pid_t pid; +}; - poll_mouse_data last_poll_mouse; /* Used only when no server exts. */ - /* ======================================================================= - subprocs - ======================================================================= */ +/* From dpms.c */ +extern void sync_server_dpms_settings (Display *, struct saver_preferences *); - int current_hack; /* Index into `prefs.screenhacks' */ - pid_t pid; - int stderr_text_x; - int stderr_text_y; - int stderr_line_height; - XFontStruct *stderr_font; - GC stderr_gc; - Window stderr_overlay_window; /* Used if the server has overlay planes */ - Colormap stderr_cmap; -}; +const char *init_file_name (void); +extern Bool init_file_changed_p (saver_preferences *); +extern void load_init_file (Display *, saver_preferences *); +extern int write_init_file (Display *, + saver_preferences *, const char *version_string, + Bool verbose_p); +extern Bool senescent_p (void); +char *make_hack_name (Display *, const char *shell_command); #endif /* __XSCREENSAVER_TYPES_H__ */ diff --git a/driver/vms-getpwnam.c b/driver/vms-getpwnam.c deleted file mode 100644 index ec0650c..0000000 --- a/driver/vms-getpwnam.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * getpwnam(name) - retrieves a UAF entry - * - * Author: Patrick L. Mahan - * Location: TGV, Inc - * Date: 15-Nov-1991 - * - * Purpose: Provides emulation for the UNIX getpwname routine. - * - * Modification History - * - * Date | Who | Version | Reason - * ------------+-----------+---------------+--------------------------- - * 15-Nov-1991 | PLM | 1.0 | First Write - */ - -#define PASSWDROUTINES - -#include -#include -#include -#include -#include -#include -#include "vms-pwd.h" - -struct uic { - unsigned short uid; - unsigned short gid; -}; - -#define TEST(ptr, str) { if (ptr == NULL) { \ - fprintf(stderr, "getpwnam: memory allocation failure for \"%s\"\n", \ - str); \ - return ((struct passwd *)(NULL)); \ - } } - -struct passwd *getpwnam(name) -char *name; -{ - int istatus; - int UserNameLen; - int UserOwnerLen; - int UserDeviceLen; - int UserDirLen; - static char UserName[13]; - static char UserOwner[32]; - static char UserDevice[32]; - static char UserDir[64]; - char *cptr, *sptr; - unsigned long int UserPwd[2]; - unsigned short int UserSalt; - unsigned long int UserEncrypt; - struct uic UicValue; - struct passwd *entry; - - struct dsc$descriptor_s VMSNAME = - {strlen(name), DSC$K_DTYPE_T, DSC$K_CLASS_S, name}; - - struct itmlist3 { - unsigned short int length; - unsigned short int item; - unsigned long int addr; - unsigned long int retaddr; - } ItemList[] = { - {12, UAI$_USERNAME, (unsigned long)&UserName, (unsigned long)&UserNameLen}, - {8, UAI$_PWD, (unsigned long)&UserPwd, 0}, - {4, UAI$_UIC, (unsigned long)&UicValue, 0}, - {32, UAI$_OWNER, (unsigned long)&UserOwner, (unsigned long)&UserOwnerLen}, - {32, UAI$_DEFDEV, (unsigned long)&UserDevice, (unsigned long)&UserDeviceLen}, - {64, UAI$_DEFDIR, (unsigned long)&UserDir, (unsigned long)&UserDirLen}, - {2, UAI$_SALT, (unsigned long)&UserSalt, 0}, - {4, UAI$_ENCRYPT, (unsigned long)&UserEncrypt, 0}, - {0, 0, 0, 0} - }; - - UserNameLen = 0; - istatus = sys$getuai (0, 0, &VMSNAME, &ItemList, 0, 0, 0); - - if (!(istatus & 1)) { - fprintf (stderr, "getpwnam: unable to retrieve passwd entry for %s\n", - name); - fprintf (stderr, "getpwnam: vms error number is 0x%x\n", istatus); - return ((struct passwd *)NULL); - } - - entry = (struct passwd *) calloc (1, sizeof(struct passwd)); - TEST(entry, "PASSWD_ENTRY"); - - entry->pw_uid = UicValue.uid; - entry->pw_gid = UicValue.gid; - entry->pw_salt = UserSalt; - entry->pw_encrypt = UserEncrypt; - - sptr = UserName; - cptr = calloc (UserNameLen+1, sizeof(char)); - TEST(cptr, "USERNAME"); - strncpy (cptr, sptr, UserNameLen); - cptr[UserNameLen] = '\0'; - entry->pw_name = cptr; - - cptr = calloc(8, sizeof(char)); - TEST(cptr, "PASSWORD"); - memcpy(cptr, UserPwd, 8); - entry->pw_passwd = cptr; - - sptr = UserOwner; sptr++; - cptr = calloc ((int)UserOwner[0]+1, sizeof(char)); - TEST(cptr, "FULLNAME"); - strncpy (cptr, sptr, (int)UserOwner[0]); - cptr[(int)UserOwner[0]] = '\0'; - entry->pw_gecos = cptr; - - cptr = calloc ((int)UserDevice[0]+(int)UserDir[0]+1, sizeof(char)); - TEST(cptr, "HOME"); - sptr = UserDevice; sptr++; - strncpy (cptr, sptr, (int)UserDevice[0]); - sptr = UserDir; sptr++; - strncat (cptr, sptr, (int)UserDir[0]); - cptr[(int)UserDevice[0]+(int)UserDir[0]] = '\0'; - entry->pw_dir = cptr; - - cptr = calloc (strlen("SYS$SYSTEM:LOGINOUT.EXE")+1, sizeof(char)); - TEST(cptr,"SHELL"); - strcpy (cptr, "SYS$SYSTEM:LOGINOUT.EXE"); - entry->pw_shell = cptr; - - return (entry); -} diff --git a/driver/vms-hpwd.c b/driver/vms-hpwd.c deleted file mode 100644 index 707e3ea..0000000 --- a/driver/vms-hpwd.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * VAX/VMS Password hashing routines: - * - * uses the System Service SYS$HASH_PASSWORD - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - * - */ - -#include -#include -#include -#include -/* - * Hashing routine - */ -hash_vms_password(output_buf,input_buf,input_length,username,encryption_type,salt) -char *output_buf; -char *input_buf; -int input_length; -char *username; -int encryption_type; -unsigned short salt; -{ - struct dsc$descriptor_s password; - struct dsc$descriptor_s user; - - /* - * Check the VMS Version. If this is V5.4 or later, then - * we can use the new system service SYS$HASH_PASSWORD. Else - * fail and return garbage. - */ - - static char VMS_Version[32]; - struct { - unsigned short int Size; - unsigned short int Code; - char *Buffer; - unsigned short int *Resultant_Size; - } Item_List[2]={32, SYI$_VERSION, VMS_Version, 0, 0, 0}; - struct {int Size; char *Ptr;} Descr1; - - /* - * Get the information - */ - sys$getsyiw(0,0,0,Item_List,0,0,0); - /* - * Call the old routine if this isn't V5.4 or later... - */ -#ifndef __DECC - if ((VMS_Version[1] < '5') || - ((VMS_Version[1] == '5') && (VMS_Version[3] < '4'))) { - printf("Unsupported OS version\n"); - return(1); - } -#endif /* !__DECC */ - /* - * Call the SYS$HASH_PASSWORD system service... - */ - password.dsc$b_dtype = DSC$K_DTYPE_T; - password.dsc$b_class = DSC$K_CLASS_S; - password.dsc$w_length = input_length; - password.dsc$a_pointer = input_buf; - user.dsc$b_dtype = DSC$K_DTYPE_T; - user.dsc$b_class = DSC$K_CLASS_S; - user.dsc$w_length = strlen(username); - user.dsc$a_pointer = username; - sys$hash_password (&password, encryption_type, salt, &user, output_buf); -} diff --git a/driver/vms-pwd.h b/driver/vms-pwd.h deleted file mode 100644 index 6cb73d3..0000000 --- a/driver/vms-pwd.h +++ /dev/null @@ -1,48 +0,0 @@ -/* @(#)pwd.h 1.7 89/08/24 SMI; from S5R2 1.1 */ - -#ifndef __pwd_h -#define __pwd_h - -#ifdef vax11c -#include -#else -#include -#endif /* vax11c */ - -#ifdef PASSWDROUTINES -#define EXTERN -#else -#define EXTERN extern -#endif /* PASSWDROUTINES */ - -struct passwd { - char *pw_name; - char *pw_passwd; - int pw_uid; - int pw_gid; - short pw_salt; - int pw_encrypt; - char *pw_age; - char *pw_comment; - char *pw_gecos; - char *pw_dir; - char *pw_shell; -}; - - -#ifndef _POSIX_SOURCE -extern struct passwd *getpwent(); - -struct comment { - char *c_dept; - char *c_name; - char *c_acct; - char *c_bin; -}; - -#endif - -EXTERN struct passwd *getpwuid(/* uid_t uid */); -EXTERN struct passwd *getpwnam(/* char *name */); - -#endif /* !__pwd_h */ diff --git a/driver/vms-validate.c b/driver/vms-validate.c deleted file mode 100644 index 8f7141d..0000000 --- a/driver/vms-validate.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * validate a password for a user - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ - -/* - * Includes - */ -#include -#include -#include - -#include "vms-pwd.h" -int hash_vms_password(char *output_buf,char *input_buf,int input_length, - char *username,int encryption_type,unsigned short salt); - -/* - * - * Validate a VMS UserName/Password pair. - * - */ - -int validate_user(name,password) -char *name; -char *password; -{ - char password_buf[64]; - char username_buf[31]; - char encrypt_buf[8]; - register int i; - register char *cp,*cp1; - struct passwd *user_entry; - - /* - * Get the users UAF entry - */ - user_entry = getpwnam(name); - - /* - * If user_entry == NULL then we got a bad error - * return -1 to indicate a bad error - */ - if (user_entry == NULL) return (-1); - - /* - * Uppercase the password - */ - cp = password; - cp1 = password_buf; - while (*cp) - if (islower(*cp)) - *cp1++ = toupper(*cp++); - else - *cp1++ = *cp++; - /* - * Get the length of the password - */ - i = strlen(password); - /* - * Encrypt the password - */ - hash_vms_password(encrypt_buf,password_buf,i,user_entry->pw_name, - user_entry->pw_encrypt, user_entry->pw_salt); - if (memcmp(encrypt_buf,user_entry->pw_passwd,8) == 0) - return(1); - else return(0); -} - diff --git a/driver/windows.c b/driver/windows.c index 5854c49..c9cdf9d 100644 --- a/driver/windows.c +++ b/driver/windows.c @@ -1,5 +1,5 @@ /* windows.c --- turning the screen black; dealing with visuals, virtual roots. - * xscreensaver, Copyright (c) 1991-2020 Jamie Zawinski + * xscreensaver, Copyright © 1991-2021 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 @@ -14,42 +14,17 @@ # include "config.h" #endif -#ifdef VMS -# include /* for getpid() */ -# include "vms-gtod.h" /* for gettimeofday() */ -#endif /* VMS */ - -#ifndef VMS -# include /* for getpwuid() */ -#else /* VMS */ -# include "vms-pwd.h" -#endif /* VMS */ - #ifdef HAVE_UNAME # include /* for uname() */ #endif /* HAVE_UNAME */ #include -/* #include / * for CARD32 */ +#include /* for getpwuid() */ #include #include /* for XSetClassHint() */ #include -#include /* for time() */ -#include -#include /* for the signal names */ +#include #include -#include - -/* You might think that to store an array of 32-bit quantities onto a - server-side property, you would pass an array of 32-bit data quantities - into XChangeProperty(). You would be wrong. You have to use an array - of longs, even if long is 64 bits (using 32 of each 64.) - */ -typedef long PROP32; - -#ifdef HAVE_MIT_SAVER_EXTENSION -# include -#endif /* HAVE_MIT_SAVER_EXTENSION */ #ifdef HAVE_XF86VMODE # include @@ -59,1144 +34,100 @@ typedef long PROP32; # include #endif /* HAVE_XINERAMA */ -/* This file doesn't need the Xt headers, so stub these types out... */ -#undef XtPointer -#define XtAppContext void* -#define XrmDatabase void* -#define XtIntervalId void* -#define XtPointer void* -#define Widget void* - #include "xscreensaver.h" +#include "atoms.h" #include "visual.h" +#include "screens.h" #include "fade.h" +#include "resources.h" +#include "xft.h" +#include "font-retry.h" extern int kill (pid_t, int); /* signal() is in sys/signal.h... */ -Atom XA_VROOT, XA_XSETROOT_ID, XA_ESETROOT_PMAP_ID, XA_XROOTPMAP_ID; -Atom XA_NET_WM_USER_TIME; -Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID; -Atom XA_SCREENSAVER_STATUS; - -extern saver_info *global_si_kludge; /* I hate C so much... */ - -static void maybe_transfer_grabs (saver_screen_info *ssi, - Window old_w, Window new_w, int new_screen); - -#define ALL_POINTER_EVENTS \ - (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \ - LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \ - Button1MotionMask | Button2MotionMask | Button3MotionMask | \ - Button4MotionMask | Button5MotionMask | ButtonMotionMask) - - -static const char * -grab_string(int status) -{ - switch (status) - { - case GrabSuccess: return "GrabSuccess"; - case AlreadyGrabbed: return "AlreadyGrabbed"; - case GrabInvalidTime: return "GrabInvalidTime"; - case GrabNotViewable: return "GrabNotViewable"; - case GrabFrozen: return "GrabFrozen"; - default: - { - static char foo[255]; - sprintf(foo, "unknown status: %d", status); - return foo; - } - } -} - -static int -grab_kbd(saver_info *si, Window w, int screen_no) -{ - saver_preferences *p = &si->prefs; - int status = XGrabKeyboard (si->dpy, w, True, - /* I don't really understand Sync vs Async, - but these seem to work... */ - GrabModeSync, GrabModeAsync, - CurrentTime); - if (status == GrabSuccess) - { - si->keyboard_grab_window = w; - si->keyboard_grab_screen = screen_no; - } - - if (p->verbose_p) - fprintf(stderr, "%s: %d: grabbing keyboard on 0x%lx... %s.\n", - blurb(), screen_no, (unsigned long) w, grab_string(status)); - return status; -} - - -static int -grab_mouse (saver_info *si, Window w, Cursor cursor, int screen_no) -{ - saver_preferences *p = &si->prefs; - int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS, - GrabModeAsync, GrabModeAsync, w, - cursor, CurrentTime); - if (status == GrabSuccess) - { - si->mouse_grab_window = w; - si->mouse_grab_screen = screen_no; - } - - if (p->verbose_p) - fprintf(stderr, "%s: %d: grabbing mouse on 0x%lx... %s.\n", - blurb(), screen_no, (unsigned long) w, grab_string(status)); - return status; -} - - -static void -ungrab_kbd(saver_info *si) -{ - saver_preferences *p = &si->prefs; - XUngrabKeyboard(si->dpy, CurrentTime); - if (p->verbose_p) - fprintf(stderr, "%s: %d: ungrabbing keyboard (was 0x%lx).\n", - blurb(), si->keyboard_grab_screen, - (unsigned long) si->keyboard_grab_window); - si->keyboard_grab_window = 0; -} - - -static void -ungrab_mouse(saver_info *si) -{ - saver_preferences *p = &si->prefs; - XUngrabPointer(si->dpy, CurrentTime); - if (p->verbose_p) - fprintf(stderr, "%s: %d: ungrabbing mouse (was 0x%lx).\n", - blurb(), si->mouse_grab_screen, - (unsigned long) si->mouse_grab_window); - si->mouse_grab_window = 0; -} - - -/* Apparently there is this program called "rdesktop" which is a windows - terminal server client for Unix. It would seem that this program holds - the keyboard GRABBED the whole time it has focus! This is, of course, - completely idiotic: the whole point of grabbing is to get events when - you do *not* have focus, so grabbing *only when* you have focus is - completely redundant -- unless your goal is to make xscreensaver not - able to ever lock the screen when your program is running. - - If xscreensaver blanks while rdesktop still has a keyboard grab, then - when we try to prompt for the password, we won't get the characters: - they'll be typed into rdesktop. - - Perhaps rdesktop will release its keyboard grab if it loses focus? - What the hell, let's give it a try. If we fail to grab the keyboard - four times in a row, we forcibly set focus to "None" and try four - more times. (We don't touch focus unless we're already having a hard - time getting a grab.) - */ -static void -nuke_focus (saver_info *si, int screen_no) -{ - saver_preferences *p = &si->prefs; - Window focus = 0; - int rev = 0; - - XGetInputFocus (si->dpy, &focus, &rev); - - if (p->verbose_p) - { - char w[255], r[255]; - - if (focus == PointerRoot) strcpy (w, "PointerRoot"); - else if (focus == None) strcpy (w, "None"); - else sprintf (w, "0x%lx", (unsigned long) focus); - - if (rev == RevertToParent) strcpy (r, "RevertToParent"); - else if (rev == RevertToPointerRoot) strcpy (r, "RevertToPointerRoot"); - else if (rev == RevertToNone) strcpy (r, "RevertToNone"); - else sprintf (r, "0x%x", rev); - - fprintf (stderr, "%s: %d: removing focus from %s / %s.\n", - blurb(), screen_no, w, r); - } - - XSetInputFocus (si->dpy, None, RevertToNone, CurrentTime); - XSync (si->dpy, False); -} - - -static void -ungrab_keyboard_and_mouse (saver_info *si) -{ - ungrab_mouse (si); - ungrab_kbd (si); -} - - -static Bool -grab_keyboard_and_mouse_real (saver_info *si, Window window, Cursor cursor, - int screen_no) -{ - Status mstatus = 0, kstatus = 0; - int i; - int retries = 4; - Bool focus_fuckus = False; - - AGAIN: - - for (i = 0; i < retries; i++) - { - XSync (si->dpy, False); - kstatus = grab_kbd (si, window, screen_no); - if (kstatus == GrabSuccess) - break; - - /* else, wait a second and try to grab again. */ - sleep (1); - } - - if (kstatus != GrabSuccess) - { - fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n", - blurb(), grab_string(kstatus)); - - if (! focus_fuckus) - { - focus_fuckus = True; - nuke_focus (si, screen_no); - goto AGAIN; - } - } - - for (i = 0; i < retries; i++) - { - XSync (si->dpy, False); - mstatus = grab_mouse (si, window, cursor, screen_no); - if (mstatus == GrabSuccess) - break; - - /* else, wait a second and try to grab again. */ - sleep (1); - } - - if (mstatus != GrabSuccess) - fprintf (stderr, "%s: couldn't grab pointer! (%s)\n", - blurb(), grab_string(mstatus)); - - - /* When should we allow blanking to proceed? The current theory - is that a keyboard grab is mandatory; a mouse grab is optional. - - - If we don't have a keyboard grab, then we won't be able to - read a password to unlock, so the kbd grab is mandatory. - (We can't conditionalize this on locked_p, because someone - might run "xscreensaver-command -lock" at any time.) - - - If we don't have a mouse grab, then we might not see mouse - clicks as a signal to unblank -- but we will still see kbd - activity, so that's not a disaster. - - It has been suggested that we should allow blanking if locking - is disabled, and we have a mouse grab but no keyboard grab - (that is: kstatus != GrabSuccess && - mstatus == GrabSuccess && - si->locking_disabled_p) - That would allow screen blanking (but not locking) while the gdm - login screen had the keyboard grabbed, but one would have to use - the mouse to unblank. Keyboard characters would go to the gdm - login field without unblanking. I have not made this change - because I'm not completely convinced it is a safe thing to do. - */ - - if (kstatus != GrabSuccess) /* Do not blank without a kbd grab. */ - { - /* If we didn't get both grabs, release the one we did get. */ - ungrab_keyboard_and_mouse (si); - return False; - } - - return True; /* Grab is good, go ahead and blank. */ -} - - -static Bool -grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor, - int screen_no) -{ - Bool ret; - char *euc = si->prefs.external_ungrab_command; - char cmd[200]; - if (euc && *euc) - { - snprintf (cmd, sizeof(cmd), "%s %s", euc, "pre"); - exec_and_wait (si, cmd); - } - ret = grab_keyboard_and_mouse_real (si, window, cursor, screen_no); - if (euc && *euc) - { - snprintf (cmd, sizeof(cmd), "%s %s", euc, "post"); - exec_and_wait (si, cmd); - if (ret) { - /* The external command might have generated some x/input events */ - flush_events (si); - } - } - return ret; -} - - -int -move_mouse_grab (saver_info *si, Window to, Cursor cursor, int to_screen_no) -{ - Window old = si->mouse_grab_window; - - if (old == 0) - return grab_mouse (si, to, cursor, to_screen_no); - else - { - saver_preferences *p = &si->prefs; - int status; - - XSync (si->dpy, False); - XGrabServer (si->dpy); /* ############ DANGER! */ - XSync (si->dpy, False); - - if (p->verbose_p) - fprintf(stderr, "%s: grabbing server...\n", blurb()); - - ungrab_mouse (si); - status = grab_mouse (si, to, cursor, to_screen_no); - - if (status != GrabSuccess) /* Augh! */ - { - sleep (1); /* Note dramatic evil of sleeping - with server grabbed. */ - XSync (si->dpy, False); - status = grab_mouse (si, to, cursor, to_screen_no); - } - - if (status != GrabSuccess) /* Augh! Try to get the old one back... */ - grab_mouse (si, old, cursor, to_screen_no); - - XUngrabServer (si->dpy); - XSync (si->dpy, False); /* ###### (danger over) */ - - if (p->verbose_p) - fprintf(stderr, "%s: ungrabbing server.\n", blurb()); - - return status; - } -} - - -/* Prints an error message to stderr and returns True if there is another - xscreensaver running already. Silently returns False otherwise. */ -Bool -ensure_no_screensaver_running (Display *dpy, Screen *screen) -{ - Bool status = 0; - int i; - Window root = RootWindowOfScreen (screen); - Window root2, parent, *kids; - unsigned int nkids; - XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler); - - if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) - abort (); - if (root != root2) - abort (); - if (parent) - abort (); - for (i = 0; i < nkids; i++) - { - Atom type; - int format; - unsigned long nitems, bytesafter; - unsigned char *version; - - if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1, - False, XA_STRING, &type, &format, &nitems, - &bytesafter, &version) - == Success - && type != None) - { - unsigned char *id; - if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512, - False, XA_STRING, &type, &format, &nitems, - &bytesafter, &id) - != Success - || type == None) - id = (unsigned char *) "???"; - - fprintf (stderr, - "%s: already running on display %s (window 0x%x)\n from process %s.\n", - blurb(), DisplayString (dpy), (int) kids [i], - (char *) id); - status = True; - } - - else if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128, - False, XA_STRING, &type, &format, &nitems, - &bytesafter, &version) - == Success - && type != None - && !strcmp ((char *) version, "gnome-screensaver")) - { - fprintf (stderr, - "%s: \"%s\" is already running on display %s (window 0x%x)\n", - blurb(), (char *) version, - DisplayString (dpy), (int) kids [i]); - status = True; - break; - } - } - - if (kids) XFree ((char *) kids); - XSync (dpy, False); - XSetErrorHandler (old_handler); - return status; -} - - - -/* Virtual-root hackery */ - #ifdef _VROOT_H_ ERROR! You must not include vroot.h in this file. #endif -static void -store_vroot_property (Display *dpy, Window win, Window value) -{ -#if 0 - if (p->verbose_p) - fprintf (stderr, - "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(), - win, - (win == screensaver_window ? "ScreenSaver" : - (win == real_vroot ? "VRoot" : - (win == real_vroot_value ? "Vroot_value" : "???"))), - value, - (value == screensaver_window ? "ScreenSaver" : - (value == real_vroot ? "VRoot" : - (value == real_vroot_value ? "Vroot_value" : "???")))); -#endif - XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace, - (unsigned char *) &value, 1); -} - -static void -remove_vroot_property (Display *dpy, Window win) -{ -#if 0 - if (p->verbose_p) - fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win, - (win == screensaver_window ? "ScreenSaver" : - (win == real_vroot ? "VRoot" : - (win == real_vroot_value ? "Vroot_value" : "???")))); -#endif - XDeleteProperty (dpy, win, XA_VROOT); -} - -static Bool safe_XKillClient (Display *dpy, XID id); +static void reset_watchdog_timer (saver_info *si); -static void -kill_xsetroot_data_1 (Display *dpy, Window window, - Atom prop, const char *atom_name, - Bool verbose_p) +void +store_saver_status (saver_info *si) { - Atom type; - int format; - unsigned long nitems, bytesafter; - unsigned char *dataP = 0; + /* The contents of XA_SCREENSAVER_STATUS has LOCK/BLANK/0 in the first slot, + the time at which that state began in the second slot, and the ordinal of + the running hacks on each screen (1-based) in subsequent slots. Since + we don't know the blank-versus-lock status here, we leave whatever was + there before unchanged: it will be updated by "xscreensaver". - /* If the user has been using xv or xsetroot as a screensaver (to display - an image on the screensaver window, as a kind of slideshow) then the - pixmap and its associated color cells have been put in RetainPermanent - CloseDown mode. Since we're not destroying the xscreensaver window, - but merely unmapping it, we need to free these resources or those - colormap cells will stay allocated while the screensaver is off. (We - could just delete the screensaver window and recreate it later, but - that could cause other problems.) This code does an atomic read-and- - delete of the _XSETROOT_ID property, and if it held a pixmap, then we - cause the RetainPermanent resources of the client which created it - (and which no longer exists) to be freed. - - Update: it seems that Gnome and KDE do this same trick, but with the - properties "ESETROOT_PMAP_ID" and/or "_XROOTPMAP_ID" instead of - "_XSETROOT_ID". So, we'll kill those too. - */ - if (XGetWindowProperty (dpy, window, prop, 0, 1, - True, AnyPropertyType, &type, &format, &nitems, - &bytesafter, &dataP) - == Success - && type != None) - { - Pixmap *pixP = (Pixmap *) dataP; - if (pixP && *pixP && type == XA_PIXMAP && format == 32 && - nitems == 1 && bytesafter == 0) - { - if (verbose_p) - fprintf (stderr, "%s: destroying %s data (0x%lX).\n", - blurb(), atom_name, *pixP); - safe_XKillClient (dpy, *pixP); - } - else - fprintf (stderr, - "%s: deleted unrecognised %s property: \n" - "\t%lu, %lu; type: %lu, format: %d, " - "nitems: %lu, bytesafter %ld\n", - blurb(), atom_name, - (unsigned long) pixP, (pixP ? *pixP : 0), type, - format, nitems, bytesafter); - } -} + XA_SCREENSAVER_STATUS is stored on the (real) root window of screen 0. + XA_SCREENSAVER_VERSION and XA_SCREENSAVER_ID are stored on the unmapped + window created by the "xscreensaver" process. ClientMessage events are + sent to that window, and the responses are sent via the + XA_SCREENSAVER_RESPONSE property on it. -static void -kill_xsetroot_data (Display *dpy, Window w, Bool verbose_p) -{ - kill_xsetroot_data_1 (dpy, w, XA_XSETROOT_ID, "_XSETROOT_ID", verbose_p); - kill_xsetroot_data_1 (dpy, w, XA_ESETROOT_PMAP_ID, "ESETROOT_PMAP_ID", - verbose_p); - kill_xsetroot_data_1 (dpy, w, XA_XROOTPMAP_ID, "_XROOTPMAP_ID", verbose_p); -} - + These properties are not used on the windows created by "xscreensaver-gfx" + for use by the display hacks. -static void -save_real_vroot (saver_screen_info *ssi) -{ - saver_info *si = ssi->global; + See the different version of this function in xscreensaver.c. + */ Display *dpy = si->dpy; - Screen *screen = ssi->screen; + Window w = RootWindow (dpy, 0); /* always screen 0 */ + Atom type; + unsigned char *dataP = 0; + PROP32 *status = 0; + int format; + unsigned long nitems, bytesafter; + int nitems2 = si->nscreens + 2; int i; - Window root = RootWindowOfScreen (screen); - Window root2, parent, *kids; - unsigned int nkids; - XErrorHandler old_handler; - - /* It's possible that a window might be deleted between our call to - XQueryTree() and our call to XGetWindowProperty(). Don't die if - that happens (but just ignore that window, it's not the one we're - interested in anyway.) - */ - XSync (dpy, False); - old_handler = XSetErrorHandler (BadWindow_ehandler); - XSync (dpy, False); - - ssi->real_vroot = 0; - ssi->real_vroot_value = 0; - if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) - abort (); - if (root != root2) - abort (); - if (parent) - abort (); - for (i = 0; i < nkids; i++) - { - Atom type; - int format; - unsigned long nitems, bytesafter; - unsigned char *dataP = 0; - Window *vrootP; - int j; - - /* Skip this window if it is the xscreensaver window of any other - screen (this can happen in the Xinerama case.) - */ - for (j = 0; j < si->nscreens; j++) - { - saver_screen_info *ssi2 = &si->screens[j]; - if (kids[i] == ssi2->screensaver_window) - goto SKIP; - } - - if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW, - &type, &format, &nitems, &bytesafter, - &dataP) - != Success) - continue; - if (! dataP) - continue; - - vrootP = (Window *) dataP; - if (ssi->real_vroot) - { - if (*vrootP == ssi->screensaver_window) abort (); - fprintf (stderr, - "%s: more than one virtual root window found (0x%x and 0x%x).\n", - blurb(), (int) ssi->real_vroot, (int) kids [i]); - exit (1); - } - ssi->real_vroot = kids [i]; - ssi->real_vroot_value = *vrootP; - SKIP: - ; - } - - XSync (dpy, False); - XSetErrorHandler (old_handler); - XSync (dpy, False); - if (ssi->real_vroot) - { - remove_vroot_property (si->dpy, ssi->real_vroot); - XSync (dpy, False); - } + /* Read the old property, so we can change just our parts. */ + XGetWindowProperty (dpy, w, + XA_SCREENSAVER_STATUS, + 0, 999, False, XA_INTEGER, + &type, &format, &nitems, &bytesafter, + &dataP); - XFree ((char *) kids); -} + status = (PROP32 *) calloc (nitems2, sizeof(PROP32)); - -static Bool -restore_real_vroot_1 (saver_screen_info *ssi) -{ - saver_info *si = ssi->global; - saver_preferences *p = &si->prefs; - if (p->verbose_p && ssi->real_vroot) - fprintf (stderr, - "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n", - blurb(), (unsigned long) ssi->real_vroot); - if (ssi->screensaver_window) - remove_vroot_property (si->dpy, ssi->screensaver_window); - if (ssi->real_vroot) + if (dataP && type == XA_INTEGER && nitems >= 3) { - store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value); - ssi->real_vroot = 0; - ssi->real_vroot_value = 0; - /* make sure the property change gets there before this process - terminates! We might be doing this because we have intercepted - SIGTERM or something. */ - XSync (si->dpy, False); - return True; + status[0] = ((PROP32 *) dataP)[0]; + status[1] = ((PROP32 *) dataP)[1]; } - return False; -} -Bool -restore_real_vroot (saver_info *si) -{ - int i; - Bool did_any = False; for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; - if (restore_real_vroot_1 (ssi)) - did_any = True; - } - return did_any; -} - - -/* Signal hackery to ensure that the vroot doesn't get left in an - inconsistent state - */ - -const char * -signal_name(int signal) -{ - switch (signal) { - case SIGHUP: return "SIGHUP"; - case SIGINT: return "SIGINT"; - case SIGQUIT: return "SIGQUIT"; - case SIGILL: return "SIGILL"; - case SIGTRAP: return "SIGTRAP"; -#ifdef SIGABRT - case SIGABRT: return "SIGABRT"; -#endif - case SIGFPE: return "SIGFPE"; - case SIGKILL: return "SIGKILL"; - case SIGBUS: return "SIGBUS"; - case SIGSEGV: return "SIGSEGV"; - case SIGPIPE: return "SIGPIPE"; - case SIGALRM: return "SIGALRM"; - case SIGTERM: return "SIGTERM"; -#ifdef SIGSTOP - case SIGSTOP: return "SIGSTOP"; -#endif -#ifdef SIGCONT - case SIGCONT: return "SIGCONT"; -#endif -#ifdef SIGUSR1 - case SIGUSR1: return "SIGUSR1"; -#endif -#ifdef SIGUSR2 - case SIGUSR2: return "SIGUSR2"; -#endif -#ifdef SIGEMT - case SIGEMT: return "SIGEMT"; -#endif -#ifdef SIGSYS - case SIGSYS: return "SIGSYS"; -#endif -#ifdef SIGCHLD - case SIGCHLD: return "SIGCHLD"; -#endif -#ifdef SIGPWR - case SIGPWR: return "SIGPWR"; -#endif -#ifdef SIGWINCH - case SIGWINCH: return "SIGWINCH"; -#endif -#ifdef SIGURG - case SIGURG: return "SIGURG"; -#endif -#ifdef SIGIO - case SIGIO: return "SIGIO"; -#endif -#ifdef SIGVTALRM - case SIGVTALRM: return "SIGVTALRM"; -#endif -#ifdef SIGXCPU - case SIGXCPU: return "SIGXCPU"; -#endif -#ifdef SIGXFSZ - case SIGXFSZ: return "SIGXFSZ"; -#endif -#ifdef SIGDANGER - case SIGDANGER: return "SIGDANGER"; -#endif - default: - { - static char buf[50]; - sprintf(buf, "signal %d\n", signal); - return buf; - } - } -} - - - -static RETSIGTYPE -restore_real_vroot_handler (int sig) -{ - saver_info *si = global_si_kludge; /* I hate C so much... */ - - signal (sig, SIG_DFL); - if (restore_real_vroot (si)) - fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n", - blurb(), signal_name(sig)); -# ifdef HAVE_LIBSYSTEMD - if (si->systemd_pid) /* Kill background xscreensaver-systemd process */ - { - /* We're exiting, so there's no need to do a full kill_job() here, - which will waitpid(). */ - /* kill_job (si, si->systemd_pid, SIGTERM); */ - kill (si->systemd_pid, SIGTERM); - si->systemd_pid = 0; + status[2 + i] = ssi->current_hack + 1; /* 1-based */ } -# endif - kill (getpid (), sig); -} -static void -catch_signal (saver_info *si, int sig, RETSIGTYPE (*handler) (int)) -{ -# ifdef HAVE_SIGACTION - - struct sigaction a; - a.sa_handler = handler; - sigemptyset (&a.sa_mask); - a.sa_flags = 0; - - /* On Linux 2.4.9 (at least) we need to tell the kernel to not mask delivery - of this signal from inside its handler, or else when we execvp() the - process again, it starts up with SIGHUP blocked, meaning that killing - it with -HUP only works *once*. You'd think that execvp() would reset - all the signal masks, but it doesn't. - */ -# if defined(SA_NOMASK) - a.sa_flags |= SA_NOMASK; -# elif defined(SA_NODEFER) - a.sa_flags |= SA_NODEFER; -# endif - - if (sigaction (sig, &a, 0) < 0) -# else /* !HAVE_SIGACTION */ - if (((long) signal (sig, handler)) == -1L) -# endif /* !HAVE_SIGACTION */ - { - char buf [255]; - sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig)); - perror (buf); - saver_exit (si, 1, 0); - } -} - -static RETSIGTYPE saver_sighup_handler (int sig); - -void -handle_signals (saver_info *si) -{ - catch_signal (si, SIGHUP, saver_sighup_handler); - - catch_signal (si, SIGINT, restore_real_vroot_handler); - catch_signal (si, SIGQUIT, restore_real_vroot_handler); - catch_signal (si, SIGILL, restore_real_vroot_handler); - catch_signal (si, SIGTRAP, restore_real_vroot_handler); -#ifdef SIGIOT - catch_signal (si, SIGIOT, restore_real_vroot_handler); -#endif - catch_signal (si, SIGABRT, restore_real_vroot_handler); -#ifdef SIGEMT - catch_signal (si, SIGEMT, restore_real_vroot_handler); -#endif - catch_signal (si, SIGFPE, restore_real_vroot_handler); - catch_signal (si, SIGBUS, restore_real_vroot_handler); - catch_signal (si, SIGSEGV, restore_real_vroot_handler); -#ifdef SIGSYS - catch_signal (si, SIGSYS, restore_real_vroot_handler); -#endif - catch_signal (si, SIGTERM, restore_real_vroot_handler); -#ifdef SIGXCPU - catch_signal (si, SIGXCPU, restore_real_vroot_handler); -#endif -#ifdef SIGXFSZ - catch_signal (si, SIGXFSZ, restore_real_vroot_handler); -#endif -#ifdef SIGDANGER - catch_signal (si, SIGDANGER, restore_real_vroot_handler); -#endif -} - - -static RETSIGTYPE -saver_sighup_handler (int sig) -{ - saver_info *si = global_si_kludge; /* I hate C so much... */ - - /* Re-establish SIGHUP handler */ - catch_signal (si, SIGHUP, saver_sighup_handler); - - fprintf (stderr, "%s: %s received: restarting...\n", - blurb(), signal_name(sig)); - - if (si->screen_blanked_p) - { - int i; - for (i = 0; i < si->nscreens; i++) - kill_screenhack (&si->screens[i]); - unblank_screen (si); - XSync (si->dpy, False); - } - - restart_process (si); /* Does not return */ - abort (); -} - - - -void -saver_exit (saver_info *si, int status, const char *dump_core_reason) -{ - saver_preferences *p = &si->prefs; - static Bool exiting = False; - Bool bugp; - Bool vrs; - - if (exiting) - exit(status); - - exiting = True; - - vrs = restore_real_vroot (si); - emergency_kill_subproc (si); - shutdown_stderr (si); - - if (p->verbose_p && vrs) - fprintf (real_stderr, "%s: old vroot restored.\n", blurb()); - -# ifdef HAVE_LIBSYSTEMD - if (si->systemd_pid) /* Kill background xscreensaver-systemd process */ - { - kill_job (si, si->systemd_pid, SIGTERM); - si->systemd_pid = 0; - } -# endif - - fflush(real_stdout); - -#ifdef VMS /* on VMS, 1 is the "normal" exit code instead of 0. */ - if (status == 0) status = 1; - else if (status == 1) status = -1; -#endif - - bugp = !!dump_core_reason; - - if (si->prefs.debug_p && !dump_core_reason) - dump_core_reason = "because of -debug"; - - if (dump_core_reason) - { - /* Note that the Linux man page for setuid() says If uid is - different from the old effective uid, the process will be - forbidden from leaving core dumps. - */ - char cwd[4096]; /* should really be PATH_MAX, but who cares. */ - cwd[0] = 0; - fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(), - dump_core_reason); - - if (bugp) - fprintf(real_stderr, - "%s: see https://www.jwz.org/xscreensaver/bugs.html\n" - "\t\t\tfor bug reporting information.\n\n", - blurb()); - -# if defined(HAVE_GETCWD) - if (!getcwd (cwd, sizeof(cwd))) -# elif defined(HAVE_GETWD) - if (!getwd (cwd)) -# endif - strcpy(cwd, "unknown."); - - fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd); - describe_uids (si, real_stderr); - - /* Do this to drop a core file, so that we can get a stack trace. */ - abort(); - } - - exit (status); -} - - -/* Managing the actual screensaver window */ - -Bool -window_exists_p (Display *dpy, Window window) -{ - XErrorHandler old_handler; - XWindowAttributes xgwa; - xgwa.screen = 0; - old_handler = XSetErrorHandler (BadWindow_ehandler); - XGetWindowAttributes (dpy, window, &xgwa); + XChangeProperty (si->dpy, w, XA_SCREENSAVER_STATUS, XA_INTEGER, 32, + PropModeReplace, (unsigned char *) status, nitems2); XSync (dpy, False); - XSetErrorHandler (old_handler); - return (xgwa.screen != 0); -} - -static void -store_saver_id (saver_screen_info *ssi) -{ - XClassHint class_hints; - saver_info *si = ssi->global; - unsigned long pid = (unsigned long) getpid (); - char buf[20]; - struct passwd *p = getpwuid (getuid ()); - const char *name, *host; - char *id; -# if defined(HAVE_UNAME) - struct utsname uts; -# endif /* UNAME */ - - /* First store the name and class on the window. - */ - class_hints.res_name = progname; - class_hints.res_class = progclass; - XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints); - XStoreName (si->dpy, ssi->screensaver_window, "screensaver"); - /* Then store the xscreensaver version number. - */ - XChangeProperty (si->dpy, ssi->screensaver_window, - XA_SCREENSAVER_VERSION, - XA_STRING, 8, PropModeReplace, - (unsigned char *) si->version, - strlen (si->version)); - - /* Now store the XSCREENSAVER_ID property, that says what user and host - xscreensaver is running as. - */ - - if (p && p->pw_name && *p->pw_name) - name = p->pw_name; - else if (p) + if (si->prefs.debug_p && si->prefs.verbose_p) { - sprintf (buf, "%lu", (unsigned long) p->pw_uid); - name = buf; - } - else - name = "???"; - -# if defined(HAVE_UNAME) - { - if (uname (&uts) < 0) - host = "???"; - else - host = uts.nodename; - } -# elif defined(VMS) - host = getenv("SYS$NODE"); -# else /* !HAVE_UNAME && !VMS */ - host = "???"; -# endif /* !HAVE_UNAME && !VMS */ - - id = (char *) malloc (strlen(name) + strlen(host) + 50); - sprintf (id, "%lu (%s@%s)", pid, name, host); - - XChangeProperty (si->dpy, ssi->screensaver_window, - XA_SCREENSAVER_ID, XA_STRING, - 8, PropModeReplace, - (unsigned char *) id, strlen (id)); - free (id); -} - - -void -store_saver_status (saver_info *si) -{ - PROP32 *status; - int size = si->nscreens + 2; - int i; - - status = (PROP32 *) calloc (size, sizeof(PROP32)); - - status[0] = (PROP32) (si->screen_blanked_p || si->locked_p - ? (si->locked_p ? XA_LOCK : XA_BLANK) - : 0); - status[1] = (PROP32) si->blank_time; - - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - status [2 + i] = ssi->current_hack + 1; + int i; + fprintf (stderr, "%s: wrote status property: 0x%lx: %s", blurb(), + (unsigned long) w, + (status[0] == XA_LOCK ? "LOCK" : + status[0] == XA_BLANK ? "BLANK" : + status[0] == 0 ? "0" : "???")); + for (i = 1; i < nitems; i++) + fprintf (stderr, ", %lu", status[i]); + fprintf (stderr, "\n"); } - XChangeProperty (si->dpy, - RootWindow (si->dpy, 0), /* always screen #0 */ - XA_SCREENSAVER_STATUS, - XA_INTEGER, 32, PropModeReplace, - (unsigned char *) status, size); free (status); + if (dataP) + XFree (dataP); } -static Bool error_handler_hit_p = False; - -static int -ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) -{ - error_handler_hit_p = True; - return 0; -} - - -/* Returns True if successful, False if an X error occurred. - We need this because other programs might have done things to - our window that will cause XChangeWindowAttributes() to fail: - if that happens, we give up, destroy the window, and re-create - it. - */ -static Bool -safe_XChangeWindowAttributes (Display *dpy, Window window, - unsigned long mask, - XSetWindowAttributes *attrs) -{ - XErrorHandler old_handler; - XSync (dpy, False); - error_handler_hit_p = False; - old_handler = XSetErrorHandler (ignore_all_errors_ehandler); - - XChangeWindowAttributes (dpy, window, mask, attrs); - - XSync (dpy, False); - XSetErrorHandler (old_handler); - XSync (dpy, False); - - return (!error_handler_hit_p); -} - - -/* This might not be necessary, but just in case. */ -static Bool -safe_XConfigureWindow (Display *dpy, Window window, - unsigned long mask, XWindowChanges *changes) -{ - XErrorHandler old_handler; - XSync (dpy, False); - error_handler_hit_p = False; - old_handler = XSetErrorHandler (ignore_all_errors_ehandler); - - XConfigureWindow (dpy, window, mask, changes); - - XSync (dpy, False); - XSetErrorHandler (old_handler); - XSync (dpy, False); - - return (!error_handler_hit_p); -} - -/* This might not be necessary, but just in case. */ -static Bool -safe_XDestroyWindow (Display *dpy, Window window) -{ - XErrorHandler old_handler; - XSync (dpy, False); - error_handler_hit_p = False; - old_handler = XSetErrorHandler (ignore_all_errors_ehandler); - - XDestroyWindow (dpy, window); - - XSync (dpy, False); - XSetErrorHandler (old_handler); - XSync (dpy, False); - - return (!error_handler_hit_p); -} - - -static Bool -safe_XKillClient (Display *dpy, XID id) -{ - XErrorHandler old_handler; - XSync (dpy, False); - error_handler_hit_p = False; - old_handler = XSetErrorHandler (ignore_all_errors_ehandler); - - XKillClient (dpy, id); - - XSync (dpy, False); - XSetErrorHandler (old_handler); - XSync (dpy, False); - - return (!error_handler_hit_p); -} - - -#ifdef HAVE_XF86VMODE -Bool -safe_XF86VidModeGetViewPort (Display *dpy, int screen, int *xP, int *yP) -{ - Bool result; - XErrorHandler old_handler; - XSync (dpy, False); - error_handler_hit_p = False; - old_handler = XSetErrorHandler (ignore_all_errors_ehandler); - - result = XF86VidModeGetViewPort (dpy, screen, xP, yP); - - XSync (dpy, False); - XSetErrorHandler (old_handler); - XSync (dpy, False); - - return (error_handler_hit_p - ? False - : result); -} - -/* There is no "safe_XF86VidModeGetModeLine" because it fails with an - untrappable I/O error instead of an X error -- so one must call - safe_XF86VidModeGetViewPort first, and assume that both have the - same error condition. Thank you XFree, may I have another. - */ - -#endif /* HAVE_XF86VMODE */ - - static void initialize_screensaver_window_1 (saver_screen_info *ssi) { @@ -1207,7 +138,8 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) /* This resets the screensaver window as fully as possible, since there's no way of knowing what some random client may have done to us in the meantime. We could just destroy and recreate the window, but that has - its own set of problems... + its own set of problems. (Update: that's exactly what we're doing + these days.) */ XColor black; XSetWindowAttributes attrs; @@ -1241,8 +173,7 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) if (ssi->cmap) { XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0); - if (ssi->cmap != ssi->demo_cmap && - ssi->cmap != def_cmap) + if (ssi->cmap != def_cmap) XFreeColormap (si->dpy, ssi->cmap); } ssi->cmap = def_cmap; @@ -1253,20 +184,18 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) CWBackPixel | CWBackingPixel | CWBorderPixel); attrs.override_redirect = True; - /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't - actually be reading these events during normal operation; but we still - need to see Button events for demo-mode to work properly. - */ attrs.event_mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask); - attrs.backing_store = NotUseful; + attrs.backing_store = Always; attrs.colormap = ssi->cmap; attrs.background_pixel = ssi->black_pixel; attrs.backing_pixel = ssi->black_pixel; attrs.border_pixel = ssi->black_pixel; + printed_visual_info = True; /* Too noisy */ + if (!p->verbose_p || printed_visual_info) ; else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen)) @@ -1287,56 +216,11 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) } printed_visual_info = True; -#ifdef HAVE_MIT_SAVER_EXTENSION - if (si->using_mit_saver_extension) - { - XScreenSaverInfo *info; - Window root = RootWindowOfScreen (ssi->screen); - -#if 0 - /* This call sets the server screensaver timeouts to what we think - they should be (based on the resources and args xscreensaver was - started with.) It's important that we do this to sync back up - with the server - if we have turned on prematurely, as by an - ACTIVATE ClientMessage, then the server may decide to activate - the screensaver while it's already active. That's ok for us, - since we would know to ignore that ScreenSaverActivate event, - but a side effect of this would be that the server would map its - saver window (which we then hide again right away) meaning that - the bits currently on the screen get blown away. Ugly. */ - - /* #### Ok, that doesn't work - when we tell the server that the - screensaver is "off" it sends us a Deactivate event, which is - sensible... but causes the saver to never come on. Hmm. */ - disable_builtin_screensaver (si, True); -#endif /* 0 */ - -#if 0 - /* #### The MIT-SCREEN-SAVER extension gives us access to the - window that the server itself uses for saving the screen. - However, using this window in any way, in particular, calling - XScreenSaverSetAttributes() as below, tends to make the X server - crash. So fuck it, let's try and get along without using it... - - It's also inconvenient to use this window because it doesn't - always exist (though the ID is constant.) So to use this - window, we'd have to reimplement the ACTIVATE ClientMessage to - tell the *server* to tell *us* to turn on, to cause the window - to get created at the right time. Gag. */ - XScreenSaverSetAttributes (si->dpy, root, - 0, 0, width, height, 0, - current_depth, InputOutput, visual, - attrmask, &attrs); - XSync (si->dpy, False); -#endif /* 0 */ - - info = XScreenSaverAllocInfo (); - XScreenSaverQueryInfo (si->dpy, root, info); - ssi->server_mit_saver_window = info->window; - if (! ssi->server_mit_saver_window) abort (); - XFree (info); + if (ssi->error_dialog) + { + XDestroyWindow (si->dpy, ssi->error_dialog); + ssi->error_dialog = 0; } -#endif /* HAVE_MIT_SAVER_EXTENSION */ if (ssi->screensaver_window) { @@ -1348,10 +232,13 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) changes.height = ssi->height; changes.border_width = 0; - if (! (safe_XConfigureWindow (si->dpy, ssi->screensaver_window, - changesmask, &changes) && - safe_XChangeWindowAttributes (si->dpy, ssi->screensaver_window, - attrmask, &attrs))) + /* XConfigureWindow and XChangeWindowAttributes can fail if a hack did + something weird to the window. In that case, we must destroy and + re-create it. */ + if (! (XConfigureWindow (si->dpy, ssi->screensaver_window, + changesmask, &changes) && + XChangeWindowAttributes (si->dpy, ssi->screensaver_window, + attrmask, &attrs))) { horked_window = ssi->screensaver_window; ssi->screensaver_window = 0; @@ -1365,35 +252,31 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) ssi->x, ssi->y, ssi->width, ssi->height, 0, ssi->current_depth, InputOutput, ssi->current_visual, attrmask, &attrs); - reset_stderr (ssi); + xscreensaver_set_wm_atoms (si->dpy, ssi->screensaver_window, + ssi->width, ssi->height, 0); if (horked_window) { fprintf (stderr, "%s: someone horked our saver window (0x%lx)! Recreating it...\n", blurb(), (unsigned long) horked_window); - maybe_transfer_grabs (ssi, horked_window, ssi->screensaver_window, - ssi->number); - safe_XDestroyWindow (si->dpy, horked_window); - horked_window = 0; + XDestroyWindow (si->dpy, horked_window); } - if (p->verbose_p) - fprintf (stderr, "%s: %d: saver window is 0x%lx.\n", + if (p->verbose_p > 1) + fprintf (stderr, "%s: %d: saver window is 0x%lx\n", blurb(), ssi->number, (unsigned long) ssi->screensaver_window); } - store_saver_id (ssi); /* store window name and IDs */ - if (!ssi->cursor) { - Pixmap bit; - bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window, - "\000", 1, 1, - BlackPixelOfScreen (ssi->screen), - BlackPixelOfScreen (ssi->screen), - 1); + Pixmap bit = + XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window, + "\000", 1, 1, + BlackPixelOfScreen (ssi->screen), + BlackPixelOfScreen (ssi->screen), + 1); ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black, 0, 0); XFreePixmap (si->dpy, bit); @@ -1407,6 +290,7 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor); } + void initialize_screensaver_window (saver_info *si) { @@ -1416,6 +300,27 @@ initialize_screensaver_window (saver_info *si) } +static void +raise_window (saver_screen_info *ssi) +{ + saver_info *si = ssi->global; + if (ssi->error_dialog) + { + /* Make the error be topmost, and the saver be directly below it. */ + Window windows[2]; + windows[0] = ssi->error_dialog; + windows[1] = ssi->screensaver_window; + XMapRaised (si->dpy, windows[0]); + XRestackWindows (si->dpy, windows, countof(windows)); + } + else + XMapRaised (si->dpy, ssi->screensaver_window); + + if (ssi->cmap) + XInstallColormap (si->dpy, ssi->cmap); +} + + /* Called when the RANDR (Resize and Rotate) extension tells us that the size of the screen has changed while the screen was blanked. Call update_screen_layout() first, then call this to synchronize @@ -1470,8 +375,8 @@ resize_screensaver_window (saver_info *si) xgwa.width, xgwa.height, xgwa.x, xgwa.y, ssi->width, ssi->height, ssi->x, ssi->y); - if (! safe_XConfigureWindow (si->dpy, ssi->screensaver_window, - changesmask, &changes)) + if (! XConfigureWindow (si->dpy, ssi->screensaver_window, + changesmask, &changes)) fprintf (stderr, "%s: %d: someone horked our saver window" " (0x%lx)! Unable to resize it!\n", blurb(), i, (unsigned long) ssi->screensaver_window); @@ -1485,27 +390,10 @@ resize_screensaver_window (saver_info *si) Note that spawn_screenhack() calls select_visual() which may destroy and re-create the window via initialize_screensaver_window_1(). */ - if (si->screen_blanked_p) - { - if (ssi->cmap) - XInstallColormap (si->dpy, ssi->cmap); - XMapRaised (si->dpy, ssi->screensaver_window); - XSync (si->dpy, False); - if (! ssi->pid) - spawn_screenhack (ssi); - - /* Make sure the act of adding a screen doesn't present as - pointer motion (and thus cause an unblank). */ - { - Window root, child; - int x, y; - unsigned int mask; - XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child, - &ssi->last_poll_mouse.root_x, - &ssi->last_poll_mouse.root_y, - &x, &y, &mask); - } - } + raise_window (ssi); + XSync (si->dpy, False); + if (! ssi->pid) + spawn_screenhack (ssi); } /* Kill off any savers running on no-longer-extant monitors. @@ -1518,44 +406,47 @@ resize_screensaver_window (saver_info *si) if (ssi->screensaver_window) { XUnmapWindow (si->dpy, ssi->screensaver_window); - restore_real_vroot_1 (ssi); } } } -void -raise_window (saver_info *si, - Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear) +static void +raise_windows (saver_info *si) { - saver_preferences *p = &si->prefs; int i; + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + raise_window (ssi); + } +} - if (si->demoing_p) - inhibit_fade = True; - - if (si->emergency_lock_p) - inhibit_fade = True; - if (!dont_clear) - initialize_screensaver_window (si); +/* Called only once, before the main loop begins. + */ +void +blank_screen (saver_info *si) +{ + saver_preferences *p = &si->prefs; + Bool user_active_p = False; + int i; - reset_watchdog_timer (si, True); + initialize_screensaver_window (si); + sync_server_dpms_settings (si->dpy, p); - if (p->fade_p && si->fading_possible_p && !inhibit_fade) + if (p->fade_p && + !si->demoing_p && + !si->emergency_p) { Window *current_windows = (Window *) - calloc(sizeof(Window), si->nscreens); - Colormap *current_maps = (Colormap *) - calloc(sizeof(Colormap), si->nscreens); + malloc (si->nscreens * sizeof(*current_windows)); + if (!current_windows) abort(); for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; current_windows[i] = ssi->screensaver_window; - current_maps[i] = (between_hacks_p - ? ssi->cmap - : DefaultColormapOfScreen (ssi->screen)); /* Ensure that the default background of the window is really black, not a pixmap or something. (This does not clear the window.) */ XSetWindowBackground (si->dpy, ssi->screensaver_window, @@ -1564,282 +455,98 @@ raise_window (saver_info *si, if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb()); - XGrabServer (si->dpy); /* ############ DANGER! */ - - /* Clear the stderr layer on each screen. - */ - if (!dont_clear) - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (ssi->stderr_overlay_window) - /* Do this before the fade, since the stderr cmap won't fade - even if we uninstall it (beats me...) */ - clear_stderr (ssi); - } - - /* Note! The server is grabbed, and this will take several seconds - to complete! */ - fade_screens (si->dpy, current_maps, - current_windows, si->nscreens, - p->fade_seconds/1000, p->fade_ticks, True, !dont_clear); - - free(current_maps); - free(current_windows); - current_maps = 0; - current_windows = 0; - - if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb()); - -#ifdef HAVE_MIT_SAVER_EXTENSION - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (ssi->server_mit_saver_window && - window_exists_p (si->dpy, ssi->server_mit_saver_window)) - XUnmapWindow (si->dpy, ssi->server_mit_saver_window); - } -#endif /* HAVE_MIT_SAVER_EXTENSION */ - - XUngrabServer (si->dpy); - XSync (si->dpy, False); /* ###### (danger over) */ - } - else - { - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (!dont_clear) - XClearWindow (si->dpy, ssi->screensaver_window); - if (!dont_clear || ssi->stderr_overlay_window) - clear_stderr (ssi); - XMapRaised (si->dpy, ssi->screensaver_window); - XSync (si->dpy, False); -#ifdef HAVE_MIT_SAVER_EXTENSION - if (ssi->server_mit_saver_window && - window_exists_p (si->dpy, ssi->server_mit_saver_window)) - XUnmapWindow (si->dpy, ssi->server_mit_saver_window); -#endif /* HAVE_MIT_SAVER_EXTENSION */ - } - } - - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (ssi->cmap) - XInstallColormap (si->dpy, ssi->cmap); - } -} - - -int -mouse_screen (saver_info *si) -{ - saver_preferences *p = &si->prefs; - Window pointer_root, pointer_child; - int root_x, root_y, win_x, win_y; - unsigned int mask; - int i; - - if (si->nscreens == 1) - return 0; - - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (XQueryPointer (si->dpy, RootWindowOfScreen (ssi->screen), - &pointer_root, &pointer_child, - &root_x, &root_y, &win_x, &win_y, &mask) && - root_x >= ssi->x && - root_y >= ssi->y && - root_x < ssi->x + ssi->width && - root_y < ssi->y + ssi->height) - { - if (p->verbose_p) - fprintf (stderr, "%s: mouse is on screen %d of %d\n", - blurb(), i, si->nscreens); - return i; - } + /* This will take several seconds to complete. */ + user_active_p = fade_screens (si->app, si->dpy, + current_windows, si->nscreens, + p->fade_seconds / 1000.0, + True, /* out_p */ + True, /* from_desktop_p */ + &si->fade_state); + free (current_windows); + + if (!p->verbose_p) + ; + else if (user_active_p) + fprintf (stderr, "%s: fading aborted\n", blurb()); + else + fprintf (stderr, "%s: fading done\n", blurb()); } - /* couldn't figure out where the mouse is? Oh well. */ - return 0; -} - -static int -get_idle_time (saver_info *si) -{ - int ret = 0; - XScreenSaverInfo *info; - info = XScreenSaverAllocInfo (); - if (info != NULL && XScreenSaverQueryInfo (si->dpy, DefaultRootWindow (si->dpy), info)) - ret = info->idle / 1000; - XFree (info); - return ret; -} + raise_windows (si); + reset_watchdog_timer (si); -Bool -blank_screen (saver_info *si) -{ - int i; - Bool ok; - Window w; - int mscreen; - int idle_s = 0; - Bool screen_on = True; - const char *euc = si->prefs.external_ungrab_command; - - if (euc && *euc) - { - screen_on = monitor_powered_on_p (si); - if (screen_on) - idle_s = get_idle_time(si); - } - /* Note: we do our grabs on the root window, not on the screensaver window. - If we grabbed on the saver window, then the demo mode and lock dialog - boxes wouldn't get any events. - - By "the root window", we mean "the root window that contains the mouse." - We use to always grab the mouse on screen 0, but that has the effect of - moving the mouse to screen 0 from whichever screen it was on, on - multi-head systems. - */ - mscreen = mouse_screen (si); - w = RootWindowOfScreen(si->screens[mscreen].screen); - ok = grab_keyboard_and_mouse (si, w, - (si->demoing_p ? 0 : si->screens[0].cursor), - mscreen); - - -# if 0 - if (si->using_mit_saver_extension || si->using_sgi_saver_extension) - /* If we're using a server extension, then failure to get a grab is - not a big deal -- even without the grab, we will still be able - to un-blank when there is user activity, since the server will - tell us. */ - /* #### No, that's not true: if we don't have a keyboard grab, - then we can't read passwords to unlock. - */ - ok = True; -# endif /* 0 */ - - if (!ok) - return False; - - if (euc && *euc) - { - /* ungrab hooks might have messed up idle time -- account for that. - this might look like the math is backwards, but this handles both - cases -- ungrab hook did mess up idle time, and ungrab hook - did *not* mess up idle time. In the latter case the value will be - zero or negative, so it gets clamped to zero. - */ - if (screen_on) - { - saver_preferences *p = &si->prefs; - store_dpms_offset (idle_s - get_idle_time(si)); - sync_server_dpms_settings (si->dpy, - (p->dpms_enabled_p && - p->mode != DONT_BLANK), - p->dpms_quickoff_p, - p->dpms_standby / 1000, - p->dpms_suspend / 1000, - p->dpms_off / 1000, - False); - } - else /* screen was off before, might have been turned on by fake events */ - monitor_power_on (si, False); - } + /* user_active_p means that the user aborted the fade-out -- but that does + not mean that we are necessarily about to exit. If we are locking, then + the user activity will cause the unlock dialog to appear, but + authentication might not succeed. */ for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (ssi->real_screen_p) - save_real_vroot (ssi); - store_vroot_property (si->dpy, - ssi->screensaver_window, - ssi->screensaver_window); - -#ifdef HAVE_XF86VMODE - { - int ev, er; - if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) || - !safe_XF86VidModeGetViewPort (si->dpy, i, - &ssi->blank_vp_x, - &ssi->blank_vp_y)) - ssi->blank_vp_x = ssi->blank_vp_y = -1; - } -#endif /* HAVE_XF86VMODE */ - } - - raise_window (si, False, False, False); + /* This also queues each screen's cycle_timer. */ + spawn_screenhack (&si->screens[i]); - si->screen_blanked_p = True; - si->blank_time = time ((time_t *) 0); - si->last_wall_clock_time = 0; + /* Turn off "next" and "prev" modes after they have happened once. */ + if (si->selection_mode < 0) + si->selection_mode = 0; - store_saver_status (si); /* store blank time */ - - return True; + /* If we are blanking only, optionally power down monitor right now. */ + if (p->mode == BLANK_ONLY && + p->dpms_quickoff_p) + monitor_power_on (si, False); } +/* Called only once, upon receipt of SIGTERM, just before exiting. + */ void unblank_screen (saver_info *si) { saver_preferences *p = &si->prefs; - Bool unfade_p = (si->fading_possible_p && p->unfade_p); + Bool unfade_p = p->unfade_p; int i; - store_dpms_offset (0); monitor_power_on (si, True); - reset_watchdog_timer (si, False); if (si->demoing_p) unfade_p = False; if (unfade_p) { + double seconds = p->fade_seconds / 1000.0; + double ratio = 1/3.0; Window *current_windows = (Window *) calloc(sizeof(Window), si->nscreens); + Bool interrupted_p = False; for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; current_windows[i] = ssi->screensaver_window; - /* Ensure that the default background of the window is really black, - not a pixmap or something. (This does not clear the window.) */ - XSetWindowBackground (si->dpy, ssi->screensaver_window, - ssi->black_pixel); } if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb()); + monitor_power_on (si, True); + + /* When we fade in to the desktop, first fade out from the saver to + black, then fade in from black to the desktop. */ + interrupted_p = fade_screens (si->app, si->dpy, + current_windows, si->nscreens, + seconds * ratio, + True, /* out_p */ + False, /* from_desktop_p */ + &si->fade_state); + if (! interrupted_p) + interrupted_p = fade_screens (si->app, si->dpy, + current_windows, si->nscreens, + seconds * (1-ratio), + False, /* out_p */ + False, /* from_desktop_p */ + &si->fade_state); + free (current_windows); - XSync (si->dpy, False); - XGrabServer (si->dpy); /* ############ DANGER! */ - XSync (si->dpy, False); - - /* Clear the stderr layer on each screen. - */ - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - clear_stderr (ssi); - } - - XUngrabServer (si->dpy); - XSync (si->dpy, False); /* ###### (danger over) */ - - fade_screens (si->dpy, 0, - current_windows, si->nscreens, - p->fade_seconds/1000, p->fade_ticks, - False, False); - - free(current_windows); - current_windows = 0; - - if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb()); + if (p->verbose_p) + fprintf (stderr, "%s: unfading done%s\n", blurb(), + (interrupted_p ? " (interrupted)" : "")); } else { @@ -1850,118 +557,33 @@ unblank_screen (saver_info *si) { Colormap c = DefaultColormapOfScreen (ssi->screen); /* avoid technicolor */ + XSetWindowBackground (si->dpy, ssi->screensaver_window, + BlackPixelOfScreen (ssi->screen)); XClearWindow (si->dpy, ssi->screensaver_window); if (c) XInstallColormap (si->dpy, c); } XUnmapWindow (si->dpy, ssi->screensaver_window); } } - - - /* If the focus window does has a non-default colormap, then install - that colormap as well. (On SGIs, this will cause both the root map - and the focus map to be installed simultaneously. It'd be nice to - pick up the other colormaps that had been installed, too; perhaps - XListInstalledColormaps could be used for that?) - */ - { - Window focus = 0; - int revert_to; - XSync (si->dpy, False); - XGetInputFocus (si->dpy, &focus, &revert_to); - if (focus && focus != PointerRoot && focus != None) - { - XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler); - XWindowAttributes xgwa; - xgwa.colormap = 0; - if (XGetWindowAttributes (si->dpy, focus, &xgwa) && - xgwa.colormap && - xgwa.colormap != DefaultColormapOfScreen (xgwa.screen)) - XInstallColormap (si->dpy, xgwa.colormap); - XSetErrorHandler (old_handler); - } - } - - - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p); - } - - store_saver_status (si); /* store unblank time */ - ungrab_keyboard_and_mouse (si); - restore_real_vroot (si); - - /* Unmap the windows a second time, dammit -- just to avoid a race - with the screen-grabbing hacks. (I'm not sure if this is really - necessary; I'm stabbing in the dark now.) - */ - for (i = 0; i < si->nscreens; i++) - XUnmapWindow (si->dpy, si->screens[i].screensaver_window); - - si->screen_blanked_p = False; - si->blank_time = time ((time_t *) 0); - si->last_wall_clock_time = 0; - - store_saver_status (si); /* store unblank time */ -} - - -/* Transfer any grabs from the old window to the new. - Actually I think none of this is necessary, since we always - hold our grabs on the root window, but I wrote this before - re-discovering that... - */ -static void -maybe_transfer_grabs (saver_screen_info *ssi, - Window old_w, Window new_w, - int new_screen_no) -{ - saver_info *si = ssi->global; - - /* If the old window held our mouse grab, transfer the grab to the new - window. (Grab the server while so doing, to avoid a race condition.) - */ - if (old_w == si->mouse_grab_window) - { - XGrabServer (si->dpy); /* ############ DANGER! */ - ungrab_mouse (si); - grab_mouse (si, ssi->screensaver_window, - (si->demoing_p ? 0 : ssi->cursor), - new_screen_no); - XUngrabServer (si->dpy); - XSync (si->dpy, False); /* ###### (danger over) */ - } - - /* If the old window held our keyboard grab, transfer the grab to the new - window. (Grab the server while so doing, to avoid a race condition.) - */ - if (old_w == si->keyboard_grab_window) - { - XGrabServer (si->dpy); /* ############ DANGER! */ - ungrab_kbd(si); - grab_kbd(si, ssi->screensaver_window, ssi->number); - XUngrabServer (si->dpy); - XSync (si->dpy, False); /* ###### (danger over) */ - } } static Visual * get_screen_gl_visual (saver_info *si, int real_screen_number) { - int i; int nscreens = ScreenCount (si->dpy); if (! si->best_gl_visuals) - si->best_gl_visuals = (Visual **) - calloc (nscreens + 1, sizeof (*si->best_gl_visuals)); + { + int i; + si->best_gl_visuals = (Visual **) + calloc (nscreens + 1, sizeof (*si->best_gl_visuals)); - for (i = 0; i < nscreens; i++) - if (! si->best_gl_visuals[i]) - si->best_gl_visuals[i] = - get_best_gl_visual (si, ScreenOfDisplay (si->dpy, i)); + for (i = 0; i < nscreens; i++) + if (! si->best_gl_visuals[i]) + si->best_gl_visuals[i] = + get_best_gl_visual (si, ScreenOfDisplay (si->dpy, i)); + } if (real_screen_number < 0 || real_screen_number >= nscreens) abort(); return si->best_gl_visuals[real_screen_number]; @@ -1999,28 +621,21 @@ select_visual (saver_screen_info *ssi, const char *visual_name) if (visual_name && *visual_name) { - if (!strcmp(visual_name, "default-i") || - !strcmp(visual_name, "Default-i") || - !strcmp(visual_name, "Default-I") - ) + if (!strcasecmp(visual_name, "default-i")) { visual_name = "default"; install_cmap_p = True; } - else if (!strcmp(visual_name, "default-n") || - !strcmp(visual_name, "Default-n") || - !strcmp(visual_name, "Default-N")) + else if (!strcasecmp(visual_name, "default-n")) { visual_name = "default"; install_cmap_p = False; } - else if (!strcmp(visual_name, "gl") || - !strcmp(visual_name, "Gl") || - !strcmp(visual_name, "GL")) + else if (!strcasecmp(visual_name, "GL")) { new_v = get_screen_gl_visual (si, ssi->real_screen_number); if (!new_v && p->verbose_p) - fprintf (stderr, "%s: no GL visuals.\n", progname); + fprintf (stderr, "%s: no GL visuals\n", blurb()); } if (!new_v) @@ -2052,8 +667,10 @@ select_visual (saver_screen_info *ssi, const char *visual_name) if (p->verbose_p) { +#if 0 fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number); describe_visual (stderr, ssi->screen, new_v, install_cmap_p); +#endif #if 0 fprintf (stderr, "%s: from ", blurb()); describe_visual (stderr, ssi->screen, ssi->current_visual, @@ -2061,39 +678,334 @@ select_visual (saver_screen_info *ssi, const char *visual_name) #endif } - reset_stderr (ssi); ssi->current_visual = new_v; ssi->current_depth = visual_depth(ssi->screen, new_v); ssi->cmap = 0; ssi->screensaver_window = 0; initialize_screensaver_window_1 (ssi); - - /* stderr_overlay_window is a child of screensaver_window, so we need - to destroy that as well (actually, we just need to invalidate and - drop our pointers to it, but this will destroy it, which is ok so - long as it happens before old_w itself is destroyed.) */ - reset_stderr (ssi); - - raise_window (si, True, True, False); - store_vroot_property (si->dpy, - ssi->screensaver_window, ssi->screensaver_window); - - /* Transfer any grabs from the old window to the new. */ - maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window, ssi->number); + raise_window (ssi); /* Now we can destroy the old window without horking our grabs. */ XDestroyWindow (si->dpy, old_w); - if (p->verbose_p) - fprintf (stderr, "%s: %d: destroyed old saver window 0x%lx.\n", + if (p->verbose_p > 1) + fprintf (stderr, "%s: %d: destroyed old saver window 0x%lx\n", blurb(), ssi->number, (unsigned long) old_w); if (old_c && - old_c != DefaultColormapOfScreen (ssi->screen) && - old_c != ssi->demo_cmap) + old_c != DefaultColormapOfScreen (ssi->screen)) XFreeColormap (si->dpy, old_c); } return got_it; } + + +/* Synchronize the contents of si->ssi to the current state of the monitors. + Doesn't change anything if nothing has changed; otherwise, alters and + reuses existing saver_screen_info structs as much as possible. + Returns True if anything changed. + */ +Bool +update_screen_layout (saver_info *si) +{ + monitor **monitors = scan_monitors (si->dpy); + int count = 0; + int good_count = 0; + int i, j; + int seen_screens[100] = { 0, }; + + if (! monitor_layouts_differ_p (monitors, si->monitor_layout)) + { + free_monitors (monitors); + return False; + } + + free_monitors (si->monitor_layout); + si->monitor_layout = monitors; + check_monitor_sanity (si->monitor_layout); + + while (monitors[count]) + { + if (monitors[count]->sanity == S_SANE) + good_count++; + count++; + } + + if (si->ssi_count == 0) + { + si->ssi_count = 10; + si->screens = (saver_screen_info *) + calloc (sizeof(*si->screens), si->ssi_count); + } + + if (si->ssi_count <= good_count) + { + si->ssi_count = good_count + 10; + si->screens = (saver_screen_info *) + realloc (si->screens, sizeof(*si->screens) * si->ssi_count); + memset (si->screens + si->nscreens, 0, + sizeof(*si->screens) * (si->ssi_count - si->nscreens)); + } + + if (! si->screens) abort(); + + si->nscreens = good_count; + + /* Regenerate the list of GL visuals as needed. */ + if (si->best_gl_visuals) + free (si->best_gl_visuals); + si->best_gl_visuals = 0; + + for (i = 0, j = 0; i < count; i++) + { + monitor *m = monitors[i]; + saver_screen_info *ssi = &si->screens[j]; + int sn; + if (monitors[i]->sanity != S_SANE) continue; + + ssi->global = si; + ssi->number = j; + + sn = screen_number (m->screen); + ssi->screen = m->screen; + ssi->real_screen_number = sn; + ssi->real_screen_p = (seen_screens[sn] == 0); + seen_screens[sn]++; + + ssi->default_visual = + get_visual_resource (ssi->screen, "visualID", "VisualID", False); + ssi->current_visual = ssi->default_visual; + ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual); + + ssi->x = m->x; + ssi->y = m->y; + ssi->width = m->width; + ssi->height = m->height; + +# ifndef DEBUG_MULTISCREEN + { + saver_preferences *p = &si->prefs; + if (p->debug_p +# ifdef QUAD_MODE + && !p->quad_p +# endif + ) + ssi->width /= 2; + } +# endif + + j++; + } + + return True; +} + + +/* When the screensaver is active, this timer will periodically change + the running program. Each screen has its own timer. + */ +void +cycle_timer (XtPointer closure, XtIntervalId *id) +{ + saver_screen_info *ssi = (saver_screen_info *) closure; + saver_info *si = ssi->global; + + if (ssi->error_dialog) + { + XDestroyWindow (si->dpy, ssi->error_dialog); + ssi->error_dialog = 0; + } + + maybe_reload_init_file (si); + kill_screenhack (ssi); + raise_window (ssi); + + /* We could do a fade-out of just this screen here; but that would only work + if the fade method is SHM, not gamma or colormap. It would also only + look right if the cycle timers never fire at the same time, which is + currently the case. */ + + XSync (si->dpy, False); + spawn_screenhack (ssi); /* This also re-adds the cycle_id timer */ +} + + +/* Called when a screenhack has exited unexpectedly. + We print a notification on the window, and in a little while, launch + a new hack (rather than waiting for the cycle timer to fire). + */ +void +screenhack_obituary (saver_screen_info *ssi, + const char *name, const char *error) +{ + saver_info *si = ssi->global; + saver_preferences *p = &si->prefs; + Time how_long = p->cycle; + Time max = 1000 * 60; /* Message stays up no longer than this */ + Window window; + Visual *visual; + XSetWindowAttributes attrs; + XWindowChanges changes; + unsigned long attrmask; + XftFont *font; + XftColor fg; + XftDraw *xftdraw; + XGlyphInfo overall; + XGCValues gcv; + GC gc; + char *fn, *cn; + char buf[255]; + int x, y, pad; + int bw = 4; + Colormap cmap; + + /* Restart the cycle timer, to take down the error dialog and launch + a new hack. + */ + if (how_long > max) + how_long = max; + if (ssi->cycle_id) + XtRemoveTimeOut (ssi->cycle_id); + ssi->cycle_id = + XtAppAddTimeOut (si->app, how_long, cycle_timer, (XtPointer) ssi); + ssi->cycle_at = time ((time_t *) 0) + how_long / 1000; + if (p->verbose_p) + fprintf (stderr, "%s: %d: cycling in %lu sec\n", blurb(), ssi->number, + how_long / 1000); + + /* Render an error message while we wait. + + We can't just render text on top of ssi->screensaver_window because + if there was an OpenGL hack running on it, Xlib graphics might not + show up at all. Likewise, creating a sub-window doesn't work. + So it must be a top-level override-redirect window atop the saver. + */ + cmap = ssi->cmap ? ssi->cmap : DefaultColormapOfScreen (ssi->screen); + window = ssi->error_dialog; + if (window) XDestroyWindow (si->dpy, window); + attrs.override_redirect = True; + attrs.background_pixel = ssi->black_pixel; + attrs.border_pixel = ssi->black_pixel; + attrs.backing_store = Always; + attrs.colormap = cmap; + attrmask = (CWOverrideRedirect | CWBackPixel | CWBorderPixel | + CWBackingStore | CWColormap); + visual = ssi->current_visual; + window = ssi->error_dialog = + XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen), + 0, 0, 1, 1, 0, ssi->current_depth, InputOutput, visual, + attrmask, &attrs); + + fn = get_string_resource (si->dpy, "errorFont", "Font"); + cn = get_string_resource (si->dpy, "errorColor", "Color"); + if (!fn || !*fn) fn = strdup ("monospace bold 16"); + if (!cn || !*cn) cn = strdup ("#FF0000"); + + font = load_xft_font_retry (si->dpy, screen_number (ssi->screen), fn); + XftColorAllocName (si->dpy, visual, cmap, cn, &fg); + xftdraw = XftDrawCreate (si->dpy, window, visual, cmap); + + gcv.foreground = + get_pixel_resource (si->dpy, cmap, "errorColor", "Color"); + gcv.line_width = bw; + gc = XCreateGC (si->dpy, window, GCForeground | GCLineWidth, &gcv); + + sprintf (buf, "\"%.100s\" %.100s", name, error); + + XftTextExtentsUtf8 (si->dpy, font, (FcChar8 *) buf, strlen(buf), &overall); + x = (ssi->width - overall.width) / 2; + y = (ssi->height - overall.height) / 2 + font->ascent; + pad = bw + font->ascent * 2; + + attrmask = CWX | CWY | CWWidth | CWHeight; + changes.x = ssi->x + x - pad; + changes.y = ssi->y + y - (font->ascent + pad); + changes.width = overall.width + pad * 2; + changes.height = font->ascent + font->descent + pad * 2; + XConfigureWindow (si->dpy, window, attrmask, &changes); + xscreensaver_set_wm_atoms (si->dpy, window, changes.width, changes.height, + ssi->screensaver_window); + XMapRaised (si->dpy, window); + XClearWindow (si->dpy, window); + + XDrawRectangle (si->dpy, window, gc, gcv.line_width/2, gcv.line_width/2, + changes.width - gcv.line_width, + changes.height - gcv.line_width); + + x = pad; + y = font->ascent + pad; + XftDrawStringUtf8 (xftdraw, &fg, font, x, y, (FcChar8 *) buf, strlen (buf)); + XSync (si->dpy, False); + + XFreeGC (si->dpy, gc); + XftDrawDestroy (xftdraw); + /* XftColorFree (si->dpy, visual, cmap, &fg); */ + XftFontClose (si->dpy, font); + free (fn); + free (cn); +} + + +/* This timer goes off every few minutes to try and clean up anything that has + gone wrong. It raises the windows, in case some other window has been + mapped on top of them, and re-sets the server's DPMS settings. + + Maybe we should respond to Expose events to detect when another window has + raised above us and re-raise ourselves sooner. But that would result in us + fighting against "xscreensaver-auth" which tries very hard to be on top. + */ +static void +watchdog_timer (XtPointer closure, XtIntervalId *id) +{ + saver_info *si = (saver_info *) closure; + saver_preferences *p = &si->prefs; + + /* If the DPMS settings on the server have changed, change them back to + what ~/.xscreensaver says they should be. */ + sync_server_dpms_settings (si->dpy, p); + + if (si->prefs.debug_p) + fprintf (stderr, "%s: watchdog timer raising screen\n", blurb()); + + raise_windows (si); + + if (any_screenhacks_running_p (si) && + !monitor_powered_on_p (si->dpy)) + { + int i; + if (si->prefs.verbose_p) + fprintf (stderr, + "%s: monitor has powered down; killing running hacks\n", + blurb()); + for (i = 0; i < si->nscreens; i++) + kill_screenhack (&si->screens[i]); + } + + /* Re-schedule this timer. The watchdog timer defaults to a bit less + than the hack cycle period, but is never longer than one hour. + */ + si->watchdog_id = 0; + reset_watchdog_timer (si); +} + + +static void +reset_watchdog_timer (saver_info *si) +{ + saver_preferences *p = &si->prefs; + + if (si->watchdog_id) + { + XtRemoveTimeOut (si->watchdog_id); + si->watchdog_id = 0; + } + + if (p->watchdog_timeout <= 0) return; + si->watchdog_id = XtAppAddTimeOut (si->app, p->watchdog_timeout, + watchdog_timer, (XtPointer) si); + if (p->debug_p) + fprintf (stderr, "%s: restarting watchdog_timer (%ld, %ld)\n", + blurb(), p->watchdog_timeout, si->watchdog_id); +} diff --git a/driver/xdpyinfo.c b/driver/xdpyinfo.c index 9f67966..4bb071c 100644 --- a/driver/xdpyinfo.c +++ b/driver/xdpyinfo.c @@ -34,9 +34,14 @@ in this Software without prior written authorization from The Open Group. * -DHAVE_XRECORD */ +#include +#include +#include + #include #include #include /* for CARD32 */ +#include #include #ifdef HAVE_XIE #include @@ -69,18 +74,16 @@ in this Software without prior written authorization from The Open Group. char *ProgramName; Bool queryExtensions = False; -static int StrCmp(a, b) - char **a, **b; +static int StrCmp(const void *a, const void *b) { - return strcmp(*a, *b); + return strcmp(*((char **)a), *((char **)b)); } #ifdef HAVE_GLX /* Added by jwz, 11-Nov-99 */ static void -print_glx_versions (dpy) - Display *dpy; +print_glx_versions (Display *dpy) { /* Note: with Mesa 3.0, this lies: it prints the info from the client's GL library, rather than the info from the GLX server. @@ -100,9 +103,7 @@ print_glx_versions (dpy) } static void -print_glx_visual_info (dpy, vip) - Display *dpy; - XVisualInfo *vip; +print_glx_visual_info (Display *dpy, XVisualInfo *vip) { int status, value = False; @@ -271,8 +272,7 @@ struct overlay_list static struct overlay_list *overlays = 0; static void -find_overlay_info (dpy) - Display *dpy; +find_overlay_info (Display *dpy) { int screen; Atom OVERLAY = XInternAtom (dpy, "SERVER_OVERLAY_VISUALS", False); @@ -307,8 +307,7 @@ find_overlay_info (dpy) } static void -print_overlay_visual_info (vip) - XVisualInfo *vip; +print_overlay_visual_info (XVisualInfo *vip) { int i; int vis = vip->visualid; @@ -327,7 +326,7 @@ print_overlay_visual_info (vip) if (ov->transparency == 1) printf ("transparent pixel %lu\n", (unsigned long) ov->value); else if (ov->transparency == 2) - printf ("transparent mask 0x%x\n", (unsigned long) ov->value); + printf ("transparent mask 0x%lx\n", (unsigned long) ov->value); else printf ("opaque\n"); } @@ -335,9 +334,8 @@ print_overlay_visual_info (vip) #endif /* HAVE_OVERLAY */ -void -print_extension_info (dpy) - Display *dpy; +static void +print_extension_info (Display *dpy) { int n = 0; char **extlist = XListExtensions (dpy, &n); @@ -367,9 +365,8 @@ print_extension_info (dpy) } } -void -print_display_info (dpy) - Display *dpy; +static void +print_display_info (Display *dpy) { char dummybuf[40]; char *cp; @@ -393,7 +390,7 @@ print_display_info (dpy) req_size = XExtendedMaxRequestSize (dpy); if (!req_size) req_size = XMaxRequestSize (dpy); printf ("maximum request size: %ld bytes\n", req_size * 4); - printf ("motion buffer size: %d\n", XDisplayMotionBufferSize (dpy)); + printf ("motion buffer size: %ld\n", XDisplayMotionBufferSize (dpy)); switch (BitmapBitOrder (dpy)) { case LSBFirst: cp = "LSBFirst"; break; @@ -470,9 +467,8 @@ print_display_info (dpy) printf ("number of screens: %d\n", ScreenCount (dpy)); } -void -print_visual_info (vip) - XVisualInfo *vip; +static void +print_visual_info (XVisualInfo *vip) { char errorbuf[40]; /* for sprintfing into */ char *class = NULL; /* for printing */ @@ -507,10 +503,15 @@ print_visual_info (vip) vip->bits_per_rgb); } -void -print_screen_info (dpy, scr) - Display *dpy; - int scr; +static int print_event_mask ( + char *buf, /* string to write into */ + int lastcol, /* strlen(buf)+1 */ + int indent, /* amount by which to indent */ + long mask); /* event mask */ + + +static void +print_screen_info (Display *dpy, int scr) { Screen *s = ScreenOfDisplay (dpy, scr); /* opaque structure */ XVisualInfo viproto; /* fill in for getting info */ @@ -565,7 +566,7 @@ print_screen_info (dpy, scr) printf (" default colormap: 0x%lx\n", DefaultColormap (dpy, scr)); printf (" default number of colormap cells: %d\n", DisplayCells (dpy, scr)); - printf (" preallocated pixels: black %d, white %d\n", + printf (" preallocated pixels: black %lu, white %lu\n", BlackPixel (dpy, scr), WhitePixel (dpy, scr)); printf (" options: backing-store %s, save-unders %s\n", (DoesBackingStore (s) == NotUseful) ? no : @@ -637,11 +638,11 @@ static struct _event_table { { "OwnerGrabButtonMask ", OwnerGrabButtonMask }, { NULL, 0 }}; -int print_event_mask (buf, lastcol, indent, mask) - char *buf; /* string to write into */ - int lastcol; /* strlen(buf)+1 */ - int indent; /* amount by which to indent */ - long mask; /* event mask */ +static int print_event_mask ( + char *buf, /* string to write into */ + int lastcol, /* strlen(buf)+1 */ + int indent, /* amount by which to indent */ + long mask) /* event mask */ { struct _event_table *etp; int len; @@ -673,11 +674,9 @@ int print_event_mask (buf, lastcol, indent, mask) return (bitsfound); } -void -print_standard_extension_info(dpy, extname, majorrev, minorrev) - Display *dpy; - char *extname; - int majorrev, minorrev; +static void +print_standard_extension_info(Display *dpy, char *extname, + int majorrev, int minorrev) { int opcode, event, error; @@ -692,10 +691,8 @@ print_standard_extension_info(dpy, extname, majorrev, minorrev) printf("\n"); } -int -print_multibuf_info(dpy, extname) - Display *dpy; - char *extname; +static int +print_multibuf_info(Display *dpy, char *extname) { int i, j; /* temp variable: iterator */ int nmono, nstereo; /* count */ @@ -850,10 +847,8 @@ print_sync_info(dpy, extname) } #endif /* HAVE_XSYNC */ -int -print_shape_info(dpy, extname) - Display *dpy; - char *extname; +static int +print_shape_info(Display *dpy, char *extname) { int majorrev, minorrev; @@ -979,9 +974,8 @@ ExtensionPrintInfo known_extensions[] = int num_known_extensions = sizeof known_extensions / sizeof known_extensions[0]; -void -print_known_extensions(f) - FILE *f; +static void +print_known_extensions(FILE *f) { int i; for (i = 0; i < num_known_extensions; i++) @@ -990,9 +984,8 @@ print_known_extensions(f) } } -void -mark_extension_for_printing(extname) - char *extname; +static void +mark_extension_for_printing(char *extname) { int i; @@ -1015,9 +1008,8 @@ mark_extension_for_printing(extname) } } -void -print_marked_extensions(dpy) - Display *dpy; +static void +print_marked_extensions(Display *dpy) { int i; for (i = 0; i < num_known_extensions; i++) @@ -1035,7 +1027,7 @@ print_marked_extensions(dpy) } } -static void usage () +static void usage (void) { fprintf (stderr, "usage: %s [options]\n", ProgramName); fprintf (stderr, "-display displayname\tserver to query\n"); @@ -1047,15 +1039,11 @@ static void usage () exit (1); } -int main (argc, argv) - int argc; - char *argv[]; +int main (int argc, char *argv[]) { Display *dpy; /* X connection */ char *displayname = NULL; /* server to contact */ int i; /* temp variable: iterator */ - Bool multibuf = False; - int mbuf_event_base, mbuf_error_base; ProgramName = argv[0]; @@ -1077,7 +1065,7 @@ int main (argc, argv) dpy = XOpenDisplay (displayname); if (!dpy) { - fprintf (stderr, "%s: unable to open display \"%s\".\n", + fprintf (stderr, "%s: unable to open display \"%s\"\n", ProgramName, XDisplayName (displayname)); exit (1); } diff --git a/driver/xinput.c b/driver/xinput.c new file mode 100644 index 0000000..3b21db0 --- /dev/null +++ b/driver/xinput.c @@ -0,0 +1,381 @@ +/* xscreensaver, Copyright © 1991-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#include "blurb.h" +#include "xinput.h" + +extern Bool debug_p; + +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + + +/* Initialize the XInput2 extension. Returns True on success. + */ +Bool +init_xinput (Display *dpy, int *opcode_ret) +{ + int nscreens = ScreenCount (dpy); + XIEventMask evmasks[1]; + unsigned char mask1[(XI_LASTEVENT + 7)/8]; + int major, minor; + int xi_opcode, ev, err; + int i; + int ndevs = 0; + XIDeviceInfo *devs; + + if (!XQueryExtension (dpy, "XInputExtension", &xi_opcode, &ev, &err)) + { + fprintf (stderr, "%s: XInput extension missing\n", blurb()); + return False; + } + + major = 2; /* Desired version */ + minor = 2; + if (XIQueryVersion (dpy, &major, &minor) != Success) + { + fprintf (stderr, "%s: server only supports XInput %d.%d\n", + blurb(), major, minor); + return False; + } + + if (verbose_p) + fprintf (stderr, "%s: XInput version %d.%d\n", blurb(), major, minor); + + memset (mask1, 0, sizeof(mask1)); + + XISetMask (mask1, XI_RawMotion); + XISetMask (mask1, XI_RawKeyPress); + XISetMask (mask1, XI_RawKeyRelease); + XISetMask (mask1, XI_RawButtonPress); + XISetMask (mask1, XI_RawButtonRelease); + XISetMask (mask1, XI_RawTouchBegin); + XISetMask (mask1, XI_RawTouchUpdate); + XISetMask (mask1, XI_RawTouchEnd); + + /* If we use XIAllDevices instead, we get double events. */ + evmasks[0].deviceid = XIAllMasterDevices; + evmasks[0].mask_len = sizeof(mask1); + evmasks[0].mask = mask1; + + for (i = 0; i < nscreens; i++) + { + Window root = RootWindow (dpy, i); + if (XISelectEvents (dpy, root, evmasks, countof(evmasks)) != Success) + { + fprintf (stderr, "%s: XISelectEvents failed\n", blurb()); + return False; + } + } + + XFlush (dpy); + + devs = XIQueryDevice (dpy, XIAllDevices, &ndevs); + if (!ndevs) + { + fprintf (stderr, "%s: XInput: no devices\n", blurb()); + return False; + } + + if (verbose_p) + for (i = 0; i < ndevs; i++) + { + XIDeviceInfo *d = &devs[i]; + fprintf (stderr, "%s: device %2d/%d: %s: %s\n", + blurb(), d->deviceid, d->attachment, + (d->use == XIMasterPointer ? "MP" : + d->use == XIMasterKeyboard ? "MK" : + d->use == XISlavePointer ? "SP" : + d->use == XISlaveKeyboard ? "SK" : + d->use == XIFloatingSlave ? "FS" : "??"), + d->name); + } + + XIFreeDeviceInfo (devs); + *opcode_ret = xi_opcode; + return True; +} + + +/* Convert an XInput2 event to corresponding old-school Xlib event. + Returns true on success. + */ +Bool +xinput_event_to_xlib (int evtype, XIDeviceEvent *in, XEvent *out) +{ + Display *dpy = in->display; + Bool ok = False; + + int root_x = 0, root_y = 0; + unsigned int mods = 0; + + /* The closest thing to actual documentation on XInput2 seems to be a series + of blog posts by Peter Hutterer. There's basically nothing about it on + www.x.org. In http://who-t.blogspot.com/2009/07/xi2-recipes-part-4.html + he says: + + "XIDeviceEvent [...] contains the state of the modifier keys [...] + The base modifiers are the ones currently pressed, latched the ones + pressed until a key is pressed that's configured to unlatch it (e.g. + some shift-capslock interactions have this behaviour) and finally + locked modifiers are the ones permanently active until unlocked + (default capslock behaviour in the US layout). The effective modifiers + are a bitwise OR of the three above - which is essentially equivalent + to the modifiers state supplied in the core protocol events." + + However, I'm seeing random noise in the various XIDeviceEvent.mods fields. + Nonsensical values like base = 0x6045FB3D. So, let's poll the actual + modifiers from XQueryPointer. This can race: maybe the modifier state + changed between when the server generated the keyboard event, and when + we receive it and poll. However, if an actual human is typing and + releasing their modifier keys on such a tight timeframe... that's + probably already not going well. + + I'm also seeing random noise in the event_xy and root_xy fields in + motion events. So just always use XQueryPointer. + */ + switch (evtype) { + case XI_RawKeyPress: + case XI_RawKeyRelease: + case XI_RawButtonPress: + case XI_RawButtonRelease: + case XI_RawMotion: + { + Window root_ret, child_ret; + int win_x, win_y; + int i; + for (i = 0; i < ScreenCount (dpy); i++) /* query on correct screen */ + if (XQueryPointer (dpy, RootWindow (dpy, i), + &root_ret, &child_ret, &root_x, &root_y, + &win_x, &win_y, &mods)) + break; + } + default: break; + } + + switch (evtype) { + case XI_RawKeyPress: + case XI_RawKeyRelease: + out->xkey.type = (evtype == XI_RawKeyPress ? KeyPress : KeyRelease); + out->xkey.display = in->display; + out->xkey.window = in->event; + out->xkey.root = in->root; + out->xkey.subwindow = in->child; + out->xkey.time = in->time; + out->xkey.x = root_x; + out->xkey.y = root_y; + out->xkey.x_root = root_x; + out->xkey.y_root = root_y; + out->xkey.state = mods; + out->xkey.keycode = in->detail; + ok = True; + break; + case XI_RawButtonPress: + case XI_RawButtonRelease: + out->xbutton.type = (evtype == XI_RawButtonPress + ? ButtonPress : ButtonRelease); + out->xbutton.display = in->display; + out->xbutton.window = in->event; + out->xbutton.root = in->root; + out->xbutton.subwindow = in->child; + out->xbutton.time = in->time; + out->xbutton.x = root_x; + out->xbutton.y = root_y; + out->xbutton.x_root = root_x; + out->xbutton.y_root = root_y; + out->xbutton.state = mods; + out->xbutton.button = in->detail; + ok = True; + break; + case XI_RawMotion: + out->xmotion.type = MotionNotify; + out->xmotion.display = in->display; + out->xmotion.window = in->event; + out->xmotion.root = in->root; + out->xmotion.subwindow = in->child; + out->xmotion.time = in->time; + out->xmotion.x = root_x; + out->xmotion.y = root_y; + out->xmotion.x_root = root_x; + out->xmotion.y_root = root_y; + out->xmotion.state = mods; + ok = True; + break; + default: + break; + } + + return ok; +} + + +static void +print_kbd_event (XEvent *xev, XComposeStatus *compose, Bool x11_p) +{ + if (debug_p) /* Passwords show up in plaintext! */ + { + KeySym keysym = 0; + char c[100]; + char M[100], *mods = M; + int n = XLookupString (&xev->xkey, c, sizeof(c)-1, &keysym, compose); + const char *ks = keysym ? XKeysymToString (keysym) : "NULL"; + c[n] = 0; + if (*c == '\n') strcpy (c, "\\n"); + else if (*c == '\r') strcpy (c, "\\r"); + else if (*c == '\t') strcpy (c, "\\t"); + + *mods = 0; + if (xev->xkey.state & ShiftMask) strcat (mods, "-Sh"); + if (xev->xkey.state & LockMask) strcat (mods, "-Lk"); + if (xev->xkey.state & ControlMask) strcat (mods, "-C"); + if (xev->xkey.state & Mod1Mask) strcat (mods, "-M1"); + if (xev->xkey.state & Mod2Mask) strcat (mods, "-M2"); + if (xev->xkey.state & Mod3Mask) strcat (mods, "-M3"); + if (xev->xkey.state & Mod4Mask) strcat (mods, "-M4"); + if (xev->xkey.state & Mod5Mask) strcat (mods, "-M5"); + if (*mods) mods++; + if (!*mods) strcat (mods, "0"); + + fprintf (stderr, "%s: %s 0x%02X %s %s \"%s\"\n", blurb(), + (x11_p + ? (xev->xkey.type == KeyPress + ? "X11 KeyPress " + : "X11 KeyRelease ") + : (xev->xkey.type == KeyPress + ? "XI_RawKeyPress " + : "XI_RawKeyRelease")), + xev->xkey.keycode, mods, ks, c); + } + else /* Log only that the KeyPress happened. */ + { + fprintf (stderr, "%s: X11 Key%s\n", blurb(), + (xev->xkey.type == KeyPress ? "Press " : "Release")); + } +} + + +void +print_xinput_event (Display *dpy, XEvent *xev, const char *desc) +{ + XIRawEvent *re; + + switch (xev->xany.type) { + case KeyPress: + case KeyRelease: + { + static XComposeStatus compose = { 0, }; + print_kbd_event (xev, &compose, True); + } + break; + + case ButtonPress: + case ButtonRelease: + fprintf (stderr, "%s: X11 Button%s %d %d\n", blurb(), + (xev->xany.type == ButtonPress ? "Press " : "Release"), + xev->xbutton.button, xev->xbutton.state); + break; + + case MotionNotify: + fprintf (stderr, "%s: X11 MotionNotify %4d, %-4d" + " %s\n", + blurb(), xev->xmotion.x_root, xev->xmotion.y_root, + (desc ? desc : "")); + break; + default: + break; + } + + if (xev->xany.type != GenericEvent) + return; /* not an XInput event */ + + if (!xev->xcookie.data) + XGetEventData (dpy, &xev->xcookie); + + re = xev->xcookie.data; + if (!re) return; /* Bogus XInput event */ + + switch (re->evtype) { + case XI_RawKeyPress: + case XI_RawKeyRelease: + if (debug_p) + { + /* Fake up an XKeyEvent in order to call XKeysymToString(). */ + XEvent ev2; + Bool ok = xinput_event_to_xlib (xev->xcookie.evtype, + (XIDeviceEvent *) re, + &ev2); + if (!ok) + fprintf (stderr, "%s: unable to translate XInput2 event\n", blurb()); + else + { + static XComposeStatus compose = { 0, }; + print_kbd_event (&ev2, &compose, False); + } + break; + } + else + fprintf (stderr, "%s: XI RawKey%s\n", blurb(), + (re->evtype == XI_RawKeyPress ? "Press " : "Release")); + break; + + case XI_RawButtonPress: + case XI_RawButtonRelease: + fprintf (stderr, "%s: XI RawButton%s %d\n", blurb(), + (re->evtype == XI_RawButtonPress ? "Press " : "Release"), + re->detail); + break; + + case XI_RawMotion: + if (verbose_p > 1) + { + Window root_ret, child_ret; + int root_x, root_y; + int win_x, win_y; + unsigned int mask; + XQueryPointer (dpy, DefaultRootWindow (dpy), + &root_ret, &child_ret, &root_x, &root_y, + &win_x, &win_y, &mask); + fprintf (stderr, + "%s: XI_RawMotion %4d, %-4d %7.02f, %-7.02f%s\n", + blurb(), root_x, root_y, re->raw_values[0], re->raw_values[1], + (desc ? desc : "")); + } + break; + + /* Touch-screens, possibly trackpads or tablets. */ + case XI_RawTouchBegin: + fprintf (stderr, "%s: XI RawTouchBegin\n", blurb()); + break; + case XI_RawTouchEnd: + fprintf (stderr, "%s: XI RawTouchEnd\n", blurb()); + break; + case XI_RawTouchUpdate: + if (verbose_p > 1) + fprintf (stderr, "%s: XI RawTouchUpdate\n", blurb()); + break; + + default: + fprintf (stderr, "%s: unknown XInput event %d\n", blurb(), re->type); + break; + } +} diff --git a/driver/xinput.h b/driver/xinput.h new file mode 100644 index 0000000..2ee20ee --- /dev/null +++ b/driver/xinput.h @@ -0,0 +1,19 @@ +/* xscreensaver, Copyright © 1991-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifndef __XSCREENSAVER_XINPUT_H__ +#define __XSCREENSAVER_XINPUT_H__ + +extern Bool init_xinput (Display *dpy, int *opcode_ret); +extern Bool xinput_event_to_xlib (int evtype, XIDeviceEvent *in, XEvent *out); +extern void print_xinput_event (Display *, XEvent *, const char *desc); + +#endif /* __XSCREENSAVER_XINPUT_H__ */ diff --git a/driver/xscreensaver-auth.c b/driver/xscreensaver-auth.c new file mode 100644 index 0000000..4344d20 --- /dev/null +++ b/driver/xscreensaver-auth.c @@ -0,0 +1,340 @@ +/* xscreensaver, Copyright © 1991-2021 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * XScreenSaver Daemon, version 6. + * + * This is "xscreensaver-auth" -- When the screen is locked and there is + * user activity, this program is launched to pop up a dialog, authenticate + * the user, and eventually exit with a status code indicating success or + * failure. See the comment atop xscreensaver.c for details of the division + * of powers. + * + * Flow of control within xscreensaver-auth: + * + * - Privileged password initialization (e.g. read /etc/shadow as root) + * - Disavow privileges + * - Unprivileged password initialization + * - Connect to X server + * - xss_authenticate (passwd.c) + * - Tries PAM, Kerberos, pwent, shadow passwords (passwd-*.c) until + * one of them works. Non-PAM methods are wrapped to act like PAM. + * - pam_conv calls our "conversation" function zero or more times. + * That function is expected to present messages to the user and/or + * to prompt the user to answer a question, wait for the answer, and + * return it. There might be only one question (the password) or + * there might be others, even multiple passwords. + * - xscreensaver_auth_conv (dialog.c) is our conversation function. + * - First time it is called, it creates the window. + * - Subsequent times, it reuses that window. + * - Runs an X11 event loop waiting for the user to complete + * or timeout, then returns the entered strings. + * - pam_conv takes the user input and returns success/failure. + * - xscreensaver_auth_finished is called to pop up a final dialog to + * present any error messages. + * - Exit with appropriate code. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_NLS +# include +# include +#endif + +#include "xscreensaver.h" +#include "version.h" +#include "atoms.h" +#include "yarandom.h" +#include "resources.h" +#include "visual.h" +#include "auth.h" + +#ifdef __GNUC__ + __extension__ /* shut up about "string length is greater than the length + ISO C89 compilers are required to support" when including + the .ad file... */ +#endif + +static char *defaults[] = { +#include "XScreenSaver_ad.h" + 0 +}; + + +char *progclass = 0; +Bool debug_p = False; + + +#ifdef HAVE_PROC_OOM +/* Linux systems have an "out-of-memory killer" that will nuke random procs in + low-memory situations. You'd think it would pick the process using the + most memory, but most of the time it seems to pick the process that would + be most comically inconvenient, such as your screen locker, or crond. + + Since killing "xscreensaver" unlocks the screen... that would be bad. + + This program, "xscreensaver-auth", is the part of the XScreenSaver daemon + that might need to be setuid for other reasons, so we handle the OOM killer + here. We could instead handle OOM in the "xscreensaver" program, but then + that program would *also* need to be setuid. + + So instead, "xscreensaver-auth" sets OOM immunity on its *parent* process. + That means that if you run it by hand, it will apply that immunity to the + parent shell. Maybe that's bad? I think I don't care. + + Linux >= 2.6.11: echo -17 > /proc/$$/oom_adj + Linux >= 2.6.37: echo -1000 > /proc/$$/oom_score_adj + + "An aircraft company discovered that it was cheaper to fly its planes with + less fuel on board. On rare occasions, however, the amount of fuel was + insufficient, and the plane would crash. In emergency cases, a passenger + was selected and thrown out of the plane. When necessary, the procedure + was repeated." + + https://lwn.net/Articles/104179/ + + The OOM killer preferentially kills processes whose *children* use a lot of + memory, and processes that are niced. So if a display mode uses a lot of + memory, the OOM-killer is more likely to shoot down the XScreenSaver + *daemon* than just that screenhack! + + To disable the OOM-killer entirely: + + echo 2 > /proc/sys/vm/overcommit_memory + echo vm.overcommit_memory = 2 >> /etc/sysctl.conf + */ +static void +oom_assassin_immunity (void) +{ +# define OOM_VAL "-1000" + char fn[1024]; + struct stat st; + FILE *fd; + pid_t pid = getppid(); /* our parent, not us */ + + sprintf (fn, "/proc/%d/oom_score_adj", pid); + if (stat(fn, &st) != 0) + { + if (verbose_p) + fprintf (stderr, "%s: OOM: %s does not exist\n", blurb(), fn); + return; + } + fd = fopen (fn, "w"); + if (!fd) goto FAIL; + if (fputs (OOM_VAL "\n", fd) <= 0) goto FAIL; + if (fclose (fd) != 0) goto FAIL; + + if (verbose_p) + fprintf (stderr, "%s: OOM: echo " OOM_VAL " > %s\n", blurb(), fn); + return; + + FAIL: + { + char buf[1024]; + const char *b = blurb(); + sprintf (buf, "%.40s: OOM: %.200s", b, fn); + perror (buf); + if (getuid() == geteuid()) + fprintf (stderr, + "%s: To prevent the kernel from randomly unlocking\n" + "%s: your screen via the out-of-memory killer,\n" + "%s: \"%s\" must be setuid root.\n", + b, b, b, progname); + } +} +#endif /* HAVE_PROC_OOM */ + + +int +main (int argc, char **argv) +{ + Display *dpy; + XtAppContext app; + Widget root_widget; + char *dpy_str = getenv ("DISPLAY"); + Bool xsync_p = False; + Bool splash_p = False; + Bool init_p = False; + int i; + +# undef ya_rand_init + ya_rand_init (0); + + /* For Xt and X resource database purposes, this program is + "xscreensaver", not "xscreensaver-auth". + */ + { + char *s = strrchr(argv[0], '/'); + if (s) s++; + else s = argv[0]; + if (strlen(s) > 20) /* keep it short. */ + s[20] = 0; + progname = s; + } + + progclass = "XScreenSaver"; + argv[0] = "xscreensaver"; + + if (! dpy_str) dpy_str = ":0"; + + for (i = 1; i < argc; i++) + { + const char *oa = argv[i]; + /* XScreenSaver predates the "--arg" convention. */ + if (argv[i][0] == '-' && argv[i][1] == '-') + argv[i]++; + + if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose")) + verbose_p++; + else if (!strcmp (argv[i], "-vv")) verbose_p += 2; + else if (!strcmp (argv[i], "-vvv")) verbose_p += 3; + else if (!strcmp (argv[i], "-vvvv")) verbose_p += 4; + else if (!strcmp (argv[i], "-q") || !strcmp (argv[i], "-quiet")) + verbose_p = False; + else if (!strcmp (argv[i], "-debug")) + /* Does nothing else at the moment but warn that "xscreensaver" + is logging keystrokes to stderr. */ + debug_p = True; + else if (!strcmp (argv[i], "-d") || + !strcmp (argv[i], "-dpy") || + !strcmp (argv[i], "-disp") || + !strcmp (argv[i], "-display")) + { + dpy_str = argv[++i]; + if (!dpy_str) goto HELP; + } + else if (!strcmp (argv[i], "-sync") || + !strcmp (argv[i], "-synch") || + !strcmp (argv[i], "-synchronize") || + !strcmp (argv[i], "-synchronise")) + xsync_p = True; + else if (!strcmp (argv[i], "-splash")) + splash_p = True; + else if (!strcmp (argv[i], "-init")) + init_p = True; + else if (!strcmp (argv[i], "-h") || !strcmp (argv[i], "-help")) + { + HELP: + fprintf (stderr, + "\n" + "\txscreensaver-auth is launched by the xscreensaver daemon\n" + "\tto authenticate the user by prompting for a password.\n" + "\tDo not run this directly.\n" + "\n" + "\tOptions:\n" + "\t\t--dpy host:display.screen\n" + "\t\t--verbose --sync --splash --init\n" + "\n" + "\tRun 'xscreensaver-settings' to configure.\n" + "\n"); + exit (1); + } + else + { + fprintf (stderr, "\n%s: unknown option: %s\n", blurb(), oa); + goto HELP; + } + } + + if (!splash_p && init_p) + { + const char *v = XSCREENSAVER_VERSION; + if (strstr (v, "a") || strstr (v, "b")) + splash_p = True; /* Not optional for alpha and beta releases */ + } + +# ifdef HAVE_PROC_OOM + if (splash_p || init_p) + oom_assassin_immunity (); +# endif + + if (!splash_p && !init_p) + lock_priv_init (); + + if (!splash_p && init_p) + exit (0); + + disavow_privileges (); + + if (!splash_p) + lock_init (); + + /* Setting the locale is necessary for XLookupString to return multi-byte + characters, enabling their use in passwords. + */ +# ifdef ENABLE_NLS + { + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + textdomain (GETTEXT_PACKAGE); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + if (!setlocale (LC_ALL, "")) + fprintf (stderr, "%s: warning: could not set default locale\n", + progname); + } +# endif /* ENABLE_NLS */ + + + /* Copy the -dpy arg to $DISPLAY for subprocesses. */ + { + char *s = (char *) malloc (strlen(dpy_str) + 20); + sprintf (s, "DISPLAY=%s", dpy_str); + putenv (s); + /* free (s); */ /* some versions of putenv do not copy */ + } + + /* Open the display */ + { + XrmOptionDescRec options; + argc = 1; /* Xt does not receive any of our command-line options. */ + root_widget = XtAppInitialize (&app, progclass, &options, 0, + &argc, argv, defaults, 0, 0); + } + + dpy = XtDisplay (root_widget); + if (xsync_p) XSynchronize (dpy, True); + init_xscreensaver_atoms (dpy); + + if (splash_p) + { + xscreensaver_splash (root_widget); + exit (0); + } + else if (xscreensaver_auth ((void *) root_widget, + xscreensaver_auth_conv, + xscreensaver_auth_finished)) + { + if (verbose_p) + fprintf (stderr, "%s: authentication succeeded\n", blurb()); + exit (200); + } + else + { + if (verbose_p) + fprintf (stderr, "%s: authentication failed\n", blurb()); + exit (-1); + } + + /* On timeout, xscreensaver_auth did exit(0) */ +} diff --git a/driver/xscreensaver-auth.man b/driver/xscreensaver-auth.man new file mode 100644 index 0000000..1655602 --- /dev/null +++ b/driver/xscreensaver-auth.man @@ -0,0 +1,27 @@ +.TH XScreenSaver 1 "06-Jan-2021 (6.00)" "X Version 11" +.SH NAME +xscreensaver - extensible screen saver and screen locking framework +.SH SYNOPSIS +.B xscreensaver-auth +[\-display \fIhost:display.screen\fP] +.SH DESCRIPTION +The +.BR xscreensaver (1) +daemon launches this program to prompt the user for their password. +Do not run this program directly. +.SH SEE ALSO +.BR xscreensaver (1), +.BR xscreensaver\-settings (1), +.BR xscreensaver\-gfx (MANSUFFIX), +.BR xscreensaver\-systemd (MANSUFFIX). +.SH COPYRIGHT +Copyright \(co 2021 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 diff --git a/driver/xscreensaver-command.c b/driver/xscreensaver-command.c index 4a4f0b4..0dab564 100644 --- a/driver/xscreensaver-command.c +++ b/driver/xscreensaver-command.c @@ -1,4 +1,4 @@ -/* xscreensaver-command, Copyright (c) 1991-2019 Jamie Zawinski +/* xscreensaver-command, Copyright © 1991-2021 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 @@ -31,30 +31,15 @@ #include /* only needed to get through xscreensaver.h */ - -/* You might think that to read an array of 32-bit quantities out of a - server-side property, you would pass an array of 32-bit data quantities - into XGetWindowProperty(). You would be wrong. You have to use an array - of longs, even if long is 64 bits (using 32 of each 64.) - */ -typedef long PROP32; - +#include "blurb.h" #include "remote.h" #include "version.h" +#include "atoms.h" #ifdef _VROOT_H_ ERROR! you must not include vroot.h in this file #endif -char *progname; - -Atom XA_VROOT; -Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_RESPONSE; -Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO, XA_EXIT; -Atom XA_BLANK, XA_LOCK, XA_ACTIVATE, XA_SUSPEND, XA_NEXT, XA_PREV; -static Atom XA_DEACTIVATE, XA_CYCLE; -static Atom XA_RESTART, XA_PREFS, XA_THROTTLE, XA_UNTHROTTLE; - static char *screensaver_version; # ifdef __GNUC__ __extension__ /* don't warn about "string length is greater than the @@ -72,10 +57,8 @@ usage: %s -