MAJOR="$$1"; MINOR="$$2"; SUF="$$3"; \
VERS="$$MAJOR.$$MINOR$$SUF" ; \
if [ -z "$$SUF" ]; then \
- MINOR=`echo $$MINOR + 1 | bc | sed 's/^\(.\)$$/0\1/'` ; \
+ MINOR=`echo $$((MINOR + 1)) | sed 's/^\(.\)$$/0\1/'` ; \
else \
set - `echo $$SUF | sed 's/^\([^0-9]*\)/\1 /'` ; \
AA="$$1"; BB="$$2"; \
- BB=`echo $$BB + 1 | bc` ; \
+ BB=$$((BB + 1)) ; \
SUF="$$AA$$BB" ; \
fi ; \
VERS2="$$MAJOR.$$MINOR$$SUF" ; \
mkdir $$DIR ; \
( cd $$DIR; mkdir BUILD RPMS RPMS/$$ARCH SOURCES SPECS SRPMS ) ; \
cp -p $${ADIR}$$TGZ $$DIR/SOURCES/ ; \
+ set -x ; \
rpmbuild --define "_topdir $$DIR" \
--define "USE_GL yes" \
+ --nodeps \
-v -ba xscreensaver.spec ; \
+ set +x ; \
echo '' ; \
echo 'RPM build complete' ; \
echo '' ; \
B=`cd hacks/glx ; ( make -s INSTALL=true install-man | \
grep true | grep -v helper | grep -v ljlatest | wc -l )` ; \
echo " GLX:" $$B ; \
- C=`echo $$A + $$B | bc` ; \
+ C=$$((A + B)) ; \
echo " Total:" $$C ; \
-# XScreenSaver for MacOS X, Copyright © 2006-2023 Jamie Zawinski.
+# XScreenSaver for MacOS X, Copyright © 2006-2025 Jamie Zawinski.
XCODE_APP = /Applications/Xcode.app
TARGETS = All Savers
release::
$(MAKE2) _release > /dev/null
+ $(MAKE2) update_installer_size
Sparkle.framework:
rm -rf "$$STAGE" ; \
+# Set the installer's REQUIRED_SPACE to the size in MB of the build/Release/
+# directory, plus 8%, rounded up to the nearest 10. That should be pretty
+# close to reality.
+#
+update_installer_size::
+ @ \
+ SIZE=`du -ks build/Release/ | sed 's/[^0-9].*//'` ; \
+ SIZE=`perl -e "print int($$SIZE / 10240 * 1.08 + 0.999) * 10;"` ; \
+ FILE="installer.sh" ; \
+ TMP="/tmp/installer.$$$$.sh" ; \
+ sed -e "s/^\(REQUIRED_SPACE=\)[0-9]*\(.*\)/\1$$SIZE\2/" \
+ < "$$FILE" > "$$TMP" ; \
+ if cmp -s "$$FILE" "$$TMP" ; then \
+ echo "$$FILE unchanged (required space $$SIZE MB)" ; \
+ else \
+ cat "$$TMP" > "$$FILE" ; \
+ echo "$$FILE updated (required space $$SIZE MB)" ; \
+ fi ; \
+ rm -f "$$TMP"
+
+
dmg:: distdepend check_versions
dmg:: build/Release/installer.pkg
dmg:: _dmg notarize staple updates.xml
// This happens when the .xml file says things like arg="-foo 'bar baz'"
if (result[0] == '\'' && result[strlen(result)-1] == '\'') {
result[strlen(result)-1] = 0;
- strcpy (result, result+1);
+ memmove (result, result+1, strlen (result));
}
// Kludge: assume that any string that begins with "~" and has a "/"
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSPrincipalClass</key>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleLongVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleGetInfoString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>NSHumanReadableCopyright</key>
- <string>6.10</string>
+ <string>6.11</string>
</dict>
</plist>
p = [dir stringByAppendingPathComponent: p];
NSString *classname = [[p lastPathComponent] stringByDeletingPathExtension];
- NSString *title = classname;
+ NSString *title;
// Get the title's capitalization right by reading the XML file.
NSAssert1 (r.length, @"no name in %@", p);
if (r.length)
title = [xml substringToIndex: r.location];
+ else
+ title = classname;
}
# else // !HAVE_IPHONE
NSBundle *nsb = [NSBundle bundleWithPath:p];
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSPrincipalClass</key>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleLongVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleGetInfoString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>NSHumanReadableCopyright</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>NSMainNibFile</key>
<string>SaverRunner</string>
<key>CFBundleIconFile</key>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSPrincipalClass</key>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleLongVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleGetInfoString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>NSHumanReadableCopyright</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>NSMainNibFile</key>
<string>Updater</string>
<key>CFBundleIconFile</key>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSPrincipalClass</key>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleLongVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleGetInfoString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>NSHumanReadableCopyright</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>NSMainNibFile</key>
<string>SaverRunner</string>
</dict>
\b0 by Jamie Zawinski\
and many others\
\
-version 6.10\
-27-Apr-2025\
+version 6.11\
+01-Jul-2025\
\
{\field{\*\fldinst{HYPERLINK "https://www.jwz.org/xscreensaver/"}}{\fldrslt \cf2 \ul \ulc2 https://www.jwz.org/xscreensaver/}}\
\pard\pardeftab720
<plist version="1.0">
<dict>
<key>URL</key>
- <string>https://itunes.apple.com/app/xscreensaver/id539014593</string>
+ <string>https://itunes.apple.com/app/xscreensaver/id539014593?mt=8</string>
</dict>
</plist>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleLongVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleGetInfoString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>NSHumanReadableCopyright</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>NSMainNibFile</key>
<string>iSaverRunner</string>
<key>CFBundleDisplayName</key>
#!/bin/bash
-# XScreenSaver, Copyright © 2013-2023 Jamie Zawinski <jwz@jwz.org>
+# XScreenSaver, Copyright © 2013-2025 Jamie Zawinski <jwz@jwz.org>
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
#set -x
DEBUG=0
-REQUIRED_SPACE=360 # MB. Highly approximate; updated 6.07.
+REQUIRED_SPACE=260 # MB
export PATH="/bin:/sbin:/usr/bin:/usr/sbin:$PATH"
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleLongVersionString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleGetInfoString</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>NSHumanReadableCopyright</key>
- <string>6.10</string>
+ <string>6.11</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleIcons</key>
Version History
===============================================================================
+6.11 * X11: Now supports Wayland (blanking only, not locking).
+ * X11: More reliable and timely DPMS activation.
+ * X11: Dead keys work in password input.
+ * X11: Fixed a couple of minor Y2038 bugs.
6.10 * New hacks, `dumpsterfire', `hopffibration', `platonicfolding' and
`klondike'.
* Rewrote the VT100 emulator for 'apple2' and 'phosphor'.
- On modern machines, OpenGL will always run faster than Xlib. It's
also more portable. Consider writing in OpenGL whenever possible.
+ - You must use OpenGL 1.3. This means "immedidate mode", no shaders.
+ (It is possible to use GLSL shader language if you do so *optionally*
+ but it is a huge pain because the savers must run when that fails a
+ runtime check.)
+
+ - If you have static image assets, keep them PNG and small. See
+ hacks/images/ and "maze" or "peepers" for examples.
+
- Free any memory you allocate. While screen savers under X11 have
their memory freed automatically when their process is killed by
the XScreenSaver daemon, under iOS and Android screen savers exist
/* config.h.in. Generated from configure.ac by autoheader. */
-/* xscreensaver, Copyright © 1991-2022 Jamie Zawinski.
+/* xscreensaver, Copyright © 1991-2025 Jamie Zawinski.
* Generate this file by running 'configure' rather than editing it by hand.
*/
/* Define to 1 if you have the <util.h> header file. */
#undef HAVE_UTIL_H
+/* Define this if you have the Wayland libraries. */
+#undef HAVE_WAYLAND
+
/* Define this if you have XF86VidModeGetViewPort, for virtual desktops. */
#undef HAVE_XF86VMODE
SETUID_HACKS
SETUID_AUTH
PROG_SETCAP
+WAYLAND_IDLE_OBJS
+WAYLAND_GEN
+WAYLAND_DATADIR
+WAYLAND_LIBS
+WAYLAND_CFLAGS
LIBCAP_LIBS
LIBCAP_CFLAGS
PASSWD_LIBS
with_libiconv_prefix
with_libintl_prefix
with_app_defaults
+enable_dependency_tracking
with_hackdir
-enable_subdir
with_configdir
with_fontdir
with_dpms_ext
with_proc_oom
with_systemd
with_elogind
+with_wayland
enable_locking
enable_root_passwd
with_pam
--with-systemd Support systemd requests to lock on suspend, and to
allow video players to inhibit the screen saver.
--with-elogind Use elogind instead of systemd.
+ --with-wayland Use native Wayland when available.
Screen Locking Options:
--disable-locking Do not allow locking of the display at all.
--with-pam Use Pluggable Authentication Modules.
--with-pam-service-name Set the name of the xscreensaver PAM service.
- --enable-pam-account Whether PAM should check the result of account
- modules when authenticating. Only do this if you
- have "account" modules configured on your system.
- --enable-root-passwd Allow the root password to unlock, if not using PAM.
+ --enable-pam-check-account-type
+ Whether PAM should check the result of account
+ modules when authenticating. Only do this if you
+ have "account" modules configured on your system.
+ --enable-root-passwd Allow the root password to unlock, if not using PAM.
--with-kerberos Include support for Kerberos authentication.
--with-shadow Include support for shadow password authentication.
printf "%s\n" "$ac_cv_x_app_defaults" >&6; }
eval ac_x_app_defaults="$ac_cv_x_app_defaults"
+# Ignore --disable-dependency-tracking, which RHEL8 rpmbuild adds by default.
+# Check whether --enable-dependency-tracking was given.
+if test ${enable_dependency_tracking+y}
+then :
+ enableval=$enable_dependency_tracking;
+fi
+
###############################################################################
#
# Handle the --with-hackdir option
# Expand HACKDIR as HACKDIR_FULL
HACKDIR_FULL=`eval eval eval eval eval eval eval eval eval echo $HACKDIR`
-# This option used to be called --enable-subdir; make sure that is no longer
-# used, since configure brain-damagedly ignores unknown --enable options.
-
-obsolete_enable=
-# Check whether --enable-subdir was given.
-if test ${enable_subdir+y}
-then :
- enableval=$enable_subdir; obsolete_enable=yes
-fi
-
-if test -n "$obsolete_enable"; then
- echo "error: the --enable-subdir option has been replaced with"
- echo " the new --with-hackdir option; see \`configure --help'"
- echo " for more information."
- exit 1
-fi
-
###############################################################################
#
# Handle the --with-configdir option
fi
+###############################################################################
+#
+# Check for Wayland
+#
+###############################################################################
+
+have_wayland=no
+with_wayland_req=unspecified
+
+# Check whether --with-wayland was given.
+if test ${with_wayland+y}
+then :
+ withval=$with_wayland; with_wayland="$withval"; with_wayland_req="$withval"
+else case e in #(
+ e) with_wayland=yes ;;
+esac
+fi
+
+ case "$with_wayland" in
+ yes) ;;
+ no) ;;
+
+ /*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Wayland headers" >&5
+printf %s "checking for Wayland headers... " >&6; }
+ d=$with_wayland/include
+ if test -d $d; then
+ X_CFLAGS="-I$d $X_CFLAGS"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $d" >&5
+printf "%s\n" "$d" >&6; }
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found ($d: no such directory)" >&5
+printf "%s\n" "not found ($d: no such directory)" >&6; }
+ fi
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Wayland libs" >&5
+printf %s "checking for Wayland libs... " >&6; }
+ d=$with_wayland/lib
+ if test -d $d; then
+ X_LIBS="-L$d $X_LIBS"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $d" >&5
+printf "%s\n" "$d" >&6; }
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found ($d: no such directory)" >&5
+printf "%s\n" "not found ($d: no such directory)" >&6; }
+ fi
+
+ # replace the directory string with "yes".
+ with_wayland_req="yes"
+ with_wayland=$with_wayland_req
+ ;;
+
+ *)
+ echo ""
+ echo "error: argument to --with-wayland must be \"yes\", \"no\", or a directory."
+ echo " If it is a directory, then \`DIR/include' will be added to"
+ echo " the -I list, and \`DIR/lib' will be added to the -L list."
+ exit 1
+ ;;
+ esac
+
+if test "$with_wayland" != yes -a "$with_wayland" != no ; then
+ echo "error: must be yes or no: --with-wayland=$with_wayland"
+ exit 1
+fi
+
+if test "$with_wayland" = yes; then
+ have_wayland=no
+
+ pkgs=''
+ ok="yes"
+ pkg_check_version wayland-server 1.0
+ pkg_check_version wayland-client 1.0
+ pkg_check_version wayland-scanner 1.0
+ ac_wayland_version_string="$vers"
+ have_wayland="$ok"
+ wayland_pkgs="$pkgs"
+
+ if test "$have_wayland" = yes; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Wayland includes" >&5
+printf %s "checking for Wayland includes... " >&6; }
+if test ${ac_cv_wayland_config_cflags+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) ac_cv_wayland_config_cflags=`$pkg_config --cflags $pkgs` ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_wayland_config_cflags" >&5
+printf "%s\n" "$ac_cv_wayland_config_cflags" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Wayland libs" >&5
+printf %s "checking for Wayland libs... " >&6; }
+if test ${ac_cv_wayland_config_libs+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) ac_cv_wayland_config_libs=`$pkg_config --libs $pkgs` ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_wayland_config_libs" >&5
+printf "%s\n" "$ac_cv_wayland_config_libs" >&6; }
+ fi
+
+ ac_wayland_config_cflags=$ac_cv_wayland_config_cflags
+ ac_wayland_config_libs=$ac_cv_wayland_config_libs
+
+ WAYLAND_DATADIR=""
+ WAYLAND_GEN=""
+ WAYLAND_IDLE_OBJS=""
+ if test "$have_wayland" = yes; then
+ WAYLAND_DATADIR=`$pkg_config --variable=prefix wayland-client`
+ WAYLAND_DATADIR="$WAYLAND_DATADIR/share"
+ WAYLAND_GEN='$(WAYLAND_GEN_HDRS) $(WAYLAND_GEN_SRCS)'
+ WAYLAND_IDLE_OBJS='$(WAYLAND_IDLE_OBJS_1)'
+ fi
+
+ if test "$have_wayland" = yes; then
+ INCLUDES="$INCLUDES $ac_wayland_config_cflags"
+ WAYLAND_LIBS="$WAYLAND_LIBS $ac_wayland_config_libs"
+ printf "%s\n" "#define HAVE_WAYLAND 1" >>confdefs.h
+
+ fi
+fi
+
+# Check for the various Gnome help and URL loading programs.
+#
+WITH_BROWSER=gnome-open
+if test "$have_wayland" = yes; then
+ for ac_prog in gnome-open
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_gnome_open_program+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) if test -n "$gnome_open_program"; then
+ ac_cv_prog_gnome_open_program="$gnome_open_program" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_gnome_open_program="$ac_prog"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi ;;
+esac
+fi
+gnome_open_program=$ac_cv_prog_gnome_open_program
+if test -n "$gnome_open_program"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $gnome_open_program" >&5
+printf "%s\n" "$gnome_open_program" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+ test -n "$gnome_open_program" && break
+done
+
+ for ac_prog in gnome-url-show
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_gnome_url_show_program+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) if test -n "$gnome_url_show_program"; then
+ ac_cv_prog_gnome_url_show_program="$gnome_url_show_program" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_gnome_url_show_program="$ac_prog"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi ;;
+esac
+fi
+gnome_url_show_program=$ac_cv_prog_gnome_url_show_program
+if test -n "$gnome_url_show_program"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $gnome_url_show_program" >&5
+printf "%s\n" "$gnome_url_show_program" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+ test -n "$gnome_url_show_program" && break
+done
+
+fi
+
###############################################################################
#
# The --enable-locking option
PASSWD_SRCS="$PASSWD_SRCS \$(PWENT_SRCS)"
PASSWD_OBJS="$PASSWD_OBJS \$(PWENT_OBJS)"
-if test "$enable_locking" = yes; then
- LOCK_SRCS='$(LOCK_SRCS_1) $(PASSWD_SRCS)'
- LOCK_OBJS='$(LOCK_OBJS_1) $(PASSWD_OBJS)'
-else
- LOCK_SRCS='$(NOLOCK_SRCS_1)'
- LOCK_OBJS='$(NOLOCK_OBJS_1)'
-fi
-
if test "$ac_macosx" = yes; then
EXES_OSX='$(EXES_OSX)'
SCRIPTS_OSX='$(SCRIPTS_OSX)'
-# configure.in --- xscreensaver, Copyright © 1997-2023 Jamie Zawinski.
+# configure.in --- xscreensaver, Copyright © 1997-2025 Jamie Zawinski.
#
# Note: upgrading past 2.69_5 breaks the world, mostly the po/ shitshow.
###############################################################################
AH_TOP([
-/* xscreensaver, Copyright © 1991-2022 Jamie Zawinski.
+/* xscreensaver, Copyright © 1991-2025 Jamie Zawinski.
* Generate this file by running 'configure' rather than editing it by hand.
*/
])
AH_TEMPLATE([HAVE_GLE3],
[Define this if GL Extrusion is version 3.])
+AH_TEMPLATE([HAVE_WAYLAND],
+ [Define this if you have the Wayland libraries.])
+
AH_TEMPLATE([HAVE_FFMPEG],
[Define this if you have the ffmpeg libraries.])
[eval ac_x_app_defaults="$withval"])
AC_PATH_X_APP_DEFAULTS
+# Ignore --disable-dependency-tracking, which RHEL8 rpmbuild adds by default.
+AC_ARG_ENABLE(dependency-tracking,,)
+
###############################################################################
#
# Expand HACKDIR as HACKDIR_FULL
HACKDIR_FULL=`eval eval eval eval eval eval eval eval eval echo $HACKDIR`
-# This option used to be called --enable-subdir; make sure that is no longer
-# used, since configure brain-damagedly ignores unknown --enable options.
-
-obsolete_enable=
-AC_ARG_ENABLE(subdir,,[obsolete_enable=yes])
-if test -n "$obsolete_enable"; then
- echo "error: the --enable-subdir option has been replaced with"
- echo " the new --with-hackdir option; see \`configure --help'"
- echo " for more information."
- exit 1
-fi
-
-
###############################################################################
#
# Handle the --with-configdir option
fi
+###############################################################################
+#
+# Check for Wayland
+#
+###############################################################################
+
+have_wayland=no
+with_wayland_req=unspecified
+AC_ARG_WITH(wayland,
+[ --with-wayland Use native Wayland when available.],
+ [with_wayland="$withval"; with_wayland_req="$withval"],[with_wayland=yes])
+
+HANDLE_X_PATH_ARG(with_wayland, --with-wayland, Wayland)
+
+if test "$with_wayland" != yes -a "$with_wayland" != no ; then
+ echo "error: must be yes or no: --with-wayland=$with_wayland"
+ exit 1
+fi
+
+
+if test "$with_wayland" = yes; then
+ have_wayland=no
+
+ pkgs=''
+ ok="yes"
+ pkg_check_version wayland-server 1.0
+ pkg_check_version wayland-client 1.0
+ pkg_check_version wayland-scanner 1.0
+ ac_wayland_version_string="$vers"
+ have_wayland="$ok"
+ wayland_pkgs="$pkgs"
+
+ if test "$have_wayland" = yes; then
+ AC_CACHE_CHECK([for Wayland includes], ac_cv_wayland_config_cflags,
+ [ac_cv_wayland_config_cflags=`$pkg_config --cflags $pkgs`])
+ AC_CACHE_CHECK([for Wayland libs], ac_cv_wayland_config_libs,
+ [ac_cv_wayland_config_libs=`$pkg_config --libs $pkgs`])
+ fi
+
+ ac_wayland_config_cflags=$ac_cv_wayland_config_cflags
+ ac_wayland_config_libs=$ac_cv_wayland_config_libs
+
+ WAYLAND_DATADIR=""
+ WAYLAND_GEN=""
+ WAYLAND_IDLE_OBJS=""
+ if test "$have_wayland" = yes; then
+ WAYLAND_DATADIR=`$pkg_config --variable=prefix wayland-client`
+ WAYLAND_DATADIR="$WAYLAND_DATADIR/share"
+ WAYLAND_GEN='$(WAYLAND_GEN_HDRS) $(WAYLAND_GEN_SRCS)'
+ WAYLAND_IDLE_OBJS='$(WAYLAND_IDLE_OBJS_1)'
+ fi
+
+ if test "$have_wayland" = yes; then
+ INCLUDES="$INCLUDES $ac_wayland_config_cflags"
+ WAYLAND_LIBS="$WAYLAND_LIBS $ac_wayland_config_libs"
+ AC_DEFINE(HAVE_WAYLAND)
+ fi
+fi
+
+
+# Check for the various Gnome help and URL loading programs.
+#
+WITH_BROWSER=gnome-open
+if test "$have_wayland" = yes; then
+ AC_CHECK_PROGS(gnome_open_program, gnome-open)
+ AC_CHECK_PROGS(gnome_url_show_program, gnome-url-show)
+fi
+
+
###############################################################################
#
# The --enable-locking option
AC_ARG_WITH([pam_service_name],
[ --with-pam-service-name Set the name of the xscreensaver PAM service.
- --enable-pam-account Whether PAM should check the result of account
- modules when authenticating. Only do this if you
- have "account" modules configured on your system.
- --enable-root-passwd Allow the root password to unlock, if not using PAM.],
+ --enable-pam-check-account-type
+ Whether PAM should check the result of account
+ modules when authenticating. Only do this if you
+ have "account" modules configured on your system.
+ --enable-root-passwd Allow the root password to unlock, if not using PAM.],
[pam_service_name="$withval"],[pam_service_name="xscreensaver"])
AC_ARG_ENABLE(pam-check-account-type,
PASSWD_OBJS="$PASSWD_OBJS \$(PWENT_OBJS)"
-if test "$enable_locking" = yes; then
- LOCK_SRCS='$(LOCK_SRCS_1) $(PASSWD_SRCS)'
- LOCK_OBJS='$(LOCK_OBJS_1) $(PASSWD_OBJS)'
-else
- LOCK_SRCS='$(NOLOCK_SRCS_1)'
- LOCK_OBJS='$(NOLOCK_OBJS_1)'
-fi
-
if test "$ac_macosx" = yes; then
EXES_OSX='$(EXES_OSX)'
SCRIPTS_OSX='$(SCRIPTS_OSX)'
AC_SUBST(PASSWD_LIBS)
AC_SUBST(LIBCAP_CFLAGS)
AC_SUBST(LIBCAP_LIBS)
+AC_SUBST(WAYLAND_CFLAGS)
+AC_SUBST(WAYLAND_LIBS)
+AC_SUBST(WAYLAND_DATADIR)
+AC_SUBST(WAYLAND_GEN)
+AC_SUBST(WAYLAND_IDLE_OBJS)
AC_SUBST(PROG_SETCAP)
AC_SUBST(SETUID_AUTH)
AC_SUBST(SETUID_HACKS)
-# driver/Makefile.in --- xscreensaver, Copyright © 1997-2023 Jamie Zawinski.
+# driver/Makefile.in --- xscreensaver, Copyright © 1997-2025 Jamie Zawinski.
# the `../configure' script generates `driver/Makefile' from this file.
@SET_MAKE@
UPDATE_ICON_CACHE = gtk-update-icon-cache
GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+WAYLAND_CFLAGS = @WAYLAND_CFLAGS@
+WAYLAND_LIBS = @WAYLAND_LIBS@
+WAYLAND_DATADIR = @WAYLAND_DATADIR@
+WAYLAND_GEN = @WAYLAND_GEN@
+
+WAYLAND_GEN_XML = xdg-shell.xml \
+ ext-idle-notify-v1.xml \
+ idle.xml
+WAYLAND_GEN_HDRS = xdg-shell-v1-client-protocol.h \
+ ext-idle-notify-v1-client-protocol.h \
+ idle-client-protocol.h
+WAYLAND_GEN_SRCS = xdg-shell-v1-protocol.c \
+ ext-idle-notify-v1-protocol.c \
+ idle-protocol.c
+
+WAYLAND_IDLE_OBJS = @WAYLAND_IDLE_OBJS@
+WAYLAND_IDLE_SRCS = wayland-idle.c
+WAYLAND_IDLE_OBJS_1 = wayland-idle.o \
+ ext-idle-notify-v1-protocol.o \
+ idle-protocol.o
+
HACKDIR = @HACKDIR@
HACK_CONF_DIR = @HACK_CONF_DIR@
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_SRCS = xscreensaver.c blurb.c atoms.c clientmsg.c xinput.c prefs.c \
+ $(WAYLAND_IDLE_SRCS)
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)
+ $(UTILS_BIN)/xmu.o $(WAYLAND_IDLE_OBJS)
+DAEMON_LIBS = $(LIBS_PRE) $(XINPUT_LIBS) -lX11 $(WAYLAND_LIBS) $(LIBS_POST)
GFX_DEFS = -DLOCALEDIR=\"$(localedir)\"
GFX_SRCS = xscreensaver-gfx.c screens.c windows.c subprocs.c \
PASSWD_SRCS = @PASSWD_SRCS@
PASSWD_OBJS = @PASSWD_OBJS@
-LOCK_SRCS = @LOCK_SRCS@
-LOCK_OBJS = @LOCK_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)
GTK_OBJS = demo-Gtk.o demo-Gtk-conf.o demo-Gtk-resources.o \
blurb.o exec.o prefs.o prefsw.o dpms.o remote.o screens.o \
clientmsg.o atoms.o \
+ $(WAYLAND_IDLE_OBJS) \
$(UTILS_BIN)/xmu.o \
$(UTILS_BIN)/resources.o \
$(UTILS_BIN)/visual.o \
HDRS = XScreenSaver_ad.h XScreenSaver_Xm_ad.h \
xscreensaver.h prefs.h remote.h exec.h \
demo-Gtk-conf.h auth.h types.h blurb.h atoms.h clientmsg.h \
- screens.h xinput.h fade.h
+ screens.h xinput.h fade.h wayland-idle.h \
+ $(WAYLAND_GEN_HDRS)
MENA = xscreensaver.man xscreensaver-settings.man \
xscreensaver-command.man
MENB = xscreensaver-gfx.man xscreensaver-auth.man \
EXTRAS = README Makefile.in \
XScreenSaver.ad.in XScreenSaver-Xm.ad xscreensaver.pam.in \
demo.ui prefs.ui gresource.xml xscreensaver.desktop.in \
- xscreensaver-settings.desktop.in xscreensaver.service.in
+ xscreensaver-settings.desktop.in xscreensaver.service.in \
+ $(WAYLAND_GEN_XML)
TARFILES = $(DAEMON_SRCS) $(GFX_SRCS) $(AUTH_SRCS) $(SYSTEMD_SRCS) \
$(CMD_SRCS) $(GTK_SRCS) $(MOTIF_SRCS) $(PWENT_SRCS) \
$(KERBEROS_SRCS) $(PAM_SRCS) \
- $(HDRS) $(MENA) $(MENB) $(TEST_SRCS) $(EXTRAS)
+ $(WAYLAND_GEN_SRCS) \
+ $(HDRS) $(MENA) $(MENB) $(TEST_SRCS) $(EXTRAS)
# Using $(MAKE) directly means the shell executes things even with "make -n"
MAKE2 = $(MAKE)
rm -f $(install_prefix)$(HACK_CONF_DIR)/README
+# Generate .c and .h files for various Wayland APIs.
+#
+xdg-shell.xml: $(WAYLAND_DATADIR)/wayland-protocols/stable/xdg-shell/xdg-shell.xml
+ cp -p $< $@
+xdg-shell-v1-client-protocol.h: xdg-shell.xml
+ wayland-scanner client-header < $< > $@
+xdg-shell-v1-protocol.c: xdg-shell.xml
+ wayland-scanner private-code < $< > $@
+
+ext-idle-notify-v1.xml: \
+ $(WAYLAND_DATADIR)/wayland-protocols/staging/ext-idle-notify/ext-idle-notify-v1.xml
+ cp -p $< $@
+ext-idle-notify-v1-client-protocol.h: ext-idle-notify-v1.xml
+ wayland-scanner client-header < $< > $@
+ext-idle-notify-v1-protocol.c: ext-idle-notify-v1.xml
+ wayland-scanner private-code < $< > $@
+
+# org_kde_kwin_idle -- I don't know why this isn't in WAYLAND_DATADIR
+#idle.xml: $(WAYLAND_DATADIR)/wayland-protocols/stable/ext-idle-notify/idle.xml
+# cp -p $< $@
+idle-client-protocol.h: idle.xml
+ wayland-scanner client-header < $< > $@
+idle-protocol.c: idle.xml
+ wayland-scanner private-code < $< > $@
+
+
##############################################################################
#
# Clean and dependencies
$(DEPEND) -s '# DO NOT DELETE: updated by make depend' \
$(DEPEND_FLAGS) -- \
$(INCLUDES_1) $(DEFS) $(DEPEND_DEFINES) $(CFLAGS) $(X_CFLAGS) -- \
- $(SAVER_SRCS) $(CMD_SRCS)
+ $(DAEMON_SRCS) $(GFX_SRCS) $(AUTH_SRCS) $(SYSTEMD_SRCS) $(CMD_SRCS) \
+ $(GTK_SRCS) $(MOTIF_SRCS) $(PWENT_SRCS) $(KERBEROS_SRCS) \
+ $(PAM_SRCS) $(WAYLAND_GEN_SRCS) $(TEST_SRCS)
# Adds some dependencies to Makefile.in -- not totally accurate, but pretty
# close. This excludes dependencies on files in /usr/include, etc. It tries
$(DEPEND) -w 0 -f - \
-s '# DO NOT DELETE: updated by make distdepend' $(DEPEND_FLAGS) -- \
$(INCLUDES_1) $(DEFS) $(DEPEND_DEFINES) $(CFLAGS) $(X_CFLAGS) -- \
- $(SAVER_SRCS_1) $(SYSTEMD_SRCS) $(MOTIF_SRCS) $(GTK_SRCS) \
- $(PWENT_SRCS) $(KERBEROS_SRCS) $(PAM_SRCS) \
- $(LOCK_SRCS_1) $(DEMO_SRCS_1) $(CMD_SRCS) \
- $(TEST_SRCS) 2>/dev/null | \
+ $(DAEMON_SRCS) $(GFX_SRCS) $(AUTH_SRCS) $(SYSTEMD_SRCS) $(CMD_SRCS) \
+ $(GTK_SRCS) $(MOTIF_SRCS) $(PWENT_SRCS) $(KERBEROS_SRCS) \
+ $(PAM_SRCS) $(WAYLAND_GEN_SRCS) $(TEST_SRCS) \
+ 2>/dev/null | \
sort -d | \
( \
awk '/^# .*Makefile.in ---/,/^# DO .*distdepend/' < Makefile.in ; \
$(UTILS_BIN)/font-retry.o: $(UTILS_SRC)/font-retry.c
$(UTILS_BIN)/xshm.o: $(UTILS_SRC)/xshm.c
$(UTILS_BIN)/aligned_malloc.o: $(UTILS_SRC)/aligned_malloc.c
+$(UTILS_BIN)/screenshot.o: $(UTILS_SRC)/screenshot.c
UTIL_OBJS = $(UTILS_BIN)/overlay.o \
$(UTILS_BIN)/utf8wc.o \
$(UTILS_BIN)/font-retry.o \
$(UTILS_BIN)/xshm.o \
- $(UTILS_BIN)/aligned_malloc.o
+ $(UTILS_BIN)/aligned_malloc.o \
+ $(UTILS_BIN)/screenshot.o
$(UTIL_OBJS):
$(MAKE2CC) -C $(UTILS_BIN) $(@F)
xscreensaver.o: xscreensaver.c
$(CC) -c $(CC_ALL) $(DAEMON_DEFS) $<
+xscreensaver.o: $(WAYLAND_GEN)
+
xscreensaver-auth.o: XScreenSaver_ad.h
xscreensaver-auth.o: xscreensaver-auth.c
$(CC) -c $(CC_ALL) $(AUTH_DEFS) $<
$(CC) -c $(CC_ALL) $(GTK_DEFS) $(GTK_WARNINGS) $<
xscreensaver-settings-Gtk: $(GTK_OBJS)
- $(CC) $(LDFLAGS) -o $@ $(GTK_OBJS) $(GTK_LIBS)
+ $(CC) $(LDFLAGS) -o $@ $(GTK_OBJS) $(GTK_LIBS) $(WAYLAND_LIBS)
demo-Xm.o: XScreenSaver_ad.h
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
+ $(UTILS_BIN)/xmu.o $(UTILS_BIN)/aligned_malloc.o \
+ $(UTILS_BIN)/screenshot.o
test-fade: $(TEST_FADE_OBJS)
$(CC) $(LDFLAGS) -o $@ $(TEST_FADE_OBJS) $(GFX_LIBS)
#
# DO NOT DELETE: updated by make distdepend
+atoms.o: $(srcdir)/atoms.h
+atoms.o: ../config.h
+atomswm.o: $(srcdir)/atoms.h
+atomswm.o: ../config.h
+blurb.o: $(srcdir)/blurb.h
+blurb.o: ../config.h
+clientmsg.o: $(srcdir)/atoms.h
+clientmsg.o: $(srcdir)/blurb.h
+clientmsg.o: $(srcdir)/clientmsg.h
+clientmsg.o: ../config.h
demo-Gtk-conf.o: $(srcdir)/blurb.h
demo-Gtk-conf.o: ../config.h
demo-Gtk-conf.o: $(srcdir)/demo-Gtk-conf.h
demo-Gtk.o: $(UTILS_SRC)/xscreensaver-intl.h
demo-Xm.o: ../config.h
demo-Xm-widgets.o: ../config.h
+dialog.o: $(srcdir)/atoms.h
+dialog.o: $(srcdir)/auth.h
+dialog.o: $(srcdir)/blurb.h
+dialog.o: ../config.h
+dialog.o: $(srcdir)/prefs.h
+dialog.o: $(srcdir)/screens.h
+dialog.o: $(UTILS_SRC)/font-retry.h
+dialog.o: $(UTILS_SRC)/resources.h
+dialog.o: $(UTILS_SRC)/usleep.h
+dialog.o: $(UTILS_SRC)/utf8wc.h
+dialog.o: $(UTILS_SRC)/version.h
+dialog.o: $(UTILS_SRC)/visual.h
+dialog.o: $(UTILS_SRC)/xft.h
+dialog.o: $(UTILS_SRC)/xftwrap.h
+dialog.o: $(srcdir)/xinput.h
+dpms.o: $(srcdir)/blurb.h
+dpms.o: ../config.h
+dpms.o: $(srcdir)/types.h
+dpms.o: $(srcdir)/xscreensaver.h
+exec.o: ../config.h
+exec.o: $(srcdir)/exec.h
+exts.o: $(srcdir)/blurb.h
+exts.o: ../config.h
+exts.o: $(srcdir)/types.h
+exts.o: $(srcdir)/xscreensaver.h
+fade.o: $(srcdir)/atoms.h
+fade.o: $(srcdir)/blurb.h
+fade.o: $(srcdir)/clientmsg.h
+fade.o: ../config.h
+fade.o: $(srcdir)/fade.h
+fade.o: $(UTILS_SRC)/usleep.h
+fade.o: $(UTILS_SRC)/visual.h
+fade.o: $(UTILS_SRC)/xmu.h
+fade.o: $(UTILS_SRC)/xshm.h
+fade.o: $(srcdir)/xinput.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: $(srcdir)/blurb.h
+passwd.o: ../config.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-pwent.o: $(srcdir)/auth.h
passwd-pwent.o: $(srcdir)/blurb.h
passwd-pwent.o: ../config.h
+prefs.o: $(srcdir)/blurb.h
+prefs.o: ../config.h
+prefs.o: $(srcdir)/prefs.h
+prefsw.o: $(srcdir)/blurb.h
+prefsw.o: ../config.h
+prefsw.o: $(srcdir)/prefs.h
+prefsw.o: $(srcdir)/types.h
+prefsw.o: $(UTILS_SRC)/resources.h
+prefsw.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: $(srcdir)/blurb.h
+screens.o: ../config.h
+screens.o: $(srcdir)/screens.h
+setuid.o: $(srcdir)/auth.h
+setuid.o: $(srcdir)/blurb.h
+setuid.o: ../config.h
+subprocs.o: $(srcdir)/atoms.h
+subprocs.o: $(srcdir)/blurb.h
+subprocs.o: ../config.h
+subprocs.o: $(srcdir)/exec.h
+subprocs.o: $(srcdir)/types.h
+subprocs.o: $(UTILS_SRC)/screenshot.h
+subprocs.o: $(UTILS_SRC)/visual.h
+subprocs.o: $(UTILS_SRC)/yarandom.h
+subprocs.o: $(srcdir)/xscreensaver.h
test-fade.o: $(srcdir)/atoms.h
test-fade.o: $(srcdir)/blurb.h
test-fade.o: ../config.h
test-fade.o: $(srcdir)/screens.h
test-fade.o: $(srcdir)/types.h
test-fade.o: $(UTILS_SRC)/resources.h
+test-fade.o: $(UTILS_SRC)/screenshot.h
test-fade.o: $(srcdir)/xscreensaver.h
test-grab.o: $(srcdir)/blurb.h
test-grab.o: ../config.h
test-yarandom.o: $(srcdir)/blurb.h
test-yarandom.o: ../config.h
test-yarandom.o: $(UTILS_SRC)/yarandom.h
+wayland-idle.o: $(srcdir)/blurb.h
+wayland-idle.o: ../config.h
+wayland-idle.o: $(srcdir)/ext-idle-notify-v1-client-protocol.h
+wayland-idle.o: $(srcdir)/idle-client-protocol.h
+wayland-idle.o: $(srcdir)/wayland-idle.h
+windows.o: $(srcdir)/atoms.h
+windows.o: $(srcdir)/blurb.h
+windows.o: ../config.h
+windows.o: $(srcdir)/exec.h
+windows.o: $(srcdir)/fade.h
+windows.o: $(srcdir)/screens.h
+windows.o: $(srcdir)/types.h
+windows.o: $(UTILS_SRC)/font-retry.h
+windows.o: $(UTILS_SRC)/resources.h
+windows.o: $(UTILS_SRC)/screenshot.h
+windows.o: $(UTILS_SRC)/visual.h
+windows.o: $(UTILS_SRC)/xft.h
+windows.o: $(srcdir)/xscreensaver.h
+xinput.o: $(srcdir)/blurb.h
+xinput.o: ../config.h
+xinput.o: $(srcdir)/xinput.h
+xscreensaver-auth.o: XScreenSaver_ad.h
+xscreensaver-auth.o: $(srcdir)/atoms.h
+xscreensaver-auth.o: $(srcdir)/auth.h
+xscreensaver-auth.o: $(srcdir)/blurb.h
+xscreensaver-auth.o: ../config.h
+xscreensaver-auth.o: $(srcdir)/types.h
+xscreensaver-auth.o: $(UTILS_SRC)/resources.h
+xscreensaver-auth.o: $(UTILS_SRC)/version.h
+xscreensaver-auth.o: $(UTILS_SRC)/visual.h
+xscreensaver-auth.o: $(UTILS_SRC)/yarandom.h
+xscreensaver-auth.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-gfx.o: XScreenSaver_ad.h
+xscreensaver-gfx.o: $(srcdir)/atoms.h
+xscreensaver-gfx.o: $(srcdir)/blurb.h
+xscreensaver-gfx.o: $(srcdir)/clientmsg.h
+xscreensaver-gfx.o: ../config.h
+xscreensaver-gfx.o: $(srcdir)/screens.h
+xscreensaver-gfx.o: $(srcdir)/types.h
+xscreensaver-gfx.o: $(UTILS_SRC)/resources.h
+xscreensaver-gfx.o: $(UTILS_SRC)/version.h
+xscreensaver-gfx.o: $(UTILS_SRC)/visual.h
+xscreensaver-gfx.o: $(UTILS_SRC)/xmu.h
+xscreensaver-gfx.o: $(UTILS_SRC)/yarandom.h
+xscreensaver-gfx.o: $(srcdir)/xscreensaver.h
+xscreensaver.o: $(srcdir)/atoms.h
+xscreensaver.o: $(srcdir)/blurb.h
+xscreensaver.o: $(srcdir)/clientmsg.h
+xscreensaver.o: ../config.h
+xscreensaver.o: $(srcdir)/prefs.h
+xscreensaver.o: $(UTILS_SRC)/version.h
+xscreensaver.o: $(UTILS_SRC)/xmu.h
+xscreensaver.o: $(srcdir)/xinput.h
xscreensaver-systemd.o: $(srcdir)/blurb.h
xscreensaver-systemd.o: ../config.h
xscreensaver-systemd.o: $(UTILS_SRC)/queue.h
! a screen saver and locker for the X window system
! by Jamie Zawinski
!
-! version 6.10
-! 27-Apr-2025
+! version 6.11
+! 01-Jul-2025
!
! See "man xscreensaver" for more info. The latest version is always
! available at https://www.jwz.org/xscreensaver/
-/* xscreensaver-command, Copyright © 1991-2023 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver-command, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
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_DEACTIVATE, XA_CYCLE, XA_RESTART, XA_AUTH,
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_STATE_STAYS_ON_TOP, XA_KDE_NET_WM_WINDOW_TYPE_OVERRIDE,
XA_DEMO = A("DEMO");
XA_LOCK = A("LOCK");
XA_BLANK = A("BLANK");
+ XA_AUTH = A("AUTH");
XA_NET_WM_PID = A("_NET_WM_PID");
XA_NET_WM_STATE = A("_NET_WM_STATE");
-/* xscreensaver-command, Copyright © 1991-2023 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver-command, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
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_DEACTIVATE, XA_CYCLE, XA_RESTART, XA_AUTH,
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_STATE_STAYS_ON_TOP, XA_KDE_NET_WM_WINDOW_TYPE_OVERRIDE,
*/
typedef long PROP32;
-
#endif /* _XSCREENSAVER_ATOMS_H_ */
/* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
- * xscreensaver, Copyright © 1993-2024 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 1993-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#ifdef HAVE_GTK /* whole file */
+#define _GNU_SOURCE
#ifdef ENABLE_NLS
# include <locale.h>
#endif /* ENABLE_NLS */
#include <gtk/gtk.h>
#include <gdk/gdkx.h> /* For gdk_x11_get_default_xdisplay(), etc. */
+#ifdef GDK_WINDOWING_WAYLAND
+# include <gdk/gdkwayland.h>
+#else
+# define GDK_IS_WAYLAND_DISPLAY(dpy) False
+#endif
+
#if (__GNUC__ >= 4)
# pragma GCC diagnostic pop
#endif
#include "screenshot.h"
#include "xmu.h"
+#ifdef HAVE_WAYLAND
+# include "wayland-idle.h"
+#endif
+
#include "demo-Gtk-conf.h"
GtkWindow *dialog;
Display *dpy;
- Bool wayland_p;
+ enum { X11_BACKEND, WAYLAND_BACKEND, XWAYLAND_BACKEND } backend;
Pixmap screenshot;
Visual *gl_visual;
Bool flushing_p; /* flag for breaking recursion loops */
Bool saving_p; /* flag for breaking recursion loops */
Bool dpms_supported_p; /* Whether XDPMS is available */
+ Bool grabbing_supported_p; /* Whether "Grab Desktop" and "Fade" work */
char *desired_preview_cmd; /* subprocess we intend to run */
char *running_preview_cmd; /* subprocess we are currently running */
Bool root_p = (geteuid () == 0);
strcpy (buf,
- _("The xscreensaver daemon did not start up properly.\n"
+ _("The XScreenSaver daemon did not start up properly.\n"
"\n"));
if (root_p)
}
}
else if (verbose_p)
- fprintf (stderr, "%s: chooser: cancelled\n", blurb());
+ fprintf (stderr, "%s: chooser: canceled\n", blurb());
gtk_widget_destroy (dialog);
return changed_p;
int hack_number = -1;
if (!s->dpy) return hack_number;
+
+ /* XA_SCREENSAVER_STATUS format documented in windows.c. */
if (XGetWindowProperty (s->dpy, RootWindow(s->dpy, 0), /* always screen #0 */
XA_SCREENSAVER_STATUS,
- 0, 3, FALSE, XA_INTEGER,
+ 0, 999, FALSE, XA_INTEGER,
&type, &format, &nitems, &bytesafter,
&dataP)
== Success
&& dataP)
{
PROP32 *data = (PROP32 *) dataP;
- hack_number = (int) data[2] - 1;
+ hack_number = (int) data[3] - 1; /* Hack running on the first screen */
}
if (dataP) XFree (dataP);
can_lock_p = FALSE;
# endif
+ if (s->backend == WAYLAND_BACKEND ||
+ s->backend == XWAYLAND_BACKEND)
+ can_lock_p = FALSE;
+
/* If there is only one screen, the mode menu contains
"random" but not "random-same".
*/
TOGGLE_ACTIVE (dpms_button, p->dpms_enabled_p && s->dpms_supported_p);
TOGGLE_ACTIVE (dpms_quickoff_button, (p->dpms_quickoff_p &&
s->dpms_supported_p));
- TOGGLE_ACTIVE (grab_desk_button, p->grab_desktop_p);
+ TOGGLE_ACTIVE (grab_desk_button, (p->grab_desktop_p &&
+ s->grabbing_supported_p));
TOGGLE_ACTIVE (grab_video_button, p->grab_video_p);
TOGGLE_ACTIVE (grab_image_button, p->random_image_p);
- TOGGLE_ACTIVE (fade_button, p->fade_p);
- TOGGLE_ACTIVE (unfade_button, p->unfade_p);
+ TOGGLE_ACTIVE (fade_button, p->fade_p && s->grabbing_supported_p);
+ TOGGLE_ACTIVE (unfade_button, p->unfade_p && s->grabbing_supported_p);
switch (p->tmode)
{
SENSITIZE (dpms_off_spinbutton, s->dpms_supported_p && p->dpms_enabled_p);
SENSITIZE (dpms_quickoff_button, s->dpms_supported_p);
- SENSITIZE (fade_label, (p->fade_p || p->unfade_p));
- SENSITIZE (fade_spinbutton, (p->fade_p || p->unfade_p));
+ /* Fading
+ */
+ SENSITIZE (fade_button, s->grabbing_supported_p);
+ SENSITIZE (unfade_button, s->grabbing_supported_p);
+ SENSITIZE (fade_label, ((p->fade_p || p->unfade_p) &&
+ s->grabbing_supported_p));
+ SENSITIZE (fade_spinbutton, ((p->fade_p || p->unfade_p) &&
+ s->grabbing_supported_p));
# undef SENSITIZE
GtkWidget *notebook = win->preview_notebook;
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook),
- (!s->running_preview_error_p ? 0 : /* ok */
- nothing_p ? 3 : /* no hacks installed */
- !available_p ? 2 : /* hack not installed */
- s->wayland_p ? 4 : /* fucking wayland */
- 1)); /* preview failed */
+ (!s->running_preview_error_p ? 0 : /* ok */
+ nothing_p ? 3 : /* no hacks installed */
+ !available_p ? 2 : /* hack not installed */
+ s->backend == WAYLAND_BACKEND ? 4 : /* no previews without X11 */
+ 1)); /* preview failed */
}
/* If a subproc is running, clear the window to black when we resize.
Without this, sometimes turds get left behind. */
- if (s->dpy && !s->wayland_p && s->running_preview_cmd)
+ if (s->dpy &&
+ s->backend != WAYLAND_BACKEND &&
+ s->running_preview_cmd)
{
GdkWindow *window = gtk_widget_get_window (self);
Window id;
*/
XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
GtkWidget *pr = win->preview;
- if (s->dpy && !s->wayland_p && gtk_widget_get_realized (pr))
+ if (s->dpy &&
+ s->backend != WAYLAND_BACKEND &&
+ gtk_widget_get_realized (pr))
{
GdkWindow *window = gtk_widget_get_window (pr);
Window oid = (window ? gdk_x11_window_get_xid (window) : 0);
new_cmd = malloc (strlen (cmd) + 40);
- id = (window && !s->wayland_p
+ id = (window && s->backend != WAYLAND_BACKEND
? gdk_x11_window_get_xid (window)
: 0);
if (id == 0)
Bool blanked_p = FALSE;
if (!s->dpy) return FALSE;
+
+ /* XA_SCREENSAVER_STATUS format documented in windows.c. */
if (XGetWindowProperty (s->dpy, RootWindow (s->dpy, 0), /* always screen 0 */
XA_SCREENSAVER_STATUS,
- 0, 3, FALSE, XA_INTEGER,
+ 0, 999, FALSE, XA_INTEGER,
&type, &format, &nitems, &bytesafter,
&dataP)
== Success
&& dataP)
{
Atom *data = (Atom *) dataP;
- blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
+ blanked_p = (data[0] != 0);
}
if (dataP) XFree (dataP);
*/
sprintf(msg,
_("%s is running as user \"%s\" on host \"%s\".\n"
- "But the xscreensaver managing display \"%.25s\"\n"
+ "But the XScreenSaver managing display \"%.25s\"\n"
"is running as user \"%s\" on host \"%s\".\n"
"\n"
"Since they are different users, they won't be reading/writing\n"
"You should either re-run %s as \"%s\", or re-run\n"
"xscreensaver as \"%s\".\n"
"\n"
- "Restart the xscreensaver daemon now?\n"),
+ "Restart the XScreenSaver daemon now?\n"),
progname, luser, lhost,
d,
(ruser ? ruser : "???"), (rhost ? rhost : "???"),
*/
sprintf (msg,
_("%s is running as user \"%s\" on host \"%s\".\n"
- "But the xscreensaver managing display \"%s\"\n"
+ "But the XScreenSaver managing display \"%s\"\n"
"is running as user \"%s\" on host \"%s\".\n"
"\n"
"If those two machines don't share a file system (that is,\n"
*/
sprintf (msg,
_("This is %s version %s.\n"
- "But the xscreensaver managing display \"%s\"\n"
+ "But the XScreenSaver managing display \"%s\"\n"
"is version %s. This could cause problems.\n"
"\n"
- "Restart the xscreensaver daemon now?\n"),
+ "Restart the XScreenSaver daemon now?\n"),
progname, s->short_version,
d,
rversion);
warning_dialog (s->window, _("Error"),
_("No GL visuals: the xscreensaver-gl* packages are required."));
- if (s->wayland_p)
- warning_dialog (s->window, _("Warning"),
- _("You are running Wayland rather than the X Window System.\n"
- "\n"
- "Under Wayland, idle-detection fails when non-X11 programs\n"
- "are selected, meaning the screen may blank prematurely.\n"
- "Also, locking is impossible.\n"
- "\n"
- "See the XScreenSaver manual for instructions on\n"
- "configuring your system to use X11 instead of Wayland.\n"));
+ if (s->backend != X11_BACKEND)
+ {
+# ifdef HAVE_WAYLAND
+ /* Connect to the Wayland server in the same way that the XScreenSaver
+ daemon will, and if we are in a state where the daemon won't work
+ properly, pop up a dialog box explaining why.
+ */
+ const char *wayland_err = 0;
+ wayland_state *wayland = wayland_idle_init (NULL, NULL, &wayland_err);
+
+ if (s->debug_p && wayland_err)
+ fprintf (stderr, "%s: wayland: %s\n", blurb(), wayland_err);
+
+ if (wayland) /* Connected to Wayland, have necessary extensions. */
+ {
+ wayland_idle_free (wayland);
+ }
+ else if (wayland_err && !strcmp (wayland_err, "connection failed"))
+ {
+ if (getenv ("WAYLAND_DISPLAY") || getenv ("WAYLAND_SOCKET"))
+ {
+ warning_dialog (s->window, _("Warning"),
+ _("Unable to connect to the Wayland server. "
+ "The XScreenSaver daemon will not work.\n"));
+ }
+ /* Otherwise, presumably running under real X11. */
+ }
+ else if (wayland_err)
+ {
+ /* Connected but the necessary extensions are missing. */
+ char msg [1024];
+ sprintf (msg,
+ _("Wayland error:\n\n"
+ "%.100s\n\n"
+ "The XScreenSaver daemon will not work.\n"),
+ wayland_err);
+ warning_dialog (s->window, _("Warning"), msg);
+ }
+# else /* !HAVE_WAYLAND */
+ if (s->debug_p)
+ fprintf (stderr, "%s: wayland: disabled at compile time\n", blurb());
+ warning_dialog (s->window, _("Warning"),
+ _("Not compiled with support for Wayland. "
+ "The XScreenSaver daemon will not work.\n"));
+# endif /* !HAVE_WAYLAND */
+ }
return FALSE; /* Only run timer once */
}
char *old = p->settings_geom;
char str[100];
- if (!s->dpy || s->wayland_p) return;
+ if (!s->dpy || s->backend == WAYLAND_BACKEND) return;
wm_decoration_origin (win, &x, &y);
if (!old || !*old ||
static void
xscreensaver_window_realize (GtkWidget *self, gpointer user_data)
{
+ GdkDisplay *gdpy = gdk_display_get_default();
XScreenSaverWindow *win = XSCREENSAVER_WINDOW (self);
state *s = &win->state;
saver_preferences *p = &s->prefs;
s->short_version = XSCREENSAVER_VERSION;
s->window = GTK_WINDOW (win);
- s->dpy = gdk_x11_get_default_xdisplay();
-
- /* Debian 11.4, Gtk 3.24.24, 2022: under Wayland, get_default_xdisplay is
- returning uninitialized data! However, gdk_x11_window_get_xid prints a
- warning and returns NULL. So let's try that, and as a fallback, also try
- and sanity check the contents of the Display structure...
- */
- if (! gdk_x11_window_get_xid (gtk_widget_get_window (self)))
- {
- s->dpy = NULL;
- s->wayland_p = TRUE;
- }
+ if (GDK_IS_WAYLAND_DISPLAY (gdpy))
+ s->backend = WAYLAND_BACKEND; /* Should not happen */
+ else if (GDK_IS_X11_DISPLAY (gdpy) &&
+ (getenv ("WAYLAND_DISPLAY") ||
+ getenv ("WAYLAND_SOCKET")))
+ s->backend = XWAYLAND_BACKEND;
+ else
+ s->backend = X11_BACKEND;
- if (s->dpy)
- {
- if (ProtocolVersion (s->dpy) != 11 ||
- ProtocolRevision (s->dpy) != 0)
- {
- fprintf (stderr, "%s: uninitialized data in Display: "
- "protocol version %d.%d!\n", blurb(),
- ProtocolVersion(s->dpy), ProtocolRevision(s->dpy));
- s->dpy = NULL;
- s->wayland_p = TRUE;
- }
- }
+ if (s->debug_p)
+ fprintf (stderr, "%s: GDK backend is %s\n", blurb(),
+ (s->backend == X11_BACKEND ? "X11" :
+ s->backend == XWAYLAND_BACKEND ? "XWayland" :
+ s->backend == WAYLAND_BACKEND ? "Wayland" : "???"));
- /* If we don't have a display connection, then we are surely under Wayland
- even if the environment variable is not set.
- */
- if (!s->dpy &&
- !getenv ("WAYLAND_DISPLAY") &&
- !getenv ("WAYLAND_SOCKET"))
- putenv ("WAYLAND_DISPLAY=probably");
+ if (s->backend != WAYLAND_BACKEND)
+ s->dpy = gdk_x11_get_default_xdisplay();
- if (getenv ("WAYLAND_DISPLAY") ||
- getenv ("WAYLAND_SOCKET"))
- s->wayland_p = TRUE;
- /* If GTK is running directly under Wayland, try to open an X11 connection
- to XWayland anyway, so that get_string_resource and load_init_file work.
+ /* If GDK is using native Wayland, try to open an X11 connection to
+ XWayland anyway, so that get_string_resource and load_init_file work.
+ This should never happen, since we should always be either X11_BACKEND
+ or XWAYLAND_BACKEND, never WAYLAND_BACKEND.
*/
- if (! s->dpy)
+ if (s->backend == WAYLAND_BACKEND)
{
s->dpy = XOpenDisplay (NULL);
if (s->debug_p)
fprintf (stderr, "%s: DPMS not supported at compile time\n", blurb());
# endif /* !HAVE_DPMS_EXTENSION */
-# if defined(__APPLE__) && !defined(__OPTIMIZE__)
+# if defined(__APPLE__) && !defined(HAVE_COCOA) && !defined(__OPTIMIZE__)
s->dpms_supported_p = TRUE; /* macOS X11: debugging kludge */
# endif
+ /* Under Wayland, we can only grab screenshots if "grim" is installed,
+ and even so, there's no way to grab screenshots under GNOME or KDE.
+ See comment in xscreensaver-getimage.c, and discussion thread here:
+ https://www.jwz.org/blog/2025/06/wayland-screenshots/#comment-260326
+ */
+ s->grabbing_supported_p = True;
+ if (getenv ("WAYLAND_DISPLAY") || getenv ("WAYLAND_SOCKET"))
+ {
+ const char *prog = "grim";
+ char *desk = getenv ("XDG_CURRENT_DESKTOP");
+fprintf(stderr,"##A %s\n", desk);
+ if (desk &&
+ (strcasestr (desk, "GNOME") ||
+ strcasestr (desk, "KDE")))
+ {
+fprintf(stderr,"##B %s\n", desk);
+ s->grabbing_supported_p = False;
+ if (s->debug_p)
+ fprintf (stderr,
+ "%s: screenshots and fading not supported on Wayland %s\n",
+ blurb(), desk);
+ }
+ else if (! on_path_p (prog))
+ {
+fprintf(stderr,"##C %s\n", desk);
+ s->grabbing_supported_p = False;
+ if (s->debug_p)
+ fprintf (stderr,
+ "%s: screenshots and fading on Wayland require \"%s\"\n",
+ blurb(), prog);
+ }
+ }
+
if (s->dpy)
init_xscreensaver_atoms (s->dpy);
populate_prefs_page (s);
sensitize_demo_widgets (s, FALSE);
scroll_to_current_hack (s);
- if (s->dpy && !s->wayland_p)
+ if (s->dpy && s->backend != WAYLAND_BACKEND)
fix_preview_visual (s);
if (! s->multi_screen_p)
hide_mode_menu_random_same (s);
# endif
/* Grab the screenshot pixmap before mapping the window. */
- if (s->dpy && !s->wayland_p)
+ if (s->dpy && s->backend != WAYLAND_BACKEND)
{
GdkWindow *gw = gtk_widget_get_window (win->preview);
Window xw = gdk_x11_window_get_xid (gw);
- s->screenshot = screenshot_grab (s->dpy, xw, TRUE, s->debug_p);
+ XWindowAttributes xgwa;
+ /* Make sure the grab is the size of the root window.
+ It would be better if it was the screen, but close enough. */
+ XGetWindowAttributes (s->dpy, xw, &xgwa);
+ if (s->grabbing_supported_p)
+ s->screenshot = screenshot_grab (s->dpy, xgwa.root, TRUE, s->debug_p);
}
/* Issue any warnings about the running xscreensaver daemon.
"Print diagnostics to stderr",
NULL);
g_signal_connect (app, "handle-local-options", G_CALLBACK (opts_cb), app);
+
+ /* If we are under Wayland, tell GDK to use XWayland as the backend rather
+ than native Wayland. This is the only way for hack previews to work.
+ If this setting is respected, we should always end up with 'backend' set
+ to either X11_BACKEND or XWAYLAND_BACKEND, and never to WAYLAND_BACKEND.
+ */
+ gdk_set_allowed_backends ("x11");
+
return app;
}
/* demo-Xm.c --- implements the interactive demo-mode and options dialogs.
- * xscreensaver, Copyright © 1993-2021 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 1993-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
Atom type;
int format;
unsigned long nitems, bytesafter;
- unsigned char *data = 0;
+ unsigned char *dataP = 0;
Display *dpy = XtDisplay (toplevel);
- int which = 0;
+ int hack_number = -1;
Widget list;
+ /* XA_SCREENSAVER_STATUS format documented in windows.c. */
if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
XA_SCREENSAVER_STATUS,
- 0, 3, False, XA_INTEGER,
+ 0, 999, False, XA_INTEGER,
&type, &format, &nitems, &bytesafter,
- &data)
+ &dataP)
== Success
&& type == XA_INTEGER
&& nitems >= 3
- && data)
- which = (int) data[2] - 1;
+ && dataP)
+ {
+ PROP32 *data = (PROP32 *) dataP;
+ hack_number = (int) data[3] - 1; /* Hack running on the first screen */
+ }
- if (data) free (data);
+ if (dataP) XFree (dataP);
- if (which < 0)
+ if (hack_number < 0)
return;
list = name_to_widget (toplevel, "list");
apply_changes_and_save (toplevel);
XmListDeselectAllItems (list); /* LessTif lossage */
- XmListSelectPos (list, which+1, True);
+ XmListSelectPos (list, hack_number+1, True);
ensure_selected_item_visible (list);
- populate_demo_window (toplevel, which, pair);
+ populate_demo_window (toplevel, hack_number, pair);
}
;
*s = 0;
s = strrchr (prog_name, '/');
- if (s) strcpy (prog_name, s+1);
+ if (s) memmove (prog_name, s+1, strlen (s));
sprintf (doc_name, "hacks.%s.documentation", pretty_name);
sprintf (doc_class, "hacks.%s.documentation", prog_name);
/* dialog.c --- the password dialog and splash screen.
- * xscreensaver, Copyright © 1993-2023 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 1993-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
auth_state auth_state;
int xi_opcode;
int xkb_opcode;
+ XIM im;
+ XIC ic;
/* Variant strings
*/
}
+/* In the olden days, the way to have multiple keyboard layouts was:
+
+ A) Run "xmodmap" to load a different layout, one at a time. Fun!
+
+ B) Have a "Mode_switch" key somewhere on your keyboard (traditionally on
+ the key labeled "AltGr"), and attached to Mod5. When "Mode_switch" was
+ toggled on, the KeyPress event's 'state' bitmask would have the Mod5 bit
+ set, meaning that the client should use the keycode's 3rd and 4th
+ columns in the map instead of the 1st and 2nd. In this way you could
+ have exactly two layouts.
+
+ But in This Modern World, the XKeyboard extension does... other magic.
+
+ C) Under LXDE circa 2021-2025, the Keyboard Preferences dialog lets you
+ select one alternate keyboard, and a key combo, typically Ctrl + Alt.
+ Through some mechanism I do not understand, it configures XKB so that
+ pressing Ctrl + Alt causes XLookupString to map the keycode to a
+ different keysym. It does this without the 'state' field having Mod5
+ set.
+
+ Bit 13 is set in 'state', but I don't know what that is or where it is
+ defined. If I had to guess, I'd say that XKB.h:XkbBuildCoreState()
+ uses bits 13 and 14 to indicate up to 4 alternate keyboard layouts, but
+ I have't found any documentation to this effect, nor do I know how
+ XLookupString knows to interpret those bits. Or why LXDE limits you to
+ just 1 alternate layout.
+
+ When you press Ctrl + Alt, an XkbEvent comes in, which is our clue that
+ the keyboard layout *may* have changed. When that happens, we call
+ this function to present the name of the current layout on the window.
+
+ Pressing Ctrl + Alt does not send a MappingNotify event, because that
+ would make too much sense.
+ */
static void
get_keyboard_layout (window_state *ws)
{
XSetWindowColormap (ws->dpy, ws->window, ws->cmap);
xscreensaver_set_wm_atoms (ws->dpy, ws->window, w, h, 0);
+ /* An input method is necessary for dead keys to work.
+ */
+ if (! ws->im)
+ ws->im = XOpenIM (ws->dpy, NULL, NULL, NULL);
+ if (ws->ic)
+ XDestroyIC (ws->ic), ws->ic = 0;
+ if (ws->im)
+ ws->ic = XCreateIC (ws->im,
+ XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
+ XNClientWindow, ws->window,
+ NULL);
+ if (ws->ic)
+ XSetICFocus (ws->ic);
+
+ if (verbose_p)
+ fprintf (stderr, "%s: %s input method\n", blurb(),
+ ws->ic ? "attached" : "failed to attach");
+
if (ow)
{
XMapRaised (ws->dpy, ws->window);
if (increment_p && clear_p) abort();
- /* Read the old property so that we can increment it. */
+ /* Read the old property so that we can increment it.
+ Structure:
+
+ XScreenSaver 6.00, 2021: XScreenSaver >= 6.11, 2025:
+
+ 0: count 0: count
+ 1: 32 bit time_t 1: 64 bit time_t hi
+ 2: 64 bit time_t lo
+ */
if (XGetWindowProperty (dpy, w, prop,
0, 999, False, XA_INTEGER,
&type, &format, &nitems, &bytesafter,
&dataP)
== Success
&& type == XA_INTEGER
- && nitems >= 2
+ && nitems >= 3
&& dataP)
{
- count = ((PROP32 *) dataP) [0];
- tt = ((PROP32 *) dataP) [1]; /* Y2038 bug: unsigned 32 bit time_t */
+ PROP32 *data = (PROP32 *) dataP;
+ count = data[0];
+ tt = (time_t) /* 64 bit time_t */
+ ((((unsigned long) data[1] & 0xFFFFFFFFL) << 32) |
+ ((unsigned long) data[2] & 0xFFFFFFFFL));
+
if (verbose_p)
fprintf (stderr, "%s: previous auth failures: %d @ %lu\n",
blurb(), count, (unsigned long) tt);
}
else if (increment_p)
{
- PROP32 vv[2];
+ PROP32 vv[3];
count++;
/* Remember the time of the *oldest* failed login. A failed login
if (tt <= 0) tt = time ((time_t *) 0);
vv[0] = (PROP32) count;
- vv[1] = (PROP32) tt;
+ vv[1] = (PROP32) (((unsigned long) tt) >> 32);
+ vv[2] = (PROP32) (((unsigned long) tt) & 0xFFFFFFFFL);
+
XChangeProperty (dpy, w, prop, XA_INTEGER, 32,
- PropModeReplace, (unsigned char *) vv, 2);
+ PropModeReplace, (unsigned char *) vv, 3);
if (verbose_p)
fprintf (stderr, "%s: saved auth failure: %d @ %lu\n",
blurb(), count, (unsigned long) tt);
static void bs_timer (XtPointer, XtIntervalId *);
static void
-handle_keypress (window_state *ws, XKeyEvent *event)
+handle_keypress (window_state *ws, XKeyEvent *event, Bool filter_p)
{
unsigned char decoded [MAX_BYTES_PER_CHAR * 10]; /* leave some slack */
KeySym keysym = 0;
int decoded_size = XLookupString (event, (char *)decoded, sizeof(decoded),
&keysym, &ws->compose_status);
+ /* But if we have an input method, we can unambiguously get UTF8.
+ */
+ if (ws->ic && event->type == KeyPress)
+ {
+ char decoded2[sizeof(decoded)];
+ Status s = 0;
+ KeySym keysym2 = 0;
+ int size2 = Xutf8LookupString (ws->ic, (XKeyPressedEvent *) event,
+ decoded2, sizeof(decoded2)-1
+ , &keysym2, &s);
+ decoded2[size2] = 0;
+
+ switch (s) {
+ case XLookupChars: /* Set 'c2' to a UTF8 string */
+ if (*decoded2)
+ {
+ strcpy ((char *) decoded, (char *) decoded2);
+ decoded_size = size2;
+ }
+ break;
+ case XLookupKeySym: /* Set 'keysym2' but not 'decoded2' */
+ if (keysym2)
+ keysym = keysym2;
+ break;
+ case XLookupBoth: /* Set 'keysym2' and 'decoded2' */
+ if (keysym2)
+ keysym = keysym2;
+ if (*decoded2)
+ {
+ strcpy ((char *) decoded, (char *) decoded2);
+ decoded_size = size2;
+ }
+ break;
+ case XLookupNone: /* No input yet */
+ case XBufferOverflow: /* 'c2' was too small */
+ break;
+ default:
+ abort();
+ break;
+ }
+ }
+
if (decoded_size > MAX_BYTES_PER_CHAR)
{
/* The multi-byte character returned is too large. */
SELF_INSERT:
nbytes = strlen (ws->plaintext_passwd);
nchars = strlen (ws->plaintext_passwd_char_size);
- if (nchars + 1 >= sizeof (ws->plaintext_passwd_char_size)-1 ||
+
+ if (filter_p)
+ ; /* Ignore: it is probably the start of a dead-key sequence. */
+ else 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)
static Bool
-handle_event (window_state *ws, XEvent *xev)
+handle_event (window_state *ws, XEvent *xev, Bool filter_p)
{
Bool refresh_p = False;
switch (xev->xany.type) {
ws->auth_state = AUTH_CANCEL;
else
{
- handle_keypress (ws, &xev->xkey);
+ handle_keypress (ws, &xev->xkey, filter_p);
ws->caps_p = (xev->xkey.state & LockMask);
if (ws->auth_state == AUTH_NOTIFY)
ws->auth_state = AUTH_CANCEL;
{
XEvent xev;
XtInputMask m = XtAppPending (ws->app);
+ Bool filtered_p = False;
if (m & XtIMXEvent)
/* Process timers then block on an X event (which we know is there) */
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 (test_mode_ignore_xi_events)
- ok = False;
- if (ok)
+ {
+ XEvent ev2;
+ Bool swap_p = False;
+
+ if (xev.xcookie.type == GenericEvent &&
+ xev.xcookie.extension == ws->xi_opcode &&
+ (xev.xcookie.data || XGetEventData (ws->dpy, &xev.xcookie)))
+ {
+ swap_p = xinput_event_to_xlib (xev.xcookie.evtype,
+ xev.xcookie.data, &ev2);
+ if (test_mode_ignore_xi_events)
+ swap_p = False;
+ }
+
+ /* XFilterEvent does not work on XInput events, so check the
+ newly-synthesized X11 event instead. */
+ filtered_p = XFilterEvent ((swap_p ? &ev2 : &xev), ws->window);
+
+ /* Log the original event, not the synthetic one. */
+ if (verbose_p || debug_p)
+ print_xinput_event (ws->dpy, &xev, ws->ic,
+ (filtered_p ? " (filtered)" : ""));
+ if (swap_p)
+ {
+ XFreeEventData (ws->dpy, &xev.xcookie);
xev = ev2;
- }
+ }
+ }
- if (handle_event (ws, &xev))
+ if (handle_event (ws, &xev, filtered_p))
refresh_p = True;
XtDispatchEvent (&xev);
keyboard layout would count as such. It does not. */
if (verbose_p)
fprintf (stderr, "%s: MappingNotify\n", blurb());
+ XRefreshKeyboardMapping (&xev.xmapping);
get_keyboard_layout (ws);
refresh_p = True;
break;
XkbEvent *xkb = (XkbEvent *) &xev;
if (verbose_p)
fprintf (stderr, "%s: XKB event %d\n", blurb(), xkb->any.xkb_type);
+ /* Possibly the only event of interest here is XkbStateNotify = 2. */
get_keyboard_layout (ws);
refresh_p = True;
}
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.
+ Returns True on success. If the user timed out or canceled, we just exit.
*/
Bool
xscreensaver_auth_conv (void *closure,
/* dpms.c --- syncing the X Display Power Management System values
- * xscreensaver, Copyright © 2001-2022 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 2001-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#endif
#include <stdio.h>
+#include <time.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
/* Disable the X11 built-in screen saver. This is not directly related
to DPMS, but it does need to be prevented from fighting with us.
*/
-static void
+static Bool
disable_builtin_saver (Display *dpy)
{
int otimeout = -1;
{
if (verbose_p > 1)
fprintf (stderr, "%s: builtin saver already disabled\n", blurb());
+ return False;
}
else
{
fprintf (stderr, "%s: disabling server's builtin saver\n", blurb());
XSetScreenSaver (dpy, 0, 0, 0, 0);
XForceScreenSaver (dpy, ScreenSaverReset);
+ return True;
}
}
CARD16 o_power = 0;
CARD16 o_standby = 0, o_suspend = 0, o_off = 0;
Bool bogus_p = False;
+ Bool changed_p = False;
+ static int change_count = 0;
Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
Bool dpms_quickoff_p = p->dpms_quickoff_p;
return;
}
- disable_builtin_saver (dpy);
+ changed_p = disable_builtin_saver (dpy);
if (dpms_quickoff_p && !off_secs)
{
warned_p = True;
return;
}
- else if (verbose_p)
- fprintf (stderr, "%s: turned DPMS %s\n", blurb(),
- enabled_p ? "on" : "off");
+ else
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: turned DPMS %s\n", blurb(),
+ enabled_p ? "on" : "off");
+ changed_p = True;
+ }
}
if (bogus_p)
fprintf (stderr, "%s: unable to set DPMS timeouts\n", blurb());
return;
}
- else if (verbose_p)
- fprintf (stderr, "%s: set DPMS timeouts: %d %d %d\n", blurb(),
- standby_secs, suspend_secs, off_secs);
+ else
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: set DPMS timeouts: %d %d %d\n", blurb(),
+ standby_secs, suspend_secs, off_secs);
+ changed_p = True;
+ }
}
else if (verbose_p > 1)
fprintf (stderr, "%s: DPMS timeouts already %d %d %d\n", blurb(),
o_standby, o_suspend, o_off);
+
+ if (changed_p)
+ change_count++;
+
+ if (change_count > 3)
+ {
+ fprintf (stderr, "%s: WARNING: some other program keeps changing"
+ " the DPMS settings. That's bad.\n", blurb());
+ change_count = 0;
+ }
}
Bool
on_p ? "on" : "off");
}
+
+/* Force the server DPMS state to be what the wall clock says it should be.
+ */
+void
+brute_force_dpms (Display *dpy, struct saver_preferences *p,
+ time_t activity_time)
+{
+ XErrorHandler old_handler;
+ int event_number, error_number;
+ BOOL onoff = False;
+ CARD16 state;
+ CARD16 target;
+ time_t age = time ((time_t *) 0) - activity_time;
+
+ if (activity_time == 0) /* Auth dialog is visible */
+ target = DPMSModeOn;
+ else if (p->mode == BLANK_ONLY && p->dpms_quickoff_p)
+ target = DPMSModeOff;
+ else if (p->dpms_off && age >= (p->dpms_off / 1000))
+ target = DPMSModeOff;
+ else if (p->dpms_suspend && age >= (p->dpms_suspend / 1000))
+ target = DPMSModeSuspend;
+ else if (p->dpms_standby && age >= (p->dpms_standby / 1000))
+ target = DPMSModeStandby;
+ else
+ /* We have no opinion about the desired DPMS state, so if it is off,
+ leave it off. It may have been powered down manually with xset. */
+ return;
+
+ if (!DPMSQueryExtension (dpy, &event_number, &error_number) ||
+ !DPMSCapable (dpy))
+ return;
+
+ DPMSInfo (dpy, &state, &onoff);
+ if (!onoff)
+ return;
+
+ if (state == target)
+ return;
+
+ XSync (dpy, False);
+ error_handler_hit_p = False;
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+ XSync (dpy, False);
+
+ DPMSForceLevel (dpy, target);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+
+ if (p->verbose_p > 1 && error_handler_hit_p)
+ {
+ fprintf (stderr, "%s: DPMSForceLevel got an X11 error\n", blurb());
+ return;
+ }
+
+ DPMSInfo (dpy, &state, &onoff);
+ {
+ const char *s = (target == DPMSModeOff ? "Off" :
+ target == DPMSModeSuspend ? "Suspend" :
+ target == DPMSModeStandby ? "Standby" :
+ target == DPMSModeOn ? "On" : "???");
+ if (state != target)
+ fprintf (stderr,
+ "%s: DPMSForceLevel(dpy, %s) did not change monitor power state\n",
+ blurb(), s);
+ else if (p->verbose_p)
+ fprintf (stderr, "%s: set monitor power to %s\n", blurb(), s);
+ }
+}
+
#endif /* HAVE_DPMS_EXTENSION -- whole file */
--- /dev/null
+/* Generated by wayland-scanner 1.23.1 */
+
+#ifndef EXT_IDLE_NOTIFY_V1_CLIENT_PROTOCOL_H
+#define EXT_IDLE_NOTIFY_V1_CLIENT_PROTOCOL_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include "wayland-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page page_ext_idle_notify_v1 The ext_idle_notify_v1 protocol
+ * @section page_ifaces_ext_idle_notify_v1 Interfaces
+ * - @subpage page_iface_ext_idle_notifier_v1 - idle notification manager
+ * - @subpage page_iface_ext_idle_notification_v1 - idle notification
+ * @section page_copyright_ext_idle_notify_v1 Copyright
+ * <pre>
+ *
+ * Copyright © 2015 Martin Gräßlin
+ * Copyright © 2022 Simon Ser
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * </pre>
+ */
+struct ext_idle_notification_v1;
+struct ext_idle_notifier_v1;
+struct wl_seat;
+
+#ifndef EXT_IDLE_NOTIFIER_V1_INTERFACE
+#define EXT_IDLE_NOTIFIER_V1_INTERFACE
+/**
+ * @page page_iface_ext_idle_notifier_v1 ext_idle_notifier_v1
+ * @section page_iface_ext_idle_notifier_v1_desc Description
+ *
+ * This interface allows clients to monitor user idle status.
+ *
+ * After binding to this global, clients can create ext_idle_notification_v1
+ * objects to get notified when the user is idle for a given amount of time.
+ * @section page_iface_ext_idle_notifier_v1_api API
+ * See @ref iface_ext_idle_notifier_v1.
+ */
+/**
+ * @defgroup iface_ext_idle_notifier_v1 The ext_idle_notifier_v1 interface
+ *
+ * This interface allows clients to monitor user idle status.
+ *
+ * After binding to this global, clients can create ext_idle_notification_v1
+ * objects to get notified when the user is idle for a given amount of time.
+ */
+extern const struct wl_interface ext_idle_notifier_v1_interface;
+#endif
+#ifndef EXT_IDLE_NOTIFICATION_V1_INTERFACE
+#define EXT_IDLE_NOTIFICATION_V1_INTERFACE
+/**
+ * @page page_iface_ext_idle_notification_v1 ext_idle_notification_v1
+ * @section page_iface_ext_idle_notification_v1_desc Description
+ *
+ * This interface is used by the compositor to send idle notification events
+ * to clients.
+ *
+ * Initially the notification object is not idle. The notification object
+ * becomes idle when no user activity has happened for at least the timeout
+ * duration, starting from the creation of the notification object. User
+ * activity may include input events or a presence sensor, but is
+ * compositor-specific.
+ *
+ * How this notification responds to idle inhibitors depends on how
+ * it was constructed. If constructed from the
+ * get_idle_notification request, then if an idle inhibitor is
+ * active (e.g. another client has created a zwp_idle_inhibitor_v1
+ * on a visible surface), the compositor must not make the
+ * notification object idle. However, if constructed from the
+ * get_input_idle_notification request, then idle inhibitors are
+ * ignored, and only input from the user, e.g. from a keyboard or
+ * mouse, counts as activity.
+ *
+ * When the notification object becomes idle, an idled event is sent. When
+ * user activity starts again, the notification object stops being idle,
+ * a resumed event is sent and the timeout is restarted.
+ * @section page_iface_ext_idle_notification_v1_api API
+ * See @ref iface_ext_idle_notification_v1.
+ */
+/**
+ * @defgroup iface_ext_idle_notification_v1 The ext_idle_notification_v1 interface
+ *
+ * This interface is used by the compositor to send idle notification events
+ * to clients.
+ *
+ * Initially the notification object is not idle. The notification object
+ * becomes idle when no user activity has happened for at least the timeout
+ * duration, starting from the creation of the notification object. User
+ * activity may include input events or a presence sensor, but is
+ * compositor-specific.
+ *
+ * How this notification responds to idle inhibitors depends on how
+ * it was constructed. If constructed from the
+ * get_idle_notification request, then if an idle inhibitor is
+ * active (e.g. another client has created a zwp_idle_inhibitor_v1
+ * on a visible surface), the compositor must not make the
+ * notification object idle. However, if constructed from the
+ * get_input_idle_notification request, then idle inhibitors are
+ * ignored, and only input from the user, e.g. from a keyboard or
+ * mouse, counts as activity.
+ *
+ * When the notification object becomes idle, an idled event is sent. When
+ * user activity starts again, the notification object stops being idle,
+ * a resumed event is sent and the timeout is restarted.
+ */
+extern const struct wl_interface ext_idle_notification_v1_interface;
+#endif
+
+#define EXT_IDLE_NOTIFIER_V1_DESTROY 0
+#define EXT_IDLE_NOTIFIER_V1_GET_IDLE_NOTIFICATION 1
+#define EXT_IDLE_NOTIFIER_V1_GET_INPUT_IDLE_NOTIFICATION 2
+
+
+/**
+ * @ingroup iface_ext_idle_notifier_v1
+ */
+#define EXT_IDLE_NOTIFIER_V1_DESTROY_SINCE_VERSION 1
+/**
+ * @ingroup iface_ext_idle_notifier_v1
+ */
+#define EXT_IDLE_NOTIFIER_V1_GET_IDLE_NOTIFICATION_SINCE_VERSION 1
+/**
+ * @ingroup iface_ext_idle_notifier_v1
+ */
+#define EXT_IDLE_NOTIFIER_V1_GET_INPUT_IDLE_NOTIFICATION_SINCE_VERSION 2
+
+/** @ingroup iface_ext_idle_notifier_v1 */
+static inline void
+ext_idle_notifier_v1_set_user_data(struct ext_idle_notifier_v1 *ext_idle_notifier_v1, void *user_data)
+{
+ wl_proxy_set_user_data((struct wl_proxy *) ext_idle_notifier_v1, user_data);
+}
+
+/** @ingroup iface_ext_idle_notifier_v1 */
+static inline void *
+ext_idle_notifier_v1_get_user_data(struct ext_idle_notifier_v1 *ext_idle_notifier_v1)
+{
+ return wl_proxy_get_user_data((struct wl_proxy *) ext_idle_notifier_v1);
+}
+
+static inline uint32_t
+ext_idle_notifier_v1_get_version(struct ext_idle_notifier_v1 *ext_idle_notifier_v1)
+{
+ return wl_proxy_get_version((struct wl_proxy *) ext_idle_notifier_v1);
+}
+
+/**
+ * @ingroup iface_ext_idle_notifier_v1
+ *
+ * Destroy the manager object. All objects created via this interface
+ * remain valid.
+ */
+static inline void
+ext_idle_notifier_v1_destroy(struct ext_idle_notifier_v1 *ext_idle_notifier_v1)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) ext_idle_notifier_v1,
+ EXT_IDLE_NOTIFIER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_idle_notifier_v1), WL_MARSHAL_FLAG_DESTROY);
+}
+
+/**
+ * @ingroup iface_ext_idle_notifier_v1
+ *
+ * Create a new idle notification object.
+ *
+ * The notification object has a minimum timeout duration and is tied to a
+ * seat. The client will be notified if the seat is inactive for at least
+ * the provided timeout. See ext_idle_notification_v1 for more details.
+ *
+ * A zero timeout is valid and means the client wants to be notified as
+ * soon as possible when the seat is inactive.
+ */
+static inline struct ext_idle_notification_v1 *
+ext_idle_notifier_v1_get_idle_notification(struct ext_idle_notifier_v1 *ext_idle_notifier_v1, uint32_t timeout, struct wl_seat *seat)
+{
+ struct wl_proxy *id;
+
+ id = wl_proxy_marshal_flags((struct wl_proxy *) ext_idle_notifier_v1,
+ EXT_IDLE_NOTIFIER_V1_GET_IDLE_NOTIFICATION, &ext_idle_notification_v1_interface, wl_proxy_get_version((struct wl_proxy *) ext_idle_notifier_v1), 0, NULL, timeout, seat);
+
+ return (struct ext_idle_notification_v1 *) id;
+}
+
+/**
+ * @ingroup iface_ext_idle_notifier_v1
+ *
+ * Create a new idle notification object to track input from the
+ * user, such as keyboard and mouse movement. Because this object is
+ * meant to track user input alone, it ignores idle inhibitors.
+ *
+ * The notification object has a minimum timeout duration and is tied to a
+ * seat. The client will be notified if the seat is inactive for at least
+ * the provided timeout. See ext_idle_notification_v1 for more details.
+ *
+ * A zero timeout is valid and means the client wants to be notified as
+ * soon as possible when the seat is inactive.
+ */
+static inline struct ext_idle_notification_v1 *
+ext_idle_notifier_v1_get_input_idle_notification(struct ext_idle_notifier_v1 *ext_idle_notifier_v1, uint32_t timeout, struct wl_seat *seat)
+{
+ struct wl_proxy *id;
+
+ id = wl_proxy_marshal_flags((struct wl_proxy *) ext_idle_notifier_v1,
+ EXT_IDLE_NOTIFIER_V1_GET_INPUT_IDLE_NOTIFICATION, &ext_idle_notification_v1_interface, wl_proxy_get_version((struct wl_proxy *) ext_idle_notifier_v1), 0, NULL, timeout, seat);
+
+ return (struct ext_idle_notification_v1 *) id;
+}
+
+/**
+ * @ingroup iface_ext_idle_notification_v1
+ * @struct ext_idle_notification_v1_listener
+ */
+struct ext_idle_notification_v1_listener {
+ /**
+ * notification object is idle
+ *
+ * This event is sent when the notification object becomes idle.
+ *
+ * It's a compositor protocol error to send this event twice
+ * without a resumed event in-between.
+ */
+ void (*idled)(void *data,
+ struct ext_idle_notification_v1 *ext_idle_notification_v1);
+ /**
+ * notification object is no longer idle
+ *
+ * This event is sent when the notification object stops being
+ * idle.
+ *
+ * It's a compositor protocol error to send this event twice
+ * without an idled event in-between. It's a compositor protocol
+ * error to send this event prior to any idled event.
+ */
+ void (*resumed)(void *data,
+ struct ext_idle_notification_v1 *ext_idle_notification_v1);
+};
+
+/**
+ * @ingroup iface_ext_idle_notification_v1
+ */
+static inline int
+ext_idle_notification_v1_add_listener(struct ext_idle_notification_v1 *ext_idle_notification_v1,
+ const struct ext_idle_notification_v1_listener *listener, void *data)
+{
+ return wl_proxy_add_listener((struct wl_proxy *) ext_idle_notification_v1,
+ (void (**)(void)) listener, data);
+}
+
+#define EXT_IDLE_NOTIFICATION_V1_DESTROY 0
+
+/**
+ * @ingroup iface_ext_idle_notification_v1
+ */
+#define EXT_IDLE_NOTIFICATION_V1_IDLED_SINCE_VERSION 1
+/**
+ * @ingroup iface_ext_idle_notification_v1
+ */
+#define EXT_IDLE_NOTIFICATION_V1_RESUMED_SINCE_VERSION 1
+
+/**
+ * @ingroup iface_ext_idle_notification_v1
+ */
+#define EXT_IDLE_NOTIFICATION_V1_DESTROY_SINCE_VERSION 1
+
+/** @ingroup iface_ext_idle_notification_v1 */
+static inline void
+ext_idle_notification_v1_set_user_data(struct ext_idle_notification_v1 *ext_idle_notification_v1, void *user_data)
+{
+ wl_proxy_set_user_data((struct wl_proxy *) ext_idle_notification_v1, user_data);
+}
+
+/** @ingroup iface_ext_idle_notification_v1 */
+static inline void *
+ext_idle_notification_v1_get_user_data(struct ext_idle_notification_v1 *ext_idle_notification_v1)
+{
+ return wl_proxy_get_user_data((struct wl_proxy *) ext_idle_notification_v1);
+}
+
+static inline uint32_t
+ext_idle_notification_v1_get_version(struct ext_idle_notification_v1 *ext_idle_notification_v1)
+{
+ return wl_proxy_get_version((struct wl_proxy *) ext_idle_notification_v1);
+}
+
+/**
+ * @ingroup iface_ext_idle_notification_v1
+ *
+ * Destroy the notification object.
+ */
+static inline void
+ext_idle_notification_v1_destroy(struct ext_idle_notification_v1 *ext_idle_notification_v1)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) ext_idle_notification_v1,
+ EXT_IDLE_NOTIFICATION_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_idle_notification_v1), WL_MARSHAL_FLAG_DESTROY);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/* Generated by wayland-scanner 1.23.1 */
+
+/*
+ * Copyright © 2015 Martin Gräßlin
+ * Copyright © 2022 Simon Ser
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "wayland-util.h"
+
+#ifndef __has_attribute
+# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
+#endif
+
+#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
+#define WL_PRIVATE __attribute__ ((visibility("hidden")))
+#else
+#define WL_PRIVATE
+#endif
+
+extern const struct wl_interface ext_idle_notification_v1_interface;
+extern const struct wl_interface wl_seat_interface;
+
+static const struct wl_interface *ext_idle_notify_v1_types[] = {
+ &ext_idle_notification_v1_interface,
+ NULL,
+ &wl_seat_interface,
+ &ext_idle_notification_v1_interface,
+ NULL,
+ &wl_seat_interface,
+};
+
+static const struct wl_message ext_idle_notifier_v1_requests[] = {
+ { "destroy", "", ext_idle_notify_v1_types + 0 },
+ { "get_idle_notification", "nuo", ext_idle_notify_v1_types + 0 },
+ { "get_input_idle_notification", "2nuo", ext_idle_notify_v1_types + 3 },
+};
+
+WL_PRIVATE const struct wl_interface ext_idle_notifier_v1_interface = {
+ "ext_idle_notifier_v1", 2,
+ 3, ext_idle_notifier_v1_requests,
+ 0, NULL,
+};
+
+static const struct wl_message ext_idle_notification_v1_requests[] = {
+ { "destroy", "", ext_idle_notify_v1_types + 0 },
+};
+
+static const struct wl_message ext_idle_notification_v1_events[] = {
+ { "idled", "", ext_idle_notify_v1_types + 0 },
+ { "resumed", "", ext_idle_notify_v1_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface ext_idle_notification_v1_interface = {
+ "ext_idle_notification_v1", 2,
+ 1, ext_idle_notification_v1_requests,
+ 2, ext_idle_notification_v1_events,
+};
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="ext_idle_notify_v1">
+ <copyright>
+ Copyright © 2015 Martin Gräßlin
+ Copyright © 2022 Simon Ser
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="ext_idle_notifier_v1" version="2">
+ <description summary="idle notification manager">
+ This interface allows clients to monitor user idle status.
+
+ After binding to this global, clients can create ext_idle_notification_v1
+ objects to get notified when the user is idle for a given amount of time.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the manager">
+ Destroy the manager object. All objects created via this interface
+ remain valid.
+ </description>
+ </request>
+
+ <request name="get_idle_notification">
+ <description summary="create a notification object">
+ Create a new idle notification object.
+
+ The notification object has a minimum timeout duration and is tied to a
+ seat. The client will be notified if the seat is inactive for at least
+ the provided timeout. See ext_idle_notification_v1 for more details.
+
+ A zero timeout is valid and means the client wants to be notified as
+ soon as possible when the seat is inactive.
+ </description>
+ <arg name="id" type="new_id" interface="ext_idle_notification_v1"/>
+ <arg name="timeout" type="uint" summary="minimum idle timeout in msec"/>
+ <arg name="seat" type="object" interface="wl_seat"/>
+ </request>
+
+ <!-- Version 2 additions -->
+
+ <request name="get_input_idle_notification" since="2">
+ <description summary="create a notification object">
+ Create a new idle notification object to track input from the
+ user, such as keyboard and mouse movement. Because this object is
+ meant to track user input alone, it ignores idle inhibitors.
+
+ The notification object has a minimum timeout duration and is tied to a
+ seat. The client will be notified if the seat is inactive for at least
+ the provided timeout. See ext_idle_notification_v1 for more details.
+
+ A zero timeout is valid and means the client wants to be notified as
+ soon as possible when the seat is inactive.
+ </description>
+ <arg name="id" type="new_id" interface="ext_idle_notification_v1"/>
+ <arg name="timeout" type="uint" summary="minimum idle timeout in msec"/>
+ <arg name="seat" type="object" interface="wl_seat"/>
+ </request>
+
+ </interface>
+
+ <interface name="ext_idle_notification_v1" version="2">
+ <description summary="idle notification">
+ This interface is used by the compositor to send idle notification events
+ to clients.
+
+ Initially the notification object is not idle. The notification object
+ becomes idle when no user activity has happened for at least the timeout
+ duration, starting from the creation of the notification object. User
+ activity may include input events or a presence sensor, but is
+ compositor-specific.
+
+ How this notification responds to idle inhibitors depends on how
+ it was constructed. If constructed from the
+ get_idle_notification request, then if an idle inhibitor is
+ active (e.g. another client has created a zwp_idle_inhibitor_v1
+ on a visible surface), the compositor must not make the
+ notification object idle. However, if constructed from the
+ get_input_idle_notification request, then idle inhibitors are
+ ignored, and only input from the user, e.g. from a keyboard or
+ mouse, counts as activity.
+
+ When the notification object becomes idle, an idled event is sent. When
+ user activity starts again, the notification object stops being idle,
+ a resumed event is sent and the timeout is restarted.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the notification object">
+ Destroy the notification object.
+ </description>
+ </request>
+
+ <event name="idled">
+ <description summary="notification object is idle">
+ This event is sent when the notification object becomes idle.
+
+ It's a compositor protocol error to send this event twice without a
+ resumed event in-between.
+ </description>
+ </event>
+
+ <event name="resumed">
+ <description summary="notification object is no longer idle">
+ This event is sent when the notification object stops being idle.
+
+ It's a compositor protocol error to send this event twice without an
+ idled event in-between. It's a compositor protocol error to send this
+ event prior to any idled event.
+ </description>
+ </event>
+ </interface>
+</protocol>
-/* xscreensaver, Copyright © 1991-2022 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
}, { "Apple-DRI", "Apple-DRI", True, 0
}, { "Apple-WM", "Apple-WM", True, 0
}, { "XInputExtension", "XInput", True, 0
+ }, { "XWAYLAND", "XWayland", True, 0
},
};
+ static const char * const envs[] = {
+ "GDMSESSION",
+ "RUNNING_UNDER_GDM",
+ "WAYLAND_DISPLAY",
+ "WAYLAND_SOCKET",
+ "XDG_CURRENT_DESKTOP",
+ "XDG_SESSION_DESKTOP",
+ "XDG_SESSION_TYPE",
+ };
fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
DisplayString(si->dpy));
fprintf (stderr, "%s\n", buf);
}
+# if !defined(HAVE_GL)
+ fprintf (stderr, "%s: OpenGL disabled at compile time\n", blurb());
+# elif defined(HAVE_EGL)
+ fprintf (stderr, "%s: Using EGL\n", blurb());
+# else
+ fprintf (stderr, "%s: Using GLX\n", blurb());
+# endif
+
# ifdef HAVE_LIBSYSTEMD
fprintf (stderr, "%s: libsystemd\n", blurb());
# endif
fprintf (stderr, "%s: libelogind\n", blurb());
# endif
# if !defined(HAVE_LIBSYSTEMD) && !defined(HAVE_LIBELOGIND)
- fprintf (stderr, "%s: libsystemd/libelogind (disabled at compile time)\n",
+ fprintf (stderr, "%s: libsystemd/libelogind disabled at compile time\n",
blurb());
# endif
+ for (i = 0; i < countof(envs); i++)
+ {
+ const char *key = envs[i];
+ char *val = getenv (key);
+ if (!val) continue;
+ fprintf (stderr, "%s: %s = \"%s\"\n", blurb(), key, val);
+ }
+
for (i = 0; i < si->nscreens; i++)
{
saver_screen_info *ssi = &si->screens[i];
-/* xscreensaver, Copyright © 1992-2022 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1992-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
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.
+ new current reality. And under Wayland, it has to get that screenshot
+ by running "xscreensaver-getimage" which in turn runs "grim", if it is
+ installed, which it might not be.
In summary, everything is terrible because X11 doesn't support alpha.
+ Another thing to consider is to implement fading with OpenGL: put the
+ screen shot in a textured quad and just change its color for fading.
+ That should be efficient on both X11 and Wayland. However it would
+ require linking the OpenGL libraries into "xscreensaver-gfx".
+
+
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
+ Screen saver activates:
+
+ - Desktop is visible
+ - Save screenshot for later
+ - Fade out:
+ - Map invisible temp windows
+ - Fade from desktop to black
+ - Erase saver windows to black and raise them
+ - Destroy temp windows
+
+ Screen saver deactivates:
+
+ - Saver graphics are visible
+ - Fade out:
+ - Get a screenshot of the current screenhack
+ - Map invisible temp windows
+ - Fade from screenhack screenshot to black
+ - Fade in:
+ - Fade from black to saved screenshot of desktop
+ - Unmap obscured saver windows
+ - Destroy temp windows
*/
#ifdef HAVE_CONFIG_H
#include <string.h>
#include <sys/time.h>
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h> /* for waitpid() and associated macros */
+#endif
+
#ifdef HAVE_JWXYZ
# include "jwxyz.h"
#else /* real X11 */
#include <X11/extensions/XInput2.h>
#include "xinput.h"
+#if defined(__APPLE__) && !defined(HAVE_COCOA)
+# define HAVE_MACOS_X11
+#elif !defined(HAVE_JWXYZ) /* Real X11, possibly Wayland */
+# define HAVE_REAL_X11
+#endif
+
typedef struct {
int nscreens;
static int xshm_whack (Display *, XShmSegmentInfo *,
xshm_fade_info *, float ratio);
+
+/* Grab a screenshot and return it.
+ It will be the size and extent of the given window.
+ Or None if we failed.
+ Somewhat duplicated in screenshot.c:screenshot_grab().
+ */
+static Pixmap
+xshm_screenshot_grab (Display *dpy, Window window,
+ Bool from_desktop_p, Bool verbose_p)
+{
+ XWindowAttributes xgwa;
+ Pixmap pixmap = 0;
+ Bool external_p = False;
+
+# ifdef HAVE_MACOS_X11
+ external_p = True;
+# else /* X11, possibly Wayland */
+ external_p = getenv ("WAYLAND_DISPLAY") || getenv ("WAYLAND_SOCKET");
+# endif
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ pixmap = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
+ if (!pixmap) return None;
+
+ XSync (dpy, False); /* So that the pixmap exists before we exec. */
+
+ /* Run xscreensaver-getimage to get a screenshot. It will, in turn,
+ run "screencapture" or "grim" to write the screenshot to a PNG file,
+ then will load that file onto our Pixmap.
+ */
+ if (external_p)
+ {
+ pid_t forked;
+ char *av[20];
+ int ac = 0;
+ char buf[1024];
+ char rootstr[20], pixstr[20];
+
+ sprintf (rootstr, "0x%0lx", (unsigned long) window);
+ sprintf (pixstr, "0x%0lx", (unsigned long) pixmap);
+ av[ac++] = "xscreensaver-getimage";
+ if (verbose_p)
+ av[ac++] = "--verbose";
+ av[ac++] = "--desktop";
+ if (!from_desktop_p)
+ av[ac++] = "--no-cache";
+ av[ac++] = "--no-images";
+ av[ac++] = "--no-video";
+ av[ac++] = rootstr;
+ av[ac++] = pixstr;
+ av[ac] = 0;
+
+ if (verbose_p)
+ {
+ int i;
+ fprintf (stderr, "%s: fade: executing:", blurb());
+ for (i = 0; i < ac; i++)
+ fprintf (stderr, " %s", av[i]);
+ fprintf (stderr, "\n");
+ }
+
+ switch ((int) (forked = fork ())) {
+ case -1:
+ {
+ sprintf (buf, "%s: couldn't fork", blurb());
+ perror (buf);
+ return 0;
+ }
+ case 0:
+ {
+ close (ConnectionNumber (dpy)); /* close display fd */
+ execvp (av[0], av); /* shouldn't return. */
+ exit (-1); /* exits fork */
+ break;
+ }
+ default:
+ {
+ int wait_status = 0, exit_status = 0;
+ /* Wait for the child to die. */
+ waitpid (forked, &wait_status, 0);
+ exit_status = WEXITSTATUS (wait_status);
+ /* Treat exit code as a signed 8-bit quantity. */
+ if (exit_status & 0x80) exit_status |= ~0xFF;
+ if (exit_status != 0)
+ {
+ fprintf (stderr, "%s: fade: %s exited with %d\n",
+ blurb(), av[0], exit_status);
+ if (pixmap) XFreePixmap (dpy, pixmap);
+ return None;
+ }
+ }
+ }
+ }
+# ifndef HAVE_MACOS_X11
+ else
+ {
+ /* Grab a screenshot using XCopyArea. */
+
+ XGCValues gcv;
+ GC gc;
+ gcv.function = GXcopy;
+ gcv.subwindow_mode = IncludeInferiors;
+ gc = XCreateGC (dpy, pixmap, GCFunction | GCSubwindowMode, &gcv);
+ XCopyArea (dpy, xgwa.root, pixmap, gc,
+ xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
+ XFreeGC (dpy, gc);
+ }
+# endif /* HAVE_MACOS_X11 */
+
+ return pixmap;
+}
+
+
/* Returns:
0: faded normally
1: canceled by user activity
XWindowAttributes xgwa;
Window root;
XGCValues gcv;
- GC gc;
unsigned long attrmask = 0;
XSetWindowAttributes attrs;
}
else
{
- /* Create a pixmap and grab a screenshot into it. */
info[screen].screenshot =
- XCreatePixmap (dpy, root, xgwa.width, xgwa.height, xgwa.depth);
+ xshm_screenshot_grab (dpy, saver_windows[screen], from_desktop_p,
+ verbose_p);
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. */
/* Now that we have mapped the screenshot on the fader windows,
take the saver windows off the screen. */
+# if 0
+ /* Under macOS X11 and Wayland, this causes a single frame of flicker.
+ I don't see how that is possible, since we just did XMapRaised,
+ above. */
if (out_p)
{
XUnmapWindow (dpy, saver_windows[screen]);
XClearWindow (dpy, saver_windows[screen]);
}
+# endif
}
/* Run the animation at the maximum frame rate in the time allotted. */
--- /dev/null
+/* Generated by wayland-scanner 1.23.1 */
+
+#ifndef IDLE_CLIENT_PROTOCOL_H
+#define IDLE_CLIENT_PROTOCOL_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include "wayland-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page page_idle The idle protocol
+ * @section page_ifaces_idle Interfaces
+ * - @subpage page_iface_org_kde_kwin_idle - User idle time manager
+ * - @subpage page_iface_org_kde_kwin_idle_timeout -
+ * @section page_copyright_idle Copyright
+ * <pre>
+ *
+ * Copyright (C) 2015 Martin Gräßlin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * </pre>
+ */
+struct org_kde_kwin_idle;
+struct org_kde_kwin_idle_timeout;
+struct wl_seat;
+
+#ifndef ORG_KDE_KWIN_IDLE_INTERFACE
+#define ORG_KDE_KWIN_IDLE_INTERFACE
+/**
+ * @page page_iface_org_kde_kwin_idle org_kde_kwin_idle
+ * @section page_iface_org_kde_kwin_idle_desc Description
+ *
+ * This interface allows to monitor user idle time on a given seat. The interface
+ * allows to register timers which trigger after no user activity was registered
+ * on the seat for a given interval. It notifies when user activity resumes.
+ *
+ * This is useful for applications wanting to perform actions when the user is not
+ * interacting with the system, e.g. chat applications setting the user as away, power
+ * management features to dim screen, etc..
+ * @section page_iface_org_kde_kwin_idle_api API
+ * See @ref iface_org_kde_kwin_idle.
+ */
+/**
+ * @defgroup iface_org_kde_kwin_idle The org_kde_kwin_idle interface
+ *
+ * This interface allows to monitor user idle time on a given seat. The interface
+ * allows to register timers which trigger after no user activity was registered
+ * on the seat for a given interval. It notifies when user activity resumes.
+ *
+ * This is useful for applications wanting to perform actions when the user is not
+ * interacting with the system, e.g. chat applications setting the user as away, power
+ * management features to dim screen, etc..
+ */
+extern const struct wl_interface org_kde_kwin_idle_interface;
+#endif
+#ifndef ORG_KDE_KWIN_IDLE_TIMEOUT_INTERFACE
+#define ORG_KDE_KWIN_IDLE_TIMEOUT_INTERFACE
+/**
+ * @page page_iface_org_kde_kwin_idle_timeout org_kde_kwin_idle_timeout
+ * @section page_iface_org_kde_kwin_idle_timeout_api API
+ * See @ref iface_org_kde_kwin_idle_timeout.
+ */
+/**
+ * @defgroup iface_org_kde_kwin_idle_timeout The org_kde_kwin_idle_timeout interface
+ */
+extern const struct wl_interface org_kde_kwin_idle_timeout_interface;
+#endif
+
+#define ORG_KDE_KWIN_IDLE_GET_IDLE_TIMEOUT 0
+
+
+/**
+ * @ingroup iface_org_kde_kwin_idle
+ */
+#define ORG_KDE_KWIN_IDLE_GET_IDLE_TIMEOUT_SINCE_VERSION 1
+
+/** @ingroup iface_org_kde_kwin_idle */
+static inline void
+org_kde_kwin_idle_set_user_data(struct org_kde_kwin_idle *org_kde_kwin_idle, void *user_data)
+{
+ wl_proxy_set_user_data((struct wl_proxy *) org_kde_kwin_idle, user_data);
+}
+
+/** @ingroup iface_org_kde_kwin_idle */
+static inline void *
+org_kde_kwin_idle_get_user_data(struct org_kde_kwin_idle *org_kde_kwin_idle)
+{
+ return wl_proxy_get_user_data((struct wl_proxy *) org_kde_kwin_idle);
+}
+
+static inline uint32_t
+org_kde_kwin_idle_get_version(struct org_kde_kwin_idle *org_kde_kwin_idle)
+{
+ return wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_idle);
+}
+
+/** @ingroup iface_org_kde_kwin_idle */
+static inline void
+org_kde_kwin_idle_destroy(struct org_kde_kwin_idle *org_kde_kwin_idle)
+{
+ wl_proxy_destroy((struct wl_proxy *) org_kde_kwin_idle);
+}
+
+/**
+ * @ingroup iface_org_kde_kwin_idle
+ */
+static inline struct org_kde_kwin_idle_timeout *
+org_kde_kwin_idle_get_idle_timeout(struct org_kde_kwin_idle *org_kde_kwin_idle, struct wl_seat *seat, uint32_t timeout)
+{
+ struct wl_proxy *id;
+
+ id = wl_proxy_marshal_flags((struct wl_proxy *) org_kde_kwin_idle,
+ ORG_KDE_KWIN_IDLE_GET_IDLE_TIMEOUT, &org_kde_kwin_idle_timeout_interface, wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_idle), 0, NULL, seat, timeout);
+
+ return (struct org_kde_kwin_idle_timeout *) id;
+}
+
+/**
+ * @ingroup iface_org_kde_kwin_idle_timeout
+ * @struct org_kde_kwin_idle_timeout_listener
+ */
+struct org_kde_kwin_idle_timeout_listener {
+ /**
+ * Triggered when there has not been any user activity in the requested idle time interval
+ *
+ *
+ */
+ void (*idle)(void *data,
+ struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout);
+ /**
+ * Triggered on the first user activity after an idle event
+ *
+ *
+ */
+ void (*resumed)(void *data,
+ struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout);
+};
+
+/**
+ * @ingroup iface_org_kde_kwin_idle_timeout
+ */
+static inline int
+org_kde_kwin_idle_timeout_add_listener(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout,
+ const struct org_kde_kwin_idle_timeout_listener *listener, void *data)
+{
+ return wl_proxy_add_listener((struct wl_proxy *) org_kde_kwin_idle_timeout,
+ (void (**)(void)) listener, data);
+}
+
+#define ORG_KDE_KWIN_IDLE_TIMEOUT_RELEASE 0
+#define ORG_KDE_KWIN_IDLE_TIMEOUT_SIMULATE_USER_ACTIVITY 1
+
+/**
+ * @ingroup iface_org_kde_kwin_idle_timeout
+ */
+#define ORG_KDE_KWIN_IDLE_TIMEOUT_IDLE_SINCE_VERSION 1
+/**
+ * @ingroup iface_org_kde_kwin_idle_timeout
+ */
+#define ORG_KDE_KWIN_IDLE_TIMEOUT_RESUMED_SINCE_VERSION 1
+
+/**
+ * @ingroup iface_org_kde_kwin_idle_timeout
+ */
+#define ORG_KDE_KWIN_IDLE_TIMEOUT_RELEASE_SINCE_VERSION 1
+/**
+ * @ingroup iface_org_kde_kwin_idle_timeout
+ */
+#define ORG_KDE_KWIN_IDLE_TIMEOUT_SIMULATE_USER_ACTIVITY_SINCE_VERSION 1
+
+/** @ingroup iface_org_kde_kwin_idle_timeout */
+static inline void
+org_kde_kwin_idle_timeout_set_user_data(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout, void *user_data)
+{
+ wl_proxy_set_user_data((struct wl_proxy *) org_kde_kwin_idle_timeout, user_data);
+}
+
+/** @ingroup iface_org_kde_kwin_idle_timeout */
+static inline void *
+org_kde_kwin_idle_timeout_get_user_data(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout)
+{
+ return wl_proxy_get_user_data((struct wl_proxy *) org_kde_kwin_idle_timeout);
+}
+
+static inline uint32_t
+org_kde_kwin_idle_timeout_get_version(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout)
+{
+ return wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_idle_timeout);
+}
+
+/** @ingroup iface_org_kde_kwin_idle_timeout */
+static inline void
+org_kde_kwin_idle_timeout_destroy(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout)
+{
+ wl_proxy_destroy((struct wl_proxy *) org_kde_kwin_idle_timeout);
+}
+
+/**
+ * @ingroup iface_org_kde_kwin_idle_timeout
+ */
+static inline void
+org_kde_kwin_idle_timeout_release(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) org_kde_kwin_idle_timeout,
+ ORG_KDE_KWIN_IDLE_TIMEOUT_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_idle_timeout), WL_MARSHAL_FLAG_DESTROY);
+}
+
+/**
+ * @ingroup iface_org_kde_kwin_idle_timeout
+ */
+static inline void
+org_kde_kwin_idle_timeout_simulate_user_activity(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) org_kde_kwin_idle_timeout,
+ ORG_KDE_KWIN_IDLE_TIMEOUT_SIMULATE_USER_ACTIVITY, NULL, wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_idle_timeout), 0);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/* Generated by wayland-scanner 1.23.1 */
+
+/*
+ * Copyright (C) 2015 Martin Gräßlin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "wayland-util.h"
+
+#ifndef __has_attribute
+# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
+#endif
+
+#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
+#define WL_PRIVATE __attribute__ ((visibility("hidden")))
+#else
+#define WL_PRIVATE
+#endif
+
+extern const struct wl_interface org_kde_kwin_idle_timeout_interface;
+extern const struct wl_interface wl_seat_interface;
+
+static const struct wl_interface *idle_types[] = {
+ &org_kde_kwin_idle_timeout_interface,
+ &wl_seat_interface,
+ NULL,
+};
+
+static const struct wl_message org_kde_kwin_idle_requests[] = {
+ { "get_idle_timeout", "nou", idle_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface org_kde_kwin_idle_interface = {
+ "org_kde_kwin_idle", 1,
+ 1, org_kde_kwin_idle_requests,
+ 0, NULL,
+};
+
+static const struct wl_message org_kde_kwin_idle_timeout_requests[] = {
+ { "release", "", idle_types + 0 },
+ { "simulate_user_activity", "", idle_types + 0 },
+};
+
+static const struct wl_message org_kde_kwin_idle_timeout_events[] = {
+ { "idle", "", idle_types + 0 },
+ { "resumed", "", idle_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface org_kde_kwin_idle_timeout_interface = {
+ "org_kde_kwin_idle_timeout", 1,
+ 2, org_kde_kwin_idle_timeout_requests,
+ 2, org_kde_kwin_idle_timeout_events,
+};
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="idle">
+ <copyright><![CDATA[
+ Copyright (C) 2015 Martin Gräßlin
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ ]]></copyright>
+ <interface name="org_kde_kwin_idle" version="1">
+ <description summary="User idle time manager">
+ This interface allows to monitor user idle time on a given seat. The interface
+ allows to register timers which trigger after no user activity was registered
+ on the seat for a given interval. It notifies when user activity resumes.
+
+ This is useful for applications wanting to perform actions when the user is not
+ interacting with the system, e.g. chat applications setting the user as away, power
+ management features to dim screen, etc..
+ </description>
+ <request name="get_idle_timeout">
+ <arg name="id" type="new_id" interface="org_kde_kwin_idle_timeout"/>
+ <arg name="seat" type="object" interface="wl_seat"/>
+ <arg name="timeout" type="uint" summary="The idle timeout in msec"/>
+ </request>
+ </interface>
+ <interface name="org_kde_kwin_idle_timeout" version="1">
+ <request name="release" type="destructor">
+ <description summary="release the timeout object"/>
+ </request>
+ <request name="simulate_user_activity">
+ <description summary="Simulates user activity for this timeout, behaves just like real user activity on the seat"/>
+ </request>
+ <event name="idle">
+ <description summary="Triggered when there has not been any user activity in the requested idle time interval"/>
+ </event>
+ <event name="resumed">
+ <description summary="Triggered on the first user activity after an idle event"/>
+ </event>
+ </interface>
+</protocol>
/* prefs.c --- reading and writing the ~/.xscreensaver file.
- * xscreensaver, Copyright © 1998-2023 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 1998-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
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;
-/* xscreensaver-command, Copyright © 1991-2024 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver-command, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
unsigned long nitems, bytesafter;
unsigned char *dataP = 0;
+ /* XA_SCREENSAVER_STATUS format documented in windows.c. */
if (XGetWindowProperty (dpy,
- RootWindow (dpy, 0),
+ RootWindow (dpy, 0), /* always screen #0 */
XA_SCREENSAVER_STATUS,
0, 999, False, XA_INTEGER,
&type, &format, &nitems, &bytesafter,
&dataP)
== Success
- && type
- && dataP)
- {
- Atom blanked;
- time_t tt;
+ && type == XA_INTEGER
+ && nitems >= 3
+ && dataP)
+ {
+ PROP32 *data = (PROP32 *) dataP;
+ Atom state = (Atom) data[0];
+ time_t tt = (time_t) /* 64 bit time_t */
+ ((((unsigned long) data[1] & 0xFFFFFFFFL) << 32) |
+ ((unsigned long) data[2] & 0xFFFFFFFFL));
char *s;
- Atom *data = (Atom *) dataP;
-
- if (type != XA_INTEGER || nitems < 3)
- {
- STATUS_LOSE:
- if (data) free (data);
- fprintf (stdout, "\n");
- fflush (stdout);
- fprintf (stderr, "bad status format on root window\n");
- status = -1;
- goto DONE;
- }
-
- blanked = (Atom) data[0];
- tt = (time_t) data[1];
-
- if (tt <= (time_t) 666000000L) /* early 1991 */
- goto STATUS_LOSE;
- if (blanked == XA_BLANK)
+ if (state == XA_BLANK)
fputs (": screen blanked since ", stdout);
- else if (blanked == XA_LOCK)
+ else if (state == XA_LOCK || state == XA_AUTH)
fputs (": screen locked since ", stdout);
- else if (blanked == 0)
- /* suggestions for a better way to phrase this are welcome. */
+ else if (state == 0)
fputs (": screen non-blanked since ", stdout);
else
- /* `blanked' has an unknown value - fail. */
goto STATUS_LOSE;
s = ctime(&tt);
fputs (s, stdout);
{
- int nhacks = nitems - 2;
+ int off = 3;
+ int nhacks = nitems - off;
Bool any = False;
int i;
for (i = 0; i < nhacks; i++)
}
if (any && nhacks == 1)
- fprintf (stdout, " (hack #%d)\n", (int) data[2]);
+ fprintf (stdout, " (hack #%d)\n", (int) data[off]);
else if (any)
{
fprintf (stdout, " (hacks: ");
for (i = 0; i < nhacks; i++)
{
- fprintf (stdout, "#%d", (int) data[2 + i]);
+ fprintf (stdout, "#%d", (int) data[off + i]);
if (i != nhacks-1)
fputs (", ", stdout);
}
fputs ("\n", stdout);
}
- if (data) free (data);
+ if (dataP) free (dataP);
}
else
{
- if (dataP) XFree (dataP);
fprintf (stdout, "\n");
+ STATUS_LOSE:
fflush (stdout);
- fprintf (stderr, "no saver status on root window\n");
+
+ if (dataP)
+ fprintf (stderr, "bad status format on root window\n");
+ else
+ fprintf (stderr, "no saver status on root window\n");
+
+ if (dataP) XFree (dataP);
status = -1;
goto DONE;
}
abort();
else if (arg != 0 && command == XA_DEMO)
{
- arg1 = 5000; /* version number of the XA_DEMO protocol, */
- arg2 = arg; /* since it didn't use to take an argument. */
+ /* 1.09, 1992: DEMO took no arguments
+ 2.35, 1998: [ '300', hack-number ] 1 indexed, 0 means random
+ 5.00, 2006: [ '5000', hack-number ] same */
+ arg1 = 5000;
+ arg2 = arg;
}
if (command == XA_DEACTIVATE)
time_t now;
struct timeval tv;
- /* Wait until the status property on the root window changes to
- BLANK or LOCKED. */
+ /* Wait until the status property changes to BLANK or LOCKED. */
+ /* XA_SCREENSAVER_STATUS format documented in windows.c. */
if (XGetWindowProperty (dpy, w,
XA_SCREENSAVER_STATUS,
0, 999, False, XA_INTEGER,
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] == XA_BLANK ? "BLANK" :
+ status[0] == XA_LOCK ? "LOCK" :
+ status[0] == XA_AUTH ? "AUTH" :
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 (state != 0)
{
if (verbose_p > 1)
fprintf (stderr, "%s: screen blanked\n", progname);
/* subprocs.c --- choosing, spawning, and killing screenhacks.
- * xscreensaver, Copyright © 1991-2024 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
them. In that case, multiple hacks have the same $DISPLAY, screen and
root window.
*/
+ char *nssw = (char *) malloc (40);
+
+# if !(defined(__APPLE__) && !defined(HAVE_COCOA)) /* not macOS X11 */
+ /* XQuartz's weird $DISPLAY pathnames do not match DisplayString() */
+
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=");
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);
-
if (putenv (ndpy))
abort ();
+# endif /* not macOS X11 */
+
+ sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX", (unsigned long) saver_window);
if (putenv (nssw))
abort ();
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));
- }
+ fprintf (stderr, "%s: %d: next cycle in %d:%02d:%02d at %s\n",
+ blurb(), ssi->number,
+ (int) (how_long / 1000) / (60 * 60),
+ (int) ((how_long / 1000) % (60 * 60)) / 60,
+ (int) (how_long / 1000) % 60,
+ timestring (ssi->cycle_at));
}
}
And some X servers apparently ignore XClearWindow if the monitor is
powered off, meaning when the monitor powers back on, the stale bits
that we just tried to erase are still in the frame buffer.
+
+ Do not clear the window when exiting, as that would prevent the
+ unfade from fading the now-dead hack to black, before fading from
+ black to desktop.
*/
- XClearWindow (si->dpy, ssi->screensaver_window);
+ if (! si->terminating_p)
+ XClearWindow (si->dpy, ssi->screensaver_window);
}
/* test-fade.c --- playing with colormap and/or gamma fading.
- * xscreensaver, Copyright © 2001-2021 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 2001-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#include "xscreensaver.h"
#include "resources.h"
#include "screens.h"
+#include "screenshot.h"
#include "fade.h"
#include "atoms.h"
Window root = RootWindow (dpy, 0);
Visual *visual = DefaultVisual (dpy, 0);
Pixmap logo, logo_clipmask;
+ Pixmap screenshot;
int logo_npixels;
unsigned long *logo_pixels;
unsigned int logo_width, logo_height;
init_xscreensaver_atoms (dpy);
+ fprintf (stderr, "\n%s:\n"
+ "\n"
+ "\tYou should see the following, with no flickering:\n"
+ "\n"
+ "\t- Plain desktop;\n"
+ "\t- Four black rectangles slowly fade in (about 4 seconds);\n"
+ "\t- Once they are solid black, XScreenSaver logos flash in;\n"
+ "\t- Those logos fade out to solid black (about 2 seconds);\n"
+ "\t- Those black squares fade to desktop (about 2 seconds);\n"
+ "\t- Plain desktop (about 1 second);\n"
+ "\t- Repeat.\n"
+ "\n",
+ blurb());
+
{
const char * version_number = "test-fade";
Window daemon_window =
&logo_clipmask, True);
XGetGeometry (dpy, logo, &root, &x, &y, &logo_width, &logo_height, &bw, &d);
+ fprintf (stderr, "\n%s: grabbing shared screenshot\n", blurb());
+ screenshot = screenshot_grab (dpy, root, True, True);
+ fprintf (stderr, "\n");
+
nwindows = 0;
{
int x, y;
attrmask, &attrs);
XSetWindowBackground (dpy, windows[nwindows], BlackPixel (dpy, 0));
XClearWindow (dpy, windows[nwindows]);
+ if (screenshot)
+ {
+ fprintf (stderr, "%s: saving screenshot 0x%0lX on 0x%lX\n",
+ blurb(), (unsigned long) screenshot, windows[nwindows]);
+ screenshot_save (dpy, windows[nwindows], screenshot);
+ }
nwindows++;
}
}
- fprintf (stderr, "%s: fading %d screen%s\n",
+ fprintf (stderr, "\n%s: fading %d screen%s\n\n",
blurb(), ScreenCount(dpy), ScreenCount(dpy) == 1 ? "" : "s");
while (1)
-/* xscreensaver, Copyright © 1993-2023 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1993-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
Time lock_timeout; /* how long after activation locking starts */
Time cycle; /* how long each hack should run */
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 using_randr_extension;
# endif
+ time_t activity_time; /* Time at which user last active. */
+ time_t blank_time; /* Time at which screen blanked. */
+ Bool auth_p; /* Whether auth dialog currently visible. */
+
Bool demoing_p; /* Whether we are demoing a single hack
(without UI.) */
Bool emergency_p; /* Restarted because of a crash */
been received; set to N if SELECT or DEMO N
has been received. (This is kind of nasty.)
*/
+
+ Bool all_clientmessages_p; /* If the daemon isn't running... */
};
/* From dpms.c */
extern void sync_server_dpms_settings (Display *, struct saver_preferences *);
+extern void brute_force_dpms (Display *, struct saver_preferences *, time_t);
const char *init_file_name (void);
--- /dev/null
+/* xscreensaver, Copyright © 2025 Jamie Zawinski <jwz@jwz.org>
+ *
+ * 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.
+ *
+ * Detecting idleness via a connection to the Wayland server and the
+ * "idle-notify-v1" protocol: https://wayland.app/protocols/ext-idle-notify-v1
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+/* #include <wayland-client-protocol.h> */
+#include <wayland-client.h>
+#include <wayland-server.h>
+/* #include <wayland-util.h> */
+#include "ext-idle-notify-v1-client-protocol.h"
+#include "idle-client-protocol.h"
+#include "wayland-idle.h"
+#include "blurb.h"
+
+
+struct wayland_state {
+ struct wl_display *dpy;
+ struct wl_registry *reg;
+ struct wl_event_loop *event_loop;
+
+ void (*activity_cb) (void *closure);
+ void *closure;
+
+ struct wl_seat *seat;
+ char *seat_name;
+ uint32_t seat_caps;
+
+ struct ext_idle_notifier_v1 *ext_idle_notifier;
+ struct ext_idle_notification_v1 *ext_idle_notification;
+
+ struct org_kde_kwin_idle *kde_idle_manager;
+ struct org_kde_kwin_idle_timeout *kde_idle_timer;
+};
+
+
+static void
+seat_handle_capabilities (void *data, struct wl_seat *seat, uint32_t caps)
+{
+ wayland_state *state = (wayland_state *) data;
+ state->seat_caps = caps;
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: seat caps: 0x%02lX\n", blurb(),
+ (unsigned long) caps);
+}
+
+static void
+seat_handle_name (void *data, struct wl_seat *seat, const char *name)
+{
+ wayland_state *state = (wayland_state *) data;
+ state->seat_name = strdup (name);
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: seat name: \"%s\"\n", blurb(), name);
+}
+
+
+/* This is an iterator for all of the extensions that Wayland provides,
+ so we can find the ones we are interested in.
+ */
+static void
+handle_global (void *data, struct wl_registry *reg,
+ uint32_t name, const char *iface, uint32_t version)
+{
+ wayland_state *state = (wayland_state *) data;
+
+ if (!strcmp (iface, wl_seat_interface.name))
+ {
+ static const struct wl_seat_listener seat_listener = {
+ .name = seat_handle_name,
+ .capabilities = seat_handle_capabilities,
+ };
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: found: %s\n", blurb(), iface);
+ state->seat = wl_registry_bind (reg, name, &wl_seat_interface, version);
+ wl_seat_add_listener (state->seat, &seat_listener, state);
+ }
+ else if (!strcmp (iface, ext_idle_notifier_v1_interface.name))
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: wayland: found: %s\n", blurb(), iface);
+ state->ext_idle_notifier =
+ wl_registry_bind (reg, name, &ext_idle_notifier_v1_interface, 1);
+ }
+ else if (!strcmp (iface, org_kde_kwin_idle_interface.name))
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: wayland: found: %s\n", blurb(), iface);
+ state->kde_idle_manager =
+ wl_registry_bind (reg, name, &org_kde_kwin_idle_interface, 1);
+ }
+}
+
+static void
+handle_global_remove (void *data, struct wl_registry *registry, uint32_t name)
+{
+}
+
+
+/* Called when the user has been idle longer than the timeout. */
+static void
+ext_handle_idled (void *data, struct ext_idle_notification_v1 *note)
+{
+ /* wayland_state *state = (wayland_state *) data; */
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: ext_idle: idle notification\n", blurb());
+}
+
+/* Called when the user is no longer idle. */
+static void
+ext_handle_resumed (void *data, struct ext_idle_notification_v1 *note)
+{
+ wayland_state *state = (wayland_state *) data;
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: ext_idle: active notification\n", blurb());
+ (*state->activity_cb) (state->closure);
+}
+
+static void
+kde_handle_idle (void *data, struct org_kde_kwin_idle_timeout *timer)
+{
+ /* wayland_state *state = (wayland_state *) data; */
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: kde_idle: idle notification\n", blurb());
+}
+
+static void
+kde_handle_resumed (void *data, struct org_kde_kwin_idle_timeout *timer)
+{
+ wayland_state *state = (wayland_state *) data;
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: kde_idle: active notification\n", blurb());
+ (*state->activity_cb) (state->closure);
+}
+
+
+static void
+destroy_timer (wayland_state *state)
+{
+ if (state->ext_idle_notification)
+ {
+ ext_idle_notification_v1_destroy (state->ext_idle_notification);
+ state->ext_idle_notification = 0;
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: destroyed kde_idle timer\n", blurb());
+ }
+ if (state->kde_idle_timer)
+ {
+ org_kde_kwin_idle_timeout_release (state->kde_idle_timer);
+ state->kde_idle_timer = 0;
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: destroyed ext_idle timer\n", blurb());
+ }
+}
+
+static void
+register_timer (wayland_state *state, int timeout)
+{
+ destroy_timer (state);
+ if (state->ext_idle_notifier)
+ {
+ static const struct ext_idle_notification_v1_listener
+ ext_idle_notification_listener = {
+ .idled = ext_handle_idled,
+ .resumed = ext_handle_resumed,
+ };
+
+ state->ext_idle_notification =
+ ext_idle_notifier_v1_get_idle_notification (state->ext_idle_notifier,
+ timeout,
+ state->seat);
+ ext_idle_notification_v1_add_listener (state->ext_idle_notification,
+ &ext_idle_notification_listener,
+ state);
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: registered ext_idle timer\n", blurb());
+ }
+ else
+ {
+ static const struct org_kde_kwin_idle_timeout_listener
+ kde_idle_timer_listener = {
+ .idle = kde_handle_idle,
+ .resumed = kde_handle_resumed,
+ };
+
+ state->kde_idle_timer =
+ org_kde_kwin_idle_get_idle_timeout (state->kde_idle_manager,
+ state->seat,
+ timeout);
+ org_kde_kwin_idle_timeout_add_listener (state->kde_idle_timer,
+ &kde_idle_timer_listener,
+ state);
+ if (verbose_p > 2)
+ fprintf (stderr, "%s: wayland: registered kde_idle timer\n", blurb());
+ }
+}
+
+
+/* Dispatch any events that have been read. */
+static int
+dpy_handle_event (int fd, uint32_t mask, void *data)
+{
+ wayland_state *state = (wayland_state *) data;
+ int count = 0;
+
+ if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR))
+ {
+ /* Nothing good can come of this... */
+ }
+
+ if (mask & WL_EVENT_READABLE)
+ count = wl_display_dispatch (state->dpy);
+
+ if (mask & WL_EVENT_WRITABLE)
+ wl_display_flush (state->dpy);
+
+ if (mask == 0)
+ {
+ count = wl_display_dispatch_pending (state->dpy);
+ wl_display_flush (state->dpy);
+ }
+
+ return count;
+}
+
+
+/* Connects to Wayland and returns an opaque state object on success.
+ When user activity is detected, the callback will be run with the
+ provided object as its argument. On failure, returns NULL and
+ an error message.
+ */
+wayland_state *
+wayland_idle_init (void (*activity_cb) (void *closure),
+ void *closure,
+ const char **error_ret)
+{
+ wayland_state *state = (wayland_state *) calloc (sizeof (*state), 1);
+ state->activity_cb = activity_cb;
+ state->closure = closure;
+
+ state->dpy = wl_display_connect (NULL);
+ if (!state->dpy)
+ {
+ free (state);
+ if (error_ret) *error_ret = "connection failed";
+ return NULL;
+ }
+
+ if (verbose_p)
+ fprintf (stderr, "%s: wayland: server connected\n", blurb());
+
+ state->reg = wl_display_get_registry (state->dpy);
+
+ {
+ static const struct wl_registry_listener listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove,
+ };
+ wl_registry_add_listener (state->reg, &listener, state);
+ }
+
+ wl_display_roundtrip (state->dpy); /* First time to register seats */
+ wl_display_roundtrip (state->dpy); /* Second time to register timers */
+
+ if (! state->seat)
+ {
+ wayland_idle_free (state);
+ if (error_ret) *error_ret = "server has no seats";
+ return NULL;
+ }
+
+ if (! (state->kde_idle_manager ||
+ state->ext_idle_notifier))
+ {
+ static char err[300];
+ sprintf (err, "server doesn't implement \"%.100s\" or \"%.100s\"",
+ ext_idle_notifier_v1_interface.name,
+ org_kde_kwin_idle_interface.name);
+ wayland_idle_free (state);
+ if (error_ret) *error_ret = err;
+ return NULL;
+ }
+
+ state->event_loop = wl_event_loop_create();
+
+ { /* Tell the event loop to select() on the display connection. */
+ struct wl_event_source *source =
+ wl_event_loop_add_fd (state->event_loop,
+ wl_display_get_fd (state->dpy),
+ WL_EVENT_READABLE,
+ dpy_handle_event,
+ state);
+ wl_event_source_check (source);
+ /* #### Do I need to free 'source'? */
+ }
+
+ register_timer (state, 1000); /* milliseconds */
+
+ wayland_idle_process_events (state);
+
+ if (error_ret) *error_ret = 0;
+ return state;
+}
+
+int
+wayland_idle_get_fd (wayland_state *state)
+{
+ return wl_display_get_fd (state->dpy);
+}
+
+
+/* Handle any notifications from the Wayland server and run callbacks.
+ */
+void
+wayland_idle_process_events (wayland_state *state)
+{
+ /* Process all outstanding events without blocking. */
+ wl_event_loop_dispatch (state->event_loop, 0);
+}
+
+
+void
+wayland_idle_free (wayland_state *state)
+{
+ wl_display_disconnect (state->dpy);
+ if (state->event_loop)
+ wl_event_loop_destroy (state->event_loop);
+ if (state->seat_name)
+ free (state->seat_name);
+ free (state);
+ if (verbose_p)
+ fprintf (stderr, "%s: wayland: disconnected\n", blurb());
+}
--- /dev/null
+/* xscreensaver, Copyright © 2025 Jamie Zawinski <jwz@jwz.org>
+ *
+ * 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_WAYLAND_IDLE_H__
+#define __XSCREENSAVER_WAYLAND_IDLE_H__
+
+typedef struct wayland_state wayland_state;
+
+/* Connects to Wayland and returns an opaque state object on success.
+ When user activity is detected, the callback will be run with the
+ provided object as its argument. On failure, returns NULL and
+ an error message.
+ */
+extern wayland_state *
+wayland_idle_init (void (*activity_cb) (void *closure),
+ void *closure,
+ const char **error_ret);
+
+/* Returns the file descriptor of the Wayland display connection.
+ You may select on this to see if it needs attention. */
+extern int wayland_idle_get_fd (wayland_state *);
+
+/* Handle any notifications from the Wayland server and run callbacks. */
+extern void wayland_idle_process_events (wayland_state *);
+
+/* Shut it all down. */
+extern void wayland_idle_free (wayland_state *);
+
+#endif /* __XSCREENSAVER_WAYLAND_H__ */
/* windows.c --- turning the screen black; dealing with visuals, virtual roots.
- * xscreensaver, Copyright © 1991-2022 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
# include "config.h"
#endif
+#define _GNU_SOURCE
#ifdef HAVE_UNAME
# include <sys/utsname.h> /* for uname() */
#endif /* HAVE_UNAME */
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <pwd.h> /* for getpwuid() */
#include <X11/Xlib.h>
#include <X11/Xutil.h> /* for XSetClassHint() */
#include "visual.h"
#include "screens.h"
#include "screenshot.h"
+#include "exec.h"
#include "fade.h"
#include "resources.h"
#include "xft.h"
#endif
-static void reset_watchdog_timer (saver_info *si);
-
void
store_saver_status (saver_info *si)
{
- /* 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".
+ /* Structure of the XA_SCREENSAVER_STATUS property:
+
+ XScreenSaver 3.20, 1999: XScreenSaver 6.11, 2025:
+
+ 0: BLANK | LOCK | 0 0: BLANK | LOCK | AUTH | 0
+ 1: 32 bit time_t 1: 64 bit time_t hi
+ 2: screen 0 hack 2: 64 bit time_t lo
+ 3: screen 1 hack 3: screen 0 hack
+ ... 4: screen 1 hack
+ ...
+
+ The first slot is the status: blanked, locked, locked with the password
+ dialog posted, or 0 for unblanked.
+
+ The time_t is the time at which that status began.
+
+ Following all of that is the list of the ordinals of the hacks running on
+ each screen, 1-based.
+
+ Since we don't know the blank/lock/auth status here, we leave whatever
+ was there before unchanged: those will be updated by "xscreensaver".
XA_SCREENSAVER_STATUS is stored on the (real) root window of screen 0.
PROP32 *status = 0;
int format;
unsigned long nitems, bytesafter;
- int nitems2 = si->nscreens + 2;
- int i;
-
- /* 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);
+ int i, j;
+ PROP32 state = XA_BLANK;
+ time_t tt = 0;
- status = (PROP32 *) calloc (nitems2, sizeof(PROP32));
+ /* I hate using XGrabServer, but the calls to read and then alter the
+ property must be atomic, and there's no other way to do that. */
+ XGrabServer (dpy);
+ XSync (dpy, False);
- if (dataP && type == XA_INTEGER && nitems >= 3)
+ /* Read the old property, so we can change just our parts. */
+ if (XGetWindowProperty (dpy, w,
+ XA_SCREENSAVER_STATUS,
+ 0, 999, False, XA_INTEGER,
+ &type, &format, &nitems, &bytesafter,
+ &dataP)
+ == Success
+ && type == XA_INTEGER
+ && nitems >= 3
+ && dataP)
{
- status[0] = ((PROP32 *) dataP)[0];
- status[1] = ((PROP32 *) dataP)[1];
+ PROP32 *data = (PROP32 *) dataP;
+ state = data[0];
+ tt = (time_t) /* 64 bit time_t */
+ ((((unsigned long) data[1] & 0xFFFFFFFFL) << 32) |
+ ((unsigned long) data[2] & 0xFFFFFFFFL));
}
+ nitems = 3 + si->nscreens;
+ status = (PROP32 *) calloc (nitems, sizeof(*status));
+
+ j = 0;
+ status[j++] = state;
+ status[j++] = (PROP32) (((unsigned long) tt) >> 32);
+ status[j++] = (PROP32) (((unsigned long) tt) & 0xFFFFFFFFL);
+
for (i = 0; i < si->nscreens; i++)
{
saver_screen_info *ssi = &si->screens[i];
- status[2 + i] = ssi->current_hack + 1; /* 1-based */
+ status[j++] = ssi->current_hack + 1; /* 1-based */
}
+ if (j != nitems) abort();
XChangeProperty (si->dpy, w, XA_SCREENSAVER_STATUS, XA_INTEGER, 32,
- PropModeReplace, (unsigned char *) status, nitems2);
+ PropModeReplace, (unsigned char *) status, nitems);
+ XUngrabServer (dpy);
XSync (dpy, False);
if (si->prefs.debug_p && si->prefs.verbose_p)
{
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, "%s: wrote status property: 0x%lx: ", blurb(),
+ (unsigned long) w);
+ for (i = 0; i < nitems; i++)
+ {
+ if (i > 0) fprintf (stderr, ", ");
+ if (i == 0 || i == 2)
+ fprintf (stderr, "%s",
+ (status[i] == XA_LOCK ? "LOCK" :
+ status[i] == XA_BLANK ? "BLANK" :
+ status[i] == XA_AUTH ? "AUTH" :
+ status[i] == 0 ? "0" : "???"));
+ else
+ fprintf (stderr, "%lu", status[i]);
+ }
fprintf (stderr, "\n");
}
saver_preferences *p = &si->prefs;
Bool user_active_p = False;
int i;
+ Bool grabbing_supported_p = True;
initialize_screensaver_window (si);
sync_server_dpms_settings (si->dpy, p);
+ /* Under Wayland, we can only grab screenshots if "grim" is installed,
+ and even so, there's no way to grab screenshots under GNOME or KDE.
+ See comment in xscreensaver-getimage.c, and discussion thread here:
+ https://www.jwz.org/blog/2025/06/wayland-screenshots/#comment-260326
+ */
+ if (getenv ("WAYLAND_DISPLAY") || getenv ("WAYLAND_SOCKET"))
+ {
+ const char *prog = "grim";
+ char *desk = getenv ("XDG_CURRENT_DESKTOP");
+ if (desk &&
+ (strcasestr (desk, "GNOME") ||
+ strcasestr (desk, "KDE")))
+ {
+ grabbing_supported_p = False;
+ if (p->verbose_p || p->fade_p || p->unfade_p || p->grab_desktop_p)
+ fprintf (stderr,
+ "%s: screenshots and fading not supported on Wayland %s\n",
+ blurb(), desk);
+ }
+ else if (! on_path_p (prog))
+ {
+ grabbing_supported_p = False;
+ if (p->verbose_p || p->fade_p || p->unfade_p || p->grab_desktop_p)
+ fprintf (stderr,
+ "%s: screenshots and fading on Wayland require \"%s\"\n",
+ blurb(), prog);
+ }
+ }
+
+ if (! grabbing_supported_p)
+ {
+ p->fade_p = False;
+ p->unfade_p = False;
+ }
+
/* Save a screenshot. Must be before fade-out. */
for (i = 0; i < si->nscreens; i++)
{
saver_screen_info *ssi = &si->screens[i];
if (ssi->screenshot)
XFreePixmap (si->dpy, ssi->screenshot);
- ssi->screenshot =
- screenshot_grab (si->dpy, ssi->screensaver_window, False, p->verbose_p);
+ if (grabbing_supported_p)
+ ssi->screenshot =
+ screenshot_grab (si->dpy, ssi->screensaver_window, False,
+ p->verbose_p);
}
if (p->fade_p &&
True, /* out_p */
False, /* from_desktop_p */
&si->fade_state);
+
+ for (i = 0; i < si->nscreens; i++)
+ {
+ saver_screen_info *ssi = &si->screens[i];
+ XClearWindow (si->dpy, ssi->screensaver_window);
+ }
+
if (! interrupted_p)
interrupted_p = fade_screens (si->app, si->dpy,
current_windows, si->nscreens,
what ~/.xscreensaver says they should be. */
sync_server_dpms_settings (si->dpy, p);
+ /* If the wall clock tells us that the monitor should be powered off now,
+ and the auth dialog is not currently on screen, make sure that the
+ monitor is off. */
+ brute_force_dpms (si->dpy, p,
+ (si->auth_p
+ ? 0
+ : si->activity_time));
+
if (si->prefs.debug_p)
fprintf (stderr, "%s: watchdog timer raising screen\n", blurb());
- raise_windows (si);
+ if (! si->auth_p) /* Do not race with the password dialog */
+ raise_windows (si);
running_p = any_screenhacks_running_p (si);
on_p = monitor_powered_on_p (si->dpy);
}
/* Re-schedule this timer. The watchdog timer defaults to a bit less
- than the hack cycle period, but is never longer than one hour.
+ than the hack cycle period, but is never longer than one minute.
*/
si->watchdog_id = 0;
reset_watchdog_timer (si);
}
-static void
+void
reset_watchdog_timer (saver_info *si)
{
saver_preferences *p = &si->prefs;
+ time_t now = time ((time_t *) 0);
+
+ /* When should the watchdog next run?
+ We have a few choices, pick the one that comes soonest.
+ - A fraction of the cycle time;
+ - A minute from now;
+ - The next DPMS event;
+ - Shortly after the password dialog goes away;
+ - Not sooner than half a minute, except for DPMS or password.
+ */
+# define WHEN(TT) do { \
+ time_t tt = (TT); \
+ if (tt > now && tt < when) when = tt; \
+ } while (0)
+
+ int max = 57;
+ int min = 27;
+ time_t when = now + max;
+
+ WHEN (now + (p->cycle * 0.6) / 1000);
+ if (when < now + min) when = now + min;
+
+ WHEN (si->activity_time + p->dpms_standby / 1000);
+ WHEN (si->activity_time + p->dpms_suspend / 1000);
+ WHEN (si->activity_time + p->dpms_off / 1000);
+
+ /* So that we do DPMS promptly after auth dialog is canceled. */
+ if (si->auth_p) WHEN (now + 2);
+
+ min = 2;
+ if (when < now + min) when = now + min;
if (si->watchdog_id)
- {
- XtRemoveTimeOut (si->watchdog_id);
- si->watchdog_id = 0;
- }
+ XtRemoveTimeOut (si->watchdog_id);
- if (p->watchdog_timeout <= 0) return;
- si->watchdog_id = XtAppAddTimeOut (si->app, p->watchdog_timeout,
+ si->watchdog_id = XtAppAddTimeOut (si->app,
+ (when - now) * 1000,
watchdog_timer, (XtPointer) si);
- if (p->debug_p)
- fprintf (stderr, "%s: restarting watchdog_timer (%ld, %ld)\n",
- blurb(), p->watchdog_timeout, si->watchdog_id);
+ if (p->verbose_p > 1)
+ fprintf (stderr, "%s: watchdog in %d:%02d:%02d\n", blurb(),
+ (int) (when - now) / (60 * 60),
+ (int) ((when - now) % (60 * 60)) / 60,
+ (int) (when - now) % 60);
}
--- /dev/null
+/* Generated by wayland-scanner 1.23.1 */
+
+#ifndef XDG_SHELL_CLIENT_PROTOCOL_H
+#define XDG_SHELL_CLIENT_PROTOCOL_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include "wayland-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page page_xdg_shell The xdg_shell protocol
+ * @section page_ifaces_xdg_shell Interfaces
+ * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces
+ * - @subpage page_iface_xdg_positioner - child surface positioner
+ * - @subpage page_iface_xdg_surface - desktop user interface surface base interface
+ * - @subpage page_iface_xdg_toplevel - toplevel surface
+ * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus
+ * @section page_copyright_xdg_shell Copyright
+ * <pre>
+ *
+ * Copyright © 2008-2013 Kristian Høgsberg
+ * Copyright © 2013 Rafael Antognolli
+ * Copyright © 2013 Jasper St. Pierre
+ * Copyright © 2010-2013 Intel Corporation
+ * Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ * Copyright © 2015-2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * </pre>
+ */
+struct wl_output;
+struct wl_seat;
+struct wl_surface;
+struct xdg_popup;
+struct xdg_positioner;
+struct xdg_surface;
+struct xdg_toplevel;
+struct xdg_wm_base;
+
+#ifndef XDG_WM_BASE_INTERFACE
+#define XDG_WM_BASE_INTERFACE
+/**
+ * @page page_iface_xdg_wm_base xdg_wm_base
+ * @section page_iface_xdg_wm_base_desc Description
+ *
+ * The xdg_wm_base interface is exposed as a global object enabling clients
+ * to turn their wl_surfaces into windows in a desktop environment. It
+ * defines the basic functionality needed for clients and the compositor to
+ * create windows that can be dragged, resized, maximized, etc, as well as
+ * creating transient windows such as popup menus.
+ * @section page_iface_xdg_wm_base_api API
+ * See @ref iface_xdg_wm_base.
+ */
+/**
+ * @defgroup iface_xdg_wm_base The xdg_wm_base interface
+ *
+ * The xdg_wm_base interface is exposed as a global object enabling clients
+ * to turn their wl_surfaces into windows in a desktop environment. It
+ * defines the basic functionality needed for clients and the compositor to
+ * create windows that can be dragged, resized, maximized, etc, as well as
+ * creating transient windows such as popup menus.
+ */
+extern const struct wl_interface xdg_wm_base_interface;
+#endif
+#ifndef XDG_POSITIONER_INTERFACE
+#define XDG_POSITIONER_INTERFACE
+/**
+ * @page page_iface_xdg_positioner xdg_positioner
+ * @section page_iface_xdg_positioner_desc Description
+ *
+ * The xdg_positioner provides a collection of rules for the placement of a
+ * child surface relative to a parent surface. Rules can be defined to ensure
+ * the child surface remains within the visible area's borders, and to
+ * specify how the child surface changes its position, such as sliding along
+ * an axis, or flipping around a rectangle. These positioner-created rules are
+ * constrained by the requirement that a child surface must intersect with or
+ * be at least partially adjacent to its parent surface.
+ *
+ * See the various requests for details about possible rules.
+ *
+ * At the time of the request, the compositor makes a copy of the rules
+ * specified by the xdg_positioner. Thus, after the request is complete the
+ * xdg_positioner object can be destroyed or reused; further changes to the
+ * object will have no effect on previous usages.
+ *
+ * For an xdg_positioner object to be considered complete, it must have a
+ * non-zero size set by set_size, and a non-zero anchor rectangle set by
+ * set_anchor_rect. Passing an incomplete xdg_positioner object when
+ * positioning a surface raises an invalid_positioner error.
+ * @section page_iface_xdg_positioner_api API
+ * See @ref iface_xdg_positioner.
+ */
+/**
+ * @defgroup iface_xdg_positioner The xdg_positioner interface
+ *
+ * The xdg_positioner provides a collection of rules for the placement of a
+ * child surface relative to a parent surface. Rules can be defined to ensure
+ * the child surface remains within the visible area's borders, and to
+ * specify how the child surface changes its position, such as sliding along
+ * an axis, or flipping around a rectangle. These positioner-created rules are
+ * constrained by the requirement that a child surface must intersect with or
+ * be at least partially adjacent to its parent surface.
+ *
+ * See the various requests for details about possible rules.
+ *
+ * At the time of the request, the compositor makes a copy of the rules
+ * specified by the xdg_positioner. Thus, after the request is complete the
+ * xdg_positioner object can be destroyed or reused; further changes to the
+ * object will have no effect on previous usages.
+ *
+ * For an xdg_positioner object to be considered complete, it must have a
+ * non-zero size set by set_size, and a non-zero anchor rectangle set by
+ * set_anchor_rect. Passing an incomplete xdg_positioner object when
+ * positioning a surface raises an invalid_positioner error.
+ */
+extern const struct wl_interface xdg_positioner_interface;
+#endif
+#ifndef XDG_SURFACE_INTERFACE
+#define XDG_SURFACE_INTERFACE
+/**
+ * @page page_iface_xdg_surface xdg_surface
+ * @section page_iface_xdg_surface_desc Description
+ *
+ * An interface that may be implemented by a wl_surface, for
+ * implementations that provide a desktop-style user interface.
+ *
+ * It provides a base set of functionality required to construct user
+ * interface elements requiring management by the compositor, such as
+ * toplevel windows, menus, etc. The types of functionality are split into
+ * xdg_surface roles.
+ *
+ * Creating an xdg_surface does not set the role for a wl_surface. In order
+ * to map an xdg_surface, the client must create a role-specific object
+ * using, e.g., get_toplevel, get_popup. The wl_surface for any given
+ * xdg_surface can have at most one role, and may not be assigned any role
+ * not based on xdg_surface.
+ *
+ * A role must be assigned before any other requests are made to the
+ * xdg_surface object.
+ *
+ * The client must call wl_surface.commit on the corresponding wl_surface
+ * for the xdg_surface state to take effect.
+ *
+ * Creating an xdg_surface from a wl_surface which has a buffer attached or
+ * committed is a client error, and any attempts by a client to attach or
+ * manipulate a buffer prior to the first xdg_surface.configure call must
+ * also be treated as errors.
+ *
+ * After creating a role-specific object and setting it up (e.g. by sending
+ * the title, app ID, size constraints, parent, etc), the client must
+ * perform an initial commit without any buffer attached. The compositor
+ * will reply with initial wl_surface state such as
+ * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure
+ * event. The client must acknowledge it and is then allowed to attach a
+ * buffer to map the surface.
+ *
+ * Mapping an xdg_surface-based role surface is defined as making it
+ * possible for the surface to be shown by the compositor. Note that
+ * a mapped surface is not guaranteed to be visible once it is mapped.
+ *
+ * For an xdg_surface to be mapped by the compositor, the following
+ * conditions must be met:
+ * (1) the client has assigned an xdg_surface-based role to the surface
+ * (2) the client has set and committed the xdg_surface state and the
+ * role-dependent state to the surface
+ * (3) the client has committed a buffer to the surface
+ *
+ * A newly-unmapped surface is considered to have met condition (1) out
+ * of the 3 required conditions for mapping a surface if its role surface
+ * has not been destroyed, i.e. the client must perform the initial commit
+ * again before attaching a buffer.
+ * @section page_iface_xdg_surface_api API
+ * See @ref iface_xdg_surface.
+ */
+/**
+ * @defgroup iface_xdg_surface The xdg_surface interface
+ *
+ * An interface that may be implemented by a wl_surface, for
+ * implementations that provide a desktop-style user interface.
+ *
+ * It provides a base set of functionality required to construct user
+ * interface elements requiring management by the compositor, such as
+ * toplevel windows, menus, etc. The types of functionality are split into
+ * xdg_surface roles.
+ *
+ * Creating an xdg_surface does not set the role for a wl_surface. In order
+ * to map an xdg_surface, the client must create a role-specific object
+ * using, e.g., get_toplevel, get_popup. The wl_surface for any given
+ * xdg_surface can have at most one role, and may not be assigned any role
+ * not based on xdg_surface.
+ *
+ * A role must be assigned before any other requests are made to the
+ * xdg_surface object.
+ *
+ * The client must call wl_surface.commit on the corresponding wl_surface
+ * for the xdg_surface state to take effect.
+ *
+ * Creating an xdg_surface from a wl_surface which has a buffer attached or
+ * committed is a client error, and any attempts by a client to attach or
+ * manipulate a buffer prior to the first xdg_surface.configure call must
+ * also be treated as errors.
+ *
+ * After creating a role-specific object and setting it up (e.g. by sending
+ * the title, app ID, size constraints, parent, etc), the client must
+ * perform an initial commit without any buffer attached. The compositor
+ * will reply with initial wl_surface state such as
+ * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure
+ * event. The client must acknowledge it and is then allowed to attach a
+ * buffer to map the surface.
+ *
+ * Mapping an xdg_surface-based role surface is defined as making it
+ * possible for the surface to be shown by the compositor. Note that
+ * a mapped surface is not guaranteed to be visible once it is mapped.
+ *
+ * For an xdg_surface to be mapped by the compositor, the following
+ * conditions must be met:
+ * (1) the client has assigned an xdg_surface-based role to the surface
+ * (2) the client has set and committed the xdg_surface state and the
+ * role-dependent state to the surface
+ * (3) the client has committed a buffer to the surface
+ *
+ * A newly-unmapped surface is considered to have met condition (1) out
+ * of the 3 required conditions for mapping a surface if its role surface
+ * has not been destroyed, i.e. the client must perform the initial commit
+ * again before attaching a buffer.
+ */
+extern const struct wl_interface xdg_surface_interface;
+#endif
+#ifndef XDG_TOPLEVEL_INTERFACE
+#define XDG_TOPLEVEL_INTERFACE
+/**
+ * @page page_iface_xdg_toplevel xdg_toplevel
+ * @section page_iface_xdg_toplevel_desc Description
+ *
+ * This interface defines an xdg_surface role which allows a surface to,
+ * among other things, set window-like properties such as maximize,
+ * fullscreen, and minimize, set application-specific metadata like title and
+ * id, and well as trigger user interactive operations such as interactive
+ * resize and move.
+ *
+ * A xdg_toplevel by default is responsible for providing the full intended
+ * visual representation of the toplevel, which depending on the window
+ * state, may mean things like a title bar, window controls and drop shadow.
+ *
+ * Unmapping an xdg_toplevel means that the surface cannot be shown
+ * by the compositor until it is explicitly mapped again.
+ * All active operations (e.g., move, resize) are canceled and all
+ * attributes (e.g. title, state, stacking, ...) are discarded for
+ * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to
+ * the state it had right after xdg_surface.get_toplevel. The client
+ * can re-map the toplevel by performing a commit without any buffer
+ * attached, waiting for a configure event and handling it as usual (see
+ * xdg_surface description).
+ *
+ * Attaching a null buffer to a toplevel unmaps the surface.
+ * @section page_iface_xdg_toplevel_api API
+ * See @ref iface_xdg_toplevel.
+ */
+/**
+ * @defgroup iface_xdg_toplevel The xdg_toplevel interface
+ *
+ * This interface defines an xdg_surface role which allows a surface to,
+ * among other things, set window-like properties such as maximize,
+ * fullscreen, and minimize, set application-specific metadata like title and
+ * id, and well as trigger user interactive operations such as interactive
+ * resize and move.
+ *
+ * A xdg_toplevel by default is responsible for providing the full intended
+ * visual representation of the toplevel, which depending on the window
+ * state, may mean things like a title bar, window controls and drop shadow.
+ *
+ * Unmapping an xdg_toplevel means that the surface cannot be shown
+ * by the compositor until it is explicitly mapped again.
+ * All active operations (e.g., move, resize) are canceled and all
+ * attributes (e.g. title, state, stacking, ...) are discarded for
+ * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to
+ * the state it had right after xdg_surface.get_toplevel. The client
+ * can re-map the toplevel by performing a commit without any buffer
+ * attached, waiting for a configure event and handling it as usual (see
+ * xdg_surface description).
+ *
+ * Attaching a null buffer to a toplevel unmaps the surface.
+ */
+extern const struct wl_interface xdg_toplevel_interface;
+#endif
+#ifndef XDG_POPUP_INTERFACE
+#define XDG_POPUP_INTERFACE
+/**
+ * @page page_iface_xdg_popup xdg_popup
+ * @section page_iface_xdg_popup_desc Description
+ *
+ * A popup surface is a short-lived, temporary surface. It can be used to
+ * implement for example menus, popovers, tooltips and other similar user
+ * interface concepts.
+ *
+ * A popup can be made to take an explicit grab. See xdg_popup.grab for
+ * details.
+ *
+ * When the popup is dismissed, a popup_done event will be sent out, and at
+ * the same time the surface will be unmapped. See the xdg_popup.popup_done
+ * event for details.
+ *
+ * Explicitly destroying the xdg_popup object will also dismiss the popup and
+ * unmap the surface. Clients that want to dismiss the popup when another
+ * surface of their own is clicked should dismiss the popup using the destroy
+ * request.
+ *
+ * A newly created xdg_popup will be stacked on top of all previously created
+ * xdg_popup surfaces associated with the same xdg_toplevel.
+ *
+ * The parent of an xdg_popup must be mapped (see the xdg_surface
+ * description) before the xdg_popup itself.
+ *
+ * The client must call wl_surface.commit on the corresponding wl_surface
+ * for the xdg_popup state to take effect.
+ * @section page_iface_xdg_popup_api API
+ * See @ref iface_xdg_popup.
+ */
+/**
+ * @defgroup iface_xdg_popup The xdg_popup interface
+ *
+ * A popup surface is a short-lived, temporary surface. It can be used to
+ * implement for example menus, popovers, tooltips and other similar user
+ * interface concepts.
+ *
+ * A popup can be made to take an explicit grab. See xdg_popup.grab for
+ * details.
+ *
+ * When the popup is dismissed, a popup_done event will be sent out, and at
+ * the same time the surface will be unmapped. See the xdg_popup.popup_done
+ * event for details.
+ *
+ * Explicitly destroying the xdg_popup object will also dismiss the popup and
+ * unmap the surface. Clients that want to dismiss the popup when another
+ * surface of their own is clicked should dismiss the popup using the destroy
+ * request.
+ *
+ * A newly created xdg_popup will be stacked on top of all previously created
+ * xdg_popup surfaces associated with the same xdg_toplevel.
+ *
+ * The parent of an xdg_popup must be mapped (see the xdg_surface
+ * description) before the xdg_popup itself.
+ *
+ * The client must call wl_surface.commit on the corresponding wl_surface
+ * for the xdg_popup state to take effect.
+ */
+extern const struct wl_interface xdg_popup_interface;
+#endif
+
+#ifndef XDG_WM_BASE_ERROR_ENUM
+#define XDG_WM_BASE_ERROR_ENUM
+enum xdg_wm_base_error {
+ /**
+ * given wl_surface has another role
+ */
+ XDG_WM_BASE_ERROR_ROLE = 0,
+ /**
+ * xdg_wm_base was destroyed before children
+ */
+ XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1,
+ /**
+ * the client tried to map or destroy a non-topmost popup
+ */
+ XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2,
+ /**
+ * the client specified an invalid popup parent surface
+ */
+ XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3,
+ /**
+ * the client provided an invalid surface state
+ */
+ XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4,
+ /**
+ * the client provided an invalid positioner
+ */
+ XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5,
+ /**
+ * the client didn’t respond to a ping event in time
+ */
+ XDG_WM_BASE_ERROR_UNRESPONSIVE = 6,
+};
+#endif /* XDG_WM_BASE_ERROR_ENUM */
+
+/**
+ * @ingroup iface_xdg_wm_base
+ * @struct xdg_wm_base_listener
+ */
+struct xdg_wm_base_listener {
+ /**
+ * check if the client is alive
+ *
+ * The ping event asks the client if it's still alive. Pass the
+ * serial specified in the event back to the compositor by sending
+ * a "pong" request back with the specified serial. See
+ * xdg_wm_base.pong.
+ *
+ * Compositors can use this to determine if the client is still
+ * alive. It's unspecified what will happen if the client doesn't
+ * respond to the ping request, or in what timeframe. Clients
+ * should try to respond in a reasonable amount of time. The
+ * “unresponsive” error is provided for compositors that wish
+ * to disconnect unresponsive clients.
+ *
+ * A compositor is free to ping in any way it wants, but a client
+ * must always respond to any xdg_wm_base object it created.
+ * @param serial pass this to the pong request
+ */
+ void (*ping)(void *data,
+ struct xdg_wm_base *xdg_wm_base,
+ uint32_t serial);
+};
+
+/**
+ * @ingroup iface_xdg_wm_base
+ */
+static inline int
+xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base,
+ const struct xdg_wm_base_listener *listener, void *data)
+{
+ return wl_proxy_add_listener((struct wl_proxy *) xdg_wm_base,
+ (void (**)(void)) listener, data);
+}
+
+#define XDG_WM_BASE_DESTROY 0
+#define XDG_WM_BASE_CREATE_POSITIONER 1
+#define XDG_WM_BASE_GET_XDG_SURFACE 2
+#define XDG_WM_BASE_PONG 3
+
+/**
+ * @ingroup iface_xdg_wm_base
+ */
+#define XDG_WM_BASE_PING_SINCE_VERSION 1
+
+/**
+ * @ingroup iface_xdg_wm_base
+ */
+#define XDG_WM_BASE_DESTROY_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_wm_base
+ */
+#define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_wm_base
+ */
+#define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_wm_base
+ */
+#define XDG_WM_BASE_PONG_SINCE_VERSION 1
+
+/** @ingroup iface_xdg_wm_base */
+static inline void
+xdg_wm_base_set_user_data(struct xdg_wm_base *xdg_wm_base, void *user_data)
+{
+ wl_proxy_set_user_data((struct wl_proxy *) xdg_wm_base, user_data);
+}
+
+/** @ingroup iface_xdg_wm_base */
+static inline void *
+xdg_wm_base_get_user_data(struct xdg_wm_base *xdg_wm_base)
+{
+ return wl_proxy_get_user_data((struct wl_proxy *) xdg_wm_base);
+}
+
+static inline uint32_t
+xdg_wm_base_get_version(struct xdg_wm_base *xdg_wm_base)
+{
+ return wl_proxy_get_version((struct wl_proxy *) xdg_wm_base);
+}
+
+/**
+ * @ingroup iface_xdg_wm_base
+ *
+ * Destroy this xdg_wm_base object.
+ *
+ * Destroying a bound xdg_wm_base object while there are surfaces
+ * still alive created by this xdg_wm_base object instance is illegal
+ * and will result in a defunct_surfaces error.
+ */
+static inline void
+xdg_wm_base_destroy(struct xdg_wm_base *xdg_wm_base)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base,
+ XDG_WM_BASE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), WL_MARSHAL_FLAG_DESTROY);
+}
+
+/**
+ * @ingroup iface_xdg_wm_base
+ *
+ * Create a positioner object. A positioner object is used to position
+ * surfaces relative to some parent surface. See the interface description
+ * and xdg_surface.get_popup for details.
+ */
+static inline struct xdg_positioner *
+xdg_wm_base_create_positioner(struct xdg_wm_base *xdg_wm_base)
+{
+ struct wl_proxy *id;
+
+ id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base,
+ XDG_WM_BASE_CREATE_POSITIONER, &xdg_positioner_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL);
+
+ return (struct xdg_positioner *) id;
+}
+
+/**
+ * @ingroup iface_xdg_wm_base
+ *
+ * This creates an xdg_surface for the given surface. While xdg_surface
+ * itself is not a role, the corresponding surface may only be assigned
+ * a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is
+ * illegal to create an xdg_surface for a wl_surface which already has an
+ * assigned role and this will result in a role error.
+ *
+ * This creates an xdg_surface for the given surface. An xdg_surface is
+ * used as basis to define a role to a given surface, such as xdg_toplevel
+ * or xdg_popup. It also manages functionality shared between xdg_surface
+ * based surface roles.
+ *
+ * See the documentation of xdg_surface for more details about what an
+ * xdg_surface is and how it is used.
+ */
+static inline struct xdg_surface *
+xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface)
+{
+ struct wl_proxy *id;
+
+ id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base,
+ XDG_WM_BASE_GET_XDG_SURFACE, &xdg_surface_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL, surface);
+
+ return (struct xdg_surface *) id;
+}
+
+/**
+ * @ingroup iface_xdg_wm_base
+ *
+ * A client must respond to a ping event with a pong request or
+ * the client may be deemed unresponsive. See xdg_wm_base.ping
+ * and xdg_wm_base.error.unresponsive.
+ */
+static inline void
+xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base,
+ XDG_WM_BASE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, serial);
+}
+
+#ifndef XDG_POSITIONER_ERROR_ENUM
+#define XDG_POSITIONER_ERROR_ENUM
+enum xdg_positioner_error {
+ /**
+ * invalid input provided
+ */
+ XDG_POSITIONER_ERROR_INVALID_INPUT = 0,
+};
+#endif /* XDG_POSITIONER_ERROR_ENUM */
+
+#ifndef XDG_POSITIONER_ANCHOR_ENUM
+#define XDG_POSITIONER_ANCHOR_ENUM
+enum xdg_positioner_anchor {
+ XDG_POSITIONER_ANCHOR_NONE = 0,
+ XDG_POSITIONER_ANCHOR_TOP = 1,
+ XDG_POSITIONER_ANCHOR_BOTTOM = 2,
+ XDG_POSITIONER_ANCHOR_LEFT = 3,
+ XDG_POSITIONER_ANCHOR_RIGHT = 4,
+ XDG_POSITIONER_ANCHOR_TOP_LEFT = 5,
+ XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6,
+ XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7,
+ XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8,
+};
+#endif /* XDG_POSITIONER_ANCHOR_ENUM */
+
+#ifndef XDG_POSITIONER_GRAVITY_ENUM
+#define XDG_POSITIONER_GRAVITY_ENUM
+enum xdg_positioner_gravity {
+ XDG_POSITIONER_GRAVITY_NONE = 0,
+ XDG_POSITIONER_GRAVITY_TOP = 1,
+ XDG_POSITIONER_GRAVITY_BOTTOM = 2,
+ XDG_POSITIONER_GRAVITY_LEFT = 3,
+ XDG_POSITIONER_GRAVITY_RIGHT = 4,
+ XDG_POSITIONER_GRAVITY_TOP_LEFT = 5,
+ XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6,
+ XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7,
+ XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8,
+};
+#endif /* XDG_POSITIONER_GRAVITY_ENUM */
+
+#ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM
+#define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM
+/**
+ * @ingroup iface_xdg_positioner
+ * constraint adjustments
+ *
+ * The constraint adjustment value define ways the compositor will adjust
+ * the position of the surface, if the unadjusted position would result
+ * in the surface being partly constrained.
+ *
+ * Whether a surface is considered 'constrained' is left to the compositor
+ * to determine. For example, the surface may be partly outside the
+ * compositor's defined 'work area', thus necessitating the child surface's
+ * position be adjusted until it is entirely inside the work area.
+ *
+ * The adjustments can be combined, according to a defined precedence: 1)
+ * Flip, 2) Slide, 3) Resize.
+ */
+enum xdg_positioner_constraint_adjustment {
+ /**
+ * don't move the child surface when constrained
+ *
+ * Don't alter the surface position even if it is constrained on
+ * some axis, for example partially outside the edge of an output.
+ */
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0,
+ /**
+ * move along the x axis until unconstrained
+ *
+ * Slide the surface along the x axis until it is no longer
+ * constrained.
+ *
+ * First try to slide towards the direction of the gravity on the x
+ * axis until either the edge in the opposite direction of the
+ * gravity is unconstrained or the edge in the direction of the
+ * gravity is constrained.
+ *
+ * Then try to slide towards the opposite direction of the gravity
+ * on the x axis until either the edge in the direction of the
+ * gravity is unconstrained or the edge in the opposite direction
+ * of the gravity is constrained.
+ */
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1,
+ /**
+ * move along the y axis until unconstrained
+ *
+ * Slide the surface along the y axis until it is no longer
+ * constrained.
+ *
+ * First try to slide towards the direction of the gravity on the y
+ * axis until either the edge in the opposite direction of the
+ * gravity is unconstrained or the edge in the direction of the
+ * gravity is constrained.
+ *
+ * Then try to slide towards the opposite direction of the gravity
+ * on the y axis until either the edge in the direction of the
+ * gravity is unconstrained or the edge in the opposite direction
+ * of the gravity is constrained.
+ */
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2,
+ /**
+ * invert the anchor and gravity on the x axis
+ *
+ * Invert the anchor and gravity on the x axis if the surface is
+ * constrained on the x axis. For example, if the left edge of the
+ * surface is constrained, the gravity is 'left' and the anchor is
+ * 'left', change the gravity to 'right' and the anchor to 'right'.
+ *
+ * If the adjusted position also ends up being constrained, the
+ * resulting position of the flip_x adjustment will be the one
+ * before the adjustment.
+ */
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4,
+ /**
+ * invert the anchor and gravity on the y axis
+ *
+ * Invert the anchor and gravity on the y axis if the surface is
+ * constrained on the y axis. For example, if the bottom edge of
+ * the surface is constrained, the gravity is 'bottom' and the
+ * anchor is 'bottom', change the gravity to 'top' and the anchor
+ * to 'top'.
+ *
+ * The adjusted position is calculated given the original anchor
+ * rectangle and offset, but with the new flipped anchor and
+ * gravity values.
+ *
+ * If the adjusted position also ends up being constrained, the
+ * resulting position of the flip_y adjustment will be the one
+ * before the adjustment.
+ */
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8,
+ /**
+ * horizontally resize the surface
+ *
+ * Resize the surface horizontally so that it is completely
+ * unconstrained.
+ */
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16,
+ /**
+ * vertically resize the surface
+ *
+ * Resize the surface vertically so that it is completely
+ * unconstrained.
+ */
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32,
+};
+#endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */
+
+#define XDG_POSITIONER_DESTROY 0
+#define XDG_POSITIONER_SET_SIZE 1
+#define XDG_POSITIONER_SET_ANCHOR_RECT 2
+#define XDG_POSITIONER_SET_ANCHOR 3
+#define XDG_POSITIONER_SET_GRAVITY 4
+#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5
+#define XDG_POSITIONER_SET_OFFSET 6
+#define XDG_POSITIONER_SET_REACTIVE 7
+#define XDG_POSITIONER_SET_PARENT_SIZE 8
+#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9
+
+
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_DESTROY_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3
+
+/** @ingroup iface_xdg_positioner */
+static inline void
+xdg_positioner_set_user_data(struct xdg_positioner *xdg_positioner, void *user_data)
+{
+ wl_proxy_set_user_data((struct wl_proxy *) xdg_positioner, user_data);
+}
+
+/** @ingroup iface_xdg_positioner */
+static inline void *
+xdg_positioner_get_user_data(struct xdg_positioner *xdg_positioner)
+{
+ return wl_proxy_get_user_data((struct wl_proxy *) xdg_positioner);
+}
+
+static inline uint32_t
+xdg_positioner_get_version(struct xdg_positioner *xdg_positioner)
+{
+ return wl_proxy_get_version((struct wl_proxy *) xdg_positioner);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Notify the compositor that the xdg_positioner will no longer be used.
+ */
+static inline void
+xdg_positioner_destroy(struct xdg_positioner *xdg_positioner)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), WL_MARSHAL_FLAG_DESTROY);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Set the size of the surface that is to be positioned with the positioner
+ * object. The size is in surface-local coordinates and corresponds to the
+ * window geometry. See xdg_surface.set_window_geometry.
+ *
+ * If a zero or negative size is set the invalid_input error is raised.
+ */
+static inline void
+xdg_positioner_set_size(struct xdg_positioner *xdg_positioner, int32_t width, int32_t height)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, width, height);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Specify the anchor rectangle within the parent surface that the child
+ * surface will be placed relative to. The rectangle is relative to the
+ * window geometry as defined by xdg_surface.set_window_geometry of the
+ * parent surface.
+ *
+ * When the xdg_positioner object is used to position a child surface, the
+ * anchor rectangle may not extend outside the window geometry of the
+ * positioned child's parent surface.
+ *
+ * If a negative size is set the invalid_input error is raised.
+ */
+static inline void
+xdg_positioner_set_anchor_rect(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y, width, height);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Defines the anchor point for the anchor rectangle. The specified anchor
+ * is used derive an anchor point that the child surface will be
+ * positioned relative to. If a corner anchor is set (e.g. 'top_left' or
+ * 'bottom_right'), the anchor point will be at the specified corner;
+ * otherwise, the derived anchor point will be centered on the specified
+ * edge, or in the center of the anchor rectangle if no edge is specified.
+ */
+static inline void
+xdg_positioner_set_anchor(struct xdg_positioner *xdg_positioner, uint32_t anchor)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, anchor);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Defines in what direction a surface should be positioned, relative to
+ * the anchor point of the parent surface. If a corner gravity is
+ * specified (e.g. 'bottom_right' or 'top_left'), then the child surface
+ * will be placed towards the specified gravity; otherwise, the child
+ * surface will be centered over the anchor point on any axis that had no
+ * gravity specified. If the gravity is not in the ‘gravity’ enum, an
+ * invalid_input error is raised.
+ */
+static inline void
+xdg_positioner_set_gravity(struct xdg_positioner *xdg_positioner, uint32_t gravity)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, gravity);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Specify how the window should be positioned if the originally intended
+ * position caused the surface to be constrained, meaning at least
+ * partially outside positioning boundaries set by the compositor. The
+ * adjustment is set by constructing a bitmask describing the adjustment to
+ * be made when the surface is constrained on that axis.
+ *
+ * If no bit for one axis is set, the compositor will assume that the child
+ * surface should not change its position on that axis when constrained.
+ *
+ * If more than one bit for one axis is set, the order of how adjustments
+ * are applied is specified in the corresponding adjustment descriptions.
+ *
+ * The default adjustment is none.
+ */
+static inline void
+xdg_positioner_set_constraint_adjustment(struct xdg_positioner *xdg_positioner, uint32_t constraint_adjustment)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, constraint_adjustment);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Specify the surface position offset relative to the position of the
+ * anchor on the anchor rectangle and the anchor on the surface. For
+ * example if the anchor of the anchor rectangle is at (x, y), the surface
+ * has the gravity bottom|right, and the offset is (ox, oy), the calculated
+ * surface position will be (x + ox, y + oy). The offset position of the
+ * surface is the one used for constraint testing. See
+ * set_constraint_adjustment.
+ *
+ * An example use case is placing a popup menu on top of a user interface
+ * element, while aligning the user interface element of the parent surface
+ * with some user interface element placed somewhere in the popup surface.
+ */
+static inline void
+xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * When set reactive, the surface is reconstrained if the conditions used
+ * for constraining changed, e.g. the parent window moved.
+ *
+ * If the conditions changed and the popup was reconstrained, an
+ * xdg_popup.configure event is sent with updated geometry, followed by an
+ * xdg_surface.configure event.
+ */
+static inline void
+xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_REACTIVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Set the parent window geometry the compositor should use when
+ * positioning the popup. The compositor may use this information to
+ * determine the future state the popup should be constrained using. If
+ * this doesn't match the dimension of the parent the popup is eventually
+ * positioned against, the behavior is undefined.
+ *
+ * The arguments are given in the surface-local coordinate space.
+ */
+static inline void
+xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_PARENT_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, parent_width, parent_height);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Set the serial of an xdg_surface.configure event this positioner will be
+ * used in response to. The compositor may use this information together
+ * with set_parent_size to determine what future state the popup should be
+ * constrained using.
+ */
+static inline void
+xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_PARENT_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, serial);
+}
+
+#ifndef XDG_SURFACE_ERROR_ENUM
+#define XDG_SURFACE_ERROR_ENUM
+enum xdg_surface_error {
+ /**
+ * Surface was not fully constructed
+ */
+ XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1,
+ /**
+ * Surface was already constructed
+ */
+ XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2,
+ /**
+ * Attaching a buffer to an unconfigured surface
+ */
+ XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3,
+ /**
+ * Invalid serial number when acking a configure event
+ */
+ XDG_SURFACE_ERROR_INVALID_SERIAL = 4,
+ /**
+ * Width or height was zero or negative
+ */
+ XDG_SURFACE_ERROR_INVALID_SIZE = 5,
+ /**
+ * Surface was destroyed before its role object
+ */
+ XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT = 6,
+};
+#endif /* XDG_SURFACE_ERROR_ENUM */
+
+/**
+ * @ingroup iface_xdg_surface
+ * @struct xdg_surface_listener
+ */
+struct xdg_surface_listener {
+ /**
+ * suggest a surface change
+ *
+ * The configure event marks the end of a configure sequence. A
+ * configure sequence is a set of one or more events configuring
+ * the state of the xdg_surface, including the final
+ * xdg_surface.configure event.
+ *
+ * Where applicable, xdg_surface surface roles will during a
+ * configure sequence extend this event as a latched state sent as
+ * events before the xdg_surface.configure event. Such events
+ * should be considered to make up a set of atomically applied
+ * configuration states, where the xdg_surface.configure commits
+ * the accumulated state.
+ *
+ * Clients should arrange their surface for the new states, and
+ * then send an ack_configure request with the serial sent in this
+ * configure event at some point before committing the new surface.
+ *
+ * If the client receives multiple configure events before it can
+ * respond to one, it is free to discard all but the last event it
+ * received.
+ * @param serial serial of the configure event
+ */
+ void (*configure)(void *data,
+ struct xdg_surface *xdg_surface,
+ uint32_t serial);
+};
+
+/**
+ * @ingroup iface_xdg_surface
+ */
+static inline int
+xdg_surface_add_listener(struct xdg_surface *xdg_surface,
+ const struct xdg_surface_listener *listener, void *data)
+{
+ return wl_proxy_add_listener((struct wl_proxy *) xdg_surface,
+ (void (**)(void)) listener, data);
+}
+
+#define XDG_SURFACE_DESTROY 0
+#define XDG_SURFACE_GET_TOPLEVEL 1
+#define XDG_SURFACE_GET_POPUP 2
+#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3
+#define XDG_SURFACE_ACK_CONFIGURE 4
+
+/**
+ * @ingroup iface_xdg_surface
+ */
+#define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1
+
+/**
+ * @ingroup iface_xdg_surface
+ */
+#define XDG_SURFACE_DESTROY_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_surface
+ */
+#define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_surface
+ */
+#define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_surface
+ */
+#define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_surface
+ */
+#define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1
+
+/** @ingroup iface_xdg_surface */
+static inline void
+xdg_surface_set_user_data(struct xdg_surface *xdg_surface, void *user_data)
+{
+ wl_proxy_set_user_data((struct wl_proxy *) xdg_surface, user_data);
+}
+
+/** @ingroup iface_xdg_surface */
+static inline void *
+xdg_surface_get_user_data(struct xdg_surface *xdg_surface)
+{
+ return wl_proxy_get_user_data((struct wl_proxy *) xdg_surface);
+}
+
+static inline uint32_t
+xdg_surface_get_version(struct xdg_surface *xdg_surface)
+{
+ return wl_proxy_get_version((struct wl_proxy *) xdg_surface);
+}
+
+/**
+ * @ingroup iface_xdg_surface
+ *
+ * Destroy the xdg_surface object. An xdg_surface must only be destroyed
+ * after its role object has been destroyed, otherwise
+ * a defunct_role_object error is raised.
+ */
+static inline void
+xdg_surface_destroy(struct xdg_surface *xdg_surface)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface,
+ XDG_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), WL_MARSHAL_FLAG_DESTROY);
+}
+
+/**
+ * @ingroup iface_xdg_surface
+ *
+ * This creates an xdg_toplevel object for the given xdg_surface and gives
+ * the associated wl_surface the xdg_toplevel role.
+ *
+ * See the documentation of xdg_toplevel for more details about what an
+ * xdg_toplevel is and how it is used.
+ */
+static inline struct xdg_toplevel *
+xdg_surface_get_toplevel(struct xdg_surface *xdg_surface)
+{
+ struct wl_proxy *id;
+
+ id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface,
+ XDG_SURFACE_GET_TOPLEVEL, &xdg_toplevel_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL);
+
+ return (struct xdg_toplevel *) id;
+}
+
+/**
+ * @ingroup iface_xdg_surface
+ *
+ * This creates an xdg_popup object for the given xdg_surface and gives
+ * the associated wl_surface the xdg_popup role.
+ *
+ * If null is passed as a parent, a parent surface must be specified using
+ * some other protocol, before committing the initial state.
+ *
+ * See the documentation of xdg_popup for more details about what an
+ * xdg_popup is and how it is used.
+ */
+static inline struct xdg_popup *
+xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *parent, struct xdg_positioner *positioner)
+{
+ struct wl_proxy *id;
+
+ id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface,
+ XDG_SURFACE_GET_POPUP, &xdg_popup_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL, parent, positioner);
+
+ return (struct xdg_popup *) id;
+}
+
+/**
+ * @ingroup iface_xdg_surface
+ *
+ * The window geometry of a surface is its "visible bounds" from the
+ * user's perspective. Client-side decorations often have invisible
+ * portions like drop-shadows which should be ignored for the
+ * purposes of aligning, placing and constraining windows.
+ *
+ * The window geometry is double-buffered state, see wl_surface.commit.
+ *
+ * When maintaining a position, the compositor should treat the (x, y)
+ * coordinate of the window geometry as the top left corner of the window.
+ * A client changing the (x, y) window geometry coordinate should in
+ * general not alter the position of the window.
+ *
+ * Once the window geometry of the surface is set, it is not possible to
+ * unset it, and it will remain the same until set_window_geometry is
+ * called again, even if a new subsurface or buffer is attached.
+ *
+ * If never set, the value is the full bounds of the surface,
+ * including any subsurfaces. This updates dynamically on every
+ * commit. This unset is meant for extremely simple clients.
+ *
+ * The arguments are given in the surface-local coordinate space of
+ * the wl_surface associated with this xdg_surface, and may extend outside
+ * of the wl_surface itself to mark parts of the subsurface tree as part of
+ * the window geometry.
+ *
+ * When applied, the effective window geometry will be the set window
+ * geometry clamped to the bounding rectangle of the combined
+ * geometry of the surface of the xdg_surface and the associated
+ * subsurfaces.
+ *
+ * The effective geometry will not be recalculated unless a new call to
+ * set_window_geometry is done and the new pending surface state is
+ * subsequently applied.
+ *
+ * The width and height of the effective window geometry must be
+ * greater than zero. Setting an invalid size will raise an
+ * invalid_size error.
+ */
+static inline void
+xdg_surface_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface,
+ XDG_SURFACE_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, x, y, width, height);
+}
+
+/**
+ * @ingroup iface_xdg_surface
+ *
+ * When a configure event is received, if a client commits the
+ * surface in response to the configure event, then the client
+ * must make an ack_configure request sometime before the commit
+ * request, passing along the serial of the configure event.
+ *
+ * For instance, for toplevel surfaces the compositor might use this
+ * information to move a surface to the top left only when the client has
+ * drawn itself for the maximized or fullscreen state.
+ *
+ * If the client receives multiple configure events before it
+ * can respond to one, it only has to ack the last configure event.
+ * Acking a configure event that was never sent raises an invalid_serial
+ * error.
+ *
+ * A client is not required to commit immediately after sending
+ * an ack_configure request - it may even ack_configure several times
+ * before its next surface commit.
+ *
+ * A client may send multiple ack_configure requests before committing, but
+ * only the last request sent before a commit indicates which configure
+ * event the client really is responding to.
+ *
+ * Sending an ack_configure request consumes the serial number sent with
+ * the request, as well as serial numbers sent by all configure events
+ * sent on this xdg_surface prior to the configure event referenced by
+ * the committed serial.
+ *
+ * It is an error to issue multiple ack_configure requests referencing a
+ * serial from the same configure event, or to issue an ack_configure
+ * request referencing a serial from a configure event issued before the
+ * event identified by the last ack_configure request for the same
+ * xdg_surface. Doing so will raise an invalid_serial error.
+ */
+static inline void
+xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface,
+ XDG_SURFACE_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, serial);
+}
+
+#ifndef XDG_TOPLEVEL_ERROR_ENUM
+#define XDG_TOPLEVEL_ERROR_ENUM
+enum xdg_toplevel_error {
+ /**
+ * provided value is not a valid variant of the resize_edge enum
+ */
+ XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE = 0,
+ /**
+ * invalid parent toplevel
+ */
+ XDG_TOPLEVEL_ERROR_INVALID_PARENT = 1,
+ /**
+ * client provided an invalid min or max size
+ */
+ XDG_TOPLEVEL_ERROR_INVALID_SIZE = 2,
+};
+#endif /* XDG_TOPLEVEL_ERROR_ENUM */
+
+#ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM
+#define XDG_TOPLEVEL_RESIZE_EDGE_ENUM
+/**
+ * @ingroup iface_xdg_toplevel
+ * edge values for resizing
+ *
+ * These values are used to indicate which edge of a surface
+ * is being dragged in a resize operation.
+ */
+enum xdg_toplevel_resize_edge {
+ XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0,
+ XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1,
+ XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2,
+ XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4,
+ XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5,
+ XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6,
+ XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8,
+ XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9,
+ XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10,
+};
+#endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */
+
+#ifndef XDG_TOPLEVEL_STATE_ENUM
+#define XDG_TOPLEVEL_STATE_ENUM
+/**
+ * @ingroup iface_xdg_toplevel
+ * types of state on the surface
+ *
+ * The different state values used on the surface. This is designed for
+ * state values like maximized, fullscreen. It is paired with the
+ * configure event to ensure that both the client and the compositor
+ * setting the state can be synchronized.
+ *
+ * States set in this way are double-buffered, see wl_surface.commit.
+ */
+enum xdg_toplevel_state {
+ /**
+ * the surface is maximized
+ * the surface is maximized
+ *
+ * The surface is maximized. The window geometry specified in the
+ * configure event must be obeyed by the client, or the
+ * xdg_wm_base.invalid_surface_state error is raised.
+ *
+ * The client should draw without shadow or other decoration
+ * outside of the window geometry.
+ */
+ XDG_TOPLEVEL_STATE_MAXIMIZED = 1,
+ /**
+ * the surface is fullscreen
+ * the surface is fullscreen
+ *
+ * The surface is fullscreen. The window geometry specified in
+ * the configure event is a maximum; the client cannot resize
+ * beyond it. For a surface to cover the whole fullscreened area,
+ * the geometry dimensions must be obeyed by the client. For more
+ * details, see xdg_toplevel.set_fullscreen.
+ */
+ XDG_TOPLEVEL_STATE_FULLSCREEN = 2,
+ /**
+ * the surface is being resized
+ * the surface is being resized
+ *
+ * The surface is being resized. The window geometry specified in
+ * the configure event is a maximum; the client cannot resize
+ * beyond it. Clients that have aspect ratio or cell sizing
+ * configuration can use a smaller size, however.
+ */
+ XDG_TOPLEVEL_STATE_RESIZING = 3,
+ /**
+ * the surface is now activated
+ * the surface is now activated
+ *
+ * Client window decorations should be painted as if the window
+ * is active. Do not assume this means that the window actually has
+ * keyboard or pointer focus.
+ */
+ XDG_TOPLEVEL_STATE_ACTIVATED = 4,
+ /**
+ * the surface’s left edge is tiled
+ *
+ * The window is currently in a tiled layout and the left edge is
+ * considered to be adjacent to another part of the tiling grid.
+ *
+ * The client should draw without shadow or other decoration
+ * outside of the window geometry on the left edge.
+ * @since 2
+ */
+ XDG_TOPLEVEL_STATE_TILED_LEFT = 5,
+ /**
+ * the surface’s right edge is tiled
+ *
+ * The window is currently in a tiled layout and the right edge
+ * is considered to be adjacent to another part of the tiling grid.
+ *
+ * The client should draw without shadow or other decoration
+ * outside of the window geometry on the right edge.
+ * @since 2
+ */
+ XDG_TOPLEVEL_STATE_TILED_RIGHT = 6,
+ /**
+ * the surface’s top edge is tiled
+ *
+ * The window is currently in a tiled layout and the top edge is
+ * considered to be adjacent to another part of the tiling grid.
+ *
+ * The client should draw without shadow or other decoration
+ * outside of the window geometry on the top edge.
+ * @since 2
+ */
+ XDG_TOPLEVEL_STATE_TILED_TOP = 7,
+ /**
+ * the surface’s bottom edge is tiled
+ *
+ * The window is currently in a tiled layout and the bottom edge
+ * is considered to be adjacent to another part of the tiling grid.
+ *
+ * The client should draw without shadow or other decoration
+ * outside of the window geometry on the bottom edge.
+ * @since 2
+ */
+ XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8,
+ /**
+ * surface repaint is suspended
+ *
+ * The surface is currently not ordinarily being repainted; for
+ * example because its content is occluded by another window, or
+ * its outputs are switched off due to screen locking.
+ * @since 6
+ */
+ XDG_TOPLEVEL_STATE_SUSPENDED = 9,
+ /**
+ * the surface’s left edge is constrained
+ *
+ * The left edge of the window is currently constrained, meaning
+ * it shouldn't attempt to resize from that edge. It can for
+ * example mean it's tiled next to a monitor edge on the
+ * constrained side of the window.
+ * @since 7
+ */
+ XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT = 10,
+ /**
+ * the surface’s right edge is constrained
+ *
+ * The right edge of the window is currently constrained, meaning
+ * it shouldn't attempt to resize from that edge. It can for
+ * example mean it's tiled next to a monitor edge on the
+ * constrained side of the window.
+ * @since 7
+ */
+ XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT = 11,
+ /**
+ * the surface’s top edge is constrained
+ *
+ * The top edge of the window is currently constrained, meaning
+ * it shouldn't attempt to resize from that edge. It can for
+ * example mean it's tiled next to a monitor edge on the
+ * constrained side of the window.
+ * @since 7
+ */
+ XDG_TOPLEVEL_STATE_CONSTRAINED_TOP = 12,
+ /**
+ * the surface’s bottom edge is tiled
+ *
+ * The bottom edge of the window is currently constrained,
+ * meaning it shouldn't attempt to resize from that edge. It can
+ * for example mean it's tiled next to a monitor edge on the
+ * constrained side of the window.
+ * @since 7
+ */
+ XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM = 13,
+};
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION 6
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION 7
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT_SINCE_VERSION 7
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_STATE_CONSTRAINED_TOP_SINCE_VERSION 7
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM_SINCE_VERSION 7
+#endif /* XDG_TOPLEVEL_STATE_ENUM */
+
+#ifndef XDG_TOPLEVEL_WM_CAPABILITIES_ENUM
+#define XDG_TOPLEVEL_WM_CAPABILITIES_ENUM
+enum xdg_toplevel_wm_capabilities {
+ /**
+ * show_window_menu is available
+ */
+ XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1,
+ /**
+ * set_maximized and unset_maximized are available
+ */
+ XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 2,
+ /**
+ * set_fullscreen and unset_fullscreen are available
+ */
+ XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3,
+ /**
+ * set_minimized is available
+ */
+ XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 4,
+};
+#endif /* XDG_TOPLEVEL_WM_CAPABILITIES_ENUM */
+
+/**
+ * @ingroup iface_xdg_toplevel
+ * @struct xdg_toplevel_listener
+ */
+struct xdg_toplevel_listener {
+ /**
+ * suggest a surface change
+ *
+ * This configure event asks the client to resize its toplevel
+ * surface or to change its state. The configured state should not
+ * be applied immediately. See xdg_surface.configure for details.
+ *
+ * The width and height arguments specify a hint to the window
+ * about how its surface should be resized in window geometry
+ * coordinates. See set_window_geometry.
+ *
+ * If the width or height arguments are zero, it means the client
+ * should decide its own window dimension. This may happen when the
+ * compositor needs to configure the state of the surface but
+ * doesn't have any information about any previous or expected
+ * dimension.
+ *
+ * The states listed in the event specify how the width/height
+ * arguments should be interpreted, and possibly how it should be
+ * drawn.
+ *
+ * Clients must send an ack_configure in response to this event.
+ * See xdg_surface.configure and xdg_surface.ack_configure for
+ * details.
+ */
+ void (*configure)(void *data,
+ struct xdg_toplevel *xdg_toplevel,
+ int32_t width,
+ int32_t height,
+ struct wl_array *states);
+ /**
+ * surface wants to be closed
+ *
+ * The close event is sent by the compositor when the user wants
+ * the surface to be closed. This should be equivalent to the user
+ * clicking the close button in client-side decorations, if your
+ * application has any.
+ *
+ * This is only a request that the user intends to close the
+ * window. The client may choose to ignore this request, or show a
+ * dialog to ask the user to save their data, etc.
+ */
+ void (*close)(void *data,
+ struct xdg_toplevel *xdg_toplevel);
+ /**
+ * recommended window geometry bounds
+ *
+ * The configure_bounds event may be sent prior to a
+ * xdg_toplevel.configure event to communicate the bounds a window
+ * geometry size is recommended to constrain to.
+ *
+ * The passed width and height are in surface coordinate space. If
+ * width and height are 0, it means bounds is unknown and
+ * equivalent to as if no configure_bounds event was ever sent for
+ * this surface.
+ *
+ * The bounds can for example correspond to the size of a monitor
+ * excluding any panels or other shell components, so that a
+ * surface isn't created in a way that it cannot fit.
+ *
+ * The bounds may change at any point, and in such a case, a new
+ * xdg_toplevel.configure_bounds will be sent, followed by
+ * xdg_toplevel.configure and xdg_surface.configure.
+ * @since 4
+ */
+ void (*configure_bounds)(void *data,
+ struct xdg_toplevel *xdg_toplevel,
+ int32_t width,
+ int32_t height);
+ /**
+ * compositor capabilities
+ *
+ * This event advertises the capabilities supported by the
+ * compositor. If a capability isn't supported, clients should hide
+ * or disable the UI elements that expose this functionality. For
+ * instance, if the compositor doesn't advertise support for
+ * minimized toplevels, a button triggering the set_minimized
+ * request should not be displayed.
+ *
+ * The compositor will ignore requests it doesn't support. For
+ * instance, a compositor which doesn't advertise support for
+ * minimized will ignore set_minimized requests.
+ *
+ * Compositors must send this event once before the first
+ * xdg_surface.configure event. When the capabilities change,
+ * compositors must send this event again and then send an
+ * xdg_surface.configure event.
+ *
+ * The configured state should not be applied immediately. See
+ * xdg_surface.configure for details.
+ *
+ * The capabilities are sent as an array of 32-bit unsigned
+ * integers in native endianness.
+ * @param capabilities array of 32-bit capabilities
+ * @since 5
+ */
+ void (*wm_capabilities)(void *data,
+ struct xdg_toplevel *xdg_toplevel,
+ struct wl_array *capabilities);
+};
+
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+static inline int
+xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel,
+ const struct xdg_toplevel_listener *listener, void *data)
+{
+ return wl_proxy_add_listener((struct wl_proxy *) xdg_toplevel,
+ (void (**)(void)) listener, data);
+}
+
+#define XDG_TOPLEVEL_DESTROY 0
+#define XDG_TOPLEVEL_SET_PARENT 1
+#define XDG_TOPLEVEL_SET_TITLE 2
+#define XDG_TOPLEVEL_SET_APP_ID 3
+#define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4
+#define XDG_TOPLEVEL_MOVE 5
+#define XDG_TOPLEVEL_RESIZE 6
+#define XDG_TOPLEVEL_SET_MAX_SIZE 7
+#define XDG_TOPLEVEL_SET_MIN_SIZE 8
+#define XDG_TOPLEVEL_SET_MAXIMIZED 9
+#define XDG_TOPLEVEL_UNSET_MAXIMIZED 10
+#define XDG_TOPLEVEL_SET_FULLSCREEN 11
+#define XDG_TOPLEVEL_UNSET_FULLSCREEN 12
+#define XDG_TOPLEVEL_SET_MINIMIZED 13
+
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION 4
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION 5
+
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_toplevel
+ */
+#define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1
+
+/** @ingroup iface_xdg_toplevel */
+static inline void
+xdg_toplevel_set_user_data(struct xdg_toplevel *xdg_toplevel, void *user_data)
+{
+ wl_proxy_set_user_data((struct wl_proxy *) xdg_toplevel, user_data);
+}
+
+/** @ingroup iface_xdg_toplevel */
+static inline void *
+xdg_toplevel_get_user_data(struct xdg_toplevel *xdg_toplevel)
+{
+ return wl_proxy_get_user_data((struct wl_proxy *) xdg_toplevel);
+}
+
+static inline uint32_t
+xdg_toplevel_get_version(struct xdg_toplevel *xdg_toplevel)
+{
+ return wl_proxy_get_version((struct wl_proxy *) xdg_toplevel);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * This request destroys the role surface and unmaps the surface;
+ * see "Unmapping" behavior in interface section for details.
+ */
+static inline void
+xdg_toplevel_destroy(struct xdg_toplevel *xdg_toplevel)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), WL_MARSHAL_FLAG_DESTROY);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Set the "parent" of this surface. This surface should be stacked
+ * above the parent surface and all other ancestor surfaces.
+ *
+ * Parent surfaces should be set on dialogs, toolboxes, or other
+ * "auxiliary" surfaces, so that the parent is raised when the dialog
+ * is raised.
+ *
+ * Setting a null parent for a child surface unsets its parent. Setting
+ * a null parent for a surface which currently has no parent is a no-op.
+ *
+ * Only mapped surfaces can have child surfaces. Setting a parent which
+ * is not mapped is equivalent to setting a null parent. If a surface
+ * becomes unmapped, its children's parent is set to the parent of
+ * the now-unmapped surface. If the now-unmapped surface has no parent,
+ * its children's parent is unset. If the now-unmapped surface becomes
+ * mapped again, its parent-child relationship is not restored.
+ *
+ * The parent toplevel must not be one of the child toplevel's
+ * descendants, and the parent must be different from the child toplevel,
+ * otherwise the invalid_parent protocol error is raised.
+ */
+static inline void
+xdg_toplevel_set_parent(struct xdg_toplevel *xdg_toplevel, struct xdg_toplevel *parent)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, parent);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Set a short title for the surface.
+ *
+ * This string may be used to identify the surface in a task bar,
+ * window list, or other user interface elements provided by the
+ * compositor.
+ *
+ * The string must be encoded in UTF-8.
+ */
+static inline void
+xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, title);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Set an application identifier for the surface.
+ *
+ * The app ID identifies the general class of applications to which
+ * the surface belongs. The compositor can use this to group multiple
+ * surfaces together, or to determine how to launch a new application.
+ *
+ * For D-Bus activatable applications, the app ID is used as the D-Bus
+ * service name.
+ *
+ * The compositor shell will try to group application surfaces together
+ * by their app ID. As a best practice, it is suggested to select app
+ * ID's that match the basename of the application's .desktop file.
+ * For example, "org.freedesktop.FooViewer" where the .desktop file is
+ * "org.freedesktop.FooViewer.desktop".
+ *
+ * Like other properties, a set_app_id request can be sent after the
+ * xdg_toplevel has been mapped to update the property.
+ *
+ * See the desktop-entry specification [0] for more details on
+ * application identifiers and how they relate to well-known D-Bus
+ * names and .desktop files.
+ *
+ * [0] https://standards.freedesktop.org/desktop-entry-spec/
+ */
+static inline void
+xdg_toplevel_set_app_id(struct xdg_toplevel *xdg_toplevel, const char *app_id)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, app_id);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Clients implementing client-side decorations might want to show
+ * a context menu when right-clicking on the decorations, giving the
+ * user a menu that they can use to maximize or minimize the window.
+ *
+ * This request asks the compositor to pop up such a window menu at
+ * the given position, relative to the local surface coordinates of
+ * the parent surface. There are no guarantees as to what menu items
+ * the window menu contains, or even if a window menu will be drawn
+ * at all.
+ *
+ * This request must be used in response to some sort of user action
+ * like a button press, key press, or touch down event.
+ */
+static inline void
+xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, x, y);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Start an interactive, user-driven move of the surface.
+ *
+ * This request must be used in response to some sort of user action
+ * like a button press, key press, or touch down event. The passed
+ * serial is used to determine the type of interactive move (touch,
+ * pointer, etc).
+ *
+ * The server may ignore move requests depending on the state of
+ * the surface (e.g. fullscreen or maximized), or if the passed serial
+ * is no longer valid.
+ *
+ * If triggered, the surface will lose the focus of the device
+ * (wl_pointer, wl_touch, etc) used for the move. It is up to the
+ * compositor to visually indicate that the move is taking place, such as
+ * updating a pointer cursor, during the move. There is no guarantee
+ * that the device focus will return when the move is completed.
+ */
+static inline void
+xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Start a user-driven, interactive resize of the surface.
+ *
+ * This request must be used in response to some sort of user action
+ * like a button press, key press, or touch down event. The passed
+ * serial is used to determine the type of interactive resize (touch,
+ * pointer, etc).
+ *
+ * The server may ignore resize requests depending on the state of
+ * the surface (e.g. fullscreen or maximized).
+ *
+ * If triggered, the client will receive configure events with the
+ * "resize" state enum value and the expected sizes. See the "resize"
+ * enum value for more details about what is required. The client
+ * must also acknowledge configure events using "ack_configure". After
+ * the resize is completed, the client will receive another "configure"
+ * event without the resize state.
+ *
+ * If triggered, the surface also will lose the focus of the device
+ * (wl_pointer, wl_touch, etc) used for the resize. It is up to the
+ * compositor to visually indicate that the resize is taking place,
+ * such as updating a pointer cursor, during the resize. There is no
+ * guarantee that the device focus will return when the resize is
+ * completed.
+ *
+ * The edges parameter specifies how the surface should be resized, and
+ * is one of the values of the resize_edge enum. Values not matching
+ * a variant of the enum will cause the invalid_resize_edge protocol error.
+ * The compositor may use this information to update the surface position
+ * for example when dragging the top left corner. The compositor may also
+ * use this information to adapt its behavior, e.g. choose an appropriate
+ * cursor image.
+ */
+static inline void
+xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, uint32_t edges)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, edges);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Set a maximum size for the window.
+ *
+ * The client can specify a maximum size so that the compositor does
+ * not try to configure the window beyond this size.
+ *
+ * The width and height arguments are in window geometry coordinates.
+ * See xdg_surface.set_window_geometry.
+ *
+ * Values set in this way are double-buffered, see wl_surface.commit.
+ *
+ * The compositor can use this information to allow or disallow
+ * different states like maximize or fullscreen and draw accurate
+ * animations.
+ *
+ * Similarly, a tiling window manager may use this information to
+ * place and resize client windows in a more effective way.
+ *
+ * The client should not rely on the compositor to obey the maximum
+ * size. The compositor may decide to ignore the values set by the
+ * client and request a larger size.
+ *
+ * If never set, or a value of zero in the request, means that the
+ * client has no expected maximum size in the given dimension.
+ * As a result, a client wishing to reset the maximum size
+ * to an unspecified state can use zero for width and height in the
+ * request.
+ *
+ * Requesting a maximum size to be smaller than the minimum size of
+ * a surface is illegal and will result in an invalid_size error.
+ *
+ * The width and height must be greater than or equal to zero. Using
+ * strictly negative values for width or height will result in a
+ * invalid_size error.
+ */
+static inline void
+xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Set a minimum size for the window.
+ *
+ * The client can specify a minimum size so that the compositor does
+ * not try to configure the window below this size.
+ *
+ * The width and height arguments are in window geometry coordinates.
+ * See xdg_surface.set_window_geometry.
+ *
+ * Values set in this way are double-buffered, see wl_surface.commit.
+ *
+ * The compositor can use this information to allow or disallow
+ * different states like maximize or fullscreen and draw accurate
+ * animations.
+ *
+ * Similarly, a tiling window manager may use this information to
+ * place and resize client windows in a more effective way.
+ *
+ * The client should not rely on the compositor to obey the minimum
+ * size. The compositor may decide to ignore the values set by the
+ * client and request a smaller size.
+ *
+ * If never set, or a value of zero in the request, means that the
+ * client has no expected minimum size in the given dimension.
+ * As a result, a client wishing to reset the minimum size
+ * to an unspecified state can use zero for width and height in the
+ * request.
+ *
+ * Requesting a minimum size to be larger than the maximum size of
+ * a surface is illegal and will result in an invalid_size error.
+ *
+ * The width and height must be greater than or equal to zero. Using
+ * strictly negative values for width and height will result in a
+ * invalid_size error.
+ */
+static inline void
+xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Maximize the surface.
+ *
+ * After requesting that the surface should be maximized, the compositor
+ * will respond by emitting a configure event. Whether this configure
+ * actually sets the window maximized is subject to compositor policies.
+ * The client must then update its content, drawing in the configured
+ * state. The client must also acknowledge the configure when committing
+ * the new content (see ack_configure).
+ *
+ * It is up to the compositor to decide how and where to maximize the
+ * surface, for example which output and what region of the screen should
+ * be used.
+ *
+ * If the surface was already maximized, the compositor will still emit
+ * a configure event with the "maximized" state.
+ *
+ * If the surface is in a fullscreen state, this request has no direct
+ * effect. It may alter the state the surface is returned to when
+ * unmaximized unless overridden by the compositor.
+ */
+static inline void
+xdg_toplevel_set_maximized(struct xdg_toplevel *xdg_toplevel)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Unmaximize the surface.
+ *
+ * After requesting that the surface should be unmaximized, the compositor
+ * will respond by emitting a configure event. Whether this actually
+ * un-maximizes the window is subject to compositor policies.
+ * If available and applicable, the compositor will include the window
+ * geometry dimensions the window had prior to being maximized in the
+ * configure event. The client must then update its content, drawing it in
+ * the configured state. The client must also acknowledge the configure
+ * when committing the new content (see ack_configure).
+ *
+ * It is up to the compositor to position the surface after it was
+ * unmaximized; usually the position the surface had before maximizing, if
+ * applicable.
+ *
+ * If the surface was already not maximized, the compositor will still
+ * emit a configure event without the "maximized" state.
+ *
+ * If the surface is in a fullscreen state, this request has no direct
+ * effect. It may alter the state the surface is returned to when
+ * unmaximized unless overridden by the compositor.
+ */
+static inline void
+xdg_toplevel_unset_maximized(struct xdg_toplevel *xdg_toplevel)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Make the surface fullscreen.
+ *
+ * After requesting that the surface should be fullscreened, the
+ * compositor will respond by emitting a configure event. Whether the
+ * client is actually put into a fullscreen state is subject to compositor
+ * policies. The client must also acknowledge the configure when
+ * committing the new content (see ack_configure).
+ *
+ * The output passed by the request indicates the client's preference as
+ * to which display it should be set fullscreen on. If this value is NULL,
+ * it's up to the compositor to choose which display will be used to map
+ * this surface.
+ *
+ * If the surface doesn't cover the whole output, the compositor will
+ * position the surface in the center of the output and compensate with
+ * with border fill covering the rest of the output. The content of the
+ * border fill is undefined, but should be assumed to be in some way that
+ * attempts to blend into the surrounding area (e.g. solid black).
+ *
+ * If the fullscreened surface is not opaque, the compositor must make
+ * sure that other screen content not part of the same surface tree (made
+ * up of subsurfaces, popups or similarly coupled surfaces) are not
+ * visible below the fullscreened surface.
+ */
+static inline void
+xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, output);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Make the surface no longer fullscreen.
+ *
+ * After requesting that the surface should be unfullscreened, the
+ * compositor will respond by emitting a configure event.
+ * Whether this actually removes the fullscreen state of the client is
+ * subject to compositor policies.
+ *
+ * Making a surface unfullscreen sets states for the surface based on the following:
+ * * the state(s) it may have had before becoming fullscreen
+ * * any state(s) decided by the compositor
+ * * any state(s) requested by the client while the surface was fullscreen
+ *
+ * The compositor may include the previous window geometry dimensions in
+ * the configure event, if applicable.
+ *
+ * The client must also acknowledge the configure when committing the new
+ * content (see ack_configure).
+ */
+static inline void
+xdg_toplevel_unset_fullscreen(struct xdg_toplevel *xdg_toplevel)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0);
+}
+
+/**
+ * @ingroup iface_xdg_toplevel
+ *
+ * Request that the compositor minimize your surface. There is no
+ * way to know if the surface is currently minimized, nor is there
+ * any way to unset minimization on this surface.
+ *
+ * If you are looking to throttle redrawing when minimized, please
+ * instead use the wl_surface.frame event for this, as this will
+ * also work with live previews on windows in Alt-Tab, Expose or
+ * similar compositor features.
+ */
+static inline void
+xdg_toplevel_set_minimized(struct xdg_toplevel *xdg_toplevel)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel,
+ XDG_TOPLEVEL_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0);
+}
+
+#ifndef XDG_POPUP_ERROR_ENUM
+#define XDG_POPUP_ERROR_ENUM
+enum xdg_popup_error {
+ /**
+ * tried to grab after being mapped
+ */
+ XDG_POPUP_ERROR_INVALID_GRAB = 0,
+};
+#endif /* XDG_POPUP_ERROR_ENUM */
+
+/**
+ * @ingroup iface_xdg_popup
+ * @struct xdg_popup_listener
+ */
+struct xdg_popup_listener {
+ /**
+ * configure the popup surface
+ *
+ * This event asks the popup surface to configure itself given
+ * the configuration. The configured state should not be applied
+ * immediately. See xdg_surface.configure for details.
+ *
+ * The x and y arguments represent the position the popup was
+ * placed at given the xdg_positioner rule, relative to the upper
+ * left corner of the window geometry of the parent surface.
+ *
+ * For version 2 or older, the configure event for an xdg_popup is
+ * only ever sent once for the initial configuration. Starting with
+ * version 3, it may be sent again if the popup is setup with an
+ * xdg_positioner with set_reactive requested, or in response to
+ * xdg_popup.reposition requests.
+ * @param x x position relative to parent surface window geometry
+ * @param y y position relative to parent surface window geometry
+ * @param width window geometry width
+ * @param height window geometry height
+ */
+ void (*configure)(void *data,
+ struct xdg_popup *xdg_popup,
+ int32_t x,
+ int32_t y,
+ int32_t width,
+ int32_t height);
+ /**
+ * popup interaction is done
+ *
+ * The popup_done event is sent out when a popup is dismissed by
+ * the compositor. The client should destroy the xdg_popup object
+ * at this point.
+ */
+ void (*popup_done)(void *data,
+ struct xdg_popup *xdg_popup);
+ /**
+ * signal the completion of a repositioned request
+ *
+ * The repositioned event is sent as part of a popup
+ * configuration sequence, together with xdg_popup.configure and
+ * lastly xdg_surface.configure to notify the completion of a
+ * reposition request.
+ *
+ * The repositioned event is to notify about the completion of a
+ * xdg_popup.reposition request. The token argument is the token
+ * passed in the xdg_popup.reposition request.
+ *
+ * Immediately after this event is emitted, xdg_popup.configure and
+ * xdg_surface.configure will be sent with the updated size and
+ * position, as well as a new configure serial.
+ *
+ * The client should optionally update the content of the popup,
+ * but must acknowledge the new popup configuration for the new
+ * position to take effect. See xdg_surface.ack_configure for
+ * details.
+ * @param token reposition request token
+ * @since 3
+ */
+ void (*repositioned)(void *data,
+ struct xdg_popup *xdg_popup,
+ uint32_t token);
+};
+
+/**
+ * @ingroup iface_xdg_popup
+ */
+static inline int
+xdg_popup_add_listener(struct xdg_popup *xdg_popup,
+ const struct xdg_popup_listener *listener, void *data)
+{
+ return wl_proxy_add_listener((struct wl_proxy *) xdg_popup,
+ (void (**)(void)) listener, data);
+}
+
+#define XDG_POPUP_DESTROY 0
+#define XDG_POPUP_GRAB 1
+#define XDG_POPUP_REPOSITION 2
+
+/**
+ * @ingroup iface_xdg_popup
+ */
+#define XDG_POPUP_CONFIGURE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_popup
+ */
+#define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_popup
+ */
+#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3
+
+/**
+ * @ingroup iface_xdg_popup
+ */
+#define XDG_POPUP_DESTROY_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_popup
+ */
+#define XDG_POPUP_GRAB_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_popup
+ */
+#define XDG_POPUP_REPOSITION_SINCE_VERSION 3
+
+/** @ingroup iface_xdg_popup */
+static inline void
+xdg_popup_set_user_data(struct xdg_popup *xdg_popup, void *user_data)
+{
+ wl_proxy_set_user_data((struct wl_proxy *) xdg_popup, user_data);
+}
+
+/** @ingroup iface_xdg_popup */
+static inline void *
+xdg_popup_get_user_data(struct xdg_popup *xdg_popup)
+{
+ return wl_proxy_get_user_data((struct wl_proxy *) xdg_popup);
+}
+
+static inline uint32_t
+xdg_popup_get_version(struct xdg_popup *xdg_popup)
+{
+ return wl_proxy_get_version((struct wl_proxy *) xdg_popup);
+}
+
+/**
+ * @ingroup iface_xdg_popup
+ *
+ * This destroys the popup. Explicitly destroying the xdg_popup
+ * object will also dismiss the popup, and unmap the surface.
+ *
+ * If this xdg_popup is not the "topmost" popup, the
+ * xdg_wm_base.not_the_topmost_popup protocol error will be sent.
+ */
+static inline void
+xdg_popup_destroy(struct xdg_popup *xdg_popup)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup,
+ XDG_POPUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), WL_MARSHAL_FLAG_DESTROY);
+}
+
+/**
+ * @ingroup iface_xdg_popup
+ *
+ * This request makes the created popup take an explicit grab. An explicit
+ * grab will be dismissed when the user dismisses the popup, or when the
+ * client destroys the xdg_popup. This can be done by the user clicking
+ * outside the surface, using the keyboard, or even locking the screen
+ * through closing the lid or a timeout.
+ *
+ * If the compositor denies the grab, the popup will be immediately
+ * dismissed.
+ *
+ * This request must be used in response to some sort of user action like a
+ * button press, key press, or touch down event. The serial number of the
+ * event should be passed as 'serial'.
+ *
+ * The parent of a grabbing popup must either be an xdg_toplevel surface or
+ * another xdg_popup with an explicit grab. If the parent is another
+ * xdg_popup it means that the popups are nested, with this popup now being
+ * the topmost popup.
+ *
+ * Nested popups must be destroyed in the reverse order they were created
+ * in, e.g. the only popup you are allowed to destroy at all times is the
+ * topmost one.
+ *
+ * When compositors choose to dismiss a popup, they may dismiss every
+ * nested grabbing popup as well. When a compositor dismisses popups, it
+ * will follow the same dismissing order as required from the client.
+ *
+ * If the topmost grabbing popup is destroyed, the grab will be returned to
+ * the parent of the popup, if that parent previously had an explicit grab.
+ *
+ * If the parent is a grabbing popup which has already been dismissed, this
+ * popup will be immediately dismissed. If the parent is a popup that did
+ * not take an explicit grab, an error will be raised.
+ *
+ * During a popup grab, the client owning the grab will receive pointer
+ * and touch events for all their surfaces as normal (similar to an
+ * "owner-events" grab in X11 parlance), while the top most grabbing popup
+ * will always have keyboard focus.
+ */
+static inline void
+xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t serial)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup,
+ XDG_POPUP_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, seat, serial);
+}
+
+/**
+ * @ingroup iface_xdg_popup
+ *
+ * Reposition an already-mapped popup. The popup will be placed given the
+ * details in the passed xdg_positioner object, and a
+ * xdg_popup.repositioned followed by xdg_popup.configure and
+ * xdg_surface.configure will be emitted in response. Any parameters set
+ * by the previous positioner will be discarded.
+ *
+ * The passed token will be sent in the corresponding
+ * xdg_popup.repositioned event. The new popup position will not take
+ * effect until the corresponding configure event is acknowledged by the
+ * client. See xdg_popup.repositioned for details. The token itself is
+ * opaque, and has no other special meaning.
+ *
+ * If multiple reposition requests are sent, the compositor may skip all
+ * but the last one.
+ *
+ * If the popup is repositioned in response to a configure event for its
+ * parent, the client should send an xdg_positioner.set_parent_configure
+ * and possibly an xdg_positioner.set_parent_size request to allow the
+ * compositor to properly constrain the popup.
+ *
+ * If the popup is repositioned together with a parent that is being
+ * resized, but not in response to a configure event, the client should
+ * send an xdg_positioner.set_parent_size request.
+ */
+static inline void
+xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token)
+{
+ wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup,
+ XDG_POPUP_REPOSITION, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, positioner, token);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/* Generated by wayland-scanner 1.23.1 */
+
+/*
+ * Copyright © 2008-2013 Kristian Høgsberg
+ * Copyright © 2013 Rafael Antognolli
+ * Copyright © 2013 Jasper St. Pierre
+ * Copyright © 2010-2013 Intel Corporation
+ * Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ * Copyright © 2015-2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "wayland-util.h"
+
+#ifndef __has_attribute
+# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
+#endif
+
+#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
+#define WL_PRIVATE __attribute__ ((visibility("hidden")))
+#else
+#define WL_PRIVATE
+#endif
+
+extern const struct wl_interface wl_output_interface;
+extern const struct wl_interface wl_seat_interface;
+extern const struct wl_interface wl_surface_interface;
+extern const struct wl_interface xdg_popup_interface;
+extern const struct wl_interface xdg_positioner_interface;
+extern const struct wl_interface xdg_surface_interface;
+extern const struct wl_interface xdg_toplevel_interface;
+
+static const struct wl_interface *xdg_shell_types[] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &xdg_positioner_interface,
+ &xdg_surface_interface,
+ &wl_surface_interface,
+ &xdg_toplevel_interface,
+ &xdg_popup_interface,
+ &xdg_surface_interface,
+ &xdg_positioner_interface,
+ &xdg_toplevel_interface,
+ &wl_seat_interface,
+ NULL,
+ NULL,
+ NULL,
+ &wl_seat_interface,
+ NULL,
+ &wl_seat_interface,
+ NULL,
+ NULL,
+ &wl_output_interface,
+ &wl_seat_interface,
+ NULL,
+ &xdg_positioner_interface,
+ NULL,
+};
+
+static const struct wl_message xdg_wm_base_requests[] = {
+ { "destroy", "", xdg_shell_types + 0 },
+ { "create_positioner", "n", xdg_shell_types + 4 },
+ { "get_xdg_surface", "no", xdg_shell_types + 5 },
+ { "pong", "u", xdg_shell_types + 0 },
+};
+
+static const struct wl_message xdg_wm_base_events[] = {
+ { "ping", "u", xdg_shell_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface xdg_wm_base_interface = {
+ "xdg_wm_base", 7,
+ 4, xdg_wm_base_requests,
+ 1, xdg_wm_base_events,
+};
+
+static const struct wl_message xdg_positioner_requests[] = {
+ { "destroy", "", xdg_shell_types + 0 },
+ { "set_size", "ii", xdg_shell_types + 0 },
+ { "set_anchor_rect", "iiii", xdg_shell_types + 0 },
+ { "set_anchor", "u", xdg_shell_types + 0 },
+ { "set_gravity", "u", xdg_shell_types + 0 },
+ { "set_constraint_adjustment", "u", xdg_shell_types + 0 },
+ { "set_offset", "ii", xdg_shell_types + 0 },
+ { "set_reactive", "3", xdg_shell_types + 0 },
+ { "set_parent_size", "3ii", xdg_shell_types + 0 },
+ { "set_parent_configure", "3u", xdg_shell_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface xdg_positioner_interface = {
+ "xdg_positioner", 7,
+ 10, xdg_positioner_requests,
+ 0, NULL,
+};
+
+static const struct wl_message xdg_surface_requests[] = {
+ { "destroy", "", xdg_shell_types + 0 },
+ { "get_toplevel", "n", xdg_shell_types + 7 },
+ { "get_popup", "n?oo", xdg_shell_types + 8 },
+ { "set_window_geometry", "iiii", xdg_shell_types + 0 },
+ { "ack_configure", "u", xdg_shell_types + 0 },
+};
+
+static const struct wl_message xdg_surface_events[] = {
+ { "configure", "u", xdg_shell_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface xdg_surface_interface = {
+ "xdg_surface", 7,
+ 5, xdg_surface_requests,
+ 1, xdg_surface_events,
+};
+
+static const struct wl_message xdg_toplevel_requests[] = {
+ { "destroy", "", xdg_shell_types + 0 },
+ { "set_parent", "?o", xdg_shell_types + 11 },
+ { "set_title", "s", xdg_shell_types + 0 },
+ { "set_app_id", "s", xdg_shell_types + 0 },
+ { "show_window_menu", "ouii", xdg_shell_types + 12 },
+ { "move", "ou", xdg_shell_types + 16 },
+ { "resize", "ouu", xdg_shell_types + 18 },
+ { "set_max_size", "ii", xdg_shell_types + 0 },
+ { "set_min_size", "ii", xdg_shell_types + 0 },
+ { "set_maximized", "", xdg_shell_types + 0 },
+ { "unset_maximized", "", xdg_shell_types + 0 },
+ { "set_fullscreen", "?o", xdg_shell_types + 21 },
+ { "unset_fullscreen", "", xdg_shell_types + 0 },
+ { "set_minimized", "", xdg_shell_types + 0 },
+};
+
+static const struct wl_message xdg_toplevel_events[] = {
+ { "configure", "iia", xdg_shell_types + 0 },
+ { "close", "", xdg_shell_types + 0 },
+ { "configure_bounds", "4ii", xdg_shell_types + 0 },
+ { "wm_capabilities", "5a", xdg_shell_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface xdg_toplevel_interface = {
+ "xdg_toplevel", 7,
+ 14, xdg_toplevel_requests,
+ 4, xdg_toplevel_events,
+};
+
+static const struct wl_message xdg_popup_requests[] = {
+ { "destroy", "", xdg_shell_types + 0 },
+ { "grab", "ou", xdg_shell_types + 22 },
+ { "reposition", "3ou", xdg_shell_types + 24 },
+};
+
+static const struct wl_message xdg_popup_events[] = {
+ { "configure", "iiii", xdg_shell_types + 0 },
+ { "popup_done", "", xdg_shell_types + 0 },
+ { "repositioned", "3u", xdg_shell_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface xdg_popup_interface = {
+ "xdg_popup", 7,
+ 3, xdg_popup_requests,
+ 3, xdg_popup_events,
+};
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="xdg_shell">
+
+ <copyright>
+ Copyright © 2008-2013 Kristian Høgsberg
+ Copyright © 2013 Rafael Antognolli
+ Copyright © 2013 Jasper St. Pierre
+ Copyright © 2010-2013 Intel Corporation
+ Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ Copyright © 2015-2017 Red Hat Inc.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="xdg_wm_base" version="7">
+ <description summary="create desktop-style surfaces">
+ The xdg_wm_base interface is exposed as a global object enabling clients
+ to turn their wl_surfaces into windows in a desktop environment. It
+ defines the basic functionality needed for clients and the compositor to
+ create windows that can be dragged, resized, maximized, etc, as well as
+ creating transient windows such as popup menus.
+ </description>
+
+ <enum name="error">
+ <entry name="role" value="0" summary="given wl_surface has another role"/>
+ <entry name="defunct_surfaces" value="1"
+ summary="xdg_wm_base was destroyed before children"/>
+ <entry name="not_the_topmost_popup" value="2"
+ summary="the client tried to map or destroy a non-topmost popup"/>
+ <entry name="invalid_popup_parent" value="3"
+ summary="the client specified an invalid popup parent surface"/>
+ <entry name="invalid_surface_state" value="4"
+ summary="the client provided an invalid surface state"/>
+ <entry name="invalid_positioner" value="5"
+ summary="the client provided an invalid positioner"/>
+ <entry name="unresponsive" value="6"
+ summary="the client didn’t respond to a ping event in time"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy xdg_wm_base">
+ Destroy this xdg_wm_base object.
+
+ Destroying a bound xdg_wm_base object while there are surfaces
+ still alive created by this xdg_wm_base object instance is illegal
+ and will result in a defunct_surfaces error.
+ </description>
+ </request>
+
+ <request name="create_positioner">
+ <description summary="create a positioner object">
+ Create a positioner object. A positioner object is used to position
+ surfaces relative to some parent surface. See the interface description
+ and xdg_surface.get_popup for details.
+ </description>
+ <arg name="id" type="new_id" interface="xdg_positioner"/>
+ </request>
+
+ <request name="get_xdg_surface">
+ <description summary="create a shell surface from a surface">
+ This creates an xdg_surface for the given surface. While xdg_surface
+ itself is not a role, the corresponding surface may only be assigned
+ a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is
+ illegal to create an xdg_surface for a wl_surface which already has an
+ assigned role and this will result in a role error.
+
+ This creates an xdg_surface for the given surface. An xdg_surface is
+ used as basis to define a role to a given surface, such as xdg_toplevel
+ or xdg_popup. It also manages functionality shared between xdg_surface
+ based surface roles.
+
+ See the documentation of xdg_surface for more details about what an
+ xdg_surface is and how it is used.
+ </description>
+ <arg name="id" type="new_id" interface="xdg_surface"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="pong">
+ <description summary="respond to a ping event">
+ A client must respond to a ping event with a pong request or
+ the client may be deemed unresponsive. See xdg_wm_base.ping
+ and xdg_wm_base.error.unresponsive.
+ </description>
+ <arg name="serial" type="uint" summary="serial of the ping event"/>
+ </request>
+
+ <event name="ping">
+ <description summary="check if the client is alive">
+ The ping event asks the client if it's still alive. Pass the
+ serial specified in the event back to the compositor by sending
+ a "pong" request back with the specified serial. See xdg_wm_base.pong.
+
+ Compositors can use this to determine if the client is still
+ alive. It's unspecified what will happen if the client doesn't
+ respond to the ping request, or in what timeframe. Clients should
+ try to respond in a reasonable amount of time. The “unresponsive”
+ error is provided for compositors that wish to disconnect unresponsive
+ clients.
+
+ A compositor is free to ping in any way it wants, but a client must
+ always respond to any xdg_wm_base object it created.
+ </description>
+ <arg name="serial" type="uint" summary="pass this to the pong request"/>
+ </event>
+ </interface>
+
+ <interface name="xdg_positioner" version="7">
+ <description summary="child surface positioner">
+ The xdg_positioner provides a collection of rules for the placement of a
+ child surface relative to a parent surface. Rules can be defined to ensure
+ the child surface remains within the visible area's borders, and to
+ specify how the child surface changes its position, such as sliding along
+ an axis, or flipping around a rectangle. These positioner-created rules are
+ constrained by the requirement that a child surface must intersect with or
+ be at least partially adjacent to its parent surface.
+
+ See the various requests for details about possible rules.
+
+ At the time of the request, the compositor makes a copy of the rules
+ specified by the xdg_positioner. Thus, after the request is complete the
+ xdg_positioner object can be destroyed or reused; further changes to the
+ object will have no effect on previous usages.
+
+ For an xdg_positioner object to be considered complete, it must have a
+ non-zero size set by set_size, and a non-zero anchor rectangle set by
+ set_anchor_rect. Passing an incomplete xdg_positioner object when
+ positioning a surface raises an invalid_positioner error.
+ </description>
+
+ <enum name="error">
+ <entry name="invalid_input" value="0" summary="invalid input provided"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the xdg_positioner object">
+ Notify the compositor that the xdg_positioner will no longer be used.
+ </description>
+ </request>
+
+ <request name="set_size">
+ <description summary="set the size of the to-be positioned rectangle">
+ Set the size of the surface that is to be positioned with the positioner
+ object. The size is in surface-local coordinates and corresponds to the
+ window geometry. See xdg_surface.set_window_geometry.
+
+ If a zero or negative size is set the invalid_input error is raised.
+ </description>
+ <arg name="width" type="int" summary="width of positioned rectangle"/>
+ <arg name="height" type="int" summary="height of positioned rectangle"/>
+ </request>
+
+ <request name="set_anchor_rect">
+ <description summary="set the anchor rectangle within the parent surface">
+ Specify the anchor rectangle within the parent surface that the child
+ surface will be placed relative to. The rectangle is relative to the
+ window geometry as defined by xdg_surface.set_window_geometry of the
+ parent surface.
+
+ When the xdg_positioner object is used to position a child surface, the
+ anchor rectangle may not extend outside the window geometry of the
+ positioned child's parent surface.
+
+ If a negative size is set the invalid_input error is raised.
+ </description>
+ <arg name="x" type="int" summary="x position of anchor rectangle"/>
+ <arg name="y" type="int" summary="y position of anchor rectangle"/>
+ <arg name="width" type="int" summary="width of anchor rectangle"/>
+ <arg name="height" type="int" summary="height of anchor rectangle"/>
+ </request>
+
+ <enum name="anchor">
+ <entry name="none" value="0"/>
+ <entry name="top" value="1"/>
+ <entry name="bottom" value="2"/>
+ <entry name="left" value="3"/>
+ <entry name="right" value="4"/>
+ <entry name="top_left" value="5"/>
+ <entry name="bottom_left" value="6"/>
+ <entry name="top_right" value="7"/>
+ <entry name="bottom_right" value="8"/>
+ </enum>
+
+ <request name="set_anchor">
+ <description summary="set anchor rectangle anchor">
+ Defines the anchor point for the anchor rectangle. The specified anchor
+ is used derive an anchor point that the child surface will be
+ positioned relative to. If a corner anchor is set (e.g. 'top_left' or
+ 'bottom_right'), the anchor point will be at the specified corner;
+ otherwise, the derived anchor point will be centered on the specified
+ edge, or in the center of the anchor rectangle if no edge is specified.
+ </description>
+ <arg name="anchor" type="uint" enum="anchor"
+ summary="anchor"/>
+ </request>
+
+ <enum name="gravity">
+ <entry name="none" value="0"/>
+ <entry name="top" value="1"/>
+ <entry name="bottom" value="2"/>
+ <entry name="left" value="3"/>
+ <entry name="right" value="4"/>
+ <entry name="top_left" value="5"/>
+ <entry name="bottom_left" value="6"/>
+ <entry name="top_right" value="7"/>
+ <entry name="bottom_right" value="8"/>
+ </enum>
+
+ <request name="set_gravity">
+ <description summary="set child surface gravity">
+ Defines in what direction a surface should be positioned, relative to
+ the anchor point of the parent surface. If a corner gravity is
+ specified (e.g. 'bottom_right' or 'top_left'), then the child surface
+ will be placed towards the specified gravity; otherwise, the child
+ surface will be centered over the anchor point on any axis that had no
+ gravity specified. If the gravity is not in the ‘gravity’ enum, an
+ invalid_input error is raised.
+ </description>
+ <arg name="gravity" type="uint" enum="gravity"
+ summary="gravity direction"/>
+ </request>
+
+ <enum name="constraint_adjustment" bitfield="true">
+ <description summary="constraint adjustments">
+ The constraint adjustment value define ways the compositor will adjust
+ the position of the surface, if the unadjusted position would result
+ in the surface being partly constrained.
+
+ Whether a surface is considered 'constrained' is left to the compositor
+ to determine. For example, the surface may be partly outside the
+ compositor's defined 'work area', thus necessitating the child surface's
+ position be adjusted until it is entirely inside the work area.
+
+ The adjustments can be combined, according to a defined precedence: 1)
+ Flip, 2) Slide, 3) Resize.
+ </description>
+ <entry name="none" value="0">
+ <description summary="don't move the child surface when constrained">
+ Don't alter the surface position even if it is constrained on some
+ axis, for example partially outside the edge of an output.
+ </description>
+ </entry>
+ <entry name="slide_x" value="1">
+ <description summary="move along the x axis until unconstrained">
+ Slide the surface along the x axis until it is no longer constrained.
+
+ First try to slide towards the direction of the gravity on the x axis
+ until either the edge in the opposite direction of the gravity is
+ unconstrained or the edge in the direction of the gravity is
+ constrained.
+
+ Then try to slide towards the opposite direction of the gravity on the
+ x axis until either the edge in the direction of the gravity is
+ unconstrained or the edge in the opposite direction of the gravity is
+ constrained.
+ </description>
+ </entry>
+ <entry name="slide_y" value="2">
+ <description summary="move along the y axis until unconstrained">
+ Slide the surface along the y axis until it is no longer constrained.
+
+ First try to slide towards the direction of the gravity on the y axis
+ until either the edge in the opposite direction of the gravity is
+ unconstrained or the edge in the direction of the gravity is
+ constrained.
+
+ Then try to slide towards the opposite direction of the gravity on the
+ y axis until either the edge in the direction of the gravity is
+ unconstrained or the edge in the opposite direction of the gravity is
+ constrained.
+ </description>
+ </entry>
+ <entry name="flip_x" value="4">
+ <description summary="invert the anchor and gravity on the x axis">
+ Invert the anchor and gravity on the x axis if the surface is
+ constrained on the x axis. For example, if the left edge of the
+ surface is constrained, the gravity is 'left' and the anchor is
+ 'left', change the gravity to 'right' and the anchor to 'right'.
+
+ If the adjusted position also ends up being constrained, the resulting
+ position of the flip_x adjustment will be the one before the
+ adjustment.
+ </description>
+ </entry>
+ <entry name="flip_y" value="8">
+ <description summary="invert the anchor and gravity on the y axis">
+ Invert the anchor and gravity on the y axis if the surface is
+ constrained on the y axis. For example, if the bottom edge of the
+ surface is constrained, the gravity is 'bottom' and the anchor is
+ 'bottom', change the gravity to 'top' and the anchor to 'top'.
+
+ The adjusted position is calculated given the original anchor
+ rectangle and offset, but with the new flipped anchor and gravity
+ values.
+
+ If the adjusted position also ends up being constrained, the resulting
+ position of the flip_y adjustment will be the one before the
+ adjustment.
+ </description>
+ </entry>
+ <entry name="resize_x" value="16">
+ <description summary="horizontally resize the surface">
+ Resize the surface horizontally so that it is completely
+ unconstrained.
+ </description>
+ </entry>
+ <entry name="resize_y" value="32">
+ <description summary="vertically resize the surface">
+ Resize the surface vertically so that it is completely unconstrained.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_constraint_adjustment">
+ <description summary="set the adjustment to be done when constrained">
+ Specify how the window should be positioned if the originally intended
+ position caused the surface to be constrained, meaning at least
+ partially outside positioning boundaries set by the compositor. The
+ adjustment is set by constructing a bitmask describing the adjustment to
+ be made when the surface is constrained on that axis.
+
+ If no bit for one axis is set, the compositor will assume that the child
+ surface should not change its position on that axis when constrained.
+
+ If more than one bit for one axis is set, the order of how adjustments
+ are applied is specified in the corresponding adjustment descriptions.
+
+ The default adjustment is none.
+ </description>
+ <arg name="constraint_adjustment" type="uint" enum="constraint_adjustment"
+ summary="bit mask of constraint adjustments"/>
+ </request>
+
+ <request name="set_offset">
+ <description summary="set surface position offset">
+ Specify the surface position offset relative to the position of the
+ anchor on the anchor rectangle and the anchor on the surface. For
+ example if the anchor of the anchor rectangle is at (x, y), the surface
+ has the gravity bottom|right, and the offset is (ox, oy), the calculated
+ surface position will be (x + ox, y + oy). The offset position of the
+ surface is the one used for constraint testing. See
+ set_constraint_adjustment.
+
+ An example use case is placing a popup menu on top of a user interface
+ element, while aligning the user interface element of the parent surface
+ with some user interface element placed somewhere in the popup surface.
+ </description>
+ <arg name="x" type="int" summary="surface position x offset"/>
+ <arg name="y" type="int" summary="surface position y offset"/>
+ </request>
+
+ <!-- Version 3 additions -->
+
+ <request name="set_reactive" since="3">
+ <description summary="continuously reconstrain the surface">
+ When set reactive, the surface is reconstrained if the conditions used
+ for constraining changed, e.g. the parent window moved.
+
+ If the conditions changed and the popup was reconstrained, an
+ xdg_popup.configure event is sent with updated geometry, followed by an
+ xdg_surface.configure event.
+ </description>
+ </request>
+
+ <request name="set_parent_size" since="3">
+ <description summary="">
+ Set the parent window geometry the compositor should use when
+ positioning the popup. The compositor may use this information to
+ determine the future state the popup should be constrained using. If
+ this doesn't match the dimension of the parent the popup is eventually
+ positioned against, the behavior is undefined.
+
+ The arguments are given in the surface-local coordinate space.
+ </description>
+ <arg name="parent_width" type="int"
+ summary="future window geometry width of parent"/>
+ <arg name="parent_height" type="int"
+ summary="future window geometry height of parent"/>
+ </request>
+
+ <request name="set_parent_configure" since="3">
+ <description summary="set parent configure this is a response to">
+ Set the serial of an xdg_surface.configure event this positioner will be
+ used in response to. The compositor may use this information together
+ with set_parent_size to determine what future state the popup should be
+ constrained using.
+ </description>
+ <arg name="serial" type="uint"
+ summary="serial of parent configure event"/>
+ </request>
+ </interface>
+
+ <interface name="xdg_surface" version="7">
+ <description summary="desktop user interface surface base interface">
+ An interface that may be implemented by a wl_surface, for
+ implementations that provide a desktop-style user interface.
+
+ It provides a base set of functionality required to construct user
+ interface elements requiring management by the compositor, such as
+ toplevel windows, menus, etc. The types of functionality are split into
+ xdg_surface roles.
+
+ Creating an xdg_surface does not set the role for a wl_surface. In order
+ to map an xdg_surface, the client must create a role-specific object
+ using, e.g., get_toplevel, get_popup. The wl_surface for any given
+ xdg_surface can have at most one role, and may not be assigned any role
+ not based on xdg_surface.
+
+ A role must be assigned before any other requests are made to the
+ xdg_surface object.
+
+ The client must call wl_surface.commit on the corresponding wl_surface
+ for the xdg_surface state to take effect.
+
+ Creating an xdg_surface from a wl_surface which has a buffer attached or
+ committed is a client error, and any attempts by a client to attach or
+ manipulate a buffer prior to the first xdg_surface.configure call must
+ also be treated as errors.
+
+ After creating a role-specific object and setting it up (e.g. by sending
+ the title, app ID, size constraints, parent, etc), the client must
+ perform an initial commit without any buffer attached. The compositor
+ will reply with initial wl_surface state such as
+ wl_surface.preferred_buffer_scale followed by an xdg_surface.configure
+ event. The client must acknowledge it and is then allowed to attach a
+ buffer to map the surface.
+
+ Mapping an xdg_surface-based role surface is defined as making it
+ possible for the surface to be shown by the compositor. Note that
+ a mapped surface is not guaranteed to be visible once it is mapped.
+
+ For an xdg_surface to be mapped by the compositor, the following
+ conditions must be met:
+ (1) the client has assigned an xdg_surface-based role to the surface
+ (2) the client has set and committed the xdg_surface state and the
+ role-dependent state to the surface
+ (3) the client has committed a buffer to the surface
+
+ A newly-unmapped surface is considered to have met condition (1) out
+ of the 3 required conditions for mapping a surface if its role surface
+ has not been destroyed, i.e. the client must perform the initial commit
+ again before attaching a buffer.
+ </description>
+
+ <enum name="error">
+ <entry name="not_constructed" value="1"
+ summary="Surface was not fully constructed"/>
+ <entry name="already_constructed" value="2"
+ summary="Surface was already constructed"/>
+ <entry name="unconfigured_buffer" value="3"
+ summary="Attaching a buffer to an unconfigured surface"/>
+ <entry name="invalid_serial" value="4"
+ summary="Invalid serial number when acking a configure event"/>
+ <entry name="invalid_size" value="5"
+ summary="Width or height was zero or negative"/>
+ <entry name="defunct_role_object" value="6"
+ summary="Surface was destroyed before its role object"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the xdg_surface">
+ Destroy the xdg_surface object. An xdg_surface must only be destroyed
+ after its role object has been destroyed, otherwise
+ a defunct_role_object error is raised.
+ </description>
+ </request>
+
+ <request name="get_toplevel">
+ <description summary="assign the xdg_toplevel surface role">
+ This creates an xdg_toplevel object for the given xdg_surface and gives
+ the associated wl_surface the xdg_toplevel role.
+
+ See the documentation of xdg_toplevel for more details about what an
+ xdg_toplevel is and how it is used.
+ </description>
+ <arg name="id" type="new_id" interface="xdg_toplevel"/>
+ </request>
+
+ <request name="get_popup">
+ <description summary="assign the xdg_popup surface role">
+ This creates an xdg_popup object for the given xdg_surface and gives
+ the associated wl_surface the xdg_popup role.
+
+ If null is passed as a parent, a parent surface must be specified using
+ some other protocol, before committing the initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+ </description>
+ <arg name="id" type="new_id" interface="xdg_popup"/>
+ <arg name="parent" type="object" interface="xdg_surface" allow-null="true"/>
+ <arg name="positioner" type="object" interface="xdg_positioner"/>
+ </request>
+
+ <request name="set_window_geometry">
+ <description summary="set the new window geometry">
+ The window geometry of a surface is its "visible bounds" from the
+ user's perspective. Client-side decorations often have invisible
+ portions like drop-shadows which should be ignored for the
+ purposes of aligning, placing and constraining windows.
+
+ The window geometry is double-buffered state, see wl_surface.commit.
+
+ When maintaining a position, the compositor should treat the (x, y)
+ coordinate of the window geometry as the top left corner of the window.
+ A client changing the (x, y) window geometry coordinate should in
+ general not alter the position of the window.
+
+ Once the window geometry of the surface is set, it is not possible to
+ unset it, and it will remain the same until set_window_geometry is
+ called again, even if a new subsurface or buffer is attached.
+
+ If never set, the value is the full bounds of the surface,
+ including any subsurfaces. This updates dynamically on every
+ commit. This unset is meant for extremely simple clients.
+
+ The arguments are given in the surface-local coordinate space of
+ the wl_surface associated with this xdg_surface, and may extend outside
+ of the wl_surface itself to mark parts of the subsurface tree as part of
+ the window geometry.
+
+ When applied, the effective window geometry will be the set window
+ geometry clamped to the bounding rectangle of the combined
+ geometry of the surface of the xdg_surface and the associated
+ subsurfaces.
+
+ The effective geometry will not be recalculated unless a new call to
+ set_window_geometry is done and the new pending surface state is
+ subsequently applied.
+
+ The width and height of the effective window geometry must be
+ greater than zero. Setting an invalid size will raise an
+ invalid_size error.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="ack_configure">
+ <description summary="ack a configure event">
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ For instance, for toplevel surfaces the compositor might use this
+ information to move a surface to the top left only when the client has
+ drawn itself for the maximized or fullscreen state.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+ Acking a configure event that was never sent raises an invalid_serial
+ error.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+
+ Sending an ack_configure request consumes the serial number sent with
+ the request, as well as serial numbers sent by all configure events
+ sent on this xdg_surface prior to the configure event referenced by
+ the committed serial.
+
+ It is an error to issue multiple ack_configure requests referencing a
+ serial from the same configure event, or to issue an ack_configure
+ request referencing a serial from a configure event issued before the
+ event identified by the last ack_configure request for the same
+ xdg_surface. Doing so will raise an invalid_serial error.
+ </description>
+ <arg name="serial" type="uint" summary="the serial from the configure event"/>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ The configure event marks the end of a configure sequence. A configure
+ sequence is a set of one or more events configuring the state of the
+ xdg_surface, including the final xdg_surface.configure event.
+
+ Where applicable, xdg_surface surface roles will during a configure
+ sequence extend this event as a latched state sent as events before the
+ xdg_surface.configure event. Such events should be considered to make up
+ a set of atomically applied configuration states, where the
+ xdg_surface.configure commits the accumulated state.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ If the client receives multiple configure events before it can respond
+ to one, it is free to discard all but the last event it received.
+ </description>
+ <arg name="serial" type="uint" summary="serial of the configure event"/>
+ </event>
+
+ </interface>
+
+ <interface name="xdg_toplevel" version="7">
+ <description summary="toplevel surface">
+ This interface defines an xdg_surface role which allows a surface to,
+ among other things, set window-like properties such as maximize,
+ fullscreen, and minimize, set application-specific metadata like title and
+ id, and well as trigger user interactive operations such as interactive
+ resize and move.
+
+ A xdg_toplevel by default is responsible for providing the full intended
+ visual representation of the toplevel, which depending on the window
+ state, may mean things like a title bar, window controls and drop shadow.
+
+ Unmapping an xdg_toplevel means that the surface cannot be shown
+ by the compositor until it is explicitly mapped again.
+ All active operations (e.g., move, resize) are canceled and all
+ attributes (e.g. title, state, stacking, ...) are discarded for
+ an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to
+ the state it had right after xdg_surface.get_toplevel. The client
+ can re-map the toplevel by performing a commit without any buffer
+ attached, waiting for a configure event and handling it as usual (see
+ xdg_surface description).
+
+ Attaching a null buffer to a toplevel unmaps the surface.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the xdg_toplevel">
+ This request destroys the role surface and unmaps the surface;
+ see "Unmapping" behavior in interface section for details.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="invalid_resize_edge" value="0" summary="provided value is
+ not a valid variant of the resize_edge enum"/>
+ <entry name="invalid_parent" value="1"
+ summary="invalid parent toplevel"/>
+ <entry name="invalid_size" value="2"
+ summary="client provided an invalid min or max size"/>
+ </enum>
+
+ <request name="set_parent">
+ <description summary="set the parent of this surface">
+ Set the "parent" of this surface. This surface should be stacked
+ above the parent surface and all other ancestor surfaces.
+
+ Parent surfaces should be set on dialogs, toolboxes, or other
+ "auxiliary" surfaces, so that the parent is raised when the dialog
+ is raised.
+
+ Setting a null parent for a child surface unsets its parent. Setting
+ a null parent for a surface which currently has no parent is a no-op.
+
+ Only mapped surfaces can have child surfaces. Setting a parent which
+ is not mapped is equivalent to setting a null parent. If a surface
+ becomes unmapped, its children's parent is set to the parent of
+ the now-unmapped surface. If the now-unmapped surface has no parent,
+ its children's parent is unset. If the now-unmapped surface becomes
+ mapped again, its parent-child relationship is not restored.
+
+ The parent toplevel must not be one of the child toplevel's
+ descendants, and the parent must be different from the child toplevel,
+ otherwise the invalid_parent protocol error is raised.
+ </description>
+ <arg name="parent" type="object" interface="xdg_toplevel" allow-null="true"/>
+ </request>
+
+ <request name="set_title">
+ <description summary="set surface title">
+ Set a short title for the surface.
+
+ This string may be used to identify the surface in a task bar,
+ window list, or other user interface elements provided by the
+ compositor.
+
+ The string must be encoded in UTF-8.
+ </description>
+ <arg name="title" type="string"/>
+ </request>
+
+ <request name="set_app_id">
+ <description summary="set application ID">
+ Set an application identifier for the surface.
+
+ The app ID identifies the general class of applications to which
+ the surface belongs. The compositor can use this to group multiple
+ surfaces together, or to determine how to launch a new application.
+
+ For D-Bus activatable applications, the app ID is used as the D-Bus
+ service name.
+
+ The compositor shell will try to group application surfaces together
+ by their app ID. As a best practice, it is suggested to select app
+ ID's that match the basename of the application's .desktop file.
+ For example, "org.freedesktop.FooViewer" where the .desktop file is
+ "org.freedesktop.FooViewer.desktop".
+
+ Like other properties, a set_app_id request can be sent after the
+ xdg_toplevel has been mapped to update the property.
+
+ See the desktop-entry specification [0] for more details on
+ application identifiers and how they relate to well-known D-Bus
+ names and .desktop files.
+
+ [0] https://standards.freedesktop.org/desktop-entry-spec/
+ </description>
+ <arg name="app_id" type="string"/>
+ </request>
+
+ <request name="show_window_menu">
+ <description summary="show the window menu">
+ Clients implementing client-side decorations might want to show
+ a context menu when right-clicking on the decorations, giving the
+ user a menu that they can use to maximize or minimize the window.
+
+ This request asks the compositor to pop up such a window menu at
+ the given position, relative to the local surface coordinates of
+ the parent surface. There are no guarantees as to what menu items
+ the window menu contains, or even if a window menu will be drawn
+ at all.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ <arg name="x" type="int" summary="the x position to pop up the window menu at"/>
+ <arg name="y" type="int" summary="the y position to pop up the window menu at"/>
+ </request>
+
+ <request name="move">
+ <description summary="start an interactive move">
+ Start an interactive, user-driven move of the surface.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event. The passed
+ serial is used to determine the type of interactive move (touch,
+ pointer, etc).
+
+ The server may ignore move requests depending on the state of
+ the surface (e.g. fullscreen or maximized), or if the passed serial
+ is no longer valid.
+
+ If triggered, the surface will lose the focus of the device
+ (wl_pointer, wl_touch, etc) used for the move. It is up to the
+ compositor to visually indicate that the move is taking place, such as
+ updating a pointer cursor, during the move. There is no guarantee
+ that the device focus will return when the move is completed.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ </request>
+
+ <enum name="resize_edge">
+ <description summary="edge values for resizing">
+ These values are used to indicate which edge of a surface
+ is being dragged in a resize operation.
+ </description>
+ <entry name="none" value="0"/>
+ <entry name="top" value="1"/>
+ <entry name="bottom" value="2"/>
+ <entry name="left" value="4"/>
+ <entry name="top_left" value="5"/>
+ <entry name="bottom_left" value="6"/>
+ <entry name="right" value="8"/>
+ <entry name="top_right" value="9"/>
+ <entry name="bottom_right" value="10"/>
+ </enum>
+
+ <request name="resize">
+ <description summary="start an interactive resize">
+ Start a user-driven, interactive resize of the surface.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event. The passed
+ serial is used to determine the type of interactive resize (touch,
+ pointer, etc).
+
+ The server may ignore resize requests depending on the state of
+ the surface (e.g. fullscreen or maximized).
+
+ If triggered, the client will receive configure events with the
+ "resize" state enum value and the expected sizes. See the "resize"
+ enum value for more details about what is required. The client
+ must also acknowledge configure events using "ack_configure". After
+ the resize is completed, the client will receive another "configure"
+ event without the resize state.
+
+ If triggered, the surface also will lose the focus of the device
+ (wl_pointer, wl_touch, etc) used for the resize. It is up to the
+ compositor to visually indicate that the resize is taking place,
+ such as updating a pointer cursor, during the resize. There is no
+ guarantee that the device focus will return when the resize is
+ completed.
+
+ The edges parameter specifies how the surface should be resized, and
+ is one of the values of the resize_edge enum. Values not matching
+ a variant of the enum will cause the invalid_resize_edge protocol error.
+ The compositor may use this information to update the surface position
+ for example when dragging the top left corner. The compositor may also
+ use this information to adapt its behavior, e.g. choose an appropriate
+ cursor image.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ <arg name="edges" type="uint" enum="resize_edge" summary="which edge or corner is being dragged"/>
+ </request>
+
+ <enum name="state">
+ <description summary="types of state on the surface">
+ The different state values used on the surface. This is designed for
+ state values like maximized, fullscreen. It is paired with the
+ configure event to ensure that both the client and the compositor
+ setting the state can be synchronized.
+
+ States set in this way are double-buffered, see wl_surface.commit.
+ </description>
+ <entry name="maximized" value="1" summary="the surface is maximized">
+ <description summary="the surface is maximized">
+ The surface is maximized. The window geometry specified in the configure
+ event must be obeyed by the client, or the xdg_wm_base.invalid_surface_state
+ error is raised.
+
+ The client should draw without shadow or other
+ decoration outside of the window geometry.
+ </description>
+ </entry>
+ <entry name="fullscreen" value="2" summary="the surface is fullscreen">
+ <description summary="the surface is fullscreen">
+ The surface is fullscreen. The window geometry specified in the
+ configure event is a maximum; the client cannot resize beyond it. For
+ a surface to cover the whole fullscreened area, the geometry
+ dimensions must be obeyed by the client. For more details, see
+ xdg_toplevel.set_fullscreen.
+ </description>
+ </entry>
+ <entry name="resizing" value="3" summary="the surface is being resized">
+ <description summary="the surface is being resized">
+ The surface is being resized. The window geometry specified in the
+ configure event is a maximum; the client cannot resize beyond it.
+ Clients that have aspect ratio or cell sizing configuration can use
+ a smaller size, however.
+ </description>
+ </entry>
+ <entry name="activated" value="4" summary="the surface is now activated">
+ <description summary="the surface is now activated">
+ Client window decorations should be painted as if the window is
+ active. Do not assume this means that the window actually has
+ keyboard or pointer focus.
+ </description>
+ </entry>
+ <entry name="tiled_left" value="5" since="2">
+ <description summary="the surface’s left edge is tiled">
+ The window is currently in a tiled layout and the left edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the left edge.
+ </description>
+ </entry>
+ <entry name="tiled_right" value="6" since="2">
+ <description summary="the surface’s right edge is tiled">
+ The window is currently in a tiled layout and the right edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the right edge.
+ </description>
+ </entry>
+ <entry name="tiled_top" value="7" since="2">
+ <description summary="the surface’s top edge is tiled">
+ The window is currently in a tiled layout and the top edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the top edge.
+ </description>
+ </entry>
+ <entry name="tiled_bottom" value="8" since="2">
+ <description summary="the surface’s bottom edge is tiled">
+ The window is currently in a tiled layout and the bottom edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the bottom edge.
+ </description>
+ </entry>
+ <entry name="suspended" value="9" since="6">
+ <description summary="surface repaint is suspended">
+ The surface is currently not ordinarily being repainted; for
+ example because its content is occluded by another window, or its
+ outputs are switched off due to screen locking.
+ </description>
+ </entry>
+ <entry name="constrained_left" value="10" since="7">
+ <description summary="the surface’s left edge is constrained">
+ The left edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+ </description>
+ </entry>
+ <entry name="constrained_right" value="11" since="7">
+ <description summary="the surface’s right edge is constrained">
+ The right edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+ </description>
+ </entry>
+ <entry name="constrained_top" value="12" since="7">
+ <description summary="the surface’s top edge is constrained">
+ The top edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+ </description>
+ </entry>
+ <entry name="constrained_bottom" value="13" since="7">
+ <description summary="the surface’s bottom edge is tiled">
+ The bottom edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_max_size">
+ <description summary="set the maximum size">
+ Set a maximum size for the window.
+
+ The client can specify a maximum size so that the compositor does
+ not try to configure the window beyond this size.
+
+ The width and height arguments are in window geometry coordinates.
+ See xdg_surface.set_window_geometry.
+
+ Values set in this way are double-buffered, see wl_surface.commit.
+
+ The compositor can use this information to allow or disallow
+ different states like maximize or fullscreen and draw accurate
+ animations.
+
+ Similarly, a tiling window manager may use this information to
+ place and resize client windows in a more effective way.
+
+ The client should not rely on the compositor to obey the maximum
+ size. The compositor may decide to ignore the values set by the
+ client and request a larger size.
+
+ If never set, or a value of zero in the request, means that the
+ client has no expected maximum size in the given dimension.
+ As a result, a client wishing to reset the maximum size
+ to an unspecified state can use zero for width and height in the
+ request.
+
+ Requesting a maximum size to be smaller than the minimum size of
+ a surface is illegal and will result in an invalid_size error.
+
+ The width and height must be greater than or equal to zero. Using
+ strictly negative values for width or height will result in a
+ invalid_size error.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="set_min_size">
+ <description summary="set the minimum size">
+ Set a minimum size for the window.
+
+ The client can specify a minimum size so that the compositor does
+ not try to configure the window below this size.
+
+ The width and height arguments are in window geometry coordinates.
+ See xdg_surface.set_window_geometry.
+
+ Values set in this way are double-buffered, see wl_surface.commit.
+
+ The compositor can use this information to allow or disallow
+ different states like maximize or fullscreen and draw accurate
+ animations.
+
+ Similarly, a tiling window manager may use this information to
+ place and resize client windows in a more effective way.
+
+ The client should not rely on the compositor to obey the minimum
+ size. The compositor may decide to ignore the values set by the
+ client and request a smaller size.
+
+ If never set, or a value of zero in the request, means that the
+ client has no expected minimum size in the given dimension.
+ As a result, a client wishing to reset the minimum size
+ to an unspecified state can use zero for width and height in the
+ request.
+
+ Requesting a minimum size to be larger than the maximum size of
+ a surface is illegal and will result in an invalid_size error.
+
+ The width and height must be greater than or equal to zero. Using
+ strictly negative values for width and height will result in a
+ invalid_size error.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="set_maximized">
+ <description summary="maximize the window">
+ Maximize the surface.
+
+ After requesting that the surface should be maximized, the compositor
+ will respond by emitting a configure event. Whether this configure
+ actually sets the window maximized is subject to compositor policies.
+ The client must then update its content, drawing in the configured
+ state. The client must also acknowledge the configure when committing
+ the new content (see ack_configure).
+
+ It is up to the compositor to decide how and where to maximize the
+ surface, for example which output and what region of the screen should
+ be used.
+
+ If the surface was already maximized, the compositor will still emit
+ a configure event with the "maximized" state.
+
+ If the surface is in a fullscreen state, this request has no direct
+ effect. It may alter the state the surface is returned to when
+ unmaximized unless overridden by the compositor.
+ </description>
+ </request>
+
+ <request name="unset_maximized">
+ <description summary="unmaximize the window">
+ Unmaximize the surface.
+
+ After requesting that the surface should be unmaximized, the compositor
+ will respond by emitting a configure event. Whether this actually
+ un-maximizes the window is subject to compositor policies.
+ If available and applicable, the compositor will include the window
+ geometry dimensions the window had prior to being maximized in the
+ configure event. The client must then update its content, drawing it in
+ the configured state. The client must also acknowledge the configure
+ when committing the new content (see ack_configure).
+
+ It is up to the compositor to position the surface after it was
+ unmaximized; usually the position the surface had before maximizing, if
+ applicable.
+
+ If the surface was already not maximized, the compositor will still
+ emit a configure event without the "maximized" state.
+
+ If the surface is in a fullscreen state, this request has no direct
+ effect. It may alter the state the surface is returned to when
+ unmaximized unless overridden by the compositor.
+ </description>
+ </request>
+
+ <request name="set_fullscreen">
+ <description summary="set the window as fullscreen on an output">
+ Make the surface fullscreen.
+
+ After requesting that the surface should be fullscreened, the
+ compositor will respond by emitting a configure event. Whether the
+ client is actually put into a fullscreen state is subject to compositor
+ policies. The client must also acknowledge the configure when
+ committing the new content (see ack_configure).
+
+ The output passed by the request indicates the client's preference as
+ to which display it should be set fullscreen on. If this value is NULL,
+ it's up to the compositor to choose which display will be used to map
+ this surface.
+
+ If the surface doesn't cover the whole output, the compositor will
+ position the surface in the center of the output and compensate with
+ with border fill covering the rest of the output. The content of the
+ border fill is undefined, but should be assumed to be in some way that
+ attempts to blend into the surrounding area (e.g. solid black).
+
+ If the fullscreened surface is not opaque, the compositor must make
+ sure that other screen content not part of the same surface tree (made
+ up of subsurfaces, popups or similarly coupled surfaces) are not
+ visible below the fullscreened surface.
+ </description>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+ </request>
+
+ <request name="unset_fullscreen">
+ <description summary="unset the window as fullscreen">
+ Make the surface no longer fullscreen.
+
+ After requesting that the surface should be unfullscreened, the
+ compositor will respond by emitting a configure event.
+ Whether this actually removes the fullscreen state of the client is
+ subject to compositor policies.
+
+ Making a surface unfullscreen sets states for the surface based on the following:
+ * the state(s) it may have had before becoming fullscreen
+ * any state(s) decided by the compositor
+ * any state(s) requested by the client while the surface was fullscreen
+
+ The compositor may include the previous window geometry dimensions in
+ the configure event, if applicable.
+
+ The client must also acknowledge the configure when committing the new
+ content (see ack_configure).
+ </description>
+ </request>
+
+ <request name="set_minimized">
+ <description summary="set the window as minimized">
+ Request that the compositor minimize your surface. There is no
+ way to know if the surface is currently minimized, nor is there
+ any way to unset minimization on this surface.
+
+ If you are looking to throttle redrawing when minimized, please
+ instead use the wl_surface.frame event for this, as this will
+ also work with live previews on windows in Alt-Tab, Expose or
+ similar compositor features.
+ </description>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ This configure event asks the client to resize its toplevel surface or
+ to change its state. The configured state should not be applied
+ immediately. See xdg_surface.configure for details.
+
+ The width and height arguments specify a hint to the window
+ about how its surface should be resized in window geometry
+ coordinates. See set_window_geometry.
+
+ If the width or height arguments are zero, it means the client
+ should decide its own window dimension. This may happen when the
+ compositor needs to configure the state of the surface but doesn't
+ have any information about any previous or expected dimension.
+
+ The states listed in the event specify how the width/height
+ arguments should be interpreted, and possibly how it should be
+ drawn.
+
+ Clients must send an ack_configure in response to this event. See
+ xdg_surface.configure and xdg_surface.ack_configure for details.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ <arg name="states" type="array"/>
+ </event>
+
+ <event name="close">
+ <description summary="surface wants to be closed">
+ The close event is sent by the compositor when the user
+ wants the surface to be closed. This should be equivalent to
+ the user clicking the close button in client-side decorations,
+ if your application has any.
+
+ This is only a request that the user intends to close the
+ window. The client may choose to ignore this request, or show
+ a dialog to ask the user to save their data, etc.
+ </description>
+ </event>
+
+ <!-- Version 4 additions -->
+
+ <event name="configure_bounds" since="4">
+ <description summary="recommended window geometry bounds">
+ The configure_bounds event may be sent prior to a xdg_toplevel.configure
+ event to communicate the bounds a window geometry size is recommended
+ to constrain to.
+
+ The passed width and height are in surface coordinate space. If width
+ and height are 0, it means bounds is unknown and equivalent to as if no
+ configure_bounds event was ever sent for this surface.
+
+ The bounds can for example correspond to the size of a monitor excluding
+ any panels or other shell components, so that a surface isn't created in
+ a way that it cannot fit.
+
+ The bounds may change at any point, and in such a case, a new
+ xdg_toplevel.configure_bounds will be sent, followed by
+ xdg_toplevel.configure and xdg_surface.configure.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </event>
+
+ <!-- Version 5 additions -->
+
+ <enum name="wm_capabilities" since="5">
+ <entry name="window_menu" value="1" summary="show_window_menu is available"/>
+ <entry name="maximize" value="2" summary="set_maximized and unset_maximized are available"/>
+ <entry name="fullscreen" value="3" summary="set_fullscreen and unset_fullscreen are available"/>
+ <entry name="minimize" value="4" summary="set_minimized is available"/>
+ </enum>
+
+ <event name="wm_capabilities" since="5">
+ <description summary="compositor capabilities">
+ This event advertises the capabilities supported by the compositor. If
+ a capability isn't supported, clients should hide or disable the UI
+ elements that expose this functionality. For instance, if the
+ compositor doesn't advertise support for minimized toplevels, a button
+ triggering the set_minimized request should not be displayed.
+
+ The compositor will ignore requests it doesn't support. For instance,
+ a compositor which doesn't advertise support for minimized will ignore
+ set_minimized requests.
+
+ Compositors must send this event once before the first
+ xdg_surface.configure event. When the capabilities change, compositors
+ must send this event again and then send an xdg_surface.configure
+ event.
+
+ The configured state should not be applied immediately. See
+ xdg_surface.configure for details.
+
+ The capabilities are sent as an array of 32-bit unsigned integers in
+ native endianness.
+ </description>
+ <arg name="capabilities" type="array" summary="array of 32-bit capabilities"/>
+ </event>
+ </interface>
+
+ <interface name="xdg_popup" version="7">
+ <description summary="short-lived, popup surfaces for menus">
+ A popup surface is a short-lived, temporary surface. It can be used to
+ implement for example menus, popovers, tooltips and other similar user
+ interface concepts.
+
+ A popup can be made to take an explicit grab. See xdg_popup.grab for
+ details.
+
+ When the popup is dismissed, a popup_done event will be sent out, and at
+ the same time the surface will be unmapped. See the xdg_popup.popup_done
+ event for details.
+
+ Explicitly destroying the xdg_popup object will also dismiss the popup and
+ unmap the surface. Clients that want to dismiss the popup when another
+ surface of their own is clicked should dismiss the popup using the destroy
+ request.
+
+ A newly created xdg_popup will be stacked on top of all previously created
+ xdg_popup surfaces associated with the same xdg_toplevel.
+
+ The parent of an xdg_popup must be mapped (see the xdg_surface
+ description) before the xdg_popup itself.
+
+ The client must call wl_surface.commit on the corresponding wl_surface
+ for the xdg_popup state to take effect.
+ </description>
+
+ <enum name="error">
+ <entry name="invalid_grab" value="0"
+ summary="tried to grab after being mapped"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="remove xdg_popup interface">
+ This destroys the popup. Explicitly destroying the xdg_popup
+ object will also dismiss the popup, and unmap the surface.
+
+ If this xdg_popup is not the "topmost" popup, the
+ xdg_wm_base.not_the_topmost_popup protocol error will be sent.
+ </description>
+ </request>
+
+ <request name="grab">
+ <description summary="make the popup take an explicit grab">
+ This request makes the created popup take an explicit grab. An explicit
+ grab will be dismissed when the user dismisses the popup, or when the
+ client destroys the xdg_popup. This can be done by the user clicking
+ outside the surface, using the keyboard, or even locking the screen
+ through closing the lid or a timeout.
+
+ If the compositor denies the grab, the popup will be immediately
+ dismissed.
+
+ This request must be used in response to some sort of user action like a
+ button press, key press, or touch down event. The serial number of the
+ event should be passed as 'serial'.
+
+ The parent of a grabbing popup must either be an xdg_toplevel surface or
+ another xdg_popup with an explicit grab. If the parent is another
+ xdg_popup it means that the popups are nested, with this popup now being
+ the topmost popup.
+
+ Nested popups must be destroyed in the reverse order they were created
+ in, e.g. the only popup you are allowed to destroy at all times is the
+ topmost one.
+
+ When compositors choose to dismiss a popup, they may dismiss every
+ nested grabbing popup as well. When a compositor dismisses popups, it
+ will follow the same dismissing order as required from the client.
+
+ If the topmost grabbing popup is destroyed, the grab will be returned to
+ the parent of the popup, if that parent previously had an explicit grab.
+
+ If the parent is a grabbing popup which has already been dismissed, this
+ popup will be immediately dismissed. If the parent is a popup that did
+ not take an explicit grab, an error will be raised.
+
+ During a popup grab, the client owning the grab will receive pointer
+ and touch events for all their surfaces as normal (similar to an
+ "owner-events" grab in X11 parlance), while the top most grabbing popup
+ will always have keyboard focus.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat"
+ summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ </request>
+
+ <event name="configure">
+ <description summary="configure the popup surface">
+ This event asks the popup surface to configure itself given the
+ configuration. The configured state should not be applied immediately.
+ See xdg_surface.configure for details.
+
+ The x and y arguments represent the position the popup was placed at
+ given the xdg_positioner rule, relative to the upper left corner of the
+ window geometry of the parent surface.
+
+ For version 2 or older, the configure event for an xdg_popup is only
+ ever sent once for the initial configuration. Starting with version 3,
+ it may be sent again if the popup is setup with an xdg_positioner with
+ set_reactive requested, or in response to xdg_popup.reposition requests.
+ </description>
+ <arg name="x" type="int"
+ summary="x position relative to parent surface window geometry"/>
+ <arg name="y" type="int"
+ summary="y position relative to parent surface window geometry"/>
+ <arg name="width" type="int" summary="window geometry width"/>
+ <arg name="height" type="int" summary="window geometry height"/>
+ </event>
+
+ <event name="popup_done">
+ <description summary="popup interaction is done">
+ The popup_done event is sent out when a popup is dismissed by the
+ compositor. The client should destroy the xdg_popup object at this
+ point.
+ </description>
+ </event>
+
+ <!-- Version 3 additions -->
+
+ <request name="reposition" since="3">
+ <description summary="recalculate the popup's location">
+ Reposition an already-mapped popup. The popup will be placed given the
+ details in the passed xdg_positioner object, and a
+ xdg_popup.repositioned followed by xdg_popup.configure and
+ xdg_surface.configure will be emitted in response. Any parameters set
+ by the previous positioner will be discarded.
+
+ The passed token will be sent in the corresponding
+ xdg_popup.repositioned event. The new popup position will not take
+ effect until the corresponding configure event is acknowledged by the
+ client. See xdg_popup.repositioned for details. The token itself is
+ opaque, and has no other special meaning.
+
+ If multiple reposition requests are sent, the compositor may skip all
+ but the last one.
+
+ If the popup is repositioned in response to a configure event for its
+ parent, the client should send an xdg_positioner.set_parent_configure
+ and possibly an xdg_positioner.set_parent_size request to allow the
+ compositor to properly constrain the popup.
+
+ If the popup is repositioned together with a parent that is being
+ resized, but not in response to a configure event, the client should
+ send an xdg_positioner.set_parent_size request.
+ </description>
+ <arg name="positioner" type="object" interface="xdg_positioner"/>
+ <arg name="token" type="uint" summary="reposition request token"/>
+ </request>
+
+ <event name="repositioned" since="3">
+ <description summary="signal the completion of a repositioned request">
+ The repositioned event is sent as part of a popup configuration
+ sequence, together with xdg_popup.configure and lastly
+ xdg_surface.configure to notify the completion of a reposition request.
+
+ The repositioned event is to notify about the completion of a
+ xdg_popup.reposition request. The token argument is the token passed
+ in the xdg_popup.reposition request.
+
+ Immediately after this event is emitted, xdg_popup.configure and
+ xdg_surface.configure will be sent with the updated size and position,
+ as well as a new configure serial.
+
+ The client should optionally update the content of the popup, but must
+ acknowledge the new popup configuration for the new position to take
+ effect. See xdg_surface.ack_configure for details.
+ </description>
+ <arg name="token" type="uint" summary="reposition request token"/>
+ </event>
+
+ </interface>
+</protocol>
-/* xscreensaver, Copyright © 1991-2022 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
static void
-print_kbd_event (XKeyEvent *xkey, XComposeStatus *compose, Bool x11_p)
+print_kbd_event (XKeyEvent *xkey, XComposeStatus *compose, XIC ic,
+ Bool x11_p, const char *desc)
{
if (debug_p) /* Passwords show up in plaintext! */
{
int n = XLookupString (xkey, c, sizeof(c)-1, &keysym, compose);
const char *ks = keysym ? XKeysymToString (keysym) : "NULL";
c[n] = 0;
+
+ if (ic && xkey->type == KeyPress)
+ {
+ char c2[sizeof(c)];
+ Status s = 0;
+ KeySym keysym2 = 0;
+ n = Xutf8LookupString (ic, (XKeyPressedEvent *) xkey,
+ c2, sizeof(c2)-1, &keysym2, &s);
+ c2[n] = 0;
+
+ switch (s) {
+ case XLookupChars: /* Set 'c2' to a UTF8 string */
+ if (*c2)
+ strcpy (c, c2);
+ break;
+ case XLookupKeySym: /* Set 'keysym2' but not 'c2' */
+ if (keysym2)
+ keysym = keysym2;
+ break;
+ case XLookupBoth: /* Set 'keysym2' and 'c2' */
+ if (keysym2)
+ keysym = keysym2;
+ if (*c2)
+ strcpy (c, c2);
+ break;
+ case XLookupNone: /* No input yet */
+ case XBufferOverflow: /* 'c2' was too small */
+ break;
+ default:
+ abort();
+ break;
+ }
+ }
+
if (*c == '\n') strcpy (c, "\\n");
else if (*c == '\r') strcpy (c, "\\r");
else if (*c == '\t') strcpy (c, "\\t");
if (*mods) mods++;
if (!*mods) strcat (mods, "0");
- fprintf (stderr, "%s: %s 0x%02X %s %s \"%s\"\n", blurb(),
+ fprintf (stderr, "%s: %s 0x%02X %s %s \"%s\"%s\n", blurb(),
(x11_p
? (xkey->type == KeyPress
? "X11 KeyPress "
: (xkey->type == KeyPress
? "XI_RawKeyPress "
: "XI_RawKeyRelease")),
- xkey->keycode, mods, ks, c);
+ xkey->keycode, mods, ks, c,
+ (desc ? desc : ""));
}
else /* Log only that the KeyPress happened. */
{
void
-print_xinput_event (Display *dpy, XEvent *xev, const char *desc)
+print_xinput_event (Display *dpy, XEvent *xev, XIC ic, const char *desc)
{
XIRawEvent *re;
case KeyRelease:
{
static XComposeStatus compose = { 0, };
- print_kbd_event (&xev->xkey, &compose, True);
+ print_kbd_event (&xev->xkey, &compose, ic, True, desc);
}
break;
else
{
static XComposeStatus compose = { 0, };
- print_kbd_event (&ev2.xkey, &compose, False);
+ print_kbd_event (&ev2.xkey, &compose, ic, False, desc);
}
break;
}
-/* xscreensaver, Copyright © 1991-2022 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
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);
+extern void print_xinput_event (Display *, XEvent *, XIC, const char *desc);
#endif /* __XSCREENSAVER_XINPUT_H__ */
-/* xscreensaver-command, Copyright © 1991-2023 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver-command, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
progname, dpyname);
}
+ /* Make sure the X11 socket doesn't get allocated to stderr: >&- 2>&-. */
+ {
+ int fd0 = open ("/dev/null", O_RDWR);
+ int fd1 = open ("/dev/null", O_RDWR);
+ int fd2 = open ("/dev/null", O_RDWR);
+ if (fd0 > 2) close (fd0);
+ if (fd1 > 2) close (fd1);
+ if (fd2 > 2) close (fd2);
+ }
+
dpy = XOpenDisplay (dpyname);
if (!dpy)
{
exit (i);
}
+# if 0
if (*cmd == XA_ACTIVATE || *cmd == XA_LOCK || *cmd == XA_SUSPEND ||
*cmd == XA_NEXT || *cmd == XA_PREV || *cmd == XA_SELECT)
/* People never guess that KeyRelease deactivates the screen saver too,
so if we're issuing an activation command, wait a second.
No need to do this if stdin is not a tty, meaning we're not being
run from the command line.
+
+ This isn't necessary as of 6.0, since the daemon ignores all user
+ activity for ~2 seconds after a ClientMessage activation.
*/
if (isatty(0))
sleep (1);
+# endif
i = xscreensaver_command (dpy, *cmd, arg, verbose_p, NULL);
if (i < 0) exit (i);
unsigned long nitems, bytesafter;
unsigned char *dataP = 0;
+ /* XA_SCREENSAVER_STATUS format documented in windows.c. */
if (XGetWindowProperty (dpy,
RootWindow (dpy, 0), /* always screen #0 */
XA_SCREENSAVER_STATUS,
&type, &format, &nitems, &bytesafter,
&dataP)
== Success
- && type
- && dataP)
+ && type == XA_INTEGER
+ && nitems >= 3
+ && dataP)
{
- time_t tt;
+ PROP32 *data = (PROP32 *) dataP;
+ time_t tt = (time_t) /* 64 bit time_t */
+ ((((unsigned long) data[1] & 0xFFFFFFFFL) << 32) |
+ ((unsigned long) data[2] & 0xFFFFFFFFL));
char *s;
Bool changed = False;
Bool running = False;
- PROP32 *data = (PROP32 *) dataP;
-
- if (type != XA_INTEGER || nitems < 3)
- {
- STATUS_LOSE:
- if (last) XFree (last);
- if (data) XFree (data);
- fprintf (stderr, "%s: bad status format on root window\n",
- progname);
- return -1;
- }
-
- tt = (time_t) data[1];
- if (tt <= (time_t) 666000000L) /* early 1991 */
- goto STATUS_LOSE;
+ Atom state = data[0];
s = ctime(&tt);
if (s[strlen(s)-1] == '\n')
s[strlen(s)-1] = 0;
- if (!last || data[0] != last[0])
+ /* Do not print changes to password dialog presence. */
+ if (state == XA_AUTH) state = XA_LOCK;
+
+ if (!last || state != last[0])
{
/* State changed. */
- if (data[0] == XA_BLANK)
+ if (state == XA_BLANK)
printf ("BLANK %s\n", s);
- else if (data[0] == XA_LOCK)
+ else if (state == XA_LOCK)
printf ("LOCK %s\n", s);
- else if (data[0] == 0)
+ else if (state == 0)
printf ("UNBLANK %s\n", s);
else
goto STATUS_LOSE;
if (running && changed)
{
+ int off = 3;
int i;
fprintf (stdout, "RUN");
- for (i = 2; i < nitems; i++)
+ for (i = off; i < nitems; i++)
fprintf (stdout, " %d", (int) data[i]);
fprintf (stdout, "\n");
}
}
else
{
- if (last) XFree (last);
- if (dataP) XFree (dataP);
+ STATUS_LOSE:
+ fflush (stdout);
fprintf (stderr, "%s: no saver status on root window\n",
progname);
+ if (last) XFree (last);
+ if (dataP) XFree (dataP);
return -1;
}
}
-/* xscreensaver, Copyright © 1991-2022 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
static void
connect_to_server (saver_info *si)
{
+ saver_preferences *p = &si->prefs;
Widget toplevel_shell;
Window daemon_window;
XrmOptionDescRec options;
- char *p;
+ char *name;
int ac = 1;
char *av[] = { "xscreensaver" }; /* For Xt and Xrm purposes */
+ /* Make sure the X11 socket doesn't get allocated to stderr: >&- 2>&-.
+ Parent "xscreensaver" already did this, but let's make sure. */
+ {
+ int fd0 = open ("/dev/null", O_RDWR);
+ int fd1 = open ("/dev/null", O_RDWR);
+ int fd2 = open ("/dev/null", O_RDWR);
+ if (fd0 > 2) close (fd0);
+ if (fd1 > 2) close (fd1);
+ if (fd2 > 2) close (fd2);
+ }
+
XSetErrorHandler (saver_ehandler);
toplevel_shell = XtAppInitialize (&si->app, progclass, &options, 0,
si->dpy = XtDisplay (toplevel_shell);
si->prefs.db = XtDatabase (si->dpy);
- XtGetApplicationNameAndClass (si->dpy, &p, &progclass);
+ XtGetApplicationNameAndClass (si->dpy, &name, &progclass);
db = si->prefs.db; /* resources.c needs this */
daemon_window = find_screensaver_window (si->dpy, 0);
if (daemon_window)
{
+ Window root = RootWindow (si->dpy, 0); /* always screen 0 */
XWindowAttributes xgwa;
XGetWindowAttributes (si->dpy, daemon_window, &xgwa);
XSelectInput (si->dpy, daemon_window,
xgwa.your_event_mask | PropertyChangeMask);
+
+ /* For tracking changes to XA_SCREENSAVER_STATUS on the root. */
+ XGetWindowAttributes (si->dpy, root, &xgwa);
+ XSelectInput (si->dpy, root,
+ xgwa.your_event_mask | PropertyChangeMask);
}
else
{
- fprintf (stderr, "%s: xscreensaver does not seem to be running!\n",
- blurb());
+ if (p->verbose_p ||
+ !(getenv ("WAYLAND_DISPLAY") || getenv ("WAYLAND_SOCKET")))
+ fprintf (stderr, "%s: xscreensaver does not seem to be running!\n",
+ blurb());
+
+ /* Under normal circumstances, that window should have been created
+ by the "xscreensaver" process. But if for some reason someone
+ has run "xscreensaver-gfx" directly (Wayland?) we need this window
+ to exist for ClientMessages to be receivable. So let's make one.
+ */
+ XClassHint class_hints;
+ XSetWindowAttributes attrs;
+ unsigned long attrmask = 0;
+ pid_t pid = getpid();
+ char *id;
+ const char *version_number = XSCREENSAVER_VERSION;
+
+ class_hints.res_name = (char *) progname; /* not const? */
+ class_hints.res_class = "XScreenSaver";
+ id = (char *) malloc (20);
+ sprintf (id, "%lu", (unsigned long) pid);
+
+ attrmask = CWOverrideRedirect | CWEventMask;
+ attrs.override_redirect = True;
+ attrs.event_mask = PropertyChangeMask;
+
+ daemon_window = XCreateWindow (si->dpy, RootWindow (si->dpy, 0),
+ 0, 0, 1, 1, 0,
+ DefaultDepth (si->dpy, 0), InputOutput,
+ DefaultVisual (si->dpy, 0), attrmask, &attrs);
+ XStoreName (si->dpy, daemon_window, "XScreenSaver GFX");
+ XSetClassHint (si->dpy, daemon_window, &class_hints);
+ XChangeProperty (si->dpy, daemon_window, XA_WM_COMMAND, XA_STRING,
+ 8, PropModeReplace, (unsigned char *) progname,
+ strlen (progname));
+ XChangeProperty (si->dpy, daemon_window, XA_SCREENSAVER_VERSION, XA_STRING,
+ 8, PropModeReplace, (unsigned char *) version_number,
+ strlen (version_number));
+ XChangeProperty (si->dpy, daemon_window, XA_SCREENSAVER_ID, XA_STRING,
+ 8, PropModeReplace, (unsigned char *) id, strlen (id));
+ free (id);
+
+ si->all_clientmessages_p = True;
}
}
ssi->current_hack = -1;
}
- XGetWindowProperty (si->dpy, w, XA_SCREENSAVER_STATUS,
- 0, 999, False, XA_INTEGER, &type, &format, &nitems,
- &bytesafter, &dataP);
- if (dataP && type == XA_INTEGER && nitems >= 3)
+ /* XA_SCREENSAVER_STATUS format documented in windows.c. */
+ if (XGetWindowProperty (si->dpy, w,
+ XA_SCREENSAVER_STATUS,
+ 0, 999, False, XA_INTEGER,
+ &type, &format, &nitems, &bytesafter,
+ &dataP)
+ == Success
+ && type == XA_INTEGER
+ && nitems >= 3
+ && dataP)
{
- for (i = 2; i < nitems; i++)
+ PROP32 *data = (PROP32 *) dataP;
+ int off = 3;
+ for (i = off; i < nitems; i++)
{
- int j = i - 2;
+ int j = i - off;
if (j < si->nscreens)
{
saver_screen_info *ssi = &si->screens[j];
- int n = ((PROP32 *) dataP)[i];
- ssi->current_hack = n-1; /* 1-based */
+ int n = data[i];
+ ssi->current_hack = n-1; /* 1-based */
}
}
}
+
if (dataP)
XFree (dataP);
}
+/* When we get a PropertyChange event on XA_SCREENSAVER_STATUS, note
+ whether the auth dialog is currently posted.
+ */
+static void
+status_prop_changed (saver_info *si)
+{
+ Window w = RootWindow (si->dpy, 0); /* always screen 0 */
+ Atom type;
+ unsigned char *dataP = 0;
+ int format;
+ unsigned long nitems, bytesafter;
+ Bool oauth_p = si->auth_p;
+
+ if (XGetWindowProperty (si->dpy, w,
+ XA_SCREENSAVER_STATUS,
+ 0, 999, False, XA_INTEGER,
+ &type, &format, &nitems, &bytesafter,
+ &dataP)
+ == Success
+ && type == XA_INTEGER
+ && nitems >= 3
+ && dataP)
+ {
+ PROP32 *data = (PROP32 *) dataP;
+ si->auth_p = (data[0] == XA_AUTH);
+ if (si->auth_p != oauth_p) /* Faster watchdog while authenticating */
+ reset_watchdog_timer (si);
+ }
+ else
+ si->auth_p = False;
+}
+
+
/* Processing ClientMessage events.
Both xscreensaver and xscreensaver-gfx handle these; some are handled
exclusively by one program or another, and a couple (next, prev) are
si->selection_mode = which;
goto CYCLE;
}
+ else if (si->all_clientmessages_p)
+ {
+ /* The xscreensaver daemon is not running, so return an error response
+ for unhandled ClientMessages so that xscreensaver-command doesn't
+ have to time out waiting for a response that will not arrive. */
+ clientmessage_response (dpy, xev, False,
+ "xscreensaver daemon not running");
+ }
else
{
/* All other ClientMessages are handled by xscreensaver rather than
if (p->verbose_p)
describe_monitor_layout (si->monitor_layout);
+ /* The screen blanked (will blank) at xscreensaver-gfx's launch time.
+ The last user activity time is presumed to be 'timeout' before that.
+ Unless the command line included --next, --prev, --demo, etc.
+ */
+ si->blank_time = time ((time_t *) 0);
+ si->activity_time = (si->selection_mode == 0
+ ? si->blank_time - p->timeout / 1000
+ : si->blank_time);
+
initialize_screensaver_window (si);
init_sigchld (si);
read_status_prop (si);
if (event.x_event.xany.type == ClientMessage)
handle_clientmessage (si, &event.x_event);
+
+ else if (event.x_event.xany.type == PropertyNotify &&
+ event.x_event.xproperty.state == PropertyNewValue &&
+ event.x_event.xproperty.atom == XA_SCREENSAVER_STATUS)
+ status_prop_changed (si);
+
# ifdef HAVE_RANDR
else if (si->using_randr_extension &&
(event.x_event.type ==
*
* - When the system is about to go to sleep (e.g., laptop lid closing)
* it locks the screen *before* the system goes to sleep, by running
- * "xscreensaver-command -suspend". And then when the system wakes
+ * "xscreensaver-command --suspend". And then when the system wakes
* up again, it runs "xscreensaver-command --deactivate" to force the
* unlock dialog to appear immediately.
*
*
* While playing, it runs "xdg-screensaver reset" every 10 seconds as a
* heartbeat. That program is a super-complicated shell script that will
- * eventually run "xscreensaver-command -reset". So MPV talks to the
+ * eventually run "xscreensaver-command --reset". So MPV talks to the
* xscreensaver daemon directly rather than going through systemd.
* That's fine.
*
* https://github.com/mpv-player/mpv/commit/c498b2846af0ee8835b9144c9f6893568a4e49c6
*
* So now I guess you're back to figuring out how to add a "heartbeat"
- * command to have MPV periodically call "xscreensaver-command -reset".
+ * command to have MPV periodically call "xscreensaver-command --reset".
* Good luck with that. Maybe you should just use VLC instead.
*
*
-/* xscreensaver, Copyright © 1991-2023 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#include <X11/Xos.h>
#include <X11/extensions/XInput2.h>
+#ifdef HAVE_WAYLAND
+# include "wayland-idle.h"
+#endif
+
+
#include "xmu.h"
#include "blurb.h"
#include "atoms.h"
static unsigned int blank_timeout = 0;
static unsigned int lock_timeout = 0;
static unsigned int pointer_hysteresis = 0;
+static unsigned int cursor_blank_interval = 60 * 30 + 17;
/* Subprocesses. */
#define SAVER_GFX_PROGRAM "xscreensaver-gfx"
if (months > 18)
fprintf (stderr,
/* Hey jerks, the only time someone will see this particular
- message is if they are running xscreensaver with '-log' in
- order to send me a bug report, and they had damned well
- better try the latest release before they do that --
+ message is if they are running xscreensaver with '--log'
+ to order to send me a bug report, and they had damned
+ well better try the latest release before they do that --
even if your perma-out-of-date distro does not make that
easily available to them. */
"\t ###################################################\n"
read_p = True;
/* Changes to verbose in .xscreenaver after startup are ignored; else
- running xscreensaver-settings would turn off cmd line -verbose. */
+ running xscreensaver-settings would turn off cmdline --verbose. */
if (!both_p) verbose_p = ov;
}
}
"#######################################################################\n"
"\n"
" If at all possible, please re-run xscreensaver with the command\n"
- " line arguments \"-sync -log log.txt\", and reproduce this bug.\n"
+ " line arguments \"--sync --log log.txt\", and reproduce this bug.\n"
" Please include the complete \"log.txt\" file with your bug report.\n"
"\n"
" https://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
*/
static void
store_saver_status (Display *dpy,
- Bool blanked_p, Bool locked_p, time_t blank_time)
+ Bool blanked_p, Bool locked_p, Bool auth_p,
+ time_t blank_time)
{
- /* 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 hacks here (or even how many monitors are attached) we
- leave whatever was there before unchanged: it will be updated by
- "xscreensaver-gfx".
+ /* See the different version of this function in windows.c, which explains
+ the structure of the XA_SCREENSAVER_STATUS property.
+
+ The property has some values updated by this program (state, time)
+ followed by some updated by "xscreensaver-gfx" (the hack ordinals).
+ So we read the existing property and only change our parts.
XA_SCREENSAVER_STATUS is stored on the (real) root window of screen 0.
These properties are not used on the windows created by "xscreensaver-gfx"
for use by the display hacks.
-
- See the different version of this function in windows.c.
*/
Window w = RootWindow (dpy, 0); /* always screen 0 */
Atom type;
int format;
unsigned long nitems, bytesafter;
- /* Read the old property, so we can change just parts. */
+ /* I hate using XGrabServer, but the calls to read and then alter the
+ property must be atomic, and there's no other way to do that. */
+ XGrabServer (dpy);
+ XSync (dpy, False);
+
+ /* Read the old property, so we can change just our parts. */
if (XGetWindowProperty (dpy, w,
XA_SCREENSAVER_STATUS,
0, 999, False, XA_INTEGER,
&& dataP)
status = (PROP32 *) dataP;
- if (!status) /* There was no existing property */
+ if (!status) /* There was no existing property, or it was bogus. */
{
nitems = 3;
- status = (PROP32 *) malloc (nitems * sizeof(*status));
+ status = (PROP32 *) calloc (nitems, sizeof(*status));
}
- status[0] = (PROP32) (locked_p ? XA_LOCK : blanked_p ? XA_BLANK : 0);
- status[1] = (PROP32) blank_time; /* Y2038 bug: unsigned 32 bit time_t */
+ status[0] = (PROP32) (auth_p ? XA_AUTH :
+ locked_p ? XA_LOCK :
+ blanked_p ? XA_BLANK :
+ 0);
+ status[1] = (PROP32) (((unsigned long) blank_time) >> 32);
+ status[2] = (PROP32) (((unsigned long) blank_time) & 0xFFFFFFFFL);
+
XChangeProperty (dpy, w, XA_SCREENSAVER_STATUS, XA_INTEGER, 32,
PropModeReplace, (unsigned char *) status, nitems);
+ XUngrabServer (dpy);
XSync (dpy, False);
# if 0
if (debug_p && verbose_p)
{
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, "%s: wrote status property: 0x%lx: ", blurb(),
+ (unsigned long) w);
+ for (i = 0; i < nitems; i++)
+ {
+ if (i > 0) fprintf (stderr, ", ");
+ if (i == 0)
+ fprintf (stderr, "%s",
+ (status[i] == XA_LOCK ? "LOCK" :
+ status[i] == XA_BLANK ? "BLANK" :
+ status[i] == XA_AUTH ? "AUTH" :
+ status[i] == 0 ? "0" : "???"));
+ else
+ fprintf (stderr, "%lu", status[i]);
+ }
fprintf (stderr, "\n");
- if (system ("xprop -root _SCREENSAVER_STATUS") <= 0)
+ if (system ("xprop -root _SCREENSAVER_STATUS") != 0)
fprintf (stderr, "%s: xprop exec failed\n", blurb());
}
# endif /* 0 */
XChangeProperty (dpy, daemon_window, XA_SCREENSAVER_ID, XA_STRING,
8, PropModeReplace, (unsigned char *) id, strlen (id));
- store_saver_status (dpy, False, False, now);
+ store_saver_status (dpy, False, False, False, now);
free (id);
}
- 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.)
+ 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
static void
-maybe_disable_locking (Display *dpy)
+maybe_disable_locking (Display *dpy, Bool wayland_p)
{
const char *why = 0;
- Bool wayland_p = (getenv ("WAYLAND_DISPLAY") ||
- getenv ("WAYLAND_SOCKET"));
# ifdef NO_LOCKING
why = "locking disabled at compile time";
fprintf (stderr, "%s: DEBUG MODE: allowing locking anyway!\n",
blurb());
}
- else if (wayland_p)
- {
- const char *s = blurb();
- locking_disabled_p = True;
-
- /* Maybe we should just refuse to launch instead? We can operate
- properly only if the user uses only X11 programs, and doesn't
- want to lock the screen.
- */
- fprintf (stderr, "\n"
- "%s: WARNING: Wayland is not supported.\n"
- "\n"
- "%s: Under Wayland, idle-detection fails when non-X11\n"
- "%s: programs are selected, meaning the screen may\n"
- "%s: blank prematurely. Also, locking is impossible.\n"
- "%s: See the manual for instructions on configuring\n"
- "%s: your system to use X11 instead of Wayland.\n\n",
- s, s, s, s, s, s);
- }
else
{
locking_disabled_p = True;
}
+static void
+query_pointer (Display *dpy, int *root_xP, int *root_yP)
+{
+ Window root_ret, child_ret;
+ int win_x, win_y;
+ unsigned int mask;
+ XQueryPointer (dpy, DefaultRootWindow (dpy),
+ &root_ret, &child_ret, root_xP, root_yP,
+ &win_x, &win_y, &mask);
+}
+
+
+#ifdef HAVE_WAYLAND
+static void
+wayland_activity_cb (void *closure)
+{
+ Bool *activeP = (Bool *) closure;
+ *activeP = True;
+}
+#endif /* HAVE_WAYLAND */
+
+
static void
main_loop (Display *dpy)
{
time_t now = time ((time_t *) 0);
time_t active_at = now;
time_t blanked_at = 0;
+ time_t locked_at = 0;
+ time_t cursor_blanked_at = 0;
time_t ignore_activity_before = now;
time_t last_checked_init_file = now;
Bool authenticated_p = False;
Bool ignore_motion_p = False;
+ Bool wayland_p = False;
enum { UNBLANKED, BLANKED, LOCKED, AUTH } current_state = UNBLANKED;
struct { time_t time; int x, y; } last_mouse = { 0, 0, 0 };
- maybe_disable_locking (dpy);
+# ifdef HAVE_WAYLAND
+ Bool wayland_active_p = False;
+ const char *wayland_err = 0;
+ wayland_state *wayland = wayland_idle_init (wayland_activity_cb,
+ &wayland_active_p,
+ &wayland_err);
+ if (wayland)
+ {
+ wayland_p = True; /* Connected to Wayland */
+ }
+ else if (wayland_err && !strcmp (wayland_err, "connection failed"))
+ {
+ if (getenv ("WAYLAND_DISPLAY") || getenv ("WAYLAND_SOCKET"))
+ {
+ /* Running under Wayland, but unable to connect. */
+ fprintf (stderr, "%s: wayland: %s\n", blurb(), wayland_err);
+ exit (1);
+ }
+ else
+ {
+ /* Running under real X11, presumably. */
+ if (verbose_p)
+ fprintf (stderr, "%s: wayland: %s (assuming real X11)\n",
+ blurb(), wayland_err);
+ }
+ }
+ else if (wayland_err)
+ {
+ /* Connected to Wayland, but initialization failed. */
+ fprintf (stderr, "%s: wayland: %s\n", blurb(), wayland_err);
+ exit (1);
+ }
+ else
+ abort();
+
+# else /* !HAVE_WAYLAND */
+
+ if (getenv ("WAYLAND_DISPLAY") || getenv ("WAYLAND_SOCKET"))
+ {
+ fprintf (stderr, "%s: not compiled with Wayland support\n", blurb());
+ exit (1);
+ }
+# endif /* !HAVE_WAYLAND */
+
+ maybe_disable_locking (dpy, wayland_p);
init_xscreensaver_atoms (dpy);
ensure_no_screensaver_running (dpy);
blank_cursor = None; /* Cursor of window under mouse (which is blank). */
auth_cursor = XCreateFontCursor (dpy, XC_top_left_arrow);
+ query_pointer (dpy, &last_mouse.x, &last_mouse.y);
+ last_mouse.time = now;
+
if (strchr (version_number, 'a') || strchr (version_number, 'b'))
splash_p = True; /* alpha and beta releases */
Atom blank_mode = 0;
char blank_mode_arg[20] = { 0 };
- /* Wait until an event comes in, or a timeout. */
- {
+ /* Wait until an event comes in, or a timeout.
+
+ There may already be X events on the queue that arrived along with an
+ earlier call to XSync(). If so we need to process those immediately
+ without waiting for more activity on the socket.
+ */
+ if (! XEventsQueued (dpy, QueuedAlready)) {
int xfd = ConnectionNumber (dpy);
fd_set in_fds;
struct timeval tv;
time_t until;
+
switch (current_state) {
case UNBLANKED: until = active_at + blank_timeout; break;
case BLANKED: until = blanked_at + lock_timeout; break;
default: until = 0;
}
+ if (current_state == BLANKED || current_state == LOCKED)
+ {
+ /* On rare occasions the mouse pointer re-appears, even though we
+ are holding the mouse grabbed with a blank cursor. This should
+ not be possible, and I don't understand how it happens or under
+ what conditions. But, let's try blanking it out again by
+ re-asserting our existing grab with the blank cursor every
+ few minutes.
+ */
+ time_t blank_cursor_at = cursor_blanked_at + cursor_blank_interval;
+ if (blank_cursor_at <= now)
+ {
+ if (verbose_p > 3)
+ fprintf (stderr, "%s: re-blanking cursor\n", blurb());
+ grab_mouse (mouse_screen (dpy), blank_cursor);
+ cursor_blanked_at = now;
+ blank_cursor_at = cursor_blanked_at + cursor_blank_interval;
+ }
+
+ if (until <= now || blank_cursor_at < until)
+ until = blank_cursor_at;
+ }
+
tv.tv_sec = 0;
tv.tv_usec = 0;
- if (until >= now)
+ if (until > now)
tv.tv_sec = until - now;
if (verbose_p > 3)
{
- if (!tv.tv_sec)
+ if (!tv.tv_sec && tv.tv_usec)
fprintf (stderr, "%s: block until input\n", blurb());
else
{
time_t t = now + tv.tv_sec;
localtime_r (&t, &tm);
fprintf (stderr,
- "%s: block for %ld sec until %02d:%02d:%02d\n",
- blurb(), tv.tv_sec, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ "%s: block for %d:%02d:%02d until %02d:%02d:%02d\n",
+ blurb(),
+ (int) tv.tv_sec / (60 * 60),
+ (int) (tv.tv_sec % (60 * 60)) / 60,
+ (int) tv.tv_sec % 60,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
}
}
- FD_ZERO (&in_fds);
- FD_SET (xfd, &in_fds);
- select (xfd + 1, &in_fds, NULL, NULL, (tv.tv_sec ? &tv : NULL));
+ {
+ int fd = xfd;
+ FD_ZERO (&in_fds);
+ FD_SET (xfd, &in_fds);
+
+# ifdef HAVE_WAYLAND
+ /* Stop blocking if there is activity on either the X11 socket
+ or the Wayland socket. */
+ if (wayland)
+ {
+ int wfd = wayland_idle_get_fd (wayland);
+ FD_SET (wfd, &in_fds);
+ fd = MAX (xfd, wfd);
+ }
+# endif /* HAVE_WAYLAND */
+
+ select (fd + 1, &in_fds, NULL, NULL, (tv.tv_sec ? &tv : NULL));
+ }
}
now = time ((time_t *) 0);
to return. Now that we are back on the program stack, handle
those signals. */
- /* SIGHUP is the same as "xscreensaver-command -restart". */
+ /* SIGHUP is the same as "xscreensaver-command --restart". */
if (sighup_received)
{
sighup_received = 0;
}
/* SIGCHLD is fired any time one of our subprocesses dies.
- When "xscreensaver-auth" dies, it analyzes its exit code.
+ When "xscreensaver-auth" dies, we analyze its exit code.
*/
if (sigchld_received)
authenticated_p = handle_sigchld (dpy, current_state != UNBLANKED);
msg == XA_NEXT ||
msg == XA_PREV)
{
- /* The others are the same as -activate except that they
+ /* The others are the same as --activate except that they
cause some extra args to be added to the xscreensaver-gfx
command line.
*/
if (current_state != AUTH && /* logged by xscreensaver-auth */
(verbose_p > 1 ||
(verbose_p && now - active_at > 1)))
- print_xinput_event (dpy, &xev, "");
+ print_xinput_event (dpy, &xev, NULL, "");
active_at = now;
continue;
break;
case ButtonRelease:
active_at = now;
if (verbose_p)
- print_xinput_event (dpy, &xev, "");
+ print_xinput_event (dpy, &xev, NULL, "");
continue;
break;
case MotionNotify:
when grabbed, we can just ignore MotionNotify and let the
XI_RawMotion clause handle hysteresis. */
if (verbose_p > 1)
- print_xinput_event (dpy, &xev, "ignored");
+ print_xinput_event (dpy, &xev, NULL, "ignored");
continue;
break;
default:
if (current_state != AUTH && /* logged by xscreensaver-auth */
(verbose_p > 1 ||
(verbose_p && now - active_at > 1)))
- print_xinput_event (dpy, &xev, "");
+ print_xinput_event (dpy, &xev, NULL, "");
active_at = now;
break;
int secs = now - last_mouse.time;
if (secs >= 1)
{
- Window root_ret, child_ret;
- int root_x, root_y;
- int win_x, win_y;
- unsigned int mask;
int dist;
Bool ignored_p = False;
-
- XQueryPointer (dpy, DefaultRootWindow (dpy),
- &root_ret, &child_ret, &root_x, &root_y,
- &win_x, &win_y, &mask);
+ int root_x = last_mouse.x, root_y = last_mouse.y;
+ query_pointer (dpy, &root_x, &root_y);
dist = MAX (ABS (last_mouse.x - root_x),
ABS (last_mouse.y - root_y));
if (verbose_p > 1 ||
(verbose_p && now - active_at > 5))
- print_xinput_event (dpy, &xev,
+ print_xinput_event (dpy, &xev, NULL,
(ignored_p ? " ignored" : ""));
}
}
default:
if (verbose_p)
- print_xinput_event (dpy, &xev, "");
+ print_xinput_event (dpy, &xev, NULL, "");
break;
}
}
+# ifdef HAVE_WAYLAND
+ if (wayland)
+ {
+ wayland_idle_process_events (wayland);
+ if (wayland_active_p)
+ {
+ active_at = now;
+ wayland_active_p = False;
+ if (verbose_p)
+ fprintf (stderr,"%s: wayland reports user activity\n",
+ blurb());
+ }
+ }
+# endif /* HAVE_WAYLAND */
+
+
/********************************************************************
Advancing the state machine
********************************************************************/
{
current_state = LOCKED;
blanked_at = now;
+ locked_at = now;
+ cursor_blanked_at = now;
authenticated_p = False;
- store_saver_status (dpy, True, True, now);
+ store_saver_status (dpy, True, True, False, locked_at);
}
else
fprintf (stderr, "%s: unable to grab -- locking aborted!\n",
{
current_state = BLANKED;
blanked_at = now;
- store_saver_status (dpy, True, False, now);
+ locked_at = 0;
+ cursor_blanked_at = now;
+ store_saver_status (dpy, True, False, False, blanked_at);
}
else
fprintf (stderr, "%s: unable to grab -- blanking aborted!\n",
(force_lock_p ? "" : " after timeout"));
current_state = LOCKED;
authenticated_p = False;
- store_saver_status (dpy, True, True, now);
+ locked_at = now;
+ store_saver_status (dpy, True, True, False, locked_at);
force_lock_p = False; /* Single shot */
}
else if (active_at >= now &&
fprintf (stderr, "%s: unblanking\n", blurb());
current_state = UNBLANKED;
ignore_motion_p = False;
- store_saver_status (dpy, False, False, now);
+ store_saver_status (dpy, False, False, False, now);
if (saver_gfx_pid)
{
the auth dialog is raised. We can ignore failures here. */
grab_mouse (mouse_screen (dpy), auth_cursor);
+ store_saver_status (dpy, True, True, True, locked_at);
+
av[ac++] = SAVER_AUTH_PROGRAM;
if (verbose_p) av[ac++] = "--verbose";
if (verbose_p > 1) av[ac++] = "--verbose";
a different mouse pointer, to hide the pointer again now that
the auth dialog is gone. We can ignore failures here. */
grab_mouse (mouse_screen (dpy), blank_cursor);
+ cursor_blanked_at = now;
/* When the unlock dialog is dismissed, ignore any input for a
second to give the user time to take their hands off of the
immediately. */
ignore_activity_before = now + 1;
+ store_saver_status (dpy, True, True, False, locked_at);
+
if (gfx_stopped_p) /* SIGCONT to resume savers */
{
if (verbose_p)
blurb(), dpy_str);
}
- /* Copy the -dpy arg to $DISPLAY for subprocesses. */
+ /* Copy the --display arg to $DISPLAY for subprocesses. */
{
char *s = (char *) malloc (strlen(dpy_str) + 20);
sprintf (s, "DISPLAY=%s", dpy_str);
-/* xscreensaver, Copyright © 1993-2021 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1993-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
extern void blank_screen (saver_info *si);
extern void unblank_screen (saver_info *si);
extern void resize_screensaver_window (saver_info *si);
+extern void reset_watchdog_timer (saver_info *);
extern void get_screen_viewport (saver_screen_info *ssi,
int *x_ret, int *y_ret,
extern Visual *get_best_gl_visual (saver_info *si, Screen *screen);
extern void maybe_reload_init_file (saver_info *);
-void print_available_extensions (saver_info *);
+extern void print_available_extensions (saver_info *);
#endif /* __XSCREENSAVER_H__ */
It's a mystery!
.SH THE WAYLAND PROBLEM
Wayland is a completely different window system that is intended to replace
-X11. After 14+ years of trying, some Linux distros have finally begun
+X11. After 16+ years of trying, some Linux distros have finally begun
enabling it by default. Most deployments of it also include XWayland, which
is a compatibility layer that allows \fIsome\fP X11 programs to continue to
work within a Wayland environment.
Unfortunately, XScreenSaver is not one of those programs.
-If your system is running XWayland, XScreenSaver will malfunction in two
-ways:
+If your system is running Wayland, XScreenSaver will malfunction in a
+few ways, some small but one quite serious:
.RS 0
.TP 3
-\fB1:\fP It will be unable to detect user activity in non-X11 programs.
-
-This means that while a native Wayland program is selected, XScreenSaver will
-think that you are idle, and may blank the screen prematurely.
-.TP 3
-\fB2:\fP It will be unable to lock the screen.
+\fB1:\fP It will be unable to lock the screen.
This is because X11 grabs don't work properly under XWayland, so there is no
way for XScreenSaver to prevent the user from switching away from the screen
locker to another application.
+.TP 3
+\fB2:\fP It won't work at all under GNOME.
+
+This is because the GNOME compositor does not support either of the Wayland
+protocols that allow us to tell when the user is idle. It works with KDE
+and most other compositors, however.
+.TP 3
+\fB2:\fP Even a single pixel of mouse motion will cause the screen to
+un-blank: there is no pointer hysteresis.
+.TP 3
+\fB2:\fP Fading in and out might not work; hacks that grab and manipulate
+a desktop screenshot might not work.
+
+Under Wayland, these features depend on the program
+.BR grim (1)
+being installed to grab screenshots for us. And \fBgrim\fP doesn't work
+under the GNOME or KDE compositors.
.RE
-In short, for XScreenSaver to work properly, you will need to switch off
+For XScreenSaver to be able to lock, you will need to switch off
Wayland and use the X Window System like in the "good old days".
.SS TO DISABLE WAYLAND UNDER GNOME
The login screen should have a gear-icon menu that lets you change the session
#%PAM-1.0
-# Fedora Core 5:
-auth include system-auth
+# Debian 12:
+# auth requisite pam_nologin.so
+# auth optional pam_group.so
+# @include common-auth
+# @include common-account
+# @include common-session
+# @include common-password
+
+# Ubuntu 18:
+# @include common-auth
+# @include common-account
-# SuSE 9.0: (along with "configure --with-passwd-helper" and "unix2_chkpwd")
-# auth required pam_unix2.so nullok
+# AL2023:
+# auth substack system-auth
+# auth include postlogin
+
+# Fedora Core 5:
+# auth include system-auth
# Distant past:
# auth required /lib/security/pam_pwdb.so shadow nullok
munge-scripts: $(SCRIPTS)
@tmp=/tmp/mf.$$$$ ; \
- perl="${PERL}" ; \
+ perl="$(PERL)" ; \
rm -f $$tmp ; \
for program in $(SCRIPTS); do \
sed "s@^\(#!\)\(/[^ ]*/perl[^ ]*\)\(.*\)\$$@\1$$perl\3@" \
fi
validate_xml:
- @cd $(srcdir) && ./check-configs.pl --force $(EXES)
+ @cd $(srcdir) && $(PERL) check-configs.pl --force $(EXES)
munge_ad_file:
@echo "Updating hack list in XScreenSaver.ad.in..." ; \
- cd $(srcdir) ; ./munge-ad.pl ../driver/XScreenSaver.ad.in
+ cd $(srcdir) && $(PERL) munge-ad.pl ../driver/XScreenSaver.ad.in
distdepend:: check_men validate_xml munge_ad_file
int scrolled_p = False;
const char *kind = "?";
- int av[255];
+ int av[255] = { UNDEF };
int ac = 0;
int i;
/*
This is a port of the javascript 6502 assembler, compiler and
debugger. The orignal code was copyright 2006 by Stian Soreng -
- www.6502asm.com
+ https://web.archive.org/web/20070516072609/http%3A//www.6502asm.com/
I changed the structure of the assembler in this version.
*/
/*
This is a port of the javascript 6502 assembler, compiler and
debugger. The orignal code was copyright 2006 by Stian Soreng -
- www.6502asm.com
+ https://web.archive.org/web/20070516072609/http%3A//www.6502asm.com/
The stack space is in page $100 to $1ff. The video buffer starts at
$200 and is 1024 bytes. Programs get loaded at address
-/* xscreensaver, Copyright © 1998-2024 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1998-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
"This place is best shunned and left uninhabited."
};
int i, y = 0;
- int top, left0, left, right, y1;
+ int top, left0, left, right, y1 = 0;
Bool clownp = !(random() % 10);
Bool honorp = !clownp && !(random() % 20);
const char * const * lines = (clownp ? lines2 : honorp ? lines3 : lines1);
}
else if (i == 1)
{
-// y += font->ascent + font->descent;
+ /* y += font->ascent + font->descent; */
y1 = y;
-// y += font->ascent + font->descent;
+ /* y += font->ascent + font->descent; */
}
else if (i == 2)
{
a screen saver and locker for the X window system
by Jamie Zawinski
- version 6.10
- 27-Apr-2025
+ version 6.11
+ 01-Jul-2025
https://www.jwz.org/xscreensaver/
convert="invert"/>
<number id="animation_speed" type="slider" arg="--speed %"
- _label="Animation Speed" _low-label="Fast" _high-label="Slow"
- low="15" high="200" default="60"/>
+ _label="Animation Speed" _low-label="Slow" _high-label="Fast"
+ low="15" high="200" default="50"
+ convert="invert"/>
<number id="camera_speed" type="slider" arg="--camera_speed %"
_label="Camera Speed" _low-label="Slow" _high-label="Fast"
][, and the NES. Some example programs are included, and it can also
read in an assembly file as input.
-Original JavaScript Version by Stian Soreng:
-https://web.archive.org/web/20070516072609/http%3A//www.6502asm.com/
-Ported to XScreenSaver by Jeremy English.
-
Written by Stian Soreng and Jeremy English; 2007.
</_description>
</screensaver>
$(UTILS_BIN)/usleep.o \
$(UTILS_BIN)/xmu.o \
$(UTILS_BIN)/yarandom.o \
- ${FPS_OBJS} $(JWZGLES_OBJS) $(HACK_GLSL_OBJS) @ANIM_OBJS@
+ $(FPS_OBJS) $(JWZGLES_OBJS) $(HACK_GLSL_OBJS) @ANIM_OBJS@
HDRS = atlantis.h bubble3d.h buildlwo.h e_textures.h \
grab-ximage.h tube.h sphere.h boxed.h \
fi ; \
done ; \
\
- exes="${SETCAP_EXES}" ; \
+ exes="$(SETCAP_EXES)" ; \
if [ @SETCAP_HACKS@ = yes ]; then \
for program in $$exes; do \
echo $(PROG_SETCAP) $(SETCAP_FLAGS) $$idir/$$program ; \
munge-scripts: $(SCRIPTS)
@tmp=/tmp/mf.$$$$ ; \
- perl="${PERL}" ; \
+ perl="$(PERL)" ; \
rm -f $$tmp ; \
for program in $(SCRIPTS); do \
sed "s@^\(#!\)\(/[^ ]*/perl[^ ]*\)\(.*\)\$$@\1$$perl\3@" \
fi
validate_xml:
- @cd $(HACK_SRC) && ./check-configs.pl --force $(EXES) $(RETIRED_EXES)
+ @cd $(HACK_SRC) && $(PERL) check-configs.pl --force $(EXES) $(RETIRED_EXES)
distdepend:: check_men validate_xml
dumpsterfire.o: $(UTILS_SRC)/yarandom.h
dumpsterfire.o: $(HACK_SRC)/xlockmoreI.h
dumpsterfire.o: $(HACK_SRC)/xlockmore.h
+dumpster_model.o: ../../config.h
+dumpster_model.o: $(HACK_SRC)/fps.h
+dumpster_model.o: $(srcdir)/gllist.h
+dumpster_model.o: $(HACK_SRC)/recanim.h
+dumpster_model.o: $(HACK_SRC)/screenhackI.h
+dumpster_model.o: $(UTILS_SRC)/colors.h
+dumpster_model.o: $(UTILS_SRC)/erase.h
+dumpster_model.o: $(UTILS_SRC)/font-retry.h
+dumpster_model.o: $(UTILS_SRC)/grabclient.h
+dumpster_model.o: $(UTILS_SRC)/hsv.h
+dumpster_model.o: $(UTILS_SRC)/resources.h
+dumpster_model.o: $(UTILS_SRC)/usleep.h
+dumpster_model.o: $(UTILS_SRC)/visual.h
+dumpster_model.o: $(UTILS_SRC)/xft.h
+dumpster_model.o: $(UTILS_SRC)/yarandom.h
+dumpster_model.o: $(HACK_SRC)/xlockmoreI.h
dymaxionmap-coords.o: ../../config.h
dymaxionmap-coords.o: $(srcdir)/dymaxionmap-coords.h
dymaxionmap.o: ../../config.h
if (frame->loading.title && frame->loading.title[0] == '/')
{ /* strip filename to part after last /. */
char *s = strrchr (frame->loading.title, '/');
- if (s) strcpy (frame->loading.title, s+1);
+ if (s) memmove (frame->loading.title, s+1, strlen (s));
}
if (debug_p)
{
/* strip filename to part between last "/" and last ".". */
char *s = strrchr (img->title, '/');
- if (s) strcpy (img->title, s+1);
+ if (s) memmove (img->title, s+1, strlen (s));
s = strrchr (img->title, '.');
if (s) *s = 0;
}
/* strip filename to part between last "/" and end. */
/* xscreensaver-getimage has already stripped off the extension. */
char *s = strrchr (img->title, '/');
- if (s) strcpy (img->title, s+1);
+ if (s) memmove (img->title, s+1, strlen (s));
}
if (verbose_p)
#include "gltrackball.h"
#include "klondike-game.h"
-// random position offset for sloppy mode
-#define RANDOM_POSITION_OFFSET (bp->sloppy ? (((float)random()) / ((float)RAND_MAX) - 0.5) * 0.0125 : 0)
-
// static const char *suits[] = {"Diamonds", "Clubs", "Hearts", "Spades"};
// static const char *short_suits[] = {"D", "C", "H", "S"};
// static const char *ranks[] = {"", "Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"};
deck[index].is_face_up = 0;
deck[index].x = bp->deck_x;
deck[index].y = bp->deck_y;
- deck[index].start_x = bp->deck_x;
- deck[index].start_y = bp->deck_y;
- deck[index].dest_x = bp->deck_x;
- deck[index].dest_y = bp->deck_y;
+ deck[index].start_x = bp->deck_x + RANDOM_POSITION_OFFSET;
+ deck[index].start_y = bp->deck_y + RANDOM_POSITION_OFFSET;
+ deck[index].dest_x = bp->deck_x + RANDOM_POSITION_OFFSET;
+ deck[index].dest_y = bp->deck_y + RANDOM_POSITION_OFFSET;
deck[index].start_frame = 0;
deck[index].end_frame = 0;
deck[index].start_angle = 0.0f;
game_state->deck[i] = game_state->waste[i];
card_struct *animated_card = &game_state->deck[i];
- animated_card->start_frame = bp->tick + (i+5) * bp->animation_ticks / 10;
- animated_card->end_frame = bp->tick + (i+5) * bp->animation_ticks / 10 + bp->animation_ticks;
+ animated_card->start_frame = bp->tick + (i+5) * bp->animation_ticks / 3;
+ animated_card->end_frame = bp->tick + (i+5) * bp->animation_ticks / 3 + bp->animation_ticks;
animated_card->start_x = animated_card->x;
animated_card->start_y = animated_card->y;
animated_card->dest_x = bp->deck_x + RANDOM_POSITION_OFFSET;
#define MAX_TEXTURE 53
#define BACK_TEXTURE 52
+// random position offset for sloppy mode
+#define RANDOM_POSITION_OFFSET (bp->sloppy ? (((float)random()) / ((float)RAND_MAX) - 0.5) * 0.0125 : 0)
+
+
typedef enum { DIAMONDS, CLUBS, HEARTS, SPADES } Suit;
typedef enum { NONE=0, ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING } Rank;
#ifdef USE_GL /* whole file */\r
\r
#define DEF_CAMERA_SPEED "50"\r
-#define DEF_SPEED "60"\r
+#define DEF_SPEED "50"\r
#define DEF_DRAW_COUNT "3"\r
#define DEF_SLOPPY "True"\r
\r
}\r
\r
initialize_placeholders(&bps[MI_SCREEN(mi)], width, height);\r
+ bps[MI_SCREEN(mi)].redeal = 1;\r
+ \r
\r
glClear(GL_COLOR_BUFFER_BIT);\r
}\r
card->end_frame = card->start_frame + animation_ticks;\r
card->start_x = bp->deck_x;\r
card->start_y = bp->deck_y;\r
- card->dest_x = bp->tableau_placeholders[j].x;\r
- card->dest_y = bp->tableau_placeholders[j].y;\r
+ card->dest_x = bp->tableau_placeholders[j].x + RANDOM_POSITION_OFFSET;\r
+ card->dest_y = bp->tableau_placeholders[j].y + RANDOM_POSITION_OFFSET;\r
card->angle = 0.0f;\r
card->start_angle = 0.0f;\r
card->is_face_up = i == bp->game_state->tableau_size[j] - 1;\r
\r
bp->trackball = gltrackball_init (True);\r
\r
+\r
// initialize the game state\r
bp->game_state = (game_state_struct *)malloc(sizeof(game_state_struct));\r
bp->tick = 0;\r
for (int j = 0; j < bp->game_state->tableau_size[i]; j++)\r
{\r
card_struct *card = &bp->game_state->tableau[i][j];\r
- card->start_frame = bp->tick + n * animation_ticks / 12;\r
+ card->start_frame = bp->tick + n * animation_ticks / 3;\r
card->end_frame = card->start_frame + animation_ticks;\r
card->start_x = card->x;\r
card->start_y = card->y;\r
- card->dest_x = bp->deck_x;\r
- card->dest_y = bp->deck_y;\r
+ card->dest_x = bp->deck_x + RANDOM_POSITION_OFFSET;\r
+ card->dest_y = bp->deck_y + RANDOM_POSITION_OFFSET;\r
card->start_angle = card->angle;\r
card->end_angle = 360.0f;\r
card->is_face_up = 0;\r
for (int j = 0; j < bp->game_state->foundation_size[i]; j++)\r
{\r
card_struct *card = &bp->game_state->foundation[i][j];\r
- card->start_frame = bp->tick + n * animation_ticks / 12;\r
+ card->start_frame = bp->tick + n * animation_ticks / 3;\r
card->end_frame = card->start_frame + animation_ticks;\r
card->start_x = card->x;\r
card->start_y = card->y;\r
- card->dest_x = bp->deck_x;\r
- card->dest_y = bp->deck_y;\r
+ card->dest_x = bp->deck_x + RANDOM_POSITION_OFFSET;\r
+ card->dest_y = bp->deck_y + RANDOM_POSITION_OFFSET;\r
card->start_angle = card->angle;\r
card->end_angle = 360.0f;\r
card->is_face_up = 0;\r
for (int i = 0; i < bp->game_state->waste_size; i++)\r
{\r
card_struct *card = &bp->game_state->waste[i];\r
- card->start_frame = bp->tick + n * animation_ticks / 12;\r
+ card->start_frame = bp->tick + n * animation_ticks / 3;\r
card->end_frame = card->start_frame + animation_ticks;\r
card->start_x = card->x;\r
card->start_y = card->y;\r
- card->dest_x = bp->deck_x;\r
- card->dest_y = bp->deck_y;\r
+ card->dest_x = bp->deck_x + RANDOM_POSITION_OFFSET;\r
+ card->dest_y = bp->deck_y + RANDOM_POSITION_OFFSET;\r
card->start_angle = card->angle;\r
card->end_angle = 360.0f;\r
card->is_face_up = 0;\r
n++;\r
}\r
\r
- bp->final_animation = bp->tick + n * animation_ticks / 12 + animation_ticks;\r
+ bp->final_animation = bp->tick + n * animation_ticks / 3 + animation_ticks;\r
}\r
\r
ENTRYPOINT void\r
0.1, -0.0, 0, // Look-at point (center)\r
0, 0, 1); // Up vector\r
\r
- glRotatef (current_device_rotation(), 0, 0, 1);\r
- gltrackball_rotate (bp->trackball);\r
+ glRotatef(current_device_rotation(), 0, 0, 1);\r
+ gltrackball_rotate(bp->trackball);\r
\r
for (int i = 0; i < animatedCardCount; i++)\r
{\r
{\r
float n = ((float)bp->tick - (float)card->start_frame) / (card->end_frame - card->start_frame);\r
float eased = ease_out_quart(n);\r
- float eased2 = ease_in_out_quart(n);\r
\r
if (card->dest_x != card->start_x)\r
{\r
\r
if (card->end_angle != card->start_angle)\r
{\r
- card->angle = card->start_angle + eased2 * (card->end_angle - card->start_angle);\r
+ card->angle = card->start_angle + n * (card->end_angle - card->start_angle);\r
}\r
else\r
{\r
else if (bp->final_animation == 0 && n == NULL)\r
{\r
animate_board_to_deck(bp);\r
-\r
- // Check for win\r
-# if 0 // unused \r
- int won_game = 0;\r
- for (int i = 0; i < 4; i++)\r
- {\r
- won_game &= (bp->game_state->foundation_size[i] == 13);\r
- }\r
-# endif \r
}\r
\r
if (n != NULL)\r
sprintf (buf, "%.3f\xC2\xB0, %.3f\xC2\xB0", bp->pos.lat, bp->pos.lon);
# elif 0
/* 37° 46' 15.63" N, 122° 24' 45.70" W */
- /* This screws up the label image. Some kind of bug in texfont.c?
- But only on X11, not Cocoa. */
double alat = bp->pos.lat >= 0 ? bp->pos.lat : -bp->pos.lat;
double alon = bp->pos.lon >= 0 ? bp->pos.lon : -bp->pos.lon;
sprintf (buf,
-/* photopile, Copyright (c) 2008-2018 Jens Kilian <jjk@acm.org>
- * Based on carousel, Copyright (c) 2005-2008 Jamie Zawinskin <jwz@jwz.org>
+/* photopile, Copyright © 2008-2018 Jens Kilian <jjk@acm.org>
+ * Based on carousel, Copyright © 2005-2008 Jamie Zawinski <jwz@jwz.org>
* Loads a sequence of images and shuffles them into a pile.
*
* Permission to use, copy, modify, distribute, and sell this software and its
{
/* strip filename to part after last /. */
char *s = strrchr (frame->title, '/');
- if (s) strcpy (frame->title, s+1);
+ if (s) memmove (frame->title, s+1, strlen (s));
}
if (debug_p)
-/* texfont, Copyright © 2005-2022 Jamie Zawinski <jwz@jwz.org>
+/* texfont, Copyright © 2005-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
XImage *image = 0;
unsigned char *data = (unsigned char *) calloc (w2 * 2, (h2 + 1));
unsigned char *out = data;
+ GLint rowpack = 0;
+ GLint alignment = 0;
/* OpenGLES doesn't support GL_INTENSITY, so instead of using a
texture with 1 byte per pixel, the intensity value, we have
image = 0;
+ glGetIntegerv (GL_UNPACK_ROW_LENGTH, &rowpack);
+ glGetIntegerv (GL_UNPACK_ALIGNMENT, &alignment);
+
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+
{
# ifdef GL_INTENSITY
GLuint iformat = GL_INTENSITY;
}
}
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, rowpack);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, alignment);
+
{
char msg[100];
sprintf (msg, "texture font %s (%d x %d)",
!get_boolean_resource (dpy, "texFontOmitDropShadow", "Boolean");
data->mipmap_p = True;
+
+# if defined(__APPLE__) && !defined(HAVE_COCOA) /* macOS X11 */
+ /* Some time before macOS 14.7.3, gluBuild2DMipmaps() started segfaulting. */
+ data->mipmap_p = False;
+# endif
+
# ifdef HAVE_JWZGLES
/* This would work, but it's wasteful for no benefit. */
/* Wait, is it ever useful? */
; 6502 assembler Sierpinsky Triangle ver.2
; by Magnus Wedmark 2007-05-02
; This program is especially written for
-; the 6502asm.com competition and
+; the 6502asm dot com competition and
; uses the 32*32 pixel display used in that
; virtual platform. The sierpinsky
; fractal is one of the simplest to
.BR xanalogtv (MANSUFFIX),
.BR apple2 (MANSUFFIX)
.SH COPYRIGHT
-Original Javascript Version by Stian Soreng - www.6502asm.com; 2006.
-Ported to XScreenSaver by Jeremy English; 2007.
-
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
suitability of this software for any purpose. It is provided "as is"
without express or implied warranty.
.SH AUTHOR
-Original Javascript Version by Stian Soreng - www.6502asm.com; 2006.
+Original Javascript Version by Stian Soreng; 2006.
Ported to XScreenSaver by Jeremy English; 2007.
-
my $progname = $0; $progname =~ s@.*/@@g;
-my ($version) = ('$Revision: 1.195 $' =~ m/\s(\d[.\d]+)\s/s);
+my ($version) = ('$Revision: 1.196 $' =~ m/\s(\d[.\d]+)\s/s);
my $copyright = "WebCollage $version, Copyright © 1999-2024" .
" Jamie Zawinski <jwz\@jwz.org>\n" .
" https://www.jwz.org/webcollage/\n";
my $delay = 2;
+my $max_load = 0;
+
# Like system, but prints status about exit codes, and kills this process
# with whatever signal killed the sub-process, if any.
#
}
+# If the load average is higher than the --max-load arg, wait.
+sub await_load() {
+ return unless $max_load;
+ my $load;
+ my $count = 0;
+ do {
+ my $line = `cat /proc/loadavg 2>&-`;
+ ($load) = ($line =~ m/^([\d.]+)/s);
+ return unless defined ($load);
+ if ($load < $max_load) {
+ LOG($verbose_warnings, "Load is $load - resuming") if ($count);
+ return;
+ } else {
+ LOG($verbose_warnings, "Load is $load - pausing")
+ if ($count == 0 || $verbose_load);
+ sleep (60);
+ }
+ $count++;
+ } while ($load > $max_load);
+}
+
+
my $png_to_root_window_cmd = undef;
unlink $image_tmp1, $image_tmp2;
sleep $delay;
+ await_load();
}
}
$delay = shift @ARGV;
} elsif (m/^--?timeout$/s) {
$http_timeout = shift @ARGV;
+ } elsif (m/^--?max-load$/s) {
+ $max_load = shift @ARGV;
} elsif (m/^--?filter$/s) {
$filter_cmd = shift @ARGV;
} elsif (m/^--?filter2$/s) {
-/* xscreensaver, Copyright © 2001-2024 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 2001-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
Grabbing screen images works in a few different ways:
A: If the hack was invoked by XScreenSaver, then before it blanked the
- screen, "xscreensaver-gfx" (or "xscreensaver-settings", in preview
- mode) saved a screenshot as a pixmap on a property on the saver
- window. This code loads that pixmap, and crops or scales it as
+ screen, "xscreensaver-gfx" (or "xscreensaver-settings", in preview mode)
+ saved a screenshot of the whole desktop as a pixmap on a property on the
+ saver window. This code loads that pixmap, and crops or scales it as
needed. This means that the screenshot used will always be what the
desktop looked like at the time that the screen blanked, not what it
might look like now if the screen happened to be un-blanked.
- B: If the pre-saved pixmap isn't there, then we do it the hard way:
- un-map our window to expose what is under it; wait an arbitrary
- amount of time for all other processes to re-paint their windows;
- copy a screen image; put our window back; and then return that image.
- This method is slow and unreliable, as there is no way to know how
- long we have to wait for the re-paint, and if you don't wait long
+ B: Under plain-old X11, if the pre-saved pixmap isn't there, then we do it
+ the hard way: un-map our window to expose what is under it; wait an
+ arbitrary amount of time for all other processes to re-paint their
+ windows; copy a screen image; put our window back; and then return that
+ image. This method is slow and unreliable, as there is no way to know
+ how long we have to wait for the re-paint, and if you don't wait long
enough, you get all black. E.g. on 2022 Raspbian 11.5/Pi4b/LXDE, it
takes nearly *five seconds* for the frame buffer to update, which is
truly awful. Also, the unmapping and remapping causes ugly flicker,
XQuartz doesn't let you make screenshots by copying the X11 root
window, so this instead runs "/usr/sbin/screencapture" to get the
Mac desktop image as a file.
+
+ D: On Wayland: We try to use "/usr/bin/grim" to get the desktop image
+ as a file. That program is not installed by default on all Wayland
+ systems; and it doesn't work under GNOME or KDE.
+
+ "grim: compositor doesn't support wlr-screencopy-unstable-v1".
+
+ There is no way for XScreenSaver to get a screen image under GNOME
+ or KDE. They invented their own, incompatible APIs, which always
+ play a camera noise and flash the screen white, and might also
+ pop up a confirmation dialog.
*/
#include "utils.h"
#endif
-#ifdef __APPLE__
- /* On macOS under X11, the usual X11 mechanism of getting a screen shot
- doesn't work, and we need to use an external program. This is only
- used when running under X11 on macOS. If it's a Cocoa build, this
- path is not taken, and OSX/grabclient-osx.m is used instead.
- */
-# define USE_EXTERNAL_SCREEN_GRABBER
-#endif
-
-
const char *progclass = "XScreenSaver";
XrmDatabase db;
XtAppContext app;
} grab_type;
-#define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video"
-#define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file"
-#define GETIMAGE_SCREEN_PROGRAM "screencapture"
+#if defined(__APPLE__) && !defined(HAVE_COCOA)
+# define HAVE_MACOS_X11
+#elif !defined(HAVE_JWXYZ) /* Real X11, possibly Wayland */
+# define HAVE_REAL_X11
+#endif
+
+#define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video"
+#define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file"
+
+#ifdef HAVE_MACOS_X11
+ /* On macOS under X11, the usual X11 mechanism of getting a screen shot
+ doesn't work, and we need to use an external program. This is only
+ used when running under X11 on macOS. If it's a Cocoa build, this
+ path is not taken, and OSX/grabclient-osx.m is used instead.
+ */
+# define USE_EXTERNAL_SCREEN_GRABBER
+# define GETIMAGE_SCREEN_PROGRAM "screencapture"
+
+#elif defined(HAVE_REAL_X11)
+ /* Under real X11, we can grab the screen directly. Under XWayland,
+ we call out to an external program. */
+# define USE_EXTERNAL_SCREEN_GRABBER
+# define GETIMAGE_SCREEN_PROGRAM "grim"
+#endif
static int
/* Figure out what kind of scaling/positioning we ought to do to display
a src-sized image in a dest-sized window/pixmap. Returns the width
and height to which the image should be scaled, and the position where
- it should be displayed to center it.
+ it should be displayed to center it. The result may be letterboxed
+ or pillarboxed to center it, as well as being scaled.
*/
static void
compute_image_scaling (int src_w, int src_h,
to run. Returned pathname may be relative to 'directory', or absolute.
*/
static char *
-get_filename_1 (Screen *screen, const char *directory, grab_type type,
+get_filename_1 (Screen *screen, Window window,
+ const char *directory, grab_type type,
Bool verbose_p)
{
Display *dpy = DisplayOfScreen (screen);
static char rect[100];
if (!tmpdir) tmpdir = "/tmp";
- /* Grab all screens */
- XGetWindowAttributes (dpy, XRootWindowOfScreen (screen), &xgwa);
- sprintf (rect, "%d,%d,%d,%d", xgwa.x, xgwa.y, xgwa.width, xgwa.height);
+ XGetWindowAttributes (dpy, window, &xgwa);
+ window_root_offset (dpy, window, &xgwa.x, &xgwa.y);
outfile = (char *) malloc (strlen(tmpdir) + 100);
sprintf (outfile, "%s/xscreensaver.%08x.png",
tmpdir, random() % 0xFFFFFFFF);
- av[ac++] = GETIMAGE_SCREEN_PROGRAM;
+#if defined(HAVE_MACOS_X11)
+
+ sprintf (rect, "%d,%d,%d,%d", xgwa.x, xgwa.y, xgwa.width, xgwa.height);
+
+ av[ac++] = GETIMAGE_SCREEN_PROGRAM; /* "screencapture" */
av[ac++] = "-x"; /* no sound */
av[ac++] = "-C"; /* capture mouse */
av[ac++] = "-R"; /* rect */
av[ac++] = "-t"; /* file type */
av[ac++] = "png";
av[ac++] = outfile;
+
+# elif defined(HAVE_REAL_X11)
+
+ sprintf (rect, "%d,%d %dx%d", xgwa.x, xgwa.y, xgwa.width, xgwa.height);
+
+ av[ac++] = GETIMAGE_SCREEN_PROGRAM; /* "grim" */
+ av[ac++] = "-c"; /* capture mouse */
+ av[ac++] = "-g"; /* geometry */
+ av[ac++] = rect;
+ av[ac++] = "-t"; /* file type */
+ av[ac++] = "png";
+ av[ac++] = outfile;
+# else
+# error USE_EXTERNAL_SCREEN_GRABBER is wrong
+# endif
}
break;
-# endif
+# endif /* USE_EXTERNAL_SCREEN_GRABBER */
default:
abort();
L = strlen (buf);
while (L && buf[L-1] == '\n')
buf[--L] = 0;
-
+
+ /* In GRAB_DESK mode, outfile is already set.
+ Otherwise, is must be printed by the subprocess. */
if (! outfile)
{
if (!*buf) return 0;
if (stat (outfile_full, &st))
{
- fprintf (stderr, "%s: file does not exist: \"%s\"\n",
- blurb(), outfile_full);
- free (outfile);
+ if (verbose_p)
+ fprintf (stderr, "%s: file does not exist: \"%s\"\n",
+ blurb(), outfile_full);
+ if (outfile_full != outfile)
+ free (outfile);
outfile = 0;
}
/* Returns a pathname to an image file. Free the string when you're done.
*/
static char *
-get_filename (Screen *screen, const char *directory, Bool verbose_p)
+get_filename (Screen *screen, Window window, const char *directory,
+ Bool verbose_p)
{
- return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
+ return get_filename_1 (screen, window, directory, GRAB_FILE, verbose_p);
}
Delete that file when you are done with it (and free the string.)
*/
static char *
-get_video_filename (Screen *screen, Bool verbose_p)
+get_video_filename (Screen *screen, Window window, Bool verbose_p)
{
- return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
+ return get_filename_1 (screen, window, 0, GRAB_VIDEO, verbose_p);
}
-/* Grabs a desktop image to a file, and returns a pathname to that file.
+/* Grabs a screenshot to a file, and returns a pathname to that file.
+ It will be the size of the provided window.
Delete that file when you are done with it (and free the string.)
*/
# ifdef USE_EXTERNAL_SCREEN_GRABBER
static char *
-get_desktop_filename (Screen *screen, Bool verbose_p)
+get_desktop_filename (Screen *screen, Window window, Bool verbose_p)
{
- return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
+ return get_filename_1 (screen, window, 0, GRAB_DESK, verbose_p);
}
#endif /* USE_EXTERNAL_SCREEN_GRABBER */
/* Grabs a video frame, and renders it on the Drawable.
- Returns False if it fails;
+ Returns False if it fails.
*/
static Bool
display_video (Screen *screen, Window window, Drawable drawable,
Bool verbose_p, XRectangle *geom_ret)
{
- char *filename = get_video_filename (screen, verbose_p);
+ char *filename = get_video_filename (screen, window, verbose_p);
Bool status;
if (!filename)
started with -install, we need to copy the contents of the default colormap
into the screensaver's colormap.
*/
+# ifndef HAVE_MACOS_X11
static void
copy_default_colormap_contents (Screen *screen,
Colormap to_cmap,
free (new_colors);
free (pixels);
}
+#endif /* !HAVE_MACOS_X11 */
/* Install the colormaps of all visible windows, deepest first.
raise_window (Display *dpy, Window window, Bool dont_wait, Bool verbose_p)
{
if (verbose_p)
- fprintf(stderr, "%s: raising window 0x%08lX (%s)\n",
+ fprintf(stderr, "%s: raising window 0x%0lX (%s)\n",
blurb(), (unsigned long) window,
(dont_wait ? "not waiting" : "waiting"));
/* Returns a pixmap of a screenshot, that is the size of the window
and covers that window's extent.
- This does it the hard way: by unmapping the window, waiting an arbitrary
- amount of time, copying some bits from the root, then re-mapping the
- window.
+ This unmaps the window, waits an arbitrary amount of time, grabs bits,
+ then re-maps the window. Grabbing bits happens either with XCopyArea
+ (on real X11) or by running an external program (macOS or Wayland).
*/
static Pixmap
-grab_screen_image_xcopyarea (Screen *screen, Window window, Bool verbose_p)
+grab_screen_image (Screen *screen, Window window, Bool cache_p, Bool verbose_p)
{
Display *dpy = DisplayOfScreen (screen);
XWindowAttributes xgwa;
Window real_root;
Bool root_p;
Bool saver_p;
+ Bool mapped_p;
double unmap_time = 0;
- Pixmap pixmap = None;
- XGCValues gcv;
- GC gc;
+ char *filename = 0;
+ Pixmap screenshot = None;
+ XRectangle geom;
real_root = XRootWindowOfScreen (screen); /* not vroot */
root_p = (window == real_root);
XGetWindowAttributes (dpy, window, &xgwa);
screen = xgwa.screen;
+ mapped_p = (xgwa.map_state == IsViewable);
+
+ if (!cache_p)
+ {
+ /* This is maybe overloading the meaning of --cache, but for fading
+ to work, we need to be able to grab a screenshot that includes
+ the window itself. This only comes into effect when using an
+ external screen grabber. */
+ mapped_p = 0;
+ unmap_time = 0;
+ }
if (root_p)
unmap_time = 0; /* real root window needs no delay */
- else if (saver_p)
+ else if (!mapped_p)
+ unmap_time = 0; /* not currently on screen */
+ else
{
unmap_time = get_float_resource (dpy, "grabRootDelay", "Seconds");
if (unmap_time <= 0.00001 || unmap_time > 20)
+ unmap_time = 0.33;
- /* 2022, Raspbian 11.5: after we call XUnmapWindow, it takes nearly
- *five seconds* for the framebuffer to update! If we delay for less
- than that, the window grabs an image of itself, which usually means
- black. One would normally suspect the compositor of being
- responsible for these sorts of shenanigans, but this is under
- LXDE...
-
- Oddly, running Debian 11.4 under VirtualBox (meaning slowwwwww)
- does not have this problem, and a delay of 0.33 is plenty.
-
- This problem does not afflict driver/fade.c as it just uses
- XCopyArea directly without needing to wait for other processes
- to react to the XUnmapWindow and re-paint.
-
- This nonsense is what led me to write screenshot.c and used a
- cached screenshot image instead.
- */
+ /* 2022, Raspbian 11.5: after we call XUnmapWindow, it takes nearly
+ *five seconds* for the framebuffer to update! If we delay for less
+ than that, the window grabs an image of itself, which usually means
+ black. One would normally suspect the compositor of being
+ responsible for these sorts of shenanigans, but this is under
+ LXDE...
+
+ Oddly, running Debian 11.4 under VirtualBox (meaning slowwwwww)
+ does not have this problem, and a delay of 0.33 is plenty.
+
+ This problem does not afflict driver/fade.c as it just uses
+ XCopyArea directly without needing to wait for other processes
+ to react to the XUnmapWindow and re-paint.
+
+ This nonsense is what led me to write screenshot.c and used a
+ cached screenshot image instead.
+ */
+ if (saver_p && unmap_time < 5.0)
unmap_time = 5.0;
}
- else /* managed window */
- {
- unmap_time = get_float_resource (dpy, "grabWindowDelay", "Seconds");
- if (unmap_time <= 0.00001 || unmap_time > 20)
- unmap_time = 0.33;
- }
if (verbose_p)
{
- fprintf (stderr, "\n%s: window 0x%08lX"
- " root: %d saver: %d wait: %.1f\n",
+ fprintf (stderr, "%s: window 0x%0lX"
+ " root: %d saver: %d map: %d wait: %.1f\n",
blurb(), (unsigned long) window,
- root_p, saver_p, unmap_time);
+ root_p, saver_p, mapped_p, unmap_time);
if (xgwa.visual->class != TrueColor)
{
}
- if (!root_p && !top_level_window_p (screen, window))
+ if (!root_p &&
+ mapped_p &&
+ !top_level_window_p (screen, window))
{
if (verbose_p)
- fprintf (stderr, "%s: not a top-level window: 0x%08lX: not grabbing\n",
+ fprintf (stderr, "%s: not a top-level window: 0x%0lX: not grabbing\n",
blurb(), (unsigned long) window);
return None;
}
-
if (unmap_time > 0)
{
if (verbose_p)
- fprintf (stderr, "%s: unmapping 0x%08lX\n", blurb(),
+ fprintf (stderr, "%s: unmapping 0x%0lX\n", blurb(),
(unsigned long) window);
XUnmapWindow (dpy, window);
if (xgwa.visual->class != TrueColor)
install_screen_colormaps (screen);
XSync (dpy, True);
+ if (verbose_p)
+ fprintf (stderr, "%s: sleeping %.02f\n", blurb(), unmap_time);
/* wait for everyone to swap in and handle exposes */
usleep ((unsigned long) (unmap_time * 1000000));
}
- /* Now that the window is off the screen and we have delayed, grab bits.
+ /* Now that the window is off the screen and we have delayed, grab bits,
+ then put the window back.
*/
- pixmap = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
-
- if (!root_p && xgwa.visual->class != TrueColor)
- copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual,
- verbose_p);
-
- gcv.function = GXcopy;
- gcv.subwindow_mode = IncludeInferiors;
- gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
- XCopyArea (dpy, real_root, pixmap, gc,
- xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
- XFreeGC (dpy, gc);
-
- if (!root_p && xgwa.visual->class != TrueColor)
- copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual,
- verbose_p);
-
- raise_window (dpy, window, saver_p, verbose_p);
-
- /* Generally it's bad news to call XInstallColormap() explicitly,
- but this file does a lot of sleazy stuff already... This is to
- make sure that the window's colormap is installed, even in the
- case where the window is OverrideRedirect. */
- if (xgwa.colormap && xgwa.visual->class != TrueColor)
- XInstallColormap (dpy, xgwa.colormap);
+# ifdef USE_EXTERNAL_SCREEN_GRABBER
- if (verbose_p)
- fprintf (stderr, "%s: grabbed screenshot to %s window\n", blurb(),
- (root_p ? "real root" : saver_p ? "saver" : "top-level"));
+# if defined(HAVE_REAL_X11)
+ if (getenv ("WAYLAND_DISPLAY") || getenv ("WAYLAND_SOCKET"))
+# endif
- return pixmap;
-}
+ {
+ /* Wayland or macOS: Grab screen via external program. */
+ if (verbose_p)
+ fprintf (stderr, "%s: grabbing via \"%s\"\n", blurb(),
+ GETIMAGE_SCREEN_PROGRAM);
+ filename = get_desktop_filename (screen, window, verbose_p);
-#ifdef USE_EXTERNAL_SCREEN_GRABBER
-static Pixmap
-grab_screen_image_external (Screen *screen, Window window, Bool verbose_p)
-{
- Display *dpy = DisplayOfScreen (screen);
- Window real_root;
- Bool root_p;
- Bool saver_p;
- double unmap_time = 0;
- XWindowAttributes xgwa;
- char *filename;
- Pixmap full, screenshot;
- XGCValues gcv;
- GC gc;
- XRectangle geom;
+ /* We can raise the window before parsing the file. */
+ if (unmap_time > 0)
+ raise_window (dpy, window, saver_p, verbose_p);
+ unmap_time = 0;
- real_root = XRootWindowOfScreen (screen); /* not vroot */
- root_p = (window == real_root);
- saver_p = xscreensaver_window_p (dpy, window);
+ if (!filename)
+ {
+ /* if (verbose_p) */
+ fprintf (stderr, "%s: screenshot via \"%s\" failed\n", blurb(),
+ GETIMAGE_SCREEN_PROGRAM);
+ return None;
+ }
- /* Using "screencapture" from within xscreensaver-settings doesn't work,
- since we don't unmap the xscreensaver-settings window first. */
- /* if (!root_p && !saver_p) return None; */
+ /* Read the file into a pixmap the size of the destination drawable. */
+ screenshot = XCreatePixmap (dpy, window, xgwa.width, xgwa.height,
+ xgwa.depth);
+ if (!screenshot)
+ {
+ unlink (filename);
+ return None;
+ }
- XGetWindowAttributes (dpy, XRootWindowOfScreen (screen), &xgwa);
- screen = xgwa.screen;
+# if defined(HAVE_GDK_PIXBUF)
+ if (! read_file_gdk (screen, window, screenshot, filename,
+ verbose_p, &geom))
+ {
+ unlink (filename);
+ XFreePixmap (dpy, screenshot);
+ return None;
+ }
+# elif defined(HAVE_JPEGLIB)
+ if (! read_file_jpeglib (screen, window, screenshot, filename,
+ verbose_p, &geom))
+ {
+ unlink (filename);
+ XFreePixmap (dpy, screenshot);
+ return None;
+ }
+# else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
+ /* shouldn't get here if we have no image-loading methods available. */
+ abort();
+# endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
- if (root_p)
- unmap_time = 0; /* real root window needs no delay */
- else
- {
- unmap_time = get_float_resource (dpy, "grabWindowDelay", "Seconds");
- if (unmap_time <= 0.00001 || unmap_time > 20)
+ if (unlink (filename))
{
- unmap_time = 0.33;
- if (saver_p) unmap_time = 5.0; /* WTF */
+ char buf[512];
+ sprintf (buf, "%s: rm %.100s", blurb(), filename);
+ perror (buf);
}
- }
+ else if (verbose_p)
+ fprintf (stderr, "%s: rm %s\n", blurb(), filename);
- if (unmap_time > 0)
- {
if (verbose_p)
- fprintf (stderr, "%s: unmapping 0x%08lX\n", blurb(),
- (unsigned long) window);
- XUnmapWindow (dpy, window);
- if (xgwa.visual->class != TrueColor)
- install_screen_colormaps (screen);
- if (verbose_p)
- fprintf (stderr, "%s: sleeping %.02f\n", blurb(), unmap_time);
- /* wait for everyone to swap in and handle exposes */
- usleep ((unsigned long) (unmap_time * 1000000));
+ fprintf (stderr, "%s: screenshot %dx%d+%d+%d via \"%s\"\n", blurb(),
+ xgwa.width, xgwa.height, xgwa.x, xgwa.y,
+ GETIMAGE_SCREEN_PROGRAM);
}
+# endif /* USE_EXTERNAL_SCREEN_GRABBER */
+# ifndef HAVE_MACOS_X11
+ else
+ {
+ /* Real X11: Grab screen via XCopyArea. */
- /* Now that the window is off the screen and we have delayed, grab bits,
- then put the window pack.
- */
- filename = get_desktop_filename (screen, verbose_p);
- raise_window (dpy, window, saver_p, verbose_p);
+ XGCValues gcv;
+ GC gc;
- if (!filename)
- {
if (verbose_p)
- fprintf (stderr, "%s: screenshot via \"%s\" failed\n", blurb(),
- GETIMAGE_SCREEN_PROGRAM);
- return False;
- }
+ fprintf (stderr, "%s: grabbing via XCopyArea\n", blurb());
- /* Read the file into a pixmap the size of the root window.
- If there are 2 screens, pixmap will be twice as wide as it needs
- to be and the screenshot will be centered on it, per 'geom'.
- */
- full = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
- if (!full)
- {
- unlink (filename);
- return False;
- }
+ screenshot = XCreatePixmap (dpy, window, xgwa.width, xgwa.height,
+ xgwa.depth);
-# if defined(HAVE_GDK_PIXBUF)
- if (! read_file_gdk (screen, window, full, filename, verbose_p, &geom))
- {
- unlink (filename);
- XFreePixmap (dpy, full);
- return False;
- }
-# elif defined(HAVE_JPEGLIB)
- if (! read_file_jpeglib (screen, window, full, filename, verbose_p, &geom))
- {
- unlink (filename);
- XFreePixmap (dpy, full);
- return False;
- }
-# else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
- /* shouldn't get here if we have no image-loading methods available. */
- abort();
-# endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
-
- if (unlink (filename))
- {
- char buf[512];
- sprintf (buf, "%s: rm %.100s", blurb(), filename);
- perror (buf);
- }
- else if (verbose_p)
- fprintf (stderr, "%s: rm %s\n", blurb(), filename);
+ if (!root_p && xgwa.visual->class != TrueColor)
+ copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual,
+ verbose_p);
- /* Retrieve the sub-rect of the full screen image that the window covers. */
- XGetWindowAttributes (dpy, window, &xgwa);
- if (!root_p && !saver_p)
- {
- /* Kludge for running inside xscreensaver-settings: show the upper
- left of the screen instead, since otherwise we would have captured
- the black rectangle that we are about to render into. */
- xgwa.x = xgwa.y = 0;
- }
- else
- {
+ gcv.function = GXcopy;
+ gcv.subwindow_mode = IncludeInferiors;
+ gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
window_root_offset (dpy, window, &xgwa.x, &xgwa.y);
+ XCopyArea (dpy, real_root, screenshot, gc,
+ xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
+ XFreeGC (dpy, gc);
+
+ if (!root_p && xgwa.visual->class != TrueColor)
+ copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual,
+ verbose_p);
}
+# endif /* !HAVE_MACOS_X11 */
- screenshot = XCreatePixmap (dpy, window, xgwa.width, xgwa.height,
- xgwa.depth);
- gc = XCreateGC (dpy, screenshot, 0, &gcv);
- XCopyArea (dpy, full, screenshot, gc,
- geom.x + xgwa.x, geom.y + xgwa.y,
- xgwa.width, xgwa.height, 0, 0);
- XFreeGC (dpy, gc);
- XFreePixmap (dpy, full);
+ if (unmap_time > 0)
+ raise_window (dpy, window, saver_p, verbose_p);
+
+ /* Generally it's bad news to call XInstallColormap() explicitly,
+ but this file does a lot of sleazy stuff already... This is to
+ make sure that the window's colormap is installed, even in the
+ case where the window is OverrideRedirect. */
+ if (xgwa.colormap && xgwa.visual->class != TrueColor)
+ XInstallColormap (dpy, xgwa.colormap);
if (verbose_p)
- fprintf (stderr, "%s: screenshot %dx%d+%d+%d via \"%s\"\n", blurb(),
- xgwa.width, xgwa.height, xgwa.x, xgwa.y,
- GETIMAGE_SCREEN_PROGRAM);
+ fprintf (stderr, "%s: grabbed screenshot to %s window\n", blurb(),
+ (root_p ? "real root" : saver_p ? "saver" : "top-level"));
return screenshot;
}
-#endif /* USE_EXTERNAL_SCREEN_GRABBER */
-/* Grabs a desktop screen shot onto the drawable and possibly the window.
+/* Grabs a desktop screen shot that is the size and position of the
+ Window (the bits under that window), and renders it onto the Drawable.
If the window and drawable are not the same size, the image in the
- drawable is scaled to fit.
- Returns False if it fails.
+ drawable is scaled. Returns False if it fails.
*/
static Bool
display_desktop (Screen *screen, Window window, Drawable drawable,
- Bool verbose_p, XRectangle *geom_ret)
+ Bool cache_p, Bool verbose_p, XRectangle *geom_ret)
{
Display *dpy = DisplayOfScreen (screen);
Window root;
if (verbose_p)
fprintf (stderr, "%s: grabbing desktop image\n", blurb());
- screenshot = screenshot_load (dpy, window, verbose_p);
+ root = XRootWindowOfScreen (screen); /* not vroot */
-# ifdef USE_EXTERNAL_SCREEN_GRABBER
- if (! screenshot)
- screenshot = grab_screen_image_external (screen, window, verbose_p);
-# endif /* USE_EXTERNAL_SCREEN_GRABBER */
+ /* See if we already have a saved screenshot of the whole desktop;
+ if so, grab a sub-rectangle from that, of *this* window. */
+ if (cache_p)
+ screenshot = screenshot_load (dpy, window, verbose_p);
- if (!screenshot && !top_level_window_p (screen, window))
+ /* Otherwise, grab a new one. */
+ if (! screenshot)
{
if (verbose_p)
- fprintf (stderr, "%s: 0x%x not top-level: can't grab desktop\n",
- blurb(), (unsigned int) window);
- return False;
+ fprintf (stderr, "%s: no saved screenshot on 0x%lx\n", blurb(),
+ window);
+ screenshot = grab_screen_image (screen, window, cache_p, verbose_p);
}
- if (! screenshot)
- screenshot = grab_screen_image_xcopyarea (screen, window, verbose_p);
-
if (!screenshot)
return False;
/* Grabs an image (from a file, video, or the desktop) and renders it on
- the Drawable. If `file' is specified, always use that file. Otherwise,
+ the Drawable. If 'file' is specified, always use that file. Otherwise,
select randomly, based on the other arguments.
*/
static void
Window window, Drawable drawable,
Bool verbose_p,
Bool desk_p,
+ Bool cache_p,
Bool video_p,
Bool image_p,
const char *dir,
if (file)
{
- desk_p = False;
+ desk_p = False;
video_p = False;
image_p = True;
}
image_p = False;
}
+
# ifndef _VROOT_H_
# error Error! This file definitely needs vroot.h!
# endif
- /* We can grab desktop images (using the normal X11 method) if:
- - the window is the real root window;
- - the window is a toplevel window.
- We cannot grab desktop images that way if:
- - the window is a non-top-level window.
-
- Under X11 on macOS, desktops are just like loaded image files.
- Under Cocoa on macOS, this code is not used at all.
- */
if (! (desk_p || video_p || image_p))
which = GRAB_BARS;
else
*/
if (which == GRAB_FILE && !file)
{
- file = get_filename (screen, dir, verbose_p);
+ file = get_filename (screen, window, dir, verbose_p);
if (!file)
{
which = GRAB_BARS;
break;
case GRAB_DESK:
- if (! display_desktop (screen, window, drawable, verbose_p, &geom))
+ if (! display_desktop (screen, window, drawable, cache_p,
+ verbose_p, &geom))
goto COLORBARS;
file_prop = "desktop";
break;
}
if (absfile) free (absfile);
+
+ XSync (dpy, False);
}
" --images / --no-images whether to allow image file loading\n" \
" --video / --no-video whether to allow video grabs\n" \
" --desktop / --no-desktop whether to allow desktop screen grabs\n"\
+ " --cache / --no-cache whether to cache the desktop image\n" \
" --directory <path> where to find image files to load\n" \
" --file <filename> load this image file\n" \
"\n" \
int i;
Bool verbose_p, grab_desktop_p, grab_video_p, random_image_p;
+ Bool cache_desktop_p;
char *image_directory;
progname = argv[0];
grab_video_p = get_boolean_resource(dpy, "grabVideoFrames", "Boolean");
random_image_p = get_boolean_resource(dpy, "chooseRandomImages", "Boolean");
image_directory = get_string_resource (dpy, "imageDirectory", "String");
+ cache_desktop_p = True;
+ if (!image_directory) image_directory = "";
if (!strncmp (image_directory, "~/", 2))
{
/* Have to re-process these, or else the .xscreensaver file
has priority over the command line...
*/
- if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
+ if (!strcmp (argv[i], "-v") ||
+ !strcmp (argv[i], "-vv") ||
+ !strcmp (argv[i], "-vvv") ||
+ !strcmp (argv[i], "-vvvv") ||
+ !strcmp (argv[i], "-vvvvv") ||
+ !strcmp (argv[i], "-vvvvvv") ||
+ !strcmp (argv[i], "-verbose"))
verbose_p = True;
- else if (!strcmp (argv[i], "-desktop")) grab_desktop_p = True;
- else if (!strcmp (argv[i], "-no-desktop")) grab_desktop_p = False;
- else if (!strcmp (argv[i], "-video")) grab_video_p = True;
- else if (!strcmp (argv[i], "-no-video")) grab_video_p = False;
- else if (!strcmp (argv[i], "-images")) random_image_p = True;
- else if (!strcmp (argv[i], "-no-images")) random_image_p = False;
+ else if (!strcmp (argv[i], "-desktop")) grab_desktop_p = True;
+ else if (!strcmp (argv[i], "-no-desktop")) grab_desktop_p = False;
+ else if (!strcmp (argv[i], "-cache")) cache_desktop_p = True;
+ else if (!strcmp (argv[i], "-no-cache")) cache_desktop_p = False;
+ else if (!strcmp (argv[i], "-video")) grab_video_p = True;
+ else if (!strcmp (argv[i], "-no-video")) grab_video_p = False;
+ else if (!strcmp (argv[i], "-images")) random_image_p = True;
+ else if (!strcmp (argv[i], "-no-images")) random_image_p = False;
else if (!strcmp (argv[i], "-file")) file = argv[++i];
else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
image_directory = argv[++i];
if (!drawable) drawable = window;
get_image (screen, window, drawable, verbose_p,
- grab_desktop_p, grab_video_p, random_image_p,
+ grab_desktop_p, cache_desktop_p, grab_video_p, random_image_p,
image_directory, file);
XSync (dpy, False);
exit (0);
-# Auto-generated: Mon Apr 28 12:45:42 PDT 2025
+# Auto-generated: Tue Jul 1 18:29:55 PDT 2025
driver/demo-Gtk-conf.c
driver/demo-Gtk.c
driver/demo.ui
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"gedurende de de jaren 70 en 80 in machines zoals de Atari 2600, Commodore "
"PET, VIC20 en C64, Apple ][ en de NES gebruikt. Enige voorbeeldprogramma's "
"zijn bijgevoegd en het kan ook een assembly-bestand als invoer lezen. "
-"Originele JavaScript versie door Stian Soreng: http://www.6502asm.com/. "
+"Originele JavaScript versie door Stian Soreng. "
"Overgebracht naar XScreenSaver door Jeremy English. Geschreven door Stian "
"Soreng en Jeremy English; 2007."
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"машинах, таких как Atari 2600, Commodore PET, VIC20 и C64, Apple] [ и NES. "
"Включены некоторые примеры программ, об этом также можно прочитать в файле "
"сборки в качестве входных данных. Оригинальная версия на JavaScript автора "
-"Стиан Сёренг: http://www.6502asm.com/. Портирована на XScreenSaver Джереми "
+"Стиан Сёренг. Портирована на XScreenSaver Джереми "
"Инглиш. Автор: Стиан Сёренг и Джереми Инглиш; 2007."
#: ../hacks/config/mapscroller.xml.h:1
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
"family of 6502 chips were used throughout the 70's and 80's in machines such "
"as the Atari 2600, Commodore PET, VIC20 and C64, Apple ][, and the NES. Some "
"example programs are included, and it can also read in an assembly file as "
-"input. Original JavaScript Version by Stian Soreng: http://www.6502asm.com/. "
+"input. Original JavaScript Version by Stian Soreng. "
"Ported to XScreenSaver by Jeremy English. Written by Stian Soreng and Jeremy "
"English; 2007."
msgstr ""
/* I first saw something like this, albeit in reverse, in an early Tetris
implementation for the Mac.
- -- Torbjörn Andersson <torbjorn@dev.eurotime.se>
+ -- Torbjörn Andersson <torbjorn@dev.eurotime.se>
*/
static void
slide_lines (eraser_state *st)
-/* xscreensaver, Copyright © 1992-2024 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1992-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
/* Store $DISPLAY into the environment, so that the $DISPLAY variable that
the spawned processes inherit is what we are actually using.
*/
+
+# if !(defined(__APPLE__) && !defined(HAVE_COCOA)) /* not macOS X11 */
+ /* XQuartz's weird $DISPLAY pathnames do not match DisplayString() */
+
const char *odpy = DisplayString (dpy);
char *ndpy = (char *) malloc(strlen(odpy) + 20);
strcpy (ndpy, "DISPLAY=");
strcat (ndpy, odpy);
-
- /* 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 ();
-# endif /* HAVE_PUTENV */
/* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2, MacOS)
do not. So we must leak it (and/or the previous setting). Yay.
*/
+# endif /* not macOS X11 */
}
-/* xscreensaver-command, Copyright © 2022 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver-command, Copyright © 2022-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
# include "config.h"
#endif
-#include <stdio.h>
-#include <stdlib.h>
-
#include <X11/Xproto.h> /* for CARD32 */
#include <X11/Xlib.h>
#include <X11/Xatom.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h> /* for waitpid() and associated macros */
+#endif
+
#include "screenshot.h"
#include "visual.h"
#include "../driver/blurb.h"
+#if defined(__APPLE__) && !defined(HAVE_COCOA)
+# define HAVE_MACOS_X11
+#elif !defined(HAVE_JWXYZ) /* Real X11, possibly Wayland */
+# define HAVE_REAL_X11
+#endif
+
static Atom XA_SCREENSAVER_SCREENSHOT = 0;
static void
It will be the size and extent of the given window,
or the full screen, as requested.
Might be None if we failed.
+ Somewhat duplicated in fade.c:xshm_screenshot_grab().
*/
Pixmap
screenshot_grab (Display *dpy, Window window, Bool full_screen_p,
GC gc;
struct { int x, y, x2, y2; } root, win;
XErrorHandler old_handler;
+ Bool external_p = False;
XGetWindowAttributes (dpy, window, &win_xgwa);
root_window = XRootWindowOfScreen (win_xgwa.screen);
return None;
}
-# ifdef __APPLE__ /* Can't XCopyArea root window under rootless XQuartz. */
- if (verbose_p)
- fprintf (stderr, "%s: no screenshot under XQuartz\n", blurb());
- return None;
-# endif
-
root.x = 0;
root.y = 0;
root.x2 = root_xgwa.width;
if (win.x2 > root.x2) win.x2 = root.x2;
if (win.y2 > root.y2) win.y2 = root.y2;
- gcv.function = GXcopy;
- gcv.subwindow_mode = IncludeInferiors;
+# ifdef HAVE_MACOS_X11
+ external_p = True;
+# else /* X11, possibly Wayland */
+ external_p = getenv ("WAYLAND_DISPLAY") || getenv ("WAYLAND_SOCKET");
+# endif
+
pixmap = XCreatePixmap (dpy, root_window,
win_xgwa.width, win_xgwa.height,
win_xgwa.depth);
- gc = XCreateGC (dpy, pixmap, GCFunction | GCSubwindowMode, &gcv);
-
- /* I do not understand why some systems get a BadMatch on this XCopyArea.
- (Sep 2022, Debian 11.5 x86 under VirtualBox.) */
- XSync (dpy, False);
- error_handler_hit_p = False;
- old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
-
- XCopyArea (dpy, root_window, pixmap, gc,
- win.x, win.y,
- win.x2 - win.x,
- win.y2 - win.y,
- 0, 0);
-
- XSync (dpy, False);
- XSetErrorHandler (old_handler);
- if (error_handler_hit_p)
+ XSync (dpy, False); /* So that the pixmap exists before we exec. */
+
+ /* Run xscreensaver-getimage to get a screenshot. It will, in turn,
+ run "screencapture" or "grim" to write the screenshot to a PNG file,
+ then will load that file onto our Pixmap.
+ */
+ if (external_p)
{
- XFreePixmap (dpy, pixmap);
- pixmap = 0;
- }
+ pid_t forked;
+ char *av[20];
+ int ac = 0;
+ char buf[1024];
+ char rootstr[20], pixstr[20];
+
+ sprintf (rootstr, "0x%0lx", (unsigned long) window);
+ sprintf (pixstr, "0x%0lx", (unsigned long) pixmap);
+ av[ac++] = "xscreensaver-getimage";
+ if (verbose_p)
+ av[ac++] = "--verbose";
+ av[ac++] = "--desktop";
+ av[ac++] = "--no-images";
+ av[ac++] = "--no-video";
+ av[ac++] = rootstr;
+ av[ac++] = pixstr;
+ av[ac] = 0;
- XFreeGC (dpy, gc);
+ if (verbose_p)
+ {
+ int i;
+ fprintf (stderr, "%s: screenshot: executing:", blurb());
+ for (i = 0; i < ac; i++)
+ fprintf (stderr, " %s", av[i]);
+ fprintf (stderr, "\n");
+ }
+
+ switch ((int) (forked = fork ())) {
+ case -1:
+ {
+ sprintf (buf, "%s: couldn't fork", blurb());
+ perror (buf);
+ return 0;
+ }
+ case 0:
+ {
+ close (ConnectionNumber (dpy)); /* close display fd */
+ execvp (av[0], av); /* shouldn't return. */
+ exit (-1); /* exits fork */
+ break;
+ }
+ default:
+ {
+ int wait_status = 0, exit_status = 0;
+ /* Wait for the child to die. */
+ waitpid (forked, &wait_status, 0);
+ exit_status = WEXITSTATUS (wait_status);
+ /* Treat exit code as a signed 8-bit quantity. */
+ if (exit_status & 0x80) exit_status |= ~0xFF;
+ if (exit_status != 0)
+ {
+ fprintf (stderr, "%s: screenshot: %s exited with %d\n",
+ blurb(), av[0], exit_status);
+ if (pixmap) XFreePixmap (dpy, pixmap);
+ return None;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Grab a screenshot using XCopyArea. */
+
+ gcv.function = GXcopy;
+ gcv.subwindow_mode = IncludeInferiors;
+ gc = XCreateGC (dpy, pixmap, GCFunction | GCSubwindowMode, &gcv);
+
+ /* I do not understand why some systems get a BadMatch on this XCopyArea.
+ (Sep 2022, Debian 11.5 x86 under VirtualBox.) */
+ XSync (dpy, False);
+ error_handler_hit_p = False;
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+ XCopyArea (dpy, root_window, pixmap, gc,
+ win.x, win.y,
+ win.x2 - win.x,
+ win.y2 - win.y,
+ 0, 0);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ if (error_handler_hit_p)
+ {
+ XFreePixmap (dpy, pixmap);
+ pixmap = 0;
+ }
+
+ XFreeGC (dpy, gc);
+ }
if (verbose_p || !pixmap)
fprintf (stderr, "%s: %s screenshot 0x%lx %dx%d"
}
-/* Loads the screenshot from screenshot_save() and returns a new pixmap
- that is the same size as the window.
+/* Loads the screenshot from screenshot_save() and returns a new pixmap that
+ covers and is the same size as the window. The saved screenshot is assumed
+ to be the size of the screen.
*/
Pixmap
screenshot_load (Display *dpy, Window window, Bool verbose_p)
/* Store the screenshot pixmap on a property on the window. */
extern void screenshot_save (Display *, Window, Pixmap);
-/* Loads the screenshot from screenshot_save() and returns a new pixmap
- that is the same size as the winow.
+/* Loads the screenshot from screenshot_save() and returns a new pixmap that
+ covers and is the same size as the window. The saved screenshot is assumed
+ to be the size of the screen.
*/
extern Pixmap screenshot_load (Display *, Window, Bool verbose_p);
static const char screensaver_id[] =
- "@(#)xscreensaver 6.10 (28-Apr-2025), by Jamie Zawinski (jwz@jwz.org)";
-#define XSCREENSAVER_VERSION "6.10"
-#define XSCREENSAVER_RELEASED 1745866800
+ "@(#)xscreensaver 6.11 (01-Jul-2025), by Jamie Zawinski (jwz@jwz.org)";
+#define XSCREENSAVER_VERSION "6.11"
+#define XSCREENSAVER_RELEASED 1751396400
%define name xscreensaver
-%define version 6.10
+%define version 6.11
Summary: X screen saver and locker
Name: %{name}
Version: %{version}
-Release: 1
-Epoch: 1
+Release: 0
License: BSD
Group: Amusements/Graphics
URL: https://www.jwz.org/xscreensaver/
Vendor: Jamie Zawinski <jwz@jwz.org>
Buildroot: %{_tmppath}/%{name}-root
+# Red Hat uses an epoch number to make RPM believe that their old RPM with
+# number "1:5.45" is newer than your "6.00". The technical term for this
+# is "a dick move". If that's happening to you, increment this number:
+#
+# Epoch: 2
+
BuildRequires: perl
BuildRequires: pkgconfig
BuildRequires: desktop-file-utils
BuildRequires: xorg-x11-proto-devel
BuildRequires: mesa-libGL-devel
BuildRequires: mesa-libGLU-devel
-BuildRequires: libgle-devel
+#BuildRequires: libgle-devel
BuildRequires: pam-devel
BuildRequires: systemd-devel
BuildRequires: gtk3-devel
BuildRequires: gettext-devel
BuildRequires: libjpeg-turbo-devel
-Requires: SysVinit
+#Requires: SysVinit
Requires: pam
Requires: /etc/pam.d/system-auth
-Requires: htmlview
-Requires: desktop-backgrounds-basic
+#Requires: htmlview
+#Requires: desktop-backgrounds-basic
Requires: xdg-utils
Requires: systemd-libs
Provides: xscreensaver
-Obsoletes: xscreensaver-base
-Obsoletes: xscreensaver-common
-Obsoletes: xscreensaver-data
-Obsoletes: xscreensaver-data-extra
-Obsoletes: xscreensaver-extra
-Obsoletes: xscreensaver-extra-base
-Obsoletes: xscreensaver-extra-gss
-Obsoletes: xscreensaver-extras
-Obsoletes: xscreensaver-extras-base
-Obsoletes: xscreensaver-extras-gss
-Obsoletes: xscreensaver-extrusion
-Obsoletes: xscreensaver-gl
-Obsoletes: xscreensaver-gl-base
-Obsoletes: xscreensaver-gl-extra
-Obsoletes: xscreensaver-gl-extra-gss
-Obsoletes: xscreensaver-gl-extras
-Obsoletes: xscreensaver-gl-extras-gss
-Obsoletes: xscreensaver-lang
-Obsoletes: xscreensaver-matrix
-Obsoletes: xscreensaver-bsod
-Obsoletes: xscreensaver-webcollage
-Obsoletes: xscreensaver-screensaver-bsod
-Obsoletes: xscreensaver-screensaver-webcollage
+Obsoletes: xscreensaver-base < %{version}
+Obsoletes: xscreensaver-common < %{version}
+Obsoletes: xscreensaver-data < %{version}
+Obsoletes: xscreensaver-data-extra < %{version}
+Obsoletes: xscreensaver-extra < %{version}
+Obsoletes: xscreensaver-extra-base < %{version}
+Obsoletes: xscreensaver-extra-gss < %{version}
+Obsoletes: xscreensaver-extras < %{version}
+Obsoletes: xscreensaver-extras-base < %{version}
+Obsoletes: xscreensaver-extras-gss < %{version}
+Obsoletes: xscreensaver-extrusion < %{version}
+Obsoletes: xscreensaver-gl < %{version}
+Obsoletes: xscreensaver-gl-base < %{version}
+Obsoletes: xscreensaver-gl-extra < %{version}
+Obsoletes: xscreensaver-gl-extra-gss < %{version}
+Obsoletes: xscreensaver-gl-extras < %{version}
+Obsoletes: xscreensaver-gl-extras-gss < %{version}
+Obsoletes: xscreensaver-lang < %{version}
+Obsoletes: xscreensaver-matrix < %{version}
+Obsoletes: xscreensaver-bsod < %{version}
+Obsoletes: xscreensaver-webcollage < %{version}
+Obsoletes: xscreensaver-screensaver-bsod < %{version}
+Obsoletes: xscreensaver-screensaver-webcollage < %{version}
+
%description
A modular screen saver and locker for the X Window System.
%prep
%setup -q
+autoreconf -v -f
+
if [ -x %{_datadir}/libtool/config.guess ]; then
# use system-wide copy
cp -p %{_datadir}/libtool/config.{sub,guess} .
-e 's@\(.*/pam\.d/\)@%config(missingok) \1@' \
> $dd/all.files
-%find_lang %{name}
-cat %{name}.lang >> $dd/all.files
+#%find_lang %{name}
+#cat %{name}.lang >> $dd/all.files
chmod -R a+r,u+w,og-w ${RPM_BUILD_ROOT}
%defattr(-,root,root)
%changelog
-* Mon Nov 16 1998 jwz
-- Created.
* Mon Jul 31 2023 jwz
- Splitting this into multiple packages is a support nightmare, please don't.
+* Mon Nov 16 1998 jwz
+- Created.