summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorEamon Walsh <ewalsh@tycho.nsa.gov>2009-09-17 19:05:06 -0400
committerEamon Walsh <ewalsh@tycho.nsa.gov>2009-09-17 19:05:06 -0400
commit72d98f6a2088ccacbe913d0523b08cd868f16470 (patch)
treec083e0fce6ee0f49ed4f0d313415563da8c26754 /plugins
Initial commit.
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Makefile.am188
-rw-r--r--plugins/Makefile.in1057
-rw-r--r--plugins/annotate.c963
-rw-r--r--plugins/blur.c3250
-rw-r--r--plugins/clone.c937
-rw-r--r--plugins/commands.c352
-rw-r--r--plugins/cube.c2233
-rw-r--r--plugins/dbus.c2596
-rw-r--r--plugins/decoration.c1759
-rw-r--r--plugins/fade.c1049
-rw-r--r--plugins/fuse.c1442
-rw-r--r--plugins/gconf.c849
-rw-r--r--plugins/glib.c306
-rw-r--r--plugins/gnomecompat.c344
-rw-r--r--plugins/ini.c1152
-rw-r--r--plugins/inotify.c316
-rw-r--r--plugins/kconfig.cpp752
-rw-r--r--plugins/minimize.c1059
-rw-r--r--plugins/move.c1048
-rw-r--r--plugins/obs.c746
-rw-r--r--plugins/place.c1762
-rw-r--r--plugins/png.c575
-rw-r--r--plugins/regex.c558
-rw-r--r--plugins/resize.c1499
-rw-r--r--plugins/rotate.c2018
-rw-r--r--plugins/scale.c2290
-rw-r--r--plugins/screenshot.c627
-rw-r--r--plugins/svg.c1053
-rw-r--r--plugins/switcher.c2134
-rw-r--r--plugins/video.c1329
-rw-r--r--plugins/water.c1824
-rw-r--r--plugins/wobbly.c2967
-rw-r--r--plugins/zoom.c1203
33 files changed, 42237 insertions, 0 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
new file mode 100644
index 0000000..0bb4d51
--- /dev/null
+++ b/plugins/Makefile.am
@@ -0,0 +1,188 @@
+libfade_la_LDFLAGS = -module -avoid-version -no-undefined
+libfade_la_SOURCES = fade.c
+
+libcube_la_LDFLAGS = -module -avoid-version -no-undefined
+libcube_la_SOURCES = cube.c
+
+librotate_la_LDFLAGS = -module -avoid-version -no-undefined
+librotate_la_SOURCES = rotate.c
+
+libzoom_la_LDFLAGS = -module -avoid-version -no-undefined
+libzoom_la_SOURCES = zoom.c
+
+libscale_la_LDFLAGS = -module -avoid-version -no-undefined
+libscale_la_SOURCES = scale.c
+
+libwobbly_la_LDFLAGS = -module -avoid-version -no-undefined
+libwobbly_la_SOURCES = wobbly.c
+
+libminimize_la_LDFLAGS = -module -avoid-version -no-undefined
+libminimize_la_SOURCES = minimize.c
+
+libmove_la_LDFLAGS = -module -avoid-version -no-undefined
+libmove_la_SOURCES = move.c
+
+libresize_la_LDFLAGS = -module -avoid-version -no-undefined
+libresize_la_SOURCES = resize.c
+
+libplace_la_LDFLAGS = -module -avoid-version -no-undefined
+libplace_la_SOURCES = place.c
+
+libdecoration_la_DEPENDENCIES = $(top_builddir)/libdecoration/libdecoration.la
+libdecoration_la_LDFLAGS = -module -avoid-version -no-undefined
+libdecoration_la_LIBADD = $(top_builddir)/libdecoration/libdecoration.la
+libdecoration_la_SOURCES = decoration.c
+
+libswitcher_la_LDFLAGS = -module -avoid-version -no-undefined
+libswitcher_la_SOURCES = switcher.c
+
+libwater_la_LDFLAGS = -module -avoid-version -no-undefined
+libwater_la_SOURCES = water.c
+
+libscreenshot_la_LDFLAGS = -module -avoid-version -no-undefined
+libscreenshot_la_SOURCES = screenshot.c
+
+libclone_la_LDFLAGS = -module -avoid-version -no-undefined
+libclone_la_SOURCES = clone.c
+
+libpng_la_LDFLAGS = -module -avoid-version -no-undefined
+libpng_la_LIBADD = @LIBPNG_LIBS@
+libpng_la_SOURCES = png.c
+
+libblur_la_DEPENDENCIES = $(top_builddir)/libdecoration/libdecoration.la
+libblur_la_LDFLAGS = -module -avoid-version -no-undefined
+libblur_la_LIBADD = $(top_builddir)/libdecoration/libdecoration.la -lGLU
+libblur_la_SOURCES = blur.c
+
+libregex_la_LDFLAGS = -module -avoid-version -no-undefined
+libregex_la_SOURCES = regex.c
+
+libvideo_la_DEPENDENCIES = $(top_builddir)/libdecoration/libdecoration.la
+libvideo_la_LDFLAGS = -module -avoid-version -no-undefined
+libvideo_la_LIBADD = $(top_builddir)/libdecoration/libdecoration.la
+libvideo_la_SOURCES = video.c
+
+libini_la_LDFLAGS = -module -avoid-version -no-undefined
+libini_la_SOURCES = ini.c
+
+libobs_la_LDFLAGS = -module -avoid-version -no-undefined
+libobs_la_SOURCES = obs.c
+
+libcommands_la_LDFLAGS = -module -avoid-version -no-undefined
+libcommands_la_SOURCES = commands.c
+
+libgnomecompat_la_LDFLAGS = -module -avoid-version -no-undefined
+libgnomecompat_la_SOURCES = gnomecompat.c
+
+if USE_LIBRSVG
+libsvg_la_DEPENDENCIES = $(top_builddir)/libdecoration/libdecoration.la
+libsvg_la_LDFLAGS = -module -avoid-version -no-undefined
+libsvg_la_LIBADD = \
+ $(top_builddir)/libdecoration/libdecoration.la \
+ @LIBRSVG_LIBS@
+libsvg_la_SOURCES = svg.c
+libsvg_module = libsvg.la
+endif
+
+if ANNOTATE_PLUGIN
+libannotate_la_LDFLAGS = -module -avoid-version -no-undefined
+libannotate_la_LIBADD = @ANNOTATE_LIBS@
+libannotate_la_SOURCES = annotate.c
+libannotate_module = libannotate.la
+endif
+
+if USE_GLIB
+libglib_la_LDFLAGS = -module -avoid-version -no-undefined
+libglib_la_LIBADD = @GLIB_LIBS@
+libglib_la_SOURCES = glib.c
+libglib_module = libglib.la
+endif
+
+if USE_GCONF
+libgconf_la_LDFLAGS = -module -avoid-version -no-undefined
+libgconf_la_LIBADD = @GCONF_LIBS@
+libgconf_la_SOURCES = gconf.c
+libgconf_module = libgconf.la
+endif
+
+if USE_KCONFIG
+libkconfig_la_LDFLAGS = -module -avoid-version -no-undefined
+libkconfig_la_LIBADD = @KCONFIG_LIBS@
+libkconfig_la_SOURCES = kconfig.cpp
+libkconfig_module = libkconfig.la
+endif
+
+if DBUS_PLUGIN
+libdbus_la_LDFLAGS = -module -avoid-version -no-undefined
+libdbus_la_LIBADD = @DBUS_LIBS@
+libdbus_la_SOURCES = dbus.c
+libdbus_module = libdbus.la
+endif
+
+if INOTIFY_PLUGIN
+libinotify_la_LDFLAGS = -module -avoid-version -no-undefined
+libinotify_la_SOURCES = inotify.c
+libinotify_module = libinotify.la
+endif
+
+if FUSE_PLUGIN
+libfs_la_LDFLAGS = -module -avoid-version -no-undefined
+libfs_la_LIBADD = @FUSE_LIBS@
+libfs_la_SOURCES = fuse.c
+libfs_module = libfs.la
+endif
+
+INCLUDES = \
+ @COMPIZ_CFLAGS@ \
+ @LIBPNG_CFLAGS@ \
+ @LIBRSVG_CFLAGS@ \
+ @ANNOTATE_CFLAGS@ \
+ @GCONF_CFLAGS@ \
+ @KCONFIG_CFLAGS@ \
+ @DBUS_CFLAGS@ \
+ @GLIB_CFLAGS@ \
+ @FUSE_CFLAGS@ \
+ -DFUSE_USE_VERSION=26 \
+ -DALL_LINGUAS="\"@ALL_LINGUAS@\"" \
+ -DLOCALEDIR="\"@datadir@/locale\"" \
+ -DPLUGINDIR=\"$(plugindir)\" \
+ -DIMAGEDIR=\"$(imagedir)\" \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ -DMETADATADIR=\"$(metadatadir)\"
+
+moduledir = $(plugindir)
+
+module_LTLIBRARIES = \
+ $(libglib_module) \
+ $(libgconf_module) \
+ $(libkconfig_module) \
+ libdecoration.la \
+ libwobbly.la \
+ libfade.la \
+ libminimize.la \
+ libcube.la \
+ librotate.la \
+ libzoom.la \
+ libscale.la \
+ libmove.la \
+ libresize.la \
+ libplace.la \
+ $(libdbus_module) \
+ libswitcher.la \
+ libwater.la \
+ libscreenshot.la \
+ libclone.la \
+ libpng.la \
+ libblur.la \
+ libregex.la \
+ libvideo.la \
+ libini.la \
+ libobs.la \
+ libcommands.la \
+ libgnomecompat.la \
+ $(libsvg_module) \
+ $(libannotate_module) \
+ $(libinotify_module) \
+ $(libfs_module)
+
diff --git a/plugins/Makefile.in b/plugins/Makefile.in
new file mode 100644
index 0000000..128016f
--- /dev/null
+++ b/plugins/Makefile.in
@@ -0,0 +1,1057 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plugins
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(moduledir)"
+moduleLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(module_LTLIBRARIES)
+libannotate_la_DEPENDENCIES =
+am__libannotate_la_SOURCES_DIST = annotate.c
+@ANNOTATE_PLUGIN_TRUE@am_libannotate_la_OBJECTS = annotate.lo
+libannotate_la_OBJECTS = $(am_libannotate_la_OBJECTS)
+libannotate_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libannotate_la_LDFLAGS) $(LDFLAGS) -o $@
+@ANNOTATE_PLUGIN_TRUE@am_libannotate_la_rpath = -rpath $(moduledir)
+am_libblur_la_OBJECTS = blur.lo
+libblur_la_OBJECTS = $(am_libblur_la_OBJECTS)
+libblur_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libblur_la_LDFLAGS) $(LDFLAGS) -o $@
+libclone_la_LIBADD =
+am_libclone_la_OBJECTS = clone.lo
+libclone_la_OBJECTS = $(am_libclone_la_OBJECTS)
+libclone_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libclone_la_LDFLAGS) $(LDFLAGS) -o $@
+libcommands_la_LIBADD =
+am_libcommands_la_OBJECTS = commands.lo
+libcommands_la_OBJECTS = $(am_libcommands_la_OBJECTS)
+libcommands_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libcommands_la_LDFLAGS) $(LDFLAGS) -o $@
+libcube_la_LIBADD =
+am_libcube_la_OBJECTS = cube.lo
+libcube_la_OBJECTS = $(am_libcube_la_OBJECTS)
+libcube_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libcube_la_LDFLAGS) $(LDFLAGS) -o $@
+libdbus_la_DEPENDENCIES =
+am__libdbus_la_SOURCES_DIST = dbus.c
+@DBUS_PLUGIN_TRUE@am_libdbus_la_OBJECTS = dbus.lo
+libdbus_la_OBJECTS = $(am_libdbus_la_OBJECTS)
+libdbus_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libdbus_la_LDFLAGS) $(LDFLAGS) -o $@
+@DBUS_PLUGIN_TRUE@am_libdbus_la_rpath = -rpath $(moduledir)
+am_libdecoration_la_OBJECTS = decoration.lo
+libdecoration_la_OBJECTS = $(am_libdecoration_la_OBJECTS)
+libdecoration_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libdecoration_la_LDFLAGS) $(LDFLAGS) -o $@
+libfade_la_LIBADD =
+am_libfade_la_OBJECTS = fade.lo
+libfade_la_OBJECTS = $(am_libfade_la_OBJECTS)
+libfade_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libfade_la_LDFLAGS) $(LDFLAGS) -o $@
+libfs_la_DEPENDENCIES =
+am__libfs_la_SOURCES_DIST = fuse.c
+@FUSE_PLUGIN_TRUE@am_libfs_la_OBJECTS = fuse.lo
+libfs_la_OBJECTS = $(am_libfs_la_OBJECTS)
+libfs_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(libfs_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@FUSE_PLUGIN_TRUE@am_libfs_la_rpath = -rpath $(moduledir)
+libgconf_la_DEPENDENCIES =
+am__libgconf_la_SOURCES_DIST = gconf.c
+@USE_GCONF_TRUE@am_libgconf_la_OBJECTS = gconf.lo
+libgconf_la_OBJECTS = $(am_libgconf_la_OBJECTS)
+libgconf_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgconf_la_LDFLAGS) $(LDFLAGS) -o $@
+@USE_GCONF_TRUE@am_libgconf_la_rpath = -rpath $(moduledir)
+libglib_la_DEPENDENCIES =
+am__libglib_la_SOURCES_DIST = glib.c
+@USE_GLIB_TRUE@am_libglib_la_OBJECTS = glib.lo
+libglib_la_OBJECTS = $(am_libglib_la_OBJECTS)
+libglib_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libglib_la_LDFLAGS) $(LDFLAGS) -o $@
+@USE_GLIB_TRUE@am_libglib_la_rpath = -rpath $(moduledir)
+libgnomecompat_la_LIBADD =
+am_libgnomecompat_la_OBJECTS = gnomecompat.lo
+libgnomecompat_la_OBJECTS = $(am_libgnomecompat_la_OBJECTS)
+libgnomecompat_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgnomecompat_la_LDFLAGS) $(LDFLAGS) -o $@
+libini_la_LIBADD =
+am_libini_la_OBJECTS = ini.lo
+libini_la_OBJECTS = $(am_libini_la_OBJECTS)
+libini_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libini_la_LDFLAGS) $(LDFLAGS) -o $@
+libinotify_la_LIBADD =
+am__libinotify_la_SOURCES_DIST = inotify.c
+@INOTIFY_PLUGIN_TRUE@am_libinotify_la_OBJECTS = inotify.lo
+libinotify_la_OBJECTS = $(am_libinotify_la_OBJECTS)
+libinotify_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libinotify_la_LDFLAGS) $(LDFLAGS) -o $@
+@INOTIFY_PLUGIN_TRUE@am_libinotify_la_rpath = -rpath $(moduledir)
+libkconfig_la_DEPENDENCIES =
+am__libkconfig_la_SOURCES_DIST = kconfig.cpp
+@USE_KCONFIG_TRUE@am_libkconfig_la_OBJECTS = kconfig.lo
+libkconfig_la_OBJECTS = $(am_libkconfig_la_OBJECTS)
+libkconfig_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(libkconfig_la_LDFLAGS) $(LDFLAGS) -o $@
+@USE_KCONFIG_TRUE@am_libkconfig_la_rpath = -rpath $(moduledir)
+libminimize_la_LIBADD =
+am_libminimize_la_OBJECTS = minimize.lo
+libminimize_la_OBJECTS = $(am_libminimize_la_OBJECTS)
+libminimize_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libminimize_la_LDFLAGS) $(LDFLAGS) -o $@
+libmove_la_LIBADD =
+am_libmove_la_OBJECTS = move.lo
+libmove_la_OBJECTS = $(am_libmove_la_OBJECTS)
+libmove_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libmove_la_LDFLAGS) $(LDFLAGS) -o $@
+libobs_la_LIBADD =
+am_libobs_la_OBJECTS = obs.lo
+libobs_la_OBJECTS = $(am_libobs_la_OBJECTS)
+libobs_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libobs_la_LDFLAGS) $(LDFLAGS) -o $@
+libplace_la_LIBADD =
+am_libplace_la_OBJECTS = place.lo
+libplace_la_OBJECTS = $(am_libplace_la_OBJECTS)
+libplace_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libplace_la_LDFLAGS) $(LDFLAGS) -o $@
+libpng_la_DEPENDENCIES =
+am_libpng_la_OBJECTS = png.lo
+libpng_la_OBJECTS = $(am_libpng_la_OBJECTS)
+libpng_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libpng_la_LDFLAGS) $(LDFLAGS) -o $@
+libregex_la_LIBADD =
+am_libregex_la_OBJECTS = regex.lo
+libregex_la_OBJECTS = $(am_libregex_la_OBJECTS)
+libregex_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libregex_la_LDFLAGS) $(LDFLAGS) -o $@
+libresize_la_LIBADD =
+am_libresize_la_OBJECTS = resize.lo
+libresize_la_OBJECTS = $(am_libresize_la_OBJECTS)
+libresize_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libresize_la_LDFLAGS) $(LDFLAGS) -o $@
+librotate_la_LIBADD =
+am_librotate_la_OBJECTS = rotate.lo
+librotate_la_OBJECTS = $(am_librotate_la_OBJECTS)
+librotate_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(librotate_la_LDFLAGS) $(LDFLAGS) -o $@
+libscale_la_LIBADD =
+am_libscale_la_OBJECTS = scale.lo
+libscale_la_OBJECTS = $(am_libscale_la_OBJECTS)
+libscale_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libscale_la_LDFLAGS) $(LDFLAGS) -o $@
+libscreenshot_la_LIBADD =
+am_libscreenshot_la_OBJECTS = screenshot.lo
+libscreenshot_la_OBJECTS = $(am_libscreenshot_la_OBJECTS)
+libscreenshot_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libscreenshot_la_LDFLAGS) $(LDFLAGS) -o $@
+am__libsvg_la_SOURCES_DIST = svg.c
+@USE_LIBRSVG_TRUE@am_libsvg_la_OBJECTS = svg.lo
+libsvg_la_OBJECTS = $(am_libsvg_la_OBJECTS)
+libsvg_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libsvg_la_LDFLAGS) $(LDFLAGS) -o $@
+@USE_LIBRSVG_TRUE@am_libsvg_la_rpath = -rpath $(moduledir)
+libswitcher_la_LIBADD =
+am_libswitcher_la_OBJECTS = switcher.lo
+libswitcher_la_OBJECTS = $(am_libswitcher_la_OBJECTS)
+libswitcher_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libswitcher_la_LDFLAGS) $(LDFLAGS) -o $@
+am_libvideo_la_OBJECTS = video.lo
+libvideo_la_OBJECTS = $(am_libvideo_la_OBJECTS)
+libvideo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libvideo_la_LDFLAGS) $(LDFLAGS) -o $@
+libwater_la_LIBADD =
+am_libwater_la_OBJECTS = water.lo
+libwater_la_OBJECTS = $(am_libwater_la_OBJECTS)
+libwater_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libwater_la_LDFLAGS) $(LDFLAGS) -o $@
+libwobbly_la_LIBADD =
+am_libwobbly_la_OBJECTS = wobbly.lo
+libwobbly_la_OBJECTS = $(am_libwobbly_la_OBJECTS)
+libwobbly_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libwobbly_la_LDFLAGS) $(LDFLAGS) -o $@
+libzoom_la_LIBADD =
+am_libzoom_la_OBJECTS = zoom.lo
+libzoom_la_OBJECTS = $(am_libzoom_la_OBJECTS)
+libzoom_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libzoom_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libannotate_la_SOURCES) $(libblur_la_SOURCES) \
+ $(libclone_la_SOURCES) $(libcommands_la_SOURCES) \
+ $(libcube_la_SOURCES) $(libdbus_la_SOURCES) \
+ $(libdecoration_la_SOURCES) $(libfade_la_SOURCES) \
+ $(libfs_la_SOURCES) $(libgconf_la_SOURCES) \
+ $(libglib_la_SOURCES) $(libgnomecompat_la_SOURCES) \
+ $(libini_la_SOURCES) $(libinotify_la_SOURCES) \
+ $(libkconfig_la_SOURCES) $(libminimize_la_SOURCES) \
+ $(libmove_la_SOURCES) $(libobs_la_SOURCES) \
+ $(libplace_la_SOURCES) $(libpng_la_SOURCES) \
+ $(libregex_la_SOURCES) $(libresize_la_SOURCES) \
+ $(librotate_la_SOURCES) $(libscale_la_SOURCES) \
+ $(libscreenshot_la_SOURCES) $(libsvg_la_SOURCES) \
+ $(libswitcher_la_SOURCES) $(libvideo_la_SOURCES) \
+ $(libwater_la_SOURCES) $(libwobbly_la_SOURCES) \
+ $(libzoom_la_SOURCES)
+DIST_SOURCES = $(am__libannotate_la_SOURCES_DIST) \
+ $(libblur_la_SOURCES) $(libclone_la_SOURCES) \
+ $(libcommands_la_SOURCES) $(libcube_la_SOURCES) \
+ $(am__libdbus_la_SOURCES_DIST) $(libdecoration_la_SOURCES) \
+ $(libfade_la_SOURCES) $(am__libfs_la_SOURCES_DIST) \
+ $(am__libgconf_la_SOURCES_DIST) $(am__libglib_la_SOURCES_DIST) \
+ $(libgnomecompat_la_SOURCES) $(libini_la_SOURCES) \
+ $(am__libinotify_la_SOURCES_DIST) \
+ $(am__libkconfig_la_SOURCES_DIST) $(libminimize_la_SOURCES) \
+ $(libmove_la_SOURCES) $(libobs_la_SOURCES) \
+ $(libplace_la_SOURCES) $(libpng_la_SOURCES) \
+ $(libregex_la_SOURCES) $(libresize_la_SOURCES) \
+ $(librotate_la_SOURCES) $(libscale_la_SOURCES) \
+ $(libscreenshot_la_SOURCES) $(am__libsvg_la_SOURCES_DIST) \
+ $(libswitcher_la_SOURCES) $(libvideo_la_SOURCES) \
+ $(libwater_la_SOURCES) $(libwobbly_la_SOURCES) \
+ $(libzoom_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+ANNOTATE_CFLAGS = @ANNOTATE_CFLAGS@
+ANNOTATE_LIBS = @ANNOTATE_LIBS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+COMPIZ_CFLAGS = @COMPIZ_CFLAGS@
+COMPIZ_LIBS = @COMPIZ_LIBS@
+COMPIZ_REQUIRES = @COMPIZ_REQUIRES@
+COMPIZ_VERSION_MAJOR = @COMPIZ_VERSION_MAJOR@
+COMPIZ_VERSION_MICRO = @COMPIZ_VERSION_MICRO@
+COMPIZ_VERSION_MINOR = @COMPIZ_VERSION_MINOR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_CFLAGS = @DBUS_GLIB_CFLAGS@
+DBUS_GLIB_LIBS = @DBUS_GLIB_LIBS@
+DBUS_LIBS = @DBUS_LIBS@
+DCOPIDL = @DCOPIDL@
+DCOPIDL2CPP = @DCOPIDL2CPP@
+DECORATION_CFLAGS = @DECORATION_CFLAGS@
+DECORATION_LIBS = @DECORATION_LIBS@
+DECORATION_REQUIRES = @DECORATION_REQUIRES@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+FUSE_CFLAGS = @FUSE_CFLAGS@
+FUSE_LIBS = @FUSE_LIBS@
+GCONFTOOL = @GCONFTOOL@
+GCONF_CFLAGS = @GCONF_CFLAGS@
+GCONF_LIBS = @GCONF_LIBS@
+GCONF_SCHEMA_CONFIG_SOURCE = @GCONF_SCHEMA_CONFIG_SOURCE@
+GCONF_SCHEMA_FILE_DIR = @GCONF_SCHEMA_FILE_DIR@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+GL_CFLAGS = @GL_CFLAGS@
+GL_LIBS = @GL_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNOME_KEY_BINDINGS_CFLAGS = @GNOME_KEY_BINDINGS_CFLAGS@
+GNOME_KEY_BINDINGS_LIBS = @GNOME_KEY_BINDINGS_LIBS@
+GNOME_WINDOW_SETTINGS_CFLAGS = @GNOME_WINDOW_SETTINGS_CFLAGS@
+GNOME_WINDOW_SETTINGS_LIBS = @GNOME_WINDOW_SETTINGS_LIBS@
+GREP = @GREP@
+GTK_WINDOW_DECORATOR_CFLAGS = @GTK_WINDOW_DECORATOR_CFLAGS@
+GTK_WINDOW_DECORATOR_LIBS = @GTK_WINDOW_DECORATOR_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_CAVES_RULE = @INTLTOOL_CAVES_RULE@
+INTLTOOL_DESKTOP_RULE = @INTLTOOL_DESKTOP_RULE@
+INTLTOOL_DIRECTORY_RULE = @INTLTOOL_DIRECTORY_RULE@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_KBD_RULE = @INTLTOOL_KBD_RULE@
+INTLTOOL_KEYS_RULE = @INTLTOOL_KEYS_RULE@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_OAF_RULE = @INTLTOOL_OAF_RULE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_POLICY_RULE = @INTLTOOL_POLICY_RULE@
+INTLTOOL_PONG_RULE = @INTLTOOL_PONG_RULE@
+INTLTOOL_PROP_RULE = @INTLTOOL_PROP_RULE@
+INTLTOOL_SCHEMAS_RULE = @INTLTOOL_SCHEMAS_RULE@
+INTLTOOL_SERVER_RULE = @INTLTOOL_SERVER_RULE@
+INTLTOOL_SERVICE_RULE = @INTLTOOL_SERVICE_RULE@
+INTLTOOL_SHEET_RULE = @INTLTOOL_SHEET_RULE@
+INTLTOOL_SOUNDLIST_RULE = @INTLTOOL_SOUNDLIST_RULE@
+INTLTOOL_THEME_RULE = @INTLTOOL_THEME_RULE@
+INTLTOOL_UI_RULE = @INTLTOOL_UI_RULE@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_XAM_RULE = @INTLTOOL_XAM_RULE@
+INTLTOOL_XML_NOMERGE_RULE = @INTLTOOL_XML_NOMERGE_RULE@
+INTLTOOL_XML_RULE = @INTLTOOL_XML_RULE@
+KCONFIG_CFLAGS = @KCONFIG_CFLAGS@
+KCONFIG_LIBS = @KCONFIG_LIBS@
+KDE4_CFLAGS = @KDE4_CFLAGS@
+KDE4_LIBS = @KDE4_LIBS@
+KDE4_WINDOW_DECORATOR_CFLAGS = @KDE4_WINDOW_DECORATOR_CFLAGS@
+KDE4_WINDOW_DECORATOR_LIBS = @KDE4_WINDOW_DECORATOR_LIBS@
+KDE_CFLAGS = @KDE_CFLAGS@
+KDE_KCFG_DIR = @KDE_KCFG_DIR@
+KDE_KCONFIG_DIR = @KDE_KCONFIG_DIR@
+KDE_LIBS = @KDE_LIBS@
+KDE_WINDOW_DECORATOR_CFLAGS = @KDE_WINDOW_DECORATOR_CFLAGS@
+KDE_WINDOW_DECORATOR_LIBS = @KDE_WINDOW_DECORATOR_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
+LIBPNG_LIBS = @LIBPNG_LIBS@
+LIBRSVG_CFLAGS = @LIBRSVG_CFLAGS@
+LIBRSVG_LIBS = @LIBRSVG_LIBS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+METACITY_CFLAGS = @METACITY_CFLAGS@
+METACITY_LIBS = @METACITY_LIBS@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NMEDIT = @NMEDIT@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+QDBUSXML2CPP = @QDBUSXML2CPP@
+QT4_MOC = @QT4_MOC@
+QT_MOC = @QT_MOC@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XSLTPROC = @XSLTPROC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+default_plugins = @default_plugins@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+imagedir = @imagedir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+kcfgdir = @kcfgdir@
+keybindingsdir = @keybindingsdir@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+metadatadir = @metadatadir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+plugindir = @plugindir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+stylesheetdir = @stylesheetdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+windowsettingsdatadir = @windowsettingsdatadir@
+windowsettingslibdir = @windowsettingslibdir@
+libfade_la_LDFLAGS = -module -avoid-version -no-undefined
+libfade_la_SOURCES = fade.c
+libcube_la_LDFLAGS = -module -avoid-version -no-undefined
+libcube_la_SOURCES = cube.c
+librotate_la_LDFLAGS = -module -avoid-version -no-undefined
+librotate_la_SOURCES = rotate.c
+libzoom_la_LDFLAGS = -module -avoid-version -no-undefined
+libzoom_la_SOURCES = zoom.c
+libscale_la_LDFLAGS = -module -avoid-version -no-undefined
+libscale_la_SOURCES = scale.c
+libwobbly_la_LDFLAGS = -module -avoid-version -no-undefined
+libwobbly_la_SOURCES = wobbly.c
+libminimize_la_LDFLAGS = -module -avoid-version -no-undefined
+libminimize_la_SOURCES = minimize.c
+libmove_la_LDFLAGS = -module -avoid-version -no-undefined
+libmove_la_SOURCES = move.c
+libresize_la_LDFLAGS = -module -avoid-version -no-undefined
+libresize_la_SOURCES = resize.c
+libplace_la_LDFLAGS = -module -avoid-version -no-undefined
+libplace_la_SOURCES = place.c
+libdecoration_la_DEPENDENCIES = $(top_builddir)/libdecoration/libdecoration.la
+libdecoration_la_LDFLAGS = -module -avoid-version -no-undefined
+libdecoration_la_LIBADD = $(top_builddir)/libdecoration/libdecoration.la
+libdecoration_la_SOURCES = decoration.c
+libswitcher_la_LDFLAGS = -module -avoid-version -no-undefined
+libswitcher_la_SOURCES = switcher.c
+libwater_la_LDFLAGS = -module -avoid-version -no-undefined
+libwater_la_SOURCES = water.c
+libscreenshot_la_LDFLAGS = -module -avoid-version -no-undefined
+libscreenshot_la_SOURCES = screenshot.c
+libclone_la_LDFLAGS = -module -avoid-version -no-undefined
+libclone_la_SOURCES = clone.c
+libpng_la_LDFLAGS = -module -avoid-version -no-undefined
+libpng_la_LIBADD = @LIBPNG_LIBS@
+libpng_la_SOURCES = png.c
+libblur_la_DEPENDENCIES = $(top_builddir)/libdecoration/libdecoration.la
+libblur_la_LDFLAGS = -module -avoid-version -no-undefined
+libblur_la_LIBADD = $(top_builddir)/libdecoration/libdecoration.la -lGLU
+libblur_la_SOURCES = blur.c
+libregex_la_LDFLAGS = -module -avoid-version -no-undefined
+libregex_la_SOURCES = regex.c
+libvideo_la_DEPENDENCIES = $(top_builddir)/libdecoration/libdecoration.la
+libvideo_la_LDFLAGS = -module -avoid-version -no-undefined
+libvideo_la_LIBADD = $(top_builddir)/libdecoration/libdecoration.la
+libvideo_la_SOURCES = video.c
+libini_la_LDFLAGS = -module -avoid-version -no-undefined
+libini_la_SOURCES = ini.c
+libobs_la_LDFLAGS = -module -avoid-version -no-undefined
+libobs_la_SOURCES = obs.c
+libcommands_la_LDFLAGS = -module -avoid-version -no-undefined
+libcommands_la_SOURCES = commands.c
+libgnomecompat_la_LDFLAGS = -module -avoid-version -no-undefined
+libgnomecompat_la_SOURCES = gnomecompat.c
+@USE_LIBRSVG_TRUE@libsvg_la_DEPENDENCIES = $(top_builddir)/libdecoration/libdecoration.la
+@USE_LIBRSVG_TRUE@libsvg_la_LDFLAGS = -module -avoid-version -no-undefined
+@USE_LIBRSVG_TRUE@libsvg_la_LIBADD = \
+@USE_LIBRSVG_TRUE@ $(top_builddir)/libdecoration/libdecoration.la \
+@USE_LIBRSVG_TRUE@ @LIBRSVG_LIBS@
+
+@USE_LIBRSVG_TRUE@libsvg_la_SOURCES = svg.c
+@USE_LIBRSVG_TRUE@libsvg_module = libsvg.la
+@ANNOTATE_PLUGIN_TRUE@libannotate_la_LDFLAGS = -module -avoid-version -no-undefined
+@ANNOTATE_PLUGIN_TRUE@libannotate_la_LIBADD = @ANNOTATE_LIBS@
+@ANNOTATE_PLUGIN_TRUE@libannotate_la_SOURCES = annotate.c
+@ANNOTATE_PLUGIN_TRUE@libannotate_module = libannotate.la
+@USE_GLIB_TRUE@libglib_la_LDFLAGS = -module -avoid-version -no-undefined
+@USE_GLIB_TRUE@libglib_la_LIBADD = @GLIB_LIBS@
+@USE_GLIB_TRUE@libglib_la_SOURCES = glib.c
+@USE_GLIB_TRUE@libglib_module = libglib.la
+@USE_GCONF_TRUE@libgconf_la_LDFLAGS = -module -avoid-version -no-undefined
+@USE_GCONF_TRUE@libgconf_la_LIBADD = @GCONF_LIBS@
+@USE_GCONF_TRUE@libgconf_la_SOURCES = gconf.c
+@USE_GCONF_TRUE@libgconf_module = libgconf.la
+@USE_KCONFIG_TRUE@libkconfig_la_LDFLAGS = -module -avoid-version -no-undefined
+@USE_KCONFIG_TRUE@libkconfig_la_LIBADD = @KCONFIG_LIBS@
+@USE_KCONFIG_TRUE@libkconfig_la_SOURCES = kconfig.cpp
+@USE_KCONFIG_TRUE@libkconfig_module = libkconfig.la
+@DBUS_PLUGIN_TRUE@libdbus_la_LDFLAGS = -module -avoid-version -no-undefined
+@DBUS_PLUGIN_TRUE@libdbus_la_LIBADD = @DBUS_LIBS@
+@DBUS_PLUGIN_TRUE@libdbus_la_SOURCES = dbus.c
+@DBUS_PLUGIN_TRUE@libdbus_module = libdbus.la
+@INOTIFY_PLUGIN_TRUE@libinotify_la_LDFLAGS = -module -avoid-version -no-undefined
+@INOTIFY_PLUGIN_TRUE@libinotify_la_SOURCES = inotify.c
+@INOTIFY_PLUGIN_TRUE@libinotify_module = libinotify.la
+@FUSE_PLUGIN_TRUE@libfs_la_LDFLAGS = -module -avoid-version -no-undefined
+@FUSE_PLUGIN_TRUE@libfs_la_LIBADD = @FUSE_LIBS@
+@FUSE_PLUGIN_TRUE@libfs_la_SOURCES = fuse.c
+@FUSE_PLUGIN_TRUE@libfs_module = libfs.la
+INCLUDES = \
+ @COMPIZ_CFLAGS@ \
+ @LIBPNG_CFLAGS@ \
+ @LIBRSVG_CFLAGS@ \
+ @ANNOTATE_CFLAGS@ \
+ @GCONF_CFLAGS@ \
+ @KCONFIG_CFLAGS@ \
+ @DBUS_CFLAGS@ \
+ @GLIB_CFLAGS@ \
+ @FUSE_CFLAGS@ \
+ -DFUSE_USE_VERSION=26 \
+ -DALL_LINGUAS="\"@ALL_LINGUAS@\"" \
+ -DLOCALEDIR="\"@datadir@/locale\"" \
+ -DPLUGINDIR=\"$(plugindir)\" \
+ -DIMAGEDIR=\"$(imagedir)\" \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ -DMETADATADIR=\"$(metadatadir)\"
+
+moduledir = $(plugindir)
+module_LTLIBRARIES = \
+ $(libglib_module) \
+ $(libgconf_module) \
+ $(libkconfig_module) \
+ libdecoration.la \
+ libwobbly.la \
+ libfade.la \
+ libminimize.la \
+ libcube.la \
+ librotate.la \
+ libzoom.la \
+ libscale.la \
+ libmove.la \
+ libresize.la \
+ libplace.la \
+ $(libdbus_module) \
+ libswitcher.la \
+ libwater.la \
+ libscreenshot.la \
+ libclone.la \
+ libpng.la \
+ libblur.la \
+ libregex.la \
+ libvideo.la \
+ libini.la \
+ libobs.la \
+ libcommands.la \
+ libgnomecompat.la \
+ $(libsvg_module) \
+ $(libannotate_module) \
+ $(libinotify_module) \
+ $(libfs_module)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .cpp .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plugins/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plugins/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-moduleLTLIBRARIES: $(module_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(moduledir)" || $(MKDIR_P) "$(DESTDIR)$(moduledir)"
+ @list='$(module_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(moduleLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(moduledir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(moduleLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(moduledir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-moduleLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(module_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$p'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$p"; \
+ done
+
+clean-moduleLTLIBRARIES:
+ -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES)
+ @list='$(module_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libannotate.la: $(libannotate_la_OBJECTS) $(libannotate_la_DEPENDENCIES)
+ $(libannotate_la_LINK) $(am_libannotate_la_rpath) $(libannotate_la_OBJECTS) $(libannotate_la_LIBADD) $(LIBS)
+libblur.la: $(libblur_la_OBJECTS) $(libblur_la_DEPENDENCIES)
+ $(libblur_la_LINK) -rpath $(moduledir) $(libblur_la_OBJECTS) $(libblur_la_LIBADD) $(LIBS)
+libclone.la: $(libclone_la_OBJECTS) $(libclone_la_DEPENDENCIES)
+ $(libclone_la_LINK) -rpath $(moduledir) $(libclone_la_OBJECTS) $(libclone_la_LIBADD) $(LIBS)
+libcommands.la: $(libcommands_la_OBJECTS) $(libcommands_la_DEPENDENCIES)
+ $(libcommands_la_LINK) -rpath $(moduledir) $(libcommands_la_OBJECTS) $(libcommands_la_LIBADD) $(LIBS)
+libcube.la: $(libcube_la_OBJECTS) $(libcube_la_DEPENDENCIES)
+ $(libcube_la_LINK) -rpath $(moduledir) $(libcube_la_OBJECTS) $(libcube_la_LIBADD) $(LIBS)
+libdbus.la: $(libdbus_la_OBJECTS) $(libdbus_la_DEPENDENCIES)
+ $(libdbus_la_LINK) $(am_libdbus_la_rpath) $(libdbus_la_OBJECTS) $(libdbus_la_LIBADD) $(LIBS)
+libdecoration.la: $(libdecoration_la_OBJECTS) $(libdecoration_la_DEPENDENCIES)
+ $(libdecoration_la_LINK) -rpath $(moduledir) $(libdecoration_la_OBJECTS) $(libdecoration_la_LIBADD) $(LIBS)
+libfade.la: $(libfade_la_OBJECTS) $(libfade_la_DEPENDENCIES)
+ $(libfade_la_LINK) -rpath $(moduledir) $(libfade_la_OBJECTS) $(libfade_la_LIBADD) $(LIBS)
+libfs.la: $(libfs_la_OBJECTS) $(libfs_la_DEPENDENCIES)
+ $(libfs_la_LINK) $(am_libfs_la_rpath) $(libfs_la_OBJECTS) $(libfs_la_LIBADD) $(LIBS)
+libgconf.la: $(libgconf_la_OBJECTS) $(libgconf_la_DEPENDENCIES)
+ $(libgconf_la_LINK) $(am_libgconf_la_rpath) $(libgconf_la_OBJECTS) $(libgconf_la_LIBADD) $(LIBS)
+libglib.la: $(libglib_la_OBJECTS) $(libglib_la_DEPENDENCIES)
+ $(libglib_la_LINK) $(am_libglib_la_rpath) $(libglib_la_OBJECTS) $(libglib_la_LIBADD) $(LIBS)
+libgnomecompat.la: $(libgnomecompat_la_OBJECTS) $(libgnomecompat_la_DEPENDENCIES)
+ $(libgnomecompat_la_LINK) -rpath $(moduledir) $(libgnomecompat_la_OBJECTS) $(libgnomecompat_la_LIBADD) $(LIBS)
+libini.la: $(libini_la_OBJECTS) $(libini_la_DEPENDENCIES)
+ $(libini_la_LINK) -rpath $(moduledir) $(libini_la_OBJECTS) $(libini_la_LIBADD) $(LIBS)
+libinotify.la: $(libinotify_la_OBJECTS) $(libinotify_la_DEPENDENCIES)
+ $(libinotify_la_LINK) $(am_libinotify_la_rpath) $(libinotify_la_OBJECTS) $(libinotify_la_LIBADD) $(LIBS)
+libkconfig.la: $(libkconfig_la_OBJECTS) $(libkconfig_la_DEPENDENCIES)
+ $(libkconfig_la_LINK) $(am_libkconfig_la_rpath) $(libkconfig_la_OBJECTS) $(libkconfig_la_LIBADD) $(LIBS)
+libminimize.la: $(libminimize_la_OBJECTS) $(libminimize_la_DEPENDENCIES)
+ $(libminimize_la_LINK) -rpath $(moduledir) $(libminimize_la_OBJECTS) $(libminimize_la_LIBADD) $(LIBS)
+libmove.la: $(libmove_la_OBJECTS) $(libmove_la_DEPENDENCIES)
+ $(libmove_la_LINK) -rpath $(moduledir) $(libmove_la_OBJECTS) $(libmove_la_LIBADD) $(LIBS)
+libobs.la: $(libobs_la_OBJECTS) $(libobs_la_DEPENDENCIES)
+ $(libobs_la_LINK) -rpath $(moduledir) $(libobs_la_OBJECTS) $(libobs_la_LIBADD) $(LIBS)
+libplace.la: $(libplace_la_OBJECTS) $(libplace_la_DEPENDENCIES)
+ $(libplace_la_LINK) -rpath $(moduledir) $(libplace_la_OBJECTS) $(libplace_la_LIBADD) $(LIBS)
+libpng.la: $(libpng_la_OBJECTS) $(libpng_la_DEPENDENCIES)
+ $(libpng_la_LINK) -rpath $(moduledir) $(libpng_la_OBJECTS) $(libpng_la_LIBADD) $(LIBS)
+libregex.la: $(libregex_la_OBJECTS) $(libregex_la_DEPENDENCIES)
+ $(libregex_la_LINK) -rpath $(moduledir) $(libregex_la_OBJECTS) $(libregex_la_LIBADD) $(LIBS)
+libresize.la: $(libresize_la_OBJECTS) $(libresize_la_DEPENDENCIES)
+ $(libresize_la_LINK) -rpath $(moduledir) $(libresize_la_OBJECTS) $(libresize_la_LIBADD) $(LIBS)
+librotate.la: $(librotate_la_OBJECTS) $(librotate_la_DEPENDENCIES)
+ $(librotate_la_LINK) -rpath $(moduledir) $(librotate_la_OBJECTS) $(librotate_la_LIBADD) $(LIBS)
+libscale.la: $(libscale_la_OBJECTS) $(libscale_la_DEPENDENCIES)
+ $(libscale_la_LINK) -rpath $(moduledir) $(libscale_la_OBJECTS) $(libscale_la_LIBADD) $(LIBS)
+libscreenshot.la: $(libscreenshot_la_OBJECTS) $(libscreenshot_la_DEPENDENCIES)
+ $(libscreenshot_la_LINK) -rpath $(moduledir) $(libscreenshot_la_OBJECTS) $(libscreenshot_la_LIBADD) $(LIBS)
+libsvg.la: $(libsvg_la_OBJECTS) $(libsvg_la_DEPENDENCIES)
+ $(libsvg_la_LINK) $(am_libsvg_la_rpath) $(libsvg_la_OBJECTS) $(libsvg_la_LIBADD) $(LIBS)
+libswitcher.la: $(libswitcher_la_OBJECTS) $(libswitcher_la_DEPENDENCIES)
+ $(libswitcher_la_LINK) -rpath $(moduledir) $(libswitcher_la_OBJECTS) $(libswitcher_la_LIBADD) $(LIBS)
+libvideo.la: $(libvideo_la_OBJECTS) $(libvideo_la_DEPENDENCIES)
+ $(libvideo_la_LINK) -rpath $(moduledir) $(libvideo_la_OBJECTS) $(libvideo_la_LIBADD) $(LIBS)
+libwater.la: $(libwater_la_OBJECTS) $(libwater_la_DEPENDENCIES)
+ $(libwater_la_LINK) -rpath $(moduledir) $(libwater_la_OBJECTS) $(libwater_la_LIBADD) $(LIBS)
+libwobbly.la: $(libwobbly_la_OBJECTS) $(libwobbly_la_DEPENDENCIES)
+ $(libwobbly_la_LINK) -rpath $(moduledir) $(libwobbly_la_OBJECTS) $(libwobbly_la_LIBADD) $(LIBS)
+libzoom.la: $(libzoom_la_OBJECTS) $(libzoom_la_DEPENDENCIES)
+ $(libzoom_la_LINK) -rpath $(moduledir) $(libzoom_la_OBJECTS) $(libzoom_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/annotate.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blur.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clone.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/commands.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cube.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decoration.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fade.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuse.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gconf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/glib.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnomecompat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ini.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inotify.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kconfig.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minimize.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/move.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/place.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/png.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resize.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rotate.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scale.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/screenshot.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svg.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/switcher.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/video.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/water.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wobbly.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zoom.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(moduledir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-moduleLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-moduleLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-moduleLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-moduleLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-moduleLTLIBRARIES install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-moduleLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plugins/annotate.c b/plugins/annotate.c
new file mode 100644
index 0000000..fea72d3
--- /dev/null
+++ b/plugins/annotate.c
@@ -0,0 +1,963 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <cairo-xlib-xrender.h>
+
+#include <compiz-core.h>
+
+static CompMetadata annoMetadata;
+
+static int displayPrivateIndex;
+
+static int annoLastPointerX = 0;
+static int annoLastPointerY = 0;
+
+#define ANNO_DISPLAY_OPTION_INITIATE_BUTTON 0
+#define ANNO_DISPLAY_OPTION_DRAW_BUTTON 1
+#define ANNO_DISPLAY_OPTION_ERASE_BUTTON 2
+#define ANNO_DISPLAY_OPTION_CLEAR_KEY 3
+#define ANNO_DISPLAY_OPTION_CLEAR_BUTTON 4
+#define ANNO_DISPLAY_OPTION_FILL_COLOR 5
+#define ANNO_DISPLAY_OPTION_STROKE_COLOR 6
+#define ANNO_DISPLAY_OPTION_LINE_WIDTH 7
+#define ANNO_DISPLAY_OPTION_STROKE_WIDTH 8
+#define ANNO_DISPLAY_OPTION_NUM 9
+
+typedef struct _AnnoDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompOption opt[ANNO_DISPLAY_OPTION_NUM];
+} AnnoDisplay;
+
+typedef struct _AnnoScreen {
+ PaintOutputProc paintOutput;
+ int grabIndex;
+
+ Pixmap pixmap;
+ CompTexture texture;
+ cairo_surface_t *surface;
+ cairo_t *cairo;
+ Bool content;
+
+ Bool eraseMode;
+} AnnoScreen;
+
+#define GET_ANNO_DISPLAY(d) \
+ ((AnnoDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define ANNO_DISPLAY(d) \
+ AnnoDisplay *ad = GET_ANNO_DISPLAY (d)
+
+#define GET_ANNO_SCREEN(s, ad) \
+ ((AnnoScreen *) (s)->base.privates[(ad)->screenPrivateIndex].ptr)
+
+#define ANNO_SCREEN(s) \
+ AnnoScreen *as = GET_ANNO_SCREEN (s, GET_ANNO_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+
+#define NUM_TOOLS (sizeof (tools) / sizeof (tools[0]))
+
+static void
+annoCairoClear (CompScreen *s,
+ cairo_t *cr)
+{
+ ANNO_SCREEN (s);
+
+ cairo_save (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (cr);
+ cairo_restore (cr);
+
+ as->content = FALSE;
+}
+
+static cairo_t *
+annoCairoContext (CompScreen *s)
+{
+ ANNO_SCREEN (s);
+
+ if (!as->cairo)
+ {
+ XRenderPictFormat *format;
+ Screen *screen;
+ int w, h;
+
+ screen = ScreenOfDisplay (s->display->display, s->screenNum);
+
+ w = s->width;
+ h = s->height;
+
+ format = XRenderFindStandardFormat (s->display->display,
+ PictStandardARGB32);
+
+ as->pixmap = XCreatePixmap (s->display->display, s->root, w, h, 32);
+
+ if (!bindPixmapToTexture (s, &as->texture, as->pixmap, w, h, 32))
+ {
+ compLogMessage ("annotate", CompLogLevelError,
+ "Couldn't bind pixmap 0x%x to texture",
+ (int) as->pixmap);
+
+ XFreePixmap (s->display->display, as->pixmap);
+
+ return NULL;
+ }
+
+ as->surface =
+ cairo_xlib_surface_create_with_xrender_format (s->display->display,
+ as->pixmap, screen,
+ format, w, h);
+
+ as->cairo = cairo_create (as->surface);
+
+ annoCairoClear (s, as->cairo);
+ }
+
+ return as->cairo;
+}
+
+static void
+annoSetSourceColor (cairo_t *cr,
+ unsigned short *color)
+{
+ cairo_set_source_rgba (cr,
+ (double) color[0] / 0xffff,
+ (double) color[1] / 0xffff,
+ (double) color[2] / 0xffff,
+ (double) color[3] / 0xffff);
+}
+
+static void
+annoDrawCircle (CompScreen *s,
+ double xc,
+ double yc,
+ double radius,
+ unsigned short *fillColor,
+ unsigned short *strokeColor,
+ double strokeWidth)
+{
+ REGION reg;
+ cairo_t *cr;
+
+ ANNO_SCREEN (s);
+
+ cr = annoCairoContext (s);
+ if (cr)
+ {
+ double ex1, ey1, ex2, ey2;
+
+ annoSetSourceColor (cr, fillColor);
+ cairo_arc (cr, xc, yc, radius, 0, 2 * M_PI);
+ cairo_fill_preserve (cr);
+ cairo_set_line_width (cr, strokeWidth);
+ cairo_stroke_extents (cr, &ex1, &ey1, &ex2, &ey2);
+ annoSetSourceColor (cr, strokeColor);
+ cairo_stroke (cr);
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ reg.extents.x1 = ex1;
+ reg.extents.y1 = ey1;
+ reg.extents.x2 = ex2;
+ reg.extents.y2 = ey2;
+
+ as->content = TRUE;
+ damageScreenRegion (s, &reg);
+ }
+}
+
+static void
+annoDrawRectangle (CompScreen *s,
+ double x,
+ double y,
+ double w,
+ double h,
+ unsigned short *fillColor,
+ unsigned short *strokeColor,
+ double strokeWidth)
+{
+ REGION reg;
+ cairo_t *cr;
+
+ ANNO_SCREEN (s);
+
+ cr = annoCairoContext (s);
+ if (cr)
+ {
+ double ex1, ey1, ex2, ey2;
+
+ annoSetSourceColor (cr, fillColor);
+ cairo_rectangle (cr, x, y, w, h);
+ cairo_fill_preserve (cr);
+ cairo_set_line_width (cr, strokeWidth);
+ cairo_stroke_extents (cr, &ex1, &ey1, &ex2, &ey2);
+ annoSetSourceColor (cr, strokeColor);
+ cairo_stroke (cr);
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ reg.extents.x1 = ex1;
+ reg.extents.y1 = ey1;
+ reg.extents.x2 = ex2 + 2.0;
+ reg.extents.y2 = ey2 + 2.0;
+
+ as->content = TRUE;
+ damageScreenRegion (s, &reg);
+ }
+}
+
+static void
+annoDrawLine (CompScreen *s,
+ double x1,
+ double y1,
+ double x2,
+ double y2,
+ double width,
+ unsigned short *color)
+{
+ REGION reg;
+ cairo_t *cr;
+
+ ANNO_SCREEN (s);
+
+ cr = annoCairoContext (s);
+ if (cr)
+ {
+ double ex1, ey1, ex2, ey2;
+
+ cairo_set_line_width (cr, width);
+ cairo_move_to (cr, x1, y1);
+ cairo_line_to (cr, x2, y2);
+ cairo_stroke_extents (cr, &ex1, &ey1, &ex2, &ey2);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ annoSetSourceColor (cr, color);
+ cairo_stroke (cr);
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ reg.extents.x1 = ex1;
+ reg.extents.y1 = ey1;
+ reg.extents.x2 = ex2;
+ reg.extents.y2 = ey2;
+
+ as->content = TRUE;
+ damageScreenRegion (s, &reg);
+ }
+}
+
+static void
+annoDrawText (CompScreen *s,
+ double x,
+ double y,
+ char *text,
+ char *fontFamily,
+ double fontSize,
+ int fontSlant,
+ int fontWeight,
+ unsigned short *fillColor,
+ unsigned short *strokeColor,
+ double strokeWidth)
+{
+ REGION reg;
+ cairo_t *cr;
+
+ ANNO_SCREEN (s);
+
+ cr = annoCairoContext (s);
+ if (cr)
+ {
+ cairo_text_extents_t extents;
+
+ cairo_set_line_width (cr, strokeWidth);
+ annoSetSourceColor (cr, fillColor);
+ cairo_select_font_face (cr, fontFamily, fontSlant, fontWeight);
+ cairo_set_font_size (cr, fontSize);
+ cairo_text_extents (cr, text, &extents);
+ cairo_save (cr);
+ cairo_move_to (cr, x, y);
+ cairo_text_path (cr, text);
+ cairo_fill_preserve (cr);
+ annoSetSourceColor (cr, strokeColor);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ reg.extents.x1 = x;
+ reg.extents.y1 = y + extents.y_bearing - 2.0;
+ reg.extents.x2 = x + extents.width + 20.0;
+ reg.extents.y2 = y + extents.height;
+
+ as->content = TRUE;
+ damageScreenRegion (s, &reg);
+ }
+}
+
+static Bool
+annoDraw (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ cairo_t *cr;
+
+ cr = annoCairoContext (s);
+ if (cr)
+ {
+ char *tool;
+ unsigned short *fillColor, *strokeColor;
+ double lineWidth, strokeWidth;
+
+ ANNO_DISPLAY (d);
+
+ tool = getStringOptionNamed (option, nOption, "tool", "line");
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+
+ fillColor = ad->opt[ANNO_DISPLAY_OPTION_FILL_COLOR].value.c;
+ fillColor = getColorOptionNamed (option, nOption, "fill_color",
+ fillColor);
+
+ strokeColor = ad->opt[ANNO_DISPLAY_OPTION_STROKE_COLOR].value.c;
+ strokeColor = getColorOptionNamed (option, nOption,
+ "stroke_color", strokeColor);
+
+ strokeWidth = ad->opt[ANNO_DISPLAY_OPTION_STROKE_WIDTH].value.f;
+ strokeWidth = getFloatOptionNamed (option, nOption, "stroke_width",
+ strokeWidth);
+
+ lineWidth = ad->opt[ANNO_DISPLAY_OPTION_LINE_WIDTH].value.f;
+ lineWidth = getFloatOptionNamed (option, nOption, "line_width",
+ lineWidth);
+
+ if (strcasecmp (tool, "rectangle") == 0)
+ {
+ double x, y, w, h;
+
+ x = getFloatOptionNamed (option, nOption, "x", 0);
+ y = getFloatOptionNamed (option, nOption, "y", 0);
+ w = getFloatOptionNamed (option, nOption, "w", 100);
+ h = getFloatOptionNamed (option, nOption, "h", 100);
+
+ annoDrawRectangle (s, x, y, w, h, fillColor, strokeColor,
+ strokeWidth);
+ }
+ else if (strcasecmp (tool, "circle") == 0)
+ {
+ double xc, yc, r;
+
+ xc = getFloatOptionNamed (option, nOption, "xc", 0);
+ yc = getFloatOptionNamed (option, nOption, "yc", 0);
+ r = getFloatOptionNamed (option, nOption, "radius", 100);
+
+ annoDrawCircle (s, xc, yc, r, fillColor, strokeColor,
+ strokeWidth);
+ }
+ else if (strcasecmp (tool, "line") == 0)
+ {
+ double x1, y1, x2, y2;
+
+ x1 = getFloatOptionNamed (option, nOption, "x1", 0);
+ y1 = getFloatOptionNamed (option, nOption, "y1", 0);
+ x2 = getFloatOptionNamed (option, nOption, "x2", 100);
+ y2 = getFloatOptionNamed (option, nOption, "y2", 100);
+
+ annoDrawLine (s, x1, y1, x2, y2, lineWidth, fillColor);
+ }
+ else if (strcasecmp (tool, "text") == 0)
+ {
+ double x, y, size;
+ char *text, *family;
+ unsigned int slant, weight;
+ char *str;
+
+ str = getStringOptionNamed (option, nOption, "slant", "");
+ if (strcasecmp (str, "oblique") == 0)
+ slant = CAIRO_FONT_SLANT_OBLIQUE;
+ else if (strcasecmp (str, "italic") == 0)
+ slant = CAIRO_FONT_SLANT_ITALIC;
+ else
+ slant = CAIRO_FONT_SLANT_NORMAL;
+
+ str = getStringOptionNamed (option, nOption, "weight", "");
+ if (strcasecmp (str, "bold") == 0)
+ weight = CAIRO_FONT_WEIGHT_BOLD;
+ else
+ weight = CAIRO_FONT_WEIGHT_NORMAL;
+
+ x = getFloatOptionNamed (option, nOption, "x", 0);
+ y = getFloatOptionNamed (option, nOption, "y", 0);
+ text = getStringOptionNamed (option, nOption, "text", "");
+ family = getStringOptionNamed (option, nOption, "family",
+ "Sans");
+ size = getFloatOptionNamed (option, nOption, "size", 36.0);
+
+ annoDrawText (s, x, y, text, family, size, slant, weight,
+ fillColor, strokeColor, strokeWidth);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+annoInitiate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ ANNO_SCREEN (s);
+
+ if (otherScreenGrabExist (s, 0))
+ return FALSE;
+
+ if (!as->grabIndex)
+ as->grabIndex = pushScreenGrab (s, None, "annotate");
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ if (state & CompActionStateInitKey)
+ action->state |= CompActionStateTermKey;
+
+ annoLastPointerX = pointerX;
+ annoLastPointerY = pointerY;
+
+ as->eraseMode = FALSE;
+ }
+
+ return TRUE;
+}
+
+static Bool
+annoTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ ANNO_SCREEN (s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (as->grabIndex)
+ {
+ removeScreenGrab (s, as->grabIndex, NULL);
+ as->grabIndex = 0;
+ }
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+static Bool
+annoEraseInitiate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ ANNO_SCREEN (s);
+
+ if (otherScreenGrabExist (s, 0))
+ return FALSE;
+
+ if (!as->grabIndex)
+ as->grabIndex = pushScreenGrab (s, None, "annotate");
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ if (state & CompActionStateInitKey)
+ action->state |= CompActionStateTermKey;
+
+ annoLastPointerX = pointerX;
+ annoLastPointerY = pointerY;
+
+ as->eraseMode = TRUE;
+ }
+
+ return FALSE;
+}
+
+static Bool
+annoClear (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ ANNO_SCREEN (s);
+
+ if (as->content)
+ {
+ cairo_t *cr;
+
+ cr = annoCairoContext (s);
+ if (cr)
+ annoCairoClear (s, as->cairo);
+
+ damageScreen (s);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static Bool
+annoPaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ Bool status;
+
+ ANNO_SCREEN (s);
+
+ UNWRAP (as, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+ WRAP (as, s, paintOutput, annoPaintOutput);
+
+ if (status && as->content && region->numRects)
+ {
+ BoxPtr pBox;
+ int nBox;
+
+ glPushMatrix ();
+
+ prepareXCoords (s, output, -DEFAULT_Z_CAMERA);
+
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ glEnable (GL_BLEND);
+
+ enableTexture (s, &as->texture, COMP_TEXTURE_FILTER_FAST);
+
+ pBox = region->rects;
+ nBox = region->numRects;
+
+ glBegin (GL_QUADS);
+
+ while (nBox--)
+ {
+ glTexCoord2f (COMP_TEX_COORD_X (&as->texture.matrix, pBox->x1),
+ COMP_TEX_COORD_Y (&as->texture.matrix, pBox->y2));
+ glVertex2i (pBox->x1, pBox->y2);
+ glTexCoord2f (COMP_TEX_COORD_X (&as->texture.matrix, pBox->x2),
+ COMP_TEX_COORD_Y (&as->texture.matrix, pBox->y2));
+ glVertex2i (pBox->x2, pBox->y2);
+ glTexCoord2f (COMP_TEX_COORD_X (&as->texture.matrix, pBox->x2),
+ COMP_TEX_COORD_Y (&as->texture.matrix, pBox->y1));
+ glVertex2i (pBox->x2, pBox->y1);
+ glTexCoord2f (COMP_TEX_COORD_X (&as->texture.matrix, pBox->x1),
+ COMP_TEX_COORD_Y (&as->texture.matrix, pBox->y1));
+ glVertex2i (pBox->x1, pBox->y1);
+
+ pBox++;
+ }
+
+ glEnd ();
+
+ disableTexture (s, &as->texture);
+
+ glDisable (GL_BLEND);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ glPopMatrix ();
+ }
+
+ return status;
+}
+
+static void
+annoHandleMotionEvent (CompScreen *s,
+ int xRoot,
+ int yRoot)
+{
+ ANNO_SCREEN (s);
+
+ if (as->grabIndex)
+ {
+ if (as->eraseMode)
+ {
+ static unsigned short color[] = { 0, 0, 0, 0 };
+
+ annoDrawLine (s,
+ annoLastPointerX, annoLastPointerY,
+ xRoot, yRoot,
+ 20.0, color);
+ }
+ else
+ {
+ ANNO_DISPLAY(s->display);
+
+ annoDrawLine (s,
+ annoLastPointerX, annoLastPointerY,
+ xRoot, yRoot,
+ ad->opt[ANNO_DISPLAY_OPTION_LINE_WIDTH].value.f,
+ ad->opt[ANNO_DISPLAY_OPTION_FILL_COLOR].value.c);
+ }
+
+ annoLastPointerX = xRoot;
+ annoLastPointerY = yRoot;
+ }
+}
+
+static void
+annoHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ ANNO_DISPLAY (d);
+
+ switch (event->type) {
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ annoHandleMotionEvent (s, pointerX, pointerY);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ s = findScreenAtDisplay (d, event->xcrossing.root);
+ if (s)
+ annoHandleMotionEvent (s, pointerX, pointerY);
+ default:
+ break;
+ }
+
+ UNWRAP (ad, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (ad, d, handleEvent, annoHandleEvent);
+}
+
+static CompOption *
+annoGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ ANNO_DISPLAY (display);
+
+ *count = NUM_OPTIONS (ad);
+ return ad->opt;
+}
+
+static Bool
+annoSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ ANNO_DISPLAY (display);
+
+ o = compFindOption (ad->opt, NUM_OPTIONS (ad), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (display, o, value);
+}
+
+static const CompMetadataOptionInfo annoDisplayOptionInfo[] = {
+ { "initiate_button", "button", 0, annoInitiate, annoTerminate },
+ { "draw", "action", 0, annoDraw, 0 },
+ { "erase_button", "button", 0, annoEraseInitiate, annoTerminate },
+ { "clear_key", "key", 0, annoClear, 0 },
+ { "clear_button", "button", 0, annoClear, 0 },
+ { "fill_color", "color", 0, 0, 0 },
+ { "stroke_color", "color", 0, 0, 0 },
+ { "line_width", "float", 0, 0, 0 },
+ { "stroke_width", "float", 0, 0, 0 }
+};
+
+static Bool
+annoInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ AnnoDisplay *ad;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ ad = malloc (sizeof (AnnoDisplay));
+ if (!ad)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &annoMetadata,
+ annoDisplayOptionInfo,
+ ad->opt,
+ ANNO_DISPLAY_OPTION_NUM))
+ {
+ free (ad);
+ return FALSE;
+ }
+
+ ad->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (ad->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, ad->opt, ANNO_DISPLAY_OPTION_NUM);
+ free (ad);
+ return FALSE;
+ }
+
+ WRAP (ad, d, handleEvent, annoHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = ad;
+
+ return TRUE;
+}
+
+static void
+annoFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ANNO_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, ad->screenPrivateIndex);
+
+ UNWRAP (ad, d, handleEvent);
+
+ compFiniDisplayOptions (d, ad->opt, ANNO_DISPLAY_OPTION_NUM);
+
+ free (ad);
+}
+
+static Bool
+annoInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ AnnoScreen *as;
+
+ ANNO_DISPLAY (s->display);
+
+ as = malloc (sizeof (AnnoScreen));
+ if (!as)
+ return FALSE;
+
+ as->grabIndex = 0;
+ as->surface = NULL;
+ as->pixmap = None;
+ as->cairo = NULL;
+ as->content = FALSE;
+
+ initTexture (s, &as->texture);
+
+ WRAP (as, s, paintOutput, annoPaintOutput);
+
+ s->base.privates[ad->screenPrivateIndex].ptr = as;
+
+ return TRUE;
+}
+
+static void
+annoFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ANNO_SCREEN (s);
+
+ if (as->cairo)
+ cairo_destroy (as->cairo);
+
+ if (as->surface)
+ cairo_surface_destroy (as->surface);
+
+ finiTexture (s, &as->texture);
+
+ if (as->pixmap)
+ XFreePixmap (s->display->display, as->pixmap);
+
+ UNWRAP (as, s, paintOutput);
+
+ free (as);
+}
+
+static CompBool
+annoInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) annoInitDisplay,
+ (InitPluginObjectProc) annoInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+annoFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) annoFiniDisplay,
+ (FiniPluginObjectProc) annoFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+annoGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) annoGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+annoSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) annoSetDisplayOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+annoInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&annoMetadata,
+ p->vTable->name,
+ annoDisplayOptionInfo,
+ ANNO_DISPLAY_OPTION_NUM,
+ 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&annoMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&annoMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+annoFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&annoMetadata);
+}
+
+static CompMetadata *
+annoGetMetadata (CompPlugin *plugin)
+{
+ return &annoMetadata;
+}
+
+static CompPluginVTable annoVTable = {
+ "annotate",
+ annoGetMetadata,
+ annoInit,
+ annoFini,
+ annoInitObject,
+ annoFiniObject,
+ annoGetObjectOptions,
+ annoSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &annoVTable;
+}
diff --git a/plugins/blur.c b/plugins/blur.c
new file mode 100644
index 0000000..0a7556f
--- /dev/null
+++ b/plugins/blur.c
@@ -0,0 +1,3250 @@
+/*
+ * Copyright © 2007 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <compiz-core.h>
+#include <decoration.h>
+
+#include <X11/Xatom.h>
+#include <GL/glu.h>
+
+static CompMetadata blurMetadata;
+
+#define BLUR_GAUSSIAN_RADIUS_MAX 15
+
+#define BLUR_FILTER_4X_BILINEAR 0
+#define BLUR_FILTER_GAUSSIAN 1
+#define BLUR_FILTER_MIPMAP 2
+#define BLUR_FILTER_LAST BLUR_FILTER_MIPMAP
+
+typedef struct _BlurFunction {
+ struct _BlurFunction *next;
+
+ int handle;
+ int target;
+ int param;
+ int unit;
+ int startTC;
+ int numITC;
+} BlurFunction;
+
+typedef struct _BlurBox {
+ decor_point_t p1;
+ decor_point_t p2;
+} BlurBox;
+
+#define BLUR_STATE_CLIENT 0
+#define BLUR_STATE_DECOR 1
+#define BLUR_STATE_NUM 2
+
+typedef struct _BlurState {
+ int threshold;
+ BlurBox *box;
+ int nBox;
+ Bool active;
+ Bool clipped;
+} BlurState;
+
+static int corePrivateIndex;
+
+typedef struct _BlurCore {
+ ObjectAddProc objectAdd;
+} BlurCore;
+
+static int displayPrivateIndex;
+
+#define BLUR_DISPLAY_OPTION_PULSE 0
+#define BLUR_DISPLAY_OPTION_NUM 1
+
+typedef struct _BlurDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+ MatchExpHandlerChangedProc matchExpHandlerChanged;
+ MatchPropertyChangedProc matchPropertyChanged;
+
+ CompOption opt[BLUR_DISPLAY_OPTION_NUM];
+
+ Atom blurAtom[BLUR_STATE_NUM];
+} BlurDisplay;
+
+#define BLUR_SCREEN_OPTION_BLUR_SPEED 0
+#define BLUR_SCREEN_OPTION_FOCUS_BLUR_MATCH 1
+#define BLUR_SCREEN_OPTION_FOCUS_BLUR 2
+#define BLUR_SCREEN_OPTION_ALPHA_BLUR_MATCH 3
+#define BLUR_SCREEN_OPTION_ALPHA_BLUR 4
+#define BLUR_SCREEN_OPTION_FILTER 5
+#define BLUR_SCREEN_OPTION_GAUSSIAN_RADIUS 6
+#define BLUR_SCREEN_OPTION_GAUSSIAN_STRENGTH 7
+#define BLUR_SCREEN_OPTION_MIPMAP_LOD 8
+#define BLUR_SCREEN_OPTION_SATURATION 9
+#define BLUR_SCREEN_OPTION_BLUR_OCCLUSION 10
+#define BLUR_SCREEN_OPTION_INDEPENDENT_TEX 11
+#define BLUR_SCREEN_OPTION_NUM 12
+
+typedef struct _BlurScreen {
+ int windowPrivateIndex;
+
+ CompOption opt[BLUR_SCREEN_OPTION_NUM];
+
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintOutputProc paintOutput;
+ PaintTransformedOutputProc paintTransformedOutput;
+ PaintWindowProc paintWindow;
+ DrawWindowProc drawWindow;
+ DrawWindowTextureProc drawWindowTexture;
+
+ WindowResizeNotifyProc windowResizeNotify;
+ WindowMoveNotifyProc windowMoveNotify;
+
+ Bool alphaBlur;
+
+ int blurTime;
+ Bool moreBlur;
+
+ Bool blurOcclusion;
+
+ int filterRadius;
+
+ BlurFunction *srcBlurFunctions;
+ BlurFunction *dstBlurFunctions;
+
+ Region region;
+ Region tmpRegion;
+ Region tmpRegion2;
+ Region tmpRegion3;
+ Region occlusion;
+
+ BoxRec stencilBox;
+ GLint stencilBits;
+
+ CompOutput *output;
+ int count;
+
+ GLuint texture[2];
+
+ GLenum target;
+ float tx;
+ float ty;
+ int width;
+ int height;
+
+ GLuint program;
+ int maxTemp;
+ GLuint fbo;
+ Bool fboStatus;
+
+ float amp[BLUR_GAUSSIAN_RADIUS_MAX];
+ float pos[BLUR_GAUSSIAN_RADIUS_MAX];
+ int numTexop;
+
+ CompTransform mvp;
+} BlurScreen;
+
+typedef struct _BlurWindow {
+ int blur;
+ Bool pulse;
+ Bool focusBlur;
+
+ BlurState state[BLUR_STATE_NUM];
+ Bool propSet[BLUR_STATE_NUM];
+
+ Region region;
+ Region clip;
+} BlurWindow;
+
+#define GET_BLUR_CORE(c) \
+ ((BlurCore *) (c)->base.privates[corePrivateIndex].ptr)
+
+#define BLUR_CORE(c) \
+ BlurCore *bc = GET_BLUR_CORE (c)
+
+#define GET_BLUR_DISPLAY(d) \
+ ((BlurDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define BLUR_DISPLAY(d) \
+ BlurDisplay *bd = GET_BLUR_DISPLAY (d)
+
+#define GET_BLUR_SCREEN(s, bd) \
+ ((BlurScreen *) (s)->base.privates[(bd)->screenPrivateIndex].ptr)
+
+#define BLUR_SCREEN(s) \
+ BlurScreen *bs = GET_BLUR_SCREEN (s, GET_BLUR_DISPLAY (s->display))
+
+#define GET_BLUR_WINDOW(w, bs) \
+ ((BlurWindow *) (w)->base.privates[(bs)->windowPrivateIndex].ptr)
+
+#define BLUR_WINDOW(w) \
+ BlurWindow *bw = GET_BLUR_WINDOW (w, \
+ GET_BLUR_SCREEN (w->screen, \
+ GET_BLUR_DISPLAY (w->screen->display)))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+/* pascal triangle based kernel generator */
+static int
+blurCreateGaussianLinearKernel (int radius,
+ float strength,
+ float *amp,
+ float *pos,
+ int *optSize)
+{
+ float factor = 0.5f + (strength / 2.0f);
+ float buffer1[BLUR_GAUSSIAN_RADIUS_MAX * 3];
+ float buffer2[BLUR_GAUSSIAN_RADIUS_MAX * 3];
+ float *ar1 = buffer1;
+ float *ar2 = buffer2;
+ float *tmp;
+ float sum = 0;
+ int size = (radius * 2) + 1;
+ int mySize = ceil (radius / 2.0f);
+ int i, j;
+
+ ar1[0] = 1.0;
+ ar1[1] = 1.0;
+
+ for (i = 3; i <= size; i++)
+ {
+ ar2[0] = 1;
+
+ for (j = 1; j < i - 1; j++)
+ ar2[j] = (ar1[j - 1] + ar1[j]) * factor;
+
+ ar2[i - 1] = 1;
+
+ tmp = ar1;
+ ar1 = ar2;
+ ar2 = tmp;
+ }
+
+ /* normalize */
+ for (i = 0; i < size; i++)
+ sum += ar1[i];
+
+ if (sum != 0.0f)
+ sum = 1.0f / sum;
+
+ for (i = 0; i < size; i++)
+ ar1[i] *= sum;
+
+ i = 0;
+ j = 0;
+
+ if (radius & 1)
+ {
+ pos[i] = radius;
+ amp[i] = ar1[i];
+ i = 1;
+ j = 1;
+ }
+
+ for (; i < mySize; i++)
+ {
+ pos[i] = radius - j;
+ pos[i] -= ar1[j + 1] / (ar1[j] + ar1[j + 1]);
+ amp[i] = ar1[j] + ar1[j + 1];
+
+ j += 2;
+ }
+
+ pos[mySize] = 0.0;
+ amp[mySize] = ar1[radius];
+
+ *optSize = mySize;
+
+ return radius;
+}
+
+static void
+blurUpdateFilterRadius (CompScreen *s)
+{
+ BLUR_SCREEN (s);
+
+ switch (bs->opt[BLUR_SCREEN_OPTION_FILTER].value.i) {
+ case BLUR_FILTER_4X_BILINEAR:
+ bs->filterRadius = 2;
+ break;
+ case BLUR_FILTER_GAUSSIAN: {
+ int radius = bs->opt[BLUR_SCREEN_OPTION_GAUSSIAN_RADIUS].value.i;
+ float strength = bs->opt[BLUR_SCREEN_OPTION_GAUSSIAN_STRENGTH].value.f;
+
+ blurCreateGaussianLinearKernel (radius, strength, bs->amp, bs->pos,
+ &bs->numTexop);
+
+ bs->filterRadius = radius;
+ } break;
+ case BLUR_FILTER_MIPMAP: {
+ float lod = bs->opt[BLUR_SCREEN_OPTION_MIPMAP_LOD].value.f;
+
+ bs->filterRadius = powf (2.0f, ceilf (lod));
+ } break;
+ }
+}
+
+static void
+blurDestroyFragmentFunctions (CompScreen *s,
+ BlurFunction **blurFunctions)
+{
+ BlurFunction *function, *next;
+
+ function = *blurFunctions;
+ while (function)
+ {
+ destroyFragmentFunction (s, function->handle);
+
+ next = function->next;
+ free (function);
+ function = next;
+ }
+
+ *blurFunctions = NULL;
+}
+
+static void
+blurReset (CompScreen *s)
+{
+ BLUR_SCREEN (s);
+
+ blurUpdateFilterRadius (s);
+ blurDestroyFragmentFunctions (s, &bs->srcBlurFunctions);
+ blurDestroyFragmentFunctions (s, &bs->dstBlurFunctions);
+
+ bs->width = bs->height = 0;
+
+ if (bs->program)
+ {
+ (*s->deletePrograms) (1, &bs->program);
+ bs->program = 0;
+ }
+}
+
+static Region
+regionFromBoxes (BlurBox *box,
+ int nBox,
+ int width,
+ int height)
+{
+ Region region;
+ REGION r;
+ int x, y;
+
+ region = XCreateRegion ();
+ if (!region)
+ return NULL;
+
+ r.rects = &r.extents;
+ r.numRects = r.size = 1;
+
+ while (nBox--)
+ {
+ decor_apply_gravity (box->p1.gravity, box->p1.x, box->p1.y,
+ width, height,
+ &x, &y);
+
+ r.extents.x1 = x;
+ r.extents.y1 = y;
+
+ decor_apply_gravity (box->p2.gravity, box->p2.x, box->p2.y,
+ width, height,
+ &x, &y);
+
+ r.extents.x2 = x;
+ r.extents.y2 = y;
+
+ if (r.extents.x2 > r.extents.x1 && r.extents.y2 > r.extents.y1)
+ XUnionRegion (region, &r, region);
+
+ box++;
+ }
+
+ return region;
+}
+
+static void
+blurWindowUpdateRegion (CompWindow *w)
+{
+ Region region, q;
+ REGION r;
+
+ BLUR_WINDOW (w);
+
+ region = XCreateRegion ();
+ if (!region)
+ return;
+
+ r.rects = &r.extents;
+ r.numRects = r.size = 1;
+
+ if (bw->state[BLUR_STATE_DECOR].threshold)
+ {
+ r.extents.x1 = -w->output.left;
+ r.extents.y1 = -w->output.top;
+ r.extents.x2 = w->width + w->output.right;
+ r.extents.y2 = w->height + w->output.bottom;
+
+ XUnionRegion (&r, region, region);
+
+ r.extents.x1 = 0;
+ r.extents.y1 = 0;
+ r.extents.x2 = w->width;
+ r.extents.y2 = w->height;
+
+ XSubtractRegion (region, &r, region);
+
+ bw->state[BLUR_STATE_DECOR].clipped = FALSE;
+
+ if (bw->state[BLUR_STATE_DECOR].nBox)
+ {
+ q = regionFromBoxes (bw->state[BLUR_STATE_DECOR].box,
+ bw->state[BLUR_STATE_DECOR].nBox,
+ w->width, w->height);
+ if (q)
+ {
+ XIntersectRegion (q, region, q);
+ if (!XEqualRegion (q, region))
+ {
+ XSubtractRegion (q, &emptyRegion, region);
+ bw->state[BLUR_STATE_DECOR].clipped = TRUE;
+ }
+
+ XDestroyRegion (q);
+ }
+ }
+ }
+
+ if (bw->state[BLUR_STATE_CLIENT].threshold)
+ {
+ r.extents.x1 = 0;
+ r.extents.y1 = 0;
+ r.extents.x2 = w->width;
+ r.extents.y2 = w->height;
+
+ bw->state[BLUR_STATE_CLIENT].clipped = FALSE;
+
+ if (bw->state[BLUR_STATE_CLIENT].nBox)
+ {
+ q = regionFromBoxes (bw->state[BLUR_STATE_CLIENT].box,
+ bw->state[BLUR_STATE_CLIENT].nBox,
+ w->width, w->height);
+ if (q)
+ {
+ XIntersectRegion (q, &r, q);
+ if (!XEqualRegion (q, &r))
+ bw->state[BLUR_STATE_CLIENT].clipped = TRUE;
+
+ XUnionRegion (q, region, region);
+ XDestroyRegion (q);
+ }
+ }
+ else
+ {
+ XUnionRegion (&r, region, region);
+ }
+ }
+
+ if (bw->region)
+ XDestroyRegion (bw->region);
+
+ if (XEmptyRegion (region))
+ {
+ bw->region = NULL;
+ XDestroyRegion (region);
+ }
+ else
+ {
+ bw->region = region;
+ XOffsetRegion (bw->region, w->attrib.x, w->attrib.y);
+ }
+}
+
+static void
+blurSetWindowBlur (CompWindow *w,
+ int state,
+ int threshold,
+ BlurBox *box,
+ int nBox)
+{
+ BLUR_WINDOW (w);
+
+ if (bw->state[state].box)
+ free (bw->state[state].box);
+
+ bw->state[state].threshold = threshold;
+ bw->state[state].box = box;
+ bw->state[state].nBox = nBox;
+
+ blurWindowUpdateRegion (w);
+
+ addWindowDamage (w);
+}
+
+static void
+blurUpdateAlphaWindowMatch (BlurScreen *bs,
+ CompWindow *w)
+{
+ BLUR_WINDOW (w);
+
+ if (!bw->propSet[BLUR_STATE_CLIENT])
+ {
+ CompMatch *match;
+
+ match = &bs->opt[BLUR_SCREEN_OPTION_ALPHA_BLUR_MATCH].value.match;
+ if (matchEval (match, w))
+ {
+ if (!bw->state[BLUR_STATE_CLIENT].threshold)
+ blurSetWindowBlur (w, BLUR_STATE_CLIENT, 4, NULL, 0);
+ }
+ else
+ {
+ if (bw->state[BLUR_STATE_CLIENT].threshold)
+ blurSetWindowBlur (w, BLUR_STATE_CLIENT, 0, NULL, 0);
+ }
+ }
+}
+
+static void
+blurUpdateWindowMatch (BlurScreen *bs,
+ CompWindow *w)
+{
+ CompMatch *match;
+ Bool focus;
+
+ BLUR_WINDOW (w);
+
+ blurUpdateAlphaWindowMatch (bs, w);
+
+ match = &bs->opt[BLUR_SCREEN_OPTION_FOCUS_BLUR_MATCH].value.match;
+
+ focus = w->screen->fragmentProgram && matchEval (match, w);
+ if (focus != bw->focusBlur)
+ {
+ bw->focusBlur = focus;
+ addWindowDamage (w);
+ }
+}
+
+static CompOption *
+blurGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+ int *count)
+{
+ BLUR_SCREEN (screen);
+
+ *count = NUM_OPTIONS (bs);
+ return bs->opt;
+}
+
+static Bool
+blurSetScreenOption (CompPlugin *plugin,
+ CompScreen *screen,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index, filter;
+
+ BLUR_SCREEN (screen);
+
+ o = compFindOption (bs->opt, NUM_OPTIONS (bs), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case BLUR_SCREEN_OPTION_BLUR_SPEED:
+ if (compSetFloatOption (o, value))
+ {
+ bs->blurTime = 1000.0f / o->value.f;
+ return TRUE;
+ }
+ break;
+ case BLUR_SCREEN_OPTION_FOCUS_BLUR_MATCH:
+ case BLUR_SCREEN_OPTION_ALPHA_BLUR_MATCH:
+ if (compSetMatchOption (o, value))
+ {
+ CompWindow *w;
+
+ for (w = screen->windows; w; w = w->next)
+ blurUpdateWindowMatch (bs, w);
+
+ bs->moreBlur = TRUE;
+ damageScreen (screen);
+
+ return TRUE;
+ }
+ break;
+ case BLUR_SCREEN_OPTION_FOCUS_BLUR:
+ if (compSetBoolOption (o, value))
+ {
+ bs->moreBlur = TRUE;
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ case BLUR_SCREEN_OPTION_ALPHA_BLUR:
+ if (compSetBoolOption (o, value))
+ {
+ if (screen->fragmentProgram && o->value.b)
+ bs->alphaBlur = TRUE;
+ else
+ bs->alphaBlur = FALSE;
+
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ case BLUR_SCREEN_OPTION_FILTER:
+ if (compSetIntOption (o, value))
+ {
+ blurReset (screen);
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ case BLUR_SCREEN_OPTION_GAUSSIAN_RADIUS:
+ if (compSetIntOption (o, value))
+ {
+ filter = bs->opt[BLUR_SCREEN_OPTION_FILTER].value.i;
+ if (filter == BLUR_FILTER_GAUSSIAN)
+ {
+ blurReset (screen);
+ damageScreen (screen);
+ }
+ return TRUE;
+ }
+ break;
+ case BLUR_SCREEN_OPTION_GAUSSIAN_STRENGTH:
+ if (compSetFloatOption (o, value))
+ {
+ filter = bs->opt[BLUR_SCREEN_OPTION_FILTER].value.i;
+ if (filter == BLUR_FILTER_GAUSSIAN)
+ {
+ blurReset (screen);
+ damageScreen (screen);
+ }
+ return TRUE;
+ }
+ break;
+ case BLUR_SCREEN_OPTION_MIPMAP_LOD:
+ if (compSetFloatOption (o, value))
+ {
+ filter = bs->opt[BLUR_SCREEN_OPTION_FILTER].value.i;
+ if (filter == BLUR_FILTER_MIPMAP)
+ {
+ blurReset (screen);
+ damageScreen (screen);
+ }
+ return TRUE;
+ }
+ break;
+ case BLUR_SCREEN_OPTION_SATURATION:
+ if (compSetIntOption (o, value))
+ {
+ blurReset (screen);
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ case BLUR_SCREEN_OPTION_BLUR_OCCLUSION:
+ if (compSetBoolOption (o, value))
+ {
+ bs->blurOcclusion = o->value.b;
+ blurReset (screen);
+ damageScreen (screen);
+ return TRUE;
+ }
+ case BLUR_SCREEN_OPTION_INDEPENDENT_TEX:
+ if (compSetBoolOption (o, value))
+ {
+ filter = bs->opt[BLUR_SCREEN_OPTION_FILTER].value.i;
+ if (filter == BLUR_FILTER_GAUSSIAN)
+ {
+ blurReset (screen);
+ damageScreen (screen);
+ }
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+blurWindowUpdate (CompWindow *w,
+ int state)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *propData;
+ int threshold = 0;
+ BlurBox *box = NULL;
+ int nBox = 0;
+
+ BLUR_DISPLAY (w->screen->display);
+ BLUR_SCREEN (w->screen);
+ BLUR_WINDOW (w);
+
+ result = XGetWindowProperty (w->screen->display->display, w->id,
+ bd->blurAtom[state], 0L, 8192L, FALSE,
+ XA_INTEGER, &actual, &format,
+ &n, &left, &propData);
+
+ if (result == Success && propData)
+ {
+ bw->propSet[state] = TRUE;
+
+ if (n >= 2)
+ {
+ long *data = (long *) propData;
+
+ threshold = data[0];
+
+ nBox = (n - 2) / 6;
+ if (nBox)
+ {
+ box = malloc (sizeof (BlurBox) * nBox);
+ if (box)
+ {
+ int i;
+
+ data += 2;
+
+ for (i = 0; i < nBox; i++)
+ {
+ box[i].p1.gravity = *data++;
+ box[i].p1.x = *data++;
+ box[i].p1.y = *data++;
+ box[i].p2.gravity = *data++;
+ box[i].p2.x = *data++;
+ box[i].p2.y = *data++;
+ }
+ }
+ }
+ }
+
+ XFree (propData);
+ }
+ else
+ {
+ bw->propSet[state] = FALSE;
+ }
+
+ blurSetWindowBlur (w,
+ state,
+ threshold,
+ box,
+ nBox);
+
+ blurUpdateAlphaWindowMatch (bs, w);
+}
+
+static void
+blurPreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ BLUR_SCREEN (s);
+
+ if (bs->moreBlur)
+ {
+ CompWindow *w;
+ int steps;
+ Bool focus = bs->opt[BLUR_SCREEN_OPTION_FOCUS_BLUR].value.b;
+ Bool focusBlur;
+
+ steps = (msSinceLastPaint * 0xffff) / bs->blurTime;
+ if (steps < 12)
+ steps = 12;
+
+ bs->moreBlur = FALSE;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ BLUR_WINDOW (w);
+
+ focusBlur = bw->focusBlur && focus;
+
+ if (!bw->pulse && (!focusBlur || w->id == s->display->activeWindow))
+ {
+ if (bw->blur)
+ {
+ bw->blur -= steps;
+ if (bw->blur > 0)
+ bs->moreBlur = TRUE;
+ else
+ bw->blur = 0;
+ }
+ }
+ else
+ {
+ if (bw->blur < 0xffff)
+ {
+ if (bw->pulse)
+ {
+ bw->blur += steps * 2;
+
+ if (bw->blur >= 0xffff)
+ {
+ bw->blur = 0xffff - 1;
+ bw->pulse = FALSE;
+ }
+
+ bs->moreBlur = TRUE;
+ }
+ else
+ {
+ bw->blur += steps;
+ if (bw->blur < 0xffff)
+ bs->moreBlur = TRUE;
+ else
+ bw->blur = 0xffff;
+ }
+ }
+ }
+ }
+ }
+
+ UNWRAP (bs, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (bs, s, preparePaintScreen, blurPreparePaintScreen);
+
+ if (s->damageMask & COMP_SCREEN_DAMAGE_REGION_MASK)
+ {
+ /* walk from bottom to top and expand damage */
+ if (bs->alphaBlur)
+ {
+ CompWindow *w;
+ int x1, y1, x2, y2;
+ int count = 0;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ BLUR_WINDOW (w);
+
+ if (w->attrib.map_state != IsViewable || !w->damaged)
+ continue;
+
+ if (bw->region)
+ {
+ x1 = bw->region->extents.x1 - bs->filterRadius;
+ y1 = bw->region->extents.y1 - bs->filterRadius;
+ x2 = bw->region->extents.x2 + bs->filterRadius;
+ y2 = bw->region->extents.y2 + bs->filterRadius;
+
+ if (x1 < s->damage->extents.x2 &&
+ y1 < s->damage->extents.y2 &&
+ x2 > s->damage->extents.x1 &&
+ y2 > s->damage->extents.y1)
+ {
+ XShrinkRegion (s->damage,
+ -bs->filterRadius,
+ -bs->filterRadius);
+
+ count++;
+ }
+ }
+ }
+
+ bs->count = count;
+ }
+ }
+}
+
+static Bool
+blurPaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ Bool status;
+
+ BLUR_SCREEN (s);
+
+ if (bs->alphaBlur)
+ {
+ bs->stencilBox = region->extents;
+ XSubtractRegion (region, &emptyRegion, bs->region);
+
+ if (mask & PAINT_SCREEN_REGION_MASK)
+ {
+ /* we need to redraw more than the screen region being updated */
+ if (bs->count)
+ {
+ XShrinkRegion (bs->region,
+ -bs->filterRadius * 2,
+ -bs->filterRadius * 2);
+ XIntersectRegion (bs->region, &s->region, bs->region);
+
+ region = bs->region;
+ }
+ }
+ }
+
+ if (!bs->blurOcclusion)
+ {
+ CompWindow *w;
+
+ XSubtractRegion (&emptyRegion, &emptyRegion, bs->occlusion);
+
+ for (w = s->windows; w; w = w->next)
+ XSubtractRegion (&emptyRegion, &emptyRegion,
+ GET_BLUR_WINDOW (w, bs)->clip);
+ }
+
+ bs->output = output;
+
+ UNWRAP (bs, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+ WRAP (bs, s, paintOutput, blurPaintOutput);
+
+ return status;
+}
+
+static void
+blurPaintTransformedOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ BLUR_SCREEN (s);
+
+ if (!bs->blurOcclusion)
+ {
+ CompWindow *w;
+
+ XSubtractRegion (&emptyRegion, &emptyRegion, bs->occlusion);
+
+ for (w = s->windows; w; w = w->next)
+ XSubtractRegion (&emptyRegion, &emptyRegion,
+ GET_BLUR_WINDOW (w, bs)->clip);
+ }
+
+ UNWRAP (bs, s, paintTransformedOutput);
+ (*s->paintTransformedOutput) (s, sAttrib, transform,
+ region, output, mask);
+ WRAP (bs, s, paintTransformedOutput, blurPaintTransformedOutput);
+}
+
+static void
+blurDonePaintScreen (CompScreen *s)
+{
+ BLUR_SCREEN (s);
+
+ if (bs->moreBlur)
+ {
+ CompWindow *w;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ BLUR_WINDOW (w);
+
+ if (bw->blur > 0 && bw->blur < 0xffff)
+ addWindowDamage (w);
+ }
+ }
+
+ UNWRAP (bs, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (bs, s, donePaintScreen, blurDonePaintScreen);
+}
+
+static Bool
+blurPaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ BLUR_SCREEN (s);
+ BLUR_WINDOW (w);
+
+ UNWRAP (bs, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (bs, s, paintWindow, blurPaintWindow);
+
+ if (!bs->blurOcclusion && (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK))
+ {
+ XSubtractRegion (bs->occlusion, &emptyRegion, bw->clip);
+
+ if (!(w->lastMask & PAINT_WINDOW_NO_CORE_INSTANCE_MASK) &&
+ !(w->lastMask & PAINT_WINDOW_TRANSFORMED_MASK) && bw->region)
+ XUnionRegion (bs->occlusion, bw->region, bs->occlusion);
+ }
+
+ return status;
+}
+
+static int
+getSrcBlurFragmentFunction (CompScreen *s,
+ CompTexture *texture,
+ int param)
+{
+ BlurFunction *function;
+ CompFunctionData *data;
+ int target;
+
+ BLUR_SCREEN (s);
+
+ if (texture->target == GL_TEXTURE_2D)
+ target = COMP_FETCH_TARGET_2D;
+ else
+ target = COMP_FETCH_TARGET_RECT;
+
+ for (function = bs->srcBlurFunctions; function; function = function->next)
+ if (function->param == param && function->target == target)
+ return function->handle;
+
+ data = createFunctionData ();
+ if (data)
+ {
+ static char *temp[] = { "offset0", "offset1", "sum" };
+ int i, handle = 0;
+ char str[1024];
+ Bool ok = TRUE;
+
+ for (i = 0; i < sizeof (temp) / sizeof (temp[0]); i++)
+ ok &= addTempHeaderOpToFunctionData (data, temp[i]);
+
+ snprintf (str, 1024,
+ "MUL offset0, program.env[%d].xyzw, { 1.0, 1.0, 0.0, 0.0 };"
+ "MUL offset1, program.env[%d].zwww, { 1.0, 1.0, 0.0, 0.0 };",
+ param, param);
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ switch (bs->opt[BLUR_SCREEN_OPTION_FILTER].value.i) {
+ case BLUR_FILTER_4X_BILINEAR:
+ default:
+ ok &= addFetchOpToFunctionData (data, "output", "offset0", target);
+ ok &= addDataOpToFunctionData (data, "MUL sum, output, 0.25;");
+ ok &= addFetchOpToFunctionData (data, "output", "-offset0", target);
+ ok &= addDataOpToFunctionData (data, "MAD sum, output, 0.25, sum;");
+ ok &= addFetchOpToFunctionData (data, "output", "offset1", target);
+ ok &= addDataOpToFunctionData (data, "MAD sum, output, 0.25, sum;");
+ ok &= addFetchOpToFunctionData (data, "output", "-offset1", target);
+ ok &= addDataOpToFunctionData (data,
+ "MAD output, output, 0.25, sum;");
+ break;
+ }
+
+ if (!ok)
+ {
+ destroyFunctionData (data);
+ return 0;
+ }
+
+ function = malloc (sizeof (BlurFunction));
+ if (function)
+ {
+ handle = createFragmentFunction (s, "blur", data);
+
+ function->handle = handle;
+ function->target = target;
+ function->param = param;
+ function->unit = 0;
+
+ function->next = bs->srcBlurFunctions;
+ bs->srcBlurFunctions = function;
+ }
+
+ destroyFunctionData (data);
+
+ return handle;
+ }
+
+ return 0;
+}
+
+static int
+getDstBlurFragmentFunction (CompScreen *s,
+ CompTexture *texture,
+ int param,
+ int unit,
+ int numITC,
+ int startTC)
+{
+ BlurFunction *function;
+ CompFunctionData *data;
+ int target;
+ char *targetString;
+
+ BLUR_SCREEN (s);
+
+ if (texture->target == GL_TEXTURE_2D)
+ {
+ target = COMP_FETCH_TARGET_2D;
+ targetString = "2D";
+ }
+ else
+ {
+ target = COMP_FETCH_TARGET_RECT;
+ targetString = "RECT";
+ }
+
+ for (function = bs->dstBlurFunctions; function; function = function->next)
+ if (function->param == param &&
+ function->target == target &&
+ function->unit == unit &&
+ function->numITC == numITC &&
+ function->startTC == startTC)
+ return function->handle;
+
+ data = createFunctionData ();
+ if (data)
+ {
+ static char *temp[] = { "fCoord", "mask", "sum", "dst" };
+ int i, j, handle = 0;
+ char str[1024];
+ int saturation = bs->opt[BLUR_SCREEN_OPTION_SATURATION].value.i;
+ Bool ok = TRUE;
+ int numIndirect;
+ int numIndirectOp;
+ int base, end, ITCbase;
+
+ for (i = 0; i < sizeof (temp) / sizeof (temp[0]); i++)
+ ok &= addTempHeaderOpToFunctionData (data, temp[i]);
+
+ if (saturation < 100)
+ ok &= addTempHeaderOpToFunctionData (data, "sat");
+
+ switch (bs->opt[BLUR_SCREEN_OPTION_FILTER].value.i) {
+ case BLUR_FILTER_4X_BILINEAR: {
+ static char *filterTemp[] = {
+ "t0", "t1", "t2", "t3",
+ "s0", "s1", "s2", "s3"
+ };
+
+ for (i = 0; i < sizeof (filterTemp) / sizeof (filterTemp[0]); i++)
+ ok &= addTempHeaderOpToFunctionData (data, filterTemp[i]);
+
+ ok &= addFetchOpToFunctionData (data, "output", NULL, target);
+ ok &= addColorOpToFunctionData (data, "output", "output");
+
+ snprintf (str, 1024,
+ "MUL fCoord, fragment.position, program.env[%d];",
+ param);
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ snprintf (str, 1024,
+ "ADD t0, fCoord, program.env[%d];"
+ "TEX s0, t0, texture[%d], %s;"
+
+ "SUB t1, fCoord, program.env[%d];"
+ "TEX s1, t1, texture[%d], %s;"
+
+ "MAD t2, program.env[%d], { -1.0, 1.0, 0.0, 0.0 }, fCoord;"
+ "TEX s2, t2, texture[%d], %s;"
+
+ "MAD t3, program.env[%d], { 1.0, -1.0, 0.0, 0.0 }, fCoord;"
+ "TEX s3, t3, texture[%d], %s;"
+
+ "MUL_SAT mask, output.a, program.env[%d];"
+
+ "MUL sum, s0, 0.25;"
+ "MAD sum, s1, 0.25, sum;"
+ "MAD sum, s2, 0.25, sum;"
+ "MAD sum, s3, 0.25, sum;",
+
+ param + 2, unit, targetString,
+ param + 2, unit, targetString,
+ param + 2, unit, targetString,
+ param + 2, unit, targetString,
+ param + 1);
+
+ ok &= addDataOpToFunctionData (data, str);
+ } break;
+ case BLUR_FILTER_GAUSSIAN: {
+
+ /* try to use only half of the available temporaries to keep
+ other plugins working */
+ if ((bs->maxTemp / 2) - 4 >
+ (bs->numTexop + (bs->numTexop - numITC)) * 2)
+ {
+ numIndirect = 1;
+ numIndirectOp = bs->numTexop;
+ }
+ else
+ {
+ i = MAX(((bs->maxTemp / 2) - 4) / 4, 1);
+ numIndirect = ceil ((float)bs->numTexop / (float)i);
+ numIndirectOp = ceil ((float)bs->numTexop / (float)numIndirect);
+ }
+
+ /* we need to define all coordinate temporaries if we have
+ multiple indirection steps */
+ j = (numIndirect > 1) ? 0 : numITC;
+
+ for (i = 0; i < numIndirectOp * 2; i++)
+ {
+ snprintf (str, 1024, "pix_%d", i);
+ ok &= addTempHeaderOpToFunctionData (data, str);
+ }
+
+ for (i = j * 2; i < numIndirectOp * 2; i++)
+ {
+ snprintf (str, 1024, "coord_%d", i);
+ ok &= addTempHeaderOpToFunctionData (data, str);
+ }
+
+ ok &= addFetchOpToFunctionData (data, "output", NULL, target);
+ ok &= addColorOpToFunctionData (data, "output", "output");
+
+ snprintf (str, 1024,
+ "MUL fCoord, fragment.position, program.env[%d];",
+ param);
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ snprintf (str, 1024,
+ "TEX sum, fCoord, texture[%d], %s;",
+ unit + 1, targetString);
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ snprintf (str, 1024,
+ "MUL_SAT mask, output.a, program.env[%d];"
+ "MUL sum, sum, %f;",
+ param + 1, bs->amp[bs->numTexop]);
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ for (j = 0; j < numIndirect; j++)
+ {
+ base = j * numIndirectOp;
+ end = MIN ((j + 1) * numIndirectOp, bs->numTexop) - base;
+
+ ITCbase = MAX (numITC - base, 0);
+
+ for (i = ITCbase; i < end; i++)
+ {
+ snprintf (str, 1024,
+ "ADD coord_%d, fCoord, {0.0, %g, 0.0, 0.0};"
+ "SUB coord_%d, fCoord, {0.0, %g, 0.0, 0.0};",
+ i * 2, bs->pos[base + i] * bs->ty,
+ (i * 2) + 1, bs->pos[base + i] * bs->ty);
+
+ ok &= addDataOpToFunctionData (data, str);
+ }
+
+ for (i = 0; i < ITCbase; i++)
+ {
+ snprintf (str, 1024,
+ "TXP pix_%d, fragment.texcoord[%d], texture[%d], %s;"
+ "TXP pix_%d, fragment.texcoord[%d], texture[%d], %s;",
+ i * 2, startTC + ((i + base) * 2),
+ unit + 1, targetString,
+ (i * 2) + 1, startTC + 1 + ((i + base) * 2),
+ unit + 1, targetString);
+
+ ok &= addDataOpToFunctionData (data, str);
+ }
+
+ for (i = ITCbase; i < end; i++)
+ {
+ snprintf (str, 1024,
+ "TEX pix_%d, coord_%d, texture[%d], %s;"
+ "TEX pix_%d, coord_%d, texture[%d], %s;",
+ i * 2, i * 2,
+ unit + 1, targetString,
+ (i * 2) + 1, (i * 2) + 1,
+ unit + 1, targetString);
+
+ ok &= addDataOpToFunctionData (data, str);
+ }
+
+ for (i = 0; i < end * 2; i++)
+ {
+ snprintf (str, 1024,
+ "MAD sum, pix_%d, %f, sum;",
+ i, bs->amp[base + (i / 2)]);
+
+ ok &= addDataOpToFunctionData (data, str);
+ }
+ }
+
+ } break;
+ case BLUR_FILTER_MIPMAP:
+ ok &= addFetchOpToFunctionData (data, "output", NULL, target);
+ ok &= addColorOpToFunctionData (data, "output", "output");
+
+ snprintf (str, 1024,
+ "MUL fCoord, fragment.position, program.env[%d].xyzz;"
+ "MOV fCoord.w, program.env[%d].w;"
+ "TXB sum, fCoord, texture[%d], %s;"
+ "MUL_SAT mask, output.a, program.env[%d];",
+ param, param, unit, targetString,
+ param + 1);
+
+ ok &= addDataOpToFunctionData (data, str);
+ break;
+ }
+
+ if (saturation < 100)
+ {
+ snprintf (str, 1024,
+ "MUL sat, sum, { 1.0, 1.0, 1.0, 0.0 };"
+ "DP3 sat, sat, { %f, %f, %f, %f };"
+ "LRP sum.xyz, %f, sum, sat;",
+ RED_SATURATION_WEIGHT, GREEN_SATURATION_WEIGHT,
+ BLUE_SATURATION_WEIGHT, 0.0f, saturation / 100.0f);
+
+ ok &= addDataOpToFunctionData (data, str);
+ }
+
+ snprintf (str, 1024,
+ "MAD dst, mask, -output.a, mask;"
+ "MAD output.rgb, sum, dst.a, output;"
+ "ADD output.a, output.a, dst.a;");
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ if (!ok)
+ {
+ destroyFunctionData (data);
+ return 0;
+ }
+
+ function = malloc (sizeof (BlurFunction));
+ if (function)
+ {
+ handle = createFragmentFunction (s, "blur", data);
+
+ function->handle = handle;
+ function->target = target;
+ function->param = param;
+ function->unit = unit;
+ function->numITC = numITC;
+ function->startTC = startTC;
+
+ function->next = bs->dstBlurFunctions;
+ bs->dstBlurFunctions = function;
+ }
+
+ destroyFunctionData (data);
+
+ return handle;
+ }
+
+ return 0;
+}
+
+static Bool
+projectVertices (CompScreen *s,
+ CompOutput *output,
+ const CompTransform *transform,
+ const float *object,
+ float *screen,
+ int n)
+{
+ GLdouble dProjection[16];
+ GLdouble dModel[16];
+ GLint viewport[4];
+ double x, y, z;
+ int i;
+
+ viewport[0] = output->region.extents.x1;
+ viewport[1] = s->height - output->region.extents.y2;
+ viewport[2] = output->width;
+ viewport[3] = output->height;
+
+ for (i = 0; i < 16; i++)
+ {
+ dModel[i] = transform->m[i];
+ dProjection[i] = s->projection[i];
+ }
+
+ while (n--)
+ {
+ if (!gluProject (object[0], object[1], object[2],
+ dModel, dProjection, viewport,
+ &x, &y, &z))
+ return FALSE;
+
+ screen[0] = x;
+ screen[1] = y;
+
+ object += 3;
+ screen += 2;
+ }
+
+ return TRUE;
+}
+
+static Bool
+loadFragmentProgram (CompScreen *s,
+ GLuint *program,
+ const char *string)
+{
+ GLint errorPos;
+
+ /* clear errors */
+ glGetError ();
+
+ if (!*program)
+ (*s->genPrograms) (1, program);
+
+ (*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, *program);
+ (*s->programString) (GL_FRAGMENT_PROGRAM_ARB,
+ GL_PROGRAM_FORMAT_ASCII_ARB,
+ strlen (string), string);
+
+ glGetIntegerv (GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
+ if (glGetError () != GL_NO_ERROR || errorPos != -1)
+ {
+ compLogMessage ("blur", CompLogLevelError,
+ "Failed to load blur program %s", string);
+
+ (*s->deletePrograms) (1, program);
+ *program = 0;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static Bool
+loadFilterProgram (CompScreen *s, int numITC)
+{
+ char buffer[4096];
+ char *targetString;
+ char *str = buffer;
+ int i, j;
+ int numIndirect;
+ int numIndirectOp;
+ int base, end, ITCbase;
+
+ BLUR_SCREEN (s);
+
+ if (bs->target == GL_TEXTURE_2D)
+ targetString = "2D";
+ else
+ targetString = "RECT";
+
+ str += sprintf (str,
+ "!!ARBfp1.0"
+ "ATTRIB texcoord = fragment.texcoord[0];"
+ "TEMP sum;");
+
+ if (bs->maxTemp - 1 > (bs->numTexop + (bs->numTexop - numITC)) * 2)
+ {
+ numIndirect = 1;
+ numIndirectOp = bs->numTexop;
+ }
+ else
+ {
+ i = (bs->maxTemp - 1) / 4;
+ numIndirect = ceil ((float)bs->numTexop / (float)i);
+ numIndirectOp = ceil ((float)bs->numTexop / (float)numIndirect);
+ }
+
+ /* we need to define all coordinate temporaries if we have
+ multiple indirection steps */
+ j = (numIndirect > 1) ? 0 : numITC;
+
+ for (i = 0; i < numIndirectOp; i++)
+ str += sprintf (str,"TEMP pix_%d, pix_%d;", i * 2, (i * 2) + 1);
+
+ for (i = j; i < numIndirectOp; i++)
+ str += sprintf (str,"TEMP coord_%d, coord_%d;", i * 2, (i * 2) + 1);
+
+ str += sprintf (str,
+ "TEX sum, texcoord, texture[0], %s;",
+ targetString);
+
+ str += sprintf (str,
+ "MUL sum, sum, %f;",
+ bs->amp[bs->numTexop]);
+
+ for (j = 0; j < numIndirect; j++)
+ {
+ base = j * numIndirectOp;
+ end = MIN ((j + 1) * numIndirectOp, bs->numTexop) - base;
+
+ ITCbase = MAX (numITC - base, 0);
+
+ for (i = ITCbase; i < end; i++)
+ str += sprintf (str,
+ "ADD coord_%d, texcoord, {%g, 0.0, 0.0, 0.0};"
+ "SUB coord_%d, texcoord, {%g, 0.0, 0.0, 0.0};",
+ i * 2, bs->pos[base + i] * bs->tx,
+ (i * 2) + 1, bs->pos[base + i] * bs->tx);
+
+ for (i = 0; i < ITCbase; i++)
+ str += sprintf (str,
+ "TEX pix_%d, fragment.texcoord[%d], texture[0], %s;"
+ "TEX pix_%d, fragment.texcoord[%d], texture[0], %s;",
+ i * 2, ((i + base) * 2) + 1, targetString,
+ (i * 2) + 1, ((i + base) * 2) + 2, targetString);
+
+ for (i = ITCbase; i < end; i++)
+ str += sprintf (str,
+ "TEX pix_%d, coord_%d, texture[0], %s;"
+ "TEX pix_%d, coord_%d, texture[0], %s;",
+ i * 2, i * 2, targetString,
+ (i * 2) + 1, (i * 2) + 1, targetString);
+
+ for (i = 0; i < end * 2; i++)
+ str += sprintf (str,
+ "MAD sum, pix_%d, %f, sum;",
+ i, bs->amp[base + (i / 2)]);
+ }
+
+ str += sprintf (str,
+ "MOV result.color, sum;"
+ "END");
+
+ return loadFragmentProgram (s, &bs->program, buffer);
+}
+
+static int
+fboPrologue (CompScreen *s)
+{
+ BLUR_SCREEN (s);
+
+ if (!bs->fbo)
+ return FALSE;
+
+ (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, bs->fbo);
+
+ /* bind texture and check status the first time */
+ if (!bs->fboStatus)
+ {
+ (*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT,
+ bs->target, bs->texture[1],
+ 0);
+
+ bs->fboStatus = (*s->checkFramebufferStatus) (GL_FRAMEBUFFER_EXT);
+ if (bs->fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ compLogMessage ("blur", CompLogLevelError,
+ "Framebuffer incomplete");
+
+ (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0);
+ (*s->deleteFramebuffers) (1, &bs->fbo);
+
+ bs->fbo = 0;
+
+ return 0;
+ }
+ }
+
+ glPushAttrib (GL_VIEWPORT_BIT | GL_ENABLE_BIT);
+
+ glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
+ glReadBuffer (GL_COLOR_ATTACHMENT0_EXT);
+
+ glDisable (GL_CLIP_PLANE0);
+ glDisable (GL_CLIP_PLANE1);
+ glDisable (GL_CLIP_PLANE2);
+ glDisable (GL_CLIP_PLANE3);
+
+ glViewport (0, 0, bs->width, bs->height);
+ glMatrixMode (GL_PROJECTION);
+ glPushMatrix ();
+ glLoadIdentity ();
+ glOrtho (0.0, bs->width, 0.0, bs->height, -1.0, 1.0);
+ glMatrixMode (GL_MODELVIEW);
+ glPushMatrix ();
+ glLoadIdentity ();
+
+ return TRUE;
+}
+
+static void
+fboEpilogue (CompScreen *s)
+{
+ (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0);
+
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ glDepthRange (0, 1);
+ glViewport (-1, -1, 2, 2);
+ glRasterPos2f (0, 0);
+
+ s->rasterX = s->rasterY = 0;
+
+ glMatrixMode (GL_PROJECTION);
+ glPopMatrix ();
+ glMatrixMode (GL_MODELVIEW);
+ glPopMatrix ();
+
+ glDrawBuffer (GL_BACK);
+ glReadBuffer (GL_BACK);
+
+ glPopAttrib ();
+}
+
+static Bool
+fboUpdate (CompScreen *s,
+ BoxPtr pBox,
+ int nBox)
+{
+ int i, y, iTC = 0;
+ Bool wasCulled = glIsEnabled (GL_CULL_FACE);
+
+ BLUR_SCREEN (s);
+
+ if (s->maxTextureUnits &&
+ bs->opt[BLUR_SCREEN_OPTION_INDEPENDENT_TEX].value.b)
+ iTC = MIN ((s->maxTextureUnits - 1) / 2, bs->numTexop);
+
+ if (!bs->program)
+ if (!loadFilterProgram (s, iTC))
+ return FALSE;
+
+ if (!fboPrologue (s))
+ return FALSE;
+
+ glDisable (GL_CULL_FACE);
+
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ glBindTexture (bs->target, bs->texture[0]);
+
+ glEnable (GL_FRAGMENT_PROGRAM_ARB);
+ (*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, bs->program);
+
+ glBegin (GL_QUADS);
+
+ while (nBox--)
+ {
+ y = s->height - pBox->y2;
+
+ for (i = 0; i < iTC; i++)
+ {
+ (*s->multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2),
+ bs->tx * (pBox->x1 + bs->pos[i]),
+ bs->ty * y);
+ (*s->multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2) + 1,
+ bs->tx * (pBox->x1 - bs->pos[i]),
+ bs->ty * y);
+ }
+
+ glTexCoord2f (bs->tx * pBox->x1, bs->ty * y);
+ glVertex2i (pBox->x1, y);
+
+ for (i = 0; i < iTC; i++)
+ {
+ (*s->multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2),
+ bs->tx * (pBox->x2 + bs->pos[i]),
+ bs->ty * y);
+ (*s->multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2) + 1,
+ bs->tx * (pBox->x2 - bs->pos[i]),
+ bs->ty * y);
+ }
+
+ glTexCoord2f (bs->tx * pBox->x2, bs->ty * y);
+ glVertex2i (pBox->x2, y);
+
+ y = s->height - pBox->y1;
+
+ for (i = 0; i < iTC; i++)
+ {
+ (*s->multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2),
+ bs->tx * (pBox->x2 + bs->pos[i]),
+ bs->ty * y);
+ (*s->multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2) + 1,
+ bs->tx * (pBox->x2 - bs->pos[i]),
+ bs->ty * y);
+ }
+
+ glTexCoord2f (bs->tx * pBox->x2, bs->ty * y);
+ glVertex2i (pBox->x2, y);
+
+ for (i = 0; i < iTC; i++)
+ {
+ (*s->multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2),
+ bs->tx * (pBox->x1 + bs->pos[i]),
+ bs->ty * y);
+ (*s->multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2) + 1,
+ bs->tx * (pBox->x1 - bs->pos[i]),
+ bs->ty * y);
+ }
+
+ glTexCoord2f (bs->tx * pBox->x1, bs->ty * y);
+ glVertex2i (pBox->x1, y);
+
+ pBox++;
+ }
+
+ glEnd ();
+
+ glDisable (GL_FRAGMENT_PROGRAM_ARB);
+
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ if (wasCulled)
+ glEnable (GL_CULL_FACE);
+
+ fboEpilogue (s);
+
+ return TRUE;
+}
+
+#define MAX_VERTEX_PROJECT_COUNT 20
+
+static void
+blurProjectRegion (CompWindow *w,
+ CompOutput *output,
+ const CompTransform *transform)
+{
+ CompScreen *s = w->screen;
+ float screen[MAX_VERTEX_PROJECT_COUNT * 2];
+ float vertices[MAX_VERTEX_PROJECT_COUNT * 3];
+ int nVertices, nQuadCombine;
+ int i, j, stride;
+ float *v, *vert;
+ float minX, maxX, minY, maxY, minZ, maxZ;
+ float *scr;
+ REGION region;
+
+ BLUR_SCREEN(s);
+
+ w->vCount = w->indexCount = 0;
+ (*w->screen->addWindowGeometry) (w, NULL, 0, bs->tmpRegion2,
+ &infiniteRegion);
+
+ if (!w->vCount)
+ return;
+
+ nVertices = (w->indexCount) ? w->indexCount: w->vCount;
+ nQuadCombine = 1;
+
+ stride = w->vertexStride;
+ vert = w->vertices + (stride - 3);
+
+ /* we need to find the best value here */
+ if (nVertices <= MAX_VERTEX_PROJECT_COUNT)
+ {
+ for (i = 0; i < nVertices; i++)
+ {
+ if (w->indexCount)
+ {
+ v = vert + (stride * w->indices[i]);
+ }
+ else
+ {
+ v = vert + (stride * i);
+ }
+
+ vertices[i * 3] = v[0];
+ vertices[(i * 3) + 1] = v[1];
+ vertices[(i * 3) + 2] = v[2];
+ }
+ }
+ else
+ {
+ minX = s->width;
+ maxX = 0;
+ minY = s->height;
+ maxY = 0;
+ minZ = 1000000;
+ maxZ = -1000000;
+
+ for (i = 0; i < w->vCount; i++)
+ {
+ v = vert + (stride * i);
+
+ if (v[0] < minX)
+ minX = v[0];
+
+ if (v[0] > maxX)
+ maxX = v[0];
+
+ if (v[1] < minY)
+ minY = v[1];
+
+ if (v[1] > maxY)
+ maxY = v[1];
+
+ if (v[2] < minZ)
+ minZ = v[2];
+
+ if (v[2] > maxZ)
+ maxZ = v[2];
+ }
+
+ vertices[0] = vertices[9] = minX;
+ vertices[1] = vertices[4] = minY;
+ vertices[3] = vertices[6] = maxX;
+ vertices[7] = vertices[10] = maxY;
+ vertices[2] = vertices[5] = maxZ;
+ vertices[8] = vertices[11] = maxZ;
+
+ nVertices = 4;
+
+ if (maxZ != minZ)
+ {
+ vertices[12] = vertices[21] = minX;
+ vertices[13] = vertices[16] = minY;
+ vertices[15] = vertices[18] = maxX;
+ vertices[19] = vertices[22] = maxY;
+ vertices[14] = vertices[17] = minZ;
+ vertices[20] = vertices[23] = minZ;
+ nQuadCombine = 2;
+ }
+ }
+
+
+ if (!projectVertices (w->screen, output, transform, vertices, screen,
+ nVertices * nQuadCombine))
+ return;
+
+ region.rects = &region.extents;
+ region.numRects = 1;
+
+ for (i = 0; i < nVertices / 4; i++)
+ {
+ scr = screen + (i * 4 * 2);
+
+ minX = s->width;
+ maxX = 0;
+ minY = s->height;
+ maxY = 0;
+
+ for (j = 0; j < 8 * nQuadCombine; j += 2)
+ {
+ if (scr[j] < minX)
+ minX = scr[j];
+
+ if (scr[j] > maxX)
+ maxX = scr[j];
+
+ if (scr[j + 1] < minY)
+ minY = scr[j + 1];
+
+ if (scr[j + 1] > maxY)
+ maxY = scr[j + 1];
+ }
+
+ region.extents.x1 = minX - bs->filterRadius;
+ region.extents.y1 = (s->height - maxY - bs->filterRadius);
+ region.extents.x2 = maxX + bs->filterRadius + 0.5f;
+ region.extents.y2 = (s->height - minY + bs->filterRadius + 0.5f);
+
+ XUnionRegion (&region, bs->tmpRegion3, bs->tmpRegion3);
+ }
+}
+
+static Bool
+blurUpdateDstTexture (CompWindow *w,
+ const CompTransform *transform,
+ BoxPtr pExtents,
+ int clientThreshold)
+{
+ CompScreen *s = w->screen;
+ BoxPtr pBox;
+ int nBox;
+ int y;
+ int filter;
+
+ BLUR_SCREEN (s);
+ BLUR_WINDOW (w);
+
+ filter = bs->opt[BLUR_SCREEN_OPTION_FILTER].value.i;
+
+ /* create empty region */
+ XSubtractRegion (&emptyRegion, &emptyRegion, bs->tmpRegion3);
+
+ if (filter == BLUR_FILTER_GAUSSIAN)
+ {
+ REGION region;
+
+ region.rects = &region.extents;
+ region.numRects = 1;
+
+ if (bw->state[BLUR_STATE_DECOR].threshold)
+ {
+ /* top */
+ region.extents.x1 = w->attrib.x - w->output.left;
+ region.extents.y1 = w->attrib.y - w->output.top;
+ region.extents.x2 = w->attrib.x + w->width + w->output.right;
+ region.extents.y2 = w->attrib.y;
+
+ XIntersectRegion (bs->tmpRegion, &region, bs->tmpRegion2);
+ if (bs->tmpRegion2->numRects)
+ blurProjectRegion (w, bs->output, transform);
+
+ /* bottom */
+ region.extents.x1 = w->attrib.x - w->output.left;
+ region.extents.y1 = w->attrib.y + w->height;
+ region.extents.x2 = w->attrib.x + w->width + w->output.right;
+ region.extents.y2 = w->attrib.y + w->height + w->output.bottom;
+
+ XIntersectRegion (bs->tmpRegion, &region, bs->tmpRegion2);
+ if (bs->tmpRegion2->numRects)
+ blurProjectRegion (w, bs->output, transform);
+
+ /* left */
+ region.extents.x1 = w->attrib.x - w->output.left;
+ region.extents.y1 = w->attrib.y;
+ region.extents.x2 = w->attrib.x;
+ region.extents.y2 = w->attrib.y + w->height;
+
+ XIntersectRegion (bs->tmpRegion, &region, bs->tmpRegion2);
+ if (bs->tmpRegion2->numRects)
+ blurProjectRegion (w, bs->output, transform);
+
+ /* right */
+ region.extents.x1 = w->attrib.x + w->width;
+ region.extents.y1 = w->attrib.y;
+ region.extents.x2 = w->attrib.x + w->width + w->output.right;
+ region.extents.y2 = w->attrib.y + w->height;
+
+ XIntersectRegion (bs->tmpRegion, &region, bs->tmpRegion2);
+ if (bs->tmpRegion2->numRects)
+ blurProjectRegion (w, bs->output, transform);
+ }
+
+ if (clientThreshold)
+ {
+ /* center */
+ region.extents.x1 = w->attrib.x;
+ region.extents.y1 = w->attrib.y;
+ region.extents.x2 = w->attrib.x + w->width;
+ region.extents.y2 = w->attrib.y + w->height;
+
+ XIntersectRegion (bs->tmpRegion, &region, bs->tmpRegion2);
+ if (bs->tmpRegion2->numRects)
+ blurProjectRegion (w, bs->output, transform);
+ }
+ }
+ else
+ {
+ /* get region that needs blur */
+ XSubtractRegion (bs->tmpRegion, &emptyRegion, bs->tmpRegion2);
+
+ if (bs->tmpRegion2->numRects)
+ blurProjectRegion (w, bs->output, transform);
+ }
+
+ XIntersectRegion (bs->tmpRegion3, bs->region, bs->tmpRegion);
+
+ if (XEmptyRegion (bs->tmpRegion))
+ return FALSE;
+
+ pBox = &bs->tmpRegion->extents;
+ nBox = 1;
+
+ *pExtents = bs->tmpRegion->extents;
+
+ if (!bs->texture[0] || bs->width != s->width || bs->height != s->height)
+ {
+ int i, textures = 1;
+
+ bs->width = s->width;
+ bs->height = s->height;
+
+ if (s->textureNonPowerOfTwo ||
+ (POWER_OF_TWO (bs->width) && POWER_OF_TWO (bs->height)))
+ {
+ bs->target = GL_TEXTURE_2D;
+ bs->tx = 1.0f / bs->width;
+ bs->ty = 1.0f / bs->height;
+ }
+ else
+ {
+ bs->target = GL_TEXTURE_RECTANGLE_NV;
+ bs->tx = 1;
+ bs->ty = 1;
+ }
+
+ if (filter == BLUR_FILTER_GAUSSIAN)
+ {
+ if (s->fbo && !bs->fbo)
+ (*s->genFramebuffers) (1, &bs->fbo);
+
+ if (!bs->fbo)
+ compLogMessage ("blur", CompLogLevelError,
+ "Failed to create framebuffer object");
+
+ textures = 2;
+ }
+
+ bs->fboStatus = FALSE;
+
+ for (i = 0; i < textures; i++)
+ {
+ if (!bs->texture[i])
+ glGenTextures (1, &bs->texture[i]);
+
+ glBindTexture (bs->target, bs->texture[i]);
+
+ glTexImage2D (bs->target, 0, GL_RGB,
+ bs->width,
+ bs->height,
+ 0, GL_BGRA,
+
+#if IMAGE_BYTE_ORDER == MSBFirst
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+#else
+ GL_UNSIGNED_BYTE,
+#endif
+
+ NULL);
+
+ glTexParameteri (bs->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (bs->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ if (filter == BLUR_FILTER_MIPMAP)
+ {
+ if (!s->fbo)
+ {
+ compLogMessage ("blur", CompLogLevelWarn,
+ "GL_EXT_framebuffer_object extension "
+ "is required for mipmap filter");
+ }
+ else if (bs->target != GL_TEXTURE_2D)
+ {
+ compLogMessage ("blur", CompLogLevelWarn,
+ "GL_ARB_texture_non_power_of_two "
+ "extension is required for mipmap filter");
+ }
+ else
+ {
+ glTexParameteri (bs->target, GL_TEXTURE_MIN_FILTER,
+ GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri (bs->target, GL_TEXTURE_MAG_FILTER,
+ GL_LINEAR_MIPMAP_LINEAR);
+ }
+ }
+
+ glTexParameteri (bs->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri (bs->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glCopyTexSubImage2D (bs->target, 0, 0, 0, 0, 0,
+ bs->width, bs->height);
+ }
+ }
+ else
+ {
+ glBindTexture (bs->target, bs->texture[0]);
+
+ while (nBox--)
+ {
+ y = s->height - pBox->y2;
+
+ glCopyTexSubImage2D (bs->target, 0,
+ pBox->x1, y,
+ pBox->x1, y,
+ pBox->x2 - pBox->x1,
+ pBox->y2 - pBox->y1);
+
+ pBox++;
+ }
+ }
+
+ switch (filter) {
+ case BLUR_FILTER_GAUSSIAN:
+ return fboUpdate (s, bs->tmpRegion->rects, bs->tmpRegion->numRects);
+ case BLUR_FILTER_MIPMAP:
+ (*s->generateMipmap) (bs->target);
+ break;
+ case BLUR_FILTER_4X_BILINEAR:
+ break;
+ }
+
+ glBindTexture (bs->target, 0);
+
+ return TRUE;
+}
+
+static Bool
+blurDrawWindow (CompWindow *w,
+ const CompTransform *transform,
+ const FragmentAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ BLUR_SCREEN (s);
+ BLUR_WINDOW (w);
+
+ if (bs->alphaBlur && bw->region)
+ {
+ int clientThreshold;
+
+ /* only care about client window blurring when it's translucent */
+ if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
+ clientThreshold = bw->state[BLUR_STATE_CLIENT].threshold;
+ else
+ clientThreshold = 0;
+
+ if (bw->state[BLUR_STATE_DECOR].threshold || clientThreshold)
+ {
+ Bool clipped = FALSE;
+ BoxRec box = { 0, 0, 0, 0 };
+ Region reg;
+ int i;
+
+ for (i = 0; i < 16; i++)
+ bs->mvp.m[i] = s->projection[i];
+
+ matrixMultiply (&bs->mvp, &bs->mvp, transform);
+
+ if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
+ reg = &infiniteRegion;
+ else
+ reg = region;
+
+ XIntersectRegion (bw->region, reg, bs->tmpRegion);
+ if (!bs->blurOcclusion && !(mask & PAINT_WINDOW_TRANSFORMED_MASK))
+ XSubtractRegion(bs->tmpRegion, bw->clip, bs->tmpRegion);
+
+ if (blurUpdateDstTexture (w, transform, &box, clientThreshold))
+ {
+ if (clientThreshold)
+ {
+ if (bw->state[BLUR_STATE_CLIENT].clipped)
+ {
+ if (bs->stencilBits)
+ {
+ bw->state[BLUR_STATE_CLIENT].active = TRUE;
+ clipped = TRUE;
+ }
+ }
+ else
+ {
+ bw->state[BLUR_STATE_CLIENT].active = TRUE;
+ }
+ }
+
+ if (bw->state[BLUR_STATE_DECOR].threshold)
+ {
+ if (bw->state[BLUR_STATE_DECOR].clipped)
+ {
+ if (bs->stencilBits)
+ {
+ bw->state[BLUR_STATE_DECOR].active = TRUE;
+ clipped = TRUE;
+ }
+ }
+ else
+ {
+ bw->state[BLUR_STATE_DECOR].active = TRUE;
+ }
+ }
+
+ if (!bs->blurOcclusion && bw->clip->numRects)
+ clipped = TRUE;
+ }
+
+ if (!bs->blurOcclusion)
+ XSubtractRegion (bw->region, bw->clip, bs->tmpRegion);
+ else
+ XSubtractRegion (bw->region, &emptyRegion, bs->tmpRegion);
+
+ if (!clientThreshold)
+ {
+ REGION wRegion;
+ wRegion.numRects = 1;
+ wRegion.rects = &wRegion.extents;
+ wRegion.extents.x1 = w->attrib.x;
+ wRegion.extents.y1 = w->attrib.y;
+ wRegion.extents.x2 = w->attrib.x + w->width;
+ wRegion.extents.y2 = w->attrib.y + w->height;
+ XSubtractRegion (bs->tmpRegion, &wRegion, bs->tmpRegion);
+ }
+
+ if (clipped)
+ {
+ w->vCount = w->indexCount = 0;
+ (*w->screen->addWindowGeometry) (w, NULL, 0, bs->tmpRegion, reg);
+ if (w->vCount)
+ {
+ BoxRec clearBox = bs->stencilBox;
+
+ bs->stencilBox = box;
+
+ glEnable (GL_STENCIL_TEST);
+ glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+
+ if (clearBox.x2 > clearBox.x1 && clearBox.y2 > clearBox.y1)
+ {
+ glPushAttrib (GL_SCISSOR_BIT);
+ glEnable (GL_SCISSOR_TEST);
+ glScissor (clearBox.x1,
+ s->height - clearBox.y2,
+ clearBox.x2 - clearBox.x1,
+ clearBox.y2 - clearBox.y1);
+ glClear (GL_STENCIL_BUFFER_BIT);
+ glPopAttrib ();
+ }
+
+ glStencilFunc (GL_ALWAYS, 0x1, ~0);
+ glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE);
+
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ (*w->drawWindowGeometry) (w);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glDisable (GL_STENCIL_TEST);
+ }
+ }
+ }
+ }
+
+ UNWRAP (bs, s, drawWindow);
+ status = (*s->drawWindow) (w, transform, attrib, region, mask);
+ WRAP (bs, s, drawWindow, blurDrawWindow);
+
+ bw->state[BLUR_STATE_CLIENT].active = FALSE;
+ bw->state[BLUR_STATE_DECOR].active = FALSE;
+
+ return status;
+}
+
+static void
+blurDrawWindowTexture (CompWindow *w,
+ CompTexture *texture,
+ const FragmentAttrib *attrib,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ int state;
+
+ BLUR_SCREEN (s);
+ BLUR_WINDOW (w);
+
+ if (texture == w->texture)
+ state = BLUR_STATE_CLIENT;
+ else
+ state = BLUR_STATE_DECOR;
+
+ if (bw->blur || bw->state[state].active)
+ {
+ FragmentAttrib fa = *attrib;
+ int param, function;
+ int unit = 0;
+ GLfloat dx, dy;
+ int iTC = 0;
+
+ if (bw->blur)
+ {
+ param = allocFragmentParameters (&fa, 1);
+
+ function = getSrcBlurFragmentFunction (s, texture, param);
+ if (function)
+ {
+ addFragmentFunction (&fa, function);
+
+ dx = ((texture->matrix.xx / 2.1f) * bw->blur) / 65535.0f;
+ dy = ((texture->matrix.yy / 2.1f) * bw->blur) / 65535.0f;
+
+ (*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
+ param, dx, dy, dx, -dy);
+
+ /* bi-linear filtering is required */
+ mask |= PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK;
+ }
+ }
+
+ if (bw->state[state].active)
+ {
+ FragmentAttrib dstFa = fa;
+ float threshold = (float) bw->state[state].threshold;
+
+ switch (bs->opt[BLUR_SCREEN_OPTION_FILTER].value.i) {
+ case BLUR_FILTER_4X_BILINEAR:
+ dx = bs->tx / 2.1f;
+ dy = bs->ty / 2.1f;
+
+ param = allocFragmentParameters (&dstFa, 3);
+ unit = allocFragmentTextureUnits (&dstFa, 1);
+
+ function =
+ getDstBlurFragmentFunction (s, texture, param, unit, 0, 0);
+ if (function)
+ {
+ addFragmentFunction (&dstFa, function);
+
+ (*s->activeTexture) (GL_TEXTURE0_ARB + unit);
+ glBindTexture (bs->target, bs->texture[0]);
+ (*s->activeTexture) (GL_TEXTURE0_ARB);
+
+ (*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, param,
+ bs->tx, bs->ty, 0.0f, 0.0f);
+
+ (*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
+ param + 1,
+ threshold, threshold,
+ threshold, threshold);
+
+ (*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
+ param + 2,
+ dx, dy, 0.0f, 0.0f);
+ }
+ break;
+ case BLUR_FILTER_GAUSSIAN:
+ if (bs->opt[BLUR_SCREEN_OPTION_INDEPENDENT_TEX].value.b)
+ {
+ /* leave one free texture unit for fragment position */
+ iTC = MAX (0, s->maxTextureUnits - (w->texUnits + 1));
+ if (iTC)
+ iTC = MIN (iTC / 2, bs->numTexop);
+ }
+
+ param = allocFragmentParameters (&dstFa, 2);
+ unit = allocFragmentTextureUnits (&dstFa, 2);
+
+ function =
+ getDstBlurFragmentFunction (s, texture, param, unit,
+ iTC, w->texUnits);
+ if (function)
+ {
+ int i;
+
+ addFragmentFunction (&dstFa, function);
+
+ (*s->activeTexture) (GL_TEXTURE0_ARB + unit);
+ glBindTexture (bs->target, bs->texture[0]);
+ (*s->activeTexture) (GL_TEXTURE0_ARB + unit + 1);
+ glBindTexture (bs->target, bs->texture[1]);
+ (*s->activeTexture) (GL_TEXTURE0_ARB);
+
+ (*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
+ param,
+ bs->tx, bs->ty,
+ 0.0f, 0.0f);
+
+ (*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
+ param + 1,
+ threshold, threshold,
+ threshold, threshold);
+
+ if (iTC)
+ {
+ CompTransform tm, rm;
+ float s_gen[4], t_gen[4], q_gen[4];
+
+ for (i = 0; i < 16; i++)
+ tm.m[i] = 0;
+ tm.m[0] = (bs->output->width / 2.0) * bs->tx;
+ tm.m[5] = (bs->output->height / 2.0) * bs->ty;
+ tm.m[10] = 1;
+
+ tm.m[12] = (bs->output->width / 2.0 +
+ bs->output->region.extents.x1) * bs->tx;
+ tm.m[13] = (bs->output->height / 2.0 + s->height -
+ bs->output->region.extents.y2) * bs->ty;
+ tm.m[14] = 1;
+ tm.m[15] = 1;
+
+ matrixMultiply (&tm, &tm, &bs->mvp);
+
+ for (i = 0; i < iTC; i++)
+ {
+ (*s->activeTexture) (GL_TEXTURE0_ARB + w->texUnits
+ + (i * 2));
+
+ matrixGetIdentity (&rm);
+ rm.m[13] = bs->ty * bs->pos[i];
+ matrixMultiply (&rm, &rm, &tm);
+ s_gen[0] = rm.m[0];
+ s_gen[1] = rm.m[4];
+ s_gen[2] = rm.m[8];
+ s_gen[3] = rm.m[12];
+ t_gen[0] = rm.m[1];
+ t_gen[1] = rm.m[5];
+ t_gen[2] = rm.m[9];
+ t_gen[3] = rm.m[13];
+ q_gen[0] = rm.m[3];
+ q_gen[1] = rm.m[7];
+ q_gen[2] = rm.m[11];
+ q_gen[3] = rm.m[15];
+
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen);
+ glTexGenfv(GL_Q, GL_OBJECT_PLANE, q_gen);
+
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE,
+ GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE,
+ GL_OBJECT_LINEAR);
+ glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE,
+ GL_OBJECT_LINEAR);
+
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glEnable(GL_TEXTURE_GEN_Q);
+
+ (*s->activeTexture) (GL_TEXTURE0_ARB + w->texUnits
+ + 1 + (i * 2));
+
+ matrixGetIdentity (&rm);
+ rm.m[13] = -bs->ty * bs->pos[i];
+ matrixMultiply (&rm, &rm, &tm);
+ s_gen[0] = rm.m[0];
+ s_gen[1] = rm.m[4];
+ s_gen[2] = rm.m[8];
+ s_gen[3] = rm.m[12];
+ t_gen[0] = rm.m[1];
+ t_gen[1] = rm.m[5];
+ t_gen[2] = rm.m[9];
+ t_gen[3] = rm.m[13];
+ q_gen[0] = rm.m[3];
+ q_gen[1] = rm.m[7];
+ q_gen[2] = rm.m[11];
+ q_gen[3] = rm.m[15];
+
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen);
+ glTexGenfv(GL_Q, GL_OBJECT_PLANE, q_gen);
+
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE,
+ GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE,
+ GL_OBJECT_LINEAR);
+ glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE,
+ GL_OBJECT_LINEAR);
+
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glEnable(GL_TEXTURE_GEN_Q);
+ }
+
+ (*s->activeTexture) (GL_TEXTURE0_ARB);
+ }
+
+ }
+ break;
+ case BLUR_FILTER_MIPMAP:
+ param = allocFragmentParameters (&dstFa, 2);
+ unit = allocFragmentTextureUnits (&dstFa, 1);
+
+ function =
+ getDstBlurFragmentFunction (s, texture, param, unit, 0, 0);
+ if (function)
+ {
+ float lod = bs->opt[BLUR_SCREEN_OPTION_MIPMAP_LOD].value.f;
+
+ addFragmentFunction (&dstFa, function);
+
+ (*s->activeTexture) (GL_TEXTURE0_ARB + unit);
+ glBindTexture (bs->target, bs->texture[0]);
+ (*s->activeTexture) (GL_TEXTURE0_ARB);
+
+ (*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
+ param,
+ bs->tx, bs->ty,
+ 0.0f, lod);
+
+ (*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
+ param + 1,
+ threshold, threshold,
+ threshold, threshold);
+ }
+ break;
+ }
+
+ if (bw->state[state].clipped ||
+ (!bs->blurOcclusion && bw->clip->numRects))
+ {
+ glEnable (GL_STENCIL_TEST);
+
+ glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
+ glStencilFunc (GL_EQUAL, 0x1, ~0);
+
+ /* draw region with destination blur */
+ UNWRAP (bs, s, drawWindowTexture);
+ (*s->drawWindowTexture) (w, texture, &dstFa, mask);
+
+ glStencilFunc (GL_EQUAL, 0, ~0);
+
+ /* draw region without destination blur */
+ (*s->drawWindowTexture) (w, texture, &fa, mask);
+ WRAP (bs, s, drawWindowTexture, blurDrawWindowTexture);
+
+ glDisable (GL_STENCIL_TEST);
+ }
+ else
+ {
+ /* draw with destination blur */
+ UNWRAP (bs, s, drawWindowTexture);
+ (*s->drawWindowTexture) (w, texture, &dstFa, mask);
+ WRAP (bs, s, drawWindowTexture, blurDrawWindowTexture);
+ }
+ }
+ else
+ {
+ UNWRAP (bs, s, drawWindowTexture);
+ (*s->drawWindowTexture) (w, texture, &fa, mask);
+ WRAP (bs, s, drawWindowTexture, blurDrawWindowTexture);
+ }
+
+ if (unit)
+ {
+ (*s->activeTexture) (GL_TEXTURE0_ARB + unit);
+ glBindTexture (bs->target, 0);
+ (*s->activeTexture) (GL_TEXTURE0_ARB + unit + 1);
+ glBindTexture (bs->target, 0);
+ (*s->activeTexture) (GL_TEXTURE0_ARB);
+ }
+
+ if (iTC)
+ {
+ int i;
+ for (i = w->texUnits; i < w->texUnits + (2 * iTC); i++)
+ {
+ (*s->activeTexture) (GL_TEXTURE0_ARB + i);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glDisable(GL_TEXTURE_GEN_Q);
+ }
+ (*s->activeTexture) (GL_TEXTURE0_ARB);
+ }
+ }
+ else
+ {
+ UNWRAP (bs, s, drawWindowTexture);
+ (*s->drawWindowTexture) (w, texture, attrib, mask);
+ WRAP (bs, s, drawWindowTexture, blurDrawWindowTexture);
+ }
+}
+
+static void
+blurHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ Window activeWindow = d->activeWindow;
+
+ BLUR_DISPLAY (d);
+
+ UNWRAP (bd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (bd, d, handleEvent, blurHandleEvent);
+
+ if (d->activeWindow != activeWindow)
+ {
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, activeWindow);
+ if (w)
+ {
+ BLUR_SCREEN (w->screen);
+
+ if (bs->opt[BLUR_SCREEN_OPTION_FOCUS_BLUR].value.b)
+ {
+ addWindowDamage (w);
+ bs->moreBlur = TRUE;
+ }
+ }
+
+ w = findWindowAtDisplay (d, d->activeWindow);
+ if (w)
+ {
+ BLUR_SCREEN (w->screen);
+
+ if (bs->opt[BLUR_SCREEN_OPTION_FOCUS_BLUR].value.b)
+ {
+ addWindowDamage (w);
+ bs->moreBlur = TRUE;
+ }
+ }
+ }
+
+ if (event->type == PropertyNotify)
+ {
+ int i;
+
+ for (i = 0; i < BLUR_STATE_NUM; i++)
+ {
+ if (event->xproperty.atom == bd->blurAtom[i])
+ {
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, event->xproperty.window);
+ if (w)
+ blurWindowUpdate (w, i);
+ }
+ }
+ }
+}
+
+static void
+blurWindowResizeNotify (CompWindow *w,
+ int dx,
+ int dy,
+ int dwidth,
+ int dheight)
+{
+ BLUR_SCREEN (w->screen);
+
+ if (bs->alphaBlur)
+ {
+ BLUR_WINDOW (w);
+
+ if (bw->state[BLUR_STATE_CLIENT].threshold ||
+ bw->state[BLUR_STATE_DECOR].threshold)
+ blurWindowUpdateRegion (w);
+ }
+
+ UNWRAP (bs, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
+ WRAP (bs, w->screen, windowResizeNotify, blurWindowResizeNotify);
+}
+
+static void
+blurWindowMoveNotify (CompWindow *w,
+ int dx,
+ int dy,
+ Bool immediate)
+{
+ BLUR_SCREEN (w->screen);
+ BLUR_WINDOW (w);
+
+ if (bw->region)
+ XOffsetRegion (bw->region, dx, dy);
+
+ UNWRAP (bs, w->screen, windowMoveNotify);
+ (*w->screen->windowMoveNotify) (w, dx, dy, immediate);
+ WRAP (bs, w->screen, windowMoveNotify, blurWindowMoveNotify);
+}
+
+static CompOption *
+blurGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ BLUR_DISPLAY (display);
+
+ *count = NUM_OPTIONS (bd);
+ return bd->opt;
+}
+
+static Bool
+blurSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ BLUR_DISPLAY (display);
+
+ o = compFindOption (bd->opt, NUM_OPTIONS (bd), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (display, o, value);
+}
+
+static Bool
+blurPulse (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompWindow *w;
+ int xid;
+
+ xid = getIntOptionNamed (option, nOption, "window", d->activeWindow);
+
+ w = findWindowAtDisplay (d, xid);
+ if (w && w->screen->fragmentProgram)
+ {
+ BLUR_SCREEN (w->screen);
+ BLUR_WINDOW (w);
+
+ bw->pulse = TRUE;
+ bs->moreBlur = TRUE;
+
+ addWindowDamage (w);
+ }
+
+ return FALSE;
+}
+
+static void
+blurMatchExpHandlerChanged (CompDisplay *d)
+{
+ CompScreen *s;
+ CompWindow *w;
+
+ BLUR_DISPLAY (d);
+
+ UNWRAP (bd, d, matchExpHandlerChanged);
+ (*d->matchExpHandlerChanged) (d);
+ WRAP (bd, d, matchExpHandlerChanged, blurMatchExpHandlerChanged);
+
+ /* match options are up to date after the call to matchExpHandlerChanged */
+ for (s = d->screens; s; s = s->next)
+ {
+ BLUR_SCREEN (s);
+
+ for (w = s->windows; w; w = w->next)
+ blurUpdateWindowMatch (bs, w);
+ }
+}
+
+static void
+blurMatchPropertyChanged (CompDisplay *d,
+ CompWindow *w)
+{
+ BLUR_DISPLAY (d);
+ BLUR_SCREEN (w->screen);
+
+ blurUpdateWindowMatch (bs, w);
+
+ UNWRAP (bd, d, matchPropertyChanged);
+ (*d->matchPropertyChanged) (d, w);
+ WRAP (bd, d, matchPropertyChanged, blurMatchPropertyChanged);
+}
+
+static void
+blurWindowAdd (CompScreen *s,
+ CompWindow *w)
+{
+ BLUR_SCREEN (s);
+
+ blurWindowUpdate (w, BLUR_STATE_CLIENT);
+ blurWindowUpdate (w, BLUR_STATE_DECOR);
+
+ blurUpdateWindowMatch (bs, w);
+}
+
+static void
+blurObjectAdd (CompObject *parent,
+ CompObject *object)
+{
+ static ObjectAddProc dispTab[] = {
+ (ObjectAddProc) 0, /* CoreAdd */
+ (ObjectAddProc) 0, /* DisplayAdd */
+ (ObjectAddProc) 0, /* ScreenAdd */
+ (ObjectAddProc) blurWindowAdd
+ };
+
+ BLUR_CORE (&core);
+
+ UNWRAP (bc, &core, objectAdd);
+ (*core.objectAdd) (parent, object);
+ WRAP (bc, &core, objectAdd, blurObjectAdd);
+
+ DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), (parent, object));
+}
+
+static Bool
+blurInitCore (CompPlugin *p,
+ CompCore *c)
+{
+ BlurCore *bc;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ bc = malloc (sizeof (BlurCore));
+ if (!bc)
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ free (bc);
+ return FALSE;
+ }
+
+ WRAP (bc, c, objectAdd, blurObjectAdd);
+
+ c->base.privates[corePrivateIndex].ptr = bc;
+
+ return TRUE;
+}
+
+static void
+blurFiniCore (CompPlugin *p,
+ CompCore *c)
+{
+ BLUR_CORE (c);
+
+ freeDisplayPrivateIndex (displayPrivateIndex);
+
+ UNWRAP (bc, c, objectAdd);
+
+ free (bc);
+}
+
+static const CompMetadataOptionInfo blurDisplayOptionInfo[] = {
+ { "pulse", "bell", 0, blurPulse, 0 }
+};
+
+static Bool
+blurInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ BlurDisplay *bd;
+
+ bd = malloc (sizeof (BlurDisplay));
+ if (!bd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &blurMetadata,
+ blurDisplayOptionInfo,
+ bd->opt,
+ BLUR_DISPLAY_OPTION_NUM))
+ {
+ free (bd);
+ return FALSE;
+ }
+
+ bd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (bd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, bd->opt, BLUR_DISPLAY_OPTION_NUM);
+ free (bd);
+ return FALSE;
+ }
+
+ bd->blurAtom[BLUR_STATE_CLIENT] =
+ XInternAtom (d->display, "_COMPIZ_WM_WINDOW_BLUR", 0);
+ bd->blurAtom[BLUR_STATE_DECOR] =
+ XInternAtom (d->display, DECOR_BLUR_ATOM_NAME, 0);
+
+ WRAP (bd, d, handleEvent, blurHandleEvent);
+ WRAP (bd, d, matchExpHandlerChanged, blurMatchExpHandlerChanged);
+ WRAP (bd, d, matchPropertyChanged, blurMatchPropertyChanged);
+
+ d->base.privates[displayPrivateIndex].ptr = bd;
+
+ return TRUE;
+}
+
+static void
+blurFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ BLUR_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, bd->screenPrivateIndex);
+
+ UNWRAP (bd, d, handleEvent);
+ UNWRAP (bd, d, matchExpHandlerChanged);
+ UNWRAP (bd, d, matchPropertyChanged);
+
+ compFiniDisplayOptions (d, bd->opt, BLUR_DISPLAY_OPTION_NUM);
+
+ free (bd);
+}
+
+static const CompMetadataOptionInfo blurScreenOptionInfo[] = {
+ { "blur_speed", "float", "<min>0.1</min>", 0, 0 },
+ { "focus_blur_match", "match", 0, 0, 0 },
+ { "focus_blur", "bool", 0, 0, 0 },
+ { "alpha_blur_match", "match", 0, 0, 0 },
+ { "alpha_blur", "bool", 0, 0, 0 },
+ { "filter", "int", RESTOSTRING (0, BLUR_FILTER_LAST), 0, 0 },
+ { "gaussian_radius", "int", "<min>1</min><max>15</max>", 0, 0 },
+ { "gaussian_strength", "float", "<min>0.0</min><max>1.0</max>", 0, 0 },
+ { "mipmap_lod", "float", "<min>0.1</min><max>5.0</max>", 0, 0 },
+ { "saturation", "int", "<min>0</min><max>100</max>", 0, 0 },
+ { "occlusion", "bool", 0, 0, 0 },
+ { "independent_tex", "bool", 0, 0, 0 }
+};
+
+static Bool
+blurInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ BlurScreen *bs;
+ int i;
+
+ BLUR_DISPLAY (s->display);
+
+ bs = malloc (sizeof (BlurScreen));
+ if (!bs)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &blurMetadata,
+ blurScreenOptionInfo,
+ bs->opt,
+ BLUR_SCREEN_OPTION_NUM))
+ {
+ free (bs);
+ return FALSE;
+ }
+
+ bs->region = XCreateRegion ();
+ if (!bs->region)
+ {
+ compFiniScreenOptions (s, bs->opt, BLUR_SCREEN_OPTION_NUM);
+ free (bs);
+ return FALSE;
+ }
+
+ bs->tmpRegion = XCreateRegion ();
+ if (!bs->tmpRegion)
+ {
+ compFiniScreenOptions (s, bs->opt, BLUR_SCREEN_OPTION_NUM);
+ XDestroyRegion (bs->region);
+ free (bs);
+ return FALSE;
+ }
+
+ bs->tmpRegion2 = XCreateRegion ();
+ if (!bs->tmpRegion2)
+ {
+ compFiniScreenOptions (s, bs->opt, BLUR_SCREEN_OPTION_NUM);
+ XDestroyRegion (bs->region);
+ XDestroyRegion (bs->tmpRegion);
+ free (bs);
+ return FALSE;
+ }
+
+ bs->tmpRegion3 = XCreateRegion ();
+ if (!bs->tmpRegion3)
+ {
+ compFiniScreenOptions (s, bs->opt, BLUR_SCREEN_OPTION_NUM);
+ XDestroyRegion (bs->region);
+ XDestroyRegion (bs->tmpRegion);
+ XDestroyRegion (bs->tmpRegion2);
+ free (bs);
+ return FALSE;
+ }
+
+ bs->occlusion = XCreateRegion ();
+ if (!bs->occlusion)
+ {
+ compFiniScreenOptions (s, bs->opt, BLUR_SCREEN_OPTION_NUM);
+ XDestroyRegion (bs->region);
+ XDestroyRegion (bs->tmpRegion);
+ XDestroyRegion (bs->tmpRegion2);
+ XDestroyRegion (bs->tmpRegion3);
+ free (bs);
+ return FALSE;
+ }
+
+
+ bs->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (bs->windowPrivateIndex < 0)
+ {
+ compFiniScreenOptions (s, bs->opt, BLUR_SCREEN_OPTION_NUM);
+ XDestroyRegion (bs->region);
+ XDestroyRegion (bs->tmpRegion);
+ XDestroyRegion (bs->tmpRegion2);
+ XDestroyRegion (bs->tmpRegion3);
+ XDestroyRegion (bs->occlusion);
+ free (bs);
+ return FALSE;
+ }
+
+ bs->output = NULL;
+ bs->count = 0;
+
+ bs->filterRadius = 0;
+
+ bs->srcBlurFunctions = NULL;
+ bs->dstBlurFunctions = NULL;
+ bs->blurTime = 1000.0f /
+ bs->opt[BLUR_SCREEN_OPTION_BLUR_SPEED].value.f;
+ bs->moreBlur = FALSE;
+ bs->blurOcclusion =
+ bs->opt[BLUR_SCREEN_OPTION_BLUR_OCCLUSION].value.b;
+
+ for (i = 0; i < 2; i++)
+ bs->texture[i] = 0;
+
+ bs->program = 0;
+ bs->maxTemp = 32;
+ bs->fbo = 0;
+ bs->fboStatus = FALSE;
+
+ glGetIntegerv (GL_STENCIL_BITS, &bs->stencilBits);
+ if (!bs->stencilBits)
+ compLogMessage ("blur", CompLogLevelWarn,
+ "No stencil buffer. Region based blur disabled");
+
+ /* We need GL_ARB_fragment_program for blur */
+ if (s->fragmentProgram)
+ bs->alphaBlur = bs->opt[BLUR_SCREEN_OPTION_ALPHA_BLUR].value.b;
+ else
+ bs->alphaBlur = FALSE;
+
+ if (s->fragmentProgram)
+ {
+ int tmp[4];
+ s->getProgramiv (GL_FRAGMENT_PROGRAM_ARB,
+ GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB,
+ tmp);
+ bs->maxTemp = tmp[0];
+ }
+
+ WRAP (bs, s, preparePaintScreen, blurPreparePaintScreen);
+ WRAP (bs, s, donePaintScreen, blurDonePaintScreen);
+ WRAP (bs, s, paintOutput, blurPaintOutput);
+ WRAP (bs, s, paintTransformedOutput, blurPaintTransformedOutput);
+ WRAP (bs, s, paintWindow, blurPaintWindow);
+ WRAP (bs, s, drawWindow, blurDrawWindow);
+ WRAP (bs, s, drawWindowTexture, blurDrawWindowTexture);
+ WRAP (bs, s, windowResizeNotify, blurWindowResizeNotify);
+ WRAP (bs, s, windowMoveNotify, blurWindowMoveNotify);
+
+ s->base.privates[bd->screenPrivateIndex].ptr = bs;
+
+ blurUpdateFilterRadius (s);
+
+ return TRUE;
+}
+
+static void
+blurFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ int i;
+
+ BLUR_SCREEN (s);
+
+ blurDestroyFragmentFunctions (s, &bs->srcBlurFunctions);
+ blurDestroyFragmentFunctions (s, &bs->dstBlurFunctions);
+
+ damageScreen (s);
+
+ XDestroyRegion (bs->region);
+ XDestroyRegion (bs->tmpRegion);
+ XDestroyRegion (bs->tmpRegion2);
+ XDestroyRegion (bs->tmpRegion3);
+ XDestroyRegion (bs->occlusion);
+
+ if (bs->fbo)
+ (*s->deleteFramebuffers) (1, &bs->fbo);
+
+ for (i = 0; i < 2; i++)
+ if (bs->texture[i])
+ glDeleteTextures (1, &bs->texture[i]);
+
+ freeWindowPrivateIndex (s, bs->windowPrivateIndex);
+
+ UNWRAP (bs, s, preparePaintScreen);
+ UNWRAP (bs, s, donePaintScreen);
+ UNWRAP (bs, s, paintOutput);
+ UNWRAP (bs, s, paintTransformedOutput);
+ UNWRAP (bs, s, paintWindow);
+ UNWRAP (bs, s, drawWindow);
+ UNWRAP (bs, s, drawWindowTexture);
+ UNWRAP (bs, s, windowResizeNotify);
+ UNWRAP (bs, s, windowMoveNotify);
+
+ compFiniScreenOptions (s, bs->opt, BLUR_SCREEN_OPTION_NUM);
+
+ free (bs);
+}
+
+static Bool
+blurInitWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ BlurWindow *bw;
+ int i;
+
+ BLUR_SCREEN (w->screen);
+
+ bw = malloc (sizeof (BlurWindow));
+ if (!bw)
+ return FALSE;
+
+ bw->blur = 0;
+ bw->pulse = FALSE;
+ bw->focusBlur = FALSE;
+
+ for (i = 0; i < BLUR_STATE_NUM; i++)
+ {
+ bw->state[i].threshold = 0;
+ bw->state[i].box = NULL;
+ bw->state[i].nBox = 0;
+ bw->state[i].clipped = FALSE;
+ bw->state[i].active = FALSE;
+
+ bw->propSet[i] = FALSE;
+ }
+
+ bw->region = NULL;
+
+ bw->clip = XCreateRegion ();
+ if (!bw->clip)
+ {
+ free (bw);
+ return FALSE;
+ }
+
+ w->base.privates[bs->windowPrivateIndex].ptr = bw;
+
+ if (w->base.parent)
+ blurWindowAdd (w->screen, w);
+
+ return TRUE;
+}
+
+static void
+blurFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ int i;
+
+ BLUR_WINDOW (w);
+
+ for (i = 0; i < BLUR_STATE_NUM; i++)
+ if (bw->state[i].box)
+ free (bw->state[i].box);
+
+ if (bw->region)
+ XDestroyRegion (bw->region);
+
+ XDestroyRegion (bw->clip);
+
+ free (bw);
+}
+
+static CompBool
+blurInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) blurInitCore,
+ (InitPluginObjectProc) blurInitDisplay,
+ (InitPluginObjectProc) blurInitScreen,
+ (InitPluginObjectProc) blurInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+blurFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) blurFiniCore,
+ (FiniPluginObjectProc) blurFiniDisplay,
+ (FiniPluginObjectProc) blurFiniScreen,
+ (FiniPluginObjectProc) blurFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+blurGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) blurGetDisplayOptions,
+ (GetPluginObjectOptionsProc) blurGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+blurSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) blurSetDisplayOption,
+ (SetPluginObjectOptionProc) blurSetScreenOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+blurInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&blurMetadata,
+ p->vTable->name,
+ blurDisplayOptionInfo,
+ BLUR_DISPLAY_OPTION_NUM,
+ blurScreenOptionInfo,
+ BLUR_SCREEN_OPTION_NUM))
+ return FALSE;
+
+ corePrivateIndex = allocateCorePrivateIndex ();
+ if (corePrivateIndex < 0)
+ {
+ compFiniMetadata (&blurMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&blurMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+blurFini (CompPlugin *p)
+{
+ freeCorePrivateIndex (corePrivateIndex);
+ compFiniMetadata (&blurMetadata);
+}
+
+static CompMetadata *
+blurGetMetadata (CompPlugin *plugin)
+{
+ return &blurMetadata;
+}
+
+static CompPluginVTable blurVTable = {
+ "blur",
+ blurGetMetadata,
+ blurInit,
+ blurFini,
+ blurInitObject,
+ blurFiniObject,
+ blurGetObjectOptions,
+ blurSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &blurVTable;
+}
diff --git a/plugins/clone.c b/plugins/clone.c
new file mode 100644
index 0000000..bda208e
--- /dev/null
+++ b/plugins/clone.c
@@ -0,0 +1,937 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sys/time.h>
+
+#include <compiz-core.h>
+
+static CompMetadata cloneMetadata;
+
+static int displayPrivateIndex;
+
+#define CLONE_DISPLAY_OPTION_INITIATE_BUTTON 0
+#define CLONE_DISPLAY_OPTION_NUM 1
+
+typedef struct _CloneDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompOption opt[CLONE_DISPLAY_OPTION_NUM];
+} CloneDisplay;
+
+typedef struct _CloneClone {
+ int src;
+ int dst;
+ Region region;
+ Window input;
+} CloneClone;
+
+typedef struct _CloneScreen {
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintOutputProc paintOutput;
+ PaintWindowProc paintWindow;
+ OutputChangeNotifyProc outputChangeNotify;
+
+ int grabIndex;
+ Bool grab;
+
+ float offset;
+
+ Bool transformed;
+
+ CloneClone *clone;
+ int nClone;
+
+ int x, y;
+ int grabbedOutput;
+ int src, dst;
+} CloneScreen;
+
+#define GET_CLONE_DISPLAY(d) \
+ ((CloneDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define CLONE_DISPLAY(d) \
+ CloneDisplay *cd = GET_CLONE_DISPLAY (d)
+
+#define GET_CLONE_SCREEN(s, cd) \
+ ((CloneScreen *) (s)->base.privates[(cd)->screenPrivateIndex].ptr)
+
+#define CLONE_SCREEN(s) \
+ CloneScreen *cs = GET_CLONE_SCREEN (s, GET_CLONE_DISPLAY (s->display))
+
+#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
+
+static void
+cloneRemove (CompScreen *s,
+ int i)
+{
+ CloneClone *clone;
+
+ CLONE_SCREEN (s);
+
+ clone = malloc (sizeof (CloneClone) * (cs->nClone - 1));
+ if (clone)
+ {
+ int j, k = 0;
+
+ for (j = 0; j < cs->nClone; j++)
+ if (j != i)
+ memcpy (&clone[k++], &cs->clone[j],
+ sizeof (CloneClone));
+
+ XDestroyRegion (cs->clone[i].region);
+ XDestroyWindow (s->display->display, cs->clone[i].input);
+
+ free (cs->clone);
+
+ cs->clone = clone;
+ cs->nClone--;
+ }
+}
+
+static void
+cloneFinish (CompScreen *s)
+{
+ CloneClone *clone;
+ int i;
+
+ CLONE_SCREEN (s);
+
+ cs->grab = FALSE;
+
+ if (cs->src != cs->dst)
+ {
+ clone = NULL;
+
+ /* check if we should replace current clone */
+ for (i = 0; i < cs->nClone; i++)
+ {
+ if (cs->clone[i].dst == cs->dst)
+ {
+ clone = &cs->clone[i];
+ break;
+ }
+ }
+
+ /* no existing clone for this destination, we must allocate one */
+ if (!clone)
+ {
+ Region region;
+
+ region = XCreateRegion ();
+ if (region)
+ {
+ clone =
+ realloc (cs->clone,
+ sizeof (CloneClone) * (cs->nClone + 1));
+ if (clone)
+ {
+ XSetWindowAttributes attr;
+ int x, y;
+
+ cs->clone = clone;
+ clone = &cs->clone[cs->nClone++];
+ clone->region = region;
+
+ attr.override_redirect = TRUE;
+
+ x = s->outputDev[cs->dst].region.extents.x1;
+ y = s->outputDev[cs->dst].region.extents.y1;
+
+ clone->input =
+ XCreateWindow (s->display->display,
+ s->root, x, y,
+ s->outputDev[cs->dst].width,
+ s->outputDev[cs->dst].height,
+ 0, 0, InputOnly, CopyFromParent,
+ CWOverrideRedirect, &attr);
+ XMapRaised (s->display->display, clone->input);
+ }
+ else
+ {
+ XDestroyRegion (region);
+ }
+ }
+ }
+
+ if (clone)
+ {
+ clone->src = cs->src;
+ clone->dst = cs->dst;
+ }
+ }
+
+ if (cs->grabbedOutput != cs->dst)
+ {
+ /* remove clone */
+ for (i = 0; i < cs->nClone; i++)
+ {
+ if (cs->clone[i].dst == cs->grabbedOutput)
+ {
+ cloneRemove (s, i);
+ break;
+ }
+ }
+ }
+}
+
+static void
+clonePreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ int i;
+
+ CLONE_SCREEN (s);
+
+ if (cs->grab)
+ {
+ if (cs->grabIndex)
+ {
+ cs->offset -= msSinceLastPaint * 0.005f;
+ if (cs->offset < 0.0f)
+ cs->offset = 0.0f;
+ }
+ else
+ {
+ cs->offset += msSinceLastPaint * 0.005f;
+ if (cs->offset >= 1.0f)
+ cs->offset = 1.0f;
+ }
+ }
+
+ UNWRAP (cs, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (cs, s, preparePaintScreen, clonePreparePaintScreen);
+
+ for (i = 0; i < cs->nClone; i++)
+ {
+ CompOutput *src = &s->outputDev[cs->clone[i].src];
+ CompOutput *dst = &s->outputDev[cs->clone[i].dst];
+ int dx, dy;
+
+ dx = dst->region.extents.x1 - src->region.extents.x1;
+ dy = dst->region.extents.y1 - src->region.extents.y1;
+
+ if (s->damageMask & COMP_SCREEN_DAMAGE_REGION_MASK)
+ {
+ if (src->width != dst->width || src->height != dst->height)
+ {
+ XSubtractRegion (&dst->region, &emptyRegion,
+ cs->clone[i].region);
+ XUnionRegion (s->damage, cs->clone[i].region, s->damage);
+ XSubtractRegion (&src->region, &emptyRegion,
+ cs->clone[i].region);
+ }
+ else
+ {
+ XSubtractRegion (s->damage, &dst->region, cs->clone[i].region);
+ XOffsetRegion (cs->clone[i].region, dx, dy);
+ XUnionRegion (s->damage, cs->clone[i].region, s->damage);
+ XSubtractRegion (s->damage, &src->region, cs->clone[i].region);
+ XOffsetRegion (cs->clone[i].region, -dx, -dy);
+ }
+ }
+ else
+ {
+ XSubtractRegion (&src->region, &emptyRegion, cs->clone[i].region);
+ }
+ }
+}
+
+static void
+cloneDonePaintScreen (CompScreen *s)
+{
+ CLONE_SCREEN (s);
+
+ if (cs->grab)
+ {
+ if (cs->offset == 1.0f)
+ cloneFinish (s);
+
+ damageScreen (s);
+ }
+
+ UNWRAP (cs, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (cs, s, donePaintScreen, cloneDonePaintScreen);
+}
+
+static Bool
+clonePaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *outputPtr,
+ unsigned int mask)
+{
+ Bool status;
+ int i, dst, output = 0;
+
+ CLONE_SCREEN (s);
+
+ dst = output = (outputPtr->id != ~0) ? outputPtr->id : 0;
+
+ if (!cs->grab || cs->grabbedOutput != output)
+ {
+ for (i = 0; i < cs->nClone; i++)
+ {
+ if (cs->clone[i].dst == output)
+ {
+ region = cs->clone[i].region;
+ dst = cs->clone[i].src;
+
+ if (s->outputDev[dst].width != s->outputDev[output].width ||
+ s->outputDev[dst].height != s->outputDev[output].height )
+ cs->transformed = TRUE;
+ else
+ cs->transformed = FALSE;
+
+ break;
+ }
+ }
+ }
+
+ UNWRAP (cs, s, paintOutput);
+ if (outputPtr->id != ~0)
+ status = (*s->paintOutput) (s, sAttrib, transform, region,
+ &s->outputDev[dst], mask);
+ else
+ status = (*s->paintOutput) (s, sAttrib, transform, region,
+ outputPtr, mask);
+ WRAP (cs, s, paintOutput, clonePaintOutput);
+
+ if (cs->grab)
+ {
+ CompTransform sTransform = *transform;
+ CompWindow *w;
+ GLenum filter;
+ float zoom1, zoom2x, zoom2y, x1, y1, x2, y2;
+ float zoomX, zoomY;
+ int dx, dy;
+
+ zoom1 = 160.0f / s->outputDev[cs->src].height;
+
+ x1 = cs->x - (s->outputDev[cs->src].region.extents.x1 * zoom1);
+ y1 = cs->y - (s->outputDev[cs->src].region.extents.y1 * zoom1);
+
+ x1 -= (s->outputDev[cs->src].width * zoom1) / 2;
+ y1 -= (s->outputDev[cs->src].height * zoom1) / 2;
+
+ if (cs->grabIndex)
+ {
+ x2 = s->outputDev[cs->grabbedOutput].region.extents.x1 -
+ s->outputDev[cs->src].region.extents.x1;
+ y2 = s->outputDev[cs->grabbedOutput].region.extents.y1 -
+ s->outputDev[cs->src].region.extents.y1;
+
+ zoom2x = (float) s->outputDev[cs->grabbedOutput].width /
+ s->outputDev[cs->src].width;
+ zoom2y = (float) s->outputDev[cs->grabbedOutput].height /
+ s->outputDev[cs->src].height;
+ }
+ else
+ {
+ x2 = s->outputDev[cs->dst].region.extents.x1 -
+ s->outputDev[cs->src].region.extents.x1;
+ y2 = s->outputDev[cs->dst].region.extents.y1 -
+ s->outputDev[cs->src].region.extents.y1;
+
+ zoom2x = (float) s->outputDev[cs->dst].width /
+ s->outputDev[cs->src].width;
+ zoom2y = (float) s->outputDev[cs->dst].height /
+ s->outputDev[cs->src].height;
+ }
+
+ /* XXX: hmm.. why do I need this.. */
+ if (x2 < 0.0f)
+ x2 *= zoom2x;
+ if (y2 < 0.0f)
+ y2 *= zoom2y;
+
+ dx = x1 * (1.0f - cs->offset) + x2 * cs->offset;
+ dy = y1 * (1.0f - cs->offset) + y2 * cs->offset;
+
+ zoomX = zoom1 * (1.0f - cs->offset) + zoom2x * cs->offset;
+ zoomY = zoom1 * (1.0f - cs->offset) + zoom2y * cs->offset;
+
+ matrixTranslate (&sTransform, -0.5f, -0.5f, -DEFAULT_Z_CAMERA);
+ matrixScale (&sTransform,
+ 1.0f / s->outputDev[output].width,
+ -1.0f / s->outputDev[output].height,
+ 1.0f);
+ matrixTranslate (&sTransform,
+ dx - s->outputDev[output].region.extents.x1,
+ dy - s->outputDev[output].region.extents.y2,
+ 0.0f);
+ matrixScale (&sTransform, zoomX, zoomY, 1.0f);
+
+ glPushMatrix ();
+ glLoadMatrixf (sTransform.m);
+
+ filter = s->display->textureFilter;
+
+ if (cs->offset == 0.0f)
+ s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->destroyed)
+ continue;
+
+ if (!w->shaded)
+ {
+ if (w->attrib.map_state != IsViewable || !w->damaged)
+ continue;
+ }
+
+ (*s->paintWindow) (w, &w->paint, &sTransform,
+ &s->outputDev[cs->src].region,
+ PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK);
+ }
+
+ s->display->textureFilter = filter;
+
+ glPopMatrix ();
+ }
+
+ return status;
+}
+
+static Bool
+clonePaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ CLONE_SCREEN (w->screen);
+
+ if (cs->nClone && cs->transformed)
+ mask |= PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK;
+
+ UNWRAP (cs, w->screen, paintWindow);
+ status = (*w->screen->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (cs, w->screen, paintWindow, clonePaintWindow);
+
+ return status;
+}
+
+static Bool
+cloneInitiate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ int i;
+
+ CLONE_SCREEN (s);
+
+ if (cs->grab || otherScreenGrabExist (s, "clone", 0))
+ return FALSE;
+
+ if (!cs->grabIndex)
+ cs->grabIndex = pushScreenGrab (s, None, "clone");
+
+ cs->grab = TRUE;
+
+ cs->x = getIntOptionNamed (option, nOption, "x", 0);
+ cs->y = getIntOptionNamed (option, nOption, "y", 0);
+
+ cs->src = cs->grabbedOutput = outputDeviceForPoint (s, cs->x, cs->y);
+
+ /* trace source */
+ i = 0;
+ while (i < cs->nClone)
+ {
+ if (cs->clone[i].dst == cs->src)
+ {
+ cs->src = cs->clone[i].src;
+ i = 0;
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+ }
+
+ return FALSE;
+}
+
+static Bool
+cloneTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ CLONE_SCREEN (s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (cs->grabIndex)
+ {
+ int x, y;
+
+ removeScreenGrab (s, cs->grabIndex, NULL);
+ cs->grabIndex = 0;
+
+ x = getIntOptionNamed (option, nOption, "x", 0);
+ y = getIntOptionNamed (option, nOption, "y", 0);
+
+ cs->dst = outputDeviceForPoint (s, x, y);
+
+ damageScreen (s);
+ }
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+static void
+cloneSetStrutsForCloneWindow (CompScreen *s,
+ CloneClone *clone)
+{
+ CompOutput *output = &s->outputDev[clone->dst];
+ XRectangle *rect = NULL;
+ CompStruts *struts;
+ CompWindow *w;
+
+ w = findWindowAtScreen (s, clone->input);
+ if (!w)
+ return;
+
+ struts = malloc (sizeof (CompStruts));
+ if (!struts)
+ return;
+
+ if (w->struts)
+ free (w->struts);
+
+ struts->left.x = 0;
+ struts->left.y = 0;
+ struts->left.width = 0;
+ struts->left.height = s->height;
+
+ struts->right.x = s->width;
+ struts->right.y = 0;
+ struts->right.width = 0;
+ struts->right.height = s->height;
+
+ struts->top.x = 0;
+ struts->top.y = 0;
+ struts->top.width = s->width;
+ struts->top.height = 0;
+
+ struts->bottom.x = 0;
+ struts->bottom.y = s->height;
+ struts->bottom.width = s->width;
+ struts->bottom.height = 0;
+
+ /* create struts relative to a screen edge that this output is next to */
+ if (output->region.extents.x1 == 0)
+ rect = &struts->left;
+ else if (output->region.extents.x2 == s->width)
+ rect = &struts->right;
+ else if (output->region.extents.y1 == 0)
+ rect = &struts->top;
+ else if (output->region.extents.y2 == s->height)
+ rect = &struts->bottom;
+
+ if (rect)
+ {
+ rect->x = output->region.extents.x1;
+ rect->y = output->region.extents.y1;
+ rect->width = output->width;
+ rect->height = output->height;
+ }
+
+ w->struts = struts;
+}
+
+static void
+cloneHandleMotionEvent (CompScreen *s,
+ int xRoot,
+ int yRoot)
+{
+ CLONE_SCREEN (s);
+
+ if (cs->grabIndex)
+ {
+ cs->x = xRoot;
+ cs->y = yRoot;
+
+ damageScreen (s);
+ }
+}
+
+static void
+cloneHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ CLONE_DISPLAY (d);
+
+ switch (event->type) {
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ cloneHandleMotionEvent (s, pointerX, pointerY);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ s = findScreenAtDisplay (d, event->xcrossing.root);
+ if (s)
+ cloneHandleMotionEvent (s, pointerX, pointerY);
+ default:
+ break;
+ }
+
+ UNWRAP (cd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (cd, d, handleEvent, cloneHandleEvent);
+
+ switch (event->type) {
+ case CreateNotify:
+ s = findScreenAtDisplay (d, event->xcreatewindow.parent);
+ if (s)
+ {
+ int i;
+
+ CLONE_SCREEN (s);
+
+ for (i = 0; i < cs->nClone; i++)
+ if (event->xcreatewindow.window == cs->clone[i].input)
+ cloneSetStrutsForCloneWindow (s, &cs->clone[i]);
+ }
+ default:
+ break;
+ }
+}
+
+static void
+cloneOutputChangeNotify (CompScreen *s)
+{
+ int i;
+
+ CLONE_SCREEN (s);
+
+ /* remove clones with destination or source that doesn't exist */
+ for (i = 0; i < cs->nClone; i++)
+ {
+ if (cs->clone[i].dst >= s->nOutputDev ||
+ cs->clone[i].src >= s->nOutputDev)
+ {
+ cloneRemove (s, i);
+ i = 0;
+ continue;
+ }
+ }
+
+ UNWRAP (cs, s, outputChangeNotify);
+ (*s->outputChangeNotify) (s);
+ WRAP (cs, s, outputChangeNotify, cloneOutputChangeNotify);
+}
+
+static CompOption *
+cloneGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ CLONE_DISPLAY (display);
+
+ *count = NUM_OPTIONS (cd);
+ return cd->opt;
+}
+
+static Bool
+cloneSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ CLONE_DISPLAY (display);
+
+ o = compFindOption (cd->opt, NUM_OPTIONS (cd), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (display, o, value);
+}
+
+static const CompMetadataOptionInfo cloneDisplayOptionInfo[] = {
+ { "initiate_button", "button", 0, cloneInitiate, cloneTerminate }
+};
+
+static Bool
+cloneInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ CloneDisplay *cd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ cd = malloc (sizeof (CloneDisplay));
+ if (!cd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &cloneMetadata,
+ cloneDisplayOptionInfo,
+ cd->opt,
+ CLONE_DISPLAY_OPTION_NUM))
+ {
+ free (cd);
+ return FALSE;
+ }
+
+ cd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (cd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, cd->opt, CLONE_DISPLAY_OPTION_NUM);
+ free (cd);
+ return FALSE;
+ }
+
+ WRAP (cd, d, handleEvent, cloneHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = cd;
+
+ return TRUE;
+}
+
+static void
+cloneFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ CLONE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, cd->screenPrivateIndex);
+
+ UNWRAP (cd, d, handleEvent);
+
+ compFiniDisplayOptions (d, cd->opt, CLONE_DISPLAY_OPTION_NUM);
+
+ free (cd);
+}
+
+static Bool
+cloneInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ CloneScreen *cs;
+
+ CLONE_DISPLAY (s->display);
+
+ cs = malloc (sizeof (CloneScreen));
+ if (!cs)
+ return FALSE;
+
+ cs->grabIndex = 0;
+ cs->grab = FALSE;
+
+ cs->offset = 1.0f;
+
+ cs->transformed = FALSE;
+
+ cs->nClone = 0;
+ cs->clone = NULL;
+
+ cs->src = 0;
+
+ WRAP (cs, s, preparePaintScreen, clonePreparePaintScreen);
+ WRAP (cs, s, donePaintScreen, cloneDonePaintScreen);
+ WRAP (cs, s, paintOutput, clonePaintOutput);
+ WRAP (cs, s, paintWindow, clonePaintWindow);
+ WRAP (cs, s, outputChangeNotify, cloneOutputChangeNotify);
+
+ s->base.privates[cd->screenPrivateIndex].ptr = cs;
+
+ return TRUE;
+}
+
+static void
+cloneFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ int i;
+
+ CLONE_SCREEN (s);
+
+ for (i = 0; i < cs->nClone; i++)
+ cloneRemove (s, i);
+
+ if (cs->clone)
+ free (cs->clone);
+
+ UNWRAP (cs, s, preparePaintScreen);
+ UNWRAP (cs, s, donePaintScreen);
+ UNWRAP (cs, s, paintOutput);
+ UNWRAP (cs, s, paintWindow);
+ UNWRAP (cs, s, outputChangeNotify);
+
+ free (cs);
+}
+
+static CompBool
+cloneInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) cloneInitDisplay,
+ (InitPluginObjectProc) cloneInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+cloneFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) cloneFiniDisplay,
+ (FiniPluginObjectProc) cloneFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+cloneGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) cloneGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+cloneSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) cloneSetDisplayOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+cloneInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&cloneMetadata,
+ p->vTable->name,
+ cloneDisplayOptionInfo,
+ CLONE_DISPLAY_OPTION_NUM,
+ 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&cloneMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&cloneMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+cloneFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&cloneMetadata);
+}
+
+static CompMetadata *
+cloneGetMetadata (CompPlugin *plugin)
+{
+ return &cloneMetadata;
+}
+
+CompPluginVTable cloneVTable = {
+ "clone",
+ cloneGetMetadata,
+ cloneInit,
+ cloneFini,
+ cloneInitObject,
+ cloneFiniObject,
+ cloneGetObjectOptions,
+ cloneSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &cloneVTable;
+}
diff --git a/plugins/commands.c b/plugins/commands.c
new file mode 100644
index 0000000..0b2851e
--- /dev/null
+++ b/plugins/commands.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright © 2009 Danny Baumann
+ *
+ * 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, and that the name of
+ * Danny Baumann not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Danny Baumann makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * DANNY BAUMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Danny Baumann <dannybaumann@web.de>
+ */
+
+#include <compiz-core.h>
+
+static CompMetadata commandsMetadata;
+
+static int displayPrivateIndex;
+
+#define COMMANDS_DISPLAY_OPTION_COMMAND0 0
+#define COMMANDS_DISPLAY_OPTION_COMMAND1 1
+#define COMMANDS_DISPLAY_OPTION_COMMAND2 2
+#define COMMANDS_DISPLAY_OPTION_COMMAND3 3
+#define COMMANDS_DISPLAY_OPTION_COMMAND4 4
+#define COMMANDS_DISPLAY_OPTION_COMMAND5 5
+#define COMMANDS_DISPLAY_OPTION_COMMAND6 6
+#define COMMANDS_DISPLAY_OPTION_COMMAND7 7
+#define COMMANDS_DISPLAY_OPTION_COMMAND8 8
+#define COMMANDS_DISPLAY_OPTION_COMMAND9 9
+#define COMMANDS_DISPLAY_OPTION_COMMAND10 10
+#define COMMANDS_DISPLAY_OPTION_COMMAND11 11
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND0_KEY 12
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND1_KEY 13
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND2_KEY 14
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND3_KEY 15
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND4_KEY 16
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND5_KEY 17
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND6_KEY 18
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND7_KEY 19
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND8_KEY 20
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND9_KEY 21
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND10_KEY 22
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND11_KEY 23
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND0_BUTTON 24
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND1_BUTTON 25
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND2_BUTTON 26
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND3_BUTTON 27
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND4_BUTTON 28
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND5_BUTTON 29
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND6_BUTTON 30
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND7_BUTTON 31
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND8_BUTTON 32
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND9_BUTTON 33
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND10_BUTTON 34
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND11_BUTTON 35
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND0_EDGE 36
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND1_EDGE 37
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND2_EDGE 38
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND3_EDGE 39
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND4_EDGE 40
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND5_EDGE 41
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND6_EDGE 42
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND7_EDGE 43
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND8_EDGE 44
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND9_EDGE 45
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND10_EDGE 46
+#define COMMANDS_DISPLAY_OPTION_RUN_COMMAND11_EDGE 47
+#define COMMANDS_DISPLAY_OPTION_NUM 48
+
+typedef struct _CommandsDisplay {
+ CompOption opt[COMMANDS_DISPLAY_OPTION_NUM];
+} CommandsDisplay;
+
+#define GET_COMMANDS_DISPLAY(d) \
+ ((CommandsDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+#define COMMANDS_DISPLAY(d) \
+ CommandsDisplay *cd = GET_COMMANDS_DISPLAY (d)
+
+#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
+
+static Bool
+runCommandDispatch (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+ s = findScreenAtDisplay (d, xid);
+
+ if (s)
+ {
+ int index = COMMANDS_DISPLAY_OPTION_COMMAND0 + action->priv.val;
+
+ COMMANDS_DISPLAY (d);
+
+ runCommand (s, cd->opt[index].value.s);
+ }
+
+ return TRUE;
+}
+
+static const CompMetadataOptionInfo commandsDisplayOptionInfo[] = {
+ { "command0", "string", 0, 0, 0 },
+ { "command1", "string", 0, 0, 0 },
+ { "command2", "string", 0, 0, 0 },
+ { "command3", "string", 0, 0, 0 },
+ { "command4", "string", 0, 0, 0 },
+ { "command5", "string", 0, 0, 0 },
+ { "command6", "string", 0, 0, 0 },
+ { "command7", "string", 0, 0, 0 },
+ { "command8", "string", 0, 0, 0 },
+ { "command9", "string", 0, 0, 0 },
+ { "command10", "string", 0, 0, 0 },
+ { "command11", "string", 0, 0, 0 },
+ { "run_command0_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command1_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command2_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command3_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command4_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command5_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command6_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command7_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command8_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command9_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command10_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command11_key", "key", 0, runCommandDispatch, 0 },
+ { "run_command0_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command1_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command2_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command3_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command4_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command5_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command6_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command7_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command8_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command9_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command10_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command11_button", "button", 0, runCommandDispatch, 0 },
+ { "run_command0_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command1_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command2_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command3_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command4_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command5_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command6_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command7_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command8_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command9_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command10_edge", "edge", 0, runCommandDispatch, 0 },
+ { "run_command11_edge", "edge", 0, runCommandDispatch, 0 }
+};
+
+static CompBool
+commandsInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ CommandsDisplay *cd;
+ int i;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ cd = malloc (sizeof (CommandsDisplay));
+ if (!cd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &commandsMetadata,
+ commandsDisplayOptionInfo,
+ cd->opt,
+ COMMANDS_DISPLAY_OPTION_NUM))
+ {
+ free (cd);
+ return FALSE;
+ }
+
+ for (i = 0; i < 12; i++)
+ {
+ int opt;
+
+ opt = COMMANDS_DISPLAY_OPTION_RUN_COMMAND0_KEY + i;
+ cd->opt[opt].value.action.priv.val = i;
+ opt = COMMANDS_DISPLAY_OPTION_RUN_COMMAND0_BUTTON + i;
+ cd->opt[opt].value.action.priv.val = i;
+ opt = COMMANDS_DISPLAY_OPTION_RUN_COMMAND0_EDGE + i;
+ cd->opt[opt].value.action.priv.val = i;
+ }
+
+ d->base.privates[displayPrivateIndex].ptr = cd;
+
+ return TRUE;
+}
+
+static void
+commandsFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ COMMANDS_DISPLAY (d);
+
+ compFiniDisplayOptions (d, cd->opt, COMMANDS_DISPLAY_OPTION_NUM);
+
+ free (cd);
+}
+
+static CompOption *
+commandsGetDisplayOptions (CompPlugin *p,
+ CompDisplay *d,
+ int *count)
+{
+ COMMANDS_DISPLAY (d);
+
+ *count = NUM_OPTIONS (cd);
+ return cd->opt;
+}
+
+static CompBool
+commandsSetDisplayOption (CompPlugin *p,
+ CompDisplay *d,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ COMMANDS_DISPLAY (d);
+
+ o = compFindOption (cd->opt, NUM_OPTIONS (cd), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (d, o, value);
+}
+
+static CompBool
+commandsInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) commandsInitDisplay
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+commandsFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) commandsFiniDisplay
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+commandsGetObjectOptions (CompPlugin *p,
+ CompObject *o,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) commandsGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (p, o, count));
+}
+
+static CompBool
+commandsSetObjectOption (CompPlugin *p,
+ CompObject *o,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) commandsSetDisplayOption,
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (p, o, name, value));
+}
+
+static Bool
+commandsInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&commandsMetadata,
+ p->vTable->name,
+ commandsDisplayOptionInfo,
+ COMMANDS_DISPLAY_OPTION_NUM, 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&commandsMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&commandsMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+commandsFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&commandsMetadata);
+}
+
+static CompMetadata *
+commandsGetMetadata (CompPlugin *p)
+{
+ return &commandsMetadata;
+}
+
+static CompPluginVTable commandsVTable = {
+ "commands",
+ commandsGetMetadata,
+ commandsInit,
+ commandsFini,
+ commandsInitObject,
+ commandsFiniObject,
+ commandsGetObjectOptions,
+ commandsSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &commandsVTable;
+}
diff --git a/plugins/cube.c b/plugins/cube.c
new file mode 100644
index 0000000..4207ef1
--- /dev/null
+++ b/plugins/cube.c
@@ -0,0 +1,2233 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ * Mirco Müller <macslow@bangang.de> (Skydome support)
+ */
+
+#include <string.h>
+#include <math.h>
+
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+
+#include <compiz-cube.h>
+
+static CompMetadata cubeMetadata;
+
+static int cubeCorePrivateIndex;
+static int cubeDisplayPrivateIndex;
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static void
+cubeLoadImg (CompScreen *s,
+ int n)
+{
+ unsigned int width, height;
+ int pw, ph;
+ CompOptionValue *imgFiles;
+ int imgNFile;
+
+ CUBE_SCREEN (s);
+
+ imgFiles = cs->opt[CUBE_SCREEN_OPTION_IMAGES].value.list.value;
+ imgNFile = cs->opt[CUBE_SCREEN_OPTION_IMAGES].value.list.nValue;
+
+ if (!cs->fullscreenOutput)
+ {
+ pw = s->width;
+ ph = s->height;
+ }
+ else
+ {
+ pw = s->outputDev[0].width;
+ ph = s->outputDev[0].height;
+ }
+
+ if (!imgNFile || cs->pw != pw || cs->ph != ph)
+ {
+ finiTexture (s, &cs->texture);
+ initTexture (s, &cs->texture);
+
+ if (!imgNFile)
+ return;
+ }
+
+ cs->imgCurFile = n % imgNFile;
+
+ if (!readImageToTexture (s, &cs->texture,
+ imgFiles[cs->imgCurFile].s,
+ &width, &height))
+ {
+ compLogMessage ("cube", CompLogLevelWarn,
+ "Failed to load slide: %s",
+ imgFiles[cs->imgCurFile].s);
+
+ finiTexture (s, &cs->texture);
+ initTexture (s, &cs->texture);
+
+ return;
+ }
+
+ cs->tc[0] = COMP_TEX_COORD_X (&cs->texture.matrix, width / 2.0f);
+ cs->tc[1] = COMP_TEX_COORD_Y (&cs->texture.matrix, height / 2.0f);
+
+ if (cs->opt[CUBE_SCREEN_OPTION_SCALE_IMAGE].value.b)
+ {
+ cs->tc[2] = COMP_TEX_COORD_X (&cs->texture.matrix, width);
+ cs->tc[3] = COMP_TEX_COORD_Y (&cs->texture.matrix, 0.0f);
+
+ cs->tc[4] = COMP_TEX_COORD_X (&cs->texture.matrix, 0.0f);
+ cs->tc[5] = COMP_TEX_COORD_Y (&cs->texture.matrix, 0.0f);
+
+ cs->tc[6] = COMP_TEX_COORD_X (&cs->texture.matrix, 0.0f);
+ cs->tc[7] = COMP_TEX_COORD_Y (&cs->texture.matrix, height);
+
+ cs->tc[8] = COMP_TEX_COORD_X (&cs->texture.matrix, width);
+ cs->tc[9] = COMP_TEX_COORD_Y (&cs->texture.matrix, height);
+
+ cs->tc[10] = COMP_TEX_COORD_X (&cs->texture.matrix, width);
+ cs->tc[11] = COMP_TEX_COORD_Y (&cs->texture.matrix, 0.0f);
+ }
+ else
+ {
+ float x1 = width / 2.0f - pw / 2.0f;
+ float y1 = height / 2.0f - ph / 2.0f;
+ float x2 = width / 2.0f + pw / 2.0f;
+ float y2 = height / 2.0f + ph / 2.0f;
+
+ cs->tc[2] = COMP_TEX_COORD_X (&cs->texture.matrix, x2);
+ cs->tc[3] = COMP_TEX_COORD_Y (&cs->texture.matrix, y1);
+
+ cs->tc[4] = COMP_TEX_COORD_X (&cs->texture.matrix, x1);
+ cs->tc[5] = COMP_TEX_COORD_Y (&cs->texture.matrix, y1);
+
+ cs->tc[6] = COMP_TEX_COORD_X (&cs->texture.matrix, x1);
+ cs->tc[7] = COMP_TEX_COORD_Y (&cs->texture.matrix, y2);
+
+ cs->tc[8] = COMP_TEX_COORD_X (&cs->texture.matrix, x2);
+ cs->tc[9] = COMP_TEX_COORD_Y (&cs->texture.matrix, y2);
+
+ cs->tc[10] = COMP_TEX_COORD_X (&cs->texture.matrix, x2);
+ cs->tc[11] = COMP_TEX_COORD_Y (&cs->texture.matrix, y1);
+ }
+}
+
+static Bool
+cubeUpdateGeometry (CompScreen *s,
+ int sides,
+ Bool invert)
+{
+ GLfloat radius, distance;
+ GLfloat *v;
+ int i, n;
+
+ CUBE_SCREEN (s);
+
+ sides *= cs->nOutput;
+
+ distance = 0.5f / tanf (M_PI / sides);
+ radius = 0.5f / sinf (M_PI / sides);
+
+ n = (sides + 2) * 2;
+
+ if (cs->nVertices != n)
+ {
+ v = realloc (cs->vertices, sizeof (GLfloat) * n * 3);
+ if (!v)
+ return FALSE;
+
+ cs->nVertices = n;
+ cs->vertices = v;
+ }
+ else
+ v = cs->vertices;
+
+ *v++ = 0.0f;
+ *v++ = 0.5 * invert;
+ *v++ = 0.0f;
+
+ for (i = 0; i <= sides; i++)
+ {
+ *v++ = radius * sinf (i * 2 * M_PI / sides + M_PI / sides);
+ *v++ = 0.5 * invert;
+ *v++ = radius * cosf (i * 2 * M_PI / sides + M_PI / sides);
+ }
+
+ *v++ = 0.0f;
+ *v++ = -0.5 * invert;
+ *v++ = 0.0f;
+
+ for (i = sides; i >= 0; i--)
+ {
+ *v++ = radius * sinf (i * 2 * M_PI / sides + M_PI / sides);
+ *v++ = -0.5 * invert;
+ *v++ = radius * cosf (i * 2 * M_PI / sides + M_PI / sides);
+ }
+
+ cs->invert = invert;
+ cs->distance = distance;
+
+ return TRUE;
+}
+
+static void
+cubeUpdateOutputs (CompScreen *s)
+{
+ BoxPtr pBox0, pBox1;
+ int i, j, k, x;
+
+ CUBE_SCREEN (s);
+
+ k = 0;
+
+ cs->fullscreenOutput = TRUE;
+
+ for (i = 0; i < s->nOutputDev; i++)
+ {
+ cs->outputMask[i] = -1;
+
+ /* dimensions must match first output */
+ if (s->outputDev[i].width != s->outputDev[0].width ||
+ s->outputDev[i].height != s->outputDev[0].height)
+ continue;
+
+ pBox0 = &s->outputDev[0].region.extents;
+ pBox1 = &s->outputDev[i].region.extents;
+
+ /* top and bottom line must match first output */
+ if (pBox0->y1 != pBox1->y1 || pBox0->y2 != pBox1->y2)
+ continue;
+
+ k++;
+
+ for (j = 0; j < s->nOutputDev; j++)
+ {
+ pBox0 = &s->outputDev[j].region.extents;
+
+ /* must not intersect other output region */
+ if (i != j && pBox0->x2 > pBox1->x1 && pBox0->x1 < pBox1->x2)
+ {
+ k--;
+ break;
+ }
+ }
+ }
+
+ if (cs->moMode == CUBE_MOMODE_ONE)
+ {
+ cs->fullscreenOutput = FALSE;
+ cs->nOutput = 1;
+ return;
+ }
+
+ if (cs->moMode == CUBE_MOMODE_MULTI)
+ {
+ cs->fullscreenOutput = TRUE;
+ cs->nOutput = 1;
+ return;
+ }
+
+ if (k != s->nOutputDev)
+ {
+ cs->fullscreenOutput = FALSE;
+ cs->nOutput = 1;
+ return;
+ }
+
+ /* add output indices from left to right */
+ j = 0;
+ for (;;)
+ {
+ x = MAXSHORT;
+ k = -1;
+
+ for (i = 0; i < s->nOutputDev; i++)
+ {
+ if (cs->outputMask[i] != -1)
+ continue;
+
+ if (s->outputDev[i].region.extents.x1 < x)
+ {
+ x = s->outputDev[i].region.extents.x1;
+ k = i;
+ }
+ }
+
+ if (k < 0)
+ break;
+
+ cs->outputMask[k] = j;
+ cs->output[j] = k;
+
+ j++;
+ }
+
+ cs->nOutput = j;
+
+ if (cs->nOutput == 1)
+ {
+ if (s->outputDev[0].width != s->width ||
+ s->outputDev[0].height != s->height)
+ cs->fullscreenOutput = FALSE;
+ }
+}
+
+static CompOption *
+cubeGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+ int *count)
+{
+ CUBE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (cs);
+ return cs->opt;
+}
+
+static void
+cubeUpdateSkyDomeTexture (CompScreen *screen)
+{
+ CUBE_SCREEN (screen);
+
+ finiTexture (screen, &cs->sky);
+ initTexture (screen, &cs->sky);
+
+ if (!cs->opt[CUBE_SCREEN_OPTION_SKYDOME].value.b)
+ return;
+
+ if (strlen (cs->opt[CUBE_SCREEN_OPTION_SKYDOME_IMG].value.s) == 0 ||
+ !readImageToTexture (screen,
+ &cs->sky,
+ cs->opt[CUBE_SCREEN_OPTION_SKYDOME_IMG].value.s,
+ &cs->skyW,
+ &cs->skyH))
+ {
+ GLfloat aaafTextureData[128][128][3];
+ GLfloat fRStart = (GLfloat)
+ cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_START].value.c[0] / 0xffff;
+ GLfloat fGStart = (GLfloat)
+ cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_START].value.c[1] / 0xffff;
+ GLfloat fBStart = (GLfloat)
+ cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_START].value.c[2] / 0xffff;
+ GLfloat fREnd = (GLfloat)
+ cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_END].value.c[0] / 0xffff;
+ GLfloat fGEnd = (GLfloat)
+ cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_END].value.c[1] / 0xffff;
+ GLfloat fBEnd = (GLfloat)
+ cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_END].value.c[2] / 0xffff;
+ GLfloat fRStep = (fREnd - fRStart) / 128.0f;
+ GLfloat fGStep = (fGEnd - fGStart) / 128.0f;
+ GLfloat fBStep = (fBStart - fBEnd) / 128.0f;
+ GLfloat fR = fRStart;
+ GLfloat fG = fGStart;
+ GLfloat fB = fBStart;
+
+ int iX, iY;
+
+ for (iX = 127; iX >= 0; iX--)
+ {
+ fR += fRStep;
+ fG += fGStep;
+ fB -= fBStep;
+
+ for (iY = 0; iY < 128; iY++)
+ {
+ aaafTextureData[iX][iY][0] = fR;
+ aaafTextureData[iX][iY][1] = fG;
+ aaafTextureData[iX][iY][2] = fB;
+ }
+ }
+
+ cs->sky.target = GL_TEXTURE_2D;
+ cs->sky.filter = GL_LINEAR;
+ cs->sky.wrap = GL_CLAMP_TO_EDGE;
+
+ cs->sky.matrix.xx = 1.0 / 128.0;
+ cs->sky.matrix.yy = -1.0 / 128.0;
+ cs->sky.matrix.xy = 0;
+ cs->sky.matrix.yx = 0;
+ cs->sky.matrix.x0 = 0;
+ cs->sky.matrix.y0 = 1.0;
+
+ cs->skyW = 128;
+ cs->skyH = 128;
+
+ glGenTextures (1, &cs->sky.name);
+ glBindTexture (cs->sky.target, cs->sky.name);
+
+ glTexParameteri (cs->sky.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (cs->sky.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri (cs->sky.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri (cs->sky.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D (cs->sky.target,
+ 0,
+ GL_RGB,
+ 128,
+ 128,
+ 0,
+ GL_RGB,
+ GL_FLOAT,
+ aaafTextureData);
+
+ glBindTexture (cs->sky.target, 0);
+ }
+}
+
+static Bool
+fillCircleTable (GLfloat **ppSint,
+ GLfloat **ppCost,
+ const int n)
+{
+ const GLfloat angle = 2 * M_PI / (GLfloat) ((n == 0) ? 1 : n);
+ const int size = abs (n);
+ int i;
+
+ *ppSint = (GLfloat *) calloc (sizeof (GLfloat), size + 1);
+ *ppCost = (GLfloat *) calloc (sizeof (GLfloat), size + 1);
+
+ if (!(*ppSint) || !(*ppCost))
+ {
+ free (*ppSint);
+ free (*ppCost);
+
+ return FALSE;
+ }
+
+ (*ppSint)[0] = 0.0;
+ (*ppCost)[0] = 1.0;
+
+ for (i = 1; i < size; i++)
+ {
+ (*ppSint)[i] = sin (angle * i);
+ (*ppCost)[i] = cos (angle * i);
+ }
+
+ (*ppSint)[size] = (*ppSint)[0];
+ (*ppCost)[size] = (*ppCost)[0];
+
+ return TRUE;
+}
+
+static void
+cubeUpdateSkyDomeList (CompScreen *s,
+ GLfloat fRadius)
+{
+ GLint iSlices = 128;
+ GLint iStacks = 64;
+ GLfloat afTexCoordX[4];
+ GLfloat afTexCoordY[4];
+ GLfloat *sint1;
+ GLfloat *cost1;
+ GLfloat *sint2;
+ GLfloat *cost2;
+ GLfloat r;
+ GLfloat x;
+ GLfloat y;
+ GLfloat z;
+ int i;
+ int j;
+ int iStacksStart;
+ int iStacksEnd;
+ int iSlicesStart;
+ int iSlicesEnd;
+ GLfloat fStepX;
+ GLfloat fStepY;
+
+ CUBE_SCREEN (s);
+
+ if (cs->opt[CUBE_SCREEN_OPTION_SKYDOME_ANIM].value.b)
+ {
+ iStacksStart = 11; /* min. 0 */
+ iStacksEnd = 53; /* max. 64 */
+ iSlicesStart = 0; /* min. 0 */
+ iSlicesEnd = 128; /* max. 128 */
+ }
+ else
+ {
+ iStacksStart = 21; /* min. 0 */
+ iStacksEnd = 43; /* max. 64 */
+ iSlicesStart = 21; /* min. 0 */
+ iSlicesEnd = 44; /* max. 128 */
+ }
+
+ fStepX = 1.0 / (GLfloat) (iSlicesEnd - iSlicesStart);
+ fStepY = 1.0 / (GLfloat) (iStacksEnd - iStacksStart);
+
+ if (!fillCircleTable (&sint1, &cost1, -iSlices))
+ return;
+
+ if (!fillCircleTable (&sint2, &cost2, iStacks * 2))
+ {
+ free (sint1);
+ free (cost1);
+ return;
+ }
+
+ afTexCoordX[0] = 1.0f;
+ afTexCoordY[0] = 1.0f - fStepY;
+ afTexCoordX[1] = 1.0f - fStepX;
+ afTexCoordY[1] = 1.0f - fStepY;
+ afTexCoordX[2] = 1.0f - fStepX;
+ afTexCoordY[2] = 1.0f;
+ afTexCoordX[3] = 1.0f;
+ afTexCoordY[3] = 1.0f;
+
+ if (!cs->skyListId)
+ cs->skyListId = glGenLists (1);
+
+ glNewList (cs->skyListId, GL_COMPILE);
+
+ enableTexture (s, &cs->sky, COMP_TEXTURE_FILTER_GOOD);
+
+ glBegin (GL_QUADS);
+
+ for (i = iStacksStart; i < iStacksEnd; i++)
+ {
+ afTexCoordX[0] = 1.0f;
+ afTexCoordX[1] = 1.0f - fStepX;
+ afTexCoordX[2] = 1.0f - fStepX;
+ afTexCoordX[3] = 1.0f;
+
+ for (j = iSlicesStart; j < iSlicesEnd; j++)
+ {
+ /* bottom-right */
+ z = cost2[i];
+ r = sint2[i];
+ x = cost1[j];
+ y = sint1[j];
+
+ glTexCoord2f (
+ COMP_TEX_COORD_X (&cs->sky.matrix, afTexCoordX[3] * cs->skyW),
+ COMP_TEX_COORD_Y (&cs->sky.matrix, afTexCoordY[3] * cs->skyH));
+ glVertex3f (x * r * fRadius, y * r * fRadius, z * fRadius);
+
+ /* top-right */
+ z = cost2[i + 1];
+ r = sint2[i + 1];
+ x = cost1[j];
+ y = sint1[j];
+
+ glTexCoord2f (
+ COMP_TEX_COORD_X (&cs->sky.matrix, afTexCoordX[0] * cs->skyW),
+ COMP_TEX_COORD_Y (&cs->sky.matrix, afTexCoordY[0] * cs->skyH));
+ glVertex3f (x * r * fRadius, y * r * fRadius, z * fRadius);
+
+ /* top-left */
+ z = cost2[i + 1];
+ r = sint2[i + 1];
+ x = cost1[j + 1];
+ y = sint1[j + 1];
+
+ glTexCoord2f (
+ COMP_TEX_COORD_X (&cs->sky.matrix, afTexCoordX[1] * cs->skyW),
+ COMP_TEX_COORD_Y (&cs->sky.matrix, afTexCoordY[1] * cs->skyH));
+ glVertex3f (x * r * fRadius, y * r * fRadius, z * fRadius);
+
+ /* bottom-left */
+ z = cost2[i];
+ r = sint2[i];
+ x = cost1[j + 1];
+ y = sint1[j + 1];
+
+ glTexCoord2f (
+ COMP_TEX_COORD_X (&cs->sky.matrix, afTexCoordX[2] * cs->skyW),
+ COMP_TEX_COORD_Y (&cs->sky.matrix, afTexCoordY[2] * cs->skyH));
+ glVertex3f (x * r * fRadius, y * r * fRadius, z * fRadius);
+
+ afTexCoordX[0] -= fStepX;
+ afTexCoordX[1] -= fStepX;
+ afTexCoordX[2] -= fStepX;
+ afTexCoordX[3] -= fStepX;
+ }
+
+ afTexCoordY[0] -= fStepY;
+ afTexCoordY[1] -= fStepY;
+ afTexCoordY[2] -= fStepY;
+ afTexCoordY[3] -= fStepY;
+ }
+
+ glEnd ();
+
+ disableTexture (s, &cs->sky);
+
+ glEndList ();
+
+ free (sint1);
+ free (cost1);
+ free (sint2);
+ free (cost2);
+}
+
+static Bool
+cubeSetScreenOption (CompPlugin *plugin,
+ CompScreen *screen,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ CUBE_SCREEN (screen);
+
+ o = compFindOption (cs->opt, NUM_OPTIONS (cs), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case CUBE_SCREEN_OPTION_COLOR:
+ if (compSetColorOption (o, value))
+ {
+ memcpy (cs->color, o->value.c, sizeof (cs->color));
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_IN:
+ if (compSetBoolOption (o, value))
+ {
+ if (cubeUpdateGeometry (screen, screen->hsize, o->value.b ? -1 : 1))
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_SCALE_IMAGE:
+ if (compSetBoolOption (o, value))
+ {
+ cubeLoadImg (screen, cs->imgCurFile);
+ damageScreen (screen);
+
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_IMAGES:
+ if (compSetOptionList (o, value))
+ {
+ cubeLoadImg (screen, cs->imgCurFile);
+ damageScreen (screen);
+
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_SKYDOME:
+ if (compSetBoolOption (o, value))
+ {
+ cubeUpdateSkyDomeTexture (screen);
+ cubeUpdateSkyDomeList (screen, 1.0f);
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_SKYDOME_IMG:
+ if (compSetStringOption (o, value))
+ {
+ cubeUpdateSkyDomeTexture (screen);
+ cubeUpdateSkyDomeList (screen, 1.0f);
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_SKYDOME_ANIM:
+ if (compSetBoolOption (o, value))
+ {
+ cubeUpdateSkyDomeTexture (screen);
+ cubeUpdateSkyDomeList (screen, 1.0f);
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_SKYDOME_GRAD_START:
+ if (compSetColorOption (o, value))
+ {
+ cubeUpdateSkyDomeTexture (screen);
+ cubeUpdateSkyDomeList (screen, 1.0f);
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_SKYDOME_GRAD_END:
+ if (compSetColorOption (o, value))
+ {
+ cubeUpdateSkyDomeTexture (screen);
+ cubeUpdateSkyDomeList (screen, 1.0f);
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_MULTIOUTPUT_MODE:
+ if (compSetIntOption (o, value))
+ {
+ cs->moMode = o->value.i;
+ cubeUpdateOutputs (screen);
+ cubeUpdateGeometry (screen, screen->hsize, cs->invert);
+ damageScreen (screen);
+ return TRUE;
+ }
+ break;
+ default:
+ return compSetScreenOption (screen, o, value);
+ }
+
+ return FALSE;
+}
+
+static int
+adjustVelocity (CubeScreen *cs)
+{
+ float unfold, adjust, amount;
+
+ if (cs->unfolded)
+ unfold = 1.0f - cs->unfold;
+ else
+ unfold = 0.0f - cs->unfold;
+
+ adjust = unfold * 0.02f * cs->opt[CUBE_SCREEN_OPTION_ACCELERATION].value.f;
+ amount = fabs (unfold);
+ if (amount < 1.0f)
+ amount = 1.0f;
+ else if (amount > 3.0f)
+ amount = 3.0f;
+
+ cs->unfoldVelocity = (amount * cs->unfoldVelocity + adjust) /
+ (amount + 2.0f);
+
+ return (fabs (unfold) < 0.002f && fabs (cs->unfoldVelocity) < 0.01f);
+}
+
+static void
+cubePreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ int opt;
+ float x, progress;
+
+ CUBE_SCREEN (s);
+
+ if (cs->grabIndex)
+ {
+ int steps;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.2f *
+ cs->opt[CUBE_SCREEN_OPTION_SPEED].value.f;
+ steps = amount / (0.5f * cs->opt[CUBE_SCREEN_OPTION_TIMESTEP].value.f);
+ if (!steps) steps = 1;
+ chunk = amount / (float) steps;
+
+ while (steps--)
+ {
+ cs->unfold += cs->unfoldVelocity * chunk;
+ if (cs->unfold > 1.0f)
+ cs->unfold = 1.0f;
+
+ if (adjustVelocity (cs))
+ {
+ if (cs->unfold < 0.5f)
+ {
+ if (cs->grabIndex)
+ {
+ removeScreenGrab (s, cs->grabIndex, NULL);
+ cs->grabIndex = 0;
+ }
+
+ cs->unfold = 0.0f;
+ }
+ break;
+ }
+ }
+ }
+
+ memset (cs->cleared, 0, sizeof (Bool) * s->nOutputDev);
+ memset (cs->capsPainted, 0, sizeof (Bool) * s->nOutputDev);
+
+ /* Transparency handling */
+ if (cs->rotationState == RotationManual ||
+ (cs->rotationState == RotationChange &&
+ !cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT_MANUAL_ONLY].value.b))
+ {
+ opt = cs->lastOpacityIndex = CUBE_SCREEN_OPTION_ACTIVE_OPACITY;
+ }
+ else if (cs->rotationState == RotationChange)
+ {
+ opt = cs->lastOpacityIndex = CUBE_SCREEN_OPTION_INACTIVE_OPACITY;
+ }
+ else
+ {
+ opt = CUBE_SCREEN_OPTION_INACTIVE_OPACITY;
+ }
+
+ cs->toOpacity = (cs->opt[opt].value.f / 100.0f) * OPAQUE;
+
+ (*cs->getRotation) (s, &x, &x, &progress);
+
+ if (cs->desktopOpacity != cs->toOpacity ||
+ (progress > 0.0 && progress < 1.0))
+ {
+ cs->desktopOpacity =
+ (cs->opt[CUBE_SCREEN_OPTION_INACTIVE_OPACITY].value.f -
+ ((cs->opt[CUBE_SCREEN_OPTION_INACTIVE_OPACITY].value.f -
+ cs->opt[cs->lastOpacityIndex].value.f) * progress))
+ / 100.0f * OPAQUE;
+
+ }
+
+ cs->paintAllViewports = (cs->desktopOpacity != OPAQUE);
+
+ UNWRAP (cs, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (cs, s, preparePaintScreen, cubePreparePaintScreen);
+}
+
+static void
+cubePaintScreen (CompScreen *s,
+ CompOutput *outputs,
+ int numOutputs,
+ unsigned int mask)
+{
+ float x, progress;
+
+ CUBE_SCREEN (s);
+
+ (*cs->getRotation) (s, &x, &x, &progress);
+
+ UNWRAP (cs, s, paintScreen);
+ if (cs->moMode == CUBE_MOMODE_ONE && s->nOutputDev &&
+ (progress > 0.0f || cs->desktopOpacity != OPAQUE))
+ (*s->paintScreen) (s, &s->fullscreenOutput, 1, mask);
+ else
+ (*s->paintScreen) (s, outputs, numOutputs, mask);
+ WRAP (cs, s, paintScreen, cubePaintScreen);
+}
+
+static Bool
+cubePaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ Bool status;
+
+ CUBE_SCREEN (s);
+
+ if (cs->grabIndex || cs->desktopOpacity != OPAQUE)
+ {
+ mask &= ~PAINT_SCREEN_REGION_MASK;
+ mask |= PAINT_SCREEN_TRANSFORMED_MASK;
+ }
+
+ cs->srcOutput = (output->id != ~0) ? output->id : 0;
+ /* Always use BTF painting on non-transformed screen */
+ cs->paintOrder = BTF;
+
+ UNWRAP (cs, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+ WRAP (cs, s, paintOutput, cubePaintOutput);
+
+ return status;
+}
+
+static void
+cubeDonePaintScreen (CompScreen *s)
+{
+ CUBE_SCREEN (s);
+
+ if (cs->grabIndex || cs->desktopOpacity != cs->toOpacity)
+ damageScreen (s);
+
+ UNWRAP (cs, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (cs, s, donePaintScreen, cubeDonePaintScreen);
+}
+
+static Bool
+cubeCheckOrientation (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ CompOutput *outputPtr,
+ CompVector *points)
+{
+ CompTransform sTransform = *transform;
+ CompTransform mvp, pm;
+ CompVector pntA, pntB, pntC;
+ CompVector vecA, vecB, ortho;
+ Bool rv = FALSE;
+
+ CUBE_SCREEN (s);
+
+ (*s->applyScreenTransform) (s, sAttrib, outputPtr, &sTransform);
+ matrixTranslate (&sTransform, cs->outputXOffset, -cs->outputYOffset, 0.0f);
+ matrixScale (&sTransform, cs->outputXScale, cs->outputYScale, 1.0f);
+
+ memcpy (pm.m, s->projection, sizeof (pm.m));
+ matrixMultiply (&mvp, &pm, &sTransform);
+
+ matrixMultiplyVector (&pntA, &points[0], &mvp);
+
+ if (pntA.w < 0.0f)
+ rv = !rv;
+
+ matrixVectorDiv (&pntA);
+
+ matrixMultiplyVector (&pntB, &points[1], &mvp);
+
+ if (pntB.w < 0.0f)
+ rv = !rv;
+
+ matrixVectorDiv (&pntB);
+ matrixMultiplyVector (&pntC, &points[2], &mvp);
+ matrixVectorDiv (&pntC);
+
+ vecA.x = pntC.x - pntA.x;
+ vecA.y = pntC.y - pntA.y;
+ vecA.z = pntC.z - pntA.z;
+
+ vecB.x = pntC.x - pntB.x;
+ vecB.y = pntC.y - pntB.y;
+ vecB.z = pntC.z - pntB.z;
+
+ ortho.x = vecA.y * vecB.z - vecA.z * vecB.y;
+ ortho.y = vecA.z * vecB.x - vecA.x * vecB.z;
+ ortho.z = vecA.x * vecB.y - vecA.y * vecB.x;
+
+ if (ortho.z > 0.0f)
+ rv = !rv;
+
+ return rv;
+}
+
+static Bool
+cubeShouldPaintViewport (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ CompOutput *outputPtr,
+ PaintOrder order)
+{
+ Bool ftb;
+ float pointZ;
+
+ CUBE_SCREEN (s);
+
+ pointZ = cs->invert * cs->distance;
+ CompVector vPoints[3] = { {.v = { -0.5, 0.0, pointZ, 1.0 } },
+ {.v = { 0.0, 0.5, pointZ, 1.0 } },
+ {.v = { 0.0, 0.0, pointZ, 1.0 } } };
+
+ ftb = (*cs->checkOrientation) (s, sAttrib, transform, outputPtr, vPoints);
+
+ return (order == FTB && ftb) || (order == BTF && !ftb);
+}
+
+static void
+cubeMoveViewportAndPaint (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ CompOutput *outputPtr,
+ unsigned int mask,
+ PaintOrder paintOrder,
+ int dx)
+{
+ int output;
+
+ CUBE_SCREEN (s);
+
+ if (!(*cs->shouldPaintViewport) (s,
+ sAttrib,
+ transform,
+ outputPtr,
+ paintOrder))
+ return;
+
+ output = (outputPtr->id != ~0) ? outputPtr->id : 0;
+
+ cs->paintOrder = paintOrder;
+
+ if (cs->nOutput > 1)
+ {
+ int cubeOutput, dView;
+
+ /* translate to cube output */
+ cubeOutput = cs->outputMask[output];
+
+ /* convert from window movement to viewport movement */
+ dView = -dx;
+
+ cubeOutput += dView;
+
+ dView = cubeOutput / cs->nOutput;
+ cubeOutput = cubeOutput % cs->nOutput;
+
+ if (cubeOutput < 0)
+ {
+ cubeOutput += cs->nOutput;
+ dView--;
+ }
+
+ /* translate back to compiz output */
+ output = cs->srcOutput = cs->output[cubeOutput];
+
+ setWindowPaintOffset (s, -dView * s->width, 0);
+ (*cs->paintViewport) (s, sAttrib, transform,
+ &s->outputDev[output].region,
+ &s->outputDev[output], mask);
+ setWindowPaintOffset (s, 0, 0);
+ }
+ else
+ {
+ Region region;
+
+ setWindowPaintOffset (s, dx * s->width, 0);
+
+ if (cs->moMode == CUBE_MOMODE_MULTI)
+ region = &outputPtr->region;
+ else
+ region = &s->region;
+
+ (*cs->paintViewport) (s, sAttrib, transform, region, outputPtr, mask);
+
+ setWindowPaintOffset (s, 0, 0);
+ }
+}
+
+static void
+cubePaintAllViewports (CompScreen *s,
+ ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *outputPtr,
+ unsigned int mask,
+ int xMove,
+ float size,
+ int hsize,
+ PaintOrder paintOrder)
+{
+ ScreenPaintAttrib sa = *sAttrib;
+
+ int i;
+ int xMoveAdd;
+ int origXMoveAdd = 0; /* dx for the viewport we start
+ painting with (back-most). */
+ int iFirstSign; /* 1 if we do xMove += i first and
+ -1 if we do xMove -= i first. */
+
+ CUBE_SCREEN (s);
+
+ if (cs->invert == 1)
+ {
+ /* xMove ==> dx for the viewport which is the
+ nearest to the viewer in z axis.
+ xMove +/- hsize / 2 ==> dx for the viewport
+ which is the farthest to the viewer in z axis. */
+
+ if ((sa.xRotate < 0.0f && hsize % 2 == 1) ||
+ (sa.xRotate > 0.0f && hsize % 2 == 0))
+ {
+ origXMoveAdd = hsize / 2;
+ iFirstSign = 1;
+ }
+ else
+ {
+ origXMoveAdd = -hsize / 2;
+ iFirstSign = -1;
+ }
+ }
+ else
+ {
+ /* xMove is already the dx for farthest viewport. */
+ if (sa.xRotate > 0.0f)
+ iFirstSign = -1;
+ else
+ iFirstSign = 1;
+ }
+
+ for (i = 0; i <= hsize / 2; i++)
+ {
+ /* move to the correct viewport (back to front). */
+ xMoveAdd = origXMoveAdd; /* move to farthest viewport. */
+ xMoveAdd += iFirstSign * i; /* move i more viewports to
+ the right / left. */
+
+ /* Needed especially for unfold.
+ We paint the viewports around xMove viewport.
+ Adding or subtracting hsize from xMove has no effect on
+ what viewport we paint, but can make shorter paths. */
+ if (xMoveAdd < -hsize / 2)
+ xMoveAdd += hsize;
+ else if (xMoveAdd > hsize / 2)
+ xMoveAdd -= hsize;
+
+ /* Paint the viewport. */
+ xMove += xMoveAdd;
+
+ sa.yRotate -= cs->invert * xMoveAdd * 360.0f / size;
+ cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
+ paintOrder, xMove);
+ sa.yRotate += cs->invert * xMoveAdd * 360.0f / size;
+
+ xMove -= xMoveAdd;
+
+ /* do the same for an equally far viewport. */
+ if (i == 0 || i * 2 == hsize)
+ continue;
+
+ xMoveAdd = origXMoveAdd; /* move to farthest viewport. */
+ xMoveAdd -= iFirstSign * i; /* move i more viewports to the
+ left / right (opposite side
+ from the one chosen first) */
+
+ if (xMoveAdd < -hsize / 2)
+ xMoveAdd += hsize;
+ else if (xMoveAdd > hsize / 2)
+ xMoveAdd -= hsize;
+
+ xMove += xMoveAdd;
+
+ sa.yRotate -= cs->invert * xMoveAdd * 360.0f / size;
+ cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
+ paintOrder, xMove);
+ sa.yRotate += cs->invert * xMoveAdd * 360.0f / size;
+
+ xMove -= xMoveAdd;
+ }
+}
+
+static void
+cubeGetRotation (CompScreen *s,
+ float *x,
+ float *v,
+ float *progress)
+{
+ *x = 0.0f;
+ *v = 0.0f;
+ *progress = 0.0f;
+}
+
+static void
+cubeClearTargetOutput (CompScreen *s,
+ float xRotate,
+ float vRotate)
+{
+ CUBE_SCREEN (s);
+
+ if (cs->sky.name)
+ {
+ screenLighting (s, FALSE);
+
+ glPushMatrix ();
+
+ if (cs->opt[CUBE_SCREEN_OPTION_SKYDOME_ANIM].value.b &&
+ cs->grabIndex == 0)
+ {
+ glRotatef (vRotate / 5.0f + 90.0f, 1.0f, 0.0f, 0.0f);
+ glRotatef (xRotate, 0.0f, 0.0f, -1.0f);
+ }
+ else
+ {
+ glRotatef (90.0f, 1.0f, 0.0f, 0.0f);
+ }
+
+ glCallList (cs->skyListId);
+ glPopMatrix ();
+ }
+ else
+ {
+ clearTargetOutput (s->display, GL_COLOR_BUFFER_BIT);
+ }
+}
+
+static void
+cubePaintTop (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ CompOutput *output,
+ int size)
+{
+ ScreenPaintAttrib sa = *sAttrib;
+ CompTransform sTransform = *transform;
+
+ CUBE_SCREEN (s);
+
+ screenLighting (s, TRUE);
+
+ glColor4us (cs->color[0], cs->color[1], cs->color[2], cs->desktopOpacity);
+
+ glPushMatrix ();
+
+ sa.yRotate += (360.0f / size) * (cs->xRotations + 1);
+ if (!cs->opt[CUBE_SCREEN_OPTION_ADJUST_IMAGE].value.b)
+ sa.yRotate -= (360.0f / size) * s->x;
+
+ (*s->applyScreenTransform) (s, &sa, output, &sTransform);
+
+ glLoadMatrixf (sTransform.m);
+ glTranslatef (cs->outputXOffset, -cs->outputYOffset, 0.0f);
+ glScalef (cs->outputXScale, cs->outputYScale, 1.0f);
+
+ if (cs->desktopOpacity != OPAQUE)
+ {
+ screenTexEnvMode (s, GL_MODULATE);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ glVertexPointer (3, GL_FLOAT, 0, cs->vertices);
+
+ if (cs->invert == 1 && size == 4 && cs->texture.name)
+ {
+ enableTexture (s, &cs->texture, COMP_TEXTURE_FILTER_GOOD);
+ glTexCoordPointer (2, GL_FLOAT, 0, cs->tc);
+ glDrawArrays (GL_TRIANGLE_FAN, 0, cs->nVertices >> 1);
+ disableTexture (s, &cs->texture);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ }
+ else
+ {
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays (GL_TRIANGLE_FAN, 0, cs->nVertices >> 1);
+ }
+
+ glPopMatrix ();
+
+ glColor4usv (defaultColor);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ screenTexEnvMode (s, GL_REPLACE);
+ glDisable (GL_BLEND);
+ glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+static void
+cubePaintBottom (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ CompOutput *output,
+ int size)
+{
+ ScreenPaintAttrib sa = *sAttrib;
+ CompTransform sTransform = *transform;
+
+ CUBE_SCREEN (s);
+
+ screenLighting (s, TRUE);
+
+ glColor4us (cs->color[0], cs->color[1], cs->color[2], cs->desktopOpacity);
+
+ glPushMatrix ();
+
+ sa.yRotate += (360.0f / size) * (cs->xRotations + 1);
+ if (!cs->opt[CUBE_SCREEN_OPTION_ADJUST_IMAGE].value.b)
+ sa.yRotate -= (360.0f / size) * s->x;
+
+ (*s->applyScreenTransform) (s, &sa, output, &sTransform);
+
+ glLoadMatrixf (sTransform.m);
+ glTranslatef (cs->outputXOffset, -cs->outputYOffset, 0.0f);
+ glScalef (cs->outputXScale, cs->outputYScale, 1.0f);
+
+ if (cs->desktopOpacity != OPAQUE)
+ {
+ screenTexEnvMode (s, GL_MODULATE);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ glVertexPointer (3, GL_FLOAT, 0, cs->vertices);
+
+ glDrawArrays (GL_TRIANGLE_FAN, cs->nVertices >> 1,
+ cs->nVertices >> 1);
+
+ glPopMatrix ();
+
+ glColor4usv (defaultColor);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ screenTexEnvMode (s, GL_REPLACE);
+ glDisable (GL_BLEND);
+ glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+static void
+cubePaintInside (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ CompOutput *output,
+ int size)
+{
+}
+
+static void
+cubeEnableOutputClipping (CompScreen *s,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output)
+{
+ CUBE_SCREEN (s);
+
+ if (cs->rotationState != RotationNone)
+ {
+ glPushMatrix ();
+ glLoadMatrixf (transform->m);
+ glTranslatef (cs->outputXOffset, -cs->outputYOffset, 0.0f);
+ glScalef (cs->outputXScale, cs->outputYScale, 1.0f);
+
+ if (cs->invert == 1)
+ {
+ GLdouble clipPlane0[] = { 1.0, 0.0, 0.5 / cs->distance, 0.0 };
+ GLdouble clipPlane1[] = { -1.0, 0.0, 0.5 / cs->distance, 0.0 };
+ GLdouble clipPlane2[] = { 0.0, -1.0, 0.5 / cs->distance, 0.0 };
+ GLdouble clipPlane3[] = { 0.0, 1.0, 0.5 / cs->distance, 0.0 };
+ glClipPlane (GL_CLIP_PLANE0, clipPlane0);
+ glClipPlane (GL_CLIP_PLANE1, clipPlane1);
+ glClipPlane (GL_CLIP_PLANE2, clipPlane2);
+ glClipPlane (GL_CLIP_PLANE3, clipPlane3);
+ }
+ else
+ {
+ GLdouble clipPlane0[] = { -1.0, 0.0, -0.5 / cs->distance, 0.0 };
+ GLdouble clipPlane1[] = { 1.0, 0.0, -0.5 / cs->distance, 0.0 };
+ GLdouble clipPlane2[] = { 0.0, 1.0, -0.5 / cs->distance, 0.0 };
+ GLdouble clipPlane3[] = { 0.0, -1.0, -0.5 / cs->distance, 0.0 };
+ glClipPlane (GL_CLIP_PLANE0, clipPlane0);
+ glClipPlane (GL_CLIP_PLANE1, clipPlane1);
+ glClipPlane (GL_CLIP_PLANE2, clipPlane2);
+ glClipPlane (GL_CLIP_PLANE3, clipPlane3);
+ }
+
+ glEnable (GL_CLIP_PLANE0);
+ glEnable (GL_CLIP_PLANE1);
+ glEnable (GL_CLIP_PLANE2);
+ glEnable (GL_CLIP_PLANE3);
+
+ glPopMatrix ();
+ }
+ else
+ {
+ UNWRAP (cs, s, enableOutputClipping);
+ (*s->enableOutputClipping) (s, transform, region, output);
+ WRAP (cs, s, enableOutputClipping, cubeEnableOutputClipping);
+ }
+}
+
+static void
+cubePaintViewport (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ (*s->paintTransformedOutput) (s, sAttrib, transform, region, output, mask);
+}
+
+static void
+cubePaintTransformedOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *outputPtr,
+ unsigned int mask)
+{
+ ScreenPaintAttrib sa = *sAttrib;
+ float xRotate, vRotate, progress;
+ int hsize;
+ float size;
+ GLenum filter = s->display->textureFilter;
+ PaintOrder paintOrder;
+ Bool wasCulled = FALSE;
+ Bool paintCaps;
+ int cullNorm, cullInv;
+ int output = 0;
+
+ CUBE_SCREEN (s);
+
+ output = (outputPtr->id != ~0) ? outputPtr->id : 0;
+
+ if (((outputPtr->id != ~0) && cs->recalcOutput) ||
+ ((outputPtr->id == ~0) && !cs->recalcOutput && cs->nOutput > 1))
+ {
+ cs->recalcOutput = (outputPtr->id == ~0);
+ cs->nOutput = 1;
+ cubeUpdateGeometry (s, s->hsize, cs->invert);
+ }
+
+ hsize = s->hsize * cs->nOutput;
+ size = hsize;
+
+ glGetIntegerv (GL_CULL_FACE_MODE, &cullNorm);
+ cullInv = (cullNorm == GL_BACK)? GL_FRONT : GL_BACK;
+ wasCulled = glIsEnabled (GL_CULL_FACE);
+
+ if (!cs->fullscreenOutput)
+ {
+ cs->outputXScale = (float) s->width / outputPtr->width;
+ cs->outputYScale = (float) s->height / outputPtr->height;
+
+ cs->outputXOffset =
+ (s->width / 2.0f -
+ (outputPtr->region.extents.x1 +
+ outputPtr->region.extents.x2) / 2.0f) /
+ (float) outputPtr->width;
+
+ cs->outputYOffset =
+ (s->height / 2.0f -
+ (outputPtr->region.extents.y1 +
+ outputPtr->region.extents.y2) / 2.0f) /
+ (float) outputPtr->height;
+ }
+ else
+ {
+ cs->outputXScale = 1.0f;
+ cs->outputYScale = 1.0f;
+ cs->outputXOffset = 0.0f;
+ cs->outputYOffset = 0.0f;
+ }
+
+ (*cs->getRotation) (s, &xRotate, &vRotate, &progress);
+
+ sa.xRotate += xRotate;
+ sa.vRotate += vRotate;
+
+ if (!cs->cleared[output])
+ {
+ float rRotate;
+
+ rRotate = xRotate - ((s->x *360.0f) / s->hsize);
+
+ (*cs->clearTargetOutput) (s, rRotate, vRotate);
+ cs->cleared[output] = TRUE;
+ }
+
+ mask &= ~PAINT_SCREEN_CLEAR_MASK;
+
+ UNWRAP (cs, s, paintTransformedOutput);
+
+ if (cs->grabIndex)
+ {
+ sa.vRotate = 0.0f;
+
+ size += cs->unfold * 8.0f;
+ size += powf (cs->unfold, 6) * 64.0;
+ size += powf (cs->unfold, 16) * 8192.0;
+
+ sa.zTranslate = -cs->invert * (0.5f / tanf (M_PI / size));
+
+ /* distance we move the camera back when unfolding the cube.
+ currently hardcoded to 1.5 but it should probably be optional. */
+ sa.zCamera -= cs->unfold * 1.5f;
+ }
+ else
+ {
+ if (vRotate > 100.0f)
+ sa.vRotate = 100.0f;
+ else if (vRotate < -100.0f)
+ sa.vRotate = -100.0f;
+ else
+ sa.vRotate = vRotate;
+
+ sa.zTranslate = -cs->invert * cs->distance;
+ }
+
+ if (sa.xRotate > 0.0f)
+ cs->xRotations = (int) (hsize * sa.xRotate + 180.0f) / 360.0f;
+ else
+ cs->xRotations = (int) (hsize * sa.xRotate - 180.0f) / 360.0f;
+
+ sa.xRotate -= (360.0f * cs->xRotations) / hsize;
+ sa.xRotate *= cs->invert;
+
+ sa.xRotate = sa.xRotate / size * hsize;
+
+ if (cs->grabIndex && cs->opt[CUBE_SCREEN_OPTION_MIPMAP].value.b)
+ s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
+
+ if (cs->invert == 1)
+ {
+ /* Outside cube - start with FTB faces */
+ paintOrder = FTB;
+ glCullFace (cullInv);
+ }
+ else
+ {
+ /* Inside cube - start with BTF faces */
+ paintOrder = BTF;
+ }
+
+ if (cs->invert == -1 || cs->paintAllViewports)
+ cubePaintAllViewports (s, &sa, transform, region, outputPtr,
+ mask, cs->xRotations, size, hsize, paintOrder);
+
+ glCullFace (cullNorm);
+
+ if (wasCulled && cs->paintAllViewports)
+ glDisable (GL_CULL_FACE);
+
+ paintCaps = !cs->grabIndex && (hsize > 2) && !cs->capsPainted[output] &&
+ (cs->invert != 1 || cs->desktopOpacity != OPAQUE ||
+ cs->paintAllViewports || sa.vRotate != 0.0f ||
+ sa.yTranslate != 0.0f);
+
+ if (paintCaps)
+ {
+ Bool topDir, bottomDir, allCaps;
+
+ static CompVector top[3] = { { .v = { 0.5, 0.5, 0.0, 1.0} },
+ { .v = { 0.0, 0.5, -0.5, 1.0} },
+ { .v = { 0.0, 0.5, 0.0, 1.0} } };
+ static CompVector bottom[3] = { { .v = { 0.5, -0.5, 0.0, 1.0} },
+ { .v = { 0.0, -0.5, -0.5, 1.0} },
+ { .v = { 0.0, -0.5, 0.0, 1.0} } };
+
+ topDir = (*cs->checkOrientation) (s, &sa, transform, outputPtr, top);
+ bottomDir = (*cs->checkOrientation) (s, &sa, transform,
+ outputPtr, bottom);
+
+ cs->capsPainted[output] = TRUE;
+
+ allCaps = cs->paintAllViewports || cs->invert != 1;
+
+ if (topDir && bottomDir)
+ {
+ glNormal3f (0.0f, -1.0f, 0.0f);
+ if (allCaps)
+ {
+ (*cs->paintBottom) (s, &sa, transform, outputPtr, hsize);
+ glNormal3f (0.0f, 0.0f, -1.0f);
+ (*cs->paintInside) (s, &sa, transform, outputPtr, hsize);
+ glNormal3f (0.0f, -1.0f, 0.0f);
+ }
+ (*cs->paintTop) (s, &sa, transform, outputPtr, hsize);
+ }
+ else if (!topDir && !bottomDir)
+ {
+ glNormal3f (0.0f, 1.0f, 0.0f);
+ if (allCaps)
+ {
+ (*cs->paintTop) (s, &sa, transform, outputPtr, hsize);
+ glNormal3f (0.0f, 0.0f, -1.0f);
+ (*cs->paintInside) (s, &sa, transform, outputPtr, hsize);
+ glNormal3f (0.0f, 1.0f, 0.0f);
+ }
+ (*cs->paintBottom) (s, &sa, transform, outputPtr, hsize);
+ }
+ else if (allCaps)
+ {
+ glNormal3f (0.0f, 1.0f, 0.0f);
+ (*cs->paintTop) (s, &sa, transform, outputPtr, hsize);
+ glNormal3f (0.0f, -1.0f, 0.0f);
+ (*cs->paintBottom) (s, &sa, transform, outputPtr, hsize);
+ glNormal3f (0.0f, 0.0f, -1.0f);
+ (*cs->paintInside) (s, &sa, transform, outputPtr, hsize);
+ }
+ glNormal3f (0.0f, 0.0f, -1.0f);
+ }
+
+ if (wasCulled)
+ glEnable (GL_CULL_FACE);
+
+ if (cs->invert == 1)
+ {
+ /* Outside cube - continue with BTF faces */
+ paintOrder = BTF;
+ }
+ else
+ {
+ /* Inside cube - continue with FTB faces */
+ paintOrder = FTB;
+ glCullFace (cullInv);
+ }
+
+ if (cs->invert == 1 || cs->paintAllViewports)
+ cubePaintAllViewports (s, &sa, transform, region,
+ outputPtr, mask, cs->xRotations,
+ size, hsize, paintOrder);
+
+ glCullFace (cullNorm);
+
+ s->display->textureFilter = filter;
+
+ WRAP (cs, s, paintTransformedOutput, cubePaintTransformedOutput);
+}
+
+static Bool
+cubePaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+ CompScreen *s = w->screen;
+ CUBE_SCREEN (s);
+
+ if ((w->type & CompWindowTypeDesktopMask) &&
+ (attrib->opacity != cs->desktopOpacity))
+ {
+ WindowPaintAttrib wAttrib = *attrib;
+
+ wAttrib.opacity = cs->desktopOpacity;
+
+ UNWRAP (cs, s, paintWindow);
+ status = (*s->paintWindow) (w, &wAttrib, transform, region, mask);
+ WRAP (cs, s, paintWindow, cubePaintWindow);
+ }
+ else
+ {
+ UNWRAP (cs, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (cs, s, paintWindow, cubePaintWindow);
+ }
+
+ return status;
+}
+
+static void
+cubeInitWindowWalker (CompScreen *s, CompWalker* walker)
+{
+ CUBE_SCREEN (s);
+
+ UNWRAP (cs, s, initWindowWalker);
+ (*s->initWindowWalker) (s, walker);
+ WRAP (cs, s, initWindowWalker, cubeInitWindowWalker);
+
+ if (cs->paintOrder == FTB)
+ {
+ WalkInitProc tmpInit = walker->first;
+ WalkStepProc tmpStep = walker->next;
+
+ walker->first = walker->last;
+ walker->last = tmpInit;
+
+ walker->next = walker->prev;
+ walker->prev = tmpStep;
+ }
+}
+
+static void
+cubeApplyScreenTransform (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ CompOutput *output,
+ CompTransform *transform)
+{
+ CUBE_SCREEN (s);
+
+ matrixTranslate (transform, cs->outputXOffset, -cs->outputYOffset, 0.0f);
+ matrixScale (transform, cs->outputXScale, cs->outputYScale, 1.0f);
+
+ UNWRAP (cs, s, applyScreenTransform);
+ (*s->applyScreenTransform) (s, sAttrib, output, transform);
+ WRAP (cs, s, applyScreenTransform, cubeApplyScreenTransform);
+
+ matrixScale (transform, 1.0f / cs->outputXScale,
+ 1.0f / cs->outputYScale, 1.0f);
+ matrixTranslate (transform, -cs->outputXOffset, cs->outputYOffset, 0.0f);
+}
+
+static Bool
+cubeUnfold (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ CUBE_SCREEN (s);
+
+ if (s->hsize * cs->nOutput < 4)
+ return FALSE;
+
+ if (otherScreenGrabExist (s, "rotate", "switcher", "cube", 0))
+ return FALSE;
+
+ if (!cs->grabIndex)
+ cs->grabIndex = pushScreenGrab (s, s->invisibleCursor, "cube");
+
+ if (cs->grabIndex)
+ {
+ cs->unfolded = TRUE;
+ damageScreen (s);
+ }
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ if (state & CompActionStateInitKey)
+ action->state |= CompActionStateTermKey;
+ }
+
+ return FALSE;
+}
+
+static Bool
+cubeFold (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ CUBE_SCREEN (s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (cs->grabIndex)
+ {
+ cs->unfolded = FALSE;
+ damageScreen (s);
+ }
+ }
+
+ action->state &= ~(CompActionStateTermButton | CompActionStateTermKey);
+
+ return FALSE;
+}
+
+static Bool
+cubeNextImage (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ int imgNFile;
+
+ CUBE_SCREEN (s);
+
+ imgNFile = cs->opt[CUBE_SCREEN_OPTION_IMAGES].value.list.nValue;
+ if (imgNFile)
+ {
+ cubeLoadImg (s, (cs->imgCurFile + 1) % imgNFile);
+ damageScreen (s);
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+cubePrevImage (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ int imgNFile;
+
+ CUBE_SCREEN (s);
+
+ imgNFile = cs->opt[CUBE_SCREEN_OPTION_IMAGES].value.list.nValue;
+ if (imgNFile)
+ {
+ cubeLoadImg (s, (cs->imgCurFile - 1 + imgNFile) % imgNFile);
+ damageScreen (s);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+cubeOutputChangeNotify (CompScreen *s)
+{
+ CUBE_SCREEN (s);
+
+ cubeUpdateOutputs (s);
+ cubeUpdateGeometry (s, s->hsize, cs->invert);
+
+ if (cs->opt[CUBE_SCREEN_OPTION_IMAGES].value.list.nValue)
+ cubeLoadImg (s, cs->imgCurFile);
+
+ UNWRAP (cs, s, outputChangeNotify);
+ (*s->outputChangeNotify) (s);
+ WRAP (cs, s, outputChangeNotify, cubeOutputChangeNotify);
+}
+
+static Bool
+cubeSetOptionForPlugin (CompObject *o,
+ const char *plugin,
+ const char *name,
+ CompOptionValue *value)
+{
+ Bool status;
+
+ CUBE_CORE (&core);
+
+ UNWRAP (cc, &core, setOptionForPlugin);
+ status = (*core.setOptionForPlugin) (o, plugin, name, value);
+ WRAP (cc, &core, setOptionForPlugin, cubeSetOptionForPlugin);
+
+ if (status && o->type == COMP_OBJECT_TYPE_SCREEN)
+ {
+ if (strcmp (plugin, "core") == 0 && strcmp (name, "hsize") == 0)
+ {
+ CompScreen *s = (CompScreen *) o;
+
+ CUBE_SCREEN (s);
+
+ cubeUpdateGeometry (s, s->hsize, cs->invert);
+ }
+ }
+
+ return status;
+}
+
+static CompOption *
+cubeGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ CUBE_DISPLAY (display);
+
+ *count = NUM_OPTIONS (cd);
+ return cd->opt;
+}
+
+static Bool
+cubeSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ CUBE_DISPLAY (display);
+
+ o = compFindOption (cd->opt, NUM_OPTIONS (cd), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case CUBE_DISPLAY_OPTION_ABI:
+ case CUBE_DISPLAY_OPTION_INDEX:
+ break;
+ default:
+ return compSetDisplayOption (display, o, value);
+ }
+
+ return FALSE;
+}
+
+static Bool
+cubeInitCore (CompPlugin *p,
+ CompCore *c)
+{
+ CubeCore *cc;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ cc = malloc (sizeof (CubeCore));
+ if (!cc)
+ return FALSE;
+
+ cubeDisplayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (cubeDisplayPrivateIndex < 0)
+ {
+ free (cc);
+ return FALSE;
+ }
+
+ WRAP (cc, &core, setOptionForPlugin, cubeSetOptionForPlugin);
+
+ c->base.privates[cubeCorePrivateIndex].ptr = cc;
+
+ return TRUE;
+}
+
+static void
+cubeFiniCore (CompPlugin *p,
+ CompCore *c)
+{
+ CUBE_CORE (c);
+
+ UNWRAP (cc, &core, setOptionForPlugin);
+
+ freeDisplayPrivateIndex (cubeDisplayPrivateIndex);
+
+ free (cc);
+}
+
+static const CompMetadataOptionInfo cubeDisplayOptionInfo[] = {
+ { "abi", "int", 0, 0, 0 },
+ { "index", "int", 0, 0, 0 },
+ { "unfold_key", "key", 0, cubeUnfold, cubeFold },
+ { "next_slide_key", "key", "<passive_grab>false</passive_grab>",
+ cubeNextImage, 0 },
+ { "prev_slide_key", "key", "<passive_grab>false</passive_grab>",
+ cubePrevImage, 0 }
+};
+
+static Bool
+cubeInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ CubeDisplay *cd;
+
+ cd = malloc (sizeof (CubeDisplay));
+ if (!cd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &cubeMetadata,
+ cubeDisplayOptionInfo,
+ cd->opt,
+ CUBE_DISPLAY_OPTION_NUM))
+ {
+ free (cd);
+ return FALSE;
+ }
+
+ cd->opt[CUBE_DISPLAY_OPTION_ABI].value.i = CUBE_ABIVERSION;
+ cd->opt[CUBE_DISPLAY_OPTION_INDEX].value.i = cubeDisplayPrivateIndex;
+
+ cd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (cd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, cd->opt, CUBE_DISPLAY_OPTION_NUM);
+ free (cd);
+ return FALSE;
+ }
+
+ d->base.privates[cubeDisplayPrivateIndex].ptr = cd;
+
+ return TRUE;
+}
+
+static void
+cubeFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ CUBE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, cd->screenPrivateIndex);
+
+ compFiniDisplayOptions (d, cd->opt, CUBE_DISPLAY_OPTION_NUM);
+
+ free (cd);
+}
+
+static const CompMetadataOptionInfo cubeScreenOptionInfo[] = {
+ { "color", "color", 0, 0, 0 },
+ { "in", "bool", 0, 0, 0 },
+ { "scale_image", "bool", 0, 0, 0 },
+ { "images", "list", "<type>string</type>", 0, 0 },
+ { "skydome", "bool", 0, 0, 0 },
+ { "skydome_image", "string", 0, 0, 0 },
+ { "skydome_animated", "bool", 0, 0, 0 },
+ { "skydome_gradient_start_color", "color", 0, 0, 0 },
+ { "skydome_gradient_end_color", "color", 0, 0, 0 },
+ { "acceleration", "float", "<min>1.0</min>", 0, 0 },
+ { "speed", "float", "<min>0.1</min>", 0, 0 },
+ { "timestep", "float", "<min>0.1</min>", 0, 0 },
+ { "mipmap", "bool", 0, 0, 0 },
+ { "adjust_image", "bool", 0, 0, 0 },
+ { "active_opacity", "float", "<min>0.0</min><max>100.0</max>", 0, 0 },
+ { "inactive_opacity", "float", "<min>0.0</min><max>100.0</max>", 0, 0 },
+ { "transparent_manual_only", "bool", 0, 0, 0 },
+ { "multioutput_mode", "int", "<min>0</min><max>2</max>", 0, 0 }
+};
+
+static Bool
+cubeInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ CubeScreen *cs;
+
+ CUBE_DISPLAY (s->display);
+
+ cs = malloc (sizeof (CubeScreen));
+ if (!cs)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &cubeMetadata,
+ cubeScreenOptionInfo,
+ cs->opt,
+ CUBE_SCREEN_OPTION_NUM))
+ {
+ free (cs);
+ return FALSE;
+ }
+
+ cs->pw = 0;
+ cs->ph = 0;
+
+ cs->invert = 1;
+
+ cs->tc[0] = cs->tc[1] = cs->tc[2] = cs->tc[3] = 0.0f;
+ cs->tc[4] = cs->tc[5] = cs->tc[6] = cs->tc[7] = 0.0f;
+
+ memcpy (cs->color, cs->opt[CUBE_SCREEN_OPTION_COLOR].value.c,
+ sizeof (cs->color));
+
+ cs->nVertices = 0;
+ cs->vertices = NULL;
+
+ cs->grabIndex = 0;
+
+ cs->srcOutput = 0;
+
+ cs->skyListId = 0;
+
+ cs->getRotation = cubeGetRotation;
+ cs->clearTargetOutput = cubeClearTargetOutput;
+ cs->paintTop = cubePaintTop;
+ cs->paintBottom = cubePaintBottom;
+ cs->paintInside = cubePaintInside;
+ cs->checkOrientation = cubeCheckOrientation;
+ cs->paintViewport = cubePaintViewport;
+ cs->shouldPaintViewport = cubeShouldPaintViewport;
+
+ s->base.privates[cd->screenPrivateIndex].ptr = cs;
+
+ initTexture (s, &cs->texture);
+ initTexture (s, &cs->sky);
+
+ cs->imgCurFile = 0;
+
+ cs->unfolded = FALSE;
+ cs->unfold = 0.0f;
+
+ cs->unfoldVelocity = 0.0f;
+
+ cs->paintAllViewports = FALSE;
+ cs->fullscreenOutput = TRUE;
+
+ cs->outputXScale = 1.0f;
+ cs->outputYScale = 1.0f;
+ cs->outputXOffset = 0.0f;
+ cs->outputYOffset = 0.0f;
+
+ cs->rotationState = RotationNone;
+
+ cs->desktopOpacity = OPAQUE;
+
+ cs->lastOpacityIndex = CUBE_SCREEN_OPTION_INACTIVE_OPACITY;
+
+ cs->moMode = cs->opt[CUBE_SCREEN_OPTION_MULTIOUTPUT_MODE].value.i;
+
+ cs->recalcOutput = FALSE;
+
+ memset (cs->cleared, 0, sizeof (cs->cleared));
+
+ cubeUpdateOutputs (s);
+
+ if (!cubeUpdateGeometry (s, s->hsize, cs->invert))
+ {
+ compFiniScreenOptions (s, cs->opt, CUBE_SCREEN_OPTION_NUM);
+ free (cs);
+ return FALSE;
+ }
+
+ if (cs->opt[CUBE_SCREEN_OPTION_IMAGES].value.list.nValue)
+ {
+ cubeLoadImg (s, cs->imgCurFile);
+ damageScreen (s);
+ }
+
+ WRAP (cs, s, preparePaintScreen, cubePreparePaintScreen);
+ WRAP (cs, s, donePaintScreen, cubeDonePaintScreen);
+ WRAP (cs, s, paintScreen, cubePaintScreen);
+ WRAP (cs, s, paintOutput, cubePaintOutput);
+ WRAP (cs, s, paintTransformedOutput, cubePaintTransformedOutput);
+ WRAP (cs, s, enableOutputClipping, cubeEnableOutputClipping);
+ WRAP (cs, s, paintWindow, cubePaintWindow);
+ WRAP (cs, s, applyScreenTransform, cubeApplyScreenTransform);
+ WRAP (cs, s, outputChangeNotify, cubeOutputChangeNotify);
+ WRAP (cs, s, initWindowWalker, cubeInitWindowWalker);
+
+ return TRUE;
+}
+
+static void
+cubeFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ CUBE_SCREEN (s);
+
+ if (cs->vertices)
+ free (cs->vertices);
+
+ if (cs->skyListId)
+ glDeleteLists (cs->skyListId, 1);
+
+ UNWRAP (cs, s, preparePaintScreen);
+ UNWRAP (cs, s, donePaintScreen);
+ UNWRAP (cs, s, paintScreen);
+ UNWRAP (cs, s, paintOutput);
+ UNWRAP (cs, s, paintTransformedOutput);
+ UNWRAP (cs, s, enableOutputClipping);
+ UNWRAP (cs, s, paintWindow);
+ UNWRAP (cs, s, applyScreenTransform);
+ UNWRAP (cs, s, outputChangeNotify);
+ UNWRAP (cs, s, initWindowWalker);
+
+ finiTexture (s, &cs->texture);
+ finiTexture (s, &cs->sky);
+
+ compFiniScreenOptions (s, cs->opt, CUBE_SCREEN_OPTION_NUM);
+
+ free (cs);
+}
+
+static CompBool
+cubeInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) cubeInitCore,
+ (InitPluginObjectProc) cubeInitDisplay,
+ (InitPluginObjectProc) cubeInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+cubeFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) cubeFiniCore,
+ (FiniPluginObjectProc) cubeFiniDisplay,
+ (FiniPluginObjectProc) cubeFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+cubeGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) cubeGetDisplayOptions,
+ (GetPluginObjectOptionsProc) cubeGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+cubeSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) cubeSetDisplayOption,
+ (SetPluginObjectOptionProc) cubeSetScreenOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+cubeInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&cubeMetadata,
+ p->vTable->name,
+ cubeDisplayOptionInfo,
+ CUBE_DISPLAY_OPTION_NUM,
+ cubeScreenOptionInfo,
+ CUBE_SCREEN_OPTION_NUM))
+ return FALSE;
+
+ cubeCorePrivateIndex = allocateCorePrivateIndex ();
+ if (cubeCorePrivateIndex < 0)
+ {
+ compFiniMetadata (&cubeMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&cubeMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+cubeFini (CompPlugin *p)
+{
+ freeCorePrivateIndex (cubeCorePrivateIndex);
+ compFiniMetadata (&cubeMetadata);
+}
+
+static CompMetadata *
+cubeGetMetadata (CompPlugin *plugin)
+{
+ return &cubeMetadata;
+}
+
+CompPluginVTable cubeVTable = {
+ "cube",
+ cubeGetMetadata,
+ cubeInit,
+ cubeFini,
+ cubeInitObject,
+ cubeFiniObject,
+ cubeGetObjectOptions,
+ cubeSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &cubeVTable;
+}
diff --git a/plugins/dbus.c b/plugins/dbus.c
new file mode 100644
index 0000000..62ace06
--- /dev/null
+++ b/plugins/dbus.c
@@ -0,0 +1,2596 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <poll.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <libxml/xmlwriter.h>
+
+#include <compiz-core.h>
+
+static CompMetadata dbusMetadata;
+
+#define COMPIZ_DBUS_SERVICE_NAME "org.freedesktop.compiz"
+#define COMPIZ_DBUS_INTERFACE "org.freedesktop.compiz"
+#define COMPIZ_DBUS_ROOT_PATH "/org/freedesktop/compiz"
+
+#define COMPIZ_DBUS_ACTIVATE_MEMBER_NAME "activate"
+#define COMPIZ_DBUS_DEACTIVATE_MEMBER_NAME "deactivate"
+#define COMPIZ_DBUS_SET_MEMBER_NAME "set"
+#define COMPIZ_DBUS_GET_MEMBER_NAME "get"
+#define COMPIZ_DBUS_GET_METADATA_MEMBER_NAME "getMetadata"
+#define COMPIZ_DBUS_LIST_MEMBER_NAME "list"
+#define COMPIZ_DBUS_GET_PLUGINS_MEMBER_NAME "getPlugins"
+#define COMPIZ_DBUS_GET_PLUGIN_METADATA_MEMBER_NAME "getPluginMetadata"
+
+#define COMPIZ_DBUS_CHANGED_SIGNAL_NAME "changed"
+#define COMPIZ_DBUS_PLUGINS_CHANGED_SIGNAL_NAME "pluginsChanged"
+
+#define DBUS_FILE_WATCH_CURRENT 0
+#define DBUS_FILE_WATCH_PLUGIN 1
+#define DBUS_FILE_WATCH_HOME 2
+#define DBUS_FILE_WATCH_NUM 3
+
+static int corePrivateIndex;
+static int displayPrivateIndex;
+
+typedef struct _DbusCore {
+ DBusConnection *connection;
+ CompWatchFdHandle watchFdHandle;
+
+ CompFileWatchHandle fileWatch[DBUS_FILE_WATCH_NUM];
+
+ InitPluginForObjectProc initPluginForObject;
+ SetOptionForPluginProc setOptionForPlugin;
+} DbusCore;
+
+typedef struct _DbusDisplay {
+ char **pluginList;
+ unsigned int nPlugins;
+} DbusDisplay;
+
+static DBusHandlerResult dbusHandleMessage (DBusConnection *,
+ DBusMessage *,
+ void *);
+
+static DBusObjectPathVTable dbusMessagesVTable = {
+ NULL, dbusHandleMessage, /* handler function */
+ NULL, NULL, NULL, NULL
+};
+
+#define GET_DBUS_CORE(c) \
+ ((DbusCore *) (c)->base.privates[corePrivateIndex].ptr)
+
+#define DBUS_CORE(c) \
+ DbusCore *dc = GET_DBUS_CORE (c)
+
+#define GET_DBUS_DISPLAY(d) \
+ ((DbusDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define DBUS_DISPLAY(d) \
+ DbusDisplay *dd = GET_DBUS_DISPLAY (d)
+
+static void
+dbusUpdatePluginList (CompDisplay *d)
+{
+ CompListValue *pl;
+ unsigned int i;
+
+ DBUS_DISPLAY (d);
+
+ pl = &d->opt[COMP_DISPLAY_OPTION_ACTIVE_PLUGINS].value.list;
+
+ for (i = 0; i < dd->nPlugins; i++)
+ free (dd->pluginList[i]);
+
+ dd->pluginList = realloc (dd->pluginList, pl->nValue * sizeof (char *));
+ if (!dd->pluginList)
+ {
+ dd->nPlugins = 0;
+ return;
+ }
+
+ for (i = 0; i < pl->nValue; i++)
+ dd->pluginList[i] = strdup (pl->value[i].s);
+
+ dd->nPlugins = pl->nValue;
+}
+
+static CompOption *
+dbusGetOptionsFromPath (char **path,
+ CompObject **returnObject,
+ CompMetadata **returnMetadata,
+ int *nOption)
+{
+ CompPlugin *p;
+ CompObject *object;
+
+ object = compObjectFind (&core.base, COMP_OBJECT_TYPE_DISPLAY, NULL);
+ if (!object)
+ return NULL;
+
+ if (strncmp (path[1], "screen", 6) == 0)
+ {
+ object = compObjectFind (object, COMP_OBJECT_TYPE_SCREEN,
+ path[1] + 6);
+ if (!object)
+ return NULL;
+ }
+ else if (strcmp (path[1], "allscreens") != 0)
+ {
+ return NULL;
+ }
+
+ if (returnObject)
+ *returnObject = object;
+
+ for (p = getPlugins (); p; p = p->next)
+ if (strcmp (p->vTable->name, path[0]) == 0)
+ break;
+
+ if (returnMetadata)
+ {
+ if (p && p->vTable->getMetadata)
+ *returnMetadata = (*p->vTable->getMetadata) (p);
+ else
+ *returnMetadata = NULL;
+ }
+
+ if (!p)
+ return NULL;
+
+ if (!p->vTable->getObjectOptions)
+ return NULL;
+
+ return (*p->vTable->getObjectOptions) (p, object, nOption);
+}
+
+/* functions to create introspection XML */
+static void
+dbusIntrospectStartInterface (xmlTextWriterPtr writer)
+{
+ xmlTextWriterStartElement (writer, BAD_CAST "interface");
+ xmlTextWriterWriteAttribute (writer, BAD_CAST "name",
+ BAD_CAST COMPIZ_DBUS_SERVICE_NAME);
+}
+
+static void
+dbusIntrospectEndInterface (xmlTextWriterPtr writer)
+{
+ xmlTextWriterEndElement (writer);
+}
+
+static void
+dbusIntrospectAddArgument (xmlTextWriterPtr writer,
+ char *type,
+ char *direction)
+{
+ xmlTextWriterStartElement (writer, BAD_CAST "arg");
+ xmlTextWriterWriteAttribute (writer, BAD_CAST "type", BAD_CAST type);
+ xmlTextWriterWriteAttribute (writer, BAD_CAST "direction",
+ BAD_CAST direction);
+ xmlTextWriterEndElement (writer);
+}
+
+static void
+dbusIntrospectAddMethod (xmlTextWriterPtr writer,
+ char *name,
+ int nArgs,
+ ...)
+{
+ va_list var_args;
+ char *type, *direction;
+
+ xmlTextWriterStartElement (writer, BAD_CAST "method");
+ xmlTextWriterWriteAttribute (writer, BAD_CAST "name", BAD_CAST name);
+
+ va_start (var_args, nArgs);
+ while (nArgs)
+ {
+ type = va_arg (var_args, char *);
+ direction = va_arg (var_args, char *);
+ dbusIntrospectAddArgument (writer, type, direction);
+ nArgs--;
+ }
+ va_end (var_args);
+
+ xmlTextWriterEndElement (writer);
+}
+
+static void
+dbusIntrospectAddSignal (xmlTextWriterPtr writer,
+ char *name,
+ int nArgs,
+ ...)
+{
+ va_list var_args;
+ char *type;
+
+ xmlTextWriterStartElement (writer, BAD_CAST "signal");
+ xmlTextWriterWriteAttribute (writer, BAD_CAST "name", BAD_CAST name);
+
+ va_start (var_args, nArgs);
+ while (nArgs)
+ {
+ type = va_arg (var_args, char *);
+ dbusIntrospectAddArgument (writer, type, "out");
+ nArgs--;
+ }
+ va_end (var_args);
+
+ xmlTextWriterEndElement (writer);
+}
+
+static void
+dbusIntrospectAddNode (xmlTextWriterPtr writer,
+ char *name)
+{
+ xmlTextWriterStartElement (writer, BAD_CAST "node");
+ xmlTextWriterWriteAttribute (writer, BAD_CAST "name", BAD_CAST name);
+ xmlTextWriterEndElement (writer);
+}
+
+static void
+dbusIntrospectStartRoot (xmlTextWriterPtr writer)
+{
+ xmlTextWriterStartElement (writer, BAD_CAST "node");
+
+ xmlTextWriterStartElement (writer, BAD_CAST "interface");
+ xmlTextWriterWriteAttribute (writer, BAD_CAST "name",
+ BAD_CAST "org.freedesktop.DBus.Introspectable");
+
+ dbusIntrospectAddMethod (writer, "Introspect", 1, "s", "out");
+
+ xmlTextWriterEndElement (writer);
+}
+
+static void
+dbusIntrospectEndRoot (xmlTextWriterPtr writer)
+{
+ xmlTextWriterEndDocument (writer);
+}
+
+/* introspection handlers */
+static Bool
+dbusHandleRootIntrospectMessage (DBusConnection *connection,
+ DBusMessage *message)
+{
+ char **plugins, **pluginName;
+ int nPlugins;
+
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+
+ buf = xmlBufferCreate ();
+ writer = xmlNewTextWriterMemory (buf, 0);
+
+ dbusIntrospectStartRoot (writer);
+ dbusIntrospectStartInterface (writer);
+
+ dbusIntrospectAddMethod (writer, COMPIZ_DBUS_GET_PLUGINS_MEMBER_NAME, 1,
+ "as", "out");
+ dbusIntrospectAddMethod (writer,
+ COMPIZ_DBUS_GET_PLUGIN_METADATA_MEMBER_NAME, 7,
+ "s", "in", "s", "out", "s", "out", "s", "out",
+ "b", "out", "as", "out", "as", "out");
+ dbusIntrospectAddSignal (writer,
+ COMPIZ_DBUS_PLUGINS_CHANGED_SIGNAL_NAME, 0);
+
+ dbusIntrospectEndInterface (writer);
+
+ plugins = availablePlugins (&nPlugins);
+ if (plugins)
+ {
+ pluginName = plugins;
+
+ while (nPlugins--)
+ {
+ dbusIntrospectAddNode (writer, *pluginName);
+ free (*pluginName);
+ pluginName++;
+ }
+
+ free (plugins);
+ }
+ else
+ {
+ xmlFreeTextWriter (writer);
+ xmlBufferFree (buf);
+ return FALSE;
+ }
+
+ dbusIntrospectEndRoot (writer);
+
+ xmlFreeTextWriter (writer);
+
+ DBusMessage *reply = dbus_message_new_method_return (message);
+ if (!reply)
+ {
+ xmlBufferFree (buf);
+ return FALSE;
+ }
+
+ DBusMessageIter args;
+ dbus_message_iter_init_append (reply, &args);
+
+ if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING,
+ &buf->content))
+ {
+ xmlBufferFree (buf);
+ return FALSE;
+ }
+
+ xmlBufferFree (buf);
+
+ if (!dbus_connection_send (connection, reply, NULL))
+ {
+ return FALSE;
+ }
+
+ dbus_connection_flush (connection);
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+/* MULTIDPYERROR: only works with one or less displays present */
+static Bool
+dbusHandlePluginIntrospectMessage (DBusConnection *connection,
+ DBusMessage *message,
+ char **path)
+{
+ CompDisplay *d;
+ CompScreen *s;
+ char screenName[256];
+
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+
+ buf = xmlBufferCreate ();
+ writer = xmlNewTextWriterMemory (buf, 0);
+
+ dbusIntrospectStartRoot (writer);
+
+ for (d = core.displays; d; d = d->next)
+ {
+ dbusIntrospectAddNode (writer, "allscreens");
+
+ for (s = d->screens; s; s = s->next)
+ {
+ sprintf (screenName, "screen%d", s->screenNum);
+ dbusIntrospectAddNode (writer, screenName);
+ }
+ }
+
+ dbusIntrospectEndRoot (writer);
+
+ xmlFreeTextWriter (writer);
+
+ DBusMessage *reply = dbus_message_new_method_return (message);
+ if (!reply)
+ {
+ xmlBufferFree (buf);
+ return FALSE;
+ }
+
+ DBusMessageIter args;
+ dbus_message_iter_init_append (reply, &args);
+
+ if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING,
+ &buf->content))
+ {
+ xmlBufferFree (buf);
+ return FALSE;
+ }
+
+ xmlBufferFree (buf);
+
+ if (!dbus_connection_send (connection, reply, NULL))
+ {
+ return FALSE;
+ }
+
+ dbus_connection_flush (connection);
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+static Bool
+dbusHandleScreenIntrospectMessage (DBusConnection *connection,
+ DBusMessage *message,
+ char **path)
+{
+ CompOption *option = NULL;
+ int nOptions;
+
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+
+ buf = xmlBufferCreate ();
+ writer = xmlNewTextWriterMemory (buf, 0);
+
+ dbusIntrospectStartRoot (writer);
+ dbusIntrospectStartInterface (writer);
+
+ dbusIntrospectAddMethod (writer, COMPIZ_DBUS_LIST_MEMBER_NAME, 1,
+ "as", "out");
+
+ dbusIntrospectEndInterface (writer);
+
+ option = dbusGetOptionsFromPath (path, NULL, NULL, &nOptions);
+ if (option)
+ {
+ while (nOptions--)
+ {
+ dbusIntrospectAddNode (writer, option->name);
+ option++;
+ }
+ }
+
+ dbusIntrospectEndRoot (writer);
+
+ xmlFreeTextWriter (writer);
+
+ DBusMessage *reply = dbus_message_new_method_return (message);
+ if (!reply)
+ {
+ xmlBufferFree (buf);
+ return FALSE;
+ }
+
+ DBusMessageIter args;
+ dbus_message_iter_init_append (reply, &args);
+
+ if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING,
+ &buf->content))
+ {
+ xmlBufferFree (buf);
+ return FALSE;
+ }
+
+ xmlBufferFree (buf);
+
+ if (!dbus_connection_send (connection, reply, NULL))
+ {
+ return FALSE;
+ }
+
+ dbus_connection_flush (connection);
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+static Bool
+dbusHandleOptionIntrospectMessage (DBusConnection *connection,
+ DBusMessage *message,
+ char **path)
+{
+ CompOption *option;
+ int nOptions;
+ CompOptionType restrictionType;
+ Bool metadataHandled;
+ char type[3];
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+ Bool isList = FALSE;
+
+ buf = xmlBufferCreate ();
+ writer = xmlNewTextWriterMemory (buf, 0);
+
+ dbusIntrospectStartRoot (writer);
+ dbusIntrospectStartInterface (writer);
+
+ option = dbusGetOptionsFromPath (path, NULL, NULL, &nOptions);
+ if (!option)
+ {
+ xmlFreeTextWriter (writer);
+ xmlBufferFree (buf);
+ return FALSE;
+ }
+
+ while (nOptions--)
+ {
+ if (strcmp (option->name, path[2]) == 0)
+ {
+ restrictionType = option->type;
+ if (restrictionType == CompOptionTypeList)
+ {
+ restrictionType = option->value.list.type;
+ isList = TRUE;
+ }
+
+ metadataHandled = FALSE;
+ switch (restrictionType)
+ {
+ case CompOptionTypeInt:
+ if (isList)
+ strcpy (type, "ai");
+ else
+ strcpy (type, "i");
+
+ dbusIntrospectAddMethod (writer,
+ COMPIZ_DBUS_GET_METADATA_MEMBER_NAME,
+ 6, "s", "out", "s", "out",
+ "b", "out", "s", "out",
+ "i", "out", "i", "out");
+ metadataHandled = TRUE;
+ break;
+ case CompOptionTypeFloat:
+ if (isList)
+ strcpy (type, "ad");
+ else
+ strcpy (type, "d");
+
+ dbusIntrospectAddMethod (writer,
+ COMPIZ_DBUS_GET_METADATA_MEMBER_NAME,
+ 7, "s", "out", "s", "out",
+ "b", "out", "s", "out",
+ "d", "out", "d", "out",
+ "d", "out");
+ metadataHandled = TRUE;
+ break;
+ case CompOptionTypeString:
+ if (isList)
+ strcpy (type, "as");
+ else
+ strcpy (type, "s");
+
+ dbusIntrospectAddMethod (writer,
+ COMPIZ_DBUS_GET_METADATA_MEMBER_NAME,
+ 5, "s", "out", "s", "out",
+ "b", "out", "s", "out",
+ "as", "out");
+ metadataHandled = TRUE;
+ break;
+ case CompOptionTypeBool:
+ case CompOptionTypeBell:
+ if (isList)
+ strcpy (type, "ab");
+ else
+ strcpy (type, "b");
+
+ break;
+ case CompOptionTypeColor:
+ case CompOptionTypeKey:
+ case CompOptionTypeButton:
+ case CompOptionTypeEdge:
+ case CompOptionTypeMatch:
+ if (isList)
+ strcpy (type, "as");
+ else
+ strcpy (type, "s");
+ break;
+ default:
+ continue;
+ }
+
+ dbusIntrospectAddMethod (writer,
+ COMPIZ_DBUS_GET_MEMBER_NAME, 1,
+ type, "out");
+ dbusIntrospectAddMethod (writer,
+ COMPIZ_DBUS_SET_MEMBER_NAME, 1,
+ type, "in");
+ dbusIntrospectAddSignal (writer,
+ COMPIZ_DBUS_CHANGED_SIGNAL_NAME, 1,
+ type, "out");
+
+ if (!metadataHandled)
+ dbusIntrospectAddMethod (writer,
+ COMPIZ_DBUS_GET_METADATA_MEMBER_NAME,
+ 4, "s", "out", "s", "out",
+ "b", "out", "s", "out");
+ break;
+ }
+
+ option++;
+ }
+
+ dbusIntrospectEndInterface (writer);
+ dbusIntrospectEndRoot (writer);
+
+ xmlFreeTextWriter (writer);
+
+ DBusMessage *reply = dbus_message_new_method_return (message);
+ if (!reply)
+ {
+ xmlBufferFree (buf);
+ return FALSE;
+ }
+
+ DBusMessageIter args;
+ dbus_message_iter_init_append (reply, &args);
+
+ if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING,
+ &buf->content))
+ {
+ xmlBufferFree (buf);
+ return FALSE;
+ }
+
+ xmlBufferFree (buf);
+
+ if (!dbus_connection_send (connection, reply, NULL))
+ {
+ return FALSE;
+ }
+
+ dbus_connection_flush (connection);
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+
+/*
+ * Activate can be used to trigger any existing action. Arguments
+ * should be a pair of { string, bool|int32|double|string }.
+ *
+ * Example (rotate to face 1):
+ *
+ * dbus-send --type=method_call --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz/rotate/allscreens/rotate_to \
+ * org.freedesktop.compiz.activate \
+ * string:'root' \
+ * int32:`xwininfo -root | grep id: | awk '{ print $4 }'` \
+ * string:'face' int32:1
+ *
+ *
+ * You can also call the terminate function
+ *
+ * Example unfold and refold cube:
+ * dbus-send --type=method_call --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz/cube/allscreens/unfold \
+ * org.freedesktop.compiz.activate \
+ * string:'root' \
+ * int32:`xwininfo -root | grep id: | awk '{ print $4 }'`
+ *
+ * dbus-send --type=method_call --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz/cube/allscreens/unfold \
+ * org.freedesktop.compiz.deactivate \
+ * string:'root' \
+ * int32:`xwininfo -root | grep id: | awk '{ print $4 }'`
+ *
+ */
+static Bool
+dbusHandleActionMessage (DBusConnection *connection,
+ DBusMessage *message,
+ char **path,
+ Bool activate)
+{
+ CompObject *object;
+ CompOption *option;
+ int nOption;
+
+ option = dbusGetOptionsFromPath (path, &object, NULL, &nOption);
+ if (!option)
+ return FALSE;
+
+ while (nOption--)
+ {
+ if (strcmp (option->name, path[2]) == 0)
+ {
+ CompOption *argument = NULL;
+ int i, nArgument = 0;
+ DBusMessageIter iter;
+
+ if (!isActionOption (option))
+ return FALSE;
+
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return FALSE;
+
+ if (activate)
+ {
+ if (!option->value.action.initiate)
+ return FALSE;
+ }
+ else
+ {
+ if (!option->value.action.terminate)
+ return FALSE;
+ }
+
+ if (dbus_message_iter_init (message, &iter))
+ {
+ CompOptionValue value;
+ CompOptionType type = 0;
+ char *name;
+ Bool hasValue;
+
+ do
+ {
+ name = NULL;
+ hasValue = FALSE;
+
+ while (!name)
+ {
+ switch (dbus_message_iter_get_arg_type (&iter)) {
+ case DBUS_TYPE_STRING:
+ dbus_message_iter_get_basic (&iter, &name);
+ default:
+ break;
+ }
+
+ if (!dbus_message_iter_next (&iter))
+ break;
+ }
+
+ while (!hasValue)
+ {
+ double tmp;
+
+ switch (dbus_message_iter_get_arg_type (&iter)) {
+ case DBUS_TYPE_BOOLEAN:
+ hasValue = TRUE;
+ type = CompOptionTypeBool;
+
+ dbus_message_iter_get_basic (&iter, &value.b);
+ break;
+ case DBUS_TYPE_INT32:
+ hasValue = TRUE;
+ type = CompOptionTypeInt;
+
+ dbus_message_iter_get_basic (&iter, &value.i);
+ break;
+ case DBUS_TYPE_DOUBLE:
+ hasValue = TRUE;
+ type = CompOptionTypeFloat;
+
+ dbus_message_iter_get_basic (&iter, &tmp);
+
+ value.f = tmp;
+ break;
+ case DBUS_TYPE_STRING:
+ hasValue = TRUE;
+
+ /* XXX: use match option type if name is "match" */
+ if (name && strcmp (name, "match") == 0)
+ {
+ char *s;
+
+ type = CompOptionTypeMatch;
+
+ dbus_message_iter_get_basic (&iter, &s);
+
+ matchInit (&value.match);
+ matchAddFromString (&value.match, s);
+ }
+ else
+ {
+ type = CompOptionTypeString;
+
+ dbus_message_iter_get_basic (&iter, &value.s);
+ }
+ default:
+ break;
+ }
+
+ if (!dbus_message_iter_next (&iter))
+ break;
+ }
+
+ if (name && hasValue)
+ {
+ CompOption *a;
+
+ a = realloc (argument,
+ sizeof (CompOption) * (nArgument + 1));
+ if (a)
+ {
+ argument = a;
+
+ argument[nArgument].name = name;
+ argument[nArgument].type = type;
+ argument[nArgument].value = value;
+
+ nArgument++;
+ }
+ }
+ } while (dbus_message_iter_has_next (&iter));
+ }
+
+ if (activate)
+ {
+ (*option->value.action.initiate) (GET_CORE_DISPLAY (object),
+ &option->value.action,
+ 0,
+ argument, nArgument);
+ }
+ else
+ {
+ (*option->value.action.terminate) (GET_CORE_DISPLAY (object),
+ &option->value.action,
+ 0,
+ argument, nArgument);
+ }
+
+ for (i = 0; i < nArgument; i++)
+ if (argument[i].type == CompOptionTypeMatch)
+ matchFini (&argument[i].value.match);
+
+ if (argument)
+ free (argument);
+
+ if (!dbus_message_get_no_reply (message))
+ {
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_connection_send (connection, reply, NULL);
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (reply);
+ }
+
+ return TRUE;
+ }
+
+ option++;
+ }
+
+ return FALSE;
+}
+
+static Bool
+dbusTryGetValueWithType (DBusMessageIter *iter,
+ int type,
+ void *value)
+{
+ if (dbus_message_iter_get_arg_type (iter) == type)
+ {
+ dbus_message_iter_get_basic (iter, value);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static Bool
+dbusGetOptionValue (CompObject *object,
+ DBusMessageIter *iter,
+ CompOptionType type,
+ CompOptionValue *value)
+{
+ double d;
+ char *s;
+
+ switch (type) {
+ case CompOptionTypeBool:
+ return dbusTryGetValueWithType (iter,
+ DBUS_TYPE_BOOLEAN,
+ &value->b);
+ break;
+ case CompOptionTypeInt:
+ return dbusTryGetValueWithType (iter,
+ DBUS_TYPE_INT32,
+ &value->i);
+ break;
+ case CompOptionTypeFloat:
+ if (dbusTryGetValueWithType (iter,
+ DBUS_TYPE_DOUBLE,
+ &d))
+ {
+ value->f = d;
+ return TRUE;
+ }
+ break;
+ case CompOptionTypeString:
+ return dbusTryGetValueWithType (iter,
+ DBUS_TYPE_STRING,
+ &value->s);
+ break;
+ case CompOptionTypeColor:
+ if (dbusTryGetValueWithType (iter,
+ DBUS_TYPE_STRING,
+ &s))
+ {
+ if (stringToColor (s, value->c))
+ return TRUE;
+ }
+ break;
+ case CompOptionTypeKey:
+ if (dbusTryGetValueWithType (iter,
+ DBUS_TYPE_STRING,
+ &s))
+ {
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return FALSE;
+
+ stringToKeyAction (GET_CORE_DISPLAY (object), s, &value->action);
+ return TRUE;
+ }
+ break;
+ case CompOptionTypeButton:
+ if (dbusTryGetValueWithType (iter,
+ DBUS_TYPE_STRING,
+ &s))
+ {
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return FALSE;
+
+ stringToButtonAction (GET_CORE_DISPLAY (object),
+ s, &value->action);
+ return TRUE;
+ }
+ break;
+ case CompOptionTypeEdge:
+ if (dbusTryGetValueWithType (iter,
+ DBUS_TYPE_STRING,
+ &s))
+ {
+ value->action.edgeMask = stringToEdgeMask (s);
+ return TRUE;
+ }
+ break;
+ case CompOptionTypeBell:
+ return dbusTryGetValueWithType (iter,
+ DBUS_TYPE_BOOLEAN,
+ &value->action.bell);
+ break;
+ case CompOptionTypeMatch:
+ if (dbusTryGetValueWithType (iter,
+ DBUS_TYPE_STRING,
+ &s))
+ {
+ matchAddFromString (&value->match, s);
+ return TRUE;
+ }
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+/*
+ * 'Set' can be used to change any existing option. Argument
+ * should be the new value for the option.
+ *
+ * Example (will set command0 option to firefox):
+ *
+ * dbus-send --type=method_call --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz/core/allscreens/command0 \
+ * org.freedesktop.compiz.set \
+ * string:'firefox'
+ *
+ * List and action options can be changed using more than one
+ * argument.
+ *
+ * Example (will set active_plugins option to
+ * [dbus,decoration,place]):
+ *
+ * dbus-send --type=method_call --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz/core/allscreens/active_plugins \
+ * org.freedesktop.compiz.set \
+ * array:string:'dbus','decoration','place'
+ *
+ * Example (will set run_command0 option to trigger on key
+ * binding <Control><Alt>Return and not trigger on any button
+ * bindings, screen edges or bell notifications):
+ *
+ * dbus-send --type=method_call --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz/core/allscreens/run_command0 \
+ * org.freedesktop.compiz.set \
+ * string:'<Control><Alt>Return' \
+ * string:'Disabled' \
+ * boolean:'false' \
+ * string:'' \
+ * int32:'0'
+ */
+static Bool
+dbusHandleSetOptionMessage (DBusConnection *connection,
+ DBusMessage *message,
+ char **path)
+{
+ CompObject *object;
+ CompOption *option;
+ int nOption;
+
+ option = dbusGetOptionsFromPath (path, &object, NULL, &nOption);
+ if (!option)
+ return FALSE;
+
+ while (nOption--)
+ {
+ if (strcmp (option->name, path[2]) == 0)
+ {
+ DBusMessageIter iter, aiter;
+ CompOptionValue value, tmpValue;
+ Bool status = FALSE;
+
+ memset (&value, 0, sizeof (value));
+
+ if (option->type == CompOptionTypeList)
+ {
+ if (dbus_message_iter_init (message, &iter) &&
+ dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY)
+ {
+ dbus_message_iter_recurse (&iter, &aiter);
+
+ do
+ {
+ memset (&tmpValue, 0, sizeof (tmpValue));
+
+ if (dbusGetOptionValue (object,
+ &aiter,
+ option->value.list.type,
+ &tmpValue))
+ {
+ CompOptionValue *v;
+
+ v = realloc (value.list.value,
+ sizeof (CompOptionValue) *
+ (value.list.nValue + 1));
+ if (v)
+ {
+ v[value.list.nValue++] = tmpValue;
+ value.list.value = v;
+ }
+ }
+ } while (dbus_message_iter_next (&aiter));
+
+ status = TRUE;
+ }
+ }
+ else if (dbus_message_iter_init (message, &iter))
+ {
+ status = dbusGetOptionValue (object, &iter, option->type,
+ &value);
+ }
+
+ if (status)
+ {
+ (*core.setOptionForPlugin) (object,
+ path[0],
+ option->name,
+ &value);
+
+ if (!dbus_message_get_no_reply (message))
+ {
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_connection_send (connection, reply, NULL);
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (reply);
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ option++;
+ }
+
+ return FALSE;
+}
+
+static void
+dbusAppendSimpleOptionValue (CompObject *object,
+ DBusMessage *message,
+ CompOptionType type,
+ CompOptionValue *value)
+{
+ double d;
+ char *s;
+
+ switch (type) {
+ case CompOptionTypeBool:
+ dbus_message_append_args (message,
+ DBUS_TYPE_BOOLEAN, &value->b,
+ DBUS_TYPE_INVALID);
+ break;
+ case CompOptionTypeInt:
+ dbus_message_append_args (message,
+ DBUS_TYPE_INT32, &value->i,
+ DBUS_TYPE_INVALID);
+ break;
+ case CompOptionTypeFloat:
+ d = value->f;
+
+ dbus_message_append_args (message,
+ DBUS_TYPE_DOUBLE, &d,
+ DBUS_TYPE_INVALID);
+ break;
+ case CompOptionTypeString:
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &value->s,
+ DBUS_TYPE_INVALID);
+ break;
+ case CompOptionTypeColor:
+ s = colorToString (value->c);
+ if (s)
+ {
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &s,
+ DBUS_TYPE_INVALID);
+ free (s);
+ }
+ break;
+ case CompOptionTypeKey:
+ s = keyActionToString ((CompDisplay *) object, &value->action);
+ if (s)
+ {
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &s,
+ DBUS_TYPE_INVALID);
+ free (s);
+ }
+ break;
+ case CompOptionTypeButton:
+ s = buttonActionToString ((CompDisplay *) object, &value->action);
+ if (s)
+ {
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &s,
+ DBUS_TYPE_INVALID);
+ free (s);
+ }
+ break;
+ case CompOptionTypeEdge:
+ s = edgeMaskToString (value->action.edgeMask);
+ if (s)
+ {
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &s,
+ DBUS_TYPE_INVALID);
+ free (s);
+ }
+ break;
+ case CompOptionTypeBell:
+ dbus_message_append_args (message,
+ DBUS_TYPE_BOOLEAN, &value->action.bell,
+ DBUS_TYPE_INVALID);
+ break;
+ case CompOptionTypeMatch:
+ s = matchToString (&value->match);
+ if (s)
+ {
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &s,
+ DBUS_TYPE_INVALID);
+ free (s);
+ }
+ default:
+ break;
+ }
+}
+
+static void
+dbusAppendListOptionValue (CompObject *object,
+ DBusMessage *message,
+ CompOptionType type,
+ CompOptionValue *value)
+{
+ DBusMessageIter iter;
+ DBusMessageIter listIter;
+ char sig[2];
+ char *s;
+ int i;
+
+ switch (value->list.type) {
+ case CompOptionTypeInt:
+ sig[0] = DBUS_TYPE_INT32;
+ break;
+ case CompOptionTypeFloat:
+ sig[0] = DBUS_TYPE_DOUBLE;
+ break;
+ case CompOptionTypeBool:
+ case CompOptionTypeBell:
+ sig[0] = DBUS_TYPE_BOOLEAN;
+ break;
+ default:
+ sig[0] = DBUS_TYPE_STRING;
+ break;
+ }
+ sig[1] = '\0';
+
+ dbus_message_iter_init_append (message, &iter);
+
+ if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
+ sig, &listIter))
+ return;
+
+ for (i = 0; i < value->list.nValue; i++)
+ {
+ switch (value->list.type) {
+ case CompOptionTypeInt:
+ dbus_message_iter_append_basic (&listIter,
+ sig[0],
+ &value->list.value[i].i);
+ break;
+ case CompOptionTypeFloat:
+ dbus_message_iter_append_basic (&listIter,
+ sig[0],
+ &value->list.value[i].f);
+ break;
+ case CompOptionTypeBool:
+ dbus_message_iter_append_basic (&listIter,
+ sig[0],
+ &value->list.value[i].b);
+ break;
+ case CompOptionTypeString:
+ dbus_message_iter_append_basic (&listIter,
+ sig[0],
+ &value->list.value[i].s);
+ break;
+ case CompOptionTypeKey:
+ s = keyActionToString ((CompDisplay *) object,
+ &value->list.value[i].action);
+ if (s)
+ {
+ dbus_message_iter_append_basic (&listIter, sig[0], &s);
+ free (s);
+ }
+ break;
+ case CompOptionTypeButton:
+ s = buttonActionToString ((CompDisplay *) object,
+ &value->list.value[i].action);
+ if (s)
+ {
+ dbus_message_iter_append_basic (&listIter, sig[0], &s);
+ free (s);
+ }
+ break;
+ case CompOptionTypeEdge:
+ s = edgeMaskToString (value->list.value[i].action.edgeMask);
+ if (s)
+ {
+ dbus_message_iter_append_basic (&listIter, sig[0], &s);
+ free (s);
+ }
+ break;
+ case CompOptionTypeBell:
+ dbus_message_iter_append_basic (&listIter,
+ sig[0],
+ &value->list.value[i].action.bell);
+ break;
+ case CompOptionTypeMatch:
+ s = matchToString (&value->list.value[i].match);
+ if (s)
+ {
+ dbus_message_iter_append_basic (&listIter, sig[0], &s);
+ free (s);
+ }
+ break;
+ case CompOptionTypeColor:
+ s = colorToString (value->list.value[i].c);
+ if (s)
+ {
+ dbus_message_iter_append_basic (&listIter, sig[0], &s);
+ free (s);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ dbus_message_iter_close_container (&iter, &listIter);
+}
+
+static void
+dbusAppendOptionValue (CompObject *object,
+ DBusMessage *message,
+ CompOptionType type,
+ CompOptionValue *value)
+{
+ if (type == CompOptionTypeList)
+ {
+ dbusAppendListOptionValue (object, message, type, value);
+ }
+ else
+ {
+ dbusAppendSimpleOptionValue (object, message, type, value);
+ }
+}
+
+/*
+ * 'Get' can be used to retrieve the value of any existing option.
+ *
+ * Example (will retrieve the current value of command0 option):
+ *
+ * dbus-send --print-reply --type=method_call \
+ * --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz/core/allscreens/command0 \
+ * org.freedesktop.compiz.get
+ */
+static Bool
+dbusHandleGetOptionMessage (DBusConnection *connection,
+ DBusMessage *message,
+ char **path)
+{
+ CompObject *object;
+ CompOption *option;
+ int nOption = 0;
+ DBusMessage *reply = NULL;
+
+ option = dbusGetOptionsFromPath (path, &object, NULL, &nOption);
+
+ while (nOption--)
+ {
+ if (strcmp (option->name, path[2]) == 0)
+ {
+ reply = dbus_message_new_method_return (message);
+ dbusAppendOptionValue (object, reply, option->type,
+ &option->value);
+ break;
+ }
+
+ option++;
+ }
+
+ if (!reply)
+ reply = dbus_message_new_error (message,
+ DBUS_ERROR_FAILED,
+ "No such option");
+
+ dbus_connection_send (connection, reply, NULL);
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+/*
+ * 'List' can be used to retrieve a list of available options.
+ *
+ * Example:
+ *
+ * dbus-send --print-reply --type=method_call \
+ * --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz/core/allscreens \
+ * org.freedesktop.compiz.list
+ */
+static Bool
+dbusHandleListMessage (DBusConnection *connection,
+ DBusMessage *message,
+ char **path)
+{
+ CompObject *object;
+ CompOption *option;
+ int nOption = 0;
+ DBusMessage *reply;
+
+ option = dbusGetOptionsFromPath (path, &object, NULL, &nOption);
+
+ reply = dbus_message_new_method_return (message);
+
+ while (nOption--)
+ {
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &option->name,
+ DBUS_TYPE_INVALID);
+ option++;
+ }
+
+ dbus_connection_send (connection, reply, NULL);
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+/*
+ * 'GetMetadata' can be used to retrieve metadata for an option.
+ *
+ * Example:
+ *
+ * dbus-send --print-reply --type=method_call \
+ * --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz/core/allscreens/run_command0 \
+ * org.freedesktop.compiz.getMetadata
+ */
+static Bool
+dbusHandleGetMetadataMessage (DBusConnection *connection,
+ DBusMessage *message,
+ char **path)
+{
+ CompObject *object;
+ CompOption *option;
+ int nOption = 0;
+ DBusMessage *reply = NULL;
+ CompMetadata *m;
+
+ option = dbusGetOptionsFromPath (path, &object, &m, &nOption);
+
+ while (nOption--)
+ {
+ if (strcmp (option->name, path[2]) == 0)
+ {
+ CompOptionType restrictionType = option->type;
+ const char *type;
+ char *shortDesc = NULL;
+ char *longDesc = NULL;
+ const char *blankStr = "";
+
+ reply = dbus_message_new_method_return (message);
+
+ type = optionTypeToString (option->type);
+
+ if (m)
+ {
+ if (object->type == COMP_OBJECT_TYPE_SCREEN)
+ {
+ shortDesc = compGetShortScreenOptionDescription (m, option);
+ longDesc = compGetLongScreenOptionDescription (m, option);
+ }
+ else
+ {
+ shortDesc =
+ compGetShortDisplayOptionDescription (m, option);
+ longDesc = compGetLongDisplayOptionDescription (m, option);
+ }
+ }
+
+ if (shortDesc)
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &shortDesc,
+ DBUS_TYPE_INVALID);
+ else
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &blankStr,
+ DBUS_TYPE_INVALID);
+
+ if (longDesc)
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &longDesc,
+ DBUS_TYPE_INVALID);
+ else
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &blankStr,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &type,
+ DBUS_TYPE_INVALID);
+
+ if (shortDesc)
+ free (shortDesc);
+ if (longDesc)
+ free (longDesc);
+
+ if (restrictionType == CompOptionTypeList)
+ {
+ type = optionTypeToString (option->value.list.type);
+ restrictionType = option->value.list.type;
+
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &type,
+ DBUS_TYPE_INVALID);
+ }
+
+ switch (restrictionType) {
+ case CompOptionTypeInt:
+ dbus_message_append_args (reply,
+ DBUS_TYPE_INT32, &option->rest.i.min,
+ DBUS_TYPE_INT32, &option->rest.i.max,
+ DBUS_TYPE_INVALID);
+ break;
+ case CompOptionTypeFloat: {
+ double min, max, precision;
+
+ min = option->rest.f.min;
+ max = option->rest.f.max;
+ precision = option->rest.f.precision;
+
+ dbus_message_append_args (reply,
+ DBUS_TYPE_DOUBLE, &min,
+ DBUS_TYPE_DOUBLE, &max,
+ DBUS_TYPE_DOUBLE, &precision,
+ DBUS_TYPE_INVALID);
+ } break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ option++;
+ }
+
+ if (!reply)
+ reply = dbus_message_new_error (message,
+ DBUS_ERROR_FAILED,
+ "No such option");
+
+ dbus_connection_send (connection, reply, NULL);
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+/*
+ * 'GetPlugins' can be used to retrieve a list of available plugins. There's
+ * no guarantee that a plugin in this list can actually be loaded.
+ *
+ * Example:
+ *
+ * dbus-send --print-reply --type=method_call \
+ * --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz \
+ * org.freedesktop.compiz.getPlugins
+ */
+static Bool
+dbusHandleGetPluginsMessage (DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+ char **plugins, **p;
+ int n;
+
+ reply = dbus_message_new_method_return (message);
+
+ plugins = availablePlugins (&n);
+ if (plugins)
+ {
+ p = plugins;
+
+ while (n--)
+ {
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, p,
+ DBUS_TYPE_INVALID);
+ free (*p);
+
+ p++;
+ }
+
+ free (plugins);
+ }
+
+ dbus_connection_send (connection, reply, NULL);
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+/*
+ * 'GetPluginMetadata' can be used to retrieve metadata for a plugin.
+ *
+ * Example:
+ *
+ * dbus-send --print-reply --type=method_call \
+ * --dest=org.freedesktop.compiz \
+ * /org/freedesktop/compiz \
+ * org.freedesktop.compiz.getPluginMetadata \
+ * string:'png'
+ */
+static Bool
+dbusHandleGetPluginMetadataMessage (DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ char *name;
+ CompPlugin *p, *loadedPlugin = NULL;
+
+ if (!dbus_message_iter_init (message, &iter))
+ return FALSE;
+
+ if (!dbusTryGetValueWithType (&iter,
+ DBUS_TYPE_STRING,
+ &name))
+ return FALSE;
+
+ p = findActivePlugin (name);
+ if (!p)
+ p = loadedPlugin = loadPlugin (name);
+
+ if (p)
+ {
+ Bool initializedPlugin = TRUE;
+ char *shortDesc = NULL;
+ char *longDesc = NULL;
+ const char *blankStr = "";
+
+ reply = dbus_message_new_method_return (message);
+
+ if (loadedPlugin)
+ {
+ if (!(*p->vTable->init) (p))
+ initializedPlugin = FALSE;
+ }
+
+ if (initializedPlugin && p->vTable->getMetadata)
+ {
+ CompMetadata *m;
+
+ m = (*p->vTable->getMetadata) (p);
+ if (m)
+ {
+ shortDesc = compGetShortPluginDescription (m);
+ longDesc = compGetLongPluginDescription (m);
+ }
+ }
+
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &p->vTable->name,
+ DBUS_TYPE_INVALID);
+
+ if (shortDesc)
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &shortDesc,
+ DBUS_TYPE_INVALID);
+ else
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &blankStr,
+ DBUS_TYPE_INVALID);
+
+ if (longDesc)
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &longDesc,
+ DBUS_TYPE_INVALID);
+ else
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &blankStr,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_append_args (reply,
+ DBUS_TYPE_BOOLEAN, &initializedPlugin,
+ DBUS_TYPE_INVALID);
+
+ if (shortDesc)
+ free (shortDesc);
+ if (longDesc)
+ free (longDesc);
+
+ if (loadedPlugin && initializedPlugin)
+ (*p->vTable->fini) (p);
+ }
+ else
+ {
+ char *str;
+
+ str = malloc (strlen (name) + 256);
+ if (!str)
+ return FALSE;
+
+ sprintf (str, "Plugin '%s' could not be loaded", name);
+
+ reply = dbus_message_new_error (message,
+ DBUS_ERROR_FAILED,
+ str);
+
+ free (str);
+ }
+
+ if (loadedPlugin)
+ unloadPlugin (loadedPlugin);
+
+ dbus_connection_send (connection, reply, NULL);
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+static DBusHandlerResult
+dbusHandleMessage (DBusConnection *connection,
+ DBusMessage *message,
+ void *userData)
+{
+ Bool status = FALSE;
+ char **path;
+
+ if (!dbus_message_get_path_decomposed (message, &path))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!path[0] || !path[1] || !path[2])
+ {
+ dbus_free_string_array (path);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ /* root messages */
+ if (!path[3])
+ {
+ if (dbus_message_is_method_call (message,
+ DBUS_INTERFACE_INTROSPECTABLE,
+ "Introspect"))
+ {
+ if (dbusHandleRootIntrospectMessage (connection, message))
+ {
+ dbus_free_string_array (path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ }
+ else if (dbus_message_is_method_call (message, COMPIZ_DBUS_INTERFACE,
+ COMPIZ_DBUS_GET_PLUGIN_METADATA_MEMBER_NAME))
+ {
+ if (dbusHandleGetPluginMetadataMessage (connection, message))
+ {
+ dbus_free_string_array (path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ }
+ else if (dbus_message_is_method_call (message, COMPIZ_DBUS_INTERFACE,
+ COMPIZ_DBUS_GET_PLUGINS_MEMBER_NAME))
+ {
+ if (dbusHandleGetPluginsMessage (connection, message))
+ {
+ dbus_free_string_array (path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ }
+
+ dbus_free_string_array (path);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ /* plugin message */
+ else if (!path[4])
+ {
+ if (dbus_message_is_method_call (message,
+ DBUS_INTERFACE_INTROSPECTABLE,
+ "Introspect"))
+ {
+ if (dbusHandlePluginIntrospectMessage (connection, message,
+ &path[3]))
+ {
+ dbus_free_string_array (path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ }
+
+ dbus_free_string_array (path);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ /* screen message */
+ else if (!path[5])
+ {
+ if (dbus_message_is_method_call (message,
+ DBUS_INTERFACE_INTROSPECTABLE,
+ "Introspect"))
+ {
+ if (dbusHandleScreenIntrospectMessage (connection, message,
+ &path[3]))
+ {
+ dbus_free_string_array (path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ }
+ else if (dbus_message_is_method_call (message, COMPIZ_DBUS_INTERFACE,
+ COMPIZ_DBUS_LIST_MEMBER_NAME))
+ {
+ if (dbusHandleListMessage (connection, message, &path[3]))
+ {
+ dbus_free_string_array (path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ }
+
+ dbus_free_string_array (path);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ /* option message */
+ if (dbus_message_is_method_call (message, DBUS_INTERFACE_INTROSPECTABLE,
+ "Introspect"))
+ {
+ status = dbusHandleOptionIntrospectMessage (connection, message,
+ &path[3]);
+ }
+ else if (dbus_message_is_method_call (message, COMPIZ_DBUS_INTERFACE,
+ COMPIZ_DBUS_ACTIVATE_MEMBER_NAME))
+ {
+ status = dbusHandleActionMessage (connection, message, &path[3], TRUE);
+ }
+ else if (dbus_message_is_method_call (message, COMPIZ_DBUS_INTERFACE,
+ COMPIZ_DBUS_DEACTIVATE_MEMBER_NAME))
+ {
+ status = dbusHandleActionMessage (connection, message, &path[3],
+ FALSE);
+ }
+ else if (dbus_message_is_method_call (message, COMPIZ_DBUS_INTERFACE,
+ COMPIZ_DBUS_SET_MEMBER_NAME))
+ {
+ status = dbusHandleSetOptionMessage (connection, message, &path[3]);
+ }
+ else if (dbus_message_is_method_call (message, COMPIZ_DBUS_INTERFACE,
+ COMPIZ_DBUS_GET_MEMBER_NAME))
+ {
+ status = dbusHandleGetOptionMessage (connection, message, &path[3]);
+ }
+ else if (dbus_message_is_method_call (message, COMPIZ_DBUS_INTERFACE,
+ COMPIZ_DBUS_GET_METADATA_MEMBER_NAME))
+ {
+ status = dbusHandleGetMetadataMessage (connection, message, &path[3]);
+ }
+
+ dbus_free_string_array (path);
+
+ if (status)
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static Bool
+dbusProcessMessages (void *data)
+{
+ DBusDispatchStatus status;
+
+ DBUS_CORE (&core);
+
+ do
+ {
+ dbus_connection_read_write_dispatch (dc->connection, 0);
+ status = dbus_connection_get_dispatch_status (dc->connection);
+ }
+ while (status == DBUS_DISPATCH_DATA_REMAINS);
+
+ return TRUE;
+}
+
+static void
+dbusSendChangeSignalForOption (CompObject *object,
+ CompOption *o,
+ const char *plugin)
+{
+ DBusMessage *signal;
+ char *name, path[256];
+
+ DBUS_CORE (&core);
+
+ if (!o)
+ return;
+
+ name = compObjectName (object);
+ if (name)
+ {
+ sprintf (path, "%s/%s/%s%s/%s", COMPIZ_DBUS_ROOT_PATH,
+ plugin, compObjectTypeName (object->type), name, o->name);
+
+ free (name);
+ }
+ else
+ sprintf (path, "%s/%s/%s/%s", COMPIZ_DBUS_ROOT_PATH,
+ plugin, compObjectTypeName (object->type), o->name);
+
+ signal = dbus_message_new_signal (path,
+ COMPIZ_DBUS_SERVICE_NAME,
+ COMPIZ_DBUS_CHANGED_SIGNAL_NAME);
+
+ dbusAppendOptionValue (object, signal, o->type, &o->value);
+
+ dbus_connection_send (dc->connection, signal, NULL);
+ dbus_connection_flush (dc->connection);
+
+ dbus_message_unref (signal);
+}
+
+static Bool
+dbusGetPathDecomposed (char *data,
+ char ***path,
+ int *count)
+{
+ char **retval;
+ char *temp;
+ char *token;
+ int nComponents;
+ int i;
+
+ nComponents = 0;
+ if (strlen (data) > 1)
+ {
+ i = 0;
+ while (i < strlen (data))
+ {
+ if (data[i] == '/')
+ nComponents += 1;
+ ++i;
+ }
+ }
+
+ retval = malloc (sizeof (char*) * (nComponents + 1));
+
+ if (nComponents == 0)
+ {
+ retval[0] = malloc (sizeof (char));
+ retval[0][0] = '\0';
+ *path = retval;
+ *count = 1;
+
+ return TRUE;
+ }
+
+ temp = strdup (data);
+
+ i = 0;
+ token = strtok (temp, "/");
+ while (token != NULL)
+ {
+ retval[i] = strdup (token);
+ token = strtok (NULL, "/");
+ i++;
+ }
+ retval[i] = malloc (sizeof (char));
+ retval[i][0] = '\0';
+
+ free (temp);
+
+ *path = retval;
+ *count = i + 1;
+
+ return TRUE;
+}
+
+static void
+dbusFreePathDecomposed (char **path,
+ int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ free (path[i]);
+
+ free (path);
+}
+
+/* dbus registration */
+
+static Bool
+dbusRegisterOptions (DBusConnection *connection,
+ char *screenPath)
+{
+ CompOption *option = NULL;
+ int nOptions;
+ char objectPath[256];
+ char **path;
+ int count;
+
+ dbusGetPathDecomposed (screenPath, &path, &count);
+
+ option = dbusGetOptionsFromPath (&path[3], NULL, NULL, &nOptions);
+
+ if (!option) {
+ dbusFreePathDecomposed (path, count);
+ return FALSE;
+ }
+
+ while (nOptions--)
+ {
+ snprintf (objectPath, 256, "%s/%s", screenPath, option->name);
+
+ dbus_connection_register_object_path (connection, objectPath,
+ &dbusMessagesVTable, 0);
+ option++;
+ }
+
+ dbusFreePathDecomposed (path, count);
+
+ return TRUE;
+}
+
+static Bool
+dbusUnregisterOptions (DBusConnection *connection,
+ char *screenPath)
+{
+ CompOption *option = NULL;
+ int nOptions;
+ char objectPath[256];
+ char **path;
+ int count;
+
+ dbusGetPathDecomposed (screenPath, &path, &count);
+
+ option = dbusGetOptionsFromPath (&path[3], NULL, NULL, &nOptions);
+
+ dbusFreePathDecomposed (path, count);
+
+ if (!option)
+ return FALSE;
+
+ while (nOptions--)
+ {
+ snprintf (objectPath, 256, "%s/%s", screenPath, option->name);
+
+ dbus_connection_unregister_object_path (connection, objectPath);
+ option++;
+ }
+
+ return TRUE;
+}
+
+static void
+dbusRegisterPluginForDisplay (DBusConnection *connection,
+ CompDisplay *d,
+ char *pluginName)
+{
+ char objectPath[256];
+
+ /* register plugin root path */
+ snprintf (objectPath, 256, "%s/%s", COMPIZ_DBUS_ROOT_PATH, pluginName);
+ dbus_connection_register_object_path (connection, objectPath,
+ &dbusMessagesVTable, d);
+
+ /* register plugin/screen path */
+ snprintf (objectPath, 256, "%s/%s/%s", COMPIZ_DBUS_ROOT_PATH,
+ pluginName, "allscreens");
+ dbus_connection_register_object_path (connection, objectPath,
+ &dbusMessagesVTable, d);
+}
+
+static void
+dbusRegisterPluginForScreen (DBusConnection *connection,
+ CompScreen *s,
+ char *pluginName)
+{
+ char objectPath[256];
+
+ /* register plugin/screen path */
+ snprintf (objectPath, 256, "%s/%s/screen%d", COMPIZ_DBUS_ROOT_PATH,
+ pluginName, s->screenNum);
+ dbus_connection_register_object_path (connection, objectPath,
+ &dbusMessagesVTable, s->display);
+}
+
+static void
+dbusRegisterPluginsForDisplay (DBusConnection *connection,
+ CompDisplay *d)
+{
+ unsigned int i;
+ char path[256];
+
+ DBUS_DISPLAY (d);
+
+ for (i = 0; i < dd->nPlugins; i++)
+ {
+ snprintf (path, 256, "%s/%s/allscreens",
+ COMPIZ_DBUS_ROOT_PATH, dd->pluginList[i]);
+
+ dbusRegisterPluginForDisplay (connection, d, dd->pluginList[i]);
+ dbusRegisterOptions (connection, path);
+ }
+}
+
+static void
+dbusRegisterPluginsForScreen (DBusConnection *connection,
+ CompScreen *s)
+{
+ unsigned int i;
+ char path[256];
+
+ DBUS_DISPLAY (s->display);
+
+ for (i = 0; i < dd->nPlugins; i++)
+ {
+ snprintf (path, 256, "%s/%s/screen%d",
+ COMPIZ_DBUS_ROOT_PATH, dd->pluginList[i], s->screenNum);
+ dbusRegisterPluginForScreen (connection, s, dd->pluginList[i]);
+ dbusRegisterOptions (connection, path);
+ }
+}
+
+static void
+dbusUnregisterPluginForDisplay (DBusConnection *connection,
+ CompDisplay *d,
+ char *pluginName)
+{
+ char objectPath[256];
+
+ snprintf (objectPath, 256, "%s/%s/%s", COMPIZ_DBUS_ROOT_PATH,
+ pluginName, "allscreens");
+
+ dbusUnregisterOptions (connection, objectPath);
+ dbus_connection_unregister_object_path (connection, objectPath);
+
+ snprintf (objectPath, 256, "%s/%s", COMPIZ_DBUS_ROOT_PATH, pluginName);
+ dbus_connection_unregister_object_path (connection, objectPath);
+}
+
+static void
+dbusUnregisterPluginsForDisplay (DBusConnection *connection,
+ CompDisplay *d)
+{
+ unsigned int i;
+
+ DBUS_DISPLAY (d);
+
+ for (i = 0; i < dd->nPlugins; i++)
+ dbusUnregisterPluginForDisplay (connection, d, dd->pluginList[i]);
+}
+
+static void
+dbusUnregisterPluginForScreen (DBusConnection *connection,
+ CompScreen *s,
+ char *pluginName)
+{
+ char objectPath[256];
+
+ snprintf (objectPath, 256, "%s/%s/screen%d", COMPIZ_DBUS_ROOT_PATH,
+ pluginName, s->screenNum);
+
+ dbusUnregisterOptions (connection, objectPath);
+ dbus_connection_unregister_object_path (connection, objectPath);
+}
+
+static void
+dbusUnregisterPluginsForScreen (DBusConnection *connection,
+ CompScreen *s)
+{
+ unsigned int i;
+
+ DBUS_DISPLAY (s->display);
+
+ for (i = 0; i < dd->nPlugins; i++)
+ dbusUnregisterPluginForScreen (connection, s, dd->pluginList[i]);
+}
+
+static CompBool
+dbusInitPluginForDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ char objectPath[256];
+
+ DBUS_CORE (&core);
+
+ snprintf (objectPath, 256, "%s/%s/%s", COMPIZ_DBUS_ROOT_PATH,
+ p->vTable->name, "allscreens");
+ dbusRegisterOptions (dc->connection, objectPath);
+
+ return TRUE;
+}
+
+static Bool
+dbusInitPluginForScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ char objectPath[256];
+
+ DBUS_CORE (&core);
+
+ snprintf (objectPath, 256, "%s/%s/screen%d", COMPIZ_DBUS_ROOT_PATH,
+ p->vTable->name, s->screenNum);
+ dbusRegisterOptions (dc->connection, objectPath);
+
+ return TRUE;
+}
+
+static CompBool
+dbusInitPluginForObject (CompPlugin *p,
+ CompObject *o)
+{
+ CompBool status;
+
+ DBUS_CORE (&core);
+
+ UNWRAP (dc, &core, initPluginForObject);
+ status = (*core.initPluginForObject) (p, o);
+ WRAP (dc, &core, initPluginForObject, dbusInitPluginForObject);
+
+ if (status && p->vTable->getObjectOptions)
+ {
+ static InitPluginForObjectProc dispTab[] = {
+ (InitPluginForObjectProc) 0, /* InitPluginForCore */
+ (InitPluginForObjectProc) dbusInitPluginForDisplay,
+ (InitPluginForObjectProc) dbusInitPluginForScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+ }
+
+ return status;
+}
+
+static CompBool
+dbusSetOptionForPlugin (CompObject *object,
+ const char *plugin,
+ const char *name,
+ CompOptionValue *value)
+{
+ Bool status;
+
+ DBUS_CORE (&core);
+
+ UNWRAP (dc, &core, setOptionForPlugin);
+ status = (*core.setOptionForPlugin) (object, plugin, name, value);
+ WRAP (dc, &core, setOptionForPlugin, dbusSetOptionForPlugin);
+
+ if (status)
+ {
+ CompPlugin *p;
+
+ p = findActivePlugin (plugin);
+ if (p && p->vTable->getObjectOptions)
+ {
+ CompOption *option;
+ int nOption;
+
+ option = (*p->vTable->getObjectOptions) (p, object, &nOption);
+ dbusSendChangeSignalForOption (object,
+ compFindOption (option,
+ nOption,
+ name, 0),
+ p->vTable->name);
+
+ if (object->type == COMP_OBJECT_TYPE_DISPLAY &&
+ strcmp (p->vTable->name, "core") == 0 &&
+ strcmp (name, "active_plugins") == 0)
+ {
+ CompScreen *s;
+
+ CORE_DISPLAY (object);
+
+ dbusUnregisterPluginsForDisplay (dc->connection, d);
+ for (s = d->screens; s; s = s->next)
+ dbusUnregisterPluginsForScreen (dc->connection, s);
+
+ dbusUpdatePluginList (d);
+
+ dbusRegisterPluginsForDisplay (dc->connection, d);
+ for (s = d->screens; s; s = s->next)
+ dbusRegisterPluginsForScreen (dc->connection, s);
+ }
+ }
+ }
+
+ return status;
+}
+
+static void
+dbusSendPluginsChangedSignal (const char *name,
+ void *closure)
+{
+ DBusMessage *signal;
+
+ DBUS_CORE (&core);
+
+ signal = dbus_message_new_signal (COMPIZ_DBUS_ROOT_PATH,
+ COMPIZ_DBUS_SERVICE_NAME,
+ COMPIZ_DBUS_PLUGINS_CHANGED_SIGNAL_NAME);
+
+ dbus_connection_send (dc->connection, signal, NULL);
+ dbus_connection_flush (dc->connection);
+
+ dbus_message_unref (signal);
+}
+
+static Bool
+dbusInitCore (CompPlugin *p,
+ CompCore *c)
+{
+ DbusCore *dc;
+ DBusError error;
+ dbus_bool_t status;
+ int fd, ret, mask;
+ char *home, *plugindir;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ dc = malloc (sizeof (DbusCore));
+ if (!dc)
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ free (dc);
+ return FALSE;
+ }
+
+ dbus_error_init (&error);
+
+ dc->connection = dbus_bus_get (DBUS_BUS_SESSION, &error);
+ if (dbus_error_is_set (&error))
+ {
+ compLogMessage ("dbus", CompLogLevelError,
+ "dbus_bus_get error: %s", error.message);
+
+ dbus_error_free (&error);
+ free (dc);
+
+ return FALSE;
+ }
+
+ ret = dbus_bus_request_name (dc->connection,
+ COMPIZ_DBUS_SERVICE_NAME,
+ DBUS_NAME_FLAG_REPLACE_EXISTING |
+ DBUS_NAME_FLAG_ALLOW_REPLACEMENT,
+ &error);
+
+ if (dbus_error_is_set (&error))
+ {
+ compLogMessage ("dbus", CompLogLevelError,
+ "dbus_bus_request_name error: %s", error.message);
+
+ /* dbus_connection_unref (dc->connection); */
+ dbus_error_free (&error);
+ free (dc);
+
+ return FALSE;
+ }
+
+ dbus_error_free (&error);
+
+ if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ {
+ compLogMessage ("dbus", CompLogLevelError,
+ "dbus_bus_request_name reply is not primary owner");
+
+ /* dbus_connection_unref (dc->connection); */
+ free (dc);
+
+ return FALSE;
+ }
+
+ status = dbus_connection_get_unix_fd (dc->connection, &fd);
+ if (!status)
+ {
+ compLogMessage ("dbus", CompLogLevelError,
+ "dbus_connection_get_unix_fd failed");
+
+ /* dbus_connection_unref (dc->connection); */
+ free (dc);
+
+ return FALSE;
+ }
+
+ dc->watchFdHandle = compAddWatchFd (fd,
+ POLLIN | POLLPRI | POLLHUP | POLLERR,
+ dbusProcessMessages,
+ 0);
+
+ mask = NOTIFY_CREATE_MASK | NOTIFY_DELETE_MASK | NOTIFY_MOVE_MASK;
+
+ dc->fileWatch[DBUS_FILE_WATCH_CURRENT] =
+ addFileWatch (".",
+ mask,
+ dbusSendPluginsChangedSignal,
+ 0);
+ dc->fileWatch[DBUS_FILE_WATCH_PLUGIN] =
+ addFileWatch (PLUGINDIR,
+ mask,
+ dbusSendPluginsChangedSignal,
+ 0);
+ dc->fileWatch[DBUS_FILE_WATCH_HOME] = 0;
+
+ home = getenv ("HOME");
+ if (home)
+ {
+ plugindir = malloc (strlen (home) + strlen (HOME_PLUGINDIR) + 3);
+ if (plugindir)
+ {
+ sprintf (plugindir, "%s/%s", home, HOME_PLUGINDIR);
+
+ dc->fileWatch[DBUS_FILE_WATCH_HOME] =
+ addFileWatch (plugindir,
+ mask,
+ dbusSendPluginsChangedSignal,
+ 0);
+
+ free (plugindir);
+ }
+ }
+
+ WRAP (dc, c, initPluginForObject, dbusInitPluginForObject);
+ WRAP (dc, c, setOptionForPlugin, dbusSetOptionForPlugin);
+
+ c->base.privates[corePrivateIndex].ptr = dc;
+
+ /* register the objects */
+ dbus_connection_register_object_path (dc->connection,
+ COMPIZ_DBUS_ROOT_PATH,
+ &dbusMessagesVTable, 0);
+
+ return TRUE;
+}
+
+static void
+dbusFiniCore (CompPlugin *p,
+ CompCore *c)
+{
+ int i;
+
+ DBUS_CORE (c);
+
+ for (i = 0; i < DBUS_FILE_WATCH_NUM; i++)
+ removeFileWatch (dc->fileWatch[i]);
+
+ freeDisplayPrivateIndex (displayPrivateIndex);
+
+ compRemoveWatchFd (dc->watchFdHandle);
+
+ dbus_bus_release_name (dc->connection, COMPIZ_DBUS_SERVICE_NAME, NULL);
+
+ /*
+ can't unref the connection returned by dbus_bus_get as it's
+ shared and we can't know if it's closed or not.
+
+ dbus_connection_unref (dc->connection);
+ */
+
+ UNWRAP (dc, c, initPluginForObject);
+ UNWRAP (dc, c, setOptionForPlugin);
+
+ free (dc);
+}
+
+static Bool
+dbusInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ DbusDisplay *dd;
+
+ DBUS_CORE (&core);
+
+ dd = malloc (sizeof (DbusDisplay));
+ if (!dd)
+ return FALSE;
+
+ dd->pluginList = NULL;
+ dd->nPlugins = 0;
+
+ d->base.privates[displayPrivateIndex].ptr = dd;
+
+ dbusUpdatePluginList (d);
+ dbusRegisterPluginsForDisplay (dc->connection, d);
+
+ return TRUE;
+}
+
+static void
+dbusFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ DBUS_CORE (&core);
+ DBUS_DISPLAY (d);
+
+ dbusUnregisterPluginsForDisplay (dc->connection, d);
+
+ if (dd->pluginList)
+ {
+ unsigned int i;
+
+ for (i = 0; i < dd->nPlugins; i++)
+ free (dd->pluginList[i]);
+ free (dd->pluginList);
+ }
+
+ free (dd);
+}
+
+static Bool
+dbusInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ DBUS_CORE (&core);
+
+ dbusRegisterPluginsForScreen (dc->connection, s);
+
+ return TRUE;
+}
+
+static void
+dbusFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ DBUS_CORE (&core);
+
+ dbusUnregisterPluginsForScreen (dc->connection, s);
+}
+
+static CompBool
+dbusInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) dbusInitCore,
+ (InitPluginObjectProc) dbusInitDisplay,
+ (InitPluginObjectProc) dbusInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+dbusFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) dbusFiniCore,
+ (FiniPluginObjectProc) dbusFiniDisplay,
+ (FiniPluginObjectProc) dbusFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static Bool
+dbusInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&dbusMetadata, p->vTable->name,
+ 0, 0, 0, 0))
+ return FALSE;
+
+ corePrivateIndex = allocateCorePrivateIndex ();
+ if (corePrivateIndex < 0)
+ {
+ compFiniMetadata (&dbusMetadata);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+dbusFini (CompPlugin *p)
+{
+ freeCorePrivateIndex (corePrivateIndex);
+ compFiniMetadata (&dbusMetadata);
+}
+
+static CompMetadata *
+dbusGetMetadata (CompPlugin *plugin)
+{
+ return &dbusMetadata;
+}
+
+CompPluginVTable dbusVTable = {
+ "dbus",
+ dbusGetMetadata,
+ dbusInit,
+ dbusFini,
+ dbusInitObject,
+ dbusFiniObject,
+ 0, /* GetObjectOptions */
+ 0 /* SetObjectOption */
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &dbusVTable;
+}
diff --git a/plugins/decoration.c b/plugins/decoration.c
new file mode 100644
index 0000000..1776ab9
--- /dev/null
+++ b/plugins/decoration.c
@@ -0,0 +1,1759 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "../config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+
+#include <compiz-core.h>
+#include <decoration.h>
+
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+
+static CompMetadata decorMetadata;
+
+typedef struct _Vector {
+ int dx;
+ int dy;
+ int x0;
+ int y0;
+} Vector;
+
+#define DECOR_BARE 0
+#define DECOR_NORMAL 1
+#define DECOR_ACTIVE 2
+#define DECOR_NUM 3
+
+typedef struct _DecorTexture {
+ struct _DecorTexture *next;
+ int refCount;
+ Pixmap pixmap;
+ Damage damage;
+ CompTexture texture;
+} DecorTexture;
+
+typedef struct _Decoration {
+ int refCount;
+ DecorTexture *texture;
+ CompWindowExtents output;
+ CompWindowExtents input;
+ CompWindowExtents maxInput;
+ int minWidth;
+ int minHeight;
+ decor_quad_t *quad;
+ int nQuad;
+} Decoration;
+
+typedef struct _ScaledQuad {
+ CompMatrix matrix;
+ BoxRec box;
+ float sx;
+ float sy;
+} ScaledQuad;
+
+typedef struct _WindowDecoration {
+ Decoration *decor;
+ ScaledQuad *quad;
+ int nQuad;
+} WindowDecoration;
+
+static int corePrivateIndex;
+
+typedef struct _DecorCore {
+ ObjectAddProc objectAdd;
+ ObjectRemoveProc objectRemove;
+} DecorCore;
+
+#define DECOR_DISPLAY_OPTION_SHADOW_RADIUS 0
+#define DECOR_DISPLAY_OPTION_SHADOW_OPACITY 1
+#define DECOR_DISPLAY_OPTION_SHADOW_COLOR 2
+#define DECOR_DISPLAY_OPTION_SHADOW_OFFSET_X 3
+#define DECOR_DISPLAY_OPTION_SHADOW_OFFSET_Y 4
+#define DECOR_DISPLAY_OPTION_COMMAND 5
+#define DECOR_DISPLAY_OPTION_MIPMAP 6
+#define DECOR_DISPLAY_OPTION_DECOR_MATCH 7
+#define DECOR_DISPLAY_OPTION_SHADOW_MATCH 8
+#define DECOR_DISPLAY_OPTION_NUM 9
+
+static int displayPrivateIndex;
+
+typedef struct _DecorDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+ MatchPropertyChangedProc matchPropertyChanged;
+ DecorTexture *textures;
+ Atom supportingDmCheckAtom;
+ Atom winDecorAtom;
+ Atom decorAtom[DECOR_NUM];
+
+ CompOption opt[DECOR_DISPLAY_OPTION_NUM];
+} DecorDisplay;
+
+typedef struct _DecorScreen {
+ int windowPrivateIndex;
+
+ Window dmWin;
+
+ Decoration *decor[DECOR_NUM];
+
+ DrawWindowProc drawWindow;
+ DamageWindowRectProc damageWindowRect;
+ GetOutputExtentsForWindowProc getOutputExtentsForWindow;
+
+ WindowMoveNotifyProc windowMoveNotify;
+ WindowResizeNotifyProc windowResizeNotify;
+
+ WindowStateChangeNotifyProc windowStateChangeNotify;
+
+ CompTimeoutHandle decoratorStartHandle;
+} DecorScreen;
+
+typedef struct _DecorWindow {
+ WindowDecoration *wd;
+ Decoration *decor;
+
+ CompTimeoutHandle resizeUpdateHandle;
+} DecorWindow;
+
+#define GET_DECOR_CORE(c) \
+ ((DecorCore *) (c)->base.privates[corePrivateIndex].ptr)
+
+#define DECOR_CORE(c) \
+ DecorCore *dc = GET_DECOR_CORE (c)
+
+#define GET_DECOR_DISPLAY(d) \
+ ((DecorDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define DECOR_DISPLAY(d) \
+ DecorDisplay *dd = GET_DECOR_DISPLAY (d)
+
+#define GET_DECOR_SCREEN(s, dd) \
+ ((DecorScreen *) (s)->base.privates[(dd)->screenPrivateIndex].ptr)
+
+#define DECOR_SCREEN(s) \
+ DecorScreen *ds = GET_DECOR_SCREEN (s, GET_DECOR_DISPLAY (s->display))
+
+#define GET_DECOR_WINDOW(w, ds) \
+ ((DecorWindow *) (w)->base.privates[(ds)->windowPrivateIndex].ptr)
+
+#define DECOR_WINDOW(w) \
+ DecorWindow *dw = GET_DECOR_WINDOW (w, \
+ GET_DECOR_SCREEN (w->screen, \
+ GET_DECOR_DISPLAY (w->screen->display)))
+
+#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
+
+static Bool
+decorDrawWindow (CompWindow *w,
+ const CompTransform *transform,
+ const FragmentAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ DECOR_SCREEN (w->screen);
+ DECOR_WINDOW (w);
+
+ UNWRAP (ds, w->screen, drawWindow);
+ status = (*w->screen->drawWindow) (w, transform, attrib, region, mask);
+ WRAP (ds, w->screen, drawWindow, decorDrawWindow);
+
+ if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
+ region = &infiniteRegion;
+
+ if (dw->wd && region->numRects)
+ {
+ WindowDecoration *wd = dw->wd;
+ REGION box;
+ int i;
+
+ mask |= PAINT_WINDOW_BLEND_MASK;
+
+ box.rects = &box.extents;
+ box.numRects = 1;
+
+ w->vCount = w->indexCount = 0;
+
+ for (i = 0; i < wd->nQuad; i++)
+ {
+ box.extents = wd->quad[i].box;
+
+ if (box.extents.x1 < box.extents.x2 &&
+ box.extents.y1 < box.extents.y2)
+ {
+ (*w->screen->addWindowGeometry) (w,
+ &wd->quad[i].matrix, 1,
+ &box,
+ region);
+ }
+ }
+
+ if (w->vCount)
+ (*w->screen->drawWindowTexture) (w,
+ &wd->decor->texture->texture,
+ attrib, mask);
+ }
+
+ return status;
+}
+
+static DecorTexture *
+decorGetTexture (CompScreen *screen,
+ Pixmap pixmap)
+{
+ DecorTexture *texture;
+ unsigned int width, height, depth, ui;
+ Window root;
+ int i;
+
+ DECOR_DISPLAY (screen->display);
+
+ for (texture = dd->textures; texture; texture = texture->next)
+ {
+ if (texture->pixmap == pixmap)
+ {
+ texture->refCount++;
+ return texture;
+ }
+ }
+
+ texture = malloc (sizeof (DecorTexture));
+ if (!texture)
+ return NULL;
+
+ initTexture (screen, &texture->texture);
+
+ if (!XGetGeometry (screen->display->display, pixmap, &root,
+ &i, &i, &width, &height, &ui, &depth))
+ {
+ finiTexture (screen, &texture->texture);
+ free (texture);
+ return NULL;
+ }
+
+ if (!bindPixmapToTexture (screen, &texture->texture, pixmap,
+ width, height, depth))
+ {
+ finiTexture (screen, &texture->texture);
+ free (texture);
+ return NULL;
+ }
+
+ if (!dd->opt[DECOR_DISPLAY_OPTION_MIPMAP].value.b)
+ texture->texture.mipmap = FALSE;
+
+ texture->damage = XDamageCreate (screen->display->display, pixmap,
+ XDamageReportRawRectangles);
+
+ texture->refCount = 1;
+ texture->pixmap = pixmap;
+ texture->next = dd->textures;
+
+ dd->textures = texture;
+
+ return texture;
+}
+
+static void
+decorReleaseTexture (CompScreen *screen,
+ DecorTexture *texture)
+{
+ DECOR_DISPLAY (screen->display);
+
+ texture->refCount--;
+ if (texture->refCount)
+ return;
+
+ if (texture == dd->textures)
+ {
+ dd->textures = texture->next;
+ }
+ else
+ {
+ DecorTexture *t;
+
+ for (t = dd->textures; t; t = t->next)
+ {
+ if (t->next == texture)
+ {
+ t->next = texture->next;
+ break;
+ }
+ }
+ }
+
+ finiTexture (screen, &texture->texture);
+ free (texture);
+}
+
+static void
+computeQuadBox (decor_quad_t *q,
+ int width,
+ int height,
+ int *return_x1,
+ int *return_y1,
+ int *return_x2,
+ int *return_y2,
+ float *return_sx,
+ float *return_sy)
+{
+ int x1, y1, x2, y2;
+ float sx = 1.0f;
+ float sy = 1.0f;
+
+ decor_apply_gravity (q->p1.gravity, q->p1.x, q->p1.y, width, height,
+ &x1, &y1);
+ decor_apply_gravity (q->p2.gravity, q->p2.x, q->p2.y, width, height,
+ &x2, &y2);
+
+ if (q->clamp & CLAMP_HORZ)
+ {
+ if (x1 < 0)
+ x1 = 0;
+ if (x2 > width)
+ x2 = width;
+ }
+
+ if (q->clamp & CLAMP_VERT)
+ {
+ if (y1 < 0)
+ y1 = 0;
+ if (y2 > height)
+ y2 = height;
+ }
+
+ if (q->stretch & STRETCH_X)
+ {
+ sx = (float)q->max_width / ((float)(x2 - x1));
+ }
+ else if (q->max_width < x2 - x1)
+ {
+ if (q->align & ALIGN_RIGHT)
+ x1 = x2 - q->max_width;
+ else
+ x2 = x1 + q->max_width;
+ }
+
+ if (q->stretch & STRETCH_Y)
+ {
+ sy = (float)q->max_height / ((float)(y2 - y1));
+ }
+ else if (q->max_height < y2 - y1)
+ {
+ if (q->align & ALIGN_BOTTOM)
+ y1 = y2 - q->max_height;
+ else
+ y2 = y1 + q->max_height;
+ }
+
+ *return_x1 = x1;
+ *return_y1 = y1;
+ *return_x2 = x2;
+ *return_y2 = y2;
+
+ if (return_sx)
+ *return_sx = sx;
+ if (return_sy)
+ *return_sy = sy;
+}
+
+static Decoration *
+decorCreateDecoration (CompScreen *screen,
+ Window id,
+ Atom decorAtom)
+{
+ Decoration *decoration;
+ Atom actual;
+ int result, format;
+ unsigned long n, nleft;
+ unsigned char *data;
+ long *prop;
+ Pixmap pixmap;
+ decor_extents_t input;
+ decor_extents_t maxInput;
+ decor_quad_t *quad;
+ int nQuad;
+ int minWidth;
+ int minHeight;
+ int left, right, top, bottom;
+ int x1, y1, x2, y2;
+
+ result = XGetWindowProperty (screen->display->display, id,
+ decorAtom, 0L, 1024L, FALSE,
+ XA_INTEGER, &actual, &format,
+ &n, &nleft, &data);
+
+ if (result != Success || !data)
+ return NULL;
+
+ if (!n)
+ {
+ XFree (data);
+ return NULL;
+ }
+
+ prop = (long *) data;
+
+ if (decor_property_get_version (prop) != decor_version ())
+ {
+ compLogMessage ("decoration", CompLogLevelWarn,
+ "Property ignored because "
+ "version is %d and decoration plugin version is %d\n",
+ decor_property_get_version (prop), decor_version ());
+
+ XFree (data);
+ return NULL;
+ }
+
+ nQuad = (n - BASE_PROP_SIZE) / QUAD_PROP_SIZE;
+
+ quad = malloc (sizeof (decor_quad_t) * nQuad);
+ if (!quad)
+ {
+ XFree (data);
+ return NULL;
+ }
+
+ nQuad = decor_property_to_quads (prop,
+ n,
+ &pixmap,
+ &input,
+ &maxInput,
+ &minWidth,
+ &minHeight,
+ quad);
+
+ XFree (data);
+
+ if (!nQuad)
+ {
+ free (quad);
+ return NULL;
+ }
+
+ decoration = malloc (sizeof (Decoration));
+ if (!decoration)
+ {
+ free (quad);
+ return NULL;
+ }
+
+ decoration->texture = decorGetTexture (screen, pixmap);
+ if (!decoration->texture)
+ {
+ free (decoration);
+ free (quad);
+ return NULL;
+ }
+
+ decoration->minWidth = minWidth;
+ decoration->minHeight = minHeight;
+ decoration->quad = quad;
+ decoration->nQuad = nQuad;
+
+ left = 0;
+ right = minWidth;
+ top = 0;
+ bottom = minHeight;
+
+ while (nQuad--)
+ {
+ computeQuadBox (quad, minWidth, minHeight, &x1, &y1, &x2, &y2,
+ NULL, NULL);
+
+ if (x1 < left)
+ left = x1;
+ if (y1 < top)
+ top = y1;
+ if (x2 > right)
+ right = x2;
+ if (y2 > bottom)
+ bottom = y2;
+
+ quad++;
+ }
+
+ decoration->output.left = -left;
+ decoration->output.right = right - minWidth;
+ decoration->output.top = -top;
+ decoration->output.bottom = bottom - minHeight;
+
+ decoration->input.left = input.left;
+ decoration->input.right = input.right;
+ decoration->input.top = input.top;
+ decoration->input.bottom = input.bottom;
+
+ decoration->maxInput.left = maxInput.left;
+ decoration->maxInput.right = maxInput.right;
+ decoration->maxInput.top = maxInput.top;
+ decoration->maxInput.bottom = maxInput.bottom;
+
+ decoration->refCount = 1;
+
+ return decoration;
+}
+
+static void
+decorReleaseDecoration (CompScreen *screen,
+ Decoration *decoration)
+{
+ decoration->refCount--;
+ if (decoration->refCount)
+ return;
+
+ decorReleaseTexture (screen, decoration->texture);
+
+ free (decoration->quad);
+ free (decoration);
+}
+
+static void
+decorWindowUpdateDecoration (CompWindow *w)
+{
+ Decoration *decoration;
+
+ DECOR_DISPLAY (w->screen->display);
+ DECOR_WINDOW (w);
+
+ decoration = decorCreateDecoration (w->screen, w->id, dd->winDecorAtom);
+
+ if (dw->decor)
+ decorReleaseDecoration (w->screen, dw->decor);
+
+ dw->decor = decoration;
+}
+
+static WindowDecoration *
+createWindowDecoration (Decoration *d)
+{
+ WindowDecoration *wd;
+
+ wd = malloc (sizeof (WindowDecoration) +
+ sizeof (ScaledQuad) * d->nQuad);
+ if (!wd)
+ return NULL;
+
+ d->refCount++;
+
+ wd->decor = d;
+ wd->quad = (ScaledQuad *) (wd + 1);
+ wd->nQuad = d->nQuad;
+
+ return wd;
+}
+
+static void
+destroyWindowDecoration (CompScreen *screen,
+ WindowDecoration *wd)
+{
+ decorReleaseDecoration (screen, wd->decor);
+ free (wd);
+}
+
+static void
+setDecorationMatrices (CompWindow *w)
+{
+ WindowDecoration *wd;
+ int i;
+ float x0, y0;
+ decor_matrix_t a;
+ CompMatrix b;
+
+
+ DECOR_WINDOW (w);
+
+ wd = dw->wd;
+ if (!wd)
+ return;
+
+ for (i = 0; i < wd->nQuad; i++)
+ {
+ wd->quad[i].matrix = wd->decor->texture->texture.matrix;
+
+ x0 = wd->decor->quad[i].m.x0;
+ y0 = wd->decor->quad[i].m.y0;
+
+ a = wd->decor->quad[i].m;
+ b = wd->quad[i].matrix;
+
+ wd->quad[i].matrix.xx = a.xx * b.xx + a.yx * b.xy;
+ wd->quad[i].matrix.yx = a.xx * b.yx + a.yx * b.yy;
+ wd->quad[i].matrix.xy = a.xy * b.xx + a.yy * b.xy;
+ wd->quad[i].matrix.yy = a.xy * b.yx + a.yy * b.yy;
+ wd->quad[i].matrix.x0 = x0 * b.xx + y0 * b.xy + b.x0;
+ wd->quad[i].matrix.y0 = x0 * b.yx + y0 * b.yy + b.y0;
+
+ wd->quad[i].matrix.xx *= wd->quad[i].sx;
+ wd->quad[i].matrix.yx *= wd->quad[i].sx;
+ wd->quad[i].matrix.xy *= wd->quad[i].sy;
+ wd->quad[i].matrix.yy *= wd->quad[i].sy;
+
+ if (wd->decor->quad[i].align & ALIGN_RIGHT)
+ x0 = wd->quad[i].box.x2 - wd->quad[i].box.x1;
+ else
+ x0 = 0.0f;
+
+ if (wd->decor->quad[i].align & ALIGN_BOTTOM)
+ y0 = wd->quad[i].box.y2 - wd->quad[i].box.y1;
+ else
+ y0 = 0.0f;
+
+ wd->quad[i].matrix.x0 -=
+ x0 * wd->quad[i].matrix.xx +
+ y0 * wd->quad[i].matrix.xy;
+
+ wd->quad[i].matrix.y0 -=
+ y0 * wd->quad[i].matrix.yy +
+ x0 * wd->quad[i].matrix.yx;
+
+ wd->quad[i].matrix.x0 -=
+ wd->quad[i].box.x1 * wd->quad[i].matrix.xx +
+ wd->quad[i].box.y1 * wd->quad[i].matrix.xy;
+
+ wd->quad[i].matrix.y0 -=
+ wd->quad[i].box.y1 * wd->quad[i].matrix.yy +
+ wd->quad[i].box.x1 * wd->quad[i].matrix.yx;
+ }
+}
+
+static void
+updateWindowDecorationScale (CompWindow *w)
+{
+ WindowDecoration *wd;
+ int x1, y1, x2, y2;
+ float sx, sy;
+ int i;
+
+ DECOR_WINDOW (w);
+
+ wd = dw->wd;
+ if (!wd)
+ return;
+
+ for (i = 0; i < wd->nQuad; i++)
+ {
+ computeQuadBox (&wd->decor->quad[i], w->width, w->height,
+ &x1, &y1, &x2, &y2, &sx, &sy);
+
+ wd->quad[i].box.x1 = x1 + w->attrib.x;
+ wd->quad[i].box.y1 = y1 + w->attrib.y;
+ wd->quad[i].box.x2 = x2 + w->attrib.x;
+ wd->quad[i].box.y2 = y2 + w->attrib.y;
+ wd->quad[i].sx = sx;
+ wd->quad[i].sy = sy;
+ }
+
+ setDecorationMatrices (w);
+}
+
+static Bool
+decorCheckSize (CompWindow *w,
+ Decoration *decor)
+{
+ return (decor->minWidth <= w->width && decor->minHeight <= w->height);
+}
+
+static int
+decorWindowShiftX (CompWindow *w)
+{
+ switch (w->sizeHints.win_gravity) {
+ case WestGravity:
+ case NorthWestGravity:
+ case SouthWestGravity:
+ return w->input.left;
+ case EastGravity:
+ case NorthEastGravity:
+ case SouthEastGravity:
+ return -w->input.right;
+ }
+
+ return 0;
+}
+
+static int
+decorWindowShiftY (CompWindow *w)
+{
+ switch (w->sizeHints.win_gravity) {
+ case NorthGravity:
+ case NorthWestGravity:
+ case NorthEastGravity:
+ return w->input.top;
+ case SouthGravity:
+ case SouthWestGravity:
+ case SouthEastGravity:
+ return -w->input.bottom;
+ }
+
+ return 0;
+}
+
+static Bool
+decorWindowUpdate (CompWindow *w,
+ Bool allowDecoration)
+{
+ WindowDecoration *wd;
+ Decoration *old, *decor = NULL;
+ Bool decorate = FALSE;
+ CompMatch *match;
+ int moveDx, moveDy;
+ int oldShiftX = 0;
+ int oldShiftY = 0;
+
+ DECOR_DISPLAY (w->screen->display);
+ DECOR_SCREEN (w->screen);
+ DECOR_WINDOW (w);
+
+ wd = dw->wd;
+ old = (wd) ? wd->decor : NULL;
+
+ switch (w->type) {
+ case CompWindowTypeDialogMask:
+ case CompWindowTypeModalDialogMask:
+ case CompWindowTypeUtilMask:
+ case CompWindowTypeMenuMask:
+ case CompWindowTypeNormalMask:
+ if (w->mwmDecor & (MwmDecorAll | MwmDecorTitle))
+ decorate = TRUE;
+ default:
+ break;
+ }
+
+ if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
+ decorate = FALSE;
+
+ if (w->attrib.override_redirect)
+ decorate = FALSE;
+
+ if (decorate)
+ {
+ match = &dd->opt[DECOR_DISPLAY_OPTION_DECOR_MATCH].value.match;
+ if (!matchEval (match, w))
+ decorate = FALSE;
+ }
+
+ if (decorate)
+ {
+ if (dw->decor && decorCheckSize (w, dw->decor))
+ {
+ decor = dw->decor;
+ }
+ else
+ {
+ if (w->id == w->screen->display->activeWindow)
+ decor = ds->decor[DECOR_ACTIVE];
+ else
+ decor = ds->decor[DECOR_NORMAL];
+ }
+ }
+ else
+ {
+ match = &dd->opt[DECOR_DISPLAY_OPTION_SHADOW_MATCH].value.match;
+ if (matchEval (match, w))
+ {
+ if (w->region->numRects == 1)
+ decor = ds->decor[DECOR_BARE];
+
+ if (decor)
+ {
+ if (!decorCheckSize (w, decor))
+ decor = NULL;
+ }
+ }
+ }
+
+ if (!ds->dmWin || !allowDecoration)
+ decor = NULL;
+
+ if (decor == old)
+ return FALSE;
+
+ damageWindowOutputExtents (w);
+
+ if (old)
+ {
+ oldShiftX = decorWindowShiftX (w);
+ oldShiftY = decorWindowShiftY (w);
+
+ destroyWindowDecoration (w->screen, wd);
+ }
+
+ if (decor)
+ {
+ dw->wd = createWindowDecoration (decor);
+ if (!dw->wd)
+ return FALSE;
+
+ if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE)
+ setWindowFrameExtents (w, &decor->maxInput);
+ else
+ setWindowFrameExtents (w, &decor->input);
+
+ moveDx = decorWindowShiftX (w) - oldShiftX;
+ moveDy = decorWindowShiftY (w) - oldShiftY;
+
+ updateWindowOutputExtents (w);
+ damageWindowOutputExtents (w);
+ updateWindowDecorationScale (w);
+ }
+ else
+ {
+ CompWindowExtents emptyInput;
+
+ memset (&emptyInput, 0, sizeof (emptyInput));
+ setWindowFrameExtents (w, &emptyInput);
+
+ dw->wd = NULL;
+
+ moveDx = -oldShiftX;
+ moveDy = -oldShiftY;
+ }
+
+ if (w->placed && !w->attrib.override_redirect && (moveDx || moveDy))
+ {
+ XWindowChanges xwc;
+ unsigned int mask = CWX | CWY;
+
+ xwc.x = w->serverX + moveDx;
+ xwc.y = w->serverY + moveDy;
+
+ if (w->state & CompWindowStateFullscreenMask)
+ mask &= ~(CWX | CWY);
+
+ if (w->state & CompWindowStateMaximizedHorzMask)
+ mask &= ~CWX;
+
+ if (w->state & CompWindowStateMaximizedVertMask)
+ mask &= ~CWY;
+
+ if (w->saveMask & CWX)
+ w->saveWc.x += moveDx;
+
+ if (w->saveMask & CWY)
+ w->saveWc.y += moveDy;
+
+ if (mask)
+ configureXWindow (w, mask, &xwc);
+ }
+
+ return TRUE;
+}
+
+static void
+decorCheckForDmOnScreen (CompScreen *s,
+ Bool updateWindows)
+{
+ CompDisplay *d = s->display;
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+ Window dmWin = None;
+
+ DECOR_DISPLAY (s->display);
+ DECOR_SCREEN (s);
+
+ result = XGetWindowProperty (d->display, s->root,
+ dd->supportingDmCheckAtom, 0L, 1L, FALSE,
+ XA_WINDOW, &actual, &format,
+ &n, &left, &data);
+
+ if (result == Success && data)
+ {
+ if (n)
+ {
+ XWindowAttributes attr;
+
+ memcpy (&dmWin, data, sizeof (Window));
+
+ compCheckForError (d->display);
+
+ XGetWindowAttributes (d->display, dmWin, &attr);
+
+ if (compCheckForError (d->display))
+ dmWin = None;
+ }
+
+ XFree (data);
+ }
+
+ if (dmWin != ds->dmWin)
+ {
+ CompWindow *w;
+ int i;
+
+ if (dmWin)
+ {
+ for (i = 0; i < DECOR_NUM; i++)
+ ds->decor[i] =
+ decorCreateDecoration (s, s->root, dd->decorAtom[i]);
+ }
+ else
+ {
+ for (i = 0; i < DECOR_NUM; i++)
+ {
+ if (ds->decor[i])
+ {
+ decorReleaseDecoration (s, ds->decor[i]);
+ ds->decor[i] = 0;
+ }
+ }
+
+ for (w = s->windows; w; w = w->next)
+ {
+ DECOR_WINDOW (w);
+
+ if (dw->decor)
+ {
+ decorReleaseDecoration (s, dw->decor);
+ dw->decor = 0;
+ }
+ }
+ }
+
+ ds->dmWin = dmWin;
+
+ if (updateWindows)
+ {
+ for (w = s->windows; w; w = w->next)
+ decorWindowUpdate (w, TRUE);
+ }
+ }
+}
+
+static void
+decorHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ Window activeWindow = d->activeWindow;
+ CompWindow *w;
+
+ DECOR_DISPLAY (d);
+
+ switch (event->type) {
+ case DestroyNotify:
+ w = findWindowAtDisplay (d, event->xdestroywindow.window);
+ if (w)
+ {
+ DECOR_SCREEN (w->screen);
+
+ if (w->id == ds->dmWin)
+ decorCheckForDmOnScreen (w->screen, TRUE);
+ }
+ break;
+ case MapRequest:
+ w = findWindowAtDisplay (d, event->xmaprequest.window);
+ if (w)
+ decorWindowUpdate (w, TRUE);
+ break;
+ default:
+ if (event->type == d->damageEvent + XDamageNotify)
+ {
+ XDamageNotifyEvent *de = (XDamageNotifyEvent *) event;
+ DecorTexture *t;
+
+ for (t = dd->textures; t; t = t->next)
+ {
+ if (t->pixmap == de->drawable)
+ {
+ DecorWindow *dw;
+ DecorScreen *ds;
+ CompScreen *s;
+
+ t->texture.oldMipmaps = TRUE;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ ds = GET_DECOR_SCREEN (s, dd);
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->shaded || w->mapNum)
+ {
+ dw = GET_DECOR_WINDOW (w, ds);
+
+ if (dw->wd && dw->wd->decor->texture == t)
+ damageWindowOutputExtents (w);
+ }
+ }
+ }
+ return;
+ }
+ }
+ }
+ break;
+ }
+
+ UNWRAP (dd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (dd, d, handleEvent, decorHandleEvent);
+
+ if (d->activeWindow != activeWindow)
+ {
+ w = findWindowAtDisplay (d, activeWindow);
+ if (w)
+ decorWindowUpdate (w, TRUE);
+
+ w = findWindowAtDisplay (d, d->activeWindow);
+ if (w)
+ decorWindowUpdate (w, TRUE);
+ }
+
+ switch (event->type) {
+ case PropertyNotify:
+ if (event->xproperty.atom == dd->winDecorAtom)
+ {
+ w = findWindowAtDisplay (d, event->xproperty.window);
+ if (w)
+ {
+ decorWindowUpdateDecoration (w);
+ decorWindowUpdate (w, TRUE);
+ }
+ }
+ else if (event->xproperty.atom == d->mwmHintsAtom)
+ {
+ w = findWindowAtDisplay (d, event->xproperty.window);
+ if (w)
+ decorWindowUpdate (w, TRUE);
+ }
+ else
+ {
+ CompScreen *s;
+
+ s = findScreenAtDisplay (d, event->xproperty.window);
+ if (s)
+ {
+ if (event->xproperty.atom == dd->supportingDmCheckAtom)
+ {
+ decorCheckForDmOnScreen (s, TRUE);
+ }
+ else
+ {
+ int i;
+
+ for (i = 0; i < DECOR_NUM; i++)
+ {
+ if (event->xproperty.atom == dd->decorAtom[i])
+ {
+ DECOR_SCREEN (s);
+
+ if (ds->decor[i])
+ decorReleaseDecoration (s, ds->decor[i]);
+
+ ds->decor[i] =
+ decorCreateDecoration (s, s->root,
+ dd->decorAtom[i]);
+
+ for (w = s->windows; w; w = w->next)
+ decorWindowUpdate (w, TRUE);
+ }
+ }
+ }
+ }
+ }
+ break;
+ default:
+ if (d->shapeExtension && event->type == d->shapeEvent + ShapeNotify)
+ {
+ w = findWindowAtDisplay (d, ((XShapeEvent *) event)->window);
+ if (w)
+ decorWindowUpdate (w, TRUE);
+ }
+ break;
+ }
+}
+
+static Bool
+decorDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status;
+
+ DECOR_SCREEN (w->screen);
+
+ if (initial)
+ decorWindowUpdate (w, TRUE);
+
+ UNWRAP (ds, w->screen, damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (ds, w->screen, damageWindowRect, decorDamageWindowRect);
+
+ return status;
+}
+
+static void
+decorGetOutputExtentsForWindow (CompWindow *w,
+ CompWindowExtents *output)
+{
+ DECOR_SCREEN (w->screen);
+ DECOR_WINDOW (w);
+
+ UNWRAP (ds, w->screen, getOutputExtentsForWindow);
+ (*w->screen->getOutputExtentsForWindow) (w, output);
+ WRAP (ds, w->screen, getOutputExtentsForWindow,
+ decorGetOutputExtentsForWindow);
+
+ if (dw->wd)
+ {
+ CompWindowExtents *e = &dw->wd->decor->output;
+
+ if (e->left > output->left)
+ output->left = e->left;
+ if (e->right > output->right)
+ output->right = e->right;
+ if (e->top > output->top)
+ output->top = e->top;
+ if (e->bottom > output->bottom)
+ output->bottom = e->bottom;
+ }
+}
+
+static CompBool
+decorStartDecorator (void *closure)
+{
+ CompScreen *s = (CompScreen *) closure;
+
+ DECOR_DISPLAY (s->display);
+ DECOR_SCREEN (s);
+
+ ds->decoratorStartHandle = 0;
+
+ if (!ds->dmWin)
+ runCommand (s, dd->opt[DECOR_DISPLAY_OPTION_COMMAND].value.s);
+
+ return FALSE;
+}
+
+static CompOption *
+decorGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ DECOR_DISPLAY (display);
+
+ *count = NUM_OPTIONS (dd);
+ return dd->opt;
+}
+
+static Bool
+decorSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ DECOR_DISPLAY (display);
+
+ o = compFindOption (dd->opt, NUM_OPTIONS (dd), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case DECOR_DISPLAY_OPTION_COMMAND:
+ if (compSetStringOption (o, value))
+ {
+ CompScreen *s;
+
+ for (s = display->screens; s; s = s->next)
+ {
+ DECOR_SCREEN (s);
+
+ if (!ds->dmWin)
+ runCommand (s, o->value.s);
+ }
+
+ return TRUE;
+ }
+ break;
+ case DECOR_DISPLAY_OPTION_SHADOW_MATCH:
+ {
+ char *matchString;
+
+ /*
+ Make sure RGBA matching is always present and disable shadows
+ for RGBA windows by default if the user didn't specify an
+ RGBA match.
+ Reasoning for that is that shadows are desired for some RGBA
+ windows (e.g. rectangular windows that just happen to have an
+ RGBA colormap), while it's absolutely undesired for others
+ (especially shaped ones) ... by enforcing no shadows for RGBA
+ windows by default, we are flexible to user desires while still
+ making sure we don't show ugliness by default
+ */
+
+ matchString = matchToString (&value->match);
+ if (matchString)
+ {
+ if (!strstr (matchString, "rgba="))
+ {
+ CompMatch rgbaMatch;
+
+ matchInit (&rgbaMatch);
+ matchAddFromString (&rgbaMatch, "rgba=0");
+ matchAddGroup (&value->match, MATCH_OP_AND_MASK,
+ &rgbaMatch);
+ matchFini (&rgbaMatch);
+ }
+ free (matchString);
+ }
+ }
+ /* fall-through intended */
+ case DECOR_DISPLAY_OPTION_DECOR_MATCH:
+ if (compSetMatchOption (o, value))
+ {
+ CompScreen *s;
+ CompWindow *w;
+
+ for (s = display->screens; s; s = s->next)
+ for (w = s->windows; w; w = w->next)
+ decorWindowUpdate (w, TRUE);
+ }
+ break;
+ default:
+ if (compSetOption (o, value))
+ return TRUE;
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+decorWindowMoveNotify (CompWindow *w,
+ int dx,
+ int dy,
+ Bool immediate)
+{
+ DECOR_SCREEN (w->screen);
+ DECOR_WINDOW (w);
+
+ if (dw->wd)
+ {
+ WindowDecoration *wd = dw->wd;
+ int i;
+
+ for (i = 0; i < wd->nQuad; i++)
+ {
+ wd->quad[i].box.x1 += dx;
+ wd->quad[i].box.y1 += dy;
+ wd->quad[i].box.x2 += dx;
+ wd->quad[i].box.y2 += dy;
+ }
+
+ setDecorationMatrices (w);
+ }
+
+ UNWRAP (ds, w->screen, windowMoveNotify);
+ (*w->screen->windowMoveNotify) (w, dx, dy, immediate);
+ WRAP (ds, w->screen, windowMoveNotify, decorWindowMoveNotify);
+}
+
+static Bool
+decorResizeUpdateTimeout (void *closure)
+{
+ CompWindow *w = (CompWindow *) closure;
+
+ DECOR_WINDOW (w);
+
+ decorWindowUpdate (w, TRUE);
+
+ dw->resizeUpdateHandle = 0;
+
+ return FALSE;
+}
+
+static void
+decorWindowResizeNotify (CompWindow *w,
+ int dx,
+ int dy,
+ int dwidth,
+ int dheight)
+{
+ DECOR_SCREEN (w->screen);
+ DECOR_WINDOW (w);
+
+ /* FIXME: we should not need a timer for calling decorWindowUpdate,
+ and only call updateWindowDecorationScale if decorWindowUpdate
+ returns FALSE. Unfortunately, decorWindowUpdate may call
+ updateWindowOutputExtents, which may call WindowResizeNotify. As
+ we never should call a wrapped function that's currently
+ processed, we need the timer for the moment. updateWindowOutputExtents
+ should be fixed so that it does not emit a resize notification. */
+ dw->resizeUpdateHandle = compAddTimeout (0, 0, decorResizeUpdateTimeout, w);
+ updateWindowDecorationScale (w);
+
+ UNWRAP (ds, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
+ WRAP (ds, w->screen, windowResizeNotify, decorWindowResizeNotify);
+}
+
+static void
+decorWindowStateChangeNotify (CompWindow *w,
+ unsigned int lastState)
+{
+ DECOR_SCREEN (w->screen);
+ DECOR_WINDOW (w);
+
+ if (!decorWindowUpdate (w, TRUE))
+ {
+ if (dw->wd && dw->wd->decor)
+ {
+ if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE)
+ setWindowFrameExtents (w, &dw->wd->decor->maxInput);
+ else
+ setWindowFrameExtents (w, &dw->wd->decor->input);
+ }
+ }
+
+ UNWRAP (ds, w->screen, windowStateChangeNotify);
+ (*w->screen->windowStateChangeNotify) (w, lastState);
+ WRAP (ds, w->screen, windowStateChangeNotify, decorWindowStateChangeNotify);
+}
+
+static void
+decorMatchPropertyChanged (CompDisplay *d,
+ CompWindow *w)
+{
+ DECOR_DISPLAY (d);
+
+ decorWindowUpdate (w, TRUE);
+
+ UNWRAP (dd, d, matchPropertyChanged);
+ (*d->matchPropertyChanged) (d, w);
+ WRAP (dd, d, matchPropertyChanged, decorMatchPropertyChanged);
+}
+
+static void
+decorWindowAdd (CompScreen *s,
+ CompWindow *w)
+{
+ if (w->shaded || w->attrib.map_state == IsViewable)
+ decorWindowUpdate (w, TRUE);
+}
+
+static void
+decorWindowRemove (CompScreen *s,
+ CompWindow *w)
+{
+ if (!w->destroyed)
+ decorWindowUpdate (w, FALSE);
+}
+
+static void
+decorObjectAdd (CompObject *parent,
+ CompObject *object)
+{
+ static ObjectAddProc dispTab[] = {
+ (ObjectAddProc) 0, /* CoreAdd */
+ (ObjectAddProc) 0, /* DisplayAdd */
+ (ObjectAddProc) 0, /* ScreenAdd */
+ (ObjectAddProc) decorWindowAdd
+ };
+
+ DECOR_CORE (&core);
+
+ UNWRAP (dc, &core, objectAdd);
+ (*core.objectAdd) (parent, object);
+ WRAP (dc, &core, objectAdd, decorObjectAdd);
+
+ DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), (parent, object));
+}
+
+static void
+decorObjectRemove (CompObject *parent,
+ CompObject *object)
+{
+ static ObjectRemoveProc dispTab[] = {
+ (ObjectRemoveProc) 0, /* CoreRemove */
+ (ObjectRemoveProc) 0, /* DisplayRemove */
+ (ObjectRemoveProc) 0, /* ScreenRemove */
+ (ObjectRemoveProc) decorWindowRemove
+ };
+
+ DECOR_CORE (&core);
+
+ DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), (parent, object));
+
+ UNWRAP (dc, &core, objectRemove);
+ (*core.objectRemove) (parent, object);
+ WRAP (dc, &core, objectRemove, decorObjectRemove);
+}
+
+static Bool
+decorInitCore (CompPlugin *p,
+ CompCore *c)
+{
+ DecorCore *dc;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ dc = malloc (sizeof (DecorCore));
+ if (!dc)
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ free (dc);
+ return FALSE;
+ }
+
+ WRAP (dc, c, objectAdd, decorObjectAdd);
+ WRAP (dc, c, objectRemove, decorObjectRemove);
+
+ c->base.privates[corePrivateIndex].ptr = dc;
+
+ return TRUE;
+}
+
+static void
+decorFiniCore (CompPlugin *p,
+ CompCore *c)
+{
+ DECOR_CORE (c);
+
+ freeDisplayPrivateIndex (displayPrivateIndex);
+
+ UNWRAP (dc, c, objectAdd);
+ UNWRAP (dc, c, objectRemove);
+
+ free (dc);
+}
+
+static const CompMetadataOptionInfo decorDisplayOptionInfo[] = {
+ { "shadow_radius", "float", "<min>0.0</min><max>48.0</max>", 0, 0 },
+ { "shadow_opacity", "float", "<min>0.0</min>", 0, 0 },
+ { "shadow_color", "color", 0, 0, 0 },
+ { "shadow_x_offset", "int", "<min>-16</min><max>16</max>", 0, 0 },
+ { "shadow_y_offset", "int", "<min>-16</min><max>16</max>", 0, 0 },
+ { "command", "string", 0, 0, 0 },
+ { "mipmap", "bool", 0, 0, 0 },
+ { "decoration_match", "match", 0, 0, 0 },
+ { "shadow_match", "match", 0, 0, 0 }
+};
+
+static Bool
+decorInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ DecorDisplay *dd;
+
+ dd = malloc (sizeof (DecorDisplay));
+ if (!dd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &decorMetadata,
+ decorDisplayOptionInfo,
+ dd->opt,
+ DECOR_DISPLAY_OPTION_NUM))
+ {
+ free (dd);
+ return FALSE;
+ }
+
+ dd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (dd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, dd->opt, DECOR_DISPLAY_OPTION_NUM);
+ free (dd);
+ return FALSE;
+ }
+
+ dd->textures = 0;
+
+ dd->supportingDmCheckAtom =
+ XInternAtom (d->display, DECOR_SUPPORTING_DM_CHECK_ATOM_NAME, 0);
+ dd->winDecorAtom =
+ XInternAtom (d->display, DECOR_WINDOW_ATOM_NAME, 0);
+ dd->decorAtom[DECOR_BARE] =
+ XInternAtom (d->display, DECOR_BARE_ATOM_NAME, 0);
+ dd->decorAtom[DECOR_NORMAL] =
+ XInternAtom (d->display, DECOR_NORMAL_ATOM_NAME, 0);
+ dd->decorAtom[DECOR_ACTIVE] =
+ XInternAtom (d->display, DECOR_ACTIVE_ATOM_NAME, 0);
+
+ WRAP (dd, d, handleEvent, decorHandleEvent);
+ WRAP (dd, d, matchPropertyChanged, decorMatchPropertyChanged);
+
+ d->base.privates[displayPrivateIndex].ptr = dd;
+
+ return TRUE;
+}
+
+static void
+decorFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ DECOR_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, dd->screenPrivateIndex);
+
+ UNWRAP (dd, d, handleEvent);
+ UNWRAP (dd, d, matchPropertyChanged);
+
+ compFiniDisplayOptions (d, dd->opt, DECOR_DISPLAY_OPTION_NUM);
+
+ free (dd);
+}
+
+static Bool
+decorInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ DecorScreen *ds;
+
+ DECOR_DISPLAY (s->display);
+
+ ds = malloc (sizeof (DecorScreen));
+ if (!ds)
+ return FALSE;
+
+ ds->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (ds->windowPrivateIndex < 0)
+ {
+ free (ds);
+ return FALSE;
+ }
+
+ memset (ds->decor, 0, sizeof (ds->decor));
+
+ ds->dmWin = None;
+ ds->decoratorStartHandle = 0;
+
+ WRAP (ds, s, drawWindow, decorDrawWindow);
+ WRAP (ds, s, damageWindowRect, decorDamageWindowRect);
+ WRAP (ds, s, getOutputExtentsForWindow, decorGetOutputExtentsForWindow);
+ WRAP (ds, s, windowMoveNotify, decorWindowMoveNotify);
+ WRAP (ds, s, windowResizeNotify, decorWindowResizeNotify);
+ WRAP (ds, s, windowStateChangeNotify, decorWindowStateChangeNotify);
+
+ s->base.privates[dd->screenPrivateIndex].ptr = ds;
+
+ decorCheckForDmOnScreen (s, FALSE);
+
+ if (!ds->dmWin)
+ ds->decoratorStartHandle = compAddTimeout (0, -1,
+ decorStartDecorator, s);
+
+ return TRUE;
+}
+
+static void
+decorFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ int i;
+
+ DECOR_SCREEN (s);
+
+ for (i = 0; i < DECOR_NUM; i++)
+ if (ds->decor[i])
+ decorReleaseDecoration (s, ds->decor[i]);
+
+ if (ds->decoratorStartHandle)
+ compRemoveTimeout (ds->decoratorStartHandle);
+
+ freeWindowPrivateIndex (s, ds->windowPrivateIndex);
+
+ UNWRAP (ds, s, drawWindow);
+ UNWRAP (ds, s, damageWindowRect);
+ UNWRAP (ds, s, getOutputExtentsForWindow);
+ UNWRAP (ds, s, windowMoveNotify);
+ UNWRAP (ds, s, windowResizeNotify);
+ UNWRAP (ds, s, windowStateChangeNotify);
+
+ free (ds);
+}
+
+static Bool
+decorInitWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ DecorWindow *dw;
+
+ DECOR_SCREEN (w->screen);
+
+ dw = malloc (sizeof (DecorWindow));
+ if (!dw)
+ return FALSE;
+
+ dw->wd = NULL;
+ dw->decor = NULL;
+
+ dw->resizeUpdateHandle = 0;
+
+ w->base.privates[ds->windowPrivateIndex].ptr = dw;
+
+ if (!w->attrib.override_redirect)
+ decorWindowUpdateDecoration (w);
+
+ if (w->base.parent)
+ decorWindowAdd (w->screen, w);
+
+ return TRUE;
+}
+
+static void
+decorFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ DECOR_WINDOW (w);
+
+ if (dw->resizeUpdateHandle)
+ compRemoveTimeout (dw->resizeUpdateHandle);
+
+ if (w->base.parent)
+ decorWindowRemove (w->screen, w);
+
+ if (dw->wd)
+ destroyWindowDecoration (w->screen, dw->wd);
+
+ if (dw->decor)
+ decorReleaseDecoration (w->screen, dw->decor);
+
+ free (dw);
+}
+
+static CompBool
+decorInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) decorInitCore,
+ (InitPluginObjectProc) decorInitDisplay,
+ (InitPluginObjectProc) decorInitScreen,
+ (InitPluginObjectProc) decorInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+decorFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) decorFiniCore,
+ (FiniPluginObjectProc) decorFiniDisplay,
+ (FiniPluginObjectProc) decorFiniScreen,
+ (FiniPluginObjectProc) decorFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+decorGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) decorGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+decorSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) decorSetDisplayOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+decorInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&decorMetadata,
+ p->vTable->name,
+ decorDisplayOptionInfo,
+ DECOR_DISPLAY_OPTION_NUM,
+ 0, 0))
+ return FALSE;
+
+ corePrivateIndex = allocateCorePrivateIndex ();
+ if (corePrivateIndex < 0)
+ {
+ compFiniMetadata (&decorMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&decorMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+decorFini (CompPlugin *p)
+{
+ freeCorePrivateIndex (corePrivateIndex);
+ compFiniMetadata (&decorMetadata);
+}
+
+static CompMetadata *
+decorGetMetadata (CompPlugin *plugin)
+{
+ return &decorMetadata;
+}
+
+static CompPluginVTable decorVTable = {
+ "decoration",
+ decorGetMetadata,
+ decorInit,
+ decorFini,
+ decorInitObject,
+ decorFiniObject,
+ decorGetObjectOptions,
+ decorSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &decorVTable;
+}
diff --git a/plugins/fade.c b/plugins/fade.c
new file mode 100644
index 0000000..f8cbdfc
--- /dev/null
+++ b/plugins/fade.c
@@ -0,0 +1,1049 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <compiz-core.h>
+
+static CompMetadata fadeMetadata;
+
+static int displayPrivateIndex;
+
+typedef struct _FadeDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+ MatchExpHandlerChangedProc matchExpHandlerChanged;
+ int displayModals;
+ Bool suppressMinimizeOpenClose;
+ CompMatch alwaysFadeWindowMatch;
+} FadeDisplay;
+
+#define FADE_SCREEN_OPTION_FADE_MODE 0
+#define FADE_SCREEN_OPTION_FADE_SPEED 1
+#define FADE_SCREEN_OPTION_FADE_TIME 2
+#define FADE_SCREEN_OPTION_WINDOW_MATCH 3
+#define FADE_SCREEN_OPTION_VISUAL_BELL 4
+#define FADE_SCREEN_OPTION_FULLSCREEN_VISUAL_BELL 5
+#define FADE_SCREEN_OPTION_MINIMIZE_OPEN_CLOSE 6
+#define FADE_SCREEN_OPTION_DIM_UNRESPONSIVE 7
+#define FADE_SCREEN_OPTION_UNRESPONSIVE_BRIGHTNESS 8
+#define FADE_SCREEN_OPTION_UNRESPONSIVE_SATURATION 9
+#define FADE_SCREEN_OPTION_NUM 10
+
+#define FADE_MODE_CONSTANTSPEED 0
+#define FADE_MODE_CONSTANTTIME 1
+#define FADE_MODE_MAX FADE_MODE_CONSTANTTIME
+
+typedef struct _FadeScreen {
+ int windowPrivateIndex;
+ int fadeTime;
+
+ CompOption opt[FADE_SCREEN_OPTION_NUM];
+
+ PreparePaintScreenProc preparePaintScreen;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+ FocusWindowProc focusWindow;
+ WindowResizeNotifyProc windowResizeNotify;
+
+ CompMatch match;
+} FadeScreen;
+
+typedef struct _FadeWindow {
+ GLushort opacity;
+ GLushort brightness;
+ GLushort saturation;
+
+ int dModal;
+
+ int destroyCnt;
+ int unmapCnt;
+
+ Bool shaded;
+ Bool alive;
+ Bool fadeOut;
+
+ int steps;
+
+ int fadeTime;
+
+ int opacityDiff;
+ int brightnessDiff;
+ int saturationDiff;
+
+ GLushort targetOpacity;
+ GLushort targetBrightness;
+ GLushort targetSaturation;
+} FadeWindow;
+
+#define GET_FADE_DISPLAY(d) \
+ ((FadeDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define FADE_DISPLAY(d) \
+ FadeDisplay *fd = GET_FADE_DISPLAY (d)
+
+#define GET_FADE_SCREEN(s, fd) \
+ ((FadeScreen *) (s)->base.privates[(fd)->screenPrivateIndex].ptr)
+
+#define FADE_SCREEN(s) \
+ FadeScreen *fs = GET_FADE_SCREEN (s, GET_FADE_DISPLAY (s->display))
+
+#define GET_FADE_WINDOW(w, fs) \
+ ((FadeWindow *) (w)->base.privates[(fs)->windowPrivateIndex].ptr)
+
+#define FADE_WINDOW(w) \
+ FadeWindow *fw = GET_FADE_WINDOW (w, \
+ GET_FADE_SCREEN (w->screen, \
+ GET_FADE_DISPLAY (w->screen->display)))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static void
+fadeUpdateWindowFadeMatch (CompDisplay *display,
+ CompOptionValue *value,
+ CompMatch *match)
+{
+ matchFini (match);
+ matchInit (match);
+ matchAddFromString (match, "!type=desktop");
+ matchAddGroup (match, MATCH_OP_AND_MASK, &value->match);
+ matchUpdate (display, match);
+}
+
+static CompOption *
+fadeGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+ int *count)
+{
+ FADE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (fs);
+ return fs->opt;
+}
+
+static Bool
+fadeSetScreenOption (CompPlugin *plugin,
+ CompScreen *screen,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ FADE_SCREEN (screen);
+
+ o = compFindOption (fs->opt, NUM_OPTIONS (fs), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case FADE_SCREEN_OPTION_FADE_SPEED:
+ if (compSetFloatOption (o, value))
+ {
+ fs->fadeTime = 1000.0f / o->value.f;
+ return TRUE;
+ }
+ break;
+ case FADE_SCREEN_OPTION_WINDOW_MATCH:
+ if (compSetMatchOption (o, value))
+ {
+ fadeUpdateWindowFadeMatch (screen->display, &o->value, &fs->match);
+ return TRUE;
+ }
+ break;
+ default:
+ if (compSetOption (o, value))
+ return TRUE;
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+fadePreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ CompWindow *w;
+ int steps;
+
+ FADE_SCREEN (s);
+
+ switch (fs->opt[FADE_SCREEN_OPTION_FADE_MODE].value.i) {
+ case FADE_MODE_CONSTANTSPEED:
+ steps = (msSinceLastPaint * OPAQUE) / fs->fadeTime;
+ if (steps < 12)
+ steps = 12;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ FadeWindow *fw = GET_FADE_WINDOW (w, fs);
+ fw->steps = steps;
+ fw->fadeTime = 0;
+ }
+
+ break;
+ case FADE_MODE_CONSTANTTIME:
+ for (w = s->windows; w; w = w->next)
+ {
+ FadeWindow *fw = GET_FADE_WINDOW (w, fs);
+
+ if (fw->fadeTime)
+ {
+ fw->steps = 1;
+ fw->fadeTime -= msSinceLastPaint;
+ if (fw->fadeTime < 0)
+ fw->fadeTime = 0;
+ }
+ else
+ {
+ fw->steps = 0;
+ }
+ }
+
+ break;
+ }
+
+
+ UNWRAP (fs, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (fs, s, preparePaintScreen, fadePreparePaintScreen);
+}
+
+static void
+fadeWindowStop (CompWindow *w)
+{
+ FADE_WINDOW (w);
+
+ while (fw->unmapCnt)
+ {
+ unmapWindow (w);
+ fw->unmapCnt--;
+ }
+
+ while (fw->destroyCnt)
+ {
+ destroyWindow (w);
+ fw->destroyCnt--;
+ }
+}
+
+static Bool
+fadePaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ FADE_DISPLAY (s->display);
+ FADE_SCREEN (s);
+ FADE_WINDOW (w);
+
+ if (!w->screen->canDoSlightlySaturated)
+ fw->saturation = attrib->saturation;
+
+ if (!w->alive ||
+ fw->destroyCnt ||
+ fw->unmapCnt ||
+ fw->opacity != attrib->opacity ||
+ fw->brightness != attrib->brightness ||
+ fw->saturation != attrib->saturation ||
+ fd->displayModals)
+ {
+ WindowPaintAttrib fAttrib = *attrib;
+ int mode = fs->opt[FADE_SCREEN_OPTION_FADE_MODE].value.i;
+
+ if (!w->alive && fs->opt[FADE_SCREEN_OPTION_DIM_UNRESPONSIVE].value.b)
+ {
+ GLuint value;
+
+ value = fs->opt[FADE_SCREEN_OPTION_UNRESPONSIVE_BRIGHTNESS].value.i;
+ if (value != 100)
+ fAttrib.brightness = fAttrib.brightness * value / 100;
+
+ value = fs->opt[FADE_SCREEN_OPTION_UNRESPONSIVE_SATURATION].value.i;
+ if (value != 100 && s->canDoSlightlySaturated)
+ fAttrib.saturation = fAttrib.saturation * value / 100;
+ }
+ else if (fd->displayModals && !fw->dModal)
+ {
+ fAttrib.brightness = 0xa8a8;
+ fAttrib.saturation = 0;
+ }
+
+ if (fw->fadeOut)
+ fAttrib.opacity = 0;
+
+ if (mode == FADE_MODE_CONSTANTTIME)
+ {
+ if (fAttrib.opacity != fw->targetOpacity ||
+ fAttrib.brightness != fw->targetBrightness ||
+ fAttrib.saturation != fw->targetSaturation)
+ {
+ fw->fadeTime = fs->opt[FADE_SCREEN_OPTION_FADE_TIME].value.i;
+ fw->steps = 1;
+
+ fw->opacityDiff = fAttrib.opacity - fw->opacity;
+ fw->brightnessDiff = fAttrib.brightness - fw->brightness;
+ fw->saturationDiff = fAttrib.saturation - fw->saturation;
+
+ fw->targetOpacity = fAttrib.opacity;
+ fw->targetBrightness = fAttrib.brightness;
+ fw->targetSaturation = fAttrib.saturation;
+ }
+ }
+
+ if (fw->steps)
+ {
+ GLint opacity = OPAQUE;
+ GLint brightness = BRIGHT;
+ GLint saturation = COLOR;
+
+ if (mode == FADE_MODE_CONSTANTSPEED)
+ {
+ opacity = fw->opacity;
+ if (fAttrib.opacity > fw->opacity)
+ {
+ opacity = fw->opacity + fw->steps;
+ if (opacity > fAttrib.opacity)
+ opacity = fAttrib.opacity;
+ }
+ else if (fAttrib.opacity < fw->opacity)
+ {
+ opacity = fw->opacity - fw->steps;
+ if (opacity < fAttrib.opacity)
+ opacity = fAttrib.opacity;
+ }
+
+ brightness = fw->brightness;
+ if (fAttrib.brightness > fw->brightness)
+ {
+ brightness = fw->brightness + (fw->steps / 12);
+ if (brightness > fAttrib.brightness)
+ brightness = fAttrib.brightness;
+ }
+ else if (fAttrib.brightness < fw->brightness)
+ {
+ brightness = fw->brightness - (fw->steps / 12);
+ if (brightness < fAttrib.brightness)
+ brightness = fAttrib.brightness;
+ }
+
+ saturation = fw->saturation;
+ if (fAttrib.saturation > fw->saturation)
+ {
+ saturation = fw->saturation + (fw->steps / 6);
+ if (saturation > fAttrib.saturation)
+ saturation = fAttrib.saturation;
+ }
+ else if (fAttrib.saturation < fw->saturation)
+ {
+ saturation = fw->saturation - (fw->steps / 6);
+ if (saturation < fAttrib.saturation)
+ saturation = fAttrib.saturation;
+ }
+ }
+ else if (mode == FADE_MODE_CONSTANTTIME)
+ {
+ int fadeTime = fs->opt[FADE_SCREEN_OPTION_FADE_TIME].value.i;
+
+ opacity = fAttrib.opacity -
+ (fw->opacityDiff * fw->fadeTime / fadeTime);
+ brightness = fAttrib.brightness -
+ (fw->brightnessDiff * fw->fadeTime / fadeTime);
+ saturation = fAttrib.saturation -
+ (fw->saturationDiff * fw->fadeTime / fadeTime);
+ }
+
+ fw->steps = 0;
+
+ if (opacity > 0)
+ {
+ fw->opacity = opacity;
+ fw->brightness = brightness;
+ fw->saturation = saturation;
+
+ if (opacity != fAttrib.opacity ||
+ brightness != fAttrib.brightness ||
+ saturation != fAttrib.saturation)
+ addWindowDamage (w);
+ }
+ else
+ {
+ fw->opacity = 0;
+
+ fadeWindowStop (w);
+ }
+ }
+
+ fAttrib.opacity = fw->opacity;
+ fAttrib.brightness = fw->brightness;
+ fAttrib.saturation = fw->saturation;
+
+ UNWRAP (fs, s, paintWindow);
+ status = (*s->paintWindow) (w, &fAttrib, transform, region, mask);
+ WRAP (fs, s, paintWindow, fadePaintWindow);
+ }
+ else
+ {
+ UNWRAP (fs, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (fs, s, paintWindow, fadePaintWindow);
+ }
+
+ return status;
+}
+
+static void
+fadeAddDisplayModal (CompDisplay *d,
+ CompWindow *w)
+{
+ FADE_DISPLAY (d);
+ FADE_WINDOW (w);
+
+ if (!(w->state & CompWindowStateDisplayModalMask))
+ return;
+
+ if (fw->dModal)
+ return;
+
+ fw->dModal = 1;
+
+ fd->displayModals++;
+ if (fd->displayModals == 1)
+ {
+ CompScreen *s;
+ for (s = d->screens; s; s = s->next)
+ damageScreen (s);
+ }
+}
+
+static void
+fadeRemoveDisplayModal (CompDisplay *d,
+ CompWindow *w)
+{
+ FADE_DISPLAY (d);
+ FADE_WINDOW (w);
+
+ if (!fw->dModal)
+ return;
+
+ fw->dModal = 0;
+
+ fd->displayModals--;
+ if (fd->displayModals == 0)
+ {
+ CompScreen *s;
+ for (s = d->screens; s; s = s->next)
+ damageScreen (s);
+ }
+}
+
+/* Returns whether this window should be faded
+ * on open and close events. */
+static Bool
+isFadeWinForOpenClose (CompWindow *w)
+{
+ FADE_DISPLAY (w->screen->display);
+ FADE_SCREEN (w->screen);
+
+ if (fs->opt[FADE_SCREEN_OPTION_MINIMIZE_OPEN_CLOSE].value.b &&
+ !fd->suppressMinimizeOpenClose)
+ {
+ return TRUE;
+ }
+ return matchEval (&fd->alwaysFadeWindowMatch, w);
+}
+
+static void
+fadeHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompWindow *w;
+
+ FADE_DISPLAY (d);
+
+ switch (event->type) {
+ case DestroyNotify:
+ w = findWindowAtDisplay (d, event->xdestroywindow.window);
+ if (w)
+ {
+ FADE_SCREEN (w->screen);
+
+ if (w->texture->pixmap && isFadeWinForOpenClose (w) &&
+ matchEval (&fs->match, w))
+ {
+ FADE_WINDOW (w);
+
+ if (fw->opacity == 0xffff)
+ fw->opacity = 0xfffe;
+
+ fw->destroyCnt++;
+ w->destroyRefCnt++;
+
+ fw->fadeOut = TRUE;
+
+ addWindowDamage (w);
+ }
+
+ fadeRemoveDisplayModal (d, w);
+ }
+ break;
+ case UnmapNotify:
+ w = findWindowAtDisplay (d, event->xunmap.window);
+ if (w)
+ {
+ FADE_SCREEN (w->screen);
+ FADE_WINDOW (w);
+
+ fw->shaded = w->shaded;
+
+ if (fs->opt[FADE_SCREEN_OPTION_MINIMIZE_OPEN_CLOSE].value.b &&
+ !fd->suppressMinimizeOpenClose &&
+ !fw->shaded && w->texture->pixmap &&
+ matchEval (&fs->match, w))
+ {
+ if (fw->opacity == 0xffff)
+ fw->opacity = 0xfffe;
+
+ fw->unmapCnt++;
+ w->unmapRefCnt++;
+
+ fw->fadeOut = TRUE;
+
+ addWindowDamage (w);
+ }
+
+ fadeRemoveDisplayModal (d, w);
+ }
+ break;
+ case MapNotify:
+ w = findWindowAtDisplay (d, event->xmap.window);
+ if (w)
+ {
+ FADE_SCREEN (w->screen);
+
+ if (fs->opt[FADE_SCREEN_OPTION_MINIMIZE_OPEN_CLOSE].value.b &&
+ !fd->suppressMinimizeOpenClose)
+ {
+ fadeWindowStop (w);
+ }
+ if (w->state & CompWindowStateDisplayModalMask)
+ fadeAddDisplayModal (d, w);
+ }
+ break;
+ default:
+ if (event->type == d->xkbEvent)
+ {
+ XkbAnyEvent *xkbEvent = (XkbAnyEvent *) event;
+
+ if (xkbEvent->xkb_type == XkbBellNotify)
+ {
+ XkbBellNotifyEvent *xkbBellEvent = (XkbBellNotifyEvent *)
+ xkbEvent;
+
+ w = findWindowAtDisplay (d, xkbBellEvent->window);
+ if (!w)
+ w = findWindowAtDisplay (d, d->activeWindow);
+
+ if (w)
+ {
+ CompScreen *s = w->screen;
+
+ FADE_SCREEN (s);
+
+ if (fs->opt[FADE_SCREEN_OPTION_VISUAL_BELL].value.b)
+ {
+ int option;
+
+ option = FADE_SCREEN_OPTION_FULLSCREEN_VISUAL_BELL;
+ if (fs->opt[option].value.b)
+ {
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->destroyed)
+ continue;
+
+ if (w->attrib.map_state != IsViewable)
+ continue;
+
+ if (w->damaged)
+ {
+ FADE_WINDOW (w);
+
+ fw->brightness = w->paint.brightness / 2;
+ }
+ }
+
+ damageScreen (s);
+ }
+ else
+ {
+ FADE_WINDOW (w);
+
+ fw->brightness = w->paint.brightness / 2;
+
+ addWindowDamage (w);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ UNWRAP (fd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (fd, d, handleEvent, fadeHandleEvent);
+
+ switch (event->type) {
+ case PropertyNotify:
+ if (event->xproperty.atom == d->winStateAtom)
+ {
+ w = findWindowAtDisplay (d, event->xproperty.window);
+ if (w && w->attrib.map_state == IsViewable)
+ {
+ if (w->state & CompWindowStateDisplayModalMask)
+ fadeAddDisplayModal (d, w);
+ else
+ fadeRemoveDisplayModal (d, w);
+ }
+ }
+ break;
+ case ClientMessage:
+ if (event->xclient.message_type == d->wmProtocolsAtom &&
+ event->xclient.data.l[0] == d->wmPingAtom)
+ {
+ w = findWindowAtDisplay (d, event->xclient.data.l[2]);
+ if (w)
+ {
+ FADE_WINDOW (w);
+
+ if (w->alive != fw->alive)
+ {
+ addWindowDamage (w);
+ fw->alive = w->alive;
+ }
+ }
+ }
+ }
+}
+
+static Bool
+fadeDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status;
+
+ FADE_SCREEN (w->screen);
+
+ if (initial)
+ {
+ FADE_WINDOW (w);
+
+ fw->fadeOut = FALSE;
+
+ if (fw->shaded)
+ {
+ fw->shaded = w->shaded;
+ }
+ else if (matchEval (&fs->match, w))
+ {
+ if (isFadeWinForOpenClose (w))
+ {
+ fw->opacity = 0;
+ fw->targetOpacity = 0;
+ }
+ }
+ }
+
+ UNWRAP (fs, w->screen, damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (fs, w->screen, damageWindowRect, fadeDamageWindowRect);
+
+ return status;
+}
+
+static Bool
+fadeFocusWindow (CompWindow *w)
+{
+ Bool status;
+
+ FADE_SCREEN (w->screen);
+ FADE_WINDOW (w);
+
+ if (fw->destroyCnt || fw->unmapCnt)
+ return FALSE;
+
+ UNWRAP (fs, w->screen, focusWindow);
+ status = (*w->screen->focusWindow) (w);
+ WRAP (fs, w->screen, focusWindow, fadeFocusWindow);
+
+ return status;
+}
+
+static void
+fadeWindowResizeNotify (CompWindow *w,
+ int dx,
+ int dy,
+ int dwidth,
+ int dheight)
+{
+ FADE_SCREEN (w->screen);
+
+ if (!w->mapNum)
+ fadeWindowStop (w);
+
+ UNWRAP (fs, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
+ WRAP (fs, w->screen, windowResizeNotify, fadeWindowResizeNotify);
+}
+
+static void
+fadeMatchExpHandlerChanged (CompDisplay *d)
+{
+ CompScreen *s;
+
+ FADE_DISPLAY (d);
+
+ for (s = d->screens; s; s = s->next)
+ matchUpdate (d, &GET_FADE_SCREEN (s,fd)->match);
+
+ UNWRAP (fd, d, matchExpHandlerChanged);
+ (*d->matchExpHandlerChanged) (d);
+ WRAP (fd, d, matchExpHandlerChanged, fadeMatchExpHandlerChanged);
+}
+
+static Bool
+fadeInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ FadeDisplay *fd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ fd = malloc (sizeof (FadeDisplay));
+ if (!fd)
+ return FALSE;
+
+ fd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (fd->screenPrivateIndex < 0)
+ {
+ free (fd);
+ return FALSE;
+ }
+
+ fd->displayModals = 0;
+
+ fd->suppressMinimizeOpenClose = (findActivePlugin ("animation") != NULL);
+
+ /* Always fade opening and closing of screen-dimming layer of
+ logout window and gksu. */
+ matchInit (&fd->alwaysFadeWindowMatch);
+ matchAddExp (&fd->alwaysFadeWindowMatch, 0, "title=gksu");
+ matchAddExp (&fd->alwaysFadeWindowMatch, 0, "title=x-session-manager");
+ matchAddExp (&fd->alwaysFadeWindowMatch, 0, "title=gnome-session");
+ matchUpdate (d, &fd->alwaysFadeWindowMatch);
+
+ WRAP (fd, d, handleEvent, fadeHandleEvent);
+ WRAP (fd, d, matchExpHandlerChanged, fadeMatchExpHandlerChanged);
+
+ d->base.privates[displayPrivateIndex].ptr = fd;
+
+ return TRUE;
+}
+
+static void
+fadeFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ FADE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, fd->screenPrivateIndex);
+
+ matchFini (&fd->alwaysFadeWindowMatch);
+
+ UNWRAP (fd, d, handleEvent);
+ UNWRAP (fd, d, matchExpHandlerChanged);
+
+ free (fd);
+}
+
+static const CompMetadataOptionInfo fadeScreenOptionInfo[] = {
+ { "fade_mode", "int", RESTOSTRING (0, FADE_MODE_MAX), 0, 0 },
+ { "fade_speed", "float", "<min>0.1</min>", 0, 0 },
+ { "fade_time", "int", "<min>1</min>", 0, 0 },
+ { "window_match", "match", "<helper>true</helper>", 0, 0 },
+ { "visual_bell", "bool", 0, 0, 0 },
+ { "fullscreen_visual_bell", "bool", 0, 0, 0 },
+ { "minimize_open_close", "bool", 0, 0, 0 },
+ { "dim_unresponsive", "bool", 0, 0, 0 },
+ { "unresponsive_brightness", "int", "<min>0</min><max>100</max>", 0, 0 },
+ { "unresponsive_saturation", "int", "<min>0</min><max>100</max>", 0, 0 }
+};
+
+static Bool
+fadeInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ FadeScreen *fs;
+
+ FADE_DISPLAY (s->display);
+
+ fs = malloc (sizeof (FadeScreen));
+ if (!fs)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &fadeMetadata,
+ fadeScreenOptionInfo,
+ fs->opt,
+ FADE_SCREEN_OPTION_NUM))
+ {
+ free (fs);
+ return FALSE;
+ }
+
+ fs->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (fs->windowPrivateIndex < 0)
+ {
+ compFiniScreenOptions (s, fs->opt, FADE_SCREEN_OPTION_NUM);
+ free (fs);
+ return FALSE;
+ }
+
+ fs->fadeTime = 1000.0f / fs->opt[FADE_SCREEN_OPTION_FADE_SPEED].value.f;
+
+ matchInit (&fs->match);
+
+ fadeUpdateWindowFadeMatch (s->display,
+ &fs->opt[FADE_SCREEN_OPTION_WINDOW_MATCH].value,
+ &fs->match);
+
+ WRAP (fs, s, preparePaintScreen, fadePreparePaintScreen);
+ WRAP (fs, s, paintWindow, fadePaintWindow);
+ WRAP (fs, s, damageWindowRect, fadeDamageWindowRect);
+ WRAP (fs, s, focusWindow, fadeFocusWindow);
+ WRAP (fs, s, windowResizeNotify, fadeWindowResizeNotify);
+
+ s->base.privates[fd->screenPrivateIndex].ptr = fs;
+
+ return TRUE;
+}
+
+static void
+fadeFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ FADE_SCREEN (s);
+
+ matchFini (&fs->match);
+
+ freeWindowPrivateIndex (s, fs->windowPrivateIndex);
+
+ UNWRAP (fs, s, preparePaintScreen);
+ UNWRAP (fs, s, paintWindow);
+ UNWRAP (fs, s, damageWindowRect);
+ UNWRAP (fs, s, focusWindow);
+ UNWRAP (fs, s, windowResizeNotify);
+
+ compFiniScreenOptions (s, fs->opt, FADE_SCREEN_OPTION_NUM);
+
+ free (fs);
+}
+
+static Bool
+fadeInitWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ FadeWindow *fw;
+
+ FADE_SCREEN (w->screen);
+
+ fw = malloc (sizeof (FadeWindow));
+ if (!fw)
+ return FALSE;
+
+ fw->opacity = w->paint.opacity;
+ fw->brightness = w->paint.brightness;
+ fw->saturation = w->paint.saturation;
+
+ fw->targetOpacity = fw->opacity;
+ fw->targetBrightness = fw->brightness;
+ fw->targetSaturation = fw->saturation;
+
+ fw->opacityDiff = 0;
+ fw->brightnessDiff = 0;
+ fw->saturationDiff = 0;
+
+ fw->dModal = 0;
+
+ fw->destroyCnt = 0;
+ fw->unmapCnt = 0;
+ fw->shaded = w->shaded;
+ fw->fadeOut = FALSE;
+ fw->alive = w->alive;
+
+ fw->steps = 0;
+ fw->fadeTime = 0;
+
+ w->base.privates[fs->windowPrivateIndex].ptr = fw;
+
+ if (w->attrib.map_state == IsViewable)
+ {
+ if (w->state & CompWindowStateDisplayModalMask)
+ fadeAddDisplayModal (w->screen->display, w);
+ }
+
+ return TRUE;
+}
+
+static void
+fadeFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ FADE_WINDOW (w);
+
+ fadeRemoveDisplayModal (w->screen->display, w);
+ fadeWindowStop (w);
+
+ free (fw);
+}
+
+static CompBool
+fadeInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) fadeInitDisplay,
+ (InitPluginObjectProc) fadeInitScreen,
+ (InitPluginObjectProc) fadeInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+fadeFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) fadeFiniDisplay,
+ (FiniPluginObjectProc) fadeFiniScreen,
+ (FiniPluginObjectProc) fadeFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+fadeGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) 0, /* GetDisplayOptions */
+ (GetPluginObjectOptionsProc) fadeGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+fadeSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) 0, /* SetDisplayOption */
+ (SetPluginObjectOptionProc) fadeSetScreenOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+fadeInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&fadeMetadata, p->vTable->name, 0, 0,
+ fadeScreenOptionInfo,
+ FADE_SCREEN_OPTION_NUM))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&fadeMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&fadeMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+fadeFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&fadeMetadata);
+}
+
+static CompMetadata *
+fadeGetMetadata (CompPlugin *plugin)
+{
+ return &fadeMetadata;
+}
+
+static CompPluginVTable fadeVTable = {
+ "fade",
+ fadeGetMetadata,
+ fadeInit,
+ fadeFini,
+ fadeInitObject,
+ fadeFiniObject,
+ fadeGetObjectOptions,
+ fadeSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &fadeVTable;
+}
diff --git a/plugins/fuse.c b/plugins/fuse.c
new file mode 100644
index 0000000..9c6c1d3
--- /dev/null
+++ b/plugins/fuse.c
@@ -0,0 +1,1442 @@
+/*
+ * Copyright © 2007 David Reveman
+ *
+ * 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, and that the name of
+ * David Reveman not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * David Reveman makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/mount.h>
+#include <fuse.h>
+#include <fuse_lowlevel.h>
+
+#include <compiz-core.h>
+
+static CompMetadata fuseMetadata;
+
+#define FUSE_INODE_TYPE_ROOT (1 << 0)
+#define FUSE_INODE_TYPE_PLUGIN (1 << 1)
+#define FUSE_INODE_TYPE_SCREEN (1 << 2)
+#define FUSE_INODE_TYPE_DISPLAY (1 << 3)
+#define FUSE_INODE_TYPE_OPTION (1 << 4)
+#define FUSE_INODE_TYPE_TYPE (1 << 5)
+#define FUSE_INODE_TYPE_VALUE (1 << 6)
+#define FUSE_INODE_TYPE_ITEM_COUNT (1 << 7)
+#define FUSE_INODE_TYPE_ITEM_TYPE (1 << 8)
+#define FUSE_INODE_TYPE_ITEMS (1 << 9)
+#define FUSE_INODE_TYPE_ITEM_VALUE (1 << 10)
+#define FUSE_INODE_TYPE_MIN (1 << 11)
+#define FUSE_INODE_TYPE_MAX (1 << 12)
+#define FUSE_INODE_TYPE_PRECISION (1 << 13)
+
+#define DIR_MASK (FUSE_INODE_TYPE_ROOT | \
+ FUSE_INODE_TYPE_PLUGIN | \
+ FUSE_INODE_TYPE_SCREEN | \
+ FUSE_INODE_TYPE_DISPLAY | \
+ FUSE_INODE_TYPE_OPTION | \
+ FUSE_INODE_TYPE_ITEMS)
+
+#define CONST_DIR_MASK (FUSE_INODE_TYPE_PLUGIN | \
+ FUSE_INODE_TYPE_SCREEN | \
+ FUSE_INODE_TYPE_DISPLAY | \
+ FUSE_INODE_TYPE_OPTION)
+
+#define WRITE_MASK (FUSE_INODE_TYPE_VALUE | \
+ FUSE_INODE_TYPE_ITEM_VALUE)
+
+#define FUSE_INODE_FLAG_TRUNC (1 << 0)
+
+typedef struct _FuseInode {
+ struct _FuseInode *parent;
+ struct _FuseInode *child;
+ struct _FuseInode *sibling;
+
+ int type;
+ int flags;
+ fuse_ino_t ino;
+ char *name;
+} FuseInode;
+
+typedef struct _FuseWriteBuffer {
+ char *data;
+ int size;
+ Bool dirty;
+} FuseWriteBuffer;
+
+static int displayPrivateIndex;
+
+#define FUSE_DISPLAY_OPTION_MOUNT_POINT 0
+#define FUSE_DISPLAY_OPTION_NUM 1
+
+typedef struct _FuseDisplay {
+ CompOption opt[FUSE_DISPLAY_OPTION_NUM];
+
+ struct fuse_session *session;
+ struct fuse_chan *channel;
+ char *mountPoint;
+ CompWatchFdHandle watchFdHandle;
+ char *buffer;
+} FuseDisplay;
+
+#define GET_FUSE_DISPLAY(d) \
+ ((FuseDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define FUSE_DISPLAY(d) \
+ FuseDisplay *fd = GET_FUSE_DISPLAY (d)
+
+#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
+
+static fuse_ino_t nextIno = 1;
+static FuseInode *inodes = NULL;
+
+static FuseInode *
+fuseAddInode (FuseInode *parent,
+ int type,
+ const char *name)
+{
+ FuseInode *inode;
+
+ inode = malloc (sizeof (FuseInode));
+ if (!inode)
+ return NULL;
+
+ inode->parent = parent;
+ inode->sibling = NULL;
+ inode->child = NULL;
+ inode->type = type;
+ inode->flags = 0;
+ inode->ino = nextIno++;
+ inode->name = strdup (name);
+
+ if (parent)
+ {
+ if (parent->child)
+ inode->sibling = parent->child;
+
+ parent->child = inode;
+ }
+
+ return inode;
+}
+
+static void
+fuseRemoveInode (FuseInode *parent,
+ FuseInode *inode)
+{
+ while (inode->child)
+ fuseRemoveInode (inode, inode->child);
+
+ if (parent)
+ {
+ FuseInode *s, *prev = NULL;
+
+ for (s = parent->child; s; s = s->sibling)
+ {
+ if (s == inode)
+ break;
+
+ prev = s;
+ }
+
+ if (prev)
+ prev->sibling = inode->sibling;
+ else
+ parent->child = NULL;
+ }
+
+ if (inode->name)
+ free (inode->name);
+
+ free (inode);
+}
+
+static FuseInode *
+fuseFindInode (FuseInode *inode,
+ fuse_ino_t ino,
+ int mask)
+{
+ if (inode->ino != ino)
+ {
+ FuseInode *c = inode->child;
+
+ inode = NULL;
+ while (c)
+ {
+ inode = fuseFindInode (c, ino, ~0);
+ if (inode)
+ break;
+
+ c = c->sibling;
+ }
+ }
+
+ if (inode && (inode->type & mask))
+ return inode;
+
+ return NULL;
+}
+
+static FuseInode *
+fuseLookupChild (FuseInode *inode,
+ const char *name)
+{
+ FuseInode *c;
+
+ for (c = inode->child; c; c = c->sibling)
+ if (strcmp (c->name, name) == 0)
+ return c;
+
+ return NULL;
+}
+
+/* MULTIDPYERROR: only works with one or less displays present */
+/* OBJECTOPTION: only display and screen options are supported */
+static CompObject *
+fuseGetObjectFromInode (FuseInode *inode)
+{
+ CompObject *object;
+
+ object = compObjectFind (&core.base, COMP_OBJECT_TYPE_DISPLAY, NULL);
+ if (!object)
+ return NULL;
+
+ if (inode->type & FUSE_INODE_TYPE_SCREEN)
+ {
+ return compObjectFind (object, COMP_OBJECT_TYPE_SCREEN,
+ inode->name + 6);
+ }
+ else if (inode->type & FUSE_INODE_TYPE_DISPLAY)
+ {
+ return object;
+ }
+
+ return NULL;
+}
+
+static CompOption *
+fuseGetOptionsFromInode (CompObject *object,
+ FuseInode *inode,
+ int *nOption)
+{
+ CompOption *option = NULL;
+
+ if (inode->type & FUSE_INODE_TYPE_PLUGIN)
+ {
+ CompPlugin *p;
+
+ p = findActivePlugin (inode->name);
+ if (p && p->vTable->getObjectOptions)
+ option = (*p->vTable->getObjectOptions) (p, object, nOption);
+ }
+
+ return option;
+}
+
+static CompOption *
+fuseGetOptionFromInode (FuseInode *inode)
+{
+ if (inode->type & (FUSE_INODE_TYPE_OPTION |
+ FUSE_INODE_TYPE_ITEMS))
+ {
+ CompObject *object;
+ CompOption *option;
+ int nOption;
+
+ if (inode->type & FUSE_INODE_TYPE_ITEMS)
+ inode = inode->parent;
+
+ object = fuseGetObjectFromInode (inode);
+ if (!object)
+ return NULL;
+
+ option = fuseGetOptionsFromInode (object, inode->parent, &nOption);
+ if (option)
+ {
+ while (nOption--)
+ {
+ if (strcmp (inode->name, option->name) == 0)
+ return option;
+
+ option++;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* MULTIDPYERROR: only works with one or less displays present */
+static char *
+fuseGetStringFromInode (FuseInode *inode)
+{
+ CompOption *option;
+ char str[256];
+
+ if (!inode->parent)
+ return NULL;
+
+ option = fuseGetOptionFromInode (inode->parent);
+ if (!option)
+ return NULL;
+
+ if (inode->flags & FUSE_INODE_FLAG_TRUNC)
+ return strdup ("");
+
+ if (inode->type & FUSE_INODE_TYPE_TYPE)
+ {
+ return strdup (optionTypeToString (option->type));
+ }
+ else if (inode->type & (FUSE_INODE_TYPE_VALUE | FUSE_INODE_TYPE_ITEM_VALUE))
+ {
+ CompOptionValue *value = NULL;
+ CompOptionType type;
+
+ if (inode->type & FUSE_INODE_TYPE_ITEM_VALUE)
+ {
+ int i;
+
+ if (sscanf (inode->name, "value%d", &i))
+ {
+ if (i < option->value.list.nValue)
+ {
+ value = &option->value.list.value[i];
+ type = option->value.list.type;
+ }
+ }
+ }
+ else
+ {
+ value = &option->value;
+ type = option->type;
+ }
+
+ if (value)
+ {
+ switch (type) {
+ case CompOptionTypeBool:
+ return strdup (value->b ? "true" : "false");
+ case CompOptionTypeInt:
+ snprintf (str, 256, "%d", value->i);
+ return strdup (str);
+ case CompOptionTypeFloat:
+ snprintf (str, 256, "%f", value->f);
+ return strdup (str);
+ case CompOptionTypeString:
+ return strdup (value->s);
+ case CompOptionTypeColor:
+ return colorToString (value->c);
+ case CompOptionTypeKey:
+ if (core.displays)
+ return keyActionToString (core.displays, &value->action);
+ case CompOptionTypeButton:
+ if (core.displays)
+ return buttonActionToString (core.displays,
+ &value->action);
+ case CompOptionTypeEdge:
+ return edgeMaskToString (value->action.edgeMask);
+ case CompOptionTypeBell:
+ return strdup (value->action.bell ? "true" : "false");
+ case CompOptionTypeMatch:
+ return matchToString (&value->match);
+ default:
+ break;
+ }
+ }
+ }
+ else if (inode->type & FUSE_INODE_TYPE_MIN)
+ {
+ if (option->type == CompOptionTypeInt)
+ snprintf (str, 256, "%d", option->rest.i.min);
+ else
+ snprintf (str, 256, "%f", option->rest.f.min);
+
+ return strdup (str);
+ }
+ else if (inode->type & FUSE_INODE_TYPE_MAX)
+ {
+ if (option->type == CompOptionTypeInt)
+ snprintf (str, 256, "%d", option->rest.i.max);
+ else
+ snprintf (str, 256, "%f", option->rest.f.max);
+
+ return strdup (str);
+ }
+ else if (inode->type & FUSE_INODE_TYPE_PRECISION)
+ {
+ snprintf (str, 256, "%f", option->rest.f.precision);
+ return strdup (str);
+ }
+ else if (inode->type & FUSE_INODE_TYPE_ITEM_COUNT)
+ {
+ snprintf (str, 256, "%d", option->value.list.nValue);
+ return strdup (str);
+ }
+ else if (inode->type & FUSE_INODE_TYPE_ITEM_TYPE)
+ {
+ return strdup (optionTypeToString (option->value.list.type));
+ }
+
+ return NULL;
+}
+
+static void
+fuseUpdateInode (CompDisplay *d,
+ FuseInode *inode)
+{
+ CompScreen *s;
+ CompPlugin *p;
+ CompOption *option;
+ char str[256];
+
+ if (inode->type & FUSE_INODE_TYPE_ROOT)
+ {
+ FuseInode *c;
+
+ for (c = inode->child; c; c = c->sibling)
+ {
+ if (!findActivePlugin (c->name))
+ fuseRemoveInode (inode, c);
+ }
+
+ for (p = getPlugins (); p; p = p->next)
+ if (!fuseLookupChild (inode, p->vTable->name))
+ fuseAddInode (inode, FUSE_INODE_TYPE_PLUGIN,
+ p->vTable->name);
+ }
+ else if (inode->type & FUSE_INODE_TYPE_PLUGIN)
+ {
+ int n;
+
+ if (fuseGetOptionsFromInode (&d->base, inode, &n))
+ fuseAddInode (inode, FUSE_INODE_TYPE_DISPLAY, "allscreens");
+
+ for (s = d->screens; s; s = s->next)
+ {
+ if (fuseGetOptionsFromInode (&s->base, inode, &n))
+ {
+ sprintf (str, "screen%d", s->screenNum);
+ fuseAddInode (inode, FUSE_INODE_TYPE_SCREEN, str);
+ }
+ }
+ }
+ else if (inode->type & (FUSE_INODE_TYPE_DISPLAY | FUSE_INODE_TYPE_SCREEN))
+ {
+ CompObject *object;
+
+ object = fuseGetObjectFromInode (inode);
+ if (object)
+ {
+ int nOption;
+
+ option = fuseGetOptionsFromInode (object, inode->parent, &nOption);
+ if (option)
+ {
+ while (nOption--)
+ {
+ fuseAddInode (inode, FUSE_INODE_TYPE_OPTION, option->name);
+
+ option++;
+ }
+ }
+ }
+ }
+ else if (inode->type & FUSE_INODE_TYPE_OPTION)
+ {
+ option = fuseGetOptionFromInode (inode);
+ if (option)
+ {
+ fuseAddInode (inode, FUSE_INODE_TYPE_TYPE, "type");
+
+ switch (option->type) {
+ case CompOptionTypeFloat:
+ fuseAddInode (inode, FUSE_INODE_TYPE_PRECISION, "precision");
+ /* fall-through */
+ case CompOptionTypeInt:
+ fuseAddInode (inode, FUSE_INODE_TYPE_MIN, "min");
+ fuseAddInode (inode, FUSE_INODE_TYPE_MAX, "max");
+ /* fall-through */
+ case CompOptionTypeBool:
+ case CompOptionTypeString:
+ case CompOptionTypeColor:
+ case CompOptionTypeKey:
+ case CompOptionTypeButton:
+ case CompOptionTypeEdge:
+ case CompOptionTypeBell:
+ case CompOptionTypeMatch:
+ fuseAddInode (inode, FUSE_INODE_TYPE_VALUE, "value");
+ break;
+ case CompOptionTypeList:
+ fuseAddInode (inode, FUSE_INODE_TYPE_ITEMS, "items");
+ fuseAddInode (inode, FUSE_INODE_TYPE_ITEM_COUNT,
+ "number_of_items");
+ fuseAddInode (inode, FUSE_INODE_TYPE_ITEM_TYPE, "item_type");
+ default:
+ break;
+ }
+ }
+ }
+ else if (inode->type & FUSE_INODE_TYPE_ITEMS)
+ {
+ option = fuseGetOptionFromInode (inode->parent);
+ if (option && option->type == CompOptionTypeList)
+ {
+ FuseInode *c, *next;
+ int i, nValue = option->value.list.nValue;
+
+ for (i = 0; i < option->value.list.nValue; i++)
+ {
+ sprintf (str, "value%d", i);
+ if (!fuseLookupChild (inode, str))
+ fuseAddInode (inode, FUSE_INODE_TYPE_ITEM_VALUE, str);
+ }
+
+ for (c = inode->child; c; c = next)
+ {
+ next = c->sibling;
+
+ if (sscanf (c->name, "value%d", &i) == 0 || i >= nValue)
+ fuseRemoveInode (inode, c);
+ }
+ }
+ }
+}
+
+static void
+fuseInodeStat (CompDisplay *d,
+ FuseInode *inode,
+ struct stat *stbuf)
+{
+ stbuf->st_ino = inode->ino;
+
+ if (inode->type & DIR_MASK)
+ {
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ }
+ else
+ {
+ char *str;
+
+ if (inode->type & WRITE_MASK)
+ stbuf->st_mode = S_IFREG | 0666;
+ else
+ stbuf->st_mode = S_IFREG | 0444;
+
+ stbuf->st_nlink = 1;
+ stbuf->st_size = 0;
+
+ str = fuseGetStringFromInode (inode);
+ if (str)
+ {
+ stbuf->st_size = strlen (str);
+ free (str);
+ }
+ }
+}
+
+static Bool
+fuseInitValue (CompOptionValue *value,
+ CompOptionType type,
+ CompOptionValue *src)
+{
+ switch (type) {
+ case CompOptionTypeBool:
+ value->b = src->b;
+ break;
+ case CompOptionTypeInt:
+ value->i = src->i;
+ break;
+ case CompOptionTypeFloat:
+ value->f = src->f;
+ break;
+ case CompOptionTypeString:
+ value->s = strdup (src->s);
+ break;
+ case CompOptionTypeColor:
+ memcpy (value->c, src->c, sizeof (*src->c));
+ break;
+ case CompOptionTypeKey:
+ case CompOptionTypeButton:
+ case CompOptionTypeEdge:
+ case CompOptionTypeBell:
+ value->action = src->action;
+ break;
+ case CompOptionTypeMatch:
+ matchInit (&value->match);
+ matchCopy (&value->match, &src->match);
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static Bool
+fuseInitValueFromString (CompObject *object,
+ CompOptionValue *value,
+ CompOptionType type,
+ char *str)
+{
+ switch (type) {
+ case CompOptionTypeBool:
+ value->b = strcmp (str, "true") ? FALSE : TRUE;
+ break;
+ case CompOptionTypeInt:
+ value->i = atoi (str);
+ break;
+ case CompOptionTypeFloat:
+ value->f = strtod (str, NULL);
+ break;
+ case CompOptionTypeString:
+ value->s = strdup (str);
+ break;
+ case CompOptionTypeColor:
+ if (!stringToColor (str, value->c))
+ return FALSE;
+ break;
+ case CompOptionTypeKey:
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return FALSE;
+
+ stringToKeyAction (GET_CORE_DISPLAY (object), str, &value->action);
+ break;
+ case CompOptionTypeButton:
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return FALSE;
+
+ stringToButtonAction (GET_CORE_DISPLAY (object), str, &value->action);
+ break;
+ case CompOptionTypeEdge:
+ value->action.edgeMask = stringToEdgeMask (str);
+ break;
+ case CompOptionTypeBell:
+ value->action.bell = strcmp (str, "true") ? FALSE : TRUE;
+ break;
+ case CompOptionTypeMatch:
+ matchInit (&value->match);
+ matchAddFromString (&value->match, str);
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+fuseSetInodeOptionUsingString (FuseInode *inode,
+ char *str)
+{
+ CompOption *option;
+
+ option = fuseGetOptionFromInode (inode->parent);
+ if (option)
+ {
+ CompOptionValue value;
+ CompObject *object;
+ const char *pluginName;
+
+ if (inode->type & FUSE_INODE_TYPE_VALUE)
+ {
+ object = fuseGetObjectFromInode (inode->parent->parent);
+ if (!object)
+ return;
+
+ if (!fuseInitValueFromString (object, &value, option->type, str))
+ return;
+
+ pluginName = inode->parent->parent->parent->name;
+ }
+ else if (inode->type & FUSE_INODE_TYPE_ITEM_VALUE)
+ {
+ int i, item, nValue = option->value.list.nValue;
+
+ if (!sscanf (inode->name, "value%d", &item))
+ return;
+
+ if (item >= nValue)
+ return;
+
+ object = fuseGetObjectFromInode (inode->parent->parent->parent);
+ if (!object)
+ return;
+
+ value.list.value = malloc (sizeof (CompOptionValue) * nValue);
+ if (!value.list.value)
+ return;
+
+ value.list.type = option->value.list.type;
+ value.list.nValue = 0;
+
+ for (i = 0; i < nValue; i++)
+ {
+ if (i == item)
+ {
+ if (!fuseInitValueFromString (object,
+ &value.list.value[i],
+ value.list.type,
+ str))
+ break;
+ }
+ else
+ {
+ if (!fuseInitValue (&value.list.value[i],
+ value.list.type,
+ &option->value.list.value[i]))
+ break;
+ }
+
+ value.list.nValue++;
+ }
+
+ /* failed */
+ if (value.list.nValue < nValue)
+ {
+ compFiniOptionValue (&value, option->type);
+ return;
+ }
+
+ pluginName = inode->parent->parent->parent->parent->name;
+ }
+ else
+ {
+ return;
+ }
+
+ (*core.setOptionForPlugin) (object, pluginName, option->name, &value);
+
+ compFiniOptionValue (&value, option->type);
+ }
+}
+
+static void
+compiz_getattr (fuse_req_t req,
+ fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ CompDisplay *d = (CompDisplay *) fuse_req_userdata (req);
+ FuseInode *inode;
+
+ inode = fuseFindInode (inodes, ino, ~0);
+ if (inode)
+ {
+ struct stat stbuf;
+
+ memset (&stbuf, 0, sizeof (stbuf));
+
+ fuseInodeStat (d, inode, &stbuf);
+
+ fuse_reply_attr (req, &stbuf, 1.0);
+ }
+ else
+ {
+ fuse_reply_err (req, ENOENT);
+ }
+}
+
+static void
+compiz_setattr (fuse_req_t req,
+ fuse_ino_t ino,
+ struct stat *attr,
+ int to_set,
+ struct fuse_file_info *fi)
+{
+ CompDisplay *d = (CompDisplay *) fuse_req_userdata (req);
+ FuseInode *inode;
+
+ inode = fuseFindInode (inodes, ino, WRITE_MASK);
+ if (inode)
+ {
+ struct stat stbuf;
+
+ if ((to_set & FUSE_SET_ATTR_SIZE) != FUSE_SET_ATTR_SIZE)
+ {
+ fuse_reply_err (req, EACCES);
+ return;
+ }
+
+ if (attr->st_size != 0)
+ {
+ fuse_reply_err (req, EACCES);
+ return;
+ }
+
+ inode->flags |= FUSE_INODE_FLAG_TRUNC;
+
+ memset (&stbuf, 0, sizeof (stbuf));
+
+ fuseInodeStat (d, inode, &stbuf);
+
+ fuse_reply_attr (req, &stbuf, 1.0);
+ }
+ else
+ {
+ fuse_reply_err (req, ENOENT);
+ }
+}
+
+static void
+compiz_lookup (fuse_req_t req,
+ fuse_ino_t parent,
+ const char *name)
+{
+ CompDisplay *d = (CompDisplay *) fuse_req_userdata (req);
+ FuseInode *inode;
+ struct fuse_entry_param e;
+
+ inode = fuseFindInode (inodes, parent, DIR_MASK);
+ if (!inode)
+ {
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
+
+ if (!inode->child || !(inode->type & CONST_DIR_MASK))
+ fuseUpdateInode (d, inode);
+
+ inode = fuseLookupChild (inode, name);
+ if (!inode)
+ {
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
+
+ memset (&e, 0, sizeof (e));
+
+ e.attr_timeout = 1.0;
+ e.entry_timeout = 1.0;
+ e.ino = inode->ino;
+
+ fuseInodeStat (d, inode, &e.attr);
+
+ fuse_reply_entry (req, &e);
+}
+
+struct dirbuf {
+ char *p;
+ size_t size;
+};
+
+static void
+dirbuf_add (fuse_req_t req,
+ struct dirbuf *b,
+ const char *name,
+ fuse_ino_t ino)
+{
+ struct stat stbuf;
+ size_t oldSize = b->size;
+
+ b->size += fuse_add_direntry (req, NULL, 0, name, NULL, 0);
+ b->p = (char *) realloc (b->p, b->size);
+
+ memset (&stbuf, 0, sizeof (stbuf));
+
+ stbuf.st_ino = ino;
+
+ fuse_add_direntry (req, b->p + oldSize, b->size - oldSize, name, &stbuf,
+ b->size);
+}
+
+static int
+reply_buf_limited (fuse_req_t req,
+ const char *buf,
+ size_t bufsize,
+ off_t off,
+ size_t maxsize)
+{
+ if (off < bufsize)
+ return fuse_reply_buf (req, buf + off, MIN (bufsize - off, maxsize));
+ else
+ return fuse_reply_buf (req, NULL, 0);
+}
+
+static void
+compiz_readdir (fuse_req_t req,
+ fuse_ino_t ino,
+ size_t size,
+ off_t off,
+ struct fuse_file_info *fi)
+{
+ CompDisplay *d = (CompDisplay *) fuse_req_userdata (req);
+ FuseInode *inode, *c;
+ struct dirbuf b;
+
+ inode = fuseFindInode (inodes, ino, DIR_MASK);
+ if (!inode)
+ {
+ fuse_reply_err (req, ENOTDIR);
+ return;
+ }
+
+ memset (&b, 0, sizeof (b));
+
+ dirbuf_add (req, &b, ".", ino);
+ dirbuf_add (req, &b, "..", inode->parent ? inode->parent->ino : ino);
+
+ if (!inode->child || !(inode->type & CONST_DIR_MASK))
+ fuseUpdateInode (d, inode);
+
+ for (c = inode->child; c; c = c->sibling)
+ dirbuf_add (req, &b, c->name, c->ino);
+
+ reply_buf_limited (req, b.p, b.size, off, size);
+
+ free (b.p);
+}
+
+static void
+compiz_open (fuse_req_t req,
+ fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ FuseInode *inode;
+
+ inode = fuseFindInode (inodes, ino, ~0);
+ if (!inode)
+ {
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
+
+ fi->fh = 0;
+
+ if (inode->type & DIR_MASK)
+ {
+ fuse_reply_err (req, EISDIR);
+ }
+ else if (inode->type & WRITE_MASK)
+ {
+ if ((fi->flags & 3) != O_RDONLY)
+ {
+ char *data;
+
+ if (fi->flags & O_TRUNC)
+ data = strdup ("");
+ else
+ data = fuseGetStringFromInode (inode);
+
+ if (data)
+ {
+ FuseWriteBuffer *wb;
+
+ wb = malloc (sizeof (FuseWriteBuffer));
+ if (wb)
+ {
+ wb->data = data;
+ wb->size = strlen (wb->data);
+ wb->dirty = TRUE;
+
+ fi->fh = (unsigned long) wb;
+ }
+ else
+ {
+ free (data);
+ }
+ }
+ }
+
+ fuse_reply_open (req, fi);
+ }
+ else if ((fi->flags & 3) != O_RDONLY)
+ {
+ fuse_reply_err (req, EACCES);
+ }
+ else
+ {
+ fuse_reply_open (req, fi);
+ }
+}
+
+static void
+compiz_read (fuse_req_t req,
+ fuse_ino_t ino,
+ size_t size,
+ off_t off,
+ struct fuse_file_info *fi)
+{
+ FuseInode *inode;
+ char *str = NULL;
+
+ inode = fuseFindInode (inodes, ino, ~0);
+ if (inode)
+ str = fuseGetStringFromInode (inode);
+
+ if (str)
+ {
+ reply_buf_limited (req, str, strlen (str), off, size);
+ free (str);
+ }
+ else
+ {
+ reply_buf_limited (req, NULL, 0, off, size);
+ }
+}
+
+static void
+compiz_write (fuse_req_t req,
+ fuse_ino_t ino,
+ const char *buf,
+ size_t size,
+ off_t off,
+ struct fuse_file_info *fi)
+{
+ FuseInode *inode;
+
+ inode = fuseFindInode (inodes, ino, WRITE_MASK);
+ if (inode && fi->fh)
+ {
+ FuseWriteBuffer *wb = (FuseWriteBuffer *) (uintptr_t) fi->fh;
+
+ if (off + size > wb->size)
+ {
+ char *data;
+
+ data = realloc (wb->data, off + size + 1);
+ if (!data)
+ {
+ fuse_reply_err (req, ENOBUFS);
+ return;
+ }
+
+ data[off + size] = '\0';
+
+ wb->data = data;
+ wb->size = off + size;
+ }
+
+ memcpy (wb->data + off, buf, size);
+
+ wb->dirty = TRUE;
+
+ fuse_reply_write (req, size);
+ }
+ else
+ {
+ fuse_reply_err (req, ENOENT);
+ }
+}
+
+static void
+compiz_release (fuse_req_t req,
+ fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ if (fi->fh)
+ {
+ FuseWriteBuffer *wb = (FuseWriteBuffer *) (uintptr_t) fi->fh;
+ FuseInode *inode;
+
+ inode = fuseFindInode (inodes, ino, WRITE_MASK);
+ if (inode && wb->dirty)
+ {
+ fuseSetInodeOptionUsingString (inode, wb->data);
+
+ inode->flags &= ~FUSE_INODE_FLAG_TRUNC;
+ }
+
+ free (wb->data);
+ free (wb);
+ }
+
+ fuse_reply_err (req, 0);
+}
+
+static void
+compiz_fsync (fuse_req_t req,
+ fuse_ino_t ino,
+ int datasync,
+ struct fuse_file_info *fi)
+{
+ if (fi->fh)
+ {
+ FuseWriteBuffer *wb = (FuseWriteBuffer *) (uintptr_t) fi->fh;
+ FuseInode *inode;
+
+ inode = fuseFindInode (inodes, ino, WRITE_MASK);
+ if (inode && wb->dirty)
+ {
+ fuseSetInodeOptionUsingString (inode, wb->data);
+
+ inode->flags &= ~FUSE_INODE_FLAG_TRUNC;
+
+ wb->dirty = FALSE;
+ }
+ }
+
+ fuse_reply_err (req, 0);
+}
+
+static struct fuse_lowlevel_ops compiz_ll_oper = {
+ .lookup = compiz_lookup,
+ .getattr = compiz_getattr,
+ .setattr = compiz_setattr,
+ .readdir = compiz_readdir,
+ .open = compiz_open,
+ .read = compiz_read,
+ .write = compiz_write,
+ .release = compiz_release,
+ .fsync = compiz_fsync
+};
+
+static void
+fuseUnmount (CompDisplay *d)
+{
+ FUSE_DISPLAY (d);
+
+ if (fd->watchFdHandle)
+ {
+ compRemoveWatchFd (fd->watchFdHandle);
+ fd->watchFdHandle = 0;
+ }
+
+ if (fd->mountPoint)
+ {
+ /* unmount will destroy the channel */
+ fuse_unmount (fd->mountPoint, fd->channel);
+ free (fd->mountPoint);
+ fd->mountPoint = NULL;
+ fd->channel = NULL;
+ }
+
+ if (fd->buffer)
+ {
+ free (fd->buffer);
+ fd->buffer = NULL;
+ }
+}
+
+static Bool
+fuseProcessMessages (void *data)
+{
+ CompDisplay *d = (CompDisplay *) data;
+ struct fuse_chan *channel;
+ size_t bufferSize;
+ int res = 0;
+
+ FUSE_DISPLAY (d);
+
+ channel = fuse_session_next_chan (fd->session, NULL);
+ bufferSize = fuse_chan_bufsize (channel);
+
+ if (fuse_session_exited (fd->session))
+ return FALSE;
+
+ for (;;)
+ {
+ struct fuse_chan *tmpch = channel;
+
+ res = fuse_chan_recv (&tmpch, fd->buffer, bufferSize);
+ if (res == -EINTR)
+ continue;
+
+ if (res > 0)
+ fuse_session_process (fd->session, fd->buffer, res, tmpch);
+
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+fuseMount (CompDisplay *d)
+{
+ char *mountPoint;
+ struct fuse_args args = FUSE_ARGS_INIT (0, NULL);
+
+ FUSE_DISPLAY (d);
+
+ mountPoint = strdup (fd->opt[FUSE_DISPLAY_OPTION_MOUNT_POINT].value.s);
+ if (!mountPoint)
+ return;
+
+ fuse_opt_add_arg (&args, "");
+ fuse_opt_add_arg (&args, "-o");
+ fuse_opt_add_arg (&args, "allow_root");
+
+ fd->channel = fuse_mount (mountPoint, &args);
+ if (!fd->channel)
+ {
+ fuse_opt_free_args (&args);
+ free (mountPoint);
+ return;
+ }
+
+ fuse_opt_free_args (&args);
+
+ fd->buffer = malloc (fuse_chan_bufsize (fd->channel));
+ if (!fd->buffer)
+ {
+ fuse_unmount (mountPoint, fd->channel);
+ free (mountPoint);
+ fd->channel = NULL;
+ return;
+ }
+
+ fd->mountPoint = mountPoint;
+
+ fuse_session_add_chan (fd->session, fd->channel);
+
+ fd->watchFdHandle = compAddWatchFd (fuse_chan_fd (fd->channel),
+ POLLIN | POLLPRI | POLLHUP | POLLERR,
+ fuseProcessMessages,
+ d);
+}
+
+static CompOption *
+fuseGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ FUSE_DISPLAY (display);
+
+ *count = NUM_OPTIONS (fd);
+ return fd->opt;
+}
+
+static Bool
+fuseSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ FUSE_DISPLAY (display);
+
+ o = compFindOption (fd->opt, NUM_OPTIONS (fd), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case FUSE_DISPLAY_OPTION_MOUNT_POINT:
+ if (compSetStringOption (o, value))
+ {
+ fuseUnmount (display);
+ fuseMount (display);
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static const CompMetadataOptionInfo fuseDisplayOptionInfo[] = {
+ { "mount_point", "string", 0, 0, 0 }
+};
+
+static Bool
+fuseInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ FuseDisplay *fd;
+ struct sigaction sa;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ memset (&sa, 0, sizeof (struct sigaction));
+
+ sa.sa_handler = SIG_IGN;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ if (sigaction (SIGPIPE, &sa, NULL) == -1)
+ return FALSE;
+
+ fd = malloc (sizeof (FuseDisplay));
+ if (!fd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &fuseMetadata,
+ fuseDisplayOptionInfo,
+ fd->opt,
+ FUSE_DISPLAY_OPTION_NUM))
+ {
+ free (fd);
+ return FALSE;
+ }
+
+ fd->session = fuse_lowlevel_new (NULL,
+ &compiz_ll_oper, sizeof (compiz_ll_oper),
+ (void *) d);
+ if (!fd->session)
+ {
+ compFiniDisplayOptions (d, fd->opt, FUSE_DISPLAY_OPTION_NUM);
+ free (fd);
+ return FALSE;
+ }
+
+ fd->watchFdHandle = 0;
+ fd->channel = NULL;
+ fd->buffer = NULL;
+ fd->mountPoint = NULL;
+
+ d->base.privates[displayPrivateIndex].ptr = fd;
+
+ fuseMount (d);
+
+ return TRUE;
+}
+
+static void
+fuseFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ FUSE_DISPLAY (d);
+
+ fuseUnmount (d);
+
+ fuse_session_destroy (fd->session);
+
+ compFiniDisplayOptions (d, fd->opt, FUSE_DISPLAY_OPTION_NUM);
+
+ free (fd);
+}
+
+static Bool
+fuseInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&fuseMetadata,
+ p->vTable->name,
+ fuseDisplayOptionInfo,
+ FUSE_DISPLAY_OPTION_NUM,
+ 0, 0))
+ return FALSE;
+
+ inodes = fuseAddInode (NULL, FUSE_INODE_TYPE_ROOT, ".");
+ if (!inodes)
+ {
+ compFiniMetadata (&fuseMetadata);
+ return FALSE;
+ }
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ fuseRemoveInode (NULL, inodes);
+ compFiniMetadata (&fuseMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&fuseMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static CompBool
+fuseInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) fuseInitDisplay
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+fuseFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) fuseFiniDisplay
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+fuseGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) fuseGetDisplayOptions
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) (*count = 0), (plugin, object, count));
+}
+
+static CompBool
+fuseSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) fuseSetDisplayOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static void
+fuseFini (CompPlugin *p)
+{
+ fuseRemoveInode (NULL, inodes);
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&fuseMetadata);
+}
+
+static CompMetadata *
+fuseGetMetadata (CompPlugin *plugin)
+{
+ return &fuseMetadata;
+}
+
+CompPluginVTable fuseVTable = {
+ "fs",
+ fuseGetMetadata,
+ fuseInit,
+ fuseFini,
+ fuseInitObject,
+ fuseFiniObject,
+ fuseGetObjectOptions,
+ fuseSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &fuseVTable;
+}
diff --git a/plugins/gconf.c b/plugins/gconf.c
new file mode 100644
index 0000000..b34b10a
--- /dev/null
+++ b/plugins/gconf.c
@@ -0,0 +1,849 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gconf/gconf-client.h>
+
+#include <compiz-core.h>
+
+static CompMetadata gconfMetadata;
+
+#define APP_NAME "compiz"
+
+/* From gconf-internal.h. Bleah. */
+int gconf_value_compare (const GConfValue *value_a,
+ const GConfValue *value_b);
+
+static int corePrivateIndex;
+
+typedef struct _GConfCore {
+ GConfClient *client;
+ guint cnxn;
+
+ CompTimeoutHandle reloadHandle;
+
+ InitPluginForObjectProc initPluginForObject;
+ SetOptionForPluginProc setOptionForPlugin;
+} GConfCore;
+
+#define GET_GCONF_CORE(c) \
+ ((GConfCore *) (c)->base.privates[corePrivateIndex].ptr)
+
+#define GCONF_CORE(c) \
+ GConfCore *gc = GET_GCONF_CORE (c)
+
+
+static gchar *
+gconfGetKey (CompObject *object,
+ const gchar *plugin,
+ const gchar *option)
+{
+ const gchar *type;
+ gchar *key, *name, *objectName;
+
+ type = compObjectTypeName (object->type);
+ if (strcmp (type, "display") == 0)
+ type = "allscreens";
+
+ name = compObjectName (object);
+ if (name)
+ {
+ objectName = g_strdup_printf ("%s%s", type, name);
+ free (name);
+ }
+ else
+ objectName = g_strdup (type);
+
+ if (strcmp (plugin, "core") == 0)
+ key = g_strjoin ("/", "/apps", APP_NAME, "general", objectName,
+ "options", option, NULL);
+ else
+ key = g_strjoin ("/", "/apps", APP_NAME, "plugins", plugin, objectName,
+ "options", option, NULL);
+
+ g_free (objectName);
+
+ return key;
+}
+
+static GConfValueType
+gconfTypeFromCompType (CompOptionType type)
+{
+ switch (type) {
+ case CompOptionTypeBool:
+ case CompOptionTypeBell:
+ return GCONF_VALUE_BOOL;
+ case CompOptionTypeInt:
+ return GCONF_VALUE_INT;
+ case CompOptionTypeFloat:
+ return GCONF_VALUE_FLOAT;
+ case CompOptionTypeString:
+ case CompOptionTypeColor:
+ case CompOptionTypeKey:
+ case CompOptionTypeButton:
+ case CompOptionTypeEdge:
+ case CompOptionTypeMatch:
+ return GCONF_VALUE_STRING;
+ case CompOptionTypeList:
+ return GCONF_VALUE_LIST;
+ default:
+ break;
+ }
+
+ return GCONF_VALUE_INVALID;
+}
+
+static void
+gconfSetValue (CompObject *object,
+ CompOptionValue *value,
+ CompOptionType type,
+ GConfValue *gvalue)
+{
+ switch (type) {
+ case CompOptionTypeBool:
+ gconf_value_set_bool (gvalue, value->b);
+ break;
+ case CompOptionTypeInt:
+ gconf_value_set_int (gvalue, value->i);
+ break;
+ case CompOptionTypeFloat:
+ gconf_value_set_float (gvalue, value->f);
+ break;
+ case CompOptionTypeString:
+ gconf_value_set_string (gvalue, value->s);
+ break;
+ case CompOptionTypeColor: {
+ gchar *color;
+
+ color = colorToString (value->c);
+ gconf_value_set_string (gvalue, color);
+
+ free (color);
+ } break;
+ case CompOptionTypeKey: {
+ gchar *action;
+
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return;
+
+ action = keyActionToString (GET_CORE_DISPLAY (object), &value->action);
+ gconf_value_set_string (gvalue, action);
+
+ free (action);
+ } break;
+ case CompOptionTypeButton: {
+ gchar *action;
+
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return;
+
+ action = buttonActionToString (GET_CORE_DISPLAY (object),
+ &value->action);
+ gconf_value_set_string (gvalue, action);
+
+ free (action);
+ } break;
+ case CompOptionTypeEdge: {
+ gchar *edge;
+
+ edge = edgeMaskToString (value->action.edgeMask);
+ gconf_value_set_string (gvalue, edge);
+
+ free (edge);
+ } break;
+ case CompOptionTypeBell:
+ gconf_value_set_bool (gvalue, value->action.bell);
+ break;
+ case CompOptionTypeMatch: {
+ gchar *match;
+
+ match = matchToString (&value->match);
+ gconf_value_set_string (gvalue, match);
+
+ free (match);
+ } break;
+ default:
+ break;
+ }
+}
+
+static void
+gconfSetOption (CompObject *object,
+ CompOption *o,
+ const gchar *plugin)
+{
+ GConfValueType type = gconfTypeFromCompType (o->type);
+ GConfValue *gvalue, *existingValue = NULL;
+ gchar *key;
+
+ GCONF_CORE (&core);
+
+ if (type == GCONF_VALUE_INVALID)
+ return;
+
+ key = gconfGetKey (object, plugin, o->name);
+
+ existingValue = gconf_client_get (gc->client, key, NULL);
+ gvalue = gconf_value_new (type);
+
+ if (o->type == CompOptionTypeList)
+ {
+ GSList *node, *list = NULL;
+ GConfValue *gv;
+ int i;
+
+ type = gconfTypeFromCompType (o->value.list.type);
+
+ for (i = 0; i < o->value.list.nValue; i++)
+ {
+ gv = gconf_value_new (type);
+ gconfSetValue (object, &o->value.list.value[i],
+ o->value.list.type, gv);
+ list = g_slist_append (list, gv);
+ }
+
+ gconf_value_set_list_type (gvalue, type);
+ gconf_value_set_list (gvalue, list);
+
+ if (!existingValue || gconf_value_compare (existingValue, gvalue))
+ gconf_client_set (gc->client, key, gvalue, NULL);
+
+ for (node = list; node; node = node->next)
+ gconf_value_free ((GConfValue *) node->data);
+
+ g_slist_free (list);
+ }
+ else
+ {
+ gconfSetValue (object, &o->value, o->type, gvalue);
+
+ if (!existingValue || gconf_value_compare (existingValue, gvalue))
+ gconf_client_set (gc->client, key, gvalue, NULL);
+ }
+
+ gconf_value_free (gvalue);
+
+ if (existingValue)
+ gconf_value_free (existingValue);
+
+ g_free (key);
+}
+
+static Bool
+gconfGetValue (CompObject *object,
+ CompOptionValue *value,
+ CompOptionType type,
+ GConfValue *gvalue)
+
+{
+ if (type == CompOptionTypeBool &&
+ gvalue->type == GCONF_VALUE_BOOL)
+ {
+ value->b = gconf_value_get_bool (gvalue);
+ return TRUE;
+ }
+ else if (type == CompOptionTypeInt &&
+ gvalue->type == GCONF_VALUE_INT)
+ {
+ value->i = gconf_value_get_int (gvalue);
+ return TRUE;
+ }
+ else if (type == CompOptionTypeFloat &&
+ gvalue->type == GCONF_VALUE_FLOAT)
+ {
+ value->f = gconf_value_get_float (gvalue);
+ return TRUE;
+ }
+ else if (type == CompOptionTypeString &&
+ gvalue->type == GCONF_VALUE_STRING)
+ {
+ const char *str;
+
+ str = gconf_value_get_string (gvalue);
+ if (str)
+ {
+ value->s = strdup (str);
+ if (value->s)
+ return TRUE;
+ }
+ }
+ else if (type == CompOptionTypeColor &&
+ gvalue->type == GCONF_VALUE_STRING)
+ {
+ const gchar *color;
+
+ color = gconf_value_get_string (gvalue);
+
+ if (stringToColor (color, value->c))
+ return TRUE;
+ }
+ else if (type == CompOptionTypeKey &&
+ gvalue->type == GCONF_VALUE_STRING)
+ {
+ const gchar *action;
+
+ action = gconf_value_get_string (gvalue);
+
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return FALSE;
+
+ stringToKeyAction (GET_CORE_DISPLAY (object), action, &value->action);
+ return TRUE;
+ }
+ else if (type == CompOptionTypeButton &&
+ gvalue->type == GCONF_VALUE_STRING)
+ {
+ const gchar *action;
+
+ action = gconf_value_get_string (gvalue);
+
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return FALSE;
+
+ stringToButtonAction (GET_CORE_DISPLAY (object), action,
+ &value->action);
+ return TRUE;
+ }
+ else if (type == CompOptionTypeEdge &&
+ gvalue->type == GCONF_VALUE_STRING)
+ {
+ const gchar *edge;
+
+ edge = gconf_value_get_string (gvalue);
+
+ value->action.edgeMask = stringToEdgeMask (edge);
+ return TRUE;
+ }
+ else if (type == CompOptionTypeBell &&
+ gvalue->type == GCONF_VALUE_BOOL)
+ {
+ value->action.bell = gconf_value_get_bool (gvalue);
+ return TRUE;
+ }
+ else if (type == CompOptionTypeMatch &&
+ gvalue->type == GCONF_VALUE_STRING)
+ {
+ const gchar *match;
+
+ match = gconf_value_get_string (gvalue);
+
+ matchInit (&value->match);
+ matchAddFromString (&value->match, match);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static Bool
+gconfReadOptionValue (CompObject *object,
+ GConfEntry *entry,
+ CompOption *o,
+ CompOptionValue *value)
+{
+ GConfValue *gvalue;
+
+ gvalue = gconf_entry_get_value (entry);
+ if (!gvalue)
+ return FALSE;
+
+ compInitOptionValue (value);
+
+ if (o->type == CompOptionTypeList &&
+ gvalue->type == GCONF_VALUE_LIST)
+ {
+ GConfValueType type;
+ GSList *list;
+ int i, n;
+
+ type = gconf_value_get_list_type (gvalue);
+ if (gconfTypeFromCompType (o->value.list.type) != type)
+ return FALSE;
+
+ list = gconf_value_get_list (gvalue);
+ n = g_slist_length (list);
+
+ value->list.value = NULL;
+ value->list.nValue = 0;
+ value->list.type = o->value.list.type;
+
+ if (n)
+ {
+ value->list.value = malloc (sizeof (CompOptionValue) * n);
+ if (value->list.value)
+ {
+ for (i = 0; i < n; i++)
+ {
+ if (!gconfGetValue (object,
+ &value->list.value[i],
+ o->value.list.type,
+ (GConfValue *) list->data))
+ break;
+
+ value->list.nValue++;
+
+ list = g_slist_next (list);
+ }
+
+ if (value->list.nValue != n)
+ {
+ compFiniOptionValue (value, o->type);
+ return FALSE;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!gconfGetValue (object, value, o->type, gvalue))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gconfGetOption (CompObject *object,
+ CompOption *o,
+ const char *plugin)
+{
+ GConfEntry *entry;
+ gchar *key;
+
+ GCONF_CORE (&core);
+
+ key = gconfGetKey (object, plugin, o->name);
+
+ entry = gconf_client_get_entry (gc->client, key, NULL, TRUE, NULL);
+ if (entry)
+ {
+ CompOptionValue value;
+
+ if (gconfReadOptionValue (object, entry, o, &value))
+ {
+ (*core.setOptionForPlugin) (object, plugin, o->name, &value);
+ compFiniOptionValue (&value, o->type);
+ }
+ else
+ {
+ gconfSetOption (object, o, plugin);
+ }
+
+ gconf_entry_free (entry);
+ }
+
+ g_free (key);
+}
+
+static CompBool
+gconfReloadObjectTree (CompObject *object,
+ void *closure);
+
+static CompBool
+gconfReloadObjectsWithType (CompObjectType type,
+ CompObject *parent,
+ void *closure)
+{
+ compObjectForEach (parent, type, gconfReloadObjectTree, closure);
+
+ return TRUE;
+}
+
+static CompBool
+gconfReloadObjectTree (CompObject *object,
+ void *closure)
+{
+ CompPlugin *p = (CompPlugin *) closure;
+ CompOption *option;
+ int nOption;
+
+ option = (*p->vTable->getObjectOptions) (p, object, &nOption);
+ while (nOption--)
+ gconfGetOption (object, option++, p->vTable->name);
+
+ compObjectForEachType (object, gconfReloadObjectsWithType, closure);
+
+ return TRUE;
+}
+
+static Bool
+gconfReload (void *closure)
+{
+ CompPlugin *p;
+
+ GCONF_CORE (&core);
+
+ for (p = getPlugins (); p; p = p->next)
+ {
+ if (!p->vTable->getObjectOptions)
+ continue;
+
+ gconfReloadObjectTree (&core.base, (void *) p);
+ }
+
+ gc->reloadHandle = 0;
+
+ return FALSE;
+}
+
+static Bool
+gconfSetOptionForPlugin (CompObject *object,
+ const char *plugin,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompBool status;
+
+ GCONF_CORE (&core);
+
+ UNWRAP (gc, &core, setOptionForPlugin);
+ status = (*core.setOptionForPlugin) (object, plugin, name, value);
+ WRAP (gc, &core, setOptionForPlugin, gconfSetOptionForPlugin);
+
+ if (status && !gc->reloadHandle)
+ {
+ CompPlugin *p;
+
+ p = findActivePlugin (plugin);
+ if (p && p->vTable->getObjectOptions)
+ {
+ CompOption *option;
+ int nOption;
+
+ option = (*p->vTable->getObjectOptions) (p, object, &nOption);
+ option = compFindOption (option, nOption, name, 0);
+ if (option)
+ gconfSetOption (object, option, p->vTable->name);
+ }
+ }
+
+ return status;
+}
+
+static CompBool
+gconfInitPluginForObject (CompPlugin *p,
+ CompObject *o)
+{
+ CompBool status;
+
+ GCONF_CORE (&core);
+
+ UNWRAP (gc, &core, initPluginForObject);
+ status = (*core.initPluginForObject) (p, o);
+ WRAP (gc, &core, initPluginForObject, gconfInitPluginForObject);
+
+ if (status && p->vTable->getObjectOptions)
+ {
+ CompOption *option;
+ int nOption;
+
+ option = (*p->vTable->getObjectOptions) (p, o, &nOption);
+ while (nOption--)
+ gconfGetOption (o, option++, p->vTable->name);
+ }
+
+ return status;
+}
+
+/* MULTIDPYERROR: only works with one or less displays present */
+static void
+gconfKeyChanged (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ CompPlugin *plugin;
+ CompObject *object;
+ CompOption *option = NULL;
+ int nOption = 0;
+ gchar **token;
+ int objectIndex = 4;
+
+ token = g_strsplit (entry->key, "/", 8);
+
+ if (g_strv_length (token) < 7)
+ {
+ g_strfreev (token);
+ return;
+ }
+
+ if (strcmp (token[0], "") != 0 ||
+ strcmp (token[1], "apps") != 0 ||
+ strcmp (token[2], APP_NAME) != 0)
+ {
+ g_strfreev (token);
+ return;
+ }
+
+ if (strcmp (token[3], "general") == 0)
+ {
+ plugin = findActivePlugin ("core");
+ }
+ else
+ {
+ if (strcmp (token[3], "plugins") != 0 || g_strv_length (token) < 8)
+ {
+ g_strfreev (token);
+ return;
+ }
+
+ objectIndex = 5;
+ plugin = findActivePlugin (token[4]);
+ }
+
+ if (!plugin)
+ {
+ g_strfreev (token);
+ return;
+ }
+
+ object = compObjectFind (&core.base, COMP_OBJECT_TYPE_DISPLAY, NULL);
+ if (!object)
+ {
+ g_strfreev (token);
+ return;
+ }
+
+ if (strncmp (token[objectIndex], "screen", 6) == 0)
+ {
+ object = compObjectFind (object, COMP_OBJECT_TYPE_SCREEN,
+ token[objectIndex] + 6);
+ if (!object)
+ {
+ g_strfreev (token);
+ return;
+ }
+ }
+ else if (strcmp (token[objectIndex], "allscreens") != 0)
+ {
+ g_strfreev (token);
+ return;
+ }
+
+ if (strcmp (token[objectIndex + 1], "options") != 0)
+ {
+ g_strfreev (token);
+ return;
+ }
+
+ if (plugin->vTable->getObjectOptions)
+ option = (*plugin->vTable->getObjectOptions) (plugin, object,
+ &nOption);
+
+ option = compFindOption (option, nOption, token[objectIndex + 2], 0);
+ if (option)
+ {
+ CompOptionValue value;
+
+ if (gconfReadOptionValue (object, entry, option, &value))
+ {
+ (*core.setOptionForPlugin) (object,
+ plugin->vTable->name,
+ option->name,
+ &value);
+
+ compFiniOptionValue (&value, option->type);
+ }
+ }
+
+ g_strfreev (token);
+}
+
+static void
+gconfSendGLibNotify (CompScreen *s)
+{
+ Display *dpy = s->display->display;
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.display = dpy;
+ xev.xclient.format = 32;
+
+ xev.xclient.message_type = XInternAtom (dpy, "_COMPIZ_GLIB_NOTIFY", 0);
+ xev.xclient.window = s->root;
+
+ memset (xev.xclient.data.l, 0, sizeof (xev.xclient.data.l));
+
+ XSendEvent (dpy,
+ s->root,
+ FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &xev);
+}
+
+static Bool
+gconfInitCore (CompPlugin *p,
+ CompCore *c)
+{
+ GConfCore *gc;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ gc = malloc (sizeof (GConfCore));
+ if (!gc)
+ return FALSE;
+
+ g_type_init ();
+
+ gc->client = gconf_client_get_default ();
+
+ gconf_client_add_dir (gc->client, "/apps/" APP_NAME,
+ GCONF_CLIENT_PRELOAD_NONE, NULL);
+
+ gc->reloadHandle = compAddTimeout (0, 0, gconfReload, 0);
+
+ gc->cnxn = gconf_client_notify_add (gc->client, "/apps/" APP_NAME,
+ gconfKeyChanged, c, NULL, NULL);
+
+ WRAP (gc, c, initPluginForObject, gconfInitPluginForObject);
+ WRAP (gc, c, setOptionForPlugin, gconfSetOptionForPlugin);
+
+ c->base.privates[corePrivateIndex].ptr = gc;
+
+ return TRUE;
+}
+
+static void
+gconfFiniCore (CompPlugin *p,
+ CompCore *c)
+{
+ GCONF_CORE (c);
+
+ UNWRAP (gc, c, initPluginForObject);
+ UNWRAP (gc, c, setOptionForPlugin);
+
+ if (gc->reloadHandle)
+ compRemoveTimeout (gc->reloadHandle);
+
+ if (gc->cnxn)
+ gconf_client_notify_remove (gc->client, gc->cnxn);
+
+ gconf_client_remove_dir (gc->client, "/apps/" APP_NAME, NULL);
+ gconf_client_clear_cache (gc->client);
+
+ free (gc);
+}
+
+static Bool
+gconfInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ gconfSendGLibNotify (s);
+
+ return TRUE;
+}
+
+static CompBool
+gconfInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) gconfInitCore,
+ (InitPluginObjectProc) 0, /* InitDisplay */
+ (InitPluginObjectProc) gconfInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+gconfFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) gconfFiniCore
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static Bool
+gconfInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&gconfMetadata, p->vTable->name,
+ 0, 0, 0, 0))
+ return FALSE;
+
+ corePrivateIndex = allocateCorePrivateIndex ();
+ if (corePrivateIndex < 0)
+ {
+ compFiniMetadata (&gconfMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&gconfMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+gconfFini (CompPlugin *p)
+{
+ freeCorePrivateIndex (corePrivateIndex);
+ compFiniMetadata (&gconfMetadata);
+}
+
+static CompMetadata *
+gconfGetMetadata (CompPlugin *plugin)
+{
+ return &gconfMetadata;
+}
+
+CompPluginVTable gconfVTable = {
+ "gconf",
+ gconfGetMetadata,
+ gconfInit,
+ gconfFini,
+ gconfInitObject,
+ gconfFiniObject,
+ 0, /* GetObjectOptions */
+ 0 /* SetObjectOption */
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &gconfVTable;
+}
diff --git a/plugins/glib.c b/plugins/glib.c
new file mode 100644
index 0000000..2ac0069
--- /dev/null
+++ b/plugins/glib.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright © 2007 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <glib.h>
+
+#include <compiz-core.h>
+
+static CompMetadata glibMetadata;
+
+static int displayPrivateIndex;
+
+typedef struct _GLibWatch {
+ CompWatchFdHandle handle;
+ int index;
+ CompDisplay *display;
+} GLibWatch;
+
+typedef struct _GConfDisplay {
+ HandleEventProc handleEvent;
+ CompTimeoutHandle timeoutHandle;
+ gint maxPriority;
+ GPollFD *fds;
+ gint fdsSize;
+ gint nFds;
+ GLibWatch *watch;
+ Atom notifyAtom;
+} GLibDisplay;
+
+#define GET_GLIB_DISPLAY(d) \
+ ((GLibDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define GLIB_DISPLAY(d) \
+ GLibDisplay *gd = GET_GLIB_DISPLAY (d)
+
+static void
+glibDispatch (CompDisplay *display,
+ GMainContext *context)
+{
+ int i;
+
+ GLIB_DISPLAY (display);
+
+ g_main_context_check (context, gd->maxPriority, gd->fds, gd->nFds);
+ g_main_context_dispatch (context);
+
+ for (i = 0; i < gd->nFds; i++)
+ compRemoveWatchFd (gd->watch[i].handle);
+}
+
+static void
+glibPrepare (CompDisplay *display,
+ GMainContext *context);
+
+static Bool
+glibDispatchAndPrepare (void *closure)
+{
+ CompDisplay *display = (CompDisplay *) closure;
+ GMainContext *context = g_main_context_default ();
+
+ glibDispatch (display, context);
+ glibPrepare (display, context);
+
+ return FALSE;
+}
+
+static void
+glibWakeup (CompDisplay *display)
+{
+ GLIB_DISPLAY (display);
+
+ if (gd->timeoutHandle)
+ {
+ compRemoveTimeout (gd->timeoutHandle);
+ compAddTimeout (0, 0, glibDispatchAndPrepare, (void *) display);
+
+ gd->timeoutHandle = 0;
+ }
+}
+
+static Bool
+glibCollectEvents (void *closure)
+{
+ GLibWatch *watch = (GLibWatch *) closure;
+ CompDisplay *display = watch->display;
+
+ GLIB_DISPLAY (display);
+
+ gd->fds[watch->index].revents |= compWatchFdEvents (watch->handle);
+
+ glibWakeup (display);
+
+ return TRUE;
+}
+
+static void
+glibPrepare (CompDisplay *display,
+ GMainContext *context)
+{
+ int nFds = 0;
+ int timeout = -1;
+ int i;
+
+ GLIB_DISPLAY (display);
+
+ g_main_context_prepare (context, &gd->maxPriority);
+
+ do
+ {
+ if (nFds > gd->fdsSize)
+ {
+ if (gd->fds)
+ free (gd->fds);
+
+ gd->fds = malloc ((sizeof (GPollFD) + sizeof (GLibWatch)) * nFds);
+ if (!gd->fds)
+ {
+ nFds = 0;
+ break;
+ }
+
+ gd->watch = (GLibWatch *) (gd->fds + nFds);
+ gd->fdsSize = nFds;
+ }
+
+ nFds = g_main_context_query (context,
+ gd->maxPriority,
+ &timeout,
+ gd->fds,
+ gd->fdsSize);
+ } while (nFds > gd->fdsSize);
+
+ if (timeout < 0)
+ timeout = INT_MAX;
+
+ for (i = 0; i < nFds; i++)
+ {
+ gd->watch[i].display = display;
+ gd->watch[i].index = i;
+ gd->watch[i].handle = compAddWatchFd (gd->fds[i].fd,
+ gd->fds[i].events,
+ glibCollectEvents,
+ &gd->watch[i]);
+ }
+
+ gd->nFds = nFds;
+ gd->timeoutHandle =
+ compAddTimeout (timeout, timeout, glibDispatchAndPrepare, display);
+}
+
+static void
+glibHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ GLIB_DISPLAY (d);
+
+ if (event->type == ClientMessage)
+ {
+ if (event->xclient.message_type == gd->notifyAtom)
+ glibWakeup (d);
+ }
+
+ UNWRAP (gd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (gd, d, handleEvent, glibHandleEvent);
+}
+
+static Bool
+glibInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ GLibDisplay *gd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ gd = malloc (sizeof (GLibDisplay));
+ if (!gd)
+ return FALSE;
+
+ gd->fds = NULL;
+ gd->fdsSize = 0;
+ gd->timeoutHandle = 0;
+ gd->notifyAtom = XInternAtom (d->display, "_COMPIZ_GLIB_NOTIFY", 0);
+
+ WRAP (gd, d, handleEvent, glibHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = gd;
+
+ glibPrepare (d, g_main_context_default ());
+
+ return TRUE;
+}
+
+static void
+glibFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ GLIB_DISPLAY (d);
+
+ if (gd->timeoutHandle)
+ compRemoveTimeout (gd->timeoutHandle);
+
+ glibDispatch (d, g_main_context_default ());
+
+ UNWRAP (gd, d, handleEvent);
+
+ if (gd->fds)
+ free (gd->fds);
+
+ free (gd);
+}
+
+static CompBool
+glibInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) glibInitDisplay
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+glibFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) glibFiniDisplay
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static Bool
+glibInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&glibMetadata, p->vTable->name,
+ 0, 0, 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&glibMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&glibMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+glibFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&glibMetadata);
+}
+
+static CompMetadata *
+glibGetMetadata (CompPlugin *plugin)
+{
+ return &glibMetadata;
+}
+
+CompPluginVTable glibVTable = {
+ "glib",
+ glibGetMetadata,
+ glibInit,
+ glibFini,
+ glibInitObject,
+ glibFiniObject,
+ 0, /* GetObjectOptions */
+ 0 /* SetObjectOption */
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &glibVTable;
+}
diff --git a/plugins/gnomecompat.c b/plugins/gnomecompat.c
new file mode 100644
index 0000000..5f4b8c2
--- /dev/null
+++ b/plugins/gnomecompat.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright © 2009 Danny Baumann
+ *
+ * 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, and that the name of
+ * Danny Baumann not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Danny Baumann makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * DANNY BAUMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Danny Baumann <dannybaumann@web.de>
+ */
+
+#include <compiz-core.h>
+
+static CompMetadata gnomeMetadata;
+
+static int displayPrivateIndex;
+
+#define GNOME_DISPLAY_OPTION_MAIN_MENU_KEY 0
+#define GNOME_DISPLAY_OPTION_RUN_DIALOG_KEY 1
+#define GNOME_DISPLAY_OPTION_SCREENSHOT_CMD 2
+#define GNOME_DISPLAY_OPTION_RUN_SCREENSHOT_KEY 3
+#define GNOME_DISPLAY_OPTION_WINDOW_SCREENSHOT_CMD 4
+#define GNOME_DISPLAY_OPTION_RUN_WINDOW_SCREENSHOT_KEY 5
+#define GNOME_DISPLAY_OPTION_TERMINAL_CMD 6
+#define GNOME_DISPLAY_OPTION_RUN_TERMINAL_KEY 7
+#define GNOME_DISPLAY_OPTION_NUM 8
+
+typedef struct _GnomeDisplay {
+ CompOption opt[GNOME_DISPLAY_OPTION_NUM];
+
+ Atom panelActionAtom;
+ Atom panelMainMenuAtom;
+ Atom panelRunDialogAtom;
+} GnomeDisplay;
+
+#define GET_GNOME_DISPLAY(d) \
+ ((GnomeDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+#define GNOME_DISPLAY(d) \
+ GnomeDisplay *gd = GET_GNOME_DISPLAY (d)
+
+#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
+
+static Bool
+runDispatch (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+ s = findScreenAtDisplay (d, xid);
+
+ if (s)
+ {
+ GNOME_DISPLAY (d);
+
+ runCommand (s, gd->opt[action->priv.val].value.s);
+ }
+
+ return TRUE;
+}
+
+static void
+panelAction (CompDisplay *d,
+ CompOption *option,
+ int nOption,
+ Atom actionAtom)
+{
+ Window xid;
+ CompScreen *s;
+ XEvent event;
+ Time time;
+
+ GNOME_DISPLAY (d);
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+ s = findScreenAtDisplay (d, xid);
+
+ if (!s)
+ return;
+
+ time = getIntOptionNamed (option, nOption, "time", CurrentTime);
+
+ /* we need to ungrab the keyboard here, otherwise the panel main
+ menu won't popup as it wants to grab the keyboard itself */
+ XUngrabKeyboard (d->display, time);
+
+ event.type = ClientMessage;
+ event.xclient.window = s->root;
+ event.xclient.message_type = gd->panelActionAtom;
+ event.xclient.format = 32;
+ event.xclient.data.l[0] = actionAtom;
+ event.xclient.data.l[1] = time;
+ event.xclient.data.l[2] = 0;
+ event.xclient.data.l[3] = 0;
+ event.xclient.data.l[4] = 0;
+
+ XSendEvent (d->display, s->root, FALSE, StructureNotifyMask, &event);
+}
+
+static Bool
+showMainMenu (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ GNOME_DISPLAY (d);
+
+ panelAction (d, option, nOption, gd->panelMainMenuAtom);
+
+ return TRUE;
+}
+
+static Bool
+showRunDialog (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ GNOME_DISPLAY (d);
+
+ panelAction (d, option, nOption, gd->panelRunDialogAtom);
+
+ return TRUE;
+}
+static const CompMetadataOptionInfo gnomeDisplayOptionInfo[] = {
+ { "main_menu_key", "key", 0, showMainMenu, 0 },
+ { "run_key", "key", 0, showRunDialog, 0 },
+ { "command_screenshot", "string", 0, 0, 0 },
+ { "run_command_screenshot_key", "key", 0, runDispatch, 0 },
+ { "command_window_screenshot", "string", 0, 0, 0 },
+ { "run_command_window_screenshot_key", "key", 0, runDispatch, 0 },
+ { "command_terminal", "string", 0, 0, 0 },
+ { "run_command_terminal_key", "key", 0, runDispatch, 0 }
+};
+
+static CompBool
+gnomeInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ GnomeDisplay *gd;
+ int opt, index;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ gd = malloc (sizeof (GnomeDisplay));
+ if (!gd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &gnomeMetadata,
+ gnomeDisplayOptionInfo,
+ gd->opt,
+ GNOME_DISPLAY_OPTION_NUM))
+ {
+ free (gd);
+ return FALSE;
+ }
+
+ opt = GNOME_DISPLAY_OPTION_RUN_SCREENSHOT_KEY;
+ gd->opt[opt].value.action.priv.val = GNOME_DISPLAY_OPTION_SCREENSHOT_CMD;
+
+ opt = GNOME_DISPLAY_OPTION_RUN_WINDOW_SCREENSHOT_KEY;
+ index = GNOME_DISPLAY_OPTION_WINDOW_SCREENSHOT_CMD;
+ gd->opt[opt].value.action.priv.val = index;
+
+ opt = GNOME_DISPLAY_OPTION_RUN_TERMINAL_KEY;
+ gd->opt[opt].value.action.priv.val = GNOME_DISPLAY_OPTION_TERMINAL_CMD;
+
+ gd->panelActionAtom =
+ XInternAtom (d->display, "_GNOME_PANEL_ACTION", FALSE);
+ gd->panelMainMenuAtom =
+ XInternAtom (d->display, "_GNOME_PANEL_ACTION_MAIN_MENU", FALSE);
+ gd->panelRunDialogAtom =
+ XInternAtom (d->display, "_GNOME_PANEL_ACTION_RUN_DIALOG", FALSE);
+
+ d->base.privates[displayPrivateIndex].ptr = gd;
+
+ return TRUE;
+}
+
+static void
+gnomeFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ GNOME_DISPLAY (d);
+
+ compFiniDisplayOptions (d, gd->opt, GNOME_DISPLAY_OPTION_NUM);
+
+ free (gd);
+}
+
+static CompOption *
+gnomeGetDisplayOptions (CompPlugin *p,
+ CompDisplay *d,
+ int *count)
+{
+ GNOME_DISPLAY (d);
+
+ *count = NUM_OPTIONS (gd);
+ return gd->opt;
+}
+
+static CompBool
+gnomeSetDisplayOption (CompPlugin *p,
+ CompDisplay *d,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ GNOME_DISPLAY (d);
+
+ o = compFindOption (gd->opt, NUM_OPTIONS (gd), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (d, o, value);
+}
+
+static CompBool
+gnomeInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) gnomeInitDisplay
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+gnomeFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) gnomeFiniDisplay
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+gnomeGetObjectOptions (CompPlugin *p,
+ CompObject *o,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) gnomeGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (p, o, count));
+}
+
+static CompBool
+gnomeSetObjectOption (CompPlugin *p,
+ CompObject *o,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) gnomeSetDisplayOption,
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (p, o, name, value));
+}
+
+static Bool
+gnomeInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&gnomeMetadata,
+ p->vTable->name,
+ gnomeDisplayOptionInfo,
+ GNOME_DISPLAY_OPTION_NUM, 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&gnomeMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&gnomeMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+gnomeFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&gnomeMetadata);
+}
+
+static CompMetadata *
+gnomeGetMetadata (CompPlugin *p)
+{
+ return &gnomeMetadata;
+}
+
+static CompPluginVTable gnomeVTable = {
+ "gnomecompat",
+ gnomeGetMetadata,
+ gnomeInit,
+ gnomeFini,
+ gnomeInitObject,
+ gnomeFiniObject,
+ gnomeGetObjectOptions,
+ gnomeSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &gnomeVTable;
+}
diff --git a/plugins/ini.c b/plugins/ini.c
new file mode 100644
index 0000000..fdd211d
--- /dev/null
+++ b/plugins/ini.c
@@ -0,0 +1,1152 @@
+/*
+ * Copyright © 2007 Mike Dransfield
+ *
+ * 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, and that the name of
+ * Mike Dransfield not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Mike Dransfield makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * MIKE DRANSFIELD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL MIKE DRANSFIELD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Mike Dransfield <mike@blueroot.co.uk>
+ *
+ * Some code taken from gconf.c by :
+ * David Reveman <davidr@novell.com>
+ */
+
+#define _GNU_SOURCE /* for asprintf */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <compiz-core.h>
+
+#define DEFAULT_PLUGINS "ini,inotify,png,decoration,move,resize,switcher"
+#define NUM_DEFAULT_PLUGINS 7
+#define MAX_OPTION_LENGTH 1024
+#define HOME_OPTIONDIR ".compiz/options"
+#define CORE_NAME "general"
+#define FILE_SUFFIX ".conf"
+
+#define GET_INI_CORE(c) \
+ ((IniCore *) (c)->base.privates[corePrivateIndex].ptr)
+#define INI_CORE(c) \
+ IniCore *ic = GET_INI_CORE (c)
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static int corePrivateIndex;
+
+static CompMetadata iniMetadata;
+
+static Bool iniSaveOptions (CompObject *object,
+ const char *plugin);
+
+/*
+ * IniFileData
+ */
+typedef struct _IniFileData IniFileData;
+struct _IniFileData {
+ char *filename;
+ char *plugin;
+ int screen;
+
+ Bool blockWrites;
+ Bool blockReads;
+
+ IniFileData *next;
+ IniFileData *prev;
+};
+
+/*
+ * IniCore
+ */
+typedef struct _IniCore {
+ CompFileWatchHandle directoryWatch;
+
+ IniFileData *fileData;
+
+ InitPluginForObjectProc initPluginForObject;
+ SetOptionForPluginProc setOptionForPlugin;
+} IniCore;
+
+static IniFileData *
+iniGetFileDataFromFilename (const char *filename)
+{
+ int len, i;
+ int pluginSep = 0, screenSep = 0;
+ char *pluginStr, *screenStr;
+ IniFileData *fd;
+
+ INI_CORE (&core);
+
+ if (!filename)
+ return NULL;
+
+ len = strlen (filename);
+
+ if (len < (strlen(FILE_SUFFIX) + 2))
+ return NULL;
+
+ if ((filename[0]=='.') || (filename[len-1]=='~'))
+ return NULL;
+
+ for (fd = ic->fileData; fd; fd = fd->next)
+ if (strcmp (fd->filename, filename) == 0)
+ return fd;
+
+ for (i=0; i<len; i++)
+ {
+ if (filename[i] == '-')
+ {
+ if (!pluginSep)
+ pluginSep = i-1;
+ else
+ return NULL; /*found a second dash */
+ }
+ else if (filename[i] == '.')
+ {
+ if (!screenSep)
+ screenSep = i-1;
+ else
+ return NULL; /*found a second dot */
+ }
+ }
+
+ if (!pluginSep || !screenSep)
+ return NULL;
+
+ /* If we get here then there is no fd in the display variable */
+ IniFileData *newFd = malloc (sizeof (IniFileData));
+ if (!newFd)
+ return NULL;
+
+ /* fd is NULL here, see condition "fd" in first for-loop */
+ /* if (fd)
+ fd->next = newFd;
+ else
+ */
+ ic->fileData = newFd;
+
+ newFd->prev = fd;
+ newFd->next = NULL;
+
+ newFd->filename = strdup (filename);
+
+ pluginStr = calloc (1, sizeof (char) * pluginSep + 2);
+ if (!pluginStr)
+ return NULL;
+
+ screenStr = calloc (1, sizeof (char) * (screenSep - pluginSep));
+ if (!screenStr) {
+ free(pluginStr);
+ return NULL;
+ }
+
+ strncpy (pluginStr, filename, pluginSep + 1);
+ strncpy (screenStr, &filename[pluginSep+2], (screenSep - pluginSep) - 1);
+
+ if (strcmp (pluginStr, CORE_NAME) == 0)
+ newFd->plugin = NULL;
+ else
+ newFd->plugin = strdup (pluginStr);
+
+ if (strcmp (screenStr, "allscreens") == 0)
+ newFd->screen = -1;
+ else
+ newFd->screen = atoi (&screenStr[6]);
+
+ newFd->blockReads = FALSE;
+ newFd->blockWrites = FALSE;
+
+ free (pluginStr);
+ free (screenStr);
+
+ return newFd;
+}
+
+static char *
+iniOptionValueToString (CompDisplay *d, CompOptionValue *value, CompOptionType type)
+{
+ char tmp[MAX_OPTION_LENGTH];
+ tmp[0] = '\0';
+
+ switch (type)
+ {
+ case CompOptionTypeBool:
+ case CompOptionTypeInt:
+ snprintf(tmp, 256, "%i", (int)value->i);
+ break;
+ case CompOptionTypeFloat:
+ snprintf(tmp, 256, "%f", value->f);
+ break;
+ case CompOptionTypeString:
+ snprintf (tmp, MAX_OPTION_LENGTH, "%s", strdup (value->s));
+ break;
+ case CompOptionTypeColor:
+ snprintf (tmp, 10, "%s", colorToString (value->c));
+ break;
+ case CompOptionTypeKey:
+ return keyActionToString (d, &value->action);
+ break;
+ case CompOptionTypeButton:
+ return buttonActionToString (d, &value->action);
+ break;
+ case CompOptionTypeEdge:
+ return edgeMaskToString (value->action.edgeMask);
+ break;
+ case CompOptionTypeBell:
+ snprintf (tmp, 256, "%i", (int) value->action.bell);
+ break;
+ case CompOptionTypeMatch:
+ {
+ char *s = matchToString (&value->match);
+ snprintf (tmp, MAX_OPTION_LENGTH, "%s", s);
+ free(s);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return strdup (tmp);
+}
+
+static Bool
+iniGetHomeDir (char **homeDir)
+{
+ char *home = NULL, *tmp;
+
+ home = getenv ("HOME");
+ if (home)
+ {
+ tmp = malloc (strlen (home) + strlen (HOME_OPTIONDIR) + 2);
+ if (tmp)
+ {
+ sprintf (tmp, "%s/%s", home, HOME_OPTIONDIR);
+ (*homeDir) = strdup (tmp);
+ free (tmp);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+iniGetFilename (CompObject *object,
+ const char *plugin,
+ char **filename)
+{
+ int len;
+ char *fn = NULL, *screenStr;
+
+ screenStr = malloc (sizeof(char) * 12);
+ if (!screenStr)
+ return FALSE;
+
+ if (object->type == COMP_OBJECT_TYPE_SCREEN)
+ {
+ CORE_SCREEN (object);
+
+ snprintf (screenStr, 12, "screen%d", s->screenNum);
+ }
+ else
+ {
+ strncpy (screenStr, "allscreens", 12);
+ }
+
+ len = strlen (screenStr) + strlen (FILE_SUFFIX) + 2;
+
+ if (plugin)
+ len += strlen (plugin);
+ else
+ len += strlen (CORE_NAME);
+
+ fn = malloc (sizeof (char) * len);
+ if (fn)
+ {
+ sprintf (fn, "%s-%s%s",
+ plugin ? plugin : CORE_NAME, screenStr, FILE_SUFFIX);
+
+ *filename = strdup (fn);
+
+ free (screenStr);
+ free (fn);
+
+ return TRUE;
+ }
+
+ free (screenStr);
+
+ return FALSE;
+}
+
+static Bool
+iniParseLine (char *line, char **optionName, char **optionValue)
+{
+ char *splitPos;
+ int length, optionLength;
+
+ if (line[0] == '\0' || line[0] == '\n')
+ return FALSE;
+
+ splitPos = strchr (line, '=');
+ if (!splitPos)
+ return FALSE;
+
+ length = strlen (line) - strlen (splitPos);
+ *optionName = malloc (sizeof (char) * (length + 1));
+ if (*optionName)
+ {
+ strncpy (*optionName, line, length);
+ (*optionName)[length] = 0;
+ }
+ splitPos++;
+ optionLength = strlen (splitPos);
+ if (splitPos[optionLength-1] == '\n')
+ optionLength--;
+ *optionValue = malloc (sizeof (char) * (optionLength + 1));
+ if (*optionValue)
+ {
+ strncpy (*optionValue, splitPos, optionLength);
+ (*optionValue)[optionLength] = 0;
+ }
+ return TRUE;
+}
+
+static Bool
+csvToList (CompDisplay *d, char *csv, CompListValue *list, CompOptionType type)
+{
+ char *splitStart = NULL;
+ char *splitEnd = NULL;
+ char *item = NULL;
+ int itemLength, count, i;
+
+ if (csv[0] == '\0')
+ {
+ list->nValue = 0;
+ return FALSE;
+ }
+
+ int length = strlen (csv);
+ count = 1;
+ for (i = 0; csv[i] != '\0'; i++)
+ if (csv[i] == ',' && i != length-1)
+ count++;
+
+ splitStart = csv;
+ list->value = malloc (sizeof (CompOptionValue) * count);
+ list->nValue = count;
+
+ if (list->value)
+ {
+ for (i = 0; i < count; i++)
+ {
+ splitEnd = strchr (splitStart, ',');
+
+ if (splitEnd)
+ {
+ itemLength = strlen (splitStart) - strlen (splitEnd);
+ item = malloc (sizeof (char) * (itemLength + 1));
+ if (item)
+ {
+ strncpy (item, splitStart, itemLength);
+ item[itemLength] = 0;
+ }
+ }
+ else // last value
+ {
+ item = strdup (splitStart);
+ }
+
+ if (!item) {
+ compLogMessage ("ini", CompLogLevelError, "Not enough memory");
+ list->nValue = 0;
+ return FALSE;
+ }
+
+ switch (type)
+ {
+ case CompOptionTypeString:
+ list->value[i].s = strdup (item);
+ break;
+ case CompOptionTypeBool:
+ list->value[i].b = item[0] ? (Bool) atoi (item) : FALSE;
+ break;
+ case CompOptionTypeInt:
+ list->value[i].i = item[0] ? atoi (item) : 0;
+ break;
+ case CompOptionTypeFloat:
+ list->value[i].f = item[0] ? atof (item) : 0.0f;
+ break;
+ case CompOptionTypeKey:
+ stringToKeyAction (d, item, &list->value[i].action);
+ break;
+ case CompOptionTypeButton:
+ stringToButtonAction (d, item, &list->value[i].action);
+ break;
+ case CompOptionTypeEdge:
+ list->value[i].action.edgeMask = stringToEdgeMask (item);
+ break;
+ case CompOptionTypeBell:
+ list->value[i].action.bell = (Bool) atoi (item);
+ break;
+ case CompOptionTypeMatch:
+ matchInit (&list->value[i].match);
+ matchAddFromString (&list->value[i].match, item);
+ break;
+ default:
+ break;
+ }
+
+ splitStart = ++splitEnd;
+ if (item)
+ {
+ free (item);
+ item = NULL;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static Bool
+iniMakeDirectories (void)
+{
+ char *homeDir;
+
+ if (iniGetHomeDir (&homeDir))
+ {
+ mkdir (homeDir, 0700);
+ free (homeDir);
+
+ return TRUE;
+ }
+ else
+ {
+ compLogMessage ("ini", CompLogLevelWarn,
+ "Could not get HOME environmental variable");
+ return FALSE;
+ }
+}
+
+static Bool
+iniLoadOptionsFromFile (FILE *optionFile,
+ CompObject *object,
+ const char *plugin,
+ Bool *reSave)
+{
+ CompOption *option = NULL, *o;
+ CompPlugin *p = NULL;
+ CompOptionValue value;
+ char *optionName = NULL, *optionValue = NULL;
+ char tmp[MAX_OPTION_LENGTH];
+ int nOption, nOptionRead = 0;
+ Bool status = FALSE, hasValue = FALSE;
+
+ if (plugin)
+ {
+ p = findActivePlugin (plugin);
+ if (!p)
+ {
+ compLogMessage ("ini", CompLogLevelWarn,
+ "Could not find running plugin " \
+ "%s (iniLoadOptionsFromFile)", plugin);
+ return FALSE;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ if (p->vTable->getObjectOptions)
+ option = (*p->vTable->getObjectOptions) (p, object, &nOption);
+
+ while (fgets (tmp, MAX_OPTION_LENGTH, optionFile) != NULL)
+ {
+ status = FALSE;
+
+ if (!iniParseLine (tmp, &optionName, &optionValue))
+ {
+ compLogMessage ("ini", CompLogLevelWarn,
+ "Ignoring line '%s' in %s", tmp, plugin);
+ continue;
+ }
+
+ if (option)
+ {
+ o = compFindOption (option, nOption, optionName, 0);
+ if (o)
+ {
+ value = o->value;
+
+ switch (o->type)
+ {
+ case CompOptionTypeBool:
+ hasValue = TRUE;
+ value.b = (Bool) atoi (optionValue);
+ break;
+ case CompOptionTypeInt:
+ hasValue = TRUE;
+ value.i = atoi (optionValue);
+ break;
+ case CompOptionTypeFloat:
+ hasValue = TRUE;
+ value.f = atof (optionValue);
+ break;
+ case CompOptionTypeString:
+ hasValue = TRUE;
+ value.s = strdup (optionValue);
+ break;
+ case CompOptionTypeColor:
+ hasValue = stringToColor (optionValue, value.c);
+ break;
+ case CompOptionTypeKey:
+ hasValue = TRUE;
+ stringToKeyAction (GET_CORE_DISPLAY (object),
+ optionValue, &value.action);
+ break;
+ case CompOptionTypeButton:
+ hasValue = TRUE;
+ stringToButtonAction (GET_CORE_DISPLAY (object),
+ optionValue, &value.action);
+ break;
+ case CompOptionTypeEdge:
+ hasValue = TRUE;
+ value.action.edgeMask = stringToEdgeMask (optionValue);
+ break;
+ case CompOptionTypeBell:
+ hasValue = TRUE;
+ value.action.bell = (Bool) atoi (optionValue);
+ break;
+ case CompOptionTypeList:
+ hasValue = csvToList (GET_CORE_DISPLAY (object),
+ optionValue,
+ &value.list, value.list.type);
+ break;
+ case CompOptionTypeMatch:
+ hasValue = TRUE;
+ matchInit (&value.match);
+ matchAddFromString (&value.match, optionValue);
+ break;
+ default:
+ break;
+ }
+
+ if (hasValue)
+ {
+ status = (*core.setOptionForPlugin) (object,
+ plugin,
+ optionName,
+ &value);
+
+ if (o->type == CompOptionTypeMatch)
+ {
+ matchFini (&value.match);
+ }
+ }
+
+ nOptionRead++;
+ }
+ }
+
+ /* clear up */
+ if (optionName)
+ free (optionName);
+ if (optionValue)
+ free (optionValue);
+ }
+
+ if (nOption != nOptionRead)
+ {
+ *reSave = TRUE;
+ }
+
+ return TRUE;
+}
+
+static Bool
+iniSaveOptions (CompObject *object,
+ const char *plugin)
+{
+ CompOption *option = NULL;
+ int nOption = 0;
+ char *filename, *directory, *fullPath, *strVal = NULL;
+
+ if (plugin)
+ {
+ CompPlugin *p;
+ p = findActivePlugin (plugin);
+ if (!p)
+ return FALSE;
+
+ option = (*p->vTable->getObjectOptions) (p, object, &nOption);
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ if (!option)
+ return FALSE;
+
+ if (!iniGetFilename (object, plugin, &filename))
+ return FALSE;
+
+ IniFileData *fileData;
+
+ fileData = iniGetFileDataFromFilename (filename);
+ if (!fileData || (fileData && fileData->blockWrites))
+ {
+ free (filename);
+ return FALSE;
+ }
+
+ if (!iniGetHomeDir (&directory))
+ return FALSE;
+
+ fullPath = malloc (sizeof (char) * (strlen (filename) + strlen (directory) + 2));
+ if (!fullPath)
+ {
+ free (filename);
+ free (directory);
+ return FALSE;
+ }
+
+ sprintf (fullPath, "%s/%s", directory, filename);
+
+ FILE *optionFile = fopen (fullPath, "w");
+
+ if (!optionFile && iniMakeDirectories ())
+ optionFile = fopen (fullPath, "w");
+
+ if (!optionFile)
+ {
+ compLogMessage ("ini", CompLogLevelError,
+ "Failed to write to %s, check you " \
+ "have the correct permissions", fullPath);
+ free (filename);
+ free (directory);
+ free (fullPath);
+ return FALSE;
+ }
+
+ fileData->blockReads = TRUE;
+
+ Bool status, firstInList;
+ while (nOption--)
+ {
+ status = FALSE;
+ int i;
+
+ switch (option->type)
+ {
+ case CompOptionTypeBool:
+ case CompOptionTypeInt:
+ case CompOptionTypeFloat:
+ case CompOptionTypeString:
+ case CompOptionTypeColor:
+ case CompOptionTypeKey:
+ case CompOptionTypeButton:
+ case CompOptionTypeEdge:
+ case CompOptionTypeBell:
+ case CompOptionTypeMatch:
+ strVal = iniOptionValueToString (GET_CORE_DISPLAY (object),
+ &option->value, option->type);
+ if (strVal)
+ {
+ fprintf (optionFile, "%s=%s\n", option->name, strVal);
+ free (strVal);
+ }
+ else
+ fprintf (optionFile, "%s=\n", option->name);
+ break;
+ case CompOptionTypeList:
+ firstInList = TRUE;
+ switch (option->value.list.type)
+ {
+ case CompOptionTypeBool:
+ case CompOptionTypeInt:
+ case CompOptionTypeFloat:
+ case CompOptionTypeString:
+ case CompOptionTypeColor:
+ case CompOptionTypeMatch:
+ {
+ int stringLen = MAX_OPTION_LENGTH * option->value.list.nValue;
+ char *itemVal;
+
+ strVal = malloc (sizeof(char) * stringLen);
+ if (!strVal) {
+ fclose(optionFile);
+ free(fullPath);
+ return FALSE;
+ }
+ strcpy (strVal, "");
+ firstInList = TRUE;
+
+ for (i = 0; i < option->value.list.nValue; i++)
+ {
+ itemVal =
+ iniOptionValueToString (GET_CORE_DISPLAY (object),
+ &option->value.list.value[i],
+ option->value.list.type);
+ if (!firstInList)
+ strncat (strVal, ",", stringLen);
+ firstInList = FALSE;
+
+ if (itemVal)
+ {
+ strncat (strVal, itemVal, stringLen);
+ free (itemVal);
+ }
+ }
+
+ fprintf (optionFile, "%s=%s\n", option->name, strVal);
+ free (strVal);
+ break;
+ }
+ default:
+ compLogMessage ("ini", CompLogLevelWarn,
+ "Unknown list option type %d, %s\n",
+ option->value.list.type,
+ optionTypeToString (option->value.list.type));
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ option++;
+ }
+
+ fileData->blockReads = FALSE;
+
+ fclose (optionFile);
+
+ free (filename);
+ free (directory);
+ free (fullPath);
+
+ return TRUE;
+}
+
+static Bool
+iniLoadOptions (CompObject *object,
+ const char *plugin)
+{
+ char *filename, *directory, *fullPath;
+ FILE *optionFile;
+ Bool loadRes, reSave = FALSE;
+ IniFileData *fileData;
+
+ filename = directory = fullPath = NULL;
+ optionFile = NULL;
+ fileData = NULL;
+
+ if (!iniGetFilename (object, plugin, &filename))
+ return FALSE;
+
+ fileData = iniGetFileDataFromFilename (filename);
+ if (!fileData || (fileData && fileData->blockReads))
+ {
+ free(filename);
+ return FALSE;
+ }
+
+ if (!iniGetHomeDir (&directory))
+ {
+ free (filename);
+ return FALSE;
+ }
+
+ fullPath = malloc (sizeof (char) * (strlen (filename) + strlen (directory) + 2));
+ if (!fullPath)
+ {
+ free (filename);
+ free (directory);
+ return FALSE;
+ }
+
+ sprintf(fullPath, "%s/%s", directory, filename);
+
+ optionFile = fopen (fullPath, "r");
+
+ if (!optionFile && iniMakeDirectories ())
+ optionFile = fopen (fullPath, "r");
+
+ if (!optionFile)
+ {
+ if (!plugin && object->type == COMP_OBJECT_TYPE_DISPLAY)
+ {
+ CompOptionValue value;
+ value.list.value = malloc (NUM_DEFAULT_PLUGINS * sizeof (CompListValue));
+ if (!value.list.value)
+ {
+ free (filename);
+ free (directory);
+ free (fullPath);
+ return FALSE;
+ }
+
+ if (!csvToList (GET_CORE_DISPLAY (object), DEFAULT_PLUGINS,
+ &value.list,
+ CompOptionTypeString))
+ {
+ free (filename);
+ free (directory);
+ free (fullPath);
+ return FALSE;
+ }
+
+ value.list.type = CompOptionTypeString;
+
+ compLogMessage ("ini", CompLogLevelWarn,
+ "Could not open main display config file %s",
+ fullPath);
+ compLogMessage ("ini", CompLogLevelWarn,
+ "Loading default plugins (%s)", DEFAULT_PLUGINS);
+
+ (*core.setOptionForPlugin) (object,
+ "core", "active_plugins",
+ &value);
+
+ free (value.list.value);
+
+ fileData->blockWrites = FALSE;
+
+ iniSaveOptions (object, plugin);
+
+ fileData->blockWrites = TRUE;
+
+ optionFile = fopen (fullPath, "r");
+
+ if (!optionFile)
+ {
+ free (filename);
+ free (directory);
+ free (fullPath);
+ return FALSE;
+ }
+ }
+ else
+ {
+ compLogMessage ("ini", CompLogLevelWarn,
+ "Could not open config file %s - "
+ "using defaults for %s",
+ fullPath, plugin ? plugin : "core");
+
+ fileData->blockWrites = FALSE;
+
+ iniSaveOptions (object, plugin);
+
+ fileData->blockWrites = TRUE;
+
+ optionFile = fopen (fullPath, "r");
+ if (!optionFile)
+ {
+ free (filename);
+ free (directory);
+ free (fullPath);
+ return FALSE;
+ }
+ }
+ }
+
+ fileData->blockWrites = TRUE;
+
+ loadRes = iniLoadOptionsFromFile (optionFile, object, plugin, &reSave);
+
+ fileData->blockWrites = FALSE;
+
+ fclose (optionFile);
+
+ if (loadRes && reSave)
+ {
+ fileData->blockReads = TRUE;
+ iniSaveOptions (object, plugin);
+ fileData->blockReads = FALSE;
+ }
+
+ free (filename);
+ free (directory);
+ free (fullPath);
+
+ return TRUE;
+}
+
+/* MULTIDPYERROR: only works with one or less displays present */
+/* OBJECTOPTION: only display and screen options are supported */
+static void
+iniFileModified (const char *name,
+ void *closure)
+{
+ IniFileData *fd;
+
+ fd = iniGetFileDataFromFilename (name);
+ if (fd && core.displays)
+ {
+ if (fd->screen < 0)
+ {
+ iniLoadOptions (&core.displays->base, fd->plugin);
+ }
+ else
+ {
+ CompScreen *s;
+
+ for (s = core.displays->screens; s; s = s->next)
+ if (s->screenNum == fd->screen)
+ break;
+
+ if (s)
+ iniLoadOptions (&s->base, fd->plugin);
+ }
+ }
+}
+
+static void
+iniFreeFileData (void)
+{
+ IniFileData *fd, *tmp;
+
+ INI_CORE (&core);
+
+ fd = ic->fileData;
+
+ while (fd)
+ {
+ tmp = fd;
+ fd = fd->next;
+ free (tmp);
+ }
+}
+
+/*
+CORE FUNCTIONS
+*/
+
+static Bool
+iniInitPluginForDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ iniLoadOptions (&d->base, p->vTable->name);
+
+ return TRUE;
+}
+
+static Bool
+iniInitPluginForScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ iniLoadOptions (&s->base, p->vTable->name);
+
+ return TRUE;
+}
+
+static CompBool
+iniInitPluginForObject (CompPlugin *p,
+ CompObject *o)
+{
+ CompBool status;
+
+ INI_CORE (&core);
+
+ UNWRAP (ic, &core, initPluginForObject);
+ status = (*core.initPluginForObject) (p, o);
+ WRAP (ic, &core, initPluginForObject, iniInitPluginForObject);
+
+ if (status && p->vTable->getObjectOptions)
+ {
+ static InitPluginForObjectProc dispTab[] = {
+ (InitPluginForObjectProc) 0, /* InitPluginForCore */
+ (InitPluginForObjectProc) iniInitPluginForDisplay,
+ (InitPluginForObjectProc) iniInitPluginForScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+ }
+
+ return status;
+}
+
+static CompBool
+iniSetOptionForPlugin (CompObject *object,
+ const char *plugin,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompBool status;
+
+ INI_CORE (&core);
+
+ UNWRAP (ic, &core, setOptionForPlugin);
+ status = (*core.setOptionForPlugin) (object, plugin, name, value);
+ WRAP (ic, &core, setOptionForPlugin, iniSetOptionForPlugin);
+
+ if (status)
+ {
+ CompPlugin *p;
+
+ p = findActivePlugin (plugin);
+ if (p && p->vTable->getObjectOptions)
+ iniSaveOptions (object, plugin);
+ }
+
+ return status;
+}
+
+static Bool
+iniInitCore (CompPlugin *p,
+ CompCore *c)
+{
+ IniCore *ic;
+ char *homeDir;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ ic = malloc (sizeof (IniCore));
+ if (!ic)
+ return FALSE;
+
+ ic->fileData = NULL;
+ ic->directoryWatch = 0;
+
+ if (iniGetHomeDir (&homeDir))
+ {
+ ic->directoryWatch = addFileWatch (homeDir,
+ NOTIFY_DELETE_MASK |
+ NOTIFY_CREATE_MASK |
+ NOTIFY_MODIFY_MASK,
+ iniFileModified, 0);
+ free (homeDir);
+ }
+
+ WRAP (ic, c, initPluginForObject, iniInitPluginForObject);
+ WRAP (ic, c, setOptionForPlugin, iniSetOptionForPlugin);
+
+ c->base.privates[corePrivateIndex].ptr = ic;
+
+ return TRUE;
+}
+
+static void
+iniFiniCore (CompPlugin *p,
+ CompCore *c)
+{
+ INI_CORE (c);
+
+ UNWRAP (ic, c, initPluginForObject);
+ UNWRAP (ic, c, setOptionForPlugin);
+
+ if (ic->directoryWatch)
+ removeFileWatch (ic->directoryWatch);
+
+ iniFreeFileData ();
+
+ free (ic);
+}
+
+static Bool
+iniInitDisplay (CompPlugin *p, CompDisplay *d)
+{
+ iniLoadOptions (&d->base, NULL);
+
+ return TRUE;
+}
+
+static Bool
+iniInitScreen (CompPlugin *p, CompScreen *s)
+{
+ iniLoadOptions (&s->base, NULL);
+
+ return TRUE;
+}
+
+static CompBool
+iniInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) iniInitCore,
+ (InitPluginObjectProc) iniInitDisplay,
+ (InitPluginObjectProc) iniInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+iniFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) iniFiniCore
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static Bool
+iniInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&iniMetadata, p->vTable->name,
+ 0, 0, 0, 0))
+ return FALSE;
+
+ corePrivateIndex = allocateCorePrivateIndex ();
+ if (corePrivateIndex < 0)
+ {
+ compFiniMetadata (&iniMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&iniMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+iniFini (CompPlugin *p)
+{
+ freeCorePrivateIndex (corePrivateIndex);
+}
+
+static CompMetadata *
+iniGetMetadata (CompPlugin *plugin)
+{
+ return &iniMetadata;
+}
+
+CompPluginVTable iniVTable = {
+ "ini",
+ iniGetMetadata,
+ iniInit,
+ iniFini,
+ iniInitObject,
+ iniFiniObject,
+ 0, /* GetObjectOptions */
+ 0 /* SetObjectOption */
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &iniVTable;
+}
diff --git a/plugins/inotify.c b/plugins/inotify.c
new file mode 100644
index 0000000..58bff1b
--- /dev/null
+++ b/plugins/inotify.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <poll.h>
+#include <sys/inotify.h>
+
+#include <compiz-core.h>
+
+static CompMetadata inotifyMetadata;
+
+static int corePrivateIndex;
+
+typedef struct _CompInotifyWatch {
+ struct _CompInotifyWatch *next;
+ CompFileWatchHandle handle;
+ int wd;
+} CompInotifyWatch;
+
+typedef struct _InotifyCore {
+ int fd;
+ CompInotifyWatch *watch;
+ CompWatchFdHandle watchFdHandle;
+
+ FileWatchAddedProc fileWatchAdded;
+ FileWatchRemovedProc fileWatchRemoved;
+} InotifyCore;
+
+#define GET_INOTIFY_CORE(c) \
+ ((InotifyCore *) (c)->base.privates[corePrivateIndex].ptr)
+
+#define INOTIFY_CORE(c) \
+ InotifyCore *ic = GET_INOTIFY_CORE (c)
+
+
+static Bool
+inotifyProcessEvents (void *data)
+{
+ char buf[256 * (sizeof (struct inotify_event) + 16)];
+ int len;
+
+ INOTIFY_CORE (&core);
+
+ len = read (ic->fd, buf, sizeof (buf));
+ if (len < 0)
+ {
+ perror ("read");
+ }
+ else
+ {
+ struct inotify_event *event;
+ CompInotifyWatch *iw;
+ CompFileWatch *fw;
+ int i = 0;
+
+ while (i < len)
+ {
+ event = (struct inotify_event *) &buf[i];
+
+ for (iw = ic->watch; iw; iw = iw->next)
+ if (iw->wd == event->wd)
+ break;
+
+ if (iw)
+ {
+ for (fw = core.fileWatch; fw; fw = fw->next)
+ if (fw->handle == iw->handle)
+ break;
+
+ if (fw)
+ {
+ if (event->len)
+ (*fw->callBack) (event->name, fw->closure);
+ else
+ (*fw->callBack) (NULL, fw->closure);
+ }
+ }
+
+ i += sizeof (*event) + event->len;
+ }
+ }
+
+ return TRUE;
+}
+
+static int
+inotifyMask (CompFileWatch *fileWatch)
+{
+ int mask = 0;
+
+ if (fileWatch->mask & NOTIFY_CREATE_MASK)
+ mask |= IN_CREATE;
+
+ if (fileWatch->mask & NOTIFY_DELETE_MASK)
+ mask |= IN_DELETE;
+
+ if (fileWatch->mask & NOTIFY_MOVE_MASK)
+ mask |= IN_MOVE;
+
+ if (fileWatch->mask & NOTIFY_MODIFY_MASK)
+ mask |= IN_MODIFY;
+
+ return mask;
+}
+
+static void
+inotifyFileWatchAdded (CompCore *c,
+ CompFileWatch *fileWatch)
+{
+ CompInotifyWatch *iw;
+
+ INOTIFY_CORE (c);
+
+ iw = malloc (sizeof (CompInotifyWatch));
+ if (!iw)
+ return;
+
+ iw->handle = fileWatch->handle;
+ iw->wd = inotify_add_watch (ic->fd,
+ fileWatch->path,
+ inotifyMask (fileWatch));
+ if (iw->wd < 0)
+ {
+ perror ("inotify_add_watch");
+ free (iw);
+ return;
+ }
+
+ iw->next = ic->watch;
+ ic->watch = iw;
+}
+
+static void
+inotifyFileWatchRemoved (CompCore *c,
+ CompFileWatch *fileWatch)
+{
+ CompInotifyWatch *p = 0, *iw;
+
+ INOTIFY_CORE (c);
+
+ for (iw = ic->watch; iw; iw = iw->next)
+ {
+ if (iw->handle == fileWatch->handle)
+ break;
+
+ p = iw;
+ }
+
+ if (iw)
+ {
+ if (p)
+ p->next = iw->next;
+ else
+ ic->watch = iw->next;
+
+ if (inotify_rm_watch (ic->fd, iw->wd))
+ perror ("inotify_rm_watch");
+
+ free (iw);
+ }
+}
+
+static Bool
+inotifyInitCore (CompPlugin *p,
+ CompCore *c)
+{
+ InotifyCore *ic;
+ CompFileWatch *fw;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ ic = malloc (sizeof (InotifyCore));
+ if (!ic)
+ return FALSE;
+
+ ic->fd = inotify_init ();
+ if (ic->fd < 0)
+ {
+ perror ("inotify_init");
+ free (ic);
+ return FALSE;
+ }
+
+ ic->watch = NULL;
+
+ ic->watchFdHandle = compAddWatchFd (ic->fd,
+ POLLIN | POLLPRI | POLLHUP | POLLERR,
+ inotifyProcessEvents,
+ NULL);
+
+ WRAP (ic, c, fileWatchAdded, inotifyFileWatchAdded);
+ WRAP (ic, c, fileWatchRemoved, inotifyFileWatchRemoved);
+
+ c->base.privates[corePrivateIndex].ptr = ic;
+
+ for (fw = c->fileWatch; fw; fw = fw->next)
+ inotifyFileWatchAdded (c, fw);
+
+ return TRUE;
+}
+
+static void
+inotifyFiniCore (CompPlugin *p,
+ CompCore *c)
+{
+ CompFileWatch *fw;
+
+ INOTIFY_CORE (c);
+
+ compRemoveWatchFd (ic->watchFdHandle);
+
+ for (fw = c->fileWatch; fw; fw = fw->next)
+ inotifyFileWatchRemoved (c, fw);
+
+ close (ic->fd);
+
+ UNWRAP (ic, c, fileWatchAdded);
+ UNWRAP (ic, c, fileWatchRemoved);
+
+ free (ic);
+}
+
+static CompBool
+inotifyInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) inotifyInitCore
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+inotifyFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) inotifyFiniCore
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static Bool
+inotifyInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&inotifyMetadata, p->vTable->name,
+ 0, 0, 0, 0))
+ return FALSE;
+
+ corePrivateIndex = allocateCorePrivateIndex ();
+ if (corePrivateIndex < 0)
+ {
+ compFiniMetadata (&inotifyMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&inotifyMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+inotifyFini (CompPlugin *p)
+{
+ freeCorePrivateIndex (corePrivateIndex);
+ compFiniMetadata (&inotifyMetadata);
+}
+
+static CompMetadata *
+inotifyGetMetadata (CompPlugin *plugin)
+{
+ return &inotifyMetadata;
+}
+
+CompPluginVTable inotifyVTable = {
+ "inotify",
+ inotifyGetMetadata,
+ inotifyInit,
+ inotifyFini,
+ inotifyInitObject,
+ inotifyFiniObject,
+ 0, /* GetObjectOptions */
+ 0 /* SetObjectOption */
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &inotifyVTable;
+}
diff --git a/plugins/kconfig.cpp b/plugins/kconfig.cpp
new file mode 100644
index 0000000..0ecd1ea
--- /dev/null
+++ b/plugins/kconfig.cpp
@@ -0,0 +1,752 @@
+/*
+ * Copyright © 2007 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kapplication.h>
+#include <ksimpleconfig.h>
+#include <qfile.h>
+
+#include <compiz-core.h>
+
+#define COMPIZ_KCONFIG_RC "compizrc"
+
+static KInstance *kInstance;
+
+static CompMetadata kconfigMetadata;
+
+static int corePrivateIndex;
+
+typedef struct _KconfigCore {
+ KConfig *config;
+
+ CompTimeoutHandle syncHandle;
+ CompTimeoutHandle reloadHandle;
+ CompFileWatchHandle fileWatch;
+
+ InitPluginForObjectProc initPluginForObject;
+ SetOptionForPluginProc setOptionForPlugin;
+} KconfigCore;
+
+#define GET_KCONFIG_CORE(c) \
+ ((KconfigCore *) (c)->base.privates[corePrivateIndex].ptr)
+
+#define KCONFIG_CORE(c) \
+ KconfigCore *kc = GET_KCONFIG_CORE (c)
+
+
+static void
+kconfigRcChanged (const char *name,
+ void *closure);
+
+static Bool
+kconfigRcSync (void *closure)
+{
+ KCONFIG_CORE (&core);
+
+ kc->config->sync ();
+
+ kc->syncHandle = 0;
+
+ return FALSE;
+}
+
+static bool
+kconfigValueToBool (CompOptionType type,
+ CompOptionValue *value)
+{
+ switch (type) {
+ case CompOptionTypeBool:
+ return (value->b) ? true : false;
+ case CompOptionTypeBell:
+ return (value->action.bell) ? true : false;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static QString
+kconfigValueToString (CompObject *object,
+ CompOptionType type,
+ CompOptionValue *value)
+{
+ QString str;
+
+ switch (type) {
+ case CompOptionTypeBool:
+ str = QString::number (value->b ? TRUE : FALSE);
+ break;
+ case CompOptionTypeFloat:
+ str = QString::number (value->f);
+ break;
+ case CompOptionTypeString:
+ str = QString (value->s);
+ break;
+ case CompOptionTypeColor: {
+ char *color;
+
+ color = colorToString (value->c);
+ if (color)
+ {
+ str = QString (color);
+ free (color);
+ }
+ } break;
+ case CompOptionTypeKey: {
+ char *action = NULL;
+
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (object)
+ action = keyActionToString (GET_CORE_DISPLAY (object),
+ &value->action);
+ if (action)
+ {
+ str = QString (action);
+ free (action);
+ }
+ } break;
+ case CompOptionTypeButton: {
+ char *action = NULL;
+
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (object)
+ action = buttonActionToString (GET_CORE_DISPLAY (object),
+ &value->action);
+ if (action)
+ {
+ str = QString (action);
+ free (action);
+ }
+ } break;
+ case CompOptionTypeEdge: {
+ char *edge;
+
+ edge = edgeMaskToString (value->action.edgeMask);
+ if (edge)
+ {
+ str = QString (edge);
+ free (edge);
+ }
+ } break;
+ case CompOptionTypeBell:
+ str = QString::number (value->action.bell ? TRUE : FALSE);
+ break;
+ case CompOptionTypeMatch: {
+ char *match;
+
+ match = matchToString (&value->match);
+ if (match)
+ {
+ str = QString (match);
+ free (match);
+ }
+ }
+ default:
+ break;
+ }
+
+ return str;
+}
+
+static QString
+kconfigObjectString (CompObject *object)
+{
+ QString objectName (QString (compObjectTypeName (object->type)));
+ char *name;
+
+ name = compObjectName (object);
+ if (name)
+ {
+ objectName += name;
+ free (name);
+ }
+
+ return objectName;
+}
+
+static void
+kconfigSetOption (CompObject *object,
+ CompOption *o,
+ const char *plugin)
+{
+ QString group (QString (plugin) + "_" + kconfigObjectString (object));
+
+ KCONFIG_CORE (&core);
+
+ kc->config->setGroup (group);
+
+ switch (o->type) {
+ case CompOptionTypeBool:
+ case CompOptionTypeBell:
+ kc->config->writeEntry (o->name,
+ kconfigValueToBool (o->type, &o->value));
+ break;
+ case CompOptionTypeInt:
+ kc->config->writeEntry (o->name, o->value.i);
+ break;
+ case CompOptionTypeFloat:
+ kc->config->writeEntry (o->name, (double) o->value.f);
+ break;
+ case CompOptionTypeString:
+ case CompOptionTypeColor:
+ case CompOptionTypeKey:
+ case CompOptionTypeButton:
+ case CompOptionTypeEdge:
+ case CompOptionTypeMatch:
+ kc->config->writeEntry (o->name,
+ kconfigValueToString (object, o->type,
+ &o->value));
+ break;
+ case CompOptionTypeList: {
+ int i;
+
+ switch (o->value.list.type) {
+ case CompOptionTypeInt: {
+ QValueList< int > list;
+
+ for (i = 0; i < o->value.list.nValue; i++)
+ list += o->value.list.value[i].i;
+
+ kc->config->writeEntry (o->name, list);
+ } break;
+ case CompOptionTypeBool:
+ case CompOptionTypeFloat:
+ case CompOptionTypeString:
+ case CompOptionTypeColor:
+ case CompOptionTypeKey:
+ case CompOptionTypeButton:
+ case CompOptionTypeEdge:
+ case CompOptionTypeBell:
+ case CompOptionTypeMatch: {
+ QStringList list;
+
+ for (i = 0; i < o->value.list.nValue; i++)
+ list += kconfigValueToString (object,
+ o->value.list.type,
+ &o->value.list.value[i]);
+
+ kc->config->writeEntry (o->name, list);
+ } break;
+ case CompOptionTypeAction:
+ case CompOptionTypeList:
+ break;
+ }
+ } break;
+ case CompOptionTypeAction:
+ return;
+ }
+
+ if (!kc->syncHandle)
+ kc->syncHandle = compAddTimeout (0, 0, kconfigRcSync, 0);
+}
+
+static Bool
+kconfigStringToValue (CompObject *object,
+ QString str,
+ CompOptionType type,
+ CompOptionValue *value)
+{
+ switch (type) {
+ case CompOptionTypeBool:
+ value->b = str.toInt () ? TRUE : FALSE;
+ break;
+ case CompOptionTypeFloat:
+ value->f = str.toFloat ();
+ break;
+ case CompOptionTypeString:
+ value->s = strdup (str.ascii ());
+ if (!value->s)
+ return FALSE;
+ break;
+ case CompOptionTypeColor:
+ if (!stringToColor (str.ascii (), value->c))
+ return FALSE;
+ break;
+ case CompOptionTypeKey:
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return FALSE;
+
+ stringToKeyAction (GET_CORE_DISPLAY (object), str.ascii (),
+ &value->action);
+ break;
+ case CompOptionTypeButton:
+ while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
+ object = object->parent;
+
+ if (!object)
+ return FALSE;
+
+ stringToButtonAction (GET_CORE_DISPLAY (object), str.ascii (),
+ &value->action);
+ break;
+ case CompOptionTypeEdge:
+ value->action.edgeMask = stringToEdgeMask (str.ascii ());
+ break;
+ case CompOptionTypeBell:
+ value->action.bell = str.toInt () ? TRUE : FALSE;
+ break;
+ case CompOptionTypeMatch:
+ matchInit (&value->match);
+ matchAddFromString (&value->match, str.ascii ());
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+kconfigBoolToValue (bool b,
+ CompOptionType type,
+ CompOptionValue *value)
+{
+ switch (type) {
+ case CompOptionTypeBool:
+ value->b = (b) ? TRUE : FALSE;
+ break;
+ case CompOptionTypeBell:
+ value->action.bell = (b) ? TRUE : FALSE;
+ default:
+ break;
+ }
+}
+
+static Bool
+kconfigReadOptionValue (CompObject *object,
+ KConfig *config,
+ CompOption *o,
+ CompOptionValue *value)
+{
+ compInitOptionValue (value);
+
+ switch (o->type) {
+ case CompOptionTypeBool:
+ case CompOptionTypeBell:
+ kconfigBoolToValue (config->readBoolEntry (o->name), o->type, value);
+ break;
+ case CompOptionTypeInt:
+ value->i = config->readNumEntry (o->name);
+ break;
+ case CompOptionTypeFloat:
+ value->f = config->readDoubleNumEntry (o->name);
+ break;
+ case CompOptionTypeString:
+ case CompOptionTypeColor:
+ case CompOptionTypeKey:
+ case CompOptionTypeButton:
+ case CompOptionTypeEdge:
+ case CompOptionTypeMatch:
+ if (!kconfigStringToValue (object,
+ config->readEntry (o->name), o->type,
+ value))
+ return FALSE;
+ break;
+ case CompOptionTypeList: {
+ int n, i;
+
+ value->list.value = NULL;
+ value->list.nValue = 0;
+ value->list.type = o->value.list.type;
+
+ switch (o->value.list.type) {
+ case CompOptionTypeInt: {
+ QValueList< int > list;
+
+ list = config->readIntListEntry (o->name);
+
+ n = list.size ();
+ if (n)
+ {
+ value->list.value = (CompOptionValue *)
+ malloc (sizeof (CompOptionValue) * n);
+ if (value->list.value)
+ {
+ for (i = 0; i < n; i++)
+ value->list.value[i].i = list[i];
+
+ value->list.nValue = n;
+ }
+ }
+ } break;
+ case CompOptionTypeBool:
+ case CompOptionTypeFloat:
+ case CompOptionTypeString:
+ case CompOptionTypeColor:
+ case CompOptionTypeKey:
+ case CompOptionTypeButton:
+ case CompOptionTypeEdge:
+ case CompOptionTypeBell:
+ case CompOptionTypeMatch: {
+ QStringList list;
+
+ list = config->readListEntry (o->name);
+
+ n = list.size ();
+ if (n)
+ {
+ value->list.value = (CompOptionValue *)
+ malloc (sizeof (CompOptionValue) * n);
+ if (value->list.value)
+ {
+ for (i = 0; i < n; i++)
+ {
+ if (!kconfigStringToValue (object,
+ list[i],
+ value->list.type,
+ &value->list.value[i]))
+ break;
+
+ value->list.nValue++;
+ }
+
+ if (value->list.nValue != n)
+ {
+ compFiniOptionValue (value, o->type);
+ return FALSE;
+ }
+ }
+ }
+ } break;
+ case CompOptionTypeList:
+ case CompOptionTypeAction:
+ return FALSE;
+ }
+ } break;
+ case CompOptionTypeAction:
+ return FALSE;
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+kconfigGetOption (CompObject *object,
+ CompOption *o,
+ const char *plugin)
+{
+ QString group (QString (plugin) + "_" +
+ kconfigObjectString (object));
+ const QString name (o->name);
+
+ KCONFIG_CORE (&core);
+
+ kc->config->setGroup (group);
+
+ if (kc->config->hasKey (name))
+ {
+ CompOptionValue value;
+
+ if (kconfigReadOptionValue (object, kc->config, o, &value))
+ {
+ (*core.setOptionForPlugin) (object, plugin, o->name, &value);
+ compFiniOptionValue (&value, o->type);
+ }
+ }
+ else
+ {
+ kconfigSetOption (object, o, plugin);
+ }
+}
+
+static CompBool
+kconfigReloadObjectTree (CompObject *object,
+ void *closure);
+
+static CompBool
+kconfigReloadObjectsWithType (CompObjectType type,
+ CompObject *parent,
+ void *closure)
+{
+ compObjectForEach (parent, type, kconfigReloadObjectTree, closure);
+
+ return TRUE;
+}
+
+static CompBool
+kconfigReloadObjectTree (CompObject *object,
+ void *closure)
+{
+ CompPlugin *p = (CompPlugin *) closure;
+ CompOption *option;
+ int nOption;
+
+ option = (*p->vTable->getObjectOptions) (p, object, &nOption);
+ while (nOption--)
+ kconfigGetOption (object, option++, p->vTable->name);
+
+ compObjectForEachType (object, kconfigReloadObjectsWithType, closure);
+
+ return TRUE;
+}
+
+static Bool
+kconfigRcReload (void *closure)
+{
+ CompPlugin *p;
+
+ KCONFIG_CORE (&core);
+
+ kc->config->reparseConfiguration ();
+
+ for (p = getPlugins (); p; p = p->next)
+ {
+ if (!p->vTable->getObjectOptions)
+ continue;
+
+ kconfigReloadObjectTree (&core.base, (void *) p);
+ }
+
+ kc->reloadHandle = 0;
+
+ return FALSE;
+}
+
+static void
+kconfigRcChanged (const char *name,
+ void *closure)
+{
+ if (strcmp (name, COMPIZ_KCONFIG_RC) == 0)
+ {
+ KCONFIG_CORE (&core);
+
+ if (!kc->reloadHandle)
+ kc->reloadHandle = compAddTimeout (0, 0, kconfigRcReload, closure);
+ }
+}
+
+static CompBool
+kconfigSetOptionForPlugin (CompObject *object,
+ const char *plugin,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompBool status;
+
+ KCONFIG_CORE (&core);
+
+ UNWRAP (kc, &core, setOptionForPlugin);
+ status = (*core.setOptionForPlugin) (object, plugin, name, value);
+ WRAP (kc, &core, setOptionForPlugin, kconfigSetOptionForPlugin);
+
+ if (status && !kc->reloadHandle)
+ {
+ CompPlugin *p;
+
+ p = findActivePlugin (plugin);
+ if (p && p->vTable->getObjectOptions)
+ {
+ CompOption *option;
+ int nOption;
+
+ option = (*p->vTable->getObjectOptions) (p, object, &nOption);
+ option = compFindOption (option, nOption, name, 0);
+ if (option)
+ kconfigSetOption (object, option, p->vTable->name);
+ }
+ }
+
+ return status;
+}
+
+static CompBool
+kconfigInitPluginForObject (CompPlugin *p,
+ CompObject *o)
+{
+ CompBool status;
+
+ KCONFIG_CORE (&core);
+
+ UNWRAP (kc, &core, initPluginForObject);
+ status = (*core.initPluginForObject) (p, o);
+ WRAP (kc, &core, initPluginForObject, kconfigInitPluginForObject);
+
+ if (status && p->vTable->getObjectOptions)
+ {
+ CompOption *option;
+ int nOption;
+
+ option = (*p->vTable->getObjectOptions) (p, o, &nOption);
+ while (nOption--)
+ kconfigGetOption (o, option++, p->vTable->name);
+ }
+
+ return status;
+}
+
+static Bool
+kconfigInitCore (CompPlugin *p,
+ CompCore *c)
+{
+ KconfigCore *kc;
+ QString dir;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ kc = new KconfigCore;
+ if (!kc)
+ return FALSE;
+
+ kc->config = new KConfig (COMPIZ_KCONFIG_RC);
+ if (!kc->config)
+ {
+ delete kc;
+ return FALSE;
+ }
+
+ kc->reloadHandle = compAddTimeout (0, 0, kconfigRcReload, 0);
+ kc->syncHandle = 0;
+ kc->fileWatch = 0;
+
+ dir = KGlobal::dirs ()->saveLocation ("config", QString::null, false);
+
+ if (QFile::exists (dir))
+ {
+ kc->fileWatch = addFileWatch (dir.ascii (), ~0, kconfigRcChanged, 0);
+ }
+ else
+ {
+ compLogMessage ("kconfig", CompLogLevelWarn, "Bad access \"%s\"",
+ dir.ascii ());
+ }
+
+ WRAP (kc, c, initPluginForObject, kconfigInitPluginForObject);
+ WRAP (kc, c, setOptionForPlugin, kconfigSetOptionForPlugin);
+
+ c->base.privates[corePrivateIndex].ptr = kc;
+
+ return TRUE;
+}
+
+static void
+kconfigFiniCore (CompPlugin *p,
+ CompCore *c)
+{
+ KCONFIG_CORE (c);
+
+ UNWRAP (kc, c, initPluginForObject);
+ UNWRAP (kc, c, setOptionForPlugin);
+
+ if (kc->reloadHandle)
+ compRemoveTimeout (kc->reloadHandle);
+
+ if (kc->syncHandle)
+ {
+ compRemoveTimeout (kc->syncHandle);
+ kconfigRcSync (0);
+ }
+
+ if (kc->fileWatch)
+ removeFileWatch (kc->fileWatch);
+
+ delete kc->config;
+ delete kc;
+}
+
+static CompBool
+kconfigInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) kconfigInitCore
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+kconfigFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) kconfigFiniCore
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static Bool
+kconfigInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&kconfigMetadata, p->vTable->name,
+ 0, 0, 0, 0))
+ return FALSE;
+
+ corePrivateIndex = allocateCorePrivateIndex ();
+ if (corePrivateIndex < 0)
+ {
+ compFiniMetadata (&kconfigMetadata);
+ return FALSE;
+ }
+
+ kInstance = new KInstance ("compiz-kconfig");
+ if (!kInstance)
+ {
+ freeCorePrivateIndex (corePrivateIndex);
+ compFiniMetadata (&kconfigMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&kconfigMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+kconfigFini (CompPlugin *p)
+{
+ delete kInstance;
+
+ freeCorePrivateIndex (corePrivateIndex);
+ compFiniMetadata (&kconfigMetadata);
+}
+
+static CompMetadata *
+kconfigGetMetadata (CompPlugin *plugin)
+{
+ return &kconfigMetadata;
+}
+
+CompPluginVTable kconfigVTable = {
+ "kconfig",
+ kconfigGetMetadata,
+ kconfigInit,
+ kconfigFini,
+ kconfigInitObject,
+ kconfigFiniObject,
+ 0, /* GetObjectOptions */
+ 0 /* SetObjectOption */
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &kconfigVTable;
+}
diff --git a/plugins/minimize.c b/plugins/minimize.c
new file mode 100644
index 0000000..d1093c2
--- /dev/null
+++ b/plugins/minimize.c
@@ -0,0 +1,1059 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <X11/Xatom.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <compiz-core.h>
+
+static CompMetadata minMetadata;
+
+static int displayPrivateIndex;
+
+typedef struct _MinDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+ Atom winChangeStateAtom;
+} MinDisplay;
+
+#define MIN_SCREEN_OPTION_SPEED 0
+#define MIN_SCREEN_OPTION_TIMESTEP 1
+#define MIN_SCREEN_OPTION_WINDOW_MATCH 2
+#define MIN_SCREEN_OPTION_SHADE_RESISTANCE 3
+#define MIN_SCREEN_OPTION_NUM 4
+
+typedef struct _MinScreen {
+ int windowPrivateIndex;
+
+ CompOption opt[MIN_SCREEN_OPTION_NUM];
+
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintOutputProc paintOutput;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+ FocusWindowProc focusWindow;
+
+ int shadeStep;
+ int moreAdjust;
+} MinScreen;
+
+typedef struct _MinWindow {
+ GLfloat xVelocity, yVelocity, xScaleVelocity, yScaleVelocity;
+ GLfloat xScale, yScale;
+ GLfloat tx, ty;
+
+ Bool adjust;
+
+ int state, newState;
+
+ int shade;
+ Region region;
+
+ int unmapCnt;
+
+ Bool ignoreDamage;
+} MinWindow;
+
+#define GET_MIN_DISPLAY(d) \
+ ((MinDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define MIN_DISPLAY(d) \
+ MinDisplay *md = GET_MIN_DISPLAY (d)
+
+#define GET_MIN_SCREEN(s, md) \
+ ((MinScreen *) (s)->base.privates[(md)->screenPrivateIndex].ptr)
+
+#define MIN_SCREEN(s) \
+ MinScreen *ms = GET_MIN_SCREEN (s, GET_MIN_DISPLAY (s->display))
+
+#define GET_MIN_WINDOW(w, ms) \
+ ((MinWindow *) (w)->base.privates[(ms)->windowPrivateIndex].ptr)
+
+#define MIN_WINDOW(w) \
+ MinWindow *mw = GET_MIN_WINDOW (w, \
+ GET_MIN_SCREEN (w->screen, \
+ GET_MIN_DISPLAY (w->screen->display)))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static CompOption *
+minGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+ int *count)
+{
+ MIN_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ms);
+ return ms->opt;
+}
+
+static Bool
+minSetScreenOption (CompPlugin *plugin,
+ CompScreen *screen,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ MIN_SCREEN (screen);
+
+ o = compFindOption (ms->opt, NUM_OPTIONS (ms), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case MIN_SCREEN_OPTION_SHADE_RESISTANCE:
+ if (compSetIntOption (o, value))
+ {
+ if (o->value.i)
+ ms->shadeStep = o->rest.i.max - o->value.i + 1;
+ else
+ ms->shadeStep = 0;
+
+ return TRUE;
+ }
+ break;
+ default:
+ if (compSetOption (o, value))
+ return TRUE;
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+minSetShade (CompWindow *w,
+ int shade)
+{
+ REGION rect;
+ int h = w->attrib.height + w->attrib.border_width * 2;
+
+ MIN_WINDOW (w);
+
+ EMPTY_REGION (w->region);
+
+ rect.rects = &rect.extents;
+ rect.numRects = rect.size = 1;
+
+ w->height = shade;
+
+ rect.extents.x1 = 0;
+ rect.extents.y1 = h - shade;
+ rect.extents.x2 = w->width;
+ rect.extents.y2 = h;
+
+ XIntersectRegion (mw->region, &rect, w->region);
+ XOffsetRegion (w->region, w->attrib.x, w->attrib.y - (h - shade));
+
+ w->matrix = w->texture->matrix;
+ w->matrix.x0 -= (w->attrib.x * w->matrix.xx);
+ w->matrix.y0 -= ((w->attrib.y - (h - shade)) * w->matrix.yy);
+
+ (*w->screen->windowResizeNotify) (w, 0, 0, 0, 0);
+}
+
+static int
+minGetWindowState (CompWindow *w)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+ int retval = WithdrawnState;
+
+ result = XGetWindowProperty (w->screen->display->display, w->id,
+ w->screen->display->wmStateAtom, 0L, 1L, FALSE,
+ w->screen->display->wmStateAtom,
+ &actual, &format, &n, &left, &data);
+
+ if (result == Success && data)
+ {
+ if (n)
+ memcpy (&retval, data, sizeof (int));
+
+ XFree ((void *) data);
+ }
+
+ return retval;
+}
+
+static int
+adjustMinVelocity (CompWindow *w)
+{
+ float dx, dy, dxs, dys, adjust, amount;
+ float x1, y1, xScale, yScale;
+
+ MIN_WINDOW (w);
+
+ if (mw->newState == IconicState)
+ {
+ x1 = w->iconGeometry.x;
+ y1 = w->iconGeometry.y;
+ xScale = (float) w->iconGeometry.width / w->width;
+ yScale = (float) w->iconGeometry.height / w->height;
+ }
+ else
+ {
+ x1 = w->serverX;
+ y1 = w->serverY;
+ xScale = yScale = 1.0f;
+ }
+
+ dx = x1 - (w->attrib.x + mw->tx);
+
+ adjust = dx * 0.15f;
+ amount = fabs (dx) * 1.5f;
+ if (amount < 0.5f)
+ amount = 0.5f;
+ else if (amount > 5.0f)
+ amount = 5.0f;
+
+ mw->xVelocity = (amount * mw->xVelocity + adjust) / (amount + 1.0f);
+
+ dy = y1 - (w->attrib.y + mw->ty);
+
+ adjust = dy * 0.15f;
+ amount = fabs (dy) * 1.5f;
+ if (amount < 0.5f)
+ amount = 0.5f;
+ else if (amount > 5.0f)
+ amount = 5.0f;
+
+ mw->yVelocity = (amount * mw->yVelocity + adjust) / (amount + 1.0f);
+
+ dxs = xScale - mw->xScale;
+
+ adjust = dxs * 0.15f;
+ amount = fabs (dxs) * 10.0f;
+ if (amount < 0.01f)
+ amount = 0.01f;
+ else if (amount > 0.15f)
+ amount = 0.15f;
+
+ mw->xScaleVelocity = (amount * mw->xScaleVelocity + adjust) /
+ (amount + 1.0f);
+
+ dys = yScale - mw->yScale;
+
+ adjust = dys * 0.15f;
+ amount = fabs (dys) * 10.0f;
+ if (amount < 0.01f)
+ amount = 0.01f;
+ else if (amount > 0.15f)
+ amount = 0.15f;
+
+ mw->yScaleVelocity = (amount * mw->yScaleVelocity + adjust) /
+ (amount + 1.0f);
+
+ if (fabs (dx) < 0.1f && fabs (mw->xVelocity) < 0.2f &&
+ fabs (dy) < 0.1f && fabs (mw->yVelocity) < 0.2f &&
+ fabs (dxs) < 0.001f && fabs (mw->xScaleVelocity) < 0.002f &&
+ fabs (dys) < 0.001f && fabs (mw->yScaleVelocity) < 0.002f)
+ {
+ mw->xVelocity = mw->yVelocity = mw->xScaleVelocity =
+ mw->yScaleVelocity = 0.0f;
+ mw->tx = x1 - w->attrib.x;
+ mw->ty = y1 - w->attrib.y;
+ mw->xScale = xScale;
+ mw->yScale = yScale;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+minPreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ MIN_SCREEN (s);
+
+ if (ms->moreAdjust)
+ {
+ CompWindow *w;
+ int steps, h;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f *
+ ms->opt[MIN_SCREEN_OPTION_SPEED].value.f;
+ steps = amount / (0.5f * ms->opt[MIN_SCREEN_OPTION_TIMESTEP].value.f);
+ if (!steps) steps = 1;
+ chunk = amount / (float) steps;
+
+ while (steps--)
+ {
+ ms->moreAdjust = 0;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ MIN_WINDOW (w);
+
+ if (mw->adjust)
+ {
+ mw->adjust = adjustMinVelocity (w);
+
+ ms->moreAdjust |= mw->adjust;
+
+ mw->tx += mw->xVelocity * chunk;
+ mw->ty += mw->yVelocity * chunk;
+ mw->xScale += mw->xScaleVelocity * chunk;
+ mw->yScale += mw->yScaleVelocity * chunk;
+
+ if (!mw->adjust)
+ {
+ mw->state = mw->newState;
+
+ mw->ignoreDamage = TRUE;
+ while (mw->unmapCnt)
+ {
+ unmapWindow (w);
+ mw->unmapCnt--;
+ }
+ mw->ignoreDamage = FALSE;
+ }
+ }
+ else if (mw->region && w->damaged)
+ {
+ if (w->shaded)
+ {
+ if (mw->shade > 0)
+ {
+ mw->shade -= (chunk * ms->shadeStep) + 1;
+
+ if (mw->shade > 0)
+ {
+ ms->moreAdjust = TRUE;
+ }
+ else
+ {
+ mw->shade = 0;
+
+ mw->ignoreDamage = TRUE;
+ while (mw->unmapCnt)
+ {
+ unmapWindow (w);
+ mw->unmapCnt--;
+ }
+ mw->ignoreDamage = FALSE;
+ }
+ }
+ }
+ else
+ {
+ h = w->attrib.height + w->attrib.border_width * 2;
+ if (mw->shade < h)
+ {
+ mw->shade += (chunk * ms->shadeStep) + 1;
+
+ if (mw->shade < h)
+ {
+ ms->moreAdjust = TRUE;
+ }
+ else
+ {
+ mw->shade = MAXSHORT;
+
+ minSetShade (w, h);
+
+ XDestroyRegion (mw->region);
+ mw->region = NULL;
+
+ addWindowDamage (w);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ms->moreAdjust)
+ break;
+ }
+
+ if (ms->moreAdjust)
+ {
+ for (w = s->windows; w; w = w->next)
+ {
+ MIN_WINDOW (w);
+
+ if (mw->adjust)
+ {
+ addWindowDamage (w);
+ }
+ else if (mw->region && w->damaged)
+ {
+ h = w->attrib.height + w->attrib.border_width * 2;
+ if (mw->shade && mw->shade < h)
+ {
+ minSetShade (w, mw->shade);
+ addWindowDamage (w);
+ }
+ }
+ }
+ }
+ }
+
+ UNWRAP (ms, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (ms, s, preparePaintScreen, minPreparePaintScreen);
+}
+
+static void
+minDonePaintScreen (CompScreen *s)
+{
+ MIN_SCREEN (s);
+
+ if (ms->moreAdjust)
+ {
+ CompWindow *w;
+ int h;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ MIN_WINDOW (w);
+
+ if (mw->adjust)
+ {
+ addWindowDamage (w);
+ }
+ else if (mw->region)
+ {
+ h = w->attrib.height + w->attrib.border_width * 2;
+ if (mw->shade && mw->shade < h)
+ addWindowDamage (w);
+ }
+ }
+ }
+
+ UNWRAP (ms, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (ms, s, donePaintScreen, minDonePaintScreen);
+}
+
+static Bool
+minPaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ Bool status;
+
+ MIN_SCREEN (s);
+
+ if (ms->moreAdjust)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+
+ UNWRAP (ms, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+ WRAP (ms, s, paintOutput, minPaintOutput);
+
+ return status;
+}
+
+static Bool
+minPaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ MIN_SCREEN (s);
+ MIN_WINDOW (w);
+
+ if (mw->adjust)
+ {
+ FragmentAttrib fragment;
+ CompTransform wTransform = *transform;
+
+ if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)
+ return FALSE;
+
+ UNWRAP (ms, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region,
+ mask | PAINT_WINDOW_NO_CORE_INSTANCE_MASK);
+ WRAP (ms, s, paintWindow, minPaintWindow);
+
+ initFragmentAttrib (&fragment, &w->lastPaint);
+
+ if (w->alpha || fragment.opacity != OPAQUE)
+ mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
+
+ matrixTranslate (&wTransform, w->attrib.x, w->attrib.y, 0.0f);
+ matrixScale (&wTransform, mw->xScale, mw->yScale, 1.0f);
+ matrixTranslate (&wTransform,
+ mw->tx / mw->xScale - w->attrib.x,
+ mw->ty / mw->yScale - w->attrib.y,
+ 0.0f);
+
+ glPushMatrix ();
+ glLoadMatrixf (wTransform.m);
+
+ (*s->drawWindow) (w, &wTransform, &fragment, region,
+ mask | PAINT_WINDOW_TRANSFORMED_MASK);
+
+ glPopMatrix ();
+ }
+ else
+ {
+ /* no core instance from windows that have been animated */
+ if (mw->state == IconicState)
+ mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
+
+ UNWRAP (ms, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (ms, s, paintWindow, minPaintWindow);
+ }
+
+ return status;
+}
+
+static void
+minHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompWindow *w;
+
+ MIN_DISPLAY (d);
+
+ switch (event->type) {
+ case MapNotify:
+ w = findWindowAtDisplay (d, event->xmap.window);
+ if (w)
+ {
+ MIN_WINDOW (w);
+
+ if (mw->adjust)
+ mw->state = mw->newState;
+
+ if (mw->region)
+ w->height = 0;
+
+ mw->ignoreDamage = TRUE;
+ while (mw->unmapCnt)
+ {
+ unmapWindow (w);
+ mw->unmapCnt--;
+ }
+ mw->ignoreDamage = FALSE;
+ }
+ break;
+ case UnmapNotify:
+ w = findWindowAtDisplay (d, event->xunmap.window);
+ if (w)
+ {
+ MIN_SCREEN (w->screen);
+
+ if (w->pendingUnmaps && onCurrentDesktop (w)) /* Normal -> Iconic */
+ {
+ CompMatch *match =
+ &ms->opt[MIN_SCREEN_OPTION_WINDOW_MATCH].value.match;
+
+ MIN_WINDOW (w);
+
+ if (w->shaded)
+ {
+ if (!mw->region)
+ mw->region = XCreateRegion ();
+
+ if (mw->region && ms->shadeStep)
+ {
+ XSubtractRegion (w->region, &emptyRegion, mw->region);
+ XOffsetRegion (mw->region, -w->attrib.x,
+ w->attrib.height +
+ w->attrib.border_width * 2 -
+ w->height - w->attrib.y);
+
+ mw->shade = w->height;
+
+ mw->adjust = FALSE;
+ ms->moreAdjust = TRUE;
+
+ mw->unmapCnt++;
+ w->unmapRefCnt++;
+
+ addWindowDamage (w);
+ }
+ }
+ else if (!w->invisible && matchEval (match, w))
+ {
+ if (w->iconGeometrySet)
+ {
+ mw->newState = IconicState;
+
+ mw->xScale = w->paint.xScale;
+ mw->yScale = w->paint.yScale;
+ mw->tx = w->attrib.x - w->serverX;
+ mw->ty = w->attrib.y - w->serverY;
+
+ if (mw->region)
+ {
+ XDestroyRegion (mw->region);
+ mw->region = NULL;
+ }
+
+ mw->shade = MAXSHORT;
+
+ mw->adjust = TRUE;
+ ms->moreAdjust = TRUE;
+
+ mw->unmapCnt++;
+ w->unmapRefCnt++;
+
+ addWindowDamage (w);
+ }
+ }
+ }
+ else /* X -> Withdrawn */
+ {
+ MIN_WINDOW (w);
+
+ if (mw->adjust)
+ {
+ mw->adjust = FALSE;
+ mw->xScale = mw->yScale = 1.0f;
+ mw->tx = mw->ty = 0.0f;
+ mw->xVelocity = mw->yVelocity = 0.0f;
+ mw->xScaleVelocity = mw->yScaleVelocity = 1.0f;
+ mw->shade = MAXSHORT;
+
+ if (mw->region)
+ {
+ XDestroyRegion (mw->region);
+ mw->region = NULL;
+ }
+ }
+
+ mw->state = NormalState;
+ }
+ }
+ default:
+ break;
+ }
+
+ UNWRAP (md, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (md, d, handleEvent, minHandleEvent);
+}
+
+static Bool
+minDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status = FALSE;
+
+ MIN_SCREEN (w->screen);
+ MIN_WINDOW (w);
+
+ if (mw->ignoreDamage)
+ return TRUE;
+
+ if (initial)
+ {
+ if (mw->state == IconicState)
+ {
+ CompMatch *match =
+ &ms->opt[MIN_SCREEN_OPTION_WINDOW_MATCH].value.match;
+
+ mw->state = NormalState;
+
+ if (!w->invisible &&
+ w->iconGeometrySet &&
+ matchEval (match, w))
+ {
+ if (!mw->adjust)
+ {
+ mw->adjust = TRUE;
+ ms->moreAdjust = TRUE;
+
+ mw->tx = w->iconGeometry.x - w->serverX;
+ mw->ty = w->iconGeometry.y - w->serverY;
+ mw->xScale = (float) w->iconGeometry.width / w->width;
+ mw->yScale = (float) w->iconGeometry.height / w->height;
+
+ addWindowDamage (w);
+ }
+ }
+ }
+ else if (mw->region && mw->shade < w->height)
+ {
+ if (ms->shadeStep && !w->invisible)
+ {
+ XSubtractRegion (w->region, &emptyRegion, mw->region);
+ XOffsetRegion (mw->region, -w->attrib.x, -w->attrib.y);
+
+ /* bind pixmap here so we have something to unshade with */
+ if (!w->texture->pixmap && !w->bindFailed)
+ bindWindow (w);
+
+ ms->moreAdjust = TRUE;
+ }
+ else
+ {
+ mw->shade = MAXSHORT;
+ }
+ }
+
+ mw->newState = NormalState;
+ }
+ else if (mw->adjust)
+ {
+ damageTransformedWindowRect (w,
+ mw->xScale,
+ mw->yScale,
+ mw->tx,
+ mw->ty,
+ rect);
+
+ status = TRUE;
+ }
+
+ UNWRAP (ms, w->screen, damageWindowRect);
+ status |= (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (ms, w->screen, damageWindowRect, minDamageWindowRect);
+
+ return status;
+}
+
+static Bool
+minFocusWindow (CompWindow *w)
+{
+ Bool status;
+
+ MIN_SCREEN (w->screen);
+ MIN_WINDOW (w);
+
+ if (mw->unmapCnt)
+ return FALSE;
+
+ UNWRAP (ms, w->screen, focusWindow);
+ status = (*w->screen->focusWindow) (w);
+ WRAP (ms, w->screen, focusWindow, minFocusWindow);
+
+ return status;
+}
+
+static Bool
+minInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ MinDisplay *md;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ md = malloc (sizeof (MinDisplay));
+ if (!md)
+ return FALSE;
+
+ md->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (md->screenPrivateIndex < 0)
+ {
+ free (md);
+ return FALSE;
+ }
+
+ md->winChangeStateAtom = XInternAtom (d->display, "WM_CHANGE_STATE", 0);
+
+ WRAP (md, d, handleEvent, minHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = md;
+
+ return TRUE;
+}
+
+static void
+minFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ MIN_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, md->screenPrivateIndex);
+
+ UNWRAP (md, d, handleEvent);
+
+ free (md);
+}
+
+static const CompMetadataOptionInfo minScreenOptionInfo[] = {
+ { "speed", "float", "<min>0.1</min>", 0, 0 },
+ { "timestep", "float", "<min>0.1</min>", 0, 0 },
+ { "window_match", "match", 0, 0, 0 },
+ { "shade_resistance", "int", "<min>0</min><max>100</max>", 0, 0 }
+};
+
+static Bool
+minInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ MinScreen *ms;
+
+ MIN_DISPLAY (s->display);
+
+ ms = malloc (sizeof (MinScreen));
+ if (!ms)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &minMetadata,
+ minScreenOptionInfo,
+ ms->opt,
+ MIN_SCREEN_OPTION_NUM))
+ {
+ free (ms);
+ return FALSE;
+ }
+
+ ms->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (ms->windowPrivateIndex < 0)
+ {
+ compFiniScreenOptions (s, ms->opt, MIN_SCREEN_OPTION_NUM);
+ free (ms);
+ return FALSE;
+ }
+
+ ms->moreAdjust = FALSE;
+ ms->shadeStep = ms->opt[MIN_SCREEN_OPTION_SHADE_RESISTANCE].rest.i.max -
+ ms->opt[MIN_SCREEN_OPTION_SHADE_RESISTANCE].value.i + 1;
+
+ WRAP (ms, s, preparePaintScreen, minPreparePaintScreen);
+ WRAP (ms, s, donePaintScreen, minDonePaintScreen);
+ WRAP (ms, s, paintOutput, minPaintOutput);
+ WRAP (ms, s, paintWindow, minPaintWindow);
+ WRAP (ms, s, damageWindowRect, minDamageWindowRect);
+ WRAP (ms, s, focusWindow, minFocusWindow);
+
+ s->base.privates[md->screenPrivateIndex].ptr = ms;
+
+ return TRUE;
+}
+
+static void
+minFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ MIN_SCREEN (s);
+
+ freeWindowPrivateIndex (s, ms->windowPrivateIndex);
+
+ UNWRAP (ms, s, preparePaintScreen);
+ UNWRAP (ms, s, donePaintScreen);
+ UNWRAP (ms, s, paintOutput);
+ UNWRAP (ms, s, paintWindow);
+ UNWRAP (ms, s, damageWindowRect);
+ UNWRAP (ms, s, focusWindow);
+
+ compFiniScreenOptions (s, ms->opt, MIN_SCREEN_OPTION_NUM);
+
+ free (ms);
+}
+
+static Bool
+minInitWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ MinWindow *mw;
+
+ MIN_SCREEN (w->screen);
+
+ mw = malloc (sizeof (MinWindow));
+ if (!mw)
+ return FALSE;
+
+ mw->xScale = mw->yScale = 1.0f;
+ mw->tx = mw->ty = 0.0f;
+ mw->adjust = FALSE;
+ mw->xVelocity = mw->yVelocity = 0.0f;
+ mw->xScaleVelocity = mw->yScaleVelocity = 1.0f;
+
+ mw->unmapCnt = 0;
+
+ mw->ignoreDamage = FALSE;
+
+ if (w->state & CompWindowStateHiddenMask)
+ {
+ if (w->shaded)
+ {
+ mw->state = mw->newState = NormalState;
+ mw->shade = 0;
+ mw->region = XCreateRegion ();
+ }
+ else
+ {
+ mw->state = mw->newState = minGetWindowState (w);
+ mw->shade = MAXSHORT;
+ mw->region = NULL;
+ }
+ }
+ else
+ {
+ mw->state = mw->newState = NormalState;
+ mw->shade = MAXSHORT;
+ mw->region = NULL;
+ }
+
+ w->base.privates[ms->windowPrivateIndex].ptr = mw;
+
+ return TRUE;
+}
+
+static void
+minFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ MIN_WINDOW (w);
+
+ mw->ignoreDamage = TRUE;
+ while (mw->unmapCnt--)
+ unmapWindow (w);
+ mw->ignoreDamage = FALSE;
+
+ if (mw->region)
+ XDestroyRegion (mw->region);
+
+ free (mw);
+}
+
+static CompBool
+minInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) minInitDisplay,
+ (InitPluginObjectProc) minInitScreen,
+ (InitPluginObjectProc) minInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+minFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) minFiniDisplay,
+ (FiniPluginObjectProc) minFiniScreen,
+ (FiniPluginObjectProc) minFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+minGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) 0, /* GetDisplayOptions */
+ (GetPluginObjectOptionsProc) minGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+minSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) 0, /* SetDisplayOption */
+ (SetPluginObjectOptionProc) minSetScreenOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+minInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&minMetadata,
+ p->vTable->name, 0, 0,
+ minScreenOptionInfo,
+ MIN_SCREEN_OPTION_NUM))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&minMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&minMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+minFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&minMetadata);
+}
+
+static CompMetadata *
+minGetMetadata (CompPlugin *plugin)
+{
+ return &minMetadata;
+}
+
+static CompPluginVTable minVTable = {
+ "minimize",
+ minGetMetadata,
+ minInit,
+ minFini,
+ minInitObject,
+ minFiniObject,
+ minGetObjectOptions,
+ minSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &minVTable;
+}
diff --git a/plugins/move.c b/plugins/move.c
new file mode 100644
index 0000000..2255fd8
--- /dev/null
+++ b/plugins/move.c
@@ -0,0 +1,1048 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/cursorfont.h>
+
+#include <compiz-core.h>
+
+static CompMetadata moveMetadata;
+
+struct _MoveKeys {
+ char *name;
+ int dx;
+ int dy;
+} mKeys[] = {
+ { "Left", -1, 0 },
+ { "Right", 1, 0 },
+ { "Up", 0, -1 },
+ { "Down", 0, 1 }
+};
+
+#define NUM_KEYS (sizeof (mKeys) / sizeof (mKeys[0]))
+
+#define KEY_MOVE_INC 24
+
+#define SNAP_BACK 20
+#define SNAP_OFF 100
+
+static int displayPrivateIndex;
+
+#define MOVE_DISPLAY_OPTION_INITIATE_BUTTON 0
+#define MOVE_DISPLAY_OPTION_INITIATE_KEY 1
+#define MOVE_DISPLAY_OPTION_OPACITY 2
+#define MOVE_DISPLAY_OPTION_CONSTRAIN_Y 3
+#define MOVE_DISPLAY_OPTION_SNAPOFF_MAXIMIZED 4
+#define MOVE_DISPLAY_OPTION_LAZY_POSITIONING 5
+#define MOVE_DISPLAY_OPTION_NUM 6
+
+typedef struct _MoveDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompOption opt[MOVE_DISPLAY_OPTION_NUM];
+
+ CompWindow *w;
+ int savedX;
+ int savedY;
+ int x;
+ int y;
+ Region region;
+ int status;
+ KeyCode key[NUM_KEYS];
+
+ int releaseButton;
+
+ GLushort moveOpacity;
+} MoveDisplay;
+
+typedef struct _MoveScreen {
+ PaintWindowProc paintWindow;
+
+ int grabIndex;
+
+ Cursor moveCursor;
+
+ unsigned int origState;
+
+ int snapOffY;
+ int snapBackY;
+} MoveScreen;
+
+#define GET_MOVE_DISPLAY(d) \
+ ((MoveDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define MOVE_DISPLAY(d) \
+ MoveDisplay *md = GET_MOVE_DISPLAY (d)
+
+#define GET_MOVE_SCREEN(s, md) \
+ ((MoveScreen *) (s)->base.privates[(md)->screenPrivateIndex].ptr)
+
+#define MOVE_SCREEN(s) \
+ MoveScreen *ms = GET_MOVE_SCREEN (s, GET_MOVE_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static Bool
+moveInitiate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompWindow *w;
+ Window xid;
+
+ MOVE_DISPLAY (d);
+
+ xid = getIntOptionNamed (option, nOption, "window", 0);
+
+ w = findWindowAtDisplay (d, xid);
+ if (w && (w->actions & CompWindowActionMoveMask))
+ {
+ XRectangle workArea;
+ unsigned int mods;
+ int x, y, button;
+
+ MOVE_SCREEN (w->screen);
+
+ mods = getIntOptionNamed (option, nOption, "modifiers", 0);
+
+ x = getIntOptionNamed (option, nOption, "x",
+ w->attrib.x + (w->width / 2));
+ y = getIntOptionNamed (option, nOption, "y",
+ w->attrib.y + (w->height / 2));
+
+ button = getIntOptionNamed (option, nOption, "button", -1);
+
+ if (otherScreenGrabExist (w->screen, "move", 0))
+ return FALSE;
+
+ if (md->w)
+ return FALSE;
+
+ if (w->type & (CompWindowTypeDesktopMask |
+ CompWindowTypeDockMask |
+ CompWindowTypeFullscreenMask))
+ return FALSE;
+
+ if (w->attrib.override_redirect)
+ return FALSE;
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ if (md->region)
+ {
+ XDestroyRegion (md->region);
+ md->region = NULL;
+ }
+
+ md->status = RectangleOut;
+
+ md->savedX = w->serverX;
+ md->savedY = w->serverY;
+
+ md->x = 0;
+ md->y = 0;
+
+ lastPointerX = x;
+ lastPointerY = y;
+
+ ms->origState = w->state;
+
+ getWorkareaForOutput (w->screen,
+ outputDeviceForWindow (w),
+ &workArea);
+
+ ms->snapBackY = w->serverY - workArea.y;
+ ms->snapOffY = y - workArea.y;
+
+ if (!ms->grabIndex)
+ ms->grabIndex = pushScreenGrab (w->screen, ms->moveCursor, "move");
+
+ if (ms->grabIndex)
+ {
+ md->w = w;
+
+ md->releaseButton = button;
+
+ (w->screen->windowGrabNotify) (w, x, y, mods,
+ CompWindowGrabMoveMask |
+ CompWindowGrabButtonMask);
+
+ if (d->opt[COMP_DISPLAY_OPTION_RAISE_ON_CLICK].value.b)
+ updateWindowAttributes (w,
+ CompStackingUpdateModeAboveFullscreen);
+
+ if (state & CompActionStateInitKey)
+ {
+ int xRoot, yRoot;
+
+ xRoot = w->attrib.x + (w->width / 2);
+ yRoot = w->attrib.y + (w->height / 2);
+
+ warpPointer (w->screen, xRoot - pointerX, yRoot - pointerY);
+ }
+
+ if (md->moveOpacity != OPAQUE)
+ addWindowDamage (w);
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+moveTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ MOVE_DISPLAY (d);
+
+ if (md->w)
+ {
+ MOVE_SCREEN (md->w->screen);
+
+ if (state & CompActionStateCancel)
+ moveWindow (md->w,
+ md->savedX - md->w->attrib.x,
+ md->savedY - md->w->attrib.y,
+ TRUE, FALSE);
+
+ syncWindowPosition (md->w);
+
+ /* update window attributes as window constraints may have
+ changed - needed e.g. if a maximized window was moved
+ to another output device */
+ updateWindowAttributes (md->w, CompStackingUpdateModeNone);
+
+ (md->w->screen->windowUngrabNotify) (md->w);
+
+ if (ms->grabIndex)
+ {
+ removeScreenGrab (md->w->screen, ms->grabIndex, NULL);
+ ms->grabIndex = 0;
+ }
+
+ if (md->moveOpacity != OPAQUE)
+ addWindowDamage (md->w);
+
+ md->w = 0;
+ md->releaseButton = 0;
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+/* creates a region containing top and bottom struts. only struts that are
+ outside the screen workarea are considered. */
+static Region
+moveGetYConstrainRegion (CompScreen *s)
+{
+ CompWindow *w;
+ Region region;
+ REGION r;
+ XRectangle workArea;
+ BoxRec extents;
+ int i;
+
+ region = XCreateRegion ();
+ if (!region)
+ return NULL;
+
+ r.rects = &r.extents;
+ r.numRects = r.size = 1;
+
+ r.extents.x1 = MINSHORT;
+ r.extents.y1 = 0;
+ r.extents.x2 = 0;
+ r.extents.y2 = s->height;
+
+ XUnionRegion (&r, region, region);
+
+ r.extents.x1 = s->width;
+ r.extents.x2 = MAXSHORT;
+
+ XUnionRegion (&r, region, region);
+
+ for (i = 0; i < s->nOutputDev; i++)
+ {
+ XUnionRegion (&s->outputDev[i].region, region, region);
+
+ getWorkareaForOutput (s, i, &workArea);
+ extents = s->outputDev[i].region.extents;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (!w->mapNum)
+ continue;
+
+ if (w->struts)
+ {
+ r.extents.x1 = w->struts->top.x;
+ r.extents.y1 = w->struts->top.y;
+ r.extents.x2 = r.extents.x1 + w->struts->top.width;
+ r.extents.y2 = r.extents.y1 + w->struts->top.height;
+
+ if (r.extents.x1 < extents.x1)
+ r.extents.x1 = extents.x1;
+ if (r.extents.x2 > extents.x2)
+ r.extents.x2 = extents.x2;
+ if (r.extents.y1 < extents.y1)
+ r.extents.y1 = extents.y1;
+ if (r.extents.y2 > extents.y2)
+ r.extents.y2 = extents.y2;
+
+ if (r.extents.x1 < r.extents.x2 && r.extents.y1 < r.extents.y2)
+ {
+ if (r.extents.y2 <= workArea.y)
+ XSubtractRegion (region, &r, region);
+ }
+
+ r.extents.x1 = w->struts->bottom.x;
+ r.extents.y1 = w->struts->bottom.y;
+ r.extents.x2 = r.extents.x1 + w->struts->bottom.width;
+ r.extents.y2 = r.extents.y1 + w->struts->bottom.height;
+
+ if (r.extents.x1 < extents.x1)
+ r.extents.x1 = extents.x1;
+ if (r.extents.x2 > extents.x2)
+ r.extents.x2 = extents.x2;
+ if (r.extents.y1 < extents.y1)
+ r.extents.y1 = extents.y1;
+ if (r.extents.y2 > extents.y2)
+ r.extents.y2 = extents.y2;
+
+ if (r.extents.x1 < r.extents.x2 && r.extents.y1 < r.extents.y2)
+ {
+ if (r.extents.y1 >= (workArea.y + workArea.height))
+ XSubtractRegion (region, &r, region);
+ }
+ }
+ }
+ }
+
+ return region;
+}
+
+static void
+moveHandleMotionEvent (CompScreen *s,
+ int xRoot,
+ int yRoot)
+{
+ MOVE_SCREEN (s);
+
+ if (ms->grabIndex)
+ {
+ CompWindow *w;
+ int dx, dy;
+ int wX, wY;
+ int wWidth, wHeight;
+
+ MOVE_DISPLAY (s->display);
+
+ w = md->w;
+
+ wX = w->serverX;
+ wY = w->serverY;
+ wWidth = w->serverWidth + w->serverBorderWidth * 2;
+ wHeight = w->serverHeight + w->serverBorderWidth * 2;
+
+ md->x += xRoot - lastPointerX;
+ md->y += yRoot - lastPointerY;
+
+ if (w->type & CompWindowTypeFullscreenMask)
+ {
+ dx = dy = 0;
+ }
+ else
+ {
+ XRectangle workArea;
+ int min, max;
+
+ dx = md->x;
+ dy = md->y;
+
+ getWorkareaForOutput (s,
+ outputDeviceForWindow (w),
+ &workArea);
+
+ if (md->opt[MOVE_DISPLAY_OPTION_CONSTRAIN_Y].value.b)
+ {
+ if (!md->region)
+ md->region = moveGetYConstrainRegion (s);
+
+ /* make sure that the top frame extents or the top row of
+ pixels are within what is currently our valid screen
+ region */
+ if (md->region)
+ {
+ int x, y, width, height;
+ int status;
+
+ x = wX + dx - w->input.left;
+ y = wY + dy - w->input.top;
+ width = wWidth + w->input.left + w->input.right;
+ height = w->input.top ? w->input.top : 1;
+
+ status = XRectInRegion (md->region, x, y, width, height);
+
+ /* only constrain movement if previous position was valid */
+ if (md->status == RectangleIn)
+ {
+ int xStatus = status;
+
+ while (dx && xStatus != RectangleIn)
+ {
+ xStatus = XRectInRegion (md->region,
+ x, y - dy,
+ width, height);
+
+ if (xStatus != RectangleIn)
+ dx += (dx < 0) ? 1 : -1;
+
+ x = wX + dx - w->input.left;
+ }
+
+ while (dy && status != RectangleIn)
+ {
+ status = XRectInRegion (md->region,
+ x, y,
+ width, height);
+
+ if (status != RectangleIn)
+ dy += (dy < 0) ? 1 : -1;
+
+ y = wY + dy - w->input.top;
+ }
+ }
+ else
+ {
+ md->status = status;
+ }
+ }
+ }
+
+ if (md->opt[MOVE_DISPLAY_OPTION_SNAPOFF_MAXIMIZED].value.b)
+ {
+ if (w->state & CompWindowStateMaximizedVertMask)
+ {
+ if (abs ((yRoot - workArea.y) - ms->snapOffY) >= SNAP_OFF)
+ {
+ if (!otherScreenGrabExist (s, "move", 0))
+ {
+ int width = w->serverWidth;
+
+ w->saveMask |= CWX | CWY;
+
+ if (w->saveMask & CWWidth)
+ width = w->saveWc.width;
+
+ w->saveWc.x = xRoot - (width >> 1);
+ w->saveWc.y = yRoot + (w->input.top >> 1);
+
+ md->x = md->y = 0;
+
+ maximizeWindow (w, 0);
+
+ ms->snapOffY = ms->snapBackY;
+
+ return;
+ }
+ }
+ }
+ else if (ms->origState & CompWindowStateMaximizedVertMask)
+ {
+ if (abs ((yRoot - workArea.y) - ms->snapBackY) < SNAP_BACK)
+ {
+ if (!otherScreenGrabExist (s, "move", 0))
+ {
+ int wy;
+
+ /* update server position before maximizing
+ window again so that it is maximized on
+ correct output */
+ syncWindowPosition (w);
+
+ maximizeWindow (w, ms->origState);
+
+ wy = workArea.y + (w->input.top >> 1);
+ wy += w->sizeHints.height_inc >> 1;
+
+ warpPointer (s, 0, wy - pointerY);
+
+ return;
+ }
+ }
+ }
+ }
+
+ if (w->state & CompWindowStateMaximizedVertMask)
+ {
+ min = workArea.y + w->input.top;
+ max = workArea.y + workArea.height - w->input.bottom - wHeight;
+
+ if (wY + dy < min)
+ dy = min - wY;
+ else if (wY + dy > max)
+ dy = max - wY;
+ }
+
+ if (w->state & CompWindowStateMaximizedHorzMask)
+ {
+ if (wX > s->width || wX + w->width < 0)
+ return;
+
+ if (wX + wWidth < 0)
+ return;
+
+ min = workArea.x + w->input.left;
+ max = workArea.x + workArea.width - w->input.right - wWidth;
+
+ if (wX + dx < min)
+ dx = min - wX;
+ else if (wX + dx > max)
+ dx = max - wX;
+ }
+ }
+
+ if (dx || dy)
+ {
+ moveWindow (w,
+ wX + dx - w->attrib.x,
+ wY + dy - w->attrib.y,
+ TRUE, FALSE);
+
+ if (md->opt[MOVE_DISPLAY_OPTION_LAZY_POSITIONING].value.b)
+ {
+ /* FIXME: This form of lazy positioning is broken and should
+ be replaced asap. Current code exists just to avoid a
+ major performance regression in the 0.5.2 release. */
+ w->serverX = w->attrib.x;
+ w->serverY = w->attrib.y;
+ }
+ else
+ {
+ syncWindowPosition (w);
+ }
+
+ md->x -= dx;
+ md->y -= dy;
+ }
+ }
+}
+
+static void
+moveHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ MOVE_DISPLAY (d);
+
+ switch (event->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+ MOVE_SCREEN (s);
+
+ if (ms->grabIndex)
+ {
+ if (md->releaseButton == -1 ||
+ md->releaseButton == event->xbutton.button)
+ {
+ CompAction *action;
+ int opt = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
+
+ action = &md->opt[opt].value.action;
+ moveTerminate (d, action, CompActionStateTermButton,
+ NULL, 0);
+ }
+ }
+ }
+ break;
+ case KeyPress:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ MOVE_SCREEN (s);
+
+ if (ms->grabIndex)
+ {
+ int i;
+
+ for (i = 0; i < NUM_KEYS; i++)
+ {
+ if (event->xkey.keycode == md->key[i])
+ {
+ XWarpPointer (d->display, None, None, 0, 0, 0, 0,
+ mKeys[i].dx * KEY_MOVE_INC,
+ mKeys[i].dy * KEY_MOVE_INC);
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ moveHandleMotionEvent (s, pointerX, pointerY);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ s = findScreenAtDisplay (d, event->xcrossing.root);
+ if (s)
+ moveHandleMotionEvent (s, pointerX, pointerY);
+ break;
+ case ClientMessage:
+ if (event->xclient.message_type == d->wmMoveResizeAtom)
+ {
+ CompWindow *w;
+
+ if (event->xclient.data.l[2] == WmMoveResizeMove ||
+ event->xclient.data.l[2] == WmMoveResizeMoveKeyboard)
+ {
+ w = findWindowAtDisplay (d, event->xclient.window);
+ if (w)
+ {
+ CompOption o[5];
+ int xRoot, yRoot;
+ int option;
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "window";
+ o[0].value.i = event->xclient.window;
+
+ if (event->xclient.data.l[2] == WmMoveResizeMoveKeyboard)
+ {
+ option = MOVE_DISPLAY_OPTION_INITIATE_KEY;
+
+ moveInitiate (d, &md->opt[option].value.action,
+ CompActionStateInitKey,
+ o, 1);
+ }
+ else
+ {
+ unsigned int mods;
+ Window root, child;
+ int i;
+
+ option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
+
+ XQueryPointer (d->display, w->screen->root,
+ &root, &child, &xRoot, &yRoot,
+ &i, &i, &mods);
+
+ /* TODO: not only button 1 */
+ if (mods & Button1Mask)
+ {
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "modifiers";
+ o[1].value.i = mods;
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "x";
+ o[2].value.i = event->xclient.data.l[0];
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "y";
+ o[3].value.i = event->xclient.data.l[1];
+
+ o[4].type = CompOptionTypeInt;
+ o[4].name = "button";
+ o[4].value.i = event->xclient.data.l[3] ?
+ event->xclient.data.l[3] : -1;
+
+ moveInitiate (d,
+ &md->opt[option].value.action,
+ CompActionStateInitButton,
+ o, 5);
+
+ moveHandleMotionEvent (w->screen, xRoot, yRoot);
+ }
+ }
+ }
+ }
+ else if (md->w && event->xclient.data.l[2] == WmMoveResizeCancel)
+ {
+ if (md->w->id == event->xclient.window)
+ {
+ int option;
+
+ option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
+ moveTerminate (d,
+ &md->opt[option].value.action,
+ CompActionStateCancel, NULL, 0);
+ option = MOVE_DISPLAY_OPTION_INITIATE_KEY;
+ moveTerminate (d,
+ &md->opt[option].value.action,
+ CompActionStateCancel, NULL, 0);
+ }
+ }
+ }
+ break;
+ case DestroyNotify:
+ if (md->w && md->w->id == event->xdestroywindow.window)
+ {
+ int option;
+
+ option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
+ moveTerminate (d,
+ &md->opt[option].value.action,
+ 0, NULL, 0);
+ option = MOVE_DISPLAY_OPTION_INITIATE_KEY;
+ moveTerminate (d,
+ &md->opt[option].value.action,
+ 0, NULL, 0);
+ }
+ break;
+ case UnmapNotify:
+ if (md->w && md->w->id == event->xunmap.window)
+ {
+ int option;
+
+ option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
+ moveTerminate (d,
+ &md->opt[option].value.action,
+ 0, NULL, 0);
+ option = MOVE_DISPLAY_OPTION_INITIATE_KEY;
+ moveTerminate (d,
+ &md->opt[option].value.action,
+ 0, NULL, 0);
+ }
+ default:
+ break;
+ }
+
+ UNWRAP (md, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (md, d, handleEvent, moveHandleEvent);
+}
+
+static Bool
+movePaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ WindowPaintAttrib sAttrib;
+ CompScreen *s = w->screen;
+ Bool status;
+
+ MOVE_SCREEN (s);
+
+ if (ms->grabIndex)
+ {
+ MOVE_DISPLAY (s->display);
+
+ if (md->w == w && md->moveOpacity != OPAQUE)
+ {
+ /* modify opacity of windows that are not active */
+ sAttrib = *attrib;
+ attrib = &sAttrib;
+
+ sAttrib.opacity = (sAttrib.opacity * md->moveOpacity) >> 16;
+ }
+ }
+
+ UNWRAP (ms, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (ms, s, paintWindow, movePaintWindow);
+
+ return status;
+}
+
+static CompOption *
+moveGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ MOVE_DISPLAY (display);
+
+ *count = NUM_OPTIONS (md);
+ return md->opt;
+}
+
+static Bool
+moveSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ MOVE_DISPLAY (display);
+
+ o = compFindOption (md->opt, NUM_OPTIONS (md), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case MOVE_DISPLAY_OPTION_OPACITY:
+ if (compSetIntOption (o, value))
+ {
+ md->moveOpacity = (o->value.i * OPAQUE) / 100;
+ return TRUE;
+ }
+ break;
+ default:
+ return compSetDisplayOption (display, o, value);
+ }
+
+ return FALSE;
+}
+
+static const CompMetadataOptionInfo moveDisplayOptionInfo[] = {
+ { "initiate_button", "button", 0, moveInitiate, moveTerminate },
+ { "initiate_key", "key", 0, moveInitiate, moveTerminate },
+ { "opacity", "int", "<min>0</min><max>100</max>", 0, 0 },
+ { "constrain_y", "bool", 0, 0, 0 },
+ { "snapoff_maximized", "bool", 0, 0, 0 },
+ { "lazy_positioning", "bool", 0, 0, 0 }
+};
+
+static Bool
+moveInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ MoveDisplay *md;
+ int i;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ md = malloc (sizeof (MoveDisplay));
+ if (!md)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &moveMetadata,
+ moveDisplayOptionInfo,
+ md->opt,
+ MOVE_DISPLAY_OPTION_NUM))
+ {
+ free (md);
+ return FALSE;
+ }
+
+ md->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (md->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, md->opt, MOVE_DISPLAY_OPTION_NUM);
+ free (md);
+ return FALSE;
+ }
+
+ md->moveOpacity =
+ (md->opt[MOVE_DISPLAY_OPTION_OPACITY].value.i * OPAQUE) / 100;
+
+ md->w = 0;
+ md->region = NULL;
+ md->status = RectangleOut;
+ md->releaseButton = 0;
+
+ for (i = 0; i < NUM_KEYS; i++)
+ md->key[i] = XKeysymToKeycode (d->display,
+ XStringToKeysym (mKeys[i].name));
+
+ WRAP (md, d, handleEvent, moveHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = md;
+
+ return TRUE;
+}
+
+static void
+moveFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ MOVE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, md->screenPrivateIndex);
+
+ UNWRAP (md, d, handleEvent);
+
+ compFiniDisplayOptions (d, md->opt, MOVE_DISPLAY_OPTION_NUM);
+
+ free (md);
+}
+
+static Bool
+moveInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ MoveScreen *ms;
+
+ MOVE_DISPLAY (s->display);
+
+ ms = malloc (sizeof (MoveScreen));
+ if (!ms)
+ return FALSE;
+
+ ms->grabIndex = 0;
+
+ ms->moveCursor = XCreateFontCursor (s->display->display, XC_fleur);
+
+ WRAP (ms, s, paintWindow, movePaintWindow);
+
+ s->base.privates[md->screenPrivateIndex].ptr = ms;
+
+ return TRUE;
+}
+
+static void
+moveFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ MOVE_SCREEN (s);
+
+ UNWRAP (ms, s, paintWindow);
+
+ if (ms->moveCursor)
+ XFreeCursor (s->display->display, ms->moveCursor);
+
+ free (ms);
+}
+
+static CompBool
+moveInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) moveInitDisplay,
+ (InitPluginObjectProc) moveInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+moveFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) moveFiniDisplay,
+ (FiniPluginObjectProc) moveFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+moveGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) moveGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+moveSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) moveSetDisplayOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+moveInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&moveMetadata,
+ p->vTable->name,
+ moveDisplayOptionInfo,
+ MOVE_DISPLAY_OPTION_NUM,
+ 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&moveMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&moveMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+moveFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&moveMetadata);
+}
+
+static CompMetadata *
+moveGetMetadata (CompPlugin *plugin)
+{
+ return &moveMetadata;
+}
+
+CompPluginVTable moveVTable = {
+ "move",
+ moveGetMetadata,
+ moveInit,
+ moveFini,
+ moveInitObject,
+ moveFiniObject,
+ moveGetObjectOptions,
+ moveSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &moveVTable;
+}
diff --git a/plugins/obs.c b/plugins/obs.c
new file mode 100644
index 0000000..a8d67f1
--- /dev/null
+++ b/plugins/obs.c
@@ -0,0 +1,746 @@
+/*
+ * Copyright © 2008 Danny Baumann
+ *
+ * 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, and that the name of
+ * Danny Baumann not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Danny Baumann makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * DANNY BAUMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Danny Baumann <dannybaumann@web.de>
+ */
+
+#include <compiz-core.h>
+
+static CompMetadata obsMetadata;
+
+static int displayPrivateIndex;
+
+#define OBS_DISPLAY_OPTION_OPACITY_INCREASE_KEY 0
+#define OBS_DISPLAY_OPTION_OPACITY_INCREASE_BUTTON 1
+#define OBS_DISPLAY_OPTION_OPACITY_DECREASE_KEY 2
+#define OBS_DISPLAY_OPTION_OPACITY_DECREASE_BUTTON 3
+#define OBS_DISPLAY_OPTION_SATURATION_INCREASE_KEY 4
+#define OBS_DISPLAY_OPTION_SATURATION_INCREASE_BUTTON 5
+#define OBS_DISPLAY_OPTION_SATURATION_DECREASE_KEY 6
+#define OBS_DISPLAY_OPTION_SATURATION_DECREASE_BUTTON 7
+#define OBS_DISPLAY_OPTION_BRIGHTNESS_INCREASE_KEY 8
+#define OBS_DISPLAY_OPTION_BRIGHTNESS_INCREASE_BUTTON 9
+#define OBS_DISPLAY_OPTION_BRIGHTNESS_DECREASE_KEY 10
+#define OBS_DISPLAY_OPTION_BRIGHTNESS_DECREASE_BUTTON 11
+#define OBS_DISPLAY_OPTION_NUM 12
+
+typedef struct _ObsDisplay
+{
+ int screenPrivateIndex;
+
+ HandleEventProc handleEvent;
+ MatchExpHandlerChangedProc matchExpHandlerChanged;
+ MatchPropertyChangedProc matchPropertyChanged;
+
+ CompOption opt[OBS_DISPLAY_OPTION_NUM];
+} ObsDisplay;
+
+#define OBS_SCREEN_OPTION_OPACITY_STEP 0
+#define OBS_SCREEN_OPTION_SATURATION_STEP 1
+#define OBS_SCREEN_OPTION_BRIGHTNESS_STEP 2
+#define OBS_SCREEN_OPTION_OPACITY_MATCHES 3
+#define OBS_SCREEN_OPTION_OPACITY_VALUES 4
+#define OBS_SCREEN_OPTION_SATURATION_MATCHES 5
+#define OBS_SCREEN_OPTION_SATURATION_VALUES 6
+#define OBS_SCREEN_OPTION_BRIGHTNESS_MATCHES 7
+#define OBS_SCREEN_OPTION_BRIGHTNESS_VALUES 8
+#define OBS_SCREEN_OPTION_NUM 9
+
+#define MODIFIER_OPACITY 0
+#define MODIFIER_SATURATION 1
+#define MODIFIER_BRIGHTNESS 2
+#define MODIFIER_COUNT 3
+
+typedef struct _ObsScreen
+{
+ int windowPrivateIndex;
+
+ PaintWindowProc paintWindow;
+ DrawWindowProc drawWindow;
+
+ CompOption *stepOptions[MODIFIER_COUNT];
+ CompOption *matchOptions[MODIFIER_COUNT];
+ CompOption *valueOptions[MODIFIER_COUNT];
+
+ CompOption opt[OBS_SCREEN_OPTION_NUM];
+} ObsScreen;
+
+typedef struct _ObsWindow
+{
+ int customFactor[MODIFIER_COUNT];
+ int matchFactor[MODIFIER_COUNT];
+} ObsWindow;
+
+#define GET_OBS_DISPLAY(d) \
+ ((ObsDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+#define OBS_DISPLAY(d) \
+ ObsDisplay *od = GET_OBS_DISPLAY (d)
+
+#define GET_OBS_SCREEN(s, od) \
+ ((ObsScreen *) (s)->base.privates[(od)->screenPrivateIndex].ptr)
+#define OBS_SCREEN(s) \
+ ObsScreen *os = GET_OBS_SCREEN (s, GET_OBS_DISPLAY (s->display))
+
+#define GET_OBS_WINDOW(w, os) \
+ ((ObsWindow *) (w)->base.privates[(os)->windowPrivateIndex].ptr)
+#define OBS_WINDOW(w) \
+ ObsWindow *ow = GET_OBS_WINDOW (w, \
+ GET_OBS_SCREEN (w->screen, \
+ GET_OBS_DISPLAY (w->screen->display)))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static void
+changePaintModifier (CompWindow *w,
+ int modifier,
+ int direction)
+{
+ int value;
+
+ OBS_SCREEN (w->screen);
+ OBS_WINDOW (w);
+
+ if (w->attrib.override_redirect)
+ return;
+
+ if (modifier == MODIFIER_OPACITY && (w->type & CompWindowTypeDesktopMask))
+ return;
+
+ value = ow->customFactor[modifier];
+ value += os->stepOptions[modifier]->value.i * direction;
+
+ value = MIN (value, 100);
+ value = MAX (value, os->stepOptions[modifier]->value.i);
+
+ if (value != ow->customFactor[modifier])
+ {
+ ow->customFactor[modifier] = value;
+ addWindowDamage (w);
+ }
+}
+
+static void
+updatePaintModifier (CompWindow *w,
+ int modifier)
+{
+ int lastFactor;
+
+ OBS_WINDOW (w);
+ OBS_SCREEN (w->screen);
+
+ lastFactor = ow->customFactor[modifier];
+
+ if ((w->type & CompWindowTypeDesktopMask) && (modifier == MODIFIER_OPACITY))
+ {
+ ow->customFactor[modifier] = 100;
+ ow->matchFactor[modifier] = 100;
+ }
+ else
+ {
+ int i, min, lastMatchFactor;
+ CompOption *matches, *values;
+
+ matches = os->matchOptions[modifier];
+ values = os->valueOptions[modifier];
+ min = MIN (matches->value.list.nValue, values->value.list.nValue);
+
+ lastMatchFactor = ow->matchFactor[modifier];
+ ow->matchFactor[modifier] = 100;
+
+ for (i = 0; i < min; i++)
+ {
+ if (matchEval (&matches->value.list.value[i].match, w))
+ {
+ ow->matchFactor[modifier] = values->value.list.value[i].i;
+ break;
+ }
+ }
+
+ if (ow->customFactor[modifier] == lastMatchFactor)
+ ow->customFactor[modifier] = ow->matchFactor[modifier];
+ }
+
+ if (ow->customFactor[modifier] != lastFactor)
+ addWindowDamage (w);
+}
+
+static Bool
+alterPaintModifier (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompWindow *w;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "window", 0);
+ w = findTopLevelWindowAtDisplay (d, xid);
+
+ if (w)
+ changePaintModifier (w, abs (action->priv.val) - 1,
+ (action->priv.val < 0) ? -1 : 1);
+
+ return TRUE;
+}
+
+static Bool
+obsPaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ OBS_SCREEN (s);
+ OBS_WINDOW (w);
+
+ if (ow->customFactor[MODIFIER_OPACITY] != 100)
+ mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
+
+ UNWRAP (os, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (os, s, paintWindow, obsPaintWindow);
+
+ return status;
+}
+
+/* Note: Normally plugins should wrap into PaintWindow to modify opacity,
+ brightness and saturation. As some plugins bypass paintWindow when
+ they draw windows and our custom values always need to be applied,
+ we wrap into DrawWindow here */
+
+static Bool
+obsDrawWindow (CompWindow *w,
+ const CompTransform *transform,
+ const FragmentAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool hasCustomFactor = FALSE;
+ Bool status;
+ int i;
+
+ OBS_SCREEN (s);
+ OBS_WINDOW (w);
+
+ for (i = 0; i < MODIFIER_COUNT; i++)
+ if (ow->customFactor[i] != 100)
+ {
+ hasCustomFactor = TRUE;
+ break;
+ }
+
+ if (hasCustomFactor)
+ {
+ FragmentAttrib fragment = *attrib;
+ int factor;
+
+ factor = ow->customFactor[MODIFIER_OPACITY];
+ if (factor != 100)
+ {
+ fragment.opacity = (int) fragment.opacity * factor / 100;
+ mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
+ }
+
+ factor = ow->customFactor[MODIFIER_BRIGHTNESS];
+ if (factor != 100)
+ fragment.brightness = (int) fragment.brightness * factor / 100;
+
+ factor = ow->customFactor[MODIFIER_SATURATION];
+ if (factor != 100)
+ fragment.saturation = (int) fragment.saturation * factor / 100;
+
+ UNWRAP (os, s, drawWindow);
+ status = (*s->drawWindow) (w, transform, &fragment, region, mask);
+ WRAP (os, s, drawWindow, obsDrawWindow);
+ }
+ else
+ {
+ UNWRAP (os, s, drawWindow);
+ status = (*s->drawWindow) (w, transform, attrib, region, mask);
+ WRAP (os, s, drawWindow, obsDrawWindow);
+ }
+
+ return status;
+}
+
+static void
+obsMatchExpHandlerChanged (CompDisplay *d)
+{
+ CompScreen *s;
+ CompWindow *w;
+ int i;
+
+ OBS_DISPLAY (d);
+
+ UNWRAP (od, d, matchExpHandlerChanged);
+ (*d->matchExpHandlerChanged) (d);
+ WRAP (od, d, matchExpHandlerChanged, obsMatchExpHandlerChanged);
+
+ /* match options are up to date after the call to matchExpHandlerChanged */
+ for (s = d->screens; s; s = s->next)
+ for (w = s->windows; w; w = w->next)
+ for (i = 0; i < MODIFIER_COUNT; i++)
+ updatePaintModifier (w, i);
+}
+
+static void
+obsMatchPropertyChanged (CompDisplay *d,
+ CompWindow *w)
+{
+ int i;
+
+ OBS_DISPLAY (d);
+
+ for (i = 0; i < MODIFIER_COUNT; i++)
+ updatePaintModifier (w, i);
+
+ UNWRAP (od, d, matchPropertyChanged);
+ (*d->matchPropertyChanged) (d, w);
+ WRAP (od, d, matchPropertyChanged, obsMatchPropertyChanged);
+}
+
+static CompOption *
+obsGetDisplayOptions (CompPlugin *p,
+ CompDisplay *display,
+ int *count)
+{
+ OBS_DISPLAY (display);
+
+ *count = NUM_OPTIONS (od);
+ return od->opt;
+}
+
+static Bool
+obsSetDisplayOption (CompPlugin *p,
+ CompDisplay *display,
+ char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ OBS_DISPLAY (display);
+
+ o = compFindOption (od->opt, NUM_OPTIONS (od), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (display, o, value);
+}
+
+static CompOption *
+obsGetScreenOptions (CompPlugin *p,
+ CompScreen *screen,
+ int *count)
+{
+ OBS_SCREEN (screen);
+
+ *count = NUM_OPTIONS (os);
+ return os->opt;
+}
+
+static Bool
+obsSetScreenOption (CompPlugin *p,
+ CompScreen *s,
+ char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int i;
+
+ OBS_SCREEN (s);
+
+ o = compFindOption (os->opt, NUM_OPTIONS (os), name, NULL);
+ if (!o)
+ return FALSE;
+
+ for (i = 0; i < MODIFIER_COUNT; i++)
+ {
+ if (o == os->matchOptions[i])
+ {
+ if (compSetOptionList (o, value))
+ {
+ CompWindow *w;
+ int j;
+
+ for (j = 0; j < o->value.list.nValue; j++)
+ matchUpdate (s->display, &o->value.list.value[j].match);
+
+ for (w = s->windows; w; w = w->next)
+ updatePaintModifier (w, i);
+
+ return TRUE;
+ }
+ }
+ else if (o == os->valueOptions[i])
+ {
+ if (compSetOptionList (o, value))
+ {
+ CompWindow *w;
+
+ for (w = s->windows; w; w = w->next)
+ updatePaintModifier (w, i);
+
+ return TRUE;
+ }
+ }
+ }
+
+ return compSetScreenOption (s, o, value);
+}
+
+static CompOption *
+obsGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) obsGetDisplayOptions,
+ (GetPluginObjectOptionsProc) obsGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+obsSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) obsSetDisplayOption,
+ (SetPluginObjectOptionProc) obsSetScreenOption
+
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static const CompMetadataOptionInfo obsDisplayOptionInfo[] = {
+ { "opacity_increase_key", "key", 0, alterPaintModifier, 0 },
+ { "opacity_increase_button", "button", 0, alterPaintModifier, 0 },
+ { "opacity_decrease_key", "key", 0, alterPaintModifier, 0 },
+ { "opacity_decrease_button", "button", 0, alterPaintModifier, 0 },
+ { "saturation_increase_key", "key", 0, alterPaintModifier, 0 },
+ { "saturation_increase_button", "button", 0, alterPaintModifier, 0 },
+ { "saturation_decrease_key", "key", 0, alterPaintModifier, 0 },
+ { "saturation_decrease_button", "button", 0, alterPaintModifier, 0 },
+ { "brightness_increase_key", "key", 0, alterPaintModifier, 0 },
+ { "brightness_increase_button", "button", 0, alterPaintModifier, 0 },
+ { "brightness_decrease_key", "key", 0, alterPaintModifier, 0 },
+ { "brightness_decrease_button", "button", 0, alterPaintModifier, 0 }
+};
+
+static CompBool
+obsInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ObsDisplay *od;
+ int opt;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ od = malloc (sizeof (ObsDisplay));
+ if (!od)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &obsMetadata,
+ obsDisplayOptionInfo,
+ od->opt,
+ OBS_DISPLAY_OPTION_NUM))
+ {
+ free (od);
+ return FALSE;
+ }
+
+ od->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (od->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, od->opt, OBS_DISPLAY_OPTION_NUM);
+ free (od);
+ return FALSE;
+ }
+
+ opt = OBS_DISPLAY_OPTION_OPACITY_INCREASE_KEY;
+ od->opt[opt].value.action.priv.val = MODIFIER_OPACITY + 1;
+ opt = OBS_DISPLAY_OPTION_OPACITY_DECREASE_KEY;
+ od->opt[opt].value.action.priv.val = -(MODIFIER_OPACITY + 1);
+ opt = OBS_DISPLAY_OPTION_OPACITY_INCREASE_BUTTON;
+ od->opt[opt].value.action.priv.val = MODIFIER_OPACITY + 1;
+ opt = OBS_DISPLAY_OPTION_OPACITY_DECREASE_BUTTON;
+ od->opt[opt].value.action.priv.val = -(MODIFIER_OPACITY + 1);
+
+ opt = OBS_DISPLAY_OPTION_SATURATION_INCREASE_KEY;
+ od->opt[opt].value.action.priv.val = MODIFIER_SATURATION + 1;
+ opt = OBS_DISPLAY_OPTION_SATURATION_DECREASE_KEY;
+ od->opt[opt].value.action.priv.val = -(MODIFIER_SATURATION + 1);
+ opt = OBS_DISPLAY_OPTION_SATURATION_INCREASE_BUTTON;
+ od->opt[opt].value.action.priv.val = MODIFIER_SATURATION + 1;
+ opt = OBS_DISPLAY_OPTION_SATURATION_DECREASE_BUTTON;
+ od->opt[opt].value.action.priv.val = -(MODIFIER_SATURATION + 1);
+
+ opt = OBS_DISPLAY_OPTION_BRIGHTNESS_INCREASE_KEY;
+ od->opt[opt].value.action.priv.val = MODIFIER_BRIGHTNESS + 1;
+ opt = OBS_DISPLAY_OPTION_BRIGHTNESS_DECREASE_KEY;
+ od->opt[opt].value.action.priv.val = -(MODIFIER_BRIGHTNESS + 1);
+ opt = OBS_DISPLAY_OPTION_BRIGHTNESS_INCREASE_BUTTON;
+ od->opt[opt].value.action.priv.val = MODIFIER_BRIGHTNESS + 1;
+ opt = OBS_DISPLAY_OPTION_BRIGHTNESS_DECREASE_BUTTON;
+ od->opt[opt].value.action.priv.val = -(MODIFIER_BRIGHTNESS + 1);
+
+ WRAP (od, d, matchExpHandlerChanged, obsMatchExpHandlerChanged);
+ WRAP (od, d, matchPropertyChanged, obsMatchPropertyChanged);
+
+ d->base.privates[displayPrivateIndex].ptr = od;
+
+ return TRUE;
+}
+
+static void
+obsFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ OBS_DISPLAY (d);
+
+ UNWRAP (od, d, matchExpHandlerChanged);
+ UNWRAP (od, d, matchPropertyChanged);
+
+ freeScreenPrivateIndex (d, od->screenPrivateIndex);
+ compFiniDisplayOptions (d, od->opt, OBS_DISPLAY_OPTION_NUM);
+
+ free (od);
+}
+
+static const CompMetadataOptionInfo obsScreenOptionInfo[] = {
+ { "opacity_step", "int", 0, 0, 0 },
+ { "saturation_step", "int", 0, 0, 0 },
+ { "brightness_step", "int", 0, 0, 0 },
+ { "opacity_matches", "list", "<type>match</type>", 0, 0 },
+ { "opacity_values", "list", "<type>int</type>", 0, 0 },
+ { "saturation_matches", "list", "<type>match</type>", 0, 0 },
+ { "saturation_values", "list", "<type>int</type>", 0, 0 },
+ { "brightness_matches", "list", "<type>match</type>", 0, 0 },
+ { "brightness_values", "list", "<type>int</type>", 0, 0 }
+};
+
+static CompBool
+obsInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ObsScreen *os;
+ int mod;
+
+ OBS_DISPLAY (s->display);
+
+ os = malloc (sizeof (ObsScreen));
+ if (!os)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &obsMetadata,
+ obsScreenOptionInfo,
+ os->opt,
+ OBS_SCREEN_OPTION_NUM))
+ {
+ free (os);
+ return FALSE;
+ }
+
+ os->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (os->windowPrivateIndex < 0)
+ {
+ compFiniScreenOptions (s, os->opt, OBS_SCREEN_OPTION_NUM);
+ free (os);
+ return FALSE;
+ }
+
+ mod = MODIFIER_OPACITY;
+ os->stepOptions[mod] = &os->opt[OBS_SCREEN_OPTION_OPACITY_STEP];
+ os->matchOptions[mod] = &os->opt[OBS_SCREEN_OPTION_OPACITY_MATCHES];
+ os->valueOptions[mod] = &os->opt[OBS_SCREEN_OPTION_OPACITY_VALUES];
+
+ mod = MODIFIER_SATURATION;
+ os->stepOptions[mod] = &os->opt[OBS_SCREEN_OPTION_SATURATION_STEP];
+ os->matchOptions[mod] = &os->opt[OBS_SCREEN_OPTION_SATURATION_MATCHES];
+ os->valueOptions[mod] = &os->opt[OBS_SCREEN_OPTION_SATURATION_VALUES];
+
+ mod = MODIFIER_BRIGHTNESS;
+ os->stepOptions[mod] = &os->opt[OBS_SCREEN_OPTION_BRIGHTNESS_STEP];
+ os->matchOptions[mod] = &os->opt[OBS_SCREEN_OPTION_BRIGHTNESS_MATCHES];
+ os->valueOptions[mod] = &os->opt[OBS_SCREEN_OPTION_BRIGHTNESS_VALUES];
+
+ s->base.privates[od->screenPrivateIndex].ptr = os;
+
+ WRAP (os, s, paintWindow, obsPaintWindow);
+ WRAP (os, s, drawWindow, obsDrawWindow);
+
+ return TRUE;
+}
+
+static void
+obsFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ OBS_SCREEN (s);
+
+ UNWRAP (os, s, paintWindow);
+ UNWRAP (os, s, drawWindow);
+
+ damageScreen (s);
+
+ compFiniScreenOptions (s, os->opt, OBS_SCREEN_OPTION_NUM);
+
+ free (os);
+}
+
+static CompBool
+obsInitWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ ObsWindow *ow;
+ int i;
+
+ OBS_SCREEN (w->screen);
+
+ ow = malloc (sizeof (ObsWindow));
+ if (!ow)
+ return FALSE;
+
+ for (i = 0; i < MODIFIER_COUNT; i++)
+ {
+ ow->customFactor[i] = 100;
+ ow->matchFactor[i] = 100;
+ }
+
+ w->base.privates[os->windowPrivateIndex].ptr = ow;
+
+ for (i = 0; i < MODIFIER_COUNT; i++)
+ updatePaintModifier (w, i);
+
+ return TRUE;
+}
+
+static void
+obsFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ OBS_WINDOW (w);
+
+ free (ow);
+}
+
+static CompBool
+obsInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) obsInitDisplay,
+ (InitPluginObjectProc) obsInitScreen,
+ (InitPluginObjectProc) obsInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+
+}
+
+static void
+obsFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) obsFiniDisplay,
+ (FiniPluginObjectProc) obsFiniScreen,
+ (FiniPluginObjectProc) obsFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompBool
+obsInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&obsMetadata,
+ p->vTable->name,
+ obsDisplayOptionInfo,
+ OBS_DISPLAY_OPTION_NUM,
+ obsScreenOptionInfo,
+ OBS_SCREEN_OPTION_NUM))
+ {
+ return FALSE;
+ }
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&obsMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&obsMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+obsFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+
+ compFiniMetadata (&obsMetadata);
+}
+
+static CompMetadata *
+obsGetMetadata (CompPlugin *plugin)
+{
+ return &obsMetadata;
+}
+
+CompPluginVTable obsVTable = {
+ "obs",
+ obsGetMetadata,
+ obsInit,
+ obsFini,
+ obsInitObject,
+ obsFiniObject,
+ obsGetObjectOptions,
+ obsSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &obsVTable;
+}
diff --git a/plugins/place.c b/plugins/place.c
new file mode 100644
index 0000000..74ac388
--- /dev/null
+++ b/plugins/place.c
@@ -0,0 +1,1762 @@
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Rob Adams
+ * Copyright (C) 2005 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <compiz-core.h>
+
+static CompMetadata placeMetadata;
+
+static int displayPrivateIndex;
+
+typedef struct _PlaceDisplay {
+ int screenPrivateIndex;
+
+ HandleEventProc handleEvent;
+} PlaceDisplay;
+
+#define PLACE_MODE_CASCADE 0
+#define PLACE_MODE_CENTERED 1
+#define PLACE_MODE_SMART 2
+#define PLACE_MODE_MAXIMIZE 3
+#define PLACE_MODE_RANDOM 4
+#define PLACE_MODE_LAST PLACE_MODE_RANDOM
+
+#define PLACE_MOMODE_CURRENT 0
+#define PLACE_MOMODE_POINTER 1
+#define PLACE_MOMODE_ACTIVEWIN 2
+#define PLACE_MOMODE_FULLSCREEN 3
+#define PLACE_MOMODE_LAST PLACE_MOMODE_FULLSCREEN
+
+#define PLACE_SCREEN_OPTION_WORKAROUND 0
+#define PLACE_SCREEN_OPTION_MODE 1
+#define PLACE_SCREEN_OPTION_MULTIOUTPUT_MODE 2
+#define PLACE_SCREEN_OPTION_FORCE_PLACEMENT 3
+#define PLACE_SCREEN_OPTION_POSITION_MATCHES 4
+#define PLACE_SCREEN_OPTION_POSITION_X_VALUES 5
+#define PLACE_SCREEN_OPTION_POSITION_Y_VALUES 6
+#define PLACE_SCREEN_OPTION_POSITION_CONSTRAIN 7
+#define PLACE_SCREEN_OPTION_VIEWPORT_MATCHES 8
+#define PLACE_SCREEN_OPTION_VIEWPORT_X_VALUES 9
+#define PLACE_SCREEN_OPTION_VIEWPORT_Y_VALUES 10
+#define PLACE_SCREEN_OPTION_NUM 11
+
+typedef struct _PlaceScreen {
+ CompOption opt[PLACE_SCREEN_OPTION_NUM];
+
+ PlaceWindowProc placeWindow;
+ ValidateWindowResizeRequestProc validateWindowResizeRequest;
+} PlaceScreen;
+
+#define GET_PLACE_DISPLAY(d) \
+ ((PlaceDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define PLACE_DISPLAY(d) \
+ PlaceDisplay *pd = GET_PLACE_DISPLAY (d)
+
+#define GET_PLACE_SCREEN(s, pd) \
+ ((PlaceScreen *) (s)->base.privates[(pd)->screenPrivateIndex].ptr)
+
+#define PLACE_SCREEN(s) \
+ PlaceScreen *ps = GET_PLACE_SCREEN (s, GET_PLACE_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+typedef enum {
+ NoPlacement = 0,
+ PlaceOnly,
+ ConstrainOnly,
+ PlaceAndConstrain,
+ PlaceOverParent,
+ PlaceCenteredOnScreen
+} PlacementStrategy;
+
+/* helper macro that filters out windows irrelevant for placement */
+#define IS_PLACE_RELEVANT(wi, w) \
+ ((w != wi) && \
+ (wi->attrib.map_state == IsViewable || wi->shaded) && \
+ (!wi->attrib.override_redirect) && \
+ (!(wi->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))))
+
+/* helper macros to get the full dimensions of a window,
+ including decorations */
+#define WIN_FULL_X(w) ((w)->serverX - (w)->input.left)
+#define WIN_FULL_Y(w) ((w)->serverY - (w)->input.top)
+#define WIN_FULL_W(w) ((w)->serverWidth + 2 * (w)->serverBorderWidth + \
+ (w)->input.left + (w)->input.right)
+#define WIN_FULL_H(w) ((w)->serverHeight + 2 * (w)->serverBorderWidth + \
+ (w)->input.top + (w)->input.bottom)
+
+static Bool
+placeMatchXYValue (CompWindow *w,
+ CompOption *matches,
+ CompOption *xValues,
+ CompOption *yValues,
+ CompOption *constrain,
+ int *x,
+ int *y,
+ Bool *keepInWorkarea)
+{
+ int i, min;
+
+ if (w->type & CompWindowTypeDesktopMask)
+ return FALSE;
+
+ min = MIN (matches->value.list.nValue, xValues->value.list.nValue);
+ min = MIN (min, yValues->value.list.nValue);
+
+ for (i = 0; i < min; i++)
+ {
+ if (matchEval (&matches->value.list.value[i].match, w))
+ {
+ *x = xValues->value.list.value[i].i;
+ *y = yValues->value.list.value[i].i;
+
+ if (keepInWorkarea)
+ {
+ if (constrain && constrain->value.list.nValue > i)
+ *keepInWorkarea = constrain->value.list.value[i].b;
+ else
+ *keepInWorkarea = TRUE;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+placeMatchPosition (CompWindow *w,
+ int *x,
+ int *y,
+ Bool *keepInWorkarea)
+{
+ PLACE_SCREEN (w->screen);
+
+ return placeMatchXYValue (w,
+ &ps->opt[PLACE_SCREEN_OPTION_POSITION_MATCHES],
+ &ps->opt[PLACE_SCREEN_OPTION_POSITION_X_VALUES],
+ &ps->opt[PLACE_SCREEN_OPTION_POSITION_Y_VALUES],
+ &ps->opt[PLACE_SCREEN_OPTION_POSITION_CONSTRAIN],
+ x,
+ y,
+ keepInWorkarea);
+}
+
+static Bool
+placeMatchViewport (CompWindow *w,
+ int *x,
+ int *y)
+{
+ PLACE_SCREEN (w->screen);
+
+ if (placeMatchXYValue (w,
+ &ps->opt[PLACE_SCREEN_OPTION_VIEWPORT_MATCHES],
+ &ps->opt[PLACE_SCREEN_OPTION_VIEWPORT_X_VALUES],
+ &ps->opt[PLACE_SCREEN_OPTION_VIEWPORT_Y_VALUES],
+ NULL,
+ x,
+ y,
+ NULL))
+ {
+ /* Viewport matches are given 1-based, so we need to adjust that */
+ *x -= 1;
+ *y -= 1;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static CompOption *
+placeGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+ int *count)
+{
+ PLACE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ps);
+ return ps->opt;
+}
+
+static Bool
+placeSetScreenOption (CompPlugin *plugin,
+ CompScreen *screen,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ PLACE_SCREEN (screen);
+
+ o = compFindOption (ps->opt, NUM_OPTIONS (ps), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case PLACE_SCREEN_OPTION_POSITION_MATCHES:
+ case PLACE_SCREEN_OPTION_VIEWPORT_MATCHES:
+ if (compSetOptionList (o, value))
+ {
+ int i;
+
+ for (i = 0; i < o->value.list.nValue; i++)
+ matchUpdate (screen->display, &o->value.list.value[i].match);
+
+ return TRUE;
+ }
+ break;
+ default:
+ if (compSetOption (o, value))
+ return TRUE;
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+placeSendWindowMaximizationRequest (CompWindow *w)
+{
+ XEvent xev;
+ CompDisplay *d = w->screen->display;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.display = d->display;
+ xev.xclient.format = 32;
+
+ xev.xclient.message_type = d->winStateAtom;
+ xev.xclient.window = w->id;
+
+ xev.xclient.data.l[0] = 1;
+ xev.xclient.data.l[1] = d->winStateMaximizedHorzAtom;
+ xev.xclient.data.l[2] = d->winStateMaximizedVertAtom;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ XSendEvent (d->display, w->screen->root, FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+}
+
+static Bool
+rectangleIntersect (XRectangle *src1,
+ XRectangle *src2,
+ XRectangle *dest)
+{
+ int destX, destY;
+ int destW, destH;
+
+ destX = MAX (src1->x, src2->x);
+ destY = MAX (src1->y, src2->y);
+ destW = MIN (src1->x + src1->width, src2->x + src2->width) - destX;
+ destH = MIN (src1->y + src1->height, src2->y + src2->height) - destY;
+
+ if (destW <= 0 || destH <= 0)
+ {
+ dest->width = 0;
+ dest->height = 0;
+ return FALSE;
+ }
+
+ dest->x = destX;
+ dest->y = destY;
+ dest->width = destW;
+ dest->height = destH;
+
+ return TRUE;
+}
+
+static void
+getWindowExtentsRect (CompWindow *w,
+ XRectangle *rect)
+{
+ rect->x = WIN_FULL_X (w);
+ rect->y = WIN_FULL_Y (w);
+ rect->width = WIN_FULL_W (w);
+ rect->height = WIN_FULL_H (w);
+}
+
+static Bool
+rectOverlapsWindow (XRectangle *rect,
+ CompWindow **windows,
+ unsigned int winCount)
+{
+ unsigned int i;
+ XRectangle dest;
+
+ for (i = 0; i < winCount; i++)
+ {
+ CompWindow *other = windows[i];
+ XRectangle otherRect;
+
+ switch (other->type) {
+ case CompWindowTypeDockMask:
+ case CompWindowTypeSplashMask:
+ case CompWindowTypeDesktopMask:
+ case CompWindowTypeDialogMask:
+ case CompWindowTypeModalDialogMask:
+ case CompWindowTypeFullscreenMask:
+ case CompWindowTypeUnknownMask:
+ break;
+ case CompWindowTypeNormalMask:
+ case CompWindowTypeUtilMask:
+ case CompWindowTypeToolbarMask:
+ case CompWindowTypeMenuMask:
+ getWindowExtentsRect (other, &otherRect);
+
+ if (rectangleIntersect (rect, &otherRect, &dest))
+ return TRUE;
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static int
+compareLeftmost (const void *a,
+ const void *b)
+{
+ CompWindow *aw = *((CompWindow **) a);
+ CompWindow *bw = *((CompWindow **) b);
+ int ax, bx;
+
+ ax = WIN_FULL_X (aw);
+ bx = WIN_FULL_X (bw);
+
+ if (ax < bx)
+ return -1;
+ else if (ax > bx)
+ return 1;
+ else
+ return 0;
+}
+
+static int
+compareTopmost (const void *a,
+ const void *b)
+{
+ CompWindow *aw = *((CompWindow **) a);
+ CompWindow *bw = *((CompWindow **) b);
+ int ay, by;
+
+ ay = WIN_FULL_Y (aw);
+ by = WIN_FULL_Y (bw);
+
+ if (ay < by)
+ return -1;
+ else if (ay > by)
+ return 1;
+ else
+ return 0;
+}
+
+static int
+compareNorthWestCorner (const void *a,
+ const void *b)
+{
+ CompWindow *aw = *((CompWindow **) a);
+ CompWindow *bw = *((CompWindow **) b);
+ int fromOriginA;
+ int fromOriginB;
+ int ax, ay, bx, by;
+
+ ax = WIN_FULL_X (aw);
+ ay = WIN_FULL_Y (aw);
+
+ bx = WIN_FULL_X (bw);
+ by = WIN_FULL_Y (bw);
+
+ /* probably there's a fast good-enough-guess we could use here. */
+ fromOriginA = sqrt (ax * ax + ay * ay);
+ fromOriginB = sqrt (bx * bx + by * by);
+
+ if (fromOriginA < fromOriginB)
+ return -1;
+ else if (fromOriginA > fromOriginB)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+centerTileRectInArea (XRectangle *rect,
+ XRectangle *workArea)
+{
+ int fluff;
+
+ /* The point here is to tile a window such that "extra"
+ * space is equal on either side (i.e. so a full screen
+ * of windows tiled this way would center the windows
+ * as a group)
+ */
+
+ fluff = (workArea->width % (rect->width + 1)) / 2;
+ rect->x = workArea->x + fluff;
+
+ fluff = (workArea->height % (rect->height + 1)) / 3;
+ rect->y = workArea->y + fluff;
+}
+
+static Bool
+rectFitsInWorkarea (XRectangle *workArea,
+ XRectangle *rect)
+{
+ if (rect->x < workArea->x)
+ return FALSE;
+
+ if (rect->y < workArea->y)
+ return FALSE;
+
+ if (rect->x + rect->width > workArea->x + workArea->width)
+ return FALSE;
+
+ if (rect->y + rect->height > workArea->y + workArea->height)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Find the leftmost, then topmost, empty area on the workspace
+ * that can contain the new window.
+ *
+ * Cool feature to have: if we can't fit the current window size,
+ * try shrinking the window (within geometry constraints). But
+ * beware windows such as Emacs with no sane minimum size, we
+ * don't want to create a 1x1 Emacs.
+ */
+static Bool
+placeCascadeFindFirstFit (CompWindow *w,
+ CompWindow **windows,
+ unsigned int winCount,
+ XRectangle *workArea,
+ int x,
+ int y,
+ int *newX,
+ int *newY)
+{
+ /* This algorithm is limited - it just brute-force tries
+ * to fit the window in a small number of locations that are aligned
+ * with existing windows. It tries to place the window on
+ * the bottom of each existing window, and then to the right
+ * of each existing window, aligned with the left/top of the
+ * existing window in each of those cases.
+ */
+ Bool retval = FALSE;
+ unsigned int i, allocSize = winCount * sizeof (CompWindow *);
+ CompWindow **belowSorted, **rightSorted;
+ XRectangle rect;
+
+ belowSorted = malloc (allocSize);
+ if (!belowSorted)
+ return FALSE;
+
+ rightSorted = malloc (allocSize);
+ if (!rightSorted)
+ {
+ free (belowSorted);
+ return FALSE;
+ }
+
+ /* Below each window */
+ memcpy (belowSorted, windows, allocSize);
+ qsort (belowSorted, winCount, sizeof (CompWindow *), compareLeftmost);
+ qsort (belowSorted, winCount, sizeof (CompWindow *), compareTopmost);
+
+ /* To the right of each window */
+ memcpy (rightSorted, windows, allocSize);
+ qsort (rightSorted, winCount, sizeof (CompWindow *), compareTopmost);
+ qsort (rightSorted, winCount, sizeof (CompWindow *), compareLeftmost);
+
+ getWindowExtentsRect (w, &rect);
+
+ centerTileRectInArea (&rect, workArea);
+
+ if (rectFitsInWorkarea (workArea, &rect) &&
+ !rectOverlapsWindow (&rect, windows, winCount))
+ {
+ *newX = rect.x + w->input.left;
+ *newY = rect.y + w->input.top;
+
+ retval = TRUE;
+ }
+
+ if (!retval)
+ {
+ /* try below each window */
+ for (i = 0; i < winCount && !retval; i++)
+ {
+ XRectangle outerRect;
+
+ getWindowExtentsRect (belowSorted[i], &outerRect);
+
+ rect.x = outerRect.x;
+ rect.y = outerRect.y + outerRect.height;
+
+ if (rectFitsInWorkarea (workArea, &rect) &&
+ !rectOverlapsWindow (&rect, belowSorted, winCount))
+ {
+ *newX = rect.x + w->input.left;
+ *newY = rect.y + w->input.top;
+ retval = TRUE;
+ }
+ }
+ }
+
+ if (!retval)
+ {
+ /* try to the right of each window */
+ for (i = 0; i < winCount && !retval; i++)
+ {
+ XRectangle outerRect;
+
+ getWindowExtentsRect (rightSorted[i], &outerRect);
+
+ rect.x = outerRect.x + outerRect.width;
+ rect.y = outerRect.y;
+
+ if (rectFitsInWorkarea (workArea, &rect) &&
+ !rectOverlapsWindow (&rect, rightSorted, winCount))
+ {
+ *newX = rect.x + w->input.left;
+ *newY = rect.y + w->input.top;
+ retval = TRUE;
+ }
+ }
+ }
+
+ free (belowSorted);
+ free (rightSorted);
+
+ return retval;
+}
+
+static void
+placeCascadeFindNext (CompWindow *w,
+ CompWindow **windows,
+ unsigned int winCount,
+ XRectangle *workArea,
+ int x,
+ int y,
+ int *newX,
+ int *newY)
+{
+ CompWindow **sorted;
+ unsigned int allocSize = winCount * sizeof (CompWindow *);
+ int cascadeX, cascadeY;
+ int xThreshold, yThreshold;
+ int winWidth, winHeight;
+ int i, cascadeStage;
+
+ sorted = malloc (allocSize);
+ if (!sorted)
+ return;
+
+ memcpy (sorted, windows, allocSize);
+ qsort (sorted, winCount, sizeof (CompWindow *), compareNorthWestCorner);
+
+ /* This is a "fuzzy" cascade algorithm.
+ * For each window in the list, we find where we'd cascade a
+ * new window after it. If a window is already nearly at that
+ * position, we move on.
+ */
+
+ /* arbitrary-ish threshold, honors user attempts to
+ * manually cascade.
+ */
+#define CASCADE_FUZZ 15
+
+ xThreshold = MAX (w->input.left, CASCADE_FUZZ);
+ yThreshold = MAX (w->input.top, CASCADE_FUZZ);
+
+ /* Find furthest-SE origin of all workspaces.
+ * cascade_x, cascade_y are the target position
+ * of NW corner of window frame.
+ */
+
+ cascadeX = MAX (0, workArea->x);
+ cascadeY = MAX (0, workArea->y);
+
+ /* Find first cascade position that's not used. */
+
+ winWidth = WIN_FULL_W (w);
+ winHeight = WIN_FULL_H (w);
+
+ cascadeStage = 0;
+ for (i = 0; i < winCount; i++)
+ {
+ CompWindow *wi = sorted[i];
+ int wx, wy;
+
+ /* we want frame position, not window position */
+ wx = WIN_FULL_X (wi);
+ wy = WIN_FULL_Y (wi);
+
+ if (abs (wx - cascadeX) < xThreshold &&
+ abs (wy - cascadeY) < yThreshold)
+ {
+ /* This window is "in the way", move to next cascade
+ * point. The new window frame should go at the origin
+ * of the client window we're stacking above.
+ */
+ wx = cascadeX = wi->serverX;
+ wy = cascadeY = wi->serverY;
+
+ /* If we go off the screen, start over with a new cascade */
+ if ((cascadeX + winWidth > workArea->x + workArea->width) ||
+ (cascadeY + winHeight > workArea->y + workArea->height))
+ {
+ cascadeX = MAX (0, workArea->x);
+ cascadeY = MAX (0, workArea->y);
+
+#define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */
+
+ cascadeStage += 1;
+ cascadeX += CASCADE_INTERVAL * cascadeStage;
+
+ /* start over with a new cascade translated to the right,
+ * unless we are out of space
+ */
+ if (cascadeX + winWidth < workArea->x + workArea->width)
+ {
+ i = 0;
+ continue;
+ }
+ else
+ {
+ /* All out of space, this cascade_x won't work */
+ cascadeX = MAX (0, workArea->x);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* Keep searching for a further-down-the-diagonal window. */
+ }
+ }
+
+ /* cascade_x and cascade_y will match the last window in the list
+ * that was "in the way" (in the approximate cascade diagonal)
+ */
+
+ free (sorted);
+
+ /* Convert coords to position of window, not position of frame. */
+ *newX = cascadeX + w->input.left;
+ *newY = cascadeY + w->input.top;
+}
+
+static void
+placeCascade (CompWindow *w,
+ XRectangle *workArea,
+ int *x,
+ int *y)
+{
+ CompWindow **windows;
+ CompWindow *wi;
+ unsigned int count = 0;
+
+ /* get the total window count */
+ for (wi = w->screen->windows; wi; wi = wi->next)
+ count++;
+
+ windows = malloc (sizeof (CompWindow *) * count);
+ if (!windows)
+ return;
+
+ /* Find windows that matter (not minimized, on same workspace
+ * as placed window, may be shaded - if shaded we pretend it isn't
+ * for placement purposes)
+ */
+ for (wi = w->screen->windows, count = 0; wi; wi = wi->next)
+ {
+ if (!IS_PLACE_RELEVANT (wi, w))
+ continue;
+
+ if (wi->type & (CompWindowTypeFullscreenMask |
+ CompWindowTypeUnknownMask))
+ continue;
+
+ if (wi->serverX >= workArea->x + workArea->width ||
+ wi->serverX + wi->serverWidth <= workArea->x ||
+ wi->serverY >= workArea->y + workArea->height ||
+ wi->serverY + wi->serverHeight <= workArea->y)
+ continue;
+
+ windows[count++] = wi;
+ }
+
+ if (!placeCascadeFindFirstFit (w, windows, count, workArea, *x, *y, x, y))
+ {
+ /* if the window wasn't placed at the origin of screen,
+ * cascade it onto the current screen
+ */
+ placeCascadeFindNext (w, windows, count, workArea, *x, *y, x, y);
+ }
+
+ free (windows);
+}
+
+static void
+placeCentered (CompWindow *w,
+ XRectangle *workArea,
+ int *x,
+ int *y)
+{
+ *x = workArea->x + (workArea->width - w->serverWidth) / 2;
+ *y = workArea->y + (workArea->height - w->serverHeight) / 2;
+}
+
+static void
+placeRandom (CompWindow *w,
+ XRectangle *workArea,
+ int *x,
+ int *y)
+{
+ int remainX, remainY;
+
+ *x = workArea->x;
+ *y = workArea->y;
+
+ remainX = workArea->width - w->serverWidth;
+ if (remainX > 0)
+ *x += rand () % remainX;
+
+ remainY = workArea->height - w->serverHeight;
+ if (remainY > 0)
+ *y += rand () % remainY;
+}
+
+/* overlap types */
+#define NONE 0
+#define H_WRONG -1
+#define W_WRONG -2
+
+static void
+placeSmart (CompWindow *w,
+ XRectangle *workArea,
+ int *x,
+ int *y)
+{
+ /*
+ * SmartPlacement by Cristian Tibirna (tibirna@kde.org)
+ * adapted for kwm (16-19jan98) and for kwin (16Nov1999) using (with
+ * permission) ideas from fvwm, authored by
+ * Anthony Martin (amartin@engr.csulb.edu).
+ * Xinerama supported added by Balaji Ramani (balaji@yablibli.com)
+ * with ideas from xfce.
+ * adapted for Compiz by Bellegarde Cedric (gnumdk(at)gmail.com)
+ */
+ CompWindow *wi;
+ int overlap, minOverlap = 0;
+ int xOptimal, yOptimal;
+ int possible;
+
+ /* temp coords */
+ int cxl, cxr, cyt, cyb;
+ /* temp coords */
+ int xl, xr, yt, yb;
+ /* temp holder */
+ int basket;
+ /* CT lame flag. Don't like it. What else would do? */
+ Bool firstPass = TRUE;
+
+ /* get the maximum allowed windows space */
+ int xTmp = workArea->x;
+ int yTmp = workArea->y;
+
+ /* client gabarit */
+ int cw = WIN_FULL_W (w) - 1;
+ int ch = WIN_FULL_H (w) - 1;
+
+ xOptimal = xTmp;
+ yOptimal = yTmp;
+
+ /* loop over possible positions */
+ do
+ {
+ /* test if enough room in x and y directions */
+ if (yTmp + ch > workArea->y + workArea->height && ch < workArea->height)
+ overlap = H_WRONG; /* this throws the algorithm to an exit */
+ else if (xTmp + cw > workArea->x + workArea->width)
+ overlap = W_WRONG;
+ else
+ {
+ overlap = NONE; /* initialize */
+
+ cxl = xTmp;
+ cxr = xTmp + cw;
+ cyt = yTmp;
+ cyb = yTmp + ch;
+
+ for (wi = w->screen->windows; wi; wi = wi->next)
+ {
+ if (!IS_PLACE_RELEVANT (wi, w))
+ continue;
+
+ xl = WIN_FULL_X (wi);
+ yt = WIN_FULL_Y (wi);
+ xr = WIN_FULL_X (wi) + WIN_FULL_W (wi);
+ yb = WIN_FULL_Y (wi) + WIN_FULL_H (wi);
+
+ /* if windows overlap, calc the overall overlapping */
+ if (cxl < xr && cxr > xl && cyt < yb && cyb > yt)
+ {
+ xl = MAX (cxl, xl);
+ xr = MIN (cxr, xr);
+ yt = MAX (cyt, yt);
+ yb = MIN (cyb, yb);
+
+ if (wi->state & CompWindowStateAboveMask)
+ overlap += 16 * (xr - xl) * (yb - yt);
+ else if (wi->state & CompWindowStateBelowMask)
+ overlap += 0;
+ else
+ overlap += (xr - xl) * (yb - yt);
+ }
+ }
+ }
+
+ /* CT first time we get no overlap we stop */
+ if (overlap == NONE)
+ {
+ xOptimal = xTmp;
+ yOptimal = yTmp;
+ break;
+ }
+
+ if (firstPass)
+ {
+ firstPass = FALSE;
+ minOverlap = overlap;
+ }
+ /* CT save the best position and the minimum overlap up to now */
+ else if (overlap >= NONE && overlap < minOverlap)
+ {
+ minOverlap = overlap;
+ xOptimal = xTmp;
+ yOptimal = yTmp;
+ }
+
+ /* really need to loop? test if there's any overlap */
+ if (overlap > NONE)
+ {
+ possible = workArea->x + workArea->width;
+
+ if (possible - cw > xTmp)
+ possible -= cw;
+
+ /* compare to the position of each client on the same desk */
+ for (wi = w->screen->windows; wi; wi = wi->next)
+ {
+ if (!IS_PLACE_RELEVANT (wi, w))
+ continue;
+
+ xl = WIN_FULL_X (wi);
+ yt = WIN_FULL_Y (wi);
+ xr = WIN_FULL_X (wi) + WIN_FULL_W (wi);
+ yb = WIN_FULL_X (wi) + WIN_FULL_H (wi);
+
+ /* if not enough room above or under the current
+ * client determine the first non-overlapped x position
+ */
+ if (yTmp < yb && yt < ch + yTmp)
+ {
+ if (xr > xTmp && possible > xr)
+ possible = xr;
+
+ basket = xl - cw;
+ if (basket > xTmp && possible > basket)
+ possible = basket;
+ }
+ }
+ xTmp = possible;
+ }
+ /* else ==> not enough x dimension (overlap was wrong on horizontal) */
+ else if (overlap == W_WRONG)
+ {
+ xTmp = workArea->x;
+ possible = workArea->y + workArea->height;
+
+ if (possible - ch > yTmp)
+ possible -= ch;
+
+ /* test the position of each window on the desk */
+ for (wi = w->screen->windows; wi; wi = wi->next)
+ {
+ if (!IS_PLACE_RELEVANT (wi, w))
+ continue;
+
+ xl = WIN_FULL_X (wi);
+ yt = WIN_FULL_Y (wi);
+ xr = WIN_FULL_X (wi) + WIN_FULL_W (wi);
+ yb = WIN_FULL_X (wi) + WIN_FULL_H (wi);
+
+ /* if not enough room to the left or right of the current
+ * client determine the first non-overlapped y position
+ */
+ if (yb > yTmp && possible > yb)
+ possible = yb;
+
+ basket = yt - ch;
+ if (basket > yTmp && possible > basket)
+ possible = basket;
+ }
+ yTmp = possible;
+ }
+ }
+ while (overlap != NONE && overlap != H_WRONG &&
+ yTmp < workArea->y + workArea->height);
+
+ if (ch >= workArea->height)
+ yOptimal = workArea->y;
+
+ *x = xOptimal + w->input.left;
+ *y = yOptimal + w->input.top;
+}
+
+static PlacementStrategy
+placeGetStrategyForWindow (CompWindow *w)
+{
+ CompMatch *match;
+
+ PLACE_SCREEN (w->screen);
+
+ if (w->type & (CompWindowTypeDockMask | CompWindowTypeDesktopMask |
+ CompWindowTypeUtilMask | CompWindowTypeToolbarMask |
+ CompWindowTypeMenuMask | CompWindowTypeFullscreenMask |
+ CompWindowTypeUnknownMask))
+ {
+ /* assume the app knows best how to place these */
+ return NoPlacement;
+ }
+
+ if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
+ {
+ /* see above */
+ return NoPlacement;
+ }
+
+ /* no placement for unmovable windows */
+ if (!(w->actions & CompWindowActionMoveMask))
+ return NoPlacement;
+
+ match = &ps->opt[PLACE_SCREEN_OPTION_FORCE_PLACEMENT].value.match;
+ if (!matchEval (match, w))
+ {
+ if ((w->type & CompWindowTypeNormalMask) ||
+ ps->opt[PLACE_SCREEN_OPTION_WORKAROUND].value.b)
+ {
+ /* Only accept USPosition on non-normal windows if workarounds are
+ * enabled because apps claiming the user set -geometry for a
+ * dialog or dock are most likely wrong
+ */
+ if (w->sizeHints.flags & USPosition)
+ return ConstrainOnly;
+ }
+
+ if (w->sizeHints.flags & PPosition)
+ return ConstrainOnly;
+ }
+
+ if (w->transientFor &&
+ (w->type & (CompWindowTypeDialogMask |
+ CompWindowTypeModalDialogMask)))
+ {
+ CompWindow *parent = findWindowAtScreen (w->screen, w->transientFor);
+ if (parent && parent->managed)
+ return PlaceOverParent;
+ }
+
+ if (w->type & (CompWindowTypeDialogMask |
+ CompWindowTypeModalDialogMask |
+ CompWindowTypeSplashMask))
+ {
+ return PlaceCenteredOnScreen;
+ }
+
+ return PlaceAndConstrain;
+}
+
+static CompOutput *
+placeGetPlacementOutput (CompWindow *w,
+ PlacementStrategy strategy,
+ int x,
+ int y)
+{
+ CompScreen *s = w->screen;
+ int output = -1;
+
+ PLACE_SCREEN (s);
+
+ switch (strategy) {
+ case PlaceOverParent:
+ {
+ CompWindow *parent;
+
+ parent = findWindowAtScreen (s, w->transientFor);
+ if (parent)
+ output = outputDeviceForWindow (parent);
+ }
+ break;
+ case ConstrainOnly:
+ output = outputDeviceForGeometry (s, x, y,
+ w->serverWidth,
+ w->serverHeight,
+ w->serverBorderWidth);
+ break;
+ default:
+ break;
+ }
+
+ if (output >= 0)
+ return &s->outputDev[output];
+
+ switch (ps->opt[PLACE_SCREEN_OPTION_MULTIOUTPUT_MODE].value.i) {
+ case PLACE_MOMODE_CURRENT:
+ output = s->currentOutputDev;
+ break;
+ case PLACE_MOMODE_POINTER:
+ {
+ Window wDummy;
+ int iDummy, xPointer, yPointer;
+ unsigned int uiDummy;
+
+ /* this means a server roundtrip, which kind of sucks; thus
+ this code should be replaced as soon as we have software
+ cursor rendering and thus have a cached pointer coordinate */
+ if (XQueryPointer (s->display->display, s->root,
+ &wDummy, &wDummy, &xPointer, &yPointer,
+ &iDummy, &iDummy, &uiDummy))
+ {
+ output = outputDeviceForPoint (s, xPointer, yPointer);
+ }
+ }
+ break;
+ case PLACE_MOMODE_ACTIVEWIN:
+ {
+ CompWindow *active;
+
+ active = findWindowAtScreen (s, s->display->activeWindow);
+ if (active)
+ output = outputDeviceForWindow (active);
+ }
+ break;
+ case PLACE_MOMODE_FULLSCREEN:
+ /* only place on fullscreen output if not placing centered, as the
+ constraining will move the window away from the center otherwise */
+ if (strategy != PlaceCenteredOnScreen)
+ return &s->fullscreenOutput;
+ break;
+ }
+
+ if (output < 0)
+ output = s->currentOutputDev;
+
+ return &s->outputDev[output];
+}
+
+static void
+placeConstrainToWorkarea (CompWindow *w,
+ XRectangle *workArea,
+ int *x,
+ int *y)
+{
+ CompWindowExtents extents;
+ int delta;
+
+ extents.left = *x - w->input.left;
+ extents.top = *y - w->input.top;
+ extents.right = *x + w->serverWidth + w->input.right;
+ extents.bottom = *y + w->serverHeight + w->input.bottom;
+
+ delta = workArea->x + workArea->width - extents.right;
+ if (delta < 0)
+ extents.left += delta;
+
+ delta = workArea->x - extents.left;
+ if (delta > 0)
+ extents.left += delta;
+
+ delta = workArea->y + workArea->height - extents.bottom;
+ if (delta < 0)
+ extents.top += delta;
+
+ delta = workArea->y - extents.top;
+ if (delta > 0)
+ extents.top += delta;
+
+ *x = extents.left + w->input.left;
+ *y = extents.top + w->input.top;
+}
+
+static Bool
+placeDoWindowPlacement (CompWindow *w,
+ int x,
+ int y,
+ int *newX,
+ int *newY)
+{
+ CompScreen *s = w->screen;
+ XRectangle workArea;
+ int targetVpX, targetVpY;
+ CompOutput *output;
+ PlacementStrategy strategy;
+ Bool keepInWorkarea;
+
+ PLACE_SCREEN (s);
+
+ if (placeMatchPosition (w, &x, &y, &keepInWorkarea))
+ {
+ strategy = keepInWorkarea ? ConstrainOnly : NoPlacement;
+ }
+ else
+ {
+ strategy = placeGetStrategyForWindow (w);
+ if (strategy == NoPlacement)
+ return FALSE;
+ }
+
+ output = placeGetPlacementOutput (w, strategy, x, y);
+ workArea = output->workArea;
+
+ targetVpX = w->initialViewportX;
+ targetVpY = w->initialViewportY;
+
+ if (strategy == PlaceOverParent)
+ {
+ CompWindow *parent;
+
+ parent = findWindowAtScreen (s, w->transientFor);
+ if (parent)
+ {
+ /* center over parent horizontally */
+ x = parent->serverX + (parent->serverWidth / 2) -
+ (w->serverWidth / 2);
+
+ /* "visually" center vertically, leaving twice as much space below
+ as on top */
+ y = parent->serverY + (parent->serverHeight - w->serverHeight) / 3;
+
+ /* put top of child's frame, not top of child's client */
+ y += w->input.top;
+
+ /* if parent is visible on current viewport, clip to work area;
+ don't constrain further otherwise */
+ if (parent->serverX < parent->screen->width &&
+ parent->serverX + parent->serverWidth > 0 &&
+ parent->serverY < parent->screen->height &&
+ parent->serverY + parent->serverHeight > 0)
+ {
+ defaultViewportForWindow (parent, &targetVpX, &targetVpY);
+ strategy = ConstrainOnly;
+ }
+ else
+ {
+ strategy = NoPlacement;
+ }
+ }
+ }
+
+ if (strategy == PlaceCenteredOnScreen)
+ {
+ /* center window on current output device */
+
+ x = output->region.extents.x1;
+ y = output->region.extents.y1;
+
+ x += (output->width - w->serverWidth) / 2;
+ y += (output->height - w->serverHeight) / 2;
+
+ strategy = ConstrainOnly;
+ }
+
+ workArea.x += (targetVpX - s->x) * s->width;
+ workArea.y += (targetVpY - s->y) * s->height;
+
+ if (strategy == PlaceOnly || strategy == PlaceAndConstrain)
+ {
+ switch (ps->opt[PLACE_SCREEN_OPTION_MODE].value.i) {
+ case PLACE_MODE_CASCADE:
+ placeCascade (w, &workArea, &x, &y);
+ break;
+ case PLACE_MODE_CENTERED:
+ placeCentered (w, &workArea, &x, &y);
+ break;
+ case PLACE_MODE_RANDOM:
+ placeRandom (w, &workArea, &x, &y);
+ break;
+ case PLACE_MODE_MAXIMIZE:
+ placeSendWindowMaximizationRequest (w);
+ break;
+ case PLACE_MODE_SMART:
+ placeSmart (w, &workArea, &x, &y);
+ break;
+ }
+
+ /* When placing to the fullscreen output, constrain to one
+ output nevertheless */
+ if (output->id == ~0)
+ {
+ int id;
+
+ id = outputDeviceForGeometry (s, x, y,
+ w->serverWidth,
+ w->serverHeight,
+ w->serverBorderWidth);
+ getWorkareaForOutput (s, id, &workArea);
+
+ workArea.x += (targetVpX - s->x) * s->width;
+ workArea.y += (targetVpY - s->y) * s->height;
+ }
+
+ /* Maximize windows if they are too big for their work area (bit of
+ * a hack here). Assume undecorated windows probably don't intend to
+ * be maximized.
+ */
+ if ((w->actions & MAXIMIZE_STATE) == MAXIMIZE_STATE &&
+ (w->mwmDecor & (MwmDecorAll | MwmDecorTitle)) &&
+ !(w->state & CompWindowStateFullscreenMask))
+ {
+ if (WIN_FULL_W (w) >= workArea.width &&
+ WIN_FULL_H (w) >= workArea.height)
+ {
+ placeSendWindowMaximizationRequest (w);
+ }
+ }
+ }
+
+ if (strategy == ConstrainOnly || strategy == PlaceAndConstrain)
+ placeConstrainToWorkarea (w, &workArea, &x, &y);
+
+ *newX = x;
+ *newY = y;
+
+ return TRUE;
+}
+
+static void
+placeValidateWindowResizeRequest (CompWindow *w,
+ unsigned int *mask,
+ XWindowChanges *xwc,
+ unsigned int source)
+{
+ CompScreen *s = w->screen;
+ XRectangle workArea;
+ int x, y, left, right, top, bottom;
+ int output;
+ Bool sizeOnly = FALSE;
+
+ PLACE_SCREEN (s);
+
+ UNWRAP (ps, s, validateWindowResizeRequest);
+ (*s->validateWindowResizeRequest) (w, mask, xwc, source);
+ WRAP (ps, s, validateWindowResizeRequest,
+ placeValidateWindowResizeRequest);
+
+ if (*mask == 0)
+ return;
+
+ if (source == ClientTypePager)
+ return;
+
+ if (w->state & CompWindowStateFullscreenMask)
+ return;
+
+ if (w->wmType & (CompWindowTypeDockMask |
+ CompWindowTypeDesktopMask))
+ return;
+
+ if (w->sizeHints.flags & USPosition)
+ {
+ /* only respect USPosition on normal windows if
+ workarounds are disabled, reason see above */
+ if (ps->opt[PLACE_SCREEN_OPTION_WORKAROUND].value.b ||
+ (w->type & CompWindowTypeNormalMask))
+ {
+ /* try to keep the window position intact for USPosition -
+ obviously we can't do that if we need to change the size */
+ sizeOnly = TRUE;
+ }
+ }
+
+ /* left, right, top, bottom target coordinates, clamped to viewport
+ sizes as we don't need to validate movements to other viewports;
+ we are only interested in inner-viewport movements */
+ x = xwc->x % s->width;
+ if ((x + xwc->width) < 0)
+ x += s->width;
+
+ y = xwc->y % s->height;
+ if ((y + xwc->height) < 0)
+ y += s->height;
+
+ left = x - w->input.left;
+ right = x + xwc->width + w->input.right;
+ top = y - w->input.top;
+ bottom = y + xwc->height + w->input.bottom;
+
+ output = outputDeviceForGeometry (s,
+ xwc->x, xwc->y,
+ xwc->width, xwc->height,
+ w->serverBorderWidth);
+
+ getWorkareaForOutput (s, output, &workArea);
+
+ if (xwc->width >= workArea.width &&
+ xwc->height >= workArea.height)
+ {
+ if ((w->actions & MAXIMIZE_STATE) == MAXIMIZE_STATE &&
+ (w->mwmDecor & (MwmDecorAll | MwmDecorTitle)) &&
+ !(w->state & CompWindowStateFullscreenMask))
+ {
+ placeSendWindowMaximizationRequest (w);
+ }
+ }
+
+ if ((right - left) > workArea.width)
+ {
+ left = workArea.x;
+ right = left + workArea.width;
+ }
+ else
+ {
+ if (left < workArea.x)
+ {
+ right += workArea.x - left;
+ left = workArea.x;
+ }
+
+ if (right > (workArea.x + workArea.width))
+ {
+ left -= right - (workArea.x + workArea.width);
+ right = workArea.x + workArea.width;
+ }
+ }
+
+ if ((bottom - top) > workArea.height)
+ {
+ top = workArea.y;
+ bottom = top + workArea.height;
+ }
+ else
+ {
+ if (top < workArea.y)
+ {
+ bottom += workArea.y - top;
+ top = workArea.y;
+ }
+
+ if (bottom > (workArea.y + workArea.height))
+ {
+ top -= bottom - (workArea.y + workArea.height);
+ bottom = workArea.y + workArea.height;
+ }
+ }
+
+ /* bring left/right/top/bottom to actual window coordinates */
+ left += w->input.left;
+ right -= w->input.right;
+ top += w->input.top;
+ bottom -= w->input.bottom;
+
+ if ((right - left) != xwc->width)
+ {
+ xwc->width = right - left;
+ *mask |= CWWidth;
+ sizeOnly = FALSE;
+ }
+
+ if ((bottom - top) != xwc->height)
+ {
+ xwc->height = bottom - top;
+ *mask |= CWHeight;
+ sizeOnly = FALSE;
+ }
+
+ if (!sizeOnly)
+ {
+ if (left != x)
+ {
+ xwc->x += left - x;
+ *mask |= CWX;
+ }
+
+ if (top != y)
+ {
+ xwc->y += top - y;
+ *mask |= CWY;
+ }
+ }
+}
+
+static Bool
+placePlaceWindow (CompWindow *w,
+ int x,
+ int y,
+ int *newX,
+ int *newY)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ PLACE_SCREEN (s);
+
+ UNWRAP (ps, s, placeWindow);
+ status = (*s->placeWindow) (w, x, y, newX, newY);
+ WRAP (ps, s, placeWindow, placePlaceWindow);
+
+ if (!status)
+ {
+ int viewportX, viewportY;
+
+ if (!placeDoWindowPlacement (w, x, y, newX, newY))
+ {
+ *newX = x;
+ *newY = y;
+ }
+
+ if (placeMatchViewport (w, &viewportX, &viewportY))
+ {
+ viewportX = MAX (MIN (viewportX, s->hsize), 0);
+ viewportY = MAX (MIN (viewportY, s->vsize), 0);
+
+ x = *newX % s->width;
+ if (x < 0)
+ x += s->width;
+ y = *newY % s->height;
+ if (y < 0)
+ y += s->height;
+
+ *newX = x + (viewportX - s->x) * s->width;
+ *newY = y + (viewportY - s->y) * s->height;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+placeHandleScreenSizeChange (CompScreen *s,
+ int width,
+ int height)
+{
+ CompWindow *w;
+ int vpX, vpY, shiftX, shiftY;
+ XRectangle extents;
+ unsigned int mask;
+ XWindowChanges xwc;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (!w->managed)
+ continue;
+
+ if (w->wmType & (CompWindowTypeDockMask |
+ CompWindowTypeDesktopMask))
+ continue;
+
+ mask = 0;
+ getWindowExtentsRect (w, &extents);
+
+ vpX = extents.x / s->width;
+ if (extents.x < 0)
+ vpX -= 1;
+ vpY = extents.y / s->height;
+ if (extents.y < 0)
+ vpY -= 1;
+
+ shiftX = vpX * (width - s->width);
+ shiftY = vpY * (height - s->height);
+
+ extents.x = extents.x % s->width;
+ if (extents.x < 0)
+ extents.x += s->width;
+ extents.y = extents.y % s->height;
+ if (extents.y < 0)
+ extents.y += s->height;
+
+ if (extents.x + extents.width > width)
+ shiftX += width - extents.x - extents.width;
+ if (extents.y + extents.height > height)
+ shiftY += height - extents.y - extents.height;
+
+ if (shiftX)
+ {
+ mask |= CWX;
+ xwc.x = w->serverX + shiftX;
+ }
+
+ if (shiftY)
+ {
+ mask |= CWY;
+ xwc.y = w->serverY + shiftY;
+ }
+
+ if (mask)
+ configureXWindow (w, mask, &xwc);
+ }
+}
+
+static void
+placeHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ PLACE_DISPLAY (d);
+
+ switch (event->type) {
+ case ConfigureNotify:
+ {
+ CompScreen *s;
+
+ s = findScreenAtDisplay (d, event->xconfigure.window);
+ if (s)
+ placeHandleScreenSizeChange (s,
+ event->xconfigure.width,
+ event->xconfigure.height);
+ }
+ break;
+ default:
+ break;
+ }
+
+ UNWRAP (pd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (pd, d, handleEvent, placeHandleEvent);
+}
+
+static Bool
+placeInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ PlaceDisplay *pd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ pd = malloc (sizeof (PlaceDisplay));
+ if (!pd)
+ return FALSE;
+
+ pd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (pd->screenPrivateIndex < 0)
+ {
+ free (pd);
+ return FALSE;
+ }
+
+ d->base.privates[displayPrivateIndex].ptr = pd;
+
+ WRAP (pd, d, handleEvent, placeHandleEvent);
+
+ return TRUE;
+}
+
+static void
+placeFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ PLACE_DISPLAY (d);
+
+ UNWRAP (pd, d, handleEvent);
+
+ freeScreenPrivateIndex (d, pd->screenPrivateIndex);
+
+ free (pd);
+}
+
+static const CompMetadataOptionInfo placeScreenOptionInfo[] = {
+ { "workarounds", "bool", 0, 0, 0 },
+ { "mode", "int", RESTOSTRING (0, PLACE_MODE_LAST), 0, 0 },
+ { "multioutput_mode", "int", RESTOSTRING (0, PLACE_MOMODE_LAST), 0, 0 },
+ { "force_placement_match", "match", 0, 0, 0 },
+ { "position_matches", "list", "<type>match</type>", 0, 0 },
+ { "position_x_values", "list", "<type>int</type>", 0, 0 },
+ { "position_y_values", "list", "<type>int</type>", 0, 0 },
+ { "position_constrain_workarea", "list", "<type>bool</type>", 0, 0 },
+ { "viewport_matches", "list", "<type>match</type>", 0, 0 },
+ { "viewport_x_values", "list",
+ "<type>int</type><min>1</min><max>32</max>", 0, 0 },
+ { "viewport_y_values", "list",
+ "<type>int</type><min>1</min><max>32</max>", 0, 0 },
+};
+
+static Bool
+placeInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ PlaceScreen *ps;
+
+ PLACE_DISPLAY (s->display);
+
+ ps = malloc (sizeof (PlaceScreen));
+ if (!ps)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &placeMetadata,
+ placeScreenOptionInfo,
+ ps->opt,
+ PLACE_SCREEN_OPTION_NUM))
+ {
+ free (ps);
+ return FALSE;
+ }
+
+ WRAP (ps, s, placeWindow, placePlaceWindow);
+ WRAP (ps, s, validateWindowResizeRequest,
+ placeValidateWindowResizeRequest);
+
+ s->base.privates[pd->screenPrivateIndex].ptr = ps;
+
+ return TRUE;
+}
+
+static void
+placeFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ PLACE_SCREEN (s);
+
+ UNWRAP (ps, s, placeWindow);
+ UNWRAP (ps, s, validateWindowResizeRequest);
+
+ compFiniScreenOptions (s, ps->opt, PLACE_SCREEN_OPTION_NUM);
+
+ free (ps);
+}
+
+static CompBool
+placeInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) placeInitDisplay,
+ (InitPluginObjectProc) placeInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+placeFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) placeFiniDisplay,
+ (FiniPluginObjectProc) placeFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+placeGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) 0, /* GetDisplayOptions */
+ (GetPluginObjectOptionsProc) placeGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+placeSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) 0, /* SetDisplayOption */
+ (SetPluginObjectOptionProc) placeSetScreenOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+placeInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&placeMetadata,
+ p->vTable->name, 0, 0,
+ placeScreenOptionInfo,
+ PLACE_SCREEN_OPTION_NUM))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&placeMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&placeMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+placeFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&placeMetadata);
+}
+
+static CompMetadata *
+placeGetMetadata (CompPlugin *plugin)
+{
+ return &placeMetadata;
+}
+
+static CompPluginVTable placeVTable = {
+ "place",
+ placeGetMetadata,
+ placeInit,
+ placeFini,
+ placeInitObject,
+ placeFiniObject,
+ placeGetObjectOptions,
+ placeSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &placeVTable;
+}
diff --git a/plugins/png.c b/plugins/png.c
new file mode 100644
index 0000000..741cf01
--- /dev/null
+++ b/plugins/png.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <png.h>
+#include <setjmp.h>
+
+#include <compiz-core.h>
+
+static CompMetadata pngMetadata;
+
+#define PNG_SIG_SIZE 8
+
+static int displayPrivateIndex;
+
+typedef struct _PngDisplay {
+ FileToImageProc fileToImage;
+ ImageToFileProc imageToFile;
+} PngDisplay;
+
+#define GET_PNG_DISPLAY(d) \
+ ((PngDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define PNG_DISPLAY(d) \
+ PngDisplay *pd = GET_PNG_DISPLAY (d)
+
+
+static void
+premultiplyData (png_structp png,
+ png_row_infop row_info,
+ png_bytep data)
+{
+ unsigned int i;
+
+ for (i = 0; i < row_info->rowbytes; i += 4)
+ {
+ unsigned char *base = &data[i];
+ unsigned char blue = base[0];
+ unsigned char green = base[1];
+ unsigned char red = base[2];
+ unsigned char alpha = base[3];
+ int p;
+
+ red = (unsigned) red * (unsigned) alpha / 255;
+ green = (unsigned) green * (unsigned) alpha / 255;
+ blue = (unsigned) blue * (unsigned) alpha / 255;
+
+ p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
+ memcpy (base, &p, sizeof (int));
+ }
+}
+
+static Bool
+readPngData (png_struct *png,
+ png_info *info,
+ void **data,
+ int *width,
+ int *height)
+{
+ png_uint_32 png_width, png_height;
+ int depth, color_type, interlace, i;
+ unsigned int pixel_size;
+ png_byte **row_pointers;
+ char *d;
+
+ png_read_info (png, info);
+
+ png_get_IHDR (png, info,
+ &png_width, &png_height, &depth,
+ &color_type, &interlace, NULL, NULL);
+
+ *width = (int) png_width;
+ *height = (int) png_height;
+
+ /* convert palette/gray image to rgb */
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb (png);
+
+ /* expand gray bit depth if needed */
+ if (color_type == PNG_COLOR_TYPE_GRAY && depth < 8)
+ png_set_gray_1_2_4_to_8 (png);
+
+ /* transform transparency to alpha */
+ if (png_get_valid(png, info, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha (png);
+
+ if (depth == 16)
+ png_set_strip_16 (png);
+
+ if (depth < 8)
+ png_set_packing (png);
+
+ /* convert grayscale to RGB */
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb (png);
+
+ if (interlace != PNG_INTERLACE_NONE)
+ png_set_interlace_handling (png);
+
+ png_set_bgr (png);
+ png_set_filler (png, 0xff, PNG_FILLER_AFTER);
+
+ png_set_read_user_transform_fn (png, premultiplyData);
+
+ png_read_update_info (png, info);
+
+ pixel_size = 4;
+ d = (char *) malloc (png_width * png_height * pixel_size);
+ if (!d)
+ return FALSE;
+
+ *data = d;
+
+ row_pointers = (png_byte **) malloc (png_height * sizeof (char *));
+ if (!row_pointers)
+ {
+ free (d);
+ return FALSE;
+ }
+
+ for (i = 0; i < png_height; i++)
+ row_pointers[i] = (png_byte *) (d + i * png_width * pixel_size);
+
+ png_read_image (png, row_pointers);
+ png_read_end (png, info);
+
+ free (row_pointers);
+
+ return TRUE;
+}
+
+static Bool
+readPngFileToImage (FILE *file,
+ int *width,
+ int *height,
+ void **data)
+{
+ unsigned char png_sig[PNG_SIG_SIZE];
+ int sig_bytes;
+ png_struct *png;
+ png_info *info;
+ Bool status;
+
+ sig_bytes = fread (png_sig, 1, PNG_SIG_SIZE, file);
+ if (png_check_sig (png_sig, sig_bytes) == 0)
+ return FALSE;
+
+ png = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png)
+ return FALSE;
+
+ info = png_create_info_struct (png);
+ if (!info)
+ {
+ png_destroy_read_struct (&png, NULL, NULL);
+
+ return FALSE;
+ }
+
+ png_init_io (png, file);
+ png_set_sig_bytes (png, sig_bytes);
+
+ status = readPngData (png, info, data, width, height);
+
+ png_destroy_read_struct (&png, &info, NULL);
+
+ return status;
+}
+
+#if 0
+static void
+userReadData (png_structp png_ptr,
+ png_bytep data,
+ png_size_t length)
+{
+ const unsigned char **buffer = (const unsigned char **)
+ png_get_io_ptr (png_ptr);
+
+ memcpy (data, *buffer, length);
+ *buffer += length;
+}
+
+static Bool
+readPngBuffer (const unsigned char *buffer,
+ char **data,
+ unsigned int *width,
+ unsigned int *height)
+{
+ unsigned char png_sig[PNG_SIG_SIZE];
+ png_struct *png;
+ png_info *info;
+ const unsigned char *b = buffer + PNG_SIG_SIZE;
+ Bool status;
+
+ memcpy (png_sig, buffer, PNG_SIG_SIZE);
+ if (png_check_sig (png_sig, PNG_SIG_SIZE) == 0)
+ return FALSE;
+
+ png = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png)
+ return FALSE;
+
+ info = png_create_info_struct (png);
+ if (!info)
+ {
+ png_destroy_read_struct (&png, NULL, NULL);
+ return FALSE;
+ }
+
+ png_set_read_fn (png, (void *) &b, userReadData);
+ png_set_sig_bytes (png, PNG_SIG_SIZE);
+
+ status = readPngData (png, info, data, width, height);
+
+ png_destroy_read_struct (&png, &info, NULL);
+
+ return status;
+}
+#endif
+
+static Bool
+writePng (unsigned char *buffer,
+ png_rw_ptr writeFunc,
+ void *closure,
+ int width,
+ int height,
+ int stride)
+{
+ png_struct *png;
+ png_info *info;
+ png_byte **rows;
+ png_color_16 white;
+ int i;
+
+ rows = malloc (height * sizeof (png_byte *));
+ if (!rows)
+ return FALSE;
+
+ for (i = 0; i < height; i++)
+ rows[height - i - 1] = buffer + i * stride;
+
+ png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png)
+ {
+ free (rows);
+
+ return FALSE;
+ }
+
+ info = png_create_info_struct (png);
+ if (!info)
+ {
+ png_destroy_read_struct (&png, NULL, NULL);
+ free (rows);
+
+ return FALSE;
+ }
+
+ if (setjmp (png_jmpbuf (png)))
+ {
+ png_destroy_read_struct (&png, NULL, NULL);
+ free (rows);
+
+ return FALSE;
+ }
+
+ png_set_write_fn (png, closure, writeFunc, NULL);
+
+ png_set_IHDR (png, info,
+ width, height, 8,
+ PNG_COLOR_TYPE_RGB_ALPHA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ white.red = 0xff;
+ white.blue = 0xff;
+ white.green = 0xff;
+
+ png_set_bKGD (png, info, &white);
+
+ png_write_info (png, info);
+ png_write_image (png, rows);
+ png_write_end (png, info);
+
+ png_destroy_write_struct (&png, &info);
+ free (rows);
+
+ return TRUE;
+}
+
+static void
+stdioWriteFunc (png_structp png,
+ png_bytep data,
+ png_size_t size)
+{
+ FILE *fp;
+
+ fp = png_get_io_ptr (png);
+ if (fwrite (data, 1, size, fp) != size)
+ png_error (png, "Write Error");
+}
+
+static char *
+pngExtension (const char *name)
+{
+
+ if (strlen (name) > 4)
+ {
+ if (strcasecmp (name + (strlen (name) - 4), ".png") == 0)
+ return "";
+ }
+
+ return ".png";
+}
+
+static Bool
+pngImageToFile (CompDisplay *d,
+ const char *path,
+ const char *name,
+ const char *format,
+ int width,
+ int height,
+ int stride,
+ void *data)
+{
+ Bool status = FALSE;
+ char *extension = pngExtension (name);
+ char *file;
+ FILE *fp;
+ int len;
+
+ PNG_DISPLAY (d);
+
+ len = (path ? strlen (path) : 0) + strlen (name) + strlen (extension) + 2;
+
+ file = malloc (len);
+ if (file)
+ {
+ if (path)
+ sprintf (file, "%s/%s%s", path, name, extension);
+ else
+ sprintf (file, "%s%s", name, extension);
+ }
+
+ if (file && strcasecmp (format, "png") == 0)
+ {
+ fp = fopen (file, "wb");
+ if (fp)
+ {
+ status = writePng (data, stdioWriteFunc, fp, width, height, stride);
+ fclose (fp);
+ }
+
+ if (status)
+ {
+ free (file);
+ return TRUE;
+ }
+ }
+
+ UNWRAP (pd, d, imageToFile);
+ status = (*d->imageToFile) (d, path, name, format, width, height, stride,
+ data);
+ WRAP (pd, d, imageToFile, pngImageToFile);
+
+ if (!status && file)
+ {
+ fp = fopen (file, "wb");
+ if (fp)
+ {
+ status = writePng (data, stdioWriteFunc, fp, width, height, stride);
+ fclose (fp);
+ }
+ }
+
+ if (file)
+ free (file);
+
+ return status;
+}
+
+static Bool
+pngFileToImage (CompDisplay *d,
+ const char *path,
+ const char *name,
+ int *width,
+ int *height,
+ int *stride,
+ void **data)
+{
+ Bool status = FALSE;
+ char *extension = pngExtension (name);
+ char *file;
+ int len;
+
+ PNG_DISPLAY (d);
+
+ len = (path ? strlen (path) : 0) + strlen (name) + strlen (extension) + 2;
+
+ file = malloc (len);
+ if (file)
+ {
+ FILE *fp;
+
+ if (path)
+ sprintf (file, "%s/%s%s", path, name, extension);
+ else
+ sprintf (file, "%s%s", name, extension);
+
+ fp = fopen (file, "r");
+ if (fp)
+ {
+ status = readPngFileToImage (fp,
+ width,
+ height,
+ data);
+ fclose (fp);
+ }
+
+ free (file);
+
+ if (status)
+ {
+ *stride = *width * 4;
+ return TRUE;
+ }
+ }
+
+ UNWRAP (pd, d, fileToImage);
+ status = (*d->fileToImage) (d, path, name, width, height, stride, data);
+ WRAP (pd, d, fileToImage, pngFileToImage);
+
+ return status;
+}
+
+static Bool
+pngInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ PngDisplay *pd;
+ CompScreen *s;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ pd = malloc (sizeof (PngDisplay));
+ if (!pd)
+ return FALSE;
+
+ WRAP (pd, d, fileToImage, pngFileToImage);
+ WRAP (pd, d, imageToFile, pngImageToFile);
+
+ d->base.privates[displayPrivateIndex].ptr = pd;
+
+ for (s = d->screens; s; s = s->next)
+ updateDefaultIcon (s);
+
+ return TRUE;
+}
+
+static void
+pngFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ CompScreen *s;
+
+ PNG_DISPLAY (d);
+
+ UNWRAP (pd, d, fileToImage);
+ UNWRAP (pd, d, imageToFile);
+
+ for (s = d->screens; s; s = s->next)
+ updateDefaultIcon (s);
+
+ free (pd);
+}
+
+static CompBool
+pngInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) pngInitDisplay
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+pngFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) pngFiniDisplay
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static Bool
+pngInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&pngMetadata, p->vTable->name,
+ 0, 0, 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&pngMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&pngMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+pngFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&pngMetadata);
+}
+
+static CompMetadata *
+pngGetMetadata (CompPlugin *plugin)
+{
+ return &pngMetadata;
+}
+
+CompPluginVTable pngVTable = {
+ "png",
+ pngGetMetadata,
+ pngInit,
+ pngFini,
+ pngInitObject,
+ pngFiniObject,
+ 0, /* GetObjectOptions */
+ 0 /* SetObjectOption */
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &pngVTable;
+}
diff --git a/plugins/regex.c b/plugins/regex.c
new file mode 100644
index 0000000..2d32654
--- /dev/null
+++ b/plugins/regex.c
@@ -0,0 +1,558 @@
+/*
+ * Copyright © 2007 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include <regex.h>
+
+#include <X11/Xatom.h>
+
+#include <compiz-core.h>
+
+static CompMetadata regexMetadata;
+
+static int displayPrivateIndex;
+
+typedef struct _RegexDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+ MatchInitExpProc matchInitExp;
+ Atom roleAtom;
+ Atom visibleNameAtom;
+} RegexDisplay;
+
+typedef struct _RegexScreen {
+ int windowPrivateIndex;
+} RegexScreen;
+
+typedef struct _RegexWindow {
+ char *title;
+ char *role;
+} RegexWindow;
+
+#define GET_REGEX_DISPLAY(d) \
+ ((RegexDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define REGEX_DISPLAY(d) \
+ RegexDisplay *rd = GET_REGEX_DISPLAY (d)
+
+#define GET_REGEX_SCREEN(s, rd) \
+ ((RegexScreen *) (s)->base.privates[(rd)->screenPrivateIndex].ptr)
+
+#define REGEX_SCREEN(s) \
+ RegexScreen *rs = GET_REGEX_SCREEN (s, GET_REGEX_DISPLAY (s->display))
+
+#define GET_REGEX_WINDOW(w, rs) \
+ ((RegexWindow *) (w)->base.privates[(rs)->windowPrivateIndex].ptr)
+
+#define REGEX_WINDOW(w) \
+ RegexWindow *rw = GET_REGEX_WINDOW (w, \
+ GET_REGEX_SCREEN (w->screen, \
+ GET_REGEX_DISPLAY (w->screen->display)))
+
+static void
+regexMatchExpFini (CompDisplay *d,
+ CompPrivate private)
+{
+ regex_t *preg = (regex_t *) private.ptr;
+
+ if (preg)
+ {
+ regfree (preg);
+ free (preg);
+ }
+}
+
+static Bool
+regexMatchExpEvalTitle (CompDisplay *d,
+ CompWindow *w,
+ CompPrivate private)
+{
+ regex_t *preg = (regex_t *) private.ptr;
+ int status;
+
+ REGEX_WINDOW (w);
+
+ if (!preg)
+ return FALSE;
+
+ if (!rw->title)
+ return FALSE;
+
+ status = regexec (preg, rw->title, 0, NULL, 0);
+ if (status)
+ return FALSE;
+
+ return TRUE;
+}
+
+static Bool
+regexMatchExpEvalRole (CompDisplay *d,
+ CompWindow *w,
+ CompPrivate private)
+{
+ regex_t *preg = (regex_t *) private.ptr;
+ int status;
+
+ REGEX_WINDOW (w);
+
+ if (!preg)
+ return FALSE;
+
+ if (!rw->role)
+ return FALSE;
+
+ status = regexec (preg, rw->role, 0, NULL, 0);
+ if (status)
+ return FALSE;
+
+ return TRUE;
+}
+
+static Bool
+regexMatchExpEvalClass (CompDisplay *d,
+ CompWindow *w,
+ CompPrivate private)
+{
+ regex_t *preg = (regex_t *) private.ptr;
+ int status;
+
+ if (!preg)
+ return FALSE;
+
+ if (!w->resClass)
+ return FALSE;
+
+ status = regexec (preg, w->resClass, 0, NULL, 0);
+ if (status)
+ return FALSE;
+
+ return TRUE;
+}
+
+static Bool
+regexMatchExpEvalName (CompDisplay *d,
+ CompWindow *w,
+ CompPrivate private)
+{
+ regex_t *preg = (regex_t *) private.ptr;
+ int status;
+
+ if (!preg)
+ return FALSE;
+
+ if (!w->resName)
+ return FALSE;
+
+ status = regexec (preg, w->resName, 0, NULL, 0);
+ if (status)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+regexMatchInitExp (CompDisplay *d,
+ CompMatchExp *exp,
+ const char *value)
+{
+ static struct _Prefix {
+ char *s;
+ int len;
+ CompMatchExpEvalProc eval;
+ unsigned int flags;
+ } prefix[] = {
+ { "title=", 6, regexMatchExpEvalTitle, 0 },
+ { "role=", 5, regexMatchExpEvalRole, 0 },
+ { "class=", 6, regexMatchExpEvalClass, 0 },
+ { "name=", 5, regexMatchExpEvalName, 0 },
+ { "ititle=", 7, regexMatchExpEvalTitle, REG_ICASE },
+ { "irole=", 6, regexMatchExpEvalRole, REG_ICASE },
+ { "iclass=", 7, regexMatchExpEvalClass, REG_ICASE },
+ { "iname=", 6, regexMatchExpEvalName, REG_ICASE },
+ };
+ int i;
+
+ REGEX_DISPLAY (d);
+
+ for (i = 0; i < sizeof (prefix) / sizeof (prefix[0]); i++)
+ if (strncmp (value, prefix[i].s, prefix[i].len) == 0)
+ break;
+
+ if (i < sizeof (prefix) / sizeof (prefix[0]))
+ {
+ regex_t *preg;
+
+ preg = malloc (sizeof (regex_t));
+ if (preg)
+ {
+ int status;
+
+ value += prefix[i].len;
+
+ status = regcomp (preg, value, REG_NOSUB | prefix[i].flags);
+ if (status)
+ {
+ char errMsg[1024];
+
+ regerror (status, preg, errMsg, sizeof (errMsg));
+
+ compLogMessage ("regex", CompLogLevelWarn,
+ "%s = %s", errMsg, value);
+
+ regfree (preg);
+ free (preg);
+ preg = NULL;
+ }
+ }
+
+ exp->fini = regexMatchExpFini;
+ exp->eval = prefix[i].eval;
+ exp->priv.ptr = preg;
+ }
+ else
+ {
+ UNWRAP (rd, d, matchInitExp);
+ (*d->matchInitExp) (d, exp, value);
+ WRAP (rd, d, matchInitExp, regexMatchInitExp);
+ }
+}
+
+static char *
+regexGetStringProperty (CompWindow *w,
+ Atom propAtom,
+ Atom formatAtom)
+{
+ Atom type;
+ unsigned long nItems;
+ unsigned long bytesAfter;
+ unsigned char *str = NULL;
+ int format, result;
+ char *retval;
+
+ result = XGetWindowProperty (w->screen->display->display,
+ w->id, propAtom, 0, LONG_MAX,
+ FALSE, formatAtom, &type, &format, &nItems,
+ &bytesAfter, (unsigned char **) &str);
+
+ if (result != Success)
+ return NULL;
+
+ if (type != formatAtom)
+ {
+ XFree (str);
+ return NULL;
+ }
+
+ retval = strdup ((char *) str);
+
+ XFree (str);
+
+ return retval;
+}
+
+static char *
+regexGetWindowTitle (CompWindow *w)
+{
+ CompDisplay *d = w->screen->display;
+ char *title;
+
+ REGEX_DISPLAY (d);
+
+ title = regexGetStringProperty (w, rd->visibleNameAtom, d->utf8StringAtom);
+ if (title)
+ return title;
+
+ title = regexGetStringProperty (w, d->wmNameAtom, d->utf8StringAtom);
+ if (title)
+ return title;
+
+ return regexGetStringProperty (w, XA_WM_NAME, XA_STRING);
+}
+
+static void
+regexHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ REGEX_DISPLAY (d);
+
+ UNWRAP (rd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (rd, d, handleEvent, regexHandleEvent);
+
+ if (event->type == PropertyNotify)
+ {
+ CompWindow *w;
+
+ if (event->xproperty.atom == XA_WM_NAME)
+ {
+ w = findWindowAtDisplay (d, event->xproperty.window);
+ if (w)
+ {
+ REGEX_WINDOW (w);
+
+ if (rw->title)
+ free (rw->title);
+
+ rw->title = regexGetWindowTitle (w);
+
+ (*d->matchPropertyChanged) (d, w);
+ }
+ }
+ if (event->xproperty.atom == rd->roleAtom)
+ {
+ w = findWindowAtDisplay (d, event->xproperty.window);
+ if (w)
+ {
+ REGEX_WINDOW (w);
+
+ if (rw->role)
+ free (rw->role);
+
+ rw->role = regexGetStringProperty (w, rd->roleAtom, XA_STRING);
+
+ (*d->matchPropertyChanged) (d, w);
+ }
+ }
+ else if (event->xproperty.atom == XA_WM_CLASS)
+ {
+ w = findWindowAtDisplay (d, event->xproperty.window);
+ if (w)
+ (*d->matchPropertyChanged) (d, w);
+ }
+ }
+}
+
+static Bool
+regexRegisterExpHandler (void *closure)
+{
+ CompDisplay *display = (CompDisplay *) closure;
+
+ (*display->matchExpHandlerChanged) (display);
+
+ return FALSE;
+}
+
+static Bool
+regexInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ RegexDisplay *rd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ rd = malloc (sizeof (RegexDisplay));
+ if (!rd)
+ return FALSE;
+
+ rd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (rd->screenPrivateIndex < 0)
+ {
+ free (rd);
+ return FALSE;
+ }
+
+ rd->roleAtom = XInternAtom (d->display, "WM_WINDOW_ROLE", 0);
+ rd->visibleNameAtom = XInternAtom (d->display, "_NET_WM_VISIBLE_NAME", 0);
+
+ WRAP (rd, d, handleEvent, regexHandleEvent);
+ WRAP (rd, d, matchInitExp, regexMatchInitExp);
+
+ d->base.privates[displayPrivateIndex].ptr = rd;
+
+ /* one shot timeout to which will register the expression handler
+ after all screens and windows have been initialized */
+ compAddTimeout (0, 0, regexRegisterExpHandler, (void *) d);
+
+ return TRUE;
+}
+
+static void
+regexFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ REGEX_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, rd->screenPrivateIndex);
+
+ UNWRAP (rd, d, handleEvent);
+ UNWRAP (rd, d, matchInitExp);
+
+ if (d->base.parent)
+ (*d->matchExpHandlerChanged) (d);
+
+ free (rd);
+}
+
+static Bool
+regexInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ RegexScreen *rs;
+
+ REGEX_DISPLAY (s->display);
+
+ rs = malloc (sizeof (RegexScreen));
+ if (!rs)
+ return FALSE;
+
+ rs->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (rs->windowPrivateIndex < 0)
+ {
+ free (rs);
+ return FALSE;
+ }
+
+ s->base.privates[rd->screenPrivateIndex].ptr = rs;
+
+ return TRUE;
+}
+
+static void
+regexFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ REGEX_SCREEN (s);
+
+ freeWindowPrivateIndex (s, rs->windowPrivateIndex);
+
+ free (rs);
+}
+
+static Bool
+regexInitWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ RegexWindow *rw;
+
+ REGEX_DISPLAY (w->screen->display);
+ REGEX_SCREEN (w->screen);
+
+ rw = malloc (sizeof (RegexWindow));
+ if (!rw)
+ return FALSE;
+
+ rw->title = regexGetWindowTitle (w);
+ rw->role = regexGetStringProperty (w, rd->roleAtom, XA_STRING);
+
+ w->base.privates[rs->windowPrivateIndex].ptr = rw;
+
+ return TRUE;
+}
+
+static void
+regexFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ REGEX_WINDOW (w);
+
+ if (rw->title)
+ free (rw->title);
+
+ if (rw->role)
+ free (rw->role);
+
+ free (rw);
+}
+
+static CompBool
+regexInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) regexInitDisplay,
+ (InitPluginObjectProc) regexInitScreen,
+ (InitPluginObjectProc) regexInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+regexFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) regexFiniDisplay,
+ (FiniPluginObjectProc) regexFiniScreen,
+ (FiniPluginObjectProc) regexFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static Bool
+regexInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&regexMetadata, p->vTable->name,
+ 0, 0, 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&regexMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&regexMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+regexFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&regexMetadata);
+}
+
+static CompMetadata *
+regexGetMetadata (CompPlugin *plugin)
+{
+ return &regexMetadata;
+}
+
+static CompPluginVTable regexVTable = {
+ "regex",
+ regexGetMetadata,
+ regexInit,
+ regexFini,
+ regexInitObject,
+ regexFiniObject,
+ 0, /* GetObjectOptions */
+ 0 /* SetObjectOption */
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &regexVTable;
+}
diff --git a/plugins/resize.c b/plugins/resize.c
new file mode 100644
index 0000000..8943306
--- /dev/null
+++ b/plugins/resize.c
@@ -0,0 +1,1499 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+
+#include <compiz-core.h>
+
+static CompMetadata resizeMetadata;
+
+#define ResizeUpMask (1L << 0)
+#define ResizeDownMask (1L << 1)
+#define ResizeLeftMask (1L << 2)
+#define ResizeRightMask (1L << 3)
+
+#define RESIZE_MODE_NORMAL 0
+#define RESIZE_MODE_OUTLINE 1
+#define RESIZE_MODE_RECTANGLE 2
+#define RESIZE_MODE_STRETCH 3
+#define RESIZE_MODE_LAST RESIZE_MODE_STRETCH
+
+struct _ResizeKeys {
+ char *name;
+ int dx;
+ int dy;
+ unsigned int warpMask;
+ unsigned int resizeMask;
+} rKeys[] = {
+ { "Left", -1, 0, ResizeLeftMask | ResizeRightMask, ResizeLeftMask },
+ { "Right", 1, 0, ResizeLeftMask | ResizeRightMask, ResizeRightMask },
+ { "Up", 0, -1, ResizeUpMask | ResizeDownMask, ResizeUpMask },
+ { "Down", 0, 1, ResizeUpMask | ResizeDownMask, ResizeDownMask }
+};
+
+#define NUM_KEYS (sizeof (rKeys) / sizeof (rKeys[0]))
+
+#define MIN_KEY_WIDTH_INC 24
+#define MIN_KEY_HEIGHT_INC 24
+
+#define RESIZE_DISPLAY_OPTION_INITIATE_NORMAL_KEY 0
+#define RESIZE_DISPLAY_OPTION_INITIATE_OUTLINE_KEY 1
+#define RESIZE_DISPLAY_OPTION_INITIATE_RECTANGLE_KEY 2
+#define RESIZE_DISPLAY_OPTION_INITIATE_STRETCH_KEY 3
+#define RESIZE_DISPLAY_OPTION_INITIATE_BUTTON 4
+#define RESIZE_DISPLAY_OPTION_INITIATE_KEY 5
+#define RESIZE_DISPLAY_OPTION_MODE 6
+#define RESIZE_DISPLAY_OPTION_BORDER_COLOR 7
+#define RESIZE_DISPLAY_OPTION_FILL_COLOR 8
+#define RESIZE_DISPLAY_OPTION_NORMAL_MATCH 9
+#define RESIZE_DISPLAY_OPTION_OUTLINE_MATCH 10
+#define RESIZE_DISPLAY_OPTION_RECTANGLE_MATCH 11
+#define RESIZE_DISPLAY_OPTION_STRETCH_MATCH 12
+#define RESIZE_DISPLAY_OPTION_NUM 13
+
+static int displayPrivateIndex;
+
+typedef struct _ResizeDisplay {
+ CompOption opt[RESIZE_DISPLAY_OPTION_NUM];
+
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ Atom resizeNotifyAtom;
+ Atom resizeInformationAtom;
+
+ CompWindow *w;
+ int mode;
+ XRectangle savedGeometry;
+ XRectangle geometry;
+
+ int releaseButton;
+ unsigned int mask;
+ int pointerDx;
+ int pointerDy;
+ KeyCode key[NUM_KEYS];
+} ResizeDisplay;
+
+typedef struct _ResizeScreen {
+ int grabIndex;
+
+ WindowResizeNotifyProc windowResizeNotify;
+ PaintOutputProc paintOutput;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+
+ Cursor leftCursor;
+ Cursor rightCursor;
+ Cursor upCursor;
+ Cursor upLeftCursor;
+ Cursor upRightCursor;
+ Cursor downCursor;
+ Cursor downLeftCursor;
+ Cursor downRightCursor;
+ Cursor middleCursor;
+ Cursor cursor[NUM_KEYS];
+} ResizeScreen;
+
+#define GET_RESIZE_DISPLAY(d) \
+ ((ResizeDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define RESIZE_DISPLAY(d) \
+ ResizeDisplay *rd = GET_RESIZE_DISPLAY (d)
+
+#define GET_RESIZE_SCREEN(s, rd) \
+ ((ResizeScreen *) (s)->base.privates[(rd)->screenPrivateIndex].ptr)
+
+#define RESIZE_SCREEN(s) \
+ ResizeScreen *rs = GET_RESIZE_SCREEN (s, GET_RESIZE_DISPLAY (s->display))
+
+#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
+
+static void
+resizeGetPaintRectangle (CompDisplay *d,
+ BoxPtr pBox)
+{
+ RESIZE_DISPLAY (d);
+
+ pBox->x1 = rd->geometry.x - rd->w->input.left;
+ pBox->y1 = rd->geometry.y - rd->w->input.top;
+ pBox->x2 = rd->geometry.x +
+ rd->geometry.width + rd->w->serverBorderWidth * 2 +
+ rd->w->input.right;
+
+ if (rd->w->shaded)
+ {
+ pBox->y2 = rd->geometry.y + rd->w->height + rd->w->input.bottom;
+ }
+ else
+ {
+ pBox->y2 = rd->geometry.y +
+ rd->geometry.height + rd->w->serverBorderWidth * 2 +
+ rd->w->input.bottom;
+ }
+}
+
+static void
+resizeGetStretchScale (CompWindow *w,
+ BoxPtr pBox,
+ float *xScale,
+ float *yScale)
+{
+ int width, height;
+
+ width = w->width + w->input.left + w->input.right;
+ height = w->height + w->input.top + w->input.bottom;
+
+ *xScale = (width) ? (pBox->x2 - pBox->x1) / (float) width : 1.0f;
+ *yScale = (height) ? (pBox->y2 - pBox->y1) / (float) height : 1.0f;
+}
+
+static void
+resizeGetStretchRectangle (CompDisplay *d,
+ BoxPtr pBox)
+{
+ BoxRec box;
+ float xScale, yScale;
+
+ RESIZE_DISPLAY (d);
+
+ resizeGetPaintRectangle (d, &box);
+ resizeGetStretchScale (rd->w, &box, &xScale, &yScale);
+
+ pBox->x1 = box.x1 - (rd->w->output.left - rd->w->input.left) * xScale;
+ pBox->y1 = box.y1 - (rd->w->output.top - rd->w->input.top) * yScale;
+ pBox->x2 = box.x2 + rd->w->output.right * xScale;
+ pBox->y2 = box.y2 + rd->w->output.bottom * yScale;
+}
+
+static void
+resizeDamageRectangle (CompScreen *s,
+ BoxPtr pBox)
+{
+ REGION reg;
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ reg.extents = *pBox;
+
+ reg.extents.x1 -= 1;
+ reg.extents.y1 -= 1;
+ reg.extents.x2 += 1;
+ reg.extents.y2 += 1;
+
+ damageScreenRegion (s, &reg);
+}
+
+static Cursor
+resizeCursorFromResizeMask (CompScreen *s,
+ unsigned int mask)
+{
+ Cursor cursor;
+
+ RESIZE_SCREEN (s);
+
+ if (mask & ResizeLeftMask)
+ {
+ if (mask & ResizeDownMask)
+ cursor = rs->downLeftCursor;
+ else if (mask & ResizeUpMask)
+ cursor = rs->upLeftCursor;
+ else
+ cursor = rs->leftCursor;
+ }
+ else if (mask & ResizeRightMask)
+ {
+ if (mask & ResizeDownMask)
+ cursor = rs->downRightCursor;
+ else if (mask & ResizeUpMask)
+ cursor = rs->upRightCursor;
+ else
+ cursor = rs->rightCursor;
+ }
+ else if (mask & ResizeUpMask)
+ {
+ cursor = rs->upCursor;
+ }
+ else
+ {
+ cursor = rs->downCursor;
+ }
+
+ return cursor;
+}
+
+static void
+resizeSendResizeNotify (CompDisplay *d)
+{
+ XEvent xev;
+
+ RESIZE_DISPLAY (d);
+ xev.xclient.type = ClientMessage;
+ xev.xclient.display = d->display;
+ xev.xclient.format = 32;
+
+ xev.xclient.message_type = rd->resizeNotifyAtom;
+ xev.xclient.window = rd->w->id;
+
+ xev.xclient.data.l[0] = rd->geometry.x;
+ xev.xclient.data.l[1] = rd->geometry.y;
+ xev.xclient.data.l[2] = rd->geometry.width;
+ xev.xclient.data.l[3] = rd->geometry.height;
+ xev.xclient.data.l[4] = 0;
+
+ XSendEvent (d->display,
+ rd->w->screen->root,
+ FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &xev);
+}
+
+static void
+resizeUpdateWindowProperty (CompDisplay *d)
+{
+ unsigned long data[4];
+
+ RESIZE_DISPLAY (d);
+
+ data[0] = rd->geometry.x;
+ data[1] = rd->geometry.y;
+ data[2] = rd->geometry.width;
+ data[3] = rd->geometry.height;
+
+ XChangeProperty (d->display, rd->w->id,
+ rd->resizeInformationAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char*) data, 4);
+}
+
+static void
+resizeFinishResizing (CompDisplay *d)
+{
+ RESIZE_DISPLAY (d);
+
+ (*rd->w->screen->windowUngrabNotify) (rd->w);
+
+ XDeleteProperty (d->display,
+ rd->w->id,
+ rd->resizeInformationAtom);
+
+ rd->w = NULL;
+}
+
+static Bool
+resizeInitiate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompWindow *w;
+ Window xid;
+
+ RESIZE_DISPLAY (d);
+
+ xid = getIntOptionNamed (option, nOption, "window", 0);
+
+ w = findWindowAtDisplay (d, xid);
+ if (w && (w->actions & CompWindowActionResizeMask))
+ {
+ unsigned int mask;
+ int x, y;
+ int button;
+ int i;
+
+ RESIZE_SCREEN (w->screen);
+
+ x = getIntOptionNamed (option, nOption, "x", pointerX);
+ y = getIntOptionNamed (option, nOption, "y", pointerY);
+
+ button = getIntOptionNamed (option, nOption, "button", -1);
+
+ mask = getIntOptionNamed (option, nOption, "direction", 0);
+
+ /* Initiate the resize in the direction suggested by the
+ * sector of the window the mouse is in, eg drag in top left
+ * will resize up and to the left. Keyboard resize starts out
+ * with the cursor in the middle of the window and then starts
+ * resizing the edge corresponding to the next key press. */
+ if (state & CompActionStateInitKey)
+ {
+ mask = 0;
+ }
+ else if (!mask)
+ {
+ unsigned int sectorSizeX = w->serverWidth / 3;
+ unsigned int sectorSizeY = w->serverHeight / 3;
+ unsigned int posX = x - w->serverX;
+ unsigned int posY = y - w->serverY;
+
+ if (posX < sectorSizeX)
+ mask |= ResizeLeftMask;
+ else if (posX > (2 * sectorSizeX))
+ mask |= ResizeRightMask;
+
+ if (posY < sectorSizeY)
+ mask |= ResizeUpMask;
+ else if (posY > (2 * sectorSizeY))
+ mask |= ResizeDownMask;
+
+ /* if the pointer was in the middle of the window,
+ do nothing */
+ if (!mask)
+ return FALSE;
+ }
+
+ if (otherScreenGrabExist (w->screen, "resize", 0))
+ return FALSE;
+
+ if (rd->w)
+ return FALSE;
+
+ if (w->type & (CompWindowTypeDesktopMask |
+ CompWindowTypeDockMask |
+ CompWindowTypeFullscreenMask))
+ return FALSE;
+
+ if (w->attrib.override_redirect)
+ return FALSE;
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ if (w->shaded)
+ mask &= ~(ResizeUpMask | ResizeDownMask);
+
+ rd->w = w;
+ rd->mask = mask;
+
+ rd->savedGeometry.x = w->serverX;
+ rd->savedGeometry.y = w->serverY;
+ rd->savedGeometry.width = w->serverWidth;
+ rd->savedGeometry.height = w->serverHeight;
+
+ rd->geometry = rd->savedGeometry;
+
+ rd->pointerDx = x - pointerX;
+ rd->pointerDy = y - pointerY;
+
+ if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE)
+ {
+ /* if the window is fully maximized, showing the outline or
+ rectangle would be visually distracting as the window can't
+ be resized anyway; so we better don't use them in this case */
+ rd->mode = RESIZE_MODE_NORMAL;
+ }
+ else
+ {
+ rd->mode = rd->opt[RESIZE_DISPLAY_OPTION_MODE].value.i;
+ for (i = 0; i <= RESIZE_MODE_LAST; i++)
+ {
+ if (action == &rd->opt[i].value.action)
+ {
+ rd->mode = i;
+ break;
+ }
+ }
+
+ if (i > RESIZE_MODE_LAST)
+ {
+ int index;
+
+ for (i = 0; i <= RESIZE_MODE_LAST; i++)
+ {
+ index = RESIZE_DISPLAY_OPTION_NORMAL_MATCH + i;
+ if (matchEval (&rd->opt[index].value.match, w))
+ {
+ rd->mode = i;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!rs->grabIndex)
+ {
+ Cursor cursor;
+
+ if (state & CompActionStateInitKey)
+ {
+ cursor = rs->middleCursor;
+ }
+ else
+ {
+ cursor = resizeCursorFromResizeMask (w->screen, mask);
+ }
+
+ rs->grabIndex = pushScreenGrab (w->screen, cursor, "resize");
+ }
+
+ if (rs->grabIndex)
+ {
+ BoxRec box;
+
+ rd->releaseButton = button;
+
+ (w->screen->windowGrabNotify) (w, x, y, state,
+ CompWindowGrabResizeMask |
+ CompWindowGrabButtonMask);
+
+ if (d->opt[COMP_DISPLAY_OPTION_RAISE_ON_CLICK].value.b)
+ updateWindowAttributes (w,
+ CompStackingUpdateModeAboveFullscreen);
+
+ /* using the paint rectangle is enough here
+ as we don't have any stretch yet */
+ resizeGetPaintRectangle (d, &box);
+ resizeDamageRectangle (w->screen, &box);
+
+ if (state & CompActionStateInitKey)
+ {
+ int xRoot, yRoot;
+
+ xRoot = w->serverX + (w->serverWidth / 2);
+ yRoot = w->serverY + (w->serverHeight / 2);
+
+ warpPointer (w->screen, xRoot - pointerX, yRoot - pointerY);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+resizeTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ RESIZE_DISPLAY (d);
+
+ if (rd->w)
+ {
+ CompWindow *w = rd->w;
+ XWindowChanges xwc;
+ unsigned int mask = 0;
+
+ RESIZE_SCREEN (w->screen);
+
+ if (rd->mode == RESIZE_MODE_NORMAL)
+ {
+ if (state & CompActionStateCancel)
+ {
+ xwc.x = rd->savedGeometry.x;
+ xwc.y = rd->savedGeometry.y;
+ xwc.width = rd->savedGeometry.width;
+ xwc.height = rd->savedGeometry.height;
+
+ mask = CWX | CWY | CWWidth | CWHeight;
+ }
+ }
+ else
+ {
+ XRectangle geometry;
+
+ if (state & CompActionStateCancel)
+ geometry = rd->savedGeometry;
+ else
+ geometry = rd->geometry;
+
+ if (memcmp (&geometry, &rd->savedGeometry, sizeof (geometry)) == 0)
+ {
+ BoxRec box;
+
+ if (rd->mode == RESIZE_MODE_STRETCH)
+ resizeGetStretchRectangle (d, &box);
+ else
+ resizeGetPaintRectangle (d, &box);
+
+ resizeDamageRectangle (w->screen, &box);
+ }
+ else
+ {
+ xwc.x = geometry.x;
+ xwc.y = geometry.y;
+ xwc.width = geometry.width;
+ xwc.height = geometry.height;
+
+ mask = CWX | CWY | CWWidth | CWHeight;
+ }
+ }
+
+ if ((mask & CWWidth) && xwc.width == w->serverWidth)
+ mask &= ~CWWidth;
+
+ if ((mask & CWHeight) && xwc.height == w->serverHeight)
+ mask &= ~CWHeight;
+
+ if (mask)
+ {
+ if (mask & (CWWidth | CWHeight))
+ sendSyncRequest (w);
+
+ configureXWindow (w, mask, &xwc);
+ }
+
+ if (!(mask & (CWWidth | CWHeight)))
+ resizeFinishResizing (d);
+
+ if (rs->grabIndex)
+ {
+ removeScreenGrab (w->screen, rs->grabIndex, NULL);
+ rs->grabIndex = 0;
+ }
+
+ rd->releaseButton = 0;
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+static void
+resizeUpdateWindowSize (CompDisplay *d)
+{
+ RESIZE_DISPLAY (d);
+
+ if (rd->w->syncWait)
+ return;
+
+ if (rd->w->serverWidth != rd->geometry.width ||
+ rd->w->serverHeight != rd->geometry.height)
+ {
+ XWindowChanges xwc;
+
+ xwc.x = rd->geometry.x;
+ xwc.y = rd->geometry.y;
+ xwc.width = rd->geometry.width;
+ xwc.height = rd->geometry.height;
+
+ sendSyncRequest (rd->w);
+
+ configureXWindow (rd->w,
+ CWX | CWY | CWWidth | CWHeight,
+ &xwc);
+ }
+}
+
+static void
+resizeHandleKeyEvent (CompScreen *s,
+ KeyCode keycode)
+{
+ RESIZE_SCREEN (s);
+ RESIZE_DISPLAY (s->display);
+
+ if (rs->grabIndex && rd->w)
+ {
+ CompWindow *w = rd->w;
+ int widthInc, heightInc, i;
+
+ widthInc = w->sizeHints.width_inc;
+ heightInc = w->sizeHints.height_inc;
+
+ if (widthInc < MIN_KEY_WIDTH_INC)
+ widthInc = MIN_KEY_WIDTH_INC;
+
+ if (heightInc < MIN_KEY_HEIGHT_INC)
+ heightInc = MIN_KEY_HEIGHT_INC;
+
+ for (i = 0; i < NUM_KEYS; i++)
+ {
+ if (keycode != rd->key[i])
+ continue;
+
+ if (rd->mask & rKeys[i].warpMask)
+ {
+ XWarpPointer (s->display->display, None, None, 0, 0, 0, 0,
+ rKeys[i].dx * widthInc,
+ rKeys[i].dy * heightInc);
+ }
+ else
+ {
+ int x, y, left, top, width, height;
+
+ left = w->serverX - w->input.left;
+ top = w->serverY - w->input.top;
+ width = w->input.left + w->serverWidth + w->input.right;
+ height = w->input.top + w->serverHeight + w->input.bottom;
+
+ x = left + width * (rKeys[i].dx + 1) / 2;
+ y = top + height * (rKeys[i].dy + 1) / 2;
+
+ warpPointer (s, x - pointerX, y - pointerY);
+
+ rd->mask = rKeys[i].resizeMask;
+
+ updateScreenGrab (s, rs->grabIndex, rs->cursor[i]);
+ }
+ break;
+ }
+ }
+}
+
+static void
+resizeHandleMotionEvent (CompScreen *s,
+ int xRoot,
+ int yRoot)
+{
+ RESIZE_SCREEN (s);
+
+ if (rs->grabIndex)
+ {
+ BoxRec box;
+ int w, h;
+
+ RESIZE_DISPLAY (s->display);
+
+ w = rd->savedGeometry.width;
+ h = rd->savedGeometry.height;
+
+ if (!rd->mask)
+ {
+ CompWindow *w = rd->w;
+ int xDist, yDist;
+ int minPointerOffsetX, minPointerOffsetY;
+
+ xDist = xRoot - (w->serverX + (w->serverWidth / 2));
+ yDist = yRoot - (w->serverY + (w->serverHeight / 2));
+
+ /* decision threshold is 10% of window size */
+ minPointerOffsetX = MIN (20, w->serverWidth / 10);
+ minPointerOffsetY = MIN (20, w->serverHeight / 10);
+
+ /* if we reached the threshold in one direction,
+ make the threshold in the other direction smaller
+ so there is a chance that this threshold also can
+ be reached (by diagonal movement) */
+ if (abs (xDist) > minPointerOffsetX)
+ minPointerOffsetY /= 2;
+ else if (abs (yDist) > minPointerOffsetY)
+ minPointerOffsetX /= 2;
+
+ if (abs (xDist) > minPointerOffsetX)
+ {
+ if (xDist > 0)
+ rd->mask |= ResizeRightMask;
+ else
+ rd->mask |= ResizeLeftMask;
+ }
+
+ if (abs (yDist) > minPointerOffsetY)
+ {
+ if (yDist > 0)
+ rd->mask |= ResizeDownMask;
+ else
+ rd->mask |= ResizeUpMask;
+ }
+
+ /* if the pointer movement was enough to determine a
+ direction, warp the pointer to the appropriate edge
+ and set the right cursor */
+ if (rd->mask)
+ {
+ Cursor cursor;
+ CompScreen *s = rd->w->screen;
+ CompAction *action;
+ int pointerAdjustX = 0;
+ int pointerAdjustY = 0;
+ int option = RESIZE_DISPLAY_OPTION_INITIATE_KEY;
+
+ RESIZE_SCREEN (s);
+
+ action = &rd->opt[option].value.action;
+ action->state |= CompActionStateTermButton;
+
+ if (rd->mask & ResizeRightMask)
+ pointerAdjustX = w->serverX + w->serverWidth +
+ w->input.right - xRoot;
+ else if (rd->mask & ResizeLeftMask)
+ pointerAdjustX = w->serverX - w->input.left - xRoot;
+
+ if (rd->mask & ResizeDownMask)
+ pointerAdjustY = w->serverY + w->serverHeight +
+ w->input.bottom - yRoot;
+ else if (rd->mask & ResizeUpMask)
+ pointerAdjustY = w->serverY - w->input.top - yRoot;
+
+ warpPointer (s, pointerAdjustX, pointerAdjustY);
+
+ cursor = resizeCursorFromResizeMask (s, rd->mask);
+ updateScreenGrab (s, rs->grabIndex, cursor);
+ }
+ }
+ else
+ {
+ /* only accumulate pointer movement if a mask is
+ already set as we don't have a use for the
+ difference information otherwise */
+ rd->pointerDx += xRoot - lastPointerX;
+ rd->pointerDy += yRoot - lastPointerY;
+ }
+
+ if (rd->mask & ResizeLeftMask)
+ w -= rd->pointerDx;
+ else if (rd->mask & ResizeRightMask)
+ w += rd->pointerDx;
+
+ if (rd->mask & ResizeUpMask)
+ h -= rd->pointerDy;
+ else if (rd->mask & ResizeDownMask)
+ h += rd->pointerDy;
+
+ if (rd->w->state & CompWindowStateMaximizedVertMask)
+ h = rd->w->serverHeight;
+
+ if (rd->w->state & CompWindowStateMaximizedHorzMask)
+ w = rd->w->serverWidth;
+
+ constrainNewWindowSize (rd->w, w, h, &w, &h);
+
+ if (rd->mode != RESIZE_MODE_NORMAL)
+ {
+ if (rd->mode == RESIZE_MODE_STRETCH)
+ resizeGetStretchRectangle (s->display, &box);
+ else
+ resizeGetPaintRectangle (s->display, &box);
+
+ resizeDamageRectangle (s, &box);
+ }
+
+ if (rd->mask & ResizeLeftMask)
+ rd->geometry.x -= w - rd->geometry.width;
+
+ if (rd->mask & ResizeUpMask)
+ rd->geometry.y -= h - rd->geometry.height;
+
+ rd->geometry.width = w;
+ rd->geometry.height = h;
+
+ if (rd->mode != RESIZE_MODE_NORMAL)
+ {
+ if (rd->mode == RESIZE_MODE_STRETCH)
+ resizeGetStretchRectangle (s->display, &box);
+ else
+ resizeGetPaintRectangle (s->display, &box);
+
+ resizeDamageRectangle (s, &box);
+ }
+ else
+ {
+ resizeUpdateWindowSize (s->display);
+ }
+
+ resizeUpdateWindowProperty (s->display);
+ resizeSendResizeNotify (s->display);
+ }
+}
+
+static void
+resizeHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ RESIZE_DISPLAY (d);
+
+ switch (event->type) {
+ case KeyPress:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ resizeHandleKeyEvent (s, event->xkey.keycode);
+ break;
+ case ButtonRelease:
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+ RESIZE_SCREEN (s);
+
+ if (rs->grabIndex)
+ {
+ if (rd->releaseButton == -1 ||
+ event->xbutton.button == rd->releaseButton)
+ {
+ int opt = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON;
+ CompAction *action = &rd->opt[opt].value.action;
+
+ resizeTerminate (d, action, CompActionStateTermButton,
+ NULL, 0);
+ }
+ }
+ }
+ break;
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ resizeHandleMotionEvent (s, pointerX, pointerY);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ s = findScreenAtDisplay (d, event->xcrossing.root);
+ if (s)
+ resizeHandleMotionEvent (s, pointerX, pointerY);
+ break;
+ case ClientMessage:
+ if (event->xclient.message_type == d->wmMoveResizeAtom)
+ {
+ CompWindow *w;
+
+ if (event->xclient.data.l[2] <= WmMoveResizeSizeLeft ||
+ event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
+ {
+ w = findWindowAtDisplay (d, event->xclient.window);
+ if (w)
+ {
+ CompOption o[6];
+ int option;
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "window";
+ o[0].value.i = event->xclient.window;
+
+ if (event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
+ {
+ option = RESIZE_DISPLAY_OPTION_INITIATE_KEY;
+
+ resizeInitiate (d, &rd->opt[option].value.action,
+ CompActionStateInitKey,
+ o, 1);
+ }
+ else
+ {
+ static unsigned int mask[] = {
+ ResizeUpMask | ResizeLeftMask,
+ ResizeUpMask,
+ ResizeUpMask | ResizeRightMask,
+ ResizeRightMask,
+ ResizeDownMask | ResizeRightMask,
+ ResizeDownMask,
+ ResizeDownMask | ResizeLeftMask,
+ ResizeLeftMask,
+ };
+ unsigned int mods;
+ Window root, child;
+ int xRoot, yRoot, i;
+
+ option = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON;
+
+ XQueryPointer (d->display, w->screen->root,
+ &root, &child, &xRoot, &yRoot,
+ &i, &i, &mods);
+
+ /* TODO: not only button 1 */
+ if (mods & Button1Mask)
+ {
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "modifiers";
+ o[1].value.i = mods;
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "x";
+ o[2].value.i = event->xclient.data.l[0];
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "y";
+ o[3].value.i = event->xclient.data.l[1];
+
+ o[4].type = CompOptionTypeInt;
+ o[4].name = "direction";
+ o[4].value.i = mask[event->xclient.data.l[2]];
+
+ o[5].type = CompOptionTypeInt;
+ o[5].name = "button";
+ o[5].value.i = event->xclient.data.l[3] ?
+ event->xclient.data.l[3] : -1;
+
+ resizeInitiate (d,
+ &rd->opt[option].value.action,
+ CompActionStateInitButton,
+ o, 6);
+
+ resizeHandleMotionEvent (w->screen, xRoot, yRoot);
+ }
+ }
+ }
+ }
+ else if (rd->w && event->xclient.data.l[2] == WmMoveResizeCancel)
+ {
+ if (rd->w->id == event->xclient.window)
+ {
+ int option;
+
+ option = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON;
+ resizeTerminate (d, &rd->opt[option].value.action,
+ CompActionStateCancel, NULL, 0);
+ option = RESIZE_DISPLAY_OPTION_INITIATE_KEY;
+ resizeTerminate (d, &rd->opt[option].value.action,
+ CompActionStateCancel, NULL, 0);
+ }
+ }
+ }
+ break;
+ case DestroyNotify:
+ if (rd->w && rd->w->id == event->xdestroywindow.window)
+ {
+ int option;
+
+ option = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON;
+ resizeTerminate (d, &rd->opt[option].value.action, 0, NULL, 0);
+ option = RESIZE_DISPLAY_OPTION_INITIATE_KEY;
+ resizeTerminate (d, &rd->opt[option].value.action, 0, NULL, 0);
+ }
+ break;
+ case UnmapNotify:
+ if (rd->w && rd->w->id == event->xunmap.window)
+ {
+ int option;
+
+ option = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON;
+ resizeTerminate (d, &rd->opt[option].value.action, 0, NULL, 0);
+ option = RESIZE_DISPLAY_OPTION_INITIATE_KEY;
+ resizeTerminate (d, &rd->opt[option].value.action, 0, NULL, 0);
+ }
+ default:
+ break;
+ }
+
+ UNWRAP (rd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (rd, d, handleEvent, resizeHandleEvent);
+
+ if (event->type == d->syncEvent + XSyncAlarmNotify)
+ {
+ if (rd->w)
+ {
+ XSyncAlarmNotifyEvent *sa;
+
+ sa = (XSyncAlarmNotifyEvent *) event;
+
+ if (rd->w->syncAlarm == sa->alarm)
+ resizeUpdateWindowSize (d);
+ }
+ }
+}
+
+static void
+resizeWindowResizeNotify (CompWindow *w,
+ int dx,
+ int dy,
+ int dwidth,
+ int dheight)
+{
+ RESIZE_DISPLAY (w->screen->display);
+ RESIZE_SCREEN (w->screen);
+
+ UNWRAP (rs, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
+ WRAP (rs, w->screen, windowResizeNotify, resizeWindowResizeNotify);
+
+ if (rd->w == w && !rs->grabIndex)
+ resizeFinishResizing (w->screen->display);
+}
+
+static void
+resizePaintRectangle (CompScreen *s,
+ const ScreenPaintAttrib *sa,
+ const CompTransform *transform,
+ CompOutput *output,
+ unsigned short *borderColor,
+ unsigned short *fillColor)
+{
+ BoxRec box;
+ CompTransform sTransform = *transform;
+
+ resizeGetPaintRectangle (s->display, &box);
+
+ glPushMatrix ();
+
+ transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform);
+
+ glLoadMatrixf (sTransform.m);
+
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ glEnable (GL_BLEND);
+
+ /* fill rectangle */
+ if (fillColor)
+ {
+ glColor4usv (fillColor);
+ glRecti (box.x1, box.y2, box.x2, box.y1);
+ }
+
+ /* draw outline */
+ glColor4usv (borderColor);
+ glLineWidth (2.0);
+ glBegin (GL_LINE_LOOP);
+ glVertex2i (box.x1, box.y1);
+ glVertex2i (box.x2, box.y1);
+ glVertex2i (box.x2, box.y2);
+ glVertex2i (box.x1, box.y2);
+ glEnd ();
+
+ /* clean up */
+ glColor4usv (defaultColor);
+ glDisable (GL_BLEND);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ glPopMatrix ();
+}
+
+static Bool
+resizePaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ Bool status;
+
+ RESIZE_SCREEN (s);
+ RESIZE_DISPLAY (s->display);
+
+ if (rd->w && (s == rd->w->screen))
+ {
+ if (rd->mode == RESIZE_MODE_STRETCH)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+ }
+
+ UNWRAP (rs, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+ WRAP (rs, s, paintOutput, resizePaintOutput);
+
+ if (status && rd->w && (s == rd->w->screen))
+ {
+ unsigned short *border, *fill;
+
+ border = rd->opt[RESIZE_DISPLAY_OPTION_BORDER_COLOR].value.c;
+ fill = rd->opt[RESIZE_DISPLAY_OPTION_FILL_COLOR].value.c;
+
+ switch (rd->mode) {
+ case RESIZE_MODE_OUTLINE:
+ resizePaintRectangle (s, sAttrib, transform, output, border, NULL);
+ break;
+ case RESIZE_MODE_RECTANGLE:
+ resizePaintRectangle (s, sAttrib, transform, output, border, fill);
+ default:
+ break;
+ }
+ }
+
+ return status;
+}
+
+static Bool
+resizePaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ RESIZE_SCREEN (s);
+ RESIZE_DISPLAY (s->display);
+
+ if (w == rd->w && rd->mode == RESIZE_MODE_STRETCH)
+ {
+ FragmentAttrib fragment;
+ CompTransform wTransform = *transform;
+ BoxRec box;
+ float xOrigin, yOrigin;
+ float xScale, yScale;
+
+ if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)
+ return FALSE;
+
+ UNWRAP (rs, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region,
+ mask | PAINT_WINDOW_NO_CORE_INSTANCE_MASK);
+ WRAP (rs, s, paintWindow, resizePaintWindow);
+
+ initFragmentAttrib (&fragment, &w->lastPaint);
+
+ if (w->alpha || fragment.opacity != OPAQUE)
+ mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
+
+ resizeGetPaintRectangle (s->display, &box);
+ resizeGetStretchScale (w, &box, &xScale, &yScale);
+
+ xOrigin = w->attrib.x - w->input.left;
+ yOrigin = w->attrib.y - w->input.top;
+
+ matrixTranslate (&wTransform, xOrigin, yOrigin, 0.0f);
+ matrixScale (&wTransform, xScale, yScale, 1.0f);
+ matrixTranslate (&wTransform,
+ (rd->geometry.x - w->attrib.x) / xScale - xOrigin,
+ (rd->geometry.y - w->attrib.y) / yScale - yOrigin,
+ 0.0f);
+
+ glPushMatrix ();
+ glLoadMatrixf (wTransform.m);
+
+ (*s->drawWindow) (w, &wTransform, &fragment, region,
+ mask | PAINT_WINDOW_TRANSFORMED_MASK);
+
+ glPopMatrix ();
+ }
+ else
+ {
+ UNWRAP (rs, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (rs, s, paintWindow, resizePaintWindow);
+ }
+
+ return status;
+}
+
+static Bool
+resizeDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status = FALSE;
+
+ RESIZE_SCREEN (w->screen);
+ RESIZE_DISPLAY (w->screen->display);
+
+ if (w == rd->w && rd->mode == RESIZE_MODE_STRETCH)
+ {
+ BoxRec box;
+
+ resizeGetStretchRectangle (w->screen->display, &box);
+ resizeDamageRectangle (w->screen, &box);
+
+ status = TRUE;
+ }
+
+ UNWRAP (rs, w->screen, damageWindowRect);
+ status |= (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (rs, w->screen, damageWindowRect, resizeDamageWindowRect);
+
+ return status;
+}
+
+static CompOption *
+resizeGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ RESIZE_DISPLAY (display);
+
+ *count = NUM_OPTIONS (rd);
+ return rd->opt;
+}
+
+static Bool
+resizeSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ RESIZE_DISPLAY (display);
+
+ o = compFindOption (rd->opt, NUM_OPTIONS (rd), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (display, o, value);
+}
+
+static const CompMetadataOptionInfo resizeDisplayOptionInfo[] = {
+ { "initiate_normal_key", "key", 0, resizeInitiate, resizeTerminate },
+ { "initiate_outline_key", "key", 0, resizeInitiate, resizeTerminate },
+ { "initiate_rectangle_key", "key", 0, resizeInitiate, resizeTerminate },
+ { "initiate_stretch_key", "key", 0, resizeInitiate, resizeTerminate },
+ { "initiate_button", "button", 0, resizeInitiate, resizeTerminate },
+ { "initiate_key", "key", 0, resizeInitiate, resizeTerminate },
+ { "mode", "int", RESTOSTRING (0, RESIZE_MODE_LAST), 0, 0 },
+ { "border_color", "color", 0, 0, 0 },
+ { "fill_color", "color", 0, 0, 0 },
+ { "normal_match", "match", 0, 0, 0 },
+ { "outline_match", "match", 0, 0, 0 },
+ { "rectangle_match", "match", 0, 0, 0 },
+ { "stretch_match", "match", 0, 0, 0 }
+};
+
+static Bool
+resizeInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ResizeDisplay *rd;
+ int i;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ rd = malloc (sizeof (ResizeDisplay));
+ if (!rd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &resizeMetadata,
+ resizeDisplayOptionInfo,
+ rd->opt,
+ RESIZE_DISPLAY_OPTION_NUM))
+ {
+ free (rd);
+ return FALSE;
+ }
+
+ rd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (rd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, rd->opt, RESIZE_DISPLAY_OPTION_NUM);
+ free (rd);
+ return FALSE;
+ }
+
+ rd->w = 0;
+
+ rd->releaseButton = 0;
+
+ rd->resizeNotifyAtom = XInternAtom (d->display,
+ "_COMPIZ_RESIZE_NOTIFY", 0);
+ rd->resizeInformationAtom = XInternAtom (d->display,
+ "_COMPIZ_RESIZE_INFORMATION", 0);
+
+ for (i = 0; i < NUM_KEYS; i++)
+ rd->key[i] = XKeysymToKeycode (d->display,
+ XStringToKeysym (rKeys[i].name));
+
+ WRAP (rd, d, handleEvent, resizeHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = rd;
+
+ return TRUE;
+}
+
+static void
+resizeFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ RESIZE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, rd->screenPrivateIndex);
+
+ UNWRAP (rd, d, handleEvent);
+
+ compFiniDisplayOptions (d, rd->opt, RESIZE_DISPLAY_OPTION_NUM);
+
+ free (rd);
+}
+
+static Bool
+resizeInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ResizeScreen *rs;
+
+ RESIZE_DISPLAY (s->display);
+
+ rs = malloc (sizeof (ResizeScreen));
+ if (!rs)
+ return FALSE;
+
+ rs->grabIndex = 0;
+
+ rs->leftCursor = XCreateFontCursor (s->display->display, XC_left_side);
+ rs->rightCursor = XCreateFontCursor (s->display->display, XC_right_side);
+ rs->upCursor = XCreateFontCursor (s->display->display,
+ XC_top_side);
+ rs->upLeftCursor = XCreateFontCursor (s->display->display,
+ XC_top_left_corner);
+ rs->upRightCursor = XCreateFontCursor (s->display->display,
+ XC_top_right_corner);
+ rs->downCursor = XCreateFontCursor (s->display->display,
+ XC_bottom_side);
+ rs->downLeftCursor = XCreateFontCursor (s->display->display,
+ XC_bottom_left_corner);
+ rs->downRightCursor = XCreateFontCursor (s->display->display,
+ XC_bottom_right_corner);
+ rs->middleCursor = XCreateFontCursor (s->display->display, XC_fleur);
+
+ rs->cursor[0] = rs->leftCursor;
+ rs->cursor[1] = rs->rightCursor;
+ rs->cursor[2] = rs->upCursor;
+ rs->cursor[3] = rs->downCursor;
+
+ WRAP (rs, s, windowResizeNotify, resizeWindowResizeNotify);
+ WRAP (rs, s, paintOutput, resizePaintOutput);
+ WRAP (rs, s, paintWindow, resizePaintWindow);
+ WRAP (rs, s, damageWindowRect, resizeDamageWindowRect);
+
+ s->base.privates[rd->screenPrivateIndex].ptr = rs;
+
+ return TRUE;
+}
+
+static void
+resizeFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ RESIZE_SCREEN (s);
+
+ if (rs->leftCursor)
+ XFreeCursor (s->display->display, rs->leftCursor);
+ if (rs->rightCursor)
+ XFreeCursor (s->display->display, rs->rightCursor);
+ if (rs->upCursor)
+ XFreeCursor (s->display->display, rs->upCursor);
+ if (rs->downCursor)
+ XFreeCursor (s->display->display, rs->downCursor);
+ if (rs->middleCursor)
+ XFreeCursor (s->display->display, rs->middleCursor);
+ if (rs->upLeftCursor)
+ XFreeCursor (s->display->display, rs->upLeftCursor);
+ if (rs->upRightCursor)
+ XFreeCursor (s->display->display, rs->upRightCursor);
+ if (rs->downLeftCursor)
+ XFreeCursor (s->display->display, rs->downLeftCursor);
+ if (rs->downRightCursor)
+ XFreeCursor (s->display->display, rs->downRightCursor);
+
+ UNWRAP (rs, s, windowResizeNotify);
+ UNWRAP (rs, s, paintOutput);
+ UNWRAP (rs, s, paintWindow);
+ UNWRAP (rs, s, damageWindowRect);
+
+ free (rs);
+}
+
+static CompBool
+resizeInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) resizeInitDisplay,
+ (InitPluginObjectProc) resizeInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+resizeFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) resizeFiniDisplay,
+ (FiniPluginObjectProc) resizeFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+resizeGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) resizeGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+resizeSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) resizeSetDisplayOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+resizeInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&resizeMetadata,
+ p->vTable->name,
+ resizeDisplayOptionInfo,
+ RESIZE_DISPLAY_OPTION_NUM,
+ 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&resizeMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&resizeMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+resizeFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&resizeMetadata);
+}
+
+static CompMetadata *
+resizeGetMetadata (CompPlugin *plugin)
+{
+ return &resizeMetadata;
+}
+
+CompPluginVTable resizeVTable = {
+ "resize",
+ resizeGetMetadata,
+ resizeInit,
+ resizeFini,
+ resizeInitObject,
+ resizeFiniObject,
+ resizeGetObjectOptions,
+ resizeSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &resizeVTable;
+}
diff --git a/plugins/rotate.c b/plugins/rotate.c
new file mode 100644
index 0000000..8c59f9a
--- /dev/null
+++ b/plugins/rotate.c
@@ -0,0 +1,2018 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sys/time.h>
+
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+
+#include <compiz-cube.h>
+
+static int cubeDisplayPrivateIndex;
+
+#define ROTATE_POINTER_SENSITIVITY_FACTOR 0.05f
+
+static CompMetadata rotateMetadata;
+
+static int displayPrivateIndex;
+
+#define ROTATE_DISPLAY_OPTION_INITIATE_BUTTON 0
+#define ROTATE_DISPLAY_OPTION_LEFT_KEY 1
+#define ROTATE_DISPLAY_OPTION_LEFT_BUTTON 2
+#define ROTATE_DISPLAY_OPTION_RIGHT_KEY 3
+#define ROTATE_DISPLAY_OPTION_RIGHT_BUTTON 4
+#define ROTATE_DISPLAY_OPTION_LEFT_WINDOW_KEY 5
+#define ROTATE_DISPLAY_OPTION_LEFT_WINDOW_BUTTON 6
+#define ROTATE_DISPLAY_OPTION_RIGHT_WINDOW_KEY 7
+#define ROTATE_DISPLAY_OPTION_RIGHT_WINDOW_BUTTON 8
+#define ROTATE_DISPLAY_OPTION_EDGEFLIP_POINTER 9
+#define ROTATE_DISPLAY_OPTION_EDGEFLIP_WINDOW 10
+#define ROTATE_DISPLAY_OPTION_EDGEFLIP_DND 11
+#define ROTATE_DISPLAY_OPTION_FLIPTIME 12
+#define ROTATE_DISPLAY_OPTION_TO_1_KEY 13
+#define ROTATE_DISPLAY_OPTION_TO_2_KEY 14
+#define ROTATE_DISPLAY_OPTION_TO_3_KEY 15
+#define ROTATE_DISPLAY_OPTION_TO_4_KEY 16
+#define ROTATE_DISPLAY_OPTION_TO_5_KEY 17
+#define ROTATE_DISPLAY_OPTION_TO_6_KEY 18
+#define ROTATE_DISPLAY_OPTION_TO_7_KEY 19
+#define ROTATE_DISPLAY_OPTION_TO_8_KEY 20
+#define ROTATE_DISPLAY_OPTION_TO_9_KEY 21
+#define ROTATE_DISPLAY_OPTION_TO_10_KEY 22
+#define ROTATE_DISPLAY_OPTION_TO_11_KEY 23
+#define ROTATE_DISPLAY_OPTION_TO_12_KEY 24
+#define ROTATE_DISPLAY_OPTION_TO_1_WINDOW_KEY 25
+#define ROTATE_DISPLAY_OPTION_TO_2_WINDOW_KEY 26
+#define ROTATE_DISPLAY_OPTION_TO_3_WINDOW_KEY 27
+#define ROTATE_DISPLAY_OPTION_TO_4_WINDOW_KEY 28
+#define ROTATE_DISPLAY_OPTION_TO_5_WINDOW_KEY 29
+#define ROTATE_DISPLAY_OPTION_TO_6_WINDOW_KEY 30
+#define ROTATE_DISPLAY_OPTION_TO_7_WINDOW_KEY 31
+#define ROTATE_DISPLAY_OPTION_TO_8_WINDOW_KEY 32
+#define ROTATE_DISPLAY_OPTION_TO_9_WINDOW_KEY 33
+#define ROTATE_DISPLAY_OPTION_TO_10_WINDOW_KEY 34
+#define ROTATE_DISPLAY_OPTION_TO_11_WINDOW_KEY 35
+#define ROTATE_DISPLAY_OPTION_TO_12_WINDOW_KEY 36
+#define ROTATE_DISPLAY_OPTION_TO_KEY 37
+#define ROTATE_DISPLAY_OPTION_WINDOW_KEY 38
+#define ROTATE_DISPLAY_OPTION_FLIP_LEFT_EDGE 39
+#define ROTATE_DISPLAY_OPTION_FLIP_RIGHT_EDGE 40
+#define ROTATE_DISPLAY_OPTION_RAISE_ON_ROTATE 41
+#define ROTATE_DISPLAY_OPTION_NUM 42
+
+typedef struct _RotateDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompOption opt[ROTATE_DISPLAY_OPTION_NUM];
+} RotateDisplay;
+
+#define ROTATE_SCREEN_OPTION_POINTER_INVERT_Y 0
+#define ROTATE_SCREEN_OPTION_POINTER_SENSITIVITY 1
+#define ROTATE_SCREEN_OPTION_ACCELERATION 2
+#define ROTATE_SCREEN_OPTION_SNAP_TOP 3
+#define ROTATE_SCREEN_OPTION_SNAP_BOTTOM 4
+#define ROTATE_SCREEN_OPTION_SPEED 5
+#define ROTATE_SCREEN_OPTION_TIMESTEP 6
+#define ROTATE_SCREEN_OPTION_ZOOM 7
+#define ROTATE_SCREEN_OPTION_NUM 8
+
+typedef struct _RotateScreen {
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintOutputProc paintOutput;
+ WindowGrabNotifyProc windowGrabNotify;
+ WindowUngrabNotifyProc windowUngrabNotify;
+ ActivateWindowProc activateWindow;
+
+ CubeGetRotationProc getRotation;
+
+ CompOption opt[ROTATE_SCREEN_OPTION_NUM];
+
+ float pointerSensitivity;
+
+ Bool snapTop;
+ Bool snapBottom;
+
+ int grabIndex;
+
+ GLfloat xrot, xVelocity;
+ GLfloat yrot, yVelocity;
+
+ GLfloat baseXrot;
+
+ Bool moving;
+ GLfloat moveTo;
+
+ Window moveWindow;
+ int moveWindowX;
+
+ XPoint savedPointer;
+ Bool grabbed;
+ Bool focusDefault;
+
+ CompTimeoutHandle rotateHandle;
+ Bool slow;
+ unsigned int grabMask;
+ CompWindow *grabWindow;
+
+ float progress;
+ float progressVelocity;
+
+ GLfloat zoomTranslate;
+} RotateScreen;
+
+#define GET_ROTATE_DISPLAY(d) \
+ ((RotateDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define ROTATE_DISPLAY(d) \
+ RotateDisplay *rd = GET_ROTATE_DISPLAY (d)
+
+#define GET_ROTATE_SCREEN(s, rd) \
+ ((RotateScreen *) (s)->base.privates[(rd)->screenPrivateIndex].ptr)
+
+#define ROTATE_SCREEN(s) \
+ RotateScreen *rs = GET_ROTATE_SCREEN (s, GET_ROTATE_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static CompOption *
+rotateGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+ int *count)
+{
+ ROTATE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (rs);
+ return rs->opt;
+}
+
+static Bool
+rotateSetScreenOption (CompPlugin *plugin,
+ CompScreen *screen,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ ROTATE_SCREEN (screen);
+
+ o = compFindOption (rs->opt, NUM_OPTIONS (rs), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case ROTATE_SCREEN_OPTION_POINTER_SENSITIVITY:
+ if (compSetFloatOption (o, value))
+ {
+ rs->pointerSensitivity = o->value.f *
+ ROTATE_POINTER_SENSITIVITY_FACTOR;
+ return TRUE;
+ }
+ break;
+ default:
+ return compSetScreenOption (screen, o, value);
+ }
+
+ return FALSE;
+}
+
+static int
+adjustVelocity (RotateScreen *rs,
+ int size,
+ int invert)
+{
+ float xrot, yrot, adjust, amount;
+
+ if (rs->moving)
+ {
+ xrot = rs->moveTo + (rs->xrot + rs->baseXrot);
+ }
+ else
+ {
+ xrot = rs->xrot;
+ if (rs->xrot < -180.0f / size)
+ xrot = 360.0f / size + rs->xrot;
+ else if (rs->xrot > 180.0f / size)
+ xrot = rs->xrot - 360.0f / size;
+ }
+
+ adjust = -xrot * 0.05f * rs->opt[ROTATE_SCREEN_OPTION_ACCELERATION].value.f;
+ amount = fabs (xrot);
+ if (amount < 10.0f)
+ amount = 10.0f;
+ else if (amount > 30.0f)
+ amount = 30.0f;
+
+ if (rs->slow)
+ adjust *= 0.05f;
+
+ rs->xVelocity = (amount * rs->xVelocity + adjust) / (amount + 2.0f);
+
+ yrot = rs->yrot;
+ /* Only snap if more than 2 viewports */
+ if (size > 2)
+ {
+ if (rs->yrot > 50.0f && ((rs->snapTop && invert == 1) ||
+ (rs->snapBottom && invert != 1)))
+ yrot -= 90.f;
+ else if (rs->yrot < -50.0f && ((rs->snapTop && invert != 1) ||
+ (rs->snapBottom && invert == 1)))
+ yrot += 90.f;
+ }
+
+ adjust = -yrot * 0.05f * rs->opt[ROTATE_SCREEN_OPTION_ACCELERATION].value.f;
+ amount = fabs (rs->yrot);
+ if (amount < 10.0f)
+ amount = 10.0f;
+ else if (amount > 30.0f)
+ amount = 30.0f;
+
+ rs->yVelocity = (amount * rs->yVelocity + adjust) / (amount + 2.0f);
+
+ return (fabs (xrot) < 0.1f && fabs (rs->xVelocity) < 0.2f &&
+ fabs (yrot) < 0.1f && fabs (rs->yVelocity) < 0.2f);
+}
+
+static void
+rotateReleaseMoveWindow (CompScreen *s)
+{
+ CompWindow *w;
+
+ ROTATE_SCREEN (s);
+
+ w = findWindowAtScreen (s, rs->moveWindow);
+ if (w)
+ syncWindowPosition (w);
+
+ rs->moveWindow = None;
+}
+
+static void
+rotatePreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ ROTATE_SCREEN (s);
+ CUBE_SCREEN (s);
+
+ float oldXrot = rs->xrot + rs->baseXrot;
+
+ if (rs->grabIndex || rs->moving)
+ {
+ int steps;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f *
+ rs->opt[ROTATE_SCREEN_OPTION_SPEED].value.f;
+ steps = amount /
+ (0.5f * rs->opt[ROTATE_SCREEN_OPTION_TIMESTEP].value.f);
+ if (!steps) steps = 1;
+ chunk = amount / (float) steps;
+
+ while (steps--)
+ {
+ rs->xrot += rs->xVelocity * chunk;
+ rs->yrot += rs->yVelocity * chunk;
+
+ if (rs->xrot > 360.0f / s->hsize)
+ {
+ rs->baseXrot += 360.0f / s->hsize;
+ rs->xrot -= 360.0f / s->hsize;
+ }
+ else if (rs->xrot < 0.0f)
+ {
+ rs->baseXrot -= 360.0f / s->hsize;
+ rs->xrot += 360.0f / s->hsize;
+ }
+
+ if (cs->invert == -1)
+ {
+ if (rs->yrot > 45.0f)
+ {
+ rs->yVelocity = 0.0f;
+ rs->yrot = 45.0f;
+ }
+ else if (rs->yrot < -45.0f)
+ {
+ rs->yVelocity = 0.0f;
+ rs->yrot = -45.0f;
+ }
+ }
+ else
+ {
+ if (rs->yrot > 100.0f)
+ {
+ rs->yVelocity = 0.0f;
+ rs->yrot = 100.0f;
+ }
+ else if (rs->yrot < -100.0f)
+ {
+ rs->yVelocity = 0.0f;
+ rs->yrot = -100.0f;
+ }
+ }
+
+ if (rs->grabbed)
+ {
+ rs->xVelocity /= 1.25f;
+ rs->yVelocity /= 1.25f;
+
+ if (fabs (rs->xVelocity) < 0.01f)
+ rs->xVelocity = 0.0f;
+ if (fabs (rs->yVelocity) < 0.01f)
+ rs->yVelocity = 0.0f;
+ }
+ else if (adjustVelocity (rs, s->hsize, cs->invert))
+ {
+ rs->xVelocity = 0.0f;
+ rs->yVelocity = 0.0f;
+
+ if (fabs (rs->yrot) < 0.1f)
+ {
+ float xrot;
+ int tx;
+
+ xrot = rs->baseXrot + rs->xrot;
+ if (xrot < 0.0f)
+ tx = (s->hsize * xrot / 360.0f) - 0.5f;
+ else
+ tx = (s->hsize * xrot / 360.0f) + 0.5f;
+
+ /* flag end of rotation */
+ cs->rotationState = RotationNone;
+
+ moveScreenViewport (s, tx, 0, TRUE);
+
+ rs->xrot = 0.0f;
+ rs->yrot = 0.0f;
+ rs->baseXrot = rs->moveTo = 0.0f;
+ rs->moving = FALSE;
+
+ if (rs->grabIndex)
+ {
+ removeScreenGrab (s, rs->grabIndex, &rs->savedPointer);
+ rs->grabIndex = 0;
+ }
+
+ if (rs->moveWindow)
+ {
+ CompWindow *w;
+
+ w = findWindowAtScreen (s, rs->moveWindow);
+ if (w)
+ {
+ moveWindow (w, rs->moveWindowX - w->attrib.x, 0,
+ TRUE, TRUE);
+ syncWindowPosition (w);
+ }
+ }
+ else if (rs->focusDefault)
+ {
+ int i;
+
+ for (i = 0; i < s->maxGrab; i++)
+ if (s->grabs[i].active &&
+ strcmp ("switcher", s->grabs[i].name) == 0)
+ break;
+
+ /* only focus default window if switcher isn't active */
+ if (i == s->maxGrab)
+ focusDefaultWindow (s);
+ }
+
+ rs->moveWindow = 0;
+ }
+ break;
+ }
+ }
+
+ if (rs->moveWindow)
+ {
+ CompWindow *w;
+
+ w = findWindowAtScreen (s, rs->moveWindow);
+ if (w)
+ {
+ float xrot = (s->hsize * (rs->baseXrot + rs->xrot)) / 360.0f;
+
+ moveWindowToViewportPosition (w,
+ rs->moveWindowX - xrot * s->width,
+ w->attrib.y,
+ FALSE);
+ }
+ }
+ }
+
+ if (rs->moving)
+ {
+ if (fabs (rs->xrot + rs->baseXrot + rs->moveTo) <=
+ (360.0 / (s->hsize * 2.0)))
+ {
+ rs->progress = fabs (rs->xrot + rs->baseXrot + rs->moveTo) /
+ (360.0 / (s->hsize * 2.0));
+ }
+ else if (fabs (rs->xrot + rs->baseXrot) <= (360.0 / (s->hsize * 2.0)))
+ {
+ rs->progress = fabs (rs->xrot + rs->baseXrot) /
+ (360.0 / (s->hsize * 2.0));
+ }
+ else
+ {
+ rs->progress += fabs (rs->xrot + rs->baseXrot - oldXrot) /
+ (360.0 / (s->hsize * 2.0));
+ rs->progress = MIN (rs->progress, 1.0);
+ }
+ }
+ else if (rs->progress != 0.0f || rs->grabbed)
+ {
+ int steps;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f *
+ rs->opt[ROTATE_SCREEN_OPTION_SPEED].value.f;
+ steps = amount /
+ (0.5f * rs->opt[ROTATE_SCREEN_OPTION_TIMESTEP].value.f);
+ if (!steps)
+ steps = 1;
+
+ chunk = amount / (float) steps;
+
+ while (steps--)
+ {
+ float dt, adjust, tamount;
+
+ if (rs->grabbed)
+ dt = 1.0 - rs->progress;
+ else
+ dt = 0.0f - rs->progress;
+
+ adjust = dt * 0.15f;
+ tamount = fabs (dt) * 1.5f;
+ if (tamount < 0.2f)
+ tamount = 0.2f;
+ else if (tamount > 2.0f)
+ tamount = 2.0f;
+
+ rs->progressVelocity = (tamount * rs->progressVelocity + adjust) /
+ (tamount + 1.0f);
+
+ rs->progress += rs->progressVelocity * chunk;
+
+ if (fabs (dt) < 0.01f && fabs (rs->progressVelocity) < 0.0001f)
+ {
+ if (rs->grabbed)
+ rs->progress = 1.0f;
+ else
+ rs->progress = 0.0f;
+
+ break;
+ }
+ }
+ }
+
+ if (cs->invert == 1 && !cs->unfolded)
+ {
+ rs->zoomTranslate = rs->opt[ROTATE_SCREEN_OPTION_ZOOM].value.f *
+ rs->progress;
+ }
+ else
+ {
+ rs->zoomTranslate = 0.0;
+ }
+
+ UNWRAP (rs, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (rs, s, preparePaintScreen, rotatePreparePaintScreen);
+}
+
+static void
+rotateDonePaintScreen (CompScreen *s)
+{
+ ROTATE_SCREEN (s);
+
+ if (rs->grabIndex || rs->moving ||
+ (rs->progress != 0.0 && rs->progress != 1.0))
+ {
+ if ((!rs->grabbed && !rs->snapTop && !rs->snapBottom) ||
+ rs->xVelocity || rs->yVelocity || rs->progressVelocity)
+ {
+ damageScreen (s);
+ }
+ }
+
+ UNWRAP (rs, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (rs, s, donePaintScreen, rotateDonePaintScreen);
+}
+
+static void
+rotateGetRotation (CompScreen *s,
+ float *x,
+ float *v,
+ float *progress)
+{
+ CUBE_SCREEN (s);
+ ROTATE_SCREEN (s);
+
+ UNWRAP (rs, cs, getRotation);
+ (*cs->getRotation) (s, x, v, progress);
+ WRAP (rs, cs, getRotation, rotateGetRotation);
+
+ *x += rs->baseXrot + rs->xrot;
+ *v += rs->yrot;
+ *progress = MAX (*progress, rs->progress);
+}
+
+static Bool
+rotatePaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ Bool status;
+
+ ROTATE_SCREEN (s);
+
+ if (rs->grabIndex || rs->moving || rs->progress != 0.0f)
+ {
+ CompTransform sTransform = *transform;
+
+ matrixTranslate (&sTransform, 0.0f, 0.0f, -rs->zoomTranslate);
+
+ mask &= ~PAINT_SCREEN_REGION_MASK;
+ mask |= PAINT_SCREEN_TRANSFORMED_MASK;
+
+ UNWRAP (rs, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, &sTransform, region,
+ output, mask);
+ WRAP (rs, s, paintOutput, rotatePaintOutput);
+ }
+ else
+ {
+ UNWRAP (rs, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region,
+ output, mask);
+ WRAP (rs, s, paintOutput, rotatePaintOutput);
+ }
+
+ return status;
+}
+
+static Bool
+rotateInitiate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ ROTATE_SCREEN (s);
+ CUBE_SCREEN (s);
+
+ if (s->hsize < 2)
+ return FALSE;
+
+ if (rs->rotateHandle && rs->grabWindow)
+ {
+ if (otherScreenGrabExist (s, "rotate", "move", 0))
+ return FALSE;
+ }
+ else
+ {
+ if (otherScreenGrabExist (s, "rotate", "switcher", "cube", 0))
+ return FALSE;
+ }
+
+ rs->moving = FALSE;
+ rs->slow = FALSE;
+
+ /* Set the rotation state for cube - if action is non-NULL,
+ we set it to manual (as we were called from the 'Initiate
+ Rotation' binding. Otherwise, we set it to Change. */
+ if (action)
+ cs->rotationState = RotationManual;
+ else
+ cs->rotationState = RotationChange;
+
+ if (!rs->grabIndex)
+ {
+ rs->grabIndex = pushScreenGrab (s, s->invisibleCursor, "rotate");
+ if (rs->grabIndex)
+ {
+ int x, y;
+
+ x = getIntOptionNamed (option, nOption, "x", 0);
+ y = getIntOptionNamed (option, nOption, "y", 0);
+
+ rs->savedPointer.x = x;
+ rs->savedPointer.y = y;
+ }
+ }
+
+ if (rs->grabIndex)
+ {
+ rs->moveTo = 0.0f;
+
+ rs->grabbed = TRUE;
+ rs->snapTop = rs->opt[ROTATE_SCREEN_OPTION_SNAP_TOP].value.b;
+ rs->snapBottom = rs->opt[ROTATE_SCREEN_OPTION_SNAP_BOTTOM].value.b;
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ if (state & CompActionStateInitKey)
+ action->state |= CompActionStateTermKey;
+ }
+ }
+
+ return TRUE;
+}
+
+static Bool
+rotateTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ ROTATE_SCREEN (s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (rs->grabIndex)
+ {
+ if (!xid)
+ {
+ rs->snapTop = FALSE;
+ rs->snapBottom = FALSE;
+ }
+
+ rs->grabbed = FALSE;
+ damageScreen (s);
+ }
+ }
+
+ action->state &= ~(CompActionStateTermButton | CompActionStateTermKey);
+
+ return FALSE;
+}
+
+static Bool
+rotate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ int direction;
+
+ ROTATE_SCREEN (s);
+
+ if (s->hsize < 2)
+ return FALSE;
+
+ if (otherScreenGrabExist (s, "rotate", "move", "switcher",
+ "group-drag", "cube", 0))
+ return FALSE;
+
+ direction = getIntOptionNamed (option, nOption, "direction", 0);
+ if (!direction)
+ return FALSE;
+
+ if (rs->moveWindow)
+ rotateReleaseMoveWindow (s);
+
+ /* we allow the grab to fail here so that we can rotate on
+ drag-and-drop */
+ if (!rs->grabIndex)
+ {
+ CompOption o[3];
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = s->root;
+
+ rotateInitiate (d, NULL, 0, o, 3);
+ }
+
+ rs->focusDefault = getBoolOptionNamed (option, nOption,
+ "focus_default", TRUE);
+ rs->moving = TRUE;
+ rs->moveTo += (360.0f / s->hsize) * direction;
+ rs->grabbed = FALSE;
+
+ damageScreen (s);
+ }
+
+ return FALSE;
+}
+
+static Bool
+rotateWithWindow (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ ROTATE_DISPLAY (d);
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ Bool raise = rd->opt[ROTATE_DISPLAY_OPTION_RAISE_ON_ROTATE].value.b;
+ int direction;
+
+ ROTATE_SCREEN (s);
+
+ if (s->hsize < 2)
+ return FALSE;
+
+ direction = getIntOptionNamed (option, nOption, "direction", 0);
+ if (!direction)
+ return FALSE;
+
+ xid = getIntOptionNamed (option, nOption, "window", 0);
+
+ if (rs->moveWindow != xid)
+ {
+ CompWindow *w;
+
+ rotateReleaseMoveWindow (s);
+
+ if (!rs->grabIndex && !rs->moving)
+ {
+ w = findWindowAtScreen (s, xid);
+ if (w)
+ {
+ if (!(w->type & (CompWindowTypeDesktopMask |
+ CompWindowTypeDockMask)))
+ {
+ if (!(w->state & CompWindowStateStickyMask))
+ {
+ rs->moveWindow = w->id;
+ rs->moveWindowX = w->attrib.x;
+
+ if (raise)
+ raiseWindow (w);
+ }
+ }
+ }
+ }
+ }
+
+ if (!rs->grabIndex)
+ {
+ CompOption o[3];
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = s->root;
+
+ rotateInitiate (d, NULL, 0, o, 3);
+ }
+
+ if (rs->grabIndex)
+ {
+ rs->moving = TRUE;
+ rs->moveTo += (360.0f / s->hsize) * direction;
+ rs->grabbed = FALSE;
+
+ damageScreen (s);
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+rotateLeft (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompOption o[4];
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = getIntOptionNamed (option, nOption, "root", 0);
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+ o[3].value.i = -1;
+
+ rotate (d, NULL, 0, o, 4);
+
+ return FALSE;
+}
+
+static Bool
+rotateRight (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompOption o[4];
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = getIntOptionNamed (option, nOption, "root", 0);
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+ o[3].value.i = 1;
+
+ rotate (d, NULL, 0, o, 4);
+
+ return FALSE;
+}
+
+static Bool
+rotateLeftWithWindow (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompOption o[5];
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = getIntOptionNamed (option, nOption, "root", 0);
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+ o[3].value.i = -1;
+
+ o[4].type = CompOptionTypeInt;
+ o[4].name = "window";
+ o[4].value.i = getIntOptionNamed (option, nOption, "window", 0);
+
+ rotateWithWindow (d, NULL, 0, o, 5);
+
+ return FALSE;
+}
+
+static Bool
+rotateRightWithWindow (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompOption o[5];
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = getIntOptionNamed (option, nOption, "root", 0);
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+ o[3].value.i = 1;
+
+ o[4].type = CompOptionTypeInt;
+ o[4].name = "window";
+ o[4].value.i = getIntOptionNamed (option, nOption, "window", 0);
+
+ rotateWithWindow (d, NULL, 0, o, 5);
+
+ return FALSE;
+}
+
+static Bool
+rotateFlipLeft (void *closure)
+{
+ CompScreen *s = closure;
+ int warpX;
+ CompOption o[4];
+
+ ROTATE_SCREEN (s);
+
+ rs->moveTo = 0.0f;
+ rs->slow = FALSE;
+
+ if (otherScreenGrabExist (s, "rotate", "move", "group-drag", 0))
+ return FALSE;
+
+ warpX = pointerX + s->width;
+ warpPointer (s, s->width - 10, 0);
+ lastPointerX = warpX;
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = 0;
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = pointerY;
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = s->root;
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+ o[3].value.i = -1;
+
+ rotate (s->display, NULL, 0, o, 4);
+
+ XWarpPointer (s->display->display, None, None, 0, 0, 0, 0, -1, 0);
+ rs->savedPointer.x = lastPointerX - 9;
+
+ rs->rotateHandle = 0;
+
+ return FALSE;
+}
+
+static Bool
+rotateFlipRight (void *closure)
+{
+ CompScreen *s = closure;
+ int warpX;
+ CompOption o[4];
+
+ ROTATE_SCREEN (s);
+
+ rs->moveTo = 0.0f;
+ rs->slow = FALSE;
+
+ if (otherScreenGrabExist (s, "rotate", "move", "group-drag", 0))
+ return FALSE;
+
+ warpX = pointerX - s->width;
+ warpPointer (s, 10 - s->width, 0);
+ lastPointerX = warpX;
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = 0;
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = pointerY;
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = s->root;
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+ o[3].value.i = 1;
+
+ rotate (s->display, NULL, 0, o, 4);
+
+ XWarpPointer (s->display->display, None, None, 0, 0, 0, 0, 1, 0);
+
+ rs->savedPointer.x = lastPointerX + 9;
+
+ rs->rotateHandle = 0;
+
+ return FALSE;
+}
+
+static void
+rotateEdgeFlip (CompScreen *s,
+ int edge,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompOption o[4];
+
+ ROTATE_DISPLAY (s->display);
+
+ if (s->hsize < 2)
+ return;
+
+ if (otherScreenGrabExist (s, "rotate", "move", "group-drag", 0))
+ return;
+
+ if (state & CompActionStateInitEdgeDnd)
+ {
+ if (!rd->opt[ROTATE_DISPLAY_OPTION_EDGEFLIP_DND].value.b)
+ return;
+
+ if (otherScreenGrabExist (s, "rotate", 0))
+ return;
+ }
+ else if (otherScreenGrabExist (s, "rotate", "group-drag", 0))
+ {
+ ROTATE_SCREEN (s);
+
+ if (!rd->opt[ROTATE_DISPLAY_OPTION_EDGEFLIP_WINDOW].value.b)
+ return;
+
+ if (!rs->grabWindow)
+ return;
+
+ /* bail out if window is horizontally maximized or fullscreen */
+ if (rs->grabWindow->state & (CompWindowStateMaximizedHorzMask |
+ CompWindowStateFullscreenMask))
+ return;
+ }
+ else if (otherScreenGrabExist (s, "rotate", 0))
+ {
+ /* in that case, 'group-drag' must be the active screen grab */
+ if (!rd->opt[ROTATE_DISPLAY_OPTION_EDGEFLIP_WINDOW].value.b)
+ return;
+ }
+ else
+ {
+ if (!rd->opt[ROTATE_DISPLAY_OPTION_EDGEFLIP_POINTER].value.b)
+ return;
+ }
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = 0;
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = pointerY;
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = s->root;
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+
+ if (edge == SCREEN_EDGE_LEFT)
+ {
+ int flipTime = rd->opt[ROTATE_DISPLAY_OPTION_FLIPTIME].value.i;
+
+ ROTATE_SCREEN (s);
+
+ if (flipTime == 0 || (rs->moving && !rs->slow))
+ {
+ int pointerDx = pointerX - lastPointerX;
+ int warpX;
+
+ warpX = pointerX + s->width;
+ warpPointer (s, s->width - 10, 0);
+ lastPointerX = warpX - pointerDx;
+
+ o[3].value.i = -1;
+
+ rotate (s->display, NULL, 0, o, 4);
+
+ XWarpPointer (s->display->display, None, None,
+ 0, 0, 0, 0, -1, 0);
+ rs->savedPointer.x = lastPointerX - 9;
+ }
+ else
+ {
+ if (!rs->rotateHandle)
+ {
+ int flipTime = rd->opt[ROTATE_DISPLAY_OPTION_FLIPTIME].value.i;
+
+ rs->rotateHandle = compAddTimeout (flipTime,
+ (float) flipTime * 1.2,
+ rotateFlipLeft, s);
+ }
+
+ rs->moving = TRUE;
+ rs->moveTo -= 360.0f / s->hsize;
+ rs->slow = TRUE;
+
+ if (state & CompActionStateInitEdge)
+ action->state |= CompActionStateTermEdge;
+
+ if (state & CompActionStateInitEdgeDnd)
+ action->state |= CompActionStateTermEdgeDnd;
+
+ damageScreen (s);
+ }
+ }
+ else
+ {
+ int flipTime = rd->opt[ROTATE_DISPLAY_OPTION_FLIPTIME].value.i;
+
+ ROTATE_SCREEN (s);
+
+ if (flipTime == 0 || (rs->moving && !rs->slow))
+ {
+ int pointerDx = pointerX - lastPointerX;
+ int warpX;
+
+ warpX = pointerX - s->width;
+ warpPointer (s, 10 - s->width, 0);
+ lastPointerX = warpX - pointerDx;
+
+ o[3].value.i = 1;
+
+ rotate (s->display, NULL, 0, o, 4);
+
+ XWarpPointer (s->display->display, None, None,
+ 0, 0, 0, 0, 1, 0);
+ rs->savedPointer.x = lastPointerX + 9;
+ }
+ else
+ {
+ if (!rs->rotateHandle)
+ {
+ int flipTime = rd->opt[ROTATE_DISPLAY_OPTION_FLIPTIME].value.i;
+
+ rs->rotateHandle =
+ compAddTimeout (flipTime, (float) flipTime * 1.2,
+ rotateFlipRight, s);
+ }
+
+ rs->moving = TRUE;
+ rs->moveTo += 360.0f / s->hsize;
+ rs->slow = TRUE;
+
+ if (state & CompActionStateInitEdge)
+ action->state |= CompActionStateTermEdge;
+
+ if (state & CompActionStateInitEdgeDnd)
+ action->state |= CompActionStateTermEdgeDnd;
+
+ damageScreen (s);
+ }
+ }
+}
+
+static Bool
+rotateFlipTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ ROTATE_SCREEN (s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (rs->rotateHandle)
+ {
+ compRemoveTimeout (rs->rotateHandle);
+ rs->rotateHandle = 0;
+
+ if (rs->slow)
+ {
+ rs->moveTo = 0.0f;
+ rs->slow = FALSE;
+ }
+
+ damageScreen (s);
+ }
+
+ action->state &= ~(CompActionStateTermEdge |
+ CompActionStateTermEdgeDnd);
+ }
+
+ return FALSE;
+}
+
+static Bool
+rotateEdgeFlipLeft (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ rotateEdgeFlip (s, SCREEN_EDGE_LEFT, action, state, option, nOption);
+
+ return FALSE;
+}
+
+static Bool
+rotateEdgeFlipRight (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ rotateEdgeFlip (s, SCREEN_EDGE_RIGHT, action, state, option, nOption);
+
+ return FALSE;
+}
+
+static int
+rotateRotationTo (CompScreen *s,
+ int face)
+{
+ int delta;
+
+ ROTATE_SCREEN (s);
+
+ delta = face - s->x - (rs->moveTo / (360.0f / s->hsize));
+ if (delta > s->hsize / 2)
+ delta -= s->hsize;
+ else if (delta < -(s->hsize / 2))
+ delta += s->hsize;
+
+ return delta;
+}
+
+static Bool
+rotateTo (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ CompOption o[4];
+ int face = -1;
+ int i = ROTATE_DISPLAY_OPTION_TO_1_KEY;
+
+ ROTATE_DISPLAY (s->display);
+
+ while (i <= ROTATE_DISPLAY_OPTION_TO_12_KEY)
+ {
+ if (action == &rd->opt[i].value.action)
+ {
+ face = i - ROTATE_DISPLAY_OPTION_TO_1_KEY;
+ break;
+ }
+
+ i++;
+ }
+
+ if (face < 0)
+ face = getIntOptionNamed (option, nOption, "face", s->x);
+
+ if (face > s->hsize)
+ return FALSE;
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = getIntOptionNamed (option, nOption, "x", pointerX);
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = getIntOptionNamed (option, nOption, "y", pointerY);
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = s->root;
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+ o[3].value.i = rotateRotationTo (s, face);
+
+ rotate (d, NULL, 0, o, 4);
+ }
+
+ return FALSE;
+}
+
+static Bool
+rotateToWithWindow (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ CompOption o[5];
+ int face = -1;
+ int i = ROTATE_DISPLAY_OPTION_TO_1_WINDOW_KEY;
+
+ ROTATE_DISPLAY (s->display);
+
+ while (i <= ROTATE_DISPLAY_OPTION_TO_12_WINDOW_KEY)
+ {
+ if (action == &rd->opt[i].value.action)
+ {
+ face = i - ROTATE_DISPLAY_OPTION_TO_1_WINDOW_KEY;
+ break;
+ }
+
+ i++;
+ }
+
+ if (face < 0)
+ face = getIntOptionNamed (option, nOption, "face", s->x);
+
+ if (face > s->hsize)
+ return FALSE;
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = getIntOptionNamed (option, nOption, "x", pointerX);
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = getIntOptionNamed (option, nOption, "y", pointerY);
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = s->root;
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+ o[3].value.i = rotateRotationTo (s, face);
+
+ o[4].type = CompOptionTypeInt;
+ o[4].name = "window";
+ o[4].value.i = getIntOptionNamed (option, nOption, "window", 0);
+
+ rotateWithWindow (d, NULL, 0, o, 5);
+ }
+
+ return FALSE;
+}
+
+static void
+rotateHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ ROTATE_DISPLAY (d);
+
+ switch (event->type) {
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ {
+ ROTATE_SCREEN (s);
+ CUBE_SCREEN (s);
+
+ if (rs->grabIndex)
+ {
+ if (rs->grabbed)
+ {
+ GLfloat pointerDx, pointerDy;
+
+ pointerDx = pointerX - lastPointerX;
+ pointerDy = pointerY - lastPointerY;
+
+ if (event->xmotion.x_root < 50 ||
+ event->xmotion.y_root < 50 ||
+ event->xmotion.x_root > s->width - 50 ||
+ event->xmotion.y_root > s->height - 50)
+ {
+ warpPointer (s,
+ (s->width / 2) - pointerX,
+ (s->height / 2) - pointerY);
+ }
+
+ if (rs->opt[ROTATE_SCREEN_OPTION_POINTER_INVERT_Y].value.b)
+ pointerDy = -pointerDy;
+
+ rs->xVelocity += pointerDx * rs->pointerSensitivity *
+ cs->invert;
+ rs->yVelocity += pointerDy * rs->pointerSensitivity;
+
+ damageScreen (s);
+ }
+ else
+ {
+ rs->savedPointer.x += pointerX - lastPointerX;
+ rs->savedPointer.y += pointerY - lastPointerY;
+ }
+ }
+ }
+ break;
+ case ClientMessage:
+ if (event->xclient.message_type == d->desktopViewportAtom)
+ {
+ s = findScreenAtDisplay (d, event->xclient.window);
+ if (s)
+ {
+ int dx;
+
+ ROTATE_SCREEN (s);
+
+ if (otherScreenGrabExist (s, "rotate", "switcher", "cube", 0))
+ break;
+
+ /* reset movement */
+ rs->moveTo = 0.0f;
+
+ dx = event->xclient.data.l[0] / s->width - s->x;
+ if (dx)
+ {
+ Window win;
+ int i, x, y;
+ unsigned int ui;
+ CompOption o[4];
+
+ XQueryPointer (d->display, s->root,
+ &win, &win, &x, &y, &i, &i, &ui);
+
+ if (dx * 2 > s->hsize)
+ dx -= s->hsize;
+ else if (dx * 2 < -s->hsize)
+ dx += s->hsize;
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = x;
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = y;
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = s->root;
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+ o[3].value.i = dx;
+
+ rotate (d, NULL, 0, o, 4);
+ }
+ }
+ }
+ default:
+ break;
+ }
+
+ UNWRAP (rd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (rd, d, handleEvent, rotateHandleEvent);
+}
+
+static void
+rotateActivateWindow (CompWindow *w)
+{
+ CompScreen *s = w->screen;
+
+ ROTATE_SCREEN (s);
+
+ if (w->placed &&
+ !otherScreenGrabExist (s, "rotate", "switcher", "cube", 0))
+ {
+ int dx;
+
+ /* reset movement */
+ rs->moveTo = 0.0f;
+
+ defaultViewportForWindow (w, &dx, NULL);
+ dx -= s->x;
+ if (dx)
+ {
+ Window win;
+ int i, x, y;
+ unsigned int ui;
+ CompOption o[5];
+
+ XQueryPointer (s->display->display, s->root,
+ &win, &win, &x, &y, &i, &i, &ui);
+
+ if (dx * 2 > s->hsize)
+ dx -= s->hsize;
+ else if (dx * 2 < -s->hsize)
+ dx += s->hsize;
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "x";
+ o[0].value.i = x;
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "y";
+ o[1].value.i = y;
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "root";
+ o[2].value.i = s->root;
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "direction";
+ o[3].value.i = dx;
+
+ o[4].type = CompOptionTypeBool;
+ o[4].name = "focus_default";
+ o[4].value.b = FALSE;
+
+ rotate (s->display, NULL, 0, o, 5);
+ }
+ }
+
+ UNWRAP (rs, s, activateWindow);
+ (*s->activateWindow) (w);
+ WRAP (rs, s, activateWindow, rotateActivateWindow);
+}
+
+static void
+rotateWindowGrabNotify (CompWindow *w,
+ int x,
+ int y,
+ unsigned int state,
+ unsigned int mask)
+{
+ ROTATE_SCREEN (w->screen);
+
+ if (!rs->grabWindow)
+ {
+ rs->grabMask = mask;
+ rs->grabWindow = w;
+ }
+
+ UNWRAP (rs, w->screen, windowGrabNotify);
+ (*w->screen->windowGrabNotify) (w, x, y, state, mask);
+ WRAP (rs, w->screen, windowGrabNotify, rotateWindowGrabNotify);
+}
+
+static void
+rotateWindowUngrabNotify (CompWindow *w)
+{
+ ROTATE_SCREEN (w->screen);
+
+ if (w == rs->grabWindow)
+ {
+ rs->grabMask = 0;
+ rs->grabWindow = NULL;
+ }
+
+ UNWRAP (rs, w->screen, windowUngrabNotify);
+ (*w->screen->windowUngrabNotify) (w);
+ WRAP (rs, w->screen, windowUngrabNotify, rotateWindowUngrabNotify);
+}
+
+static CompOption *
+rotateGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ ROTATE_DISPLAY (display);
+
+ *count = NUM_OPTIONS (rd);
+ return rd->opt;
+}
+
+static Bool
+rotateSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ ROTATE_DISPLAY (display);
+
+ o = compFindOption (rd->opt, NUM_OPTIONS (rd), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (display, o, value);
+}
+
+static const CompMetadataOptionInfo rotateDisplayOptionInfo[] = {
+ { "initiate_button", "button", 0, rotateInitiate, rotateTerminate },
+ { "rotate_left_key", "key", 0, rotateLeft, 0 },
+ { "rotate_left_button", "button", 0, rotateLeft, 0 },
+ { "rotate_right_key", "key", 0, rotateRight, 0 },
+ { "rotate_right_button", "button", 0, rotateRight, 0 },
+ { "rotate_left_window_key", "key", 0, rotateLeftWithWindow, 0 },
+ { "rotate_left_window_button", "button", 0, rotateLeftWithWindow, 0 },
+ { "rotate_right_window_key", "key", 0, rotateRightWithWindow, 0 },
+ { "rotate_right_window_button", "button", 0, rotateRightWithWindow, 0 },
+ { "edge_flip_pointer", "bool", 0, 0, 0 },
+ { "edge_flip_window", "bool", 0, 0, 0 },
+ { "edge_flip_dnd", "bool", 0, 0, 0 },
+ { "flip_time", "int", "<min>0</min><max>1000</max>", 0, 0 },
+ { "rotate_to_1_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_2_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_3_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_4_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_5_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_6_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_7_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_8_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_9_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_10_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_11_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_12_key", "key", 0, rotateTo, 0 },
+ { "rotate_to_1_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_2_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_3_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_4_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_5_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_6_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_7_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_8_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_9_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_10_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_11_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_12_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_to_key", "key", 0, rotateTo, 0 },
+ { "rotate_window_key", "key", 0, rotateToWithWindow, 0 },
+ { "rotate_flip_left_edge", "edge", 0, rotateEdgeFlipLeft,
+ rotateFlipTerminate },
+ { "rotate_flip_right_edge", "edge", 0, rotateEdgeFlipRight,
+ rotateFlipTerminate },
+ { "raise_on_rotate", "bool", 0, 0, 0 }
+};
+
+static Bool
+rotateInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ RotateDisplay *rd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION) ||
+ !checkPluginABI ("cube", CUBE_ABIVERSION))
+ return FALSE;
+
+ if (!getPluginDisplayIndex (d, "cube", &cubeDisplayPrivateIndex))
+ return FALSE;
+
+ rd = malloc (sizeof (RotateDisplay));
+ if (!rd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &rotateMetadata,
+ rotateDisplayOptionInfo,
+ rd->opt,
+ ROTATE_DISPLAY_OPTION_NUM))
+ {
+ free (rd);
+ return FALSE;
+ }
+
+ rd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (rd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, rd->opt, ROTATE_DISPLAY_OPTION_NUM);
+ free (rd);
+ return FALSE;
+ }
+
+ WRAP (rd, d, handleEvent, rotateHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = rd;
+
+ return TRUE;
+}
+
+static void
+rotateFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ROTATE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, rd->screenPrivateIndex);
+
+ UNWRAP (rd, d, handleEvent);
+
+ compFiniDisplayOptions (d, rd->opt, ROTATE_DISPLAY_OPTION_NUM);
+
+ free (rd);
+}
+
+static const CompMetadataOptionInfo rotateScreenOptionInfo[] = {
+ { "invert_y", "bool", 0, 0, 0 },
+ { "sensitivity", "float", 0, 0, 0 },
+ { "acceleration", "float", "<min>1.0</min>", 0, 0 },
+ { "snap_top", "bool", 0, 0, 0 },
+ { "snap_bottom", "bool", 0, 0, 0 },
+ { "speed", "float", "<min>0.1</min>", 0, 0 },
+ { "timestep", "float", "<min>0.1</min>", 0, 0 },
+ { "zoom", "float", 0, 0, 0 }
+};
+
+static Bool
+rotateInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ RotateScreen *rs;
+
+ ROTATE_DISPLAY (s->display);
+ CUBE_SCREEN (s);
+
+ rs = malloc (sizeof (RotateScreen));
+ if (!rs)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &rotateMetadata,
+ rotateScreenOptionInfo,
+ rs->opt,
+ ROTATE_SCREEN_OPTION_NUM))
+ {
+ free (rs);
+ return FALSE;
+ }
+
+ rs->grabIndex = 0;
+
+ rs->xrot = 0.0f;
+ rs->xVelocity = 0.0f;
+ rs->yrot = 0.0f;
+ rs->yVelocity = 0.0f;
+
+ rs->baseXrot = 0.0f;
+
+ rs->moving = FALSE;
+ rs->moveTo = 0.0f;
+
+ rs->moveWindow = 0;
+
+ rs->savedPointer.x = 0;
+ rs->savedPointer.y = 0;
+
+ rs->focusDefault = TRUE;
+ rs->grabbed = FALSE;
+ rs->snapTop = FALSE;
+ rs->snapBottom = FALSE;
+
+ rs->slow = FALSE;
+ rs->grabMask = FALSE;
+ rs->grabWindow = NULL;
+
+ rs->pointerSensitivity =
+ rs->opt[ROTATE_SCREEN_OPTION_POINTER_SENSITIVITY].value.f *
+ ROTATE_POINTER_SENSITIVITY_FACTOR;
+
+ rs->rotateHandle = 0;
+
+ rs->progress = 0.0;
+ rs->progressVelocity = 0.0;
+
+ rs->zoomTranslate = 0.0;
+
+ WRAP (rs, s, preparePaintScreen, rotatePreparePaintScreen);
+ WRAP (rs, s, donePaintScreen, rotateDonePaintScreen);
+ WRAP (rs, s, paintOutput, rotatePaintOutput);
+ WRAP (rs, s, windowGrabNotify, rotateWindowGrabNotify);
+ WRAP (rs, s, windowUngrabNotify, rotateWindowUngrabNotify);
+ WRAP (rs, s, activateWindow, rotateActivateWindow);
+
+ WRAP (rs, cs, getRotation, rotateGetRotation);
+
+ s->base.privates[rd->screenPrivateIndex].ptr = rs;
+
+ return TRUE;
+}
+
+static void
+rotateFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ CUBE_SCREEN (s);
+ ROTATE_SCREEN (s);
+
+ UNWRAP (rs, cs, getRotation);
+
+ UNWRAP (rs, s, preparePaintScreen);
+ UNWRAP (rs, s, donePaintScreen);
+ UNWRAP (rs, s, paintOutput);
+ UNWRAP (rs, s, windowGrabNotify);
+ UNWRAP (rs, s, windowUngrabNotify);
+ UNWRAP (rs, s, activateWindow);
+
+ compFiniScreenOptions (s, rs->opt, ROTATE_SCREEN_OPTION_NUM);
+
+ free (rs);
+}
+static CompBool
+rotateInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) rotateInitDisplay,
+ (InitPluginObjectProc) rotateInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+rotateFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) rotateFiniDisplay,
+ (FiniPluginObjectProc) rotateFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+rotateGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) rotateGetDisplayOptions,
+ (GetPluginObjectOptionsProc) rotateGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+rotateSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) rotateSetDisplayOption,
+ (SetPluginObjectOptionProc) rotateSetScreenOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+rotateInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&rotateMetadata,
+ p->vTable->name,
+ rotateDisplayOptionInfo,
+ ROTATE_DISPLAY_OPTION_NUM,
+ rotateScreenOptionInfo,
+ ROTATE_SCREEN_OPTION_NUM))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&rotateMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&rotateMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+rotateFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&rotateMetadata);
+}
+
+static CompMetadata *
+rotateGetMetadata (CompPlugin *plugin)
+{
+ return &rotateMetadata;
+}
+
+CompPluginVTable rotateVTable = {
+ "rotate",
+ rotateGetMetadata,
+ rotateInit,
+ rotateFini,
+ rotateInitObject,
+ rotateFiniObject,
+ rotateGetObjectOptions,
+ rotateSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &rotateVTable;
+}
diff --git a/plugins/scale.c b/plugins/scale.c
new file mode 100644
index 0000000..1063c9e
--- /dev/null
+++ b/plugins/scale.c
@@ -0,0 +1,2290 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sys/time.h>
+
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+
+#include <compiz-scale.h>
+
+#define EDGE_STATE (CompActionStateInitEdge)
+
+#define WIN_X(w) ((w)->attrib.x - (w)->input.left)
+#define WIN_Y(w) ((w)->attrib.y - (w)->input.top)
+#define WIN_W(w) ((w)->width + (w)->input.left + (w)->input.right)
+#define WIN_H(w) ((w)->height + (w)->input.top + (w)->input.bottom)
+
+static CompMetadata scaleMetadata;
+
+static int scaleDisplayPrivateIndex;
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static CompOption *
+scaleGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+ int *count)
+{
+ SCALE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ss);
+ return ss->opt;
+}
+
+static Bool
+scaleSetScreenOption (CompPlugin *plugin,
+ CompScreen *screen,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ SCALE_SCREEN (screen);
+
+ o = compFindOption (ss->opt, NUM_OPTIONS (ss), name, &index);
+
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case SCALE_SCREEN_OPTION_OPACITY:
+ if (compSetIntOption (o, value))
+ {
+ ss->opacity = (OPAQUE * o->value.i) / 100;
+ return TRUE;
+ }
+ break;
+ default:
+ return compSetScreenOption (screen, o, value);
+ }
+
+ return FALSE;
+}
+
+static Bool
+isNeverScaleWin (CompWindow *w)
+{
+ if (w->attrib.override_redirect)
+ return TRUE;
+
+ if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
+ return TRUE;
+
+ return FALSE;
+}
+
+static Bool
+isScaleWin (CompWindow *w)
+{
+ SCALE_SCREEN (w->screen);
+
+ if (isNeverScaleWin (w))
+ return FALSE;
+
+ if (!ss->type || ss->type == ScaleTypeOutput)
+ {
+ if (!(*w->screen->focusWindow) (w))
+ return FALSE;
+ }
+
+ if (w->state & CompWindowStateSkipPagerMask)
+ return FALSE;
+
+ if (w->state & CompWindowStateShadedMask)
+ return FALSE;
+
+ if (!w->mapNum || w->attrib.map_state != IsViewable)
+ return FALSE;
+
+ switch (ss->type) {
+ case ScaleTypeGroup:
+ if (ss->clientLeader != w->clientLeader &&
+ ss->clientLeader != w->id)
+ return FALSE;
+ break;
+ case ScaleTypeOutput:
+ if (outputDeviceForWindow(w) != w->screen->currentOutputDev)
+ return FALSE;
+ default:
+ break;
+ }
+
+ if (!matchEval (ss->currentMatch, w))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+scaleActivateEvent (CompScreen *s,
+ Bool activating)
+{
+ CompOption o[2];
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "root";
+ o[0].value.i = s->root;
+
+ o[1].type = CompOptionTypeBool;
+ o[1].name = "active";
+ o[1].value.b = activating;
+
+ (*s->display->handleCompizEvent) (s->display, "scale", "activate", o, 2);
+}
+
+static void
+scalePaintDecoration (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+
+ SCALE_SCREEN (s);
+
+ if (ss->opt[SCALE_SCREEN_OPTION_ICON].value.i != SCALE_ICON_NONE)
+ {
+ WindowPaintAttrib sAttrib = *attrib;
+ CompIcon *icon;
+
+ SCALE_WINDOW (w);
+
+ icon = getWindowIcon (w, 96, 96);
+ if (!icon)
+ icon = w->screen->defaultIcon;
+
+ if (icon && (icon->texture.name || iconToTexture (w->screen, icon)))
+ {
+ REGION iconReg;
+ float scale;
+ float x, y;
+ int width, height;
+ int scaledWinWidth, scaledWinHeight;
+ float ds;
+
+ scaledWinWidth = w->width * sw->scale;
+ scaledWinHeight = w->height * sw->scale;
+
+ switch (ss->opt[SCALE_SCREEN_OPTION_ICON].value.i) {
+ case SCALE_ICON_NONE:
+ case SCALE_ICON_EMBLEM:
+ scale = 1.0f;
+ break;
+ case SCALE_ICON_BIG:
+ default:
+ sAttrib.opacity /= 3;
+ scale = MIN (((float) scaledWinWidth / icon->width),
+ ((float) scaledWinHeight / icon->height));
+ break;
+ }
+
+ width = icon->width * scale;
+ height = icon->height * scale;
+
+ switch (ss->opt[SCALE_SCREEN_OPTION_ICON].value.i) {
+ case SCALE_ICON_NONE:
+ case SCALE_ICON_EMBLEM:
+ x = w->attrib.x + scaledWinWidth - icon->width;
+ y = w->attrib.y + scaledWinHeight - icon->height;
+ break;
+ case SCALE_ICON_BIG:
+ default:
+ x = w->attrib.x + scaledWinWidth / 2 - width / 2;
+ y = w->attrib.y + scaledWinHeight / 2 - height / 2;
+ break;
+ }
+
+ x += sw->tx;
+ y += sw->ty;
+
+ if (sw->slot)
+ {
+ sw->delta =
+ fabs (sw->slot->x1 - w->attrib.x) +
+ fabs (sw->slot->y1 - w->attrib.y) +
+ fabs (1.0f - sw->slot->scale) * 500.0f;
+ }
+
+ if (sw->delta)
+ {
+ float o;
+
+ ds =
+ fabs (sw->tx) +
+ fabs (sw->ty) +
+ fabs (1.0f - sw->scale) * 500.0f;
+
+ if (ds > sw->delta)
+ ds = sw->delta;
+
+ o = ds / sw->delta;
+
+ if (sw->slot)
+ {
+ if (o < sw->lastThumbOpacity)
+ o = sw->lastThumbOpacity;
+ }
+ else
+ {
+ if (o > sw->lastThumbOpacity)
+ o = 0.0f;
+ }
+
+ sw->lastThumbOpacity = o;
+
+ sAttrib.opacity = sAttrib.opacity * o;
+ }
+
+ mask |= PAINT_WINDOW_BLEND_MASK;
+
+ iconReg.rects = &iconReg.extents;
+ iconReg.numRects = 1;
+
+ iconReg.extents.x1 = 0;
+ iconReg.extents.y1 = 0;
+ iconReg.extents.x2 = iconReg.extents.x1 + width;
+ iconReg.extents.y2 = iconReg.extents.y1 + height;
+
+ w->vCount = w->indexCount = 0;
+ if (iconReg.extents.x1 < iconReg.extents.x2 &&
+ iconReg.extents.y1 < iconReg.extents.y2)
+ (*w->screen->addWindowGeometry) (w,
+ &icon->texture.matrix, 1,
+ &iconReg, &iconReg);
+
+ if (w->vCount)
+ {
+ FragmentAttrib fragment;
+ CompTransform wTransform = *transform;
+
+ initFragmentAttrib (&fragment, &sAttrib);
+
+ matrixScale (&wTransform, scale, scale, 1.0f);
+ matrixTranslate (&wTransform, x / scale, y / scale, 0.0f);
+
+ glPushMatrix ();
+ glLoadMatrixf (wTransform.m);
+
+ (*w->screen->drawWindowTexture) (w,
+ &icon->texture, &fragment,
+ mask);
+
+ glPopMatrix ();
+ }
+ }
+ }
+}
+
+static Bool
+setScaledPaintAttributes (CompWindow *w,
+ WindowPaintAttrib *attrib)
+{
+ Bool drawScaled = FALSE;
+
+ SCALE_SCREEN (w->screen);
+ SCALE_WINDOW (w);
+
+ if (sw->adjust || sw->slot)
+ {
+ SCALE_DISPLAY (w->screen->display);
+
+ if (w->id != sd->selectedWindow &&
+ ss->opacity != OPAQUE &&
+ ss->state != SCALE_STATE_IN)
+ {
+ /* modify opacity of windows that are not active */
+ attrib->opacity = (attrib->opacity * ss->opacity) >> 16;
+ }
+
+ drawScaled = TRUE;
+ }
+ else if (ss->state != SCALE_STATE_IN)
+ {
+ if (ss->opt[SCALE_SCREEN_OPTION_DARKEN_BACK].value.b)
+ {
+ /* modify brightness of the other windows */
+ attrib->brightness = attrib->brightness / 2;
+ }
+
+ /* hide windows on the outputs used for scaling
+ that are not in scale mode */
+ if (!isNeverScaleWin (w))
+ {
+ int moMode;
+ moMode = ss->opt[SCALE_SCREEN_OPTION_MULTIOUTPUT_MODE].value.i;
+
+ switch (moMode) {
+ case SCALE_MOMODE_CURRENT:
+ if (outputDeviceForWindow (w) == w->screen->currentOutputDev)
+ attrib->opacity = 0;
+ break;
+ default:
+ attrib->opacity = 0;
+ break;
+ }
+ }
+ }
+
+ return drawScaled;
+}
+
+static Bool
+scalePaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ SCALE_SCREEN (s);
+
+ if (ss->state != SCALE_STATE_NONE)
+ {
+ WindowPaintAttrib sAttrib = *attrib;
+ Bool scaled;
+
+ SCALE_WINDOW (w);
+
+ scaled = (*ss->setScaledPaintAttributes) (w, &sAttrib);
+
+ if (sw->adjust || sw->slot)
+ mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
+
+ UNWRAP (ss, s, paintWindow);
+ status = (*s->paintWindow) (w, &sAttrib, transform, region, mask);
+ WRAP (ss, s, paintWindow, scalePaintWindow);
+
+ if (scaled)
+ {
+ FragmentAttrib fragment;
+ CompTransform wTransform = *transform;
+
+ if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)
+ return FALSE;
+
+ initFragmentAttrib (&fragment, &w->lastPaint);
+
+ if (w->alpha || fragment.opacity != OPAQUE)
+ mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
+
+ matrixTranslate (&wTransform, w->attrib.x, w->attrib.y, 0.0f);
+ matrixScale (&wTransform, sw->scale, sw->scale, 1.0f);
+ matrixTranslate (&wTransform,
+ sw->tx / sw->scale - w->attrib.x,
+ sw->ty / sw->scale - w->attrib.y,
+ 0.0f);
+
+ glPushMatrix ();
+ glLoadMatrixf (wTransform.m);
+
+ (*s->drawWindow) (w, &wTransform, &fragment, region,
+ mask | PAINT_WINDOW_TRANSFORMED_MASK);
+
+ glPopMatrix ();
+
+ (*ss->scalePaintDecoration) (w, &sAttrib, transform, region, mask);
+ }
+ }
+ else
+ {
+ UNWRAP (ss, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (ss, s, paintWindow, scalePaintWindow);
+ }
+
+ return status;
+}
+
+static int
+compareWindowsDistance (const void *elem1,
+ const void *elem2)
+{
+ CompWindow *w1 = *((CompWindow **) elem1);
+ CompWindow *w2 = *((CompWindow **) elem2);
+
+ SCALE_SCREEN (w1->screen);
+
+ return
+ GET_SCALE_WINDOW (w1, ss)->distance -
+ GET_SCALE_WINDOW (w2, ss)->distance;
+}
+
+static void
+layoutSlotsForArea (CompScreen *s,
+ XRectangle workArea,
+ int nWindows)
+{
+ int i, j;
+ int x, y, width, height;
+ int lines, n, nSlots;
+ int spacing;
+
+ SCALE_SCREEN (s);
+
+ if (!nWindows)
+ return;
+
+ lines = sqrt (nWindows + 1);
+ spacing = ss->opt[SCALE_SCREEN_OPTION_SPACING].value.i;
+ nSlots = 0;
+
+ y = workArea.y + spacing;
+ height = (workArea.height - (lines + 1) * spacing) / lines;
+
+ for (i = 0; i < lines; i++)
+ {
+ n = MIN (nWindows - nSlots,
+ ceilf ((float)nWindows / lines));
+
+ x = workArea.x + spacing;
+ width = (workArea.width - (n + 1) * spacing) / n;
+
+ for (j = 0; j < n; j++)
+ {
+ ss->slots[ss->nSlots].x1 = x;
+ ss->slots[ss->nSlots].y1 = y;
+ ss->slots[ss->nSlots].x2 = x + width;
+ ss->slots[ss->nSlots].y2 = y + height;
+
+ ss->slots[ss->nSlots].filled = FALSE;
+
+ x += width + spacing;
+
+ ss->nSlots++;
+ nSlots++;
+ }
+
+ y += height + spacing;
+ }
+}
+
+static SlotArea *
+getSlotAreas (CompScreen *s)
+{
+ int i;
+ XRectangle workArea;
+ float *size;
+ float sizePerWindow, sum = 0.0f;
+ int left;
+ SlotArea *slotAreas;
+
+ SCALE_SCREEN (s);
+
+ size = malloc (s->nOutputDev * sizeof (int));
+ if (!size)
+ return NULL;
+
+ slotAreas = malloc (s->nOutputDev * sizeof (SlotArea));
+ if (!slotAreas)
+ {
+ free (size);
+ return NULL;
+ }
+
+ left = ss->nWindows;
+
+ for (i = 0; i < s->nOutputDev; i++)
+ {
+ /* determine the size of the workarea for each output device */
+ workArea = s->outputDev[i].workArea;
+
+ size[i] = workArea.width * workArea.height;
+ sum += size[i];
+
+ slotAreas[i].nWindows = 0;
+ slotAreas[i].workArea = workArea;
+ }
+
+ /* calculate size available for each window */
+ sizePerWindow = sum / ss->nWindows;
+
+ for (i = 0; i < s->nOutputDev && left; i++)
+ {
+ /* fill the areas with windows */
+ int nw = floor (size[i] / sizePerWindow);
+
+ nw = MIN (nw, left);
+ size[i] -= nw * sizePerWindow;
+ slotAreas[i].nWindows = nw;
+ left -= nw;
+ }
+
+ /* add left windows to output devices with the biggest free space */
+ while (left > 0)
+ {
+ int num = 0;
+ float big = 0;
+
+ for (i = 0; i < s->nOutputDev; i++)
+ {
+ if (size[i] > big)
+ {
+ num = i;
+ big = size[i];
+ }
+ }
+
+ size[num] -= sizePerWindow;
+ slotAreas[num].nWindows++;
+ left--;
+ }
+
+ free (size);
+
+ return slotAreas;
+}
+
+static void
+layoutSlots (CompScreen *s)
+{
+ int i;
+ int moMode;
+
+ SCALE_SCREEN (s);
+
+ moMode = ss->opt[SCALE_SCREEN_OPTION_MULTIOUTPUT_MODE].value.i;
+
+ /* if we have only one head, we don't need the
+ additional effort of the all outputs mode */
+ if (s->nOutputDev == 1)
+ moMode = SCALE_MOMODE_CURRENT;
+
+ ss->nSlots = 0;
+
+ switch (moMode)
+ {
+ case SCALE_MOMODE_ALL:
+ {
+ SlotArea *slotAreas;
+ slotAreas = getSlotAreas (s);
+ if (slotAreas)
+ {
+ for (i = 0; i < s->nOutputDev; i++)
+ layoutSlotsForArea (s,
+ slotAreas[i].workArea,
+ slotAreas[i].nWindows);
+ free (slotAreas);
+ }
+ }
+ break;
+ case SCALE_MOMODE_CURRENT:
+ default:
+ {
+ XRectangle workArea;
+ workArea = s->outputDev[s->currentOutputDev].workArea;
+ layoutSlotsForArea (s, workArea, ss->nWindows);
+ }
+ break;
+ }
+}
+
+static void
+findBestSlots (CompScreen *s)
+{
+ CompWindow *w;
+ int i, j, d, d0 = 0;
+ float sx, sy, cx, cy;
+
+ SCALE_SCREEN (s);
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ w = ss->windows[i];
+
+ SCALE_WINDOW (w);
+
+ if (sw->slot)
+ continue;
+
+ sw->sid = 0;
+ sw->distance = MAXSHORT;
+
+ for (j = 0; j < ss->nSlots; j++)
+ {
+ if (!ss->slots[j].filled)
+ {
+ sx = (ss->slots[j].x2 + ss->slots[j].x1) / 2;
+ sy = (ss->slots[j].y2 + ss->slots[j].y1) / 2;
+
+ cx = w->serverX + w->width / 2;
+ cy = w->serverY + w->height / 2;
+
+ cx -= sx;
+ cy -= sy;
+
+ d = sqrt (cx * cx + cy * cy);
+ if (d0 + d < sw->distance)
+ {
+ sw->sid = j;
+ sw->distance = d0 + d;
+ }
+ }
+ }
+
+ d0 += sw->distance;
+ }
+}
+
+static Bool
+fillInWindows (CompScreen *s)
+{
+ CompWindow *w;
+ int i, width, height;
+ float sx, sy, cx, cy;
+
+ SCALE_SCREEN (s);
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ w = ss->windows[i];
+
+ SCALE_WINDOW (w);
+
+ if (!sw->slot)
+ {
+ if (ss->slots[sw->sid].filled)
+ return TRUE;
+
+ sw->slot = &ss->slots[sw->sid];
+
+ width = w->width + w->input.left + w->input.right;
+ height = w->height + w->input.top + w->input.bottom;
+
+ sx = (float) (sw->slot->x2 - sw->slot->x1) / width;
+ sy = (float) (sw->slot->y2 - sw->slot->y1) / height;
+
+ sw->slot->scale = MIN (MIN (sx, sy), 1.0f);
+
+ sx = width * sw->slot->scale;
+ sy = height * sw->slot->scale;
+ cx = (sw->slot->x1 + sw->slot->x2) / 2;
+ cy = (sw->slot->y1 + sw->slot->y2) / 2;
+
+ cx += w->input.left * sw->slot->scale;
+ cy += w->input.top * sw->slot->scale;
+
+ sw->slot->x1 = cx - sx / 2;
+ sw->slot->y1 = cy - sy / 2;
+ sw->slot->x2 = cx + sx / 2;
+ sw->slot->y2 = cy + sy / 2;
+
+ sw->slot->filled = TRUE;
+
+ sw->lastThumbOpacity = 0.0f;
+
+ sw->adjust = TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+layoutSlotsAndAssignWindows (CompScreen *s)
+{
+ SCALE_SCREEN (s);
+
+ /* create a grid of slots */
+ layoutSlots (s);
+
+ do
+ {
+ /* find most appropriate slots for windows */
+ findBestSlots (s);
+
+ /* sort windows, window with closest distance to a slot first */
+ qsort (ss->windows, ss->nWindows, sizeof (CompWindow *),
+ compareWindowsDistance);
+
+ } while (fillInWindows (s));
+
+ return TRUE;
+}
+
+static Bool
+layoutThumbs (CompScreen *s)
+{
+ CompWindow *w;
+
+ SCALE_SCREEN (s);
+
+ ss->nWindows = 0;
+
+ /* add windows scale list, top most window first */
+ for (w = s->reverseWindows; w; w = w->prev)
+ {
+ SCALE_WINDOW (w);
+
+ if (sw->slot)
+ sw->adjust = TRUE;
+
+ sw->slot = 0;
+
+ if (!isScaleWin (w))
+ continue;
+
+ if (ss->windowsSize <= ss->nWindows)
+ {
+ ss->windows = realloc (ss->windows,
+ sizeof (CompWindow *) * (ss->nWindows + 32));
+ if (!ss->windows)
+ return FALSE;
+
+ ss->windowsSize = ss->nWindows + 32;
+ }
+
+ ss->windows[ss->nWindows++] = w;
+ }
+
+ if (ss->nWindows == 0)
+ return FALSE;
+
+ if (ss->slotsSize < ss->nWindows)
+ {
+ ss->slots = realloc (ss->slots, sizeof (ScaleSlot) * ss->nWindows);
+ if (!ss->slots)
+ return FALSE;
+
+ ss->slotsSize = ss->nWindows;
+ }
+
+ return (*ss->layoutSlotsAndAssignWindows) (s);
+}
+
+static int
+adjustScaleVelocity (CompWindow *w)
+{
+ float dx, dy, ds, adjust, amount;
+ float x1, y1, scale;
+
+ SCALE_WINDOW (w);
+
+ if (sw->slot)
+ {
+ x1 = sw->slot->x1;
+ y1 = sw->slot->y1;
+ scale = sw->slot->scale;
+ }
+ else
+ {
+ x1 = w->attrib.x;
+ y1 = w->attrib.y;
+ scale = 1.0f;
+ }
+
+ dx = x1 - (w->attrib.x + sw->tx);
+
+ adjust = dx * 0.15f;
+ amount = fabs (dx) * 1.5f;
+ if (amount < 0.5f)
+ amount = 0.5f;
+ else if (amount > 5.0f)
+ amount = 5.0f;
+
+ sw->xVelocity = (amount * sw->xVelocity + adjust) / (amount + 1.0f);
+
+ dy = y1 - (w->attrib.y + sw->ty);
+
+ adjust = dy * 0.15f;
+ amount = fabs (dy) * 1.5f;
+ if (amount < 0.5f)
+ amount = 0.5f;
+ else if (amount > 5.0f)
+ amount = 5.0f;
+
+ sw->yVelocity = (amount * sw->yVelocity + adjust) / (amount + 1.0f);
+
+ ds = scale - sw->scale;
+
+ adjust = ds * 0.1f;
+ amount = fabs (ds) * 7.0f;
+ if (amount < 0.01f)
+ amount = 0.01f;
+ else if (amount > 0.15f)
+ amount = 0.15f;
+
+ sw->scaleVelocity = (amount * sw->scaleVelocity + adjust) /
+ (amount + 1.0f);
+
+ if (fabs (dx) < 0.1f && fabs (sw->xVelocity) < 0.2f &&
+ fabs (dy) < 0.1f && fabs (sw->yVelocity) < 0.2f &&
+ fabs (ds) < 0.001f && fabs (sw->scaleVelocity) < 0.002f)
+ {
+ sw->xVelocity = sw->yVelocity = sw->scaleVelocity = 0.0f;
+ sw->tx = x1 - w->attrib.x;
+ sw->ty = y1 - w->attrib.y;
+ sw->scale = scale;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static Bool
+scalePaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ Bool status;
+
+ SCALE_SCREEN (s);
+
+ if (ss->state != SCALE_STATE_NONE)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+
+ UNWRAP (ss, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+ WRAP (ss, s, paintOutput, scalePaintOutput);
+
+ return status;
+}
+
+static void
+scalePreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ SCALE_SCREEN (s);
+
+ if (ss->state != SCALE_STATE_NONE && ss->state != SCALE_STATE_WAIT)
+ {
+ CompWindow *w;
+ int steps;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f *
+ ss->opt[SCALE_SCREEN_OPTION_SPEED].value.f;
+ steps = amount /
+ (0.5f * ss->opt[SCALE_SCREEN_OPTION_TIMESTEP].value.f);
+ if (!steps) steps = 1;
+ chunk = amount / (float) steps;
+
+ while (steps--)
+ {
+ ss->moreAdjust = 0;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ SCALE_WINDOW (w);
+
+ if (sw->adjust)
+ {
+ sw->adjust = adjustScaleVelocity (w);
+
+ ss->moreAdjust |= sw->adjust;
+
+ sw->tx += sw->xVelocity * chunk;
+ sw->ty += sw->yVelocity * chunk;
+ sw->scale += sw->scaleVelocity * chunk;
+ }
+ }
+
+ if (!ss->moreAdjust)
+ break;
+ }
+ }
+
+ UNWRAP (ss, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (ss, s, preparePaintScreen, scalePreparePaintScreen);
+}
+
+static void
+scaleDonePaintScreen (CompScreen *s)
+{
+ SCALE_SCREEN (s);
+
+ if (ss->state != SCALE_STATE_NONE)
+ {
+ if (ss->moreAdjust)
+ {
+ damageScreen (s);
+ }
+ else
+ {
+ if (ss->state == SCALE_STATE_IN)
+ {
+ /* The FALSE activate event is sent when scale state
+ goes back to normal, to avoid animation conflicts
+ with other plugins. */
+ scaleActivateEvent (s, FALSE);
+ ss->state = SCALE_STATE_NONE;
+ }
+ else if (ss->state == SCALE_STATE_OUT)
+ ss->state = SCALE_STATE_WAIT;
+ }
+ }
+
+ UNWRAP (ss, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (ss, s, donePaintScreen, scaleDonePaintScreen);
+}
+
+static CompWindow *
+scaleCheckForWindowAt (CompScreen *s,
+ int x,
+ int y)
+{
+ int x1, y1, x2, y2;
+ CompWindow *w;
+
+ for (w = s->reverseWindows; w; w = w->prev)
+ {
+ SCALE_WINDOW (w);
+
+ if (sw->slot)
+ {
+ x1 = w->attrib.x - w->input.left * sw->scale;
+ y1 = w->attrib.y - w->input.top * sw->scale;
+ x2 = w->attrib.x + (w->width + w->input.right) * sw->scale;
+ y2 = w->attrib.y + (w->height + w->input.bottom) * sw->scale;
+
+ x1 += sw->tx;
+ y1 += sw->ty;
+ x2 += sw->tx;
+ y2 += sw->ty;
+
+ if (x1 <= x && y1 <= y && x2 > x && y2 > y)
+ return w;
+ }
+ }
+
+ return 0;
+}
+
+static void
+sendDndStatusMessage (CompScreen *s,
+ Window source)
+{
+ XEvent xev;
+
+ SCALE_SCREEN (s);
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.display = s->display->display;
+ xev.xclient.format = 32;
+
+ xev.xclient.message_type = s->display->xdndStatusAtom;
+ xev.xclient.window = source;
+
+ xev.xclient.data.l[0] = ss->dndTarget;
+ xev.xclient.data.l[1] = 2;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = None;
+
+ XSendEvent (s->display->display, source, FALSE, 0, &xev);
+}
+
+static Bool
+scaleActionShouldToggle (CompDisplay *d,
+ CompAction *action,
+ CompActionState state)
+{
+ SCALE_DISPLAY (d);
+
+ if (state & EDGE_STATE)
+ return TRUE;
+
+ if (state & (CompActionStateInitKey | CompActionStateTermKey))
+ {
+ if (sd->opt[SCALE_DISPLAY_OPTION_KEY_BINDINGS_TOGGLE].value.b)
+ return TRUE;
+ else if (!action->key.modifiers)
+ return TRUE;
+ }
+
+ if (state & (CompActionStateInitButton | CompActionStateTermButton))
+ if (sd->opt[SCALE_DISPLAY_OPTION_BUTTON_BINDINGS_TOGGLE].value.b)
+ return TRUE;
+
+ return FALSE;
+}
+
+static Bool
+scaleTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ SCALE_DISPLAY (d);
+
+ if (!scaleActionShouldToggle (d, action, state))
+ {
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ SCALE_SCREEN (s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (!ss->grab)
+ continue;
+
+ if (ss->grabIndex)
+ {
+ removeScreenGrab (s, ss->grabIndex, 0);
+ ss->grabIndex = 0;
+ }
+
+ if (ss->dndTarget)
+ XUnmapWindow (d->display, ss->dndTarget);
+
+ ss->grab = FALSE;
+
+ if (ss->state != SCALE_STATE_NONE)
+ {
+ CompWindow *w;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ SCALE_WINDOW (w);
+
+ if (sw->slot)
+ {
+ sw->slot = 0;
+ sw->adjust = TRUE;
+ }
+ }
+
+ if (state & CompActionStateCancel)
+ {
+ if (d->activeWindow != sd->previousActiveWindow)
+ {
+ w = findWindowAtScreen (s, sd->previousActiveWindow);
+ if (w)
+ moveInputFocusToWindow (w);
+ }
+ }
+ else if (ss->state != SCALE_STATE_IN)
+ {
+ w = findWindowAtScreen (s, sd->selectedWindow);
+ if (w)
+ (*s->activateWindow) (w);
+ }
+
+ ss->state = SCALE_STATE_IN;
+
+ damageScreen (s);
+ }
+
+ sd->lastActiveNum = 0;
+ }
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+static Bool
+scaleEnsureDndRedirectWindow (CompScreen *s)
+{
+ SCALE_SCREEN (s);
+
+ if (!ss->dndTarget)
+ {
+ XSetWindowAttributes attr;
+ long xdndVersion = 3;
+
+ attr.override_redirect = TRUE;
+
+ ss->dndTarget = XCreateWindow (s->display->display,
+ s->root,
+ 0, 0, 1, 1, 0,
+ CopyFromParent,
+ InputOnly,
+ CopyFromParent,
+ CWOverrideRedirect, &attr);
+
+ XChangeProperty (s->display->display, ss->dndTarget,
+ s->display->xdndAwareAtom,
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) &xdndVersion, 1);
+ }
+
+ XMoveResizeWindow (s->display->display, ss->dndTarget,
+ 0, 0, s->width, s->height);
+ XMapRaised (s->display->display, ss->dndTarget);
+
+ return TRUE;
+}
+
+static Bool
+scaleInitiateCommon (CompScreen *s,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompMatch *match;
+
+ SCALE_DISPLAY (s->display);
+ SCALE_SCREEN (s);
+
+ if (otherScreenGrabExist (s, "scale", 0))
+ return FALSE;
+
+ ss->currentMatch = &ss->opt[SCALE_SCREEN_OPTION_WINDOW_MATCH].value.match;
+
+ match = getMatchOptionNamed (option, nOption, "match", NULL);
+ if (match)
+ {
+ matchFini (&ss->match);
+ matchInit (&ss->match);
+ if (matchCopy (&ss->match, match))
+ {
+ matchUpdate (s->display, &ss->match);
+ ss->currentMatch = &ss->match;
+ }
+ }
+
+ if (!layoutThumbs (s))
+ return FALSE;
+
+ if (state & CompActionStateInitEdgeDnd)
+ {
+ if (scaleEnsureDndRedirectWindow (s))
+ ss->grab = TRUE;
+ }
+ else if (!ss->grabIndex)
+ {
+ ss->grabIndex = pushScreenGrab (s, ss->cursor, "scale");
+ if (ss->grabIndex)
+ ss->grab = TRUE;
+ }
+
+ if (ss->grab)
+ {
+ if (!sd->lastActiveNum)
+ sd->lastActiveNum = s->activeNum - 1;
+
+ sd->previousActiveWindow = s->display->activeWindow;
+ sd->lastActiveWindow = s->display->activeWindow;
+ sd->selectedWindow = s->display->activeWindow;
+ sd->hoveredWindow = None;
+
+ ss->state = SCALE_STATE_OUT;
+
+ scaleActivateEvent (s, TRUE);
+
+ damageScreen (s);
+ }
+
+ if ((state & (CompActionStateInitButton | EDGE_STATE)) ==
+ CompActionStateInitButton)
+ {
+ action->state |= CompActionStateTermButton;
+ }
+
+ if (state & CompActionStateInitKey)
+ action->state |= CompActionStateTermKey;
+
+ return FALSE;
+}
+
+static Bool
+scaleInitiate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ SCALE_SCREEN (s);
+
+ if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT)
+ {
+ ss->type = ScaleTypeNormal;
+ return scaleInitiateCommon (s, action, state, option, nOption);
+ }
+ else if (scaleActionShouldToggle (d, action, state))
+ {
+ if (ss->type == ScaleTypeNormal)
+ return scaleTerminate (s->display, action,
+ CompActionStateCancel, option, nOption);
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+scaleInitiateAll (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ SCALE_SCREEN (s);
+
+ if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT)
+ {
+ ss->type = ScaleTypeAll;
+ return scaleInitiateCommon (s, action, state, option, nOption);
+ }
+ else if (scaleActionShouldToggle (d, action, state))
+ {
+ if (ss->type == ScaleTypeAll)
+ return scaleTerminate (s->display, action,
+ CompActionStateCancel, option, nOption);
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+scaleInitiateGroup (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ SCALE_SCREEN (s);
+
+ if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT)
+ {
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, getIntOptionNamed (option, nOption,
+ "window", 0));
+ if (w)
+ {
+ ss->type = ScaleTypeGroup;
+ ss->clientLeader = (w->clientLeader) ? w->clientLeader : w->id;
+
+ return scaleInitiateCommon (s, action, state, option, nOption);
+ }
+ }
+ else if (scaleActionShouldToggle (d, action, state))
+ {
+ if (ss->type == ScaleTypeGroup)
+ return scaleTerminate (s->display, action,
+ CompActionStateCancel, option, nOption);
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+scaleInitiateOutput (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ SCALE_SCREEN (s);
+
+ if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT)
+ {
+ ss->type = ScaleTypeOutput;
+ return scaleInitiateCommon (s, action, state, option, nOption);
+ }
+ else if (scaleActionShouldToggle (d, action, state))
+ {
+ if (ss->type == ScaleTypeOutput)
+ return scaleTerminate (s->display, action,
+ CompActionStateCancel, option, nOption);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+scaleSelectWindow (CompWindow *w)
+
+{
+ SCALE_DISPLAY (w->screen->display);
+
+ if (sd->selectedWindow != w->id)
+ {
+ CompWindow *old, *new;
+
+ old = findWindowAtScreen (w->screen, sd->selectedWindow);
+ new = findWindowAtScreen (w->screen, w->id);
+
+ sd->selectedWindow = w->id;
+
+ if (old)
+ addWindowDamage (old);
+
+ if (new)
+ addWindowDamage (new);
+ }
+}
+
+static Bool
+scaleSelectWindowAt (CompScreen *s,
+ int x,
+ int y,
+ Bool moveInputFocus)
+
+{
+ CompWindow *w;
+
+ SCALE_DISPLAY (s->display);
+
+ w = scaleCheckForWindowAt (s, x, y);
+ if (w && isScaleWin (w))
+ {
+ SCALE_SCREEN (s);
+
+ (*ss->selectWindow) (w);
+
+ if (moveInputFocus)
+ {
+ sd->lastActiveNum = w->activeNum;
+ sd->lastActiveWindow = w->id;
+
+ moveInputFocusToWindow (w);
+ }
+
+ sd->hoveredWindow = w->id;
+
+ return TRUE;
+ }
+
+ sd->hoveredWindow = None;
+
+ return FALSE;
+}
+
+static void
+scaleMoveFocusWindow (CompScreen *s,
+ int dx,
+ int dy)
+
+{
+ CompWindow *active;
+ CompWindow *focus = NULL;
+
+ active = findWindowAtScreen (s, s->display->activeWindow);
+ if (active)
+ {
+ SCALE_WINDOW (active);
+
+ if (sw->slot)
+ {
+ SCALE_SCREEN (s);
+
+ CompWindow *w;
+ ScaleSlot *slot;
+ int x, y, cx, cy, d, min = MAXSHORT;
+
+ cx = (sw->slot->x1 + sw->slot->x2) / 2;
+ cy = (sw->slot->y1 + sw->slot->y2) / 2;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ slot = GET_SCALE_WINDOW (w, ss)->slot;
+ if (!slot)
+ continue;
+
+ x = (slot->x1 + slot->x2) / 2;
+ y = (slot->y1 + slot->y2) / 2;
+
+ d = abs (x - cx) + abs (y - cy);
+ if (d < min)
+ {
+ if ((dx > 0 && slot->x1 < sw->slot->x2) ||
+ (dx < 0 && slot->x2 > sw->slot->x1) ||
+ (dy > 0 && slot->y1 < sw->slot->y2) ||
+ (dy < 0 && slot->y2 > sw->slot->y1))
+ continue;
+
+ min = d;
+ focus = w;
+ }
+ }
+ }
+ }
+
+ /* move focus to the last focused window if no slot window is currently
+ focused */
+ if (!focus)
+ {
+ CompWindow *w;
+
+ SCALE_SCREEN (s);
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (!GET_SCALE_WINDOW (w, ss)->slot)
+ continue;
+
+ if (!focus || focus->activeNum < w->activeNum)
+ focus = w;
+ }
+ }
+
+ if (focus)
+ {
+ SCALE_DISPLAY (s->display);
+ SCALE_SCREEN (s);
+
+ (*ss->selectWindow) (focus);
+
+ sd->lastActiveNum = focus->activeNum;
+ sd->lastActiveWindow = focus->id;
+
+ moveInputFocusToWindow (focus);
+ }
+}
+
+static Bool
+scaleRelayoutSlots (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ SCALE_SCREEN (s);
+
+ if (ss->state != SCALE_STATE_NONE && ss->state != SCALE_STATE_IN)
+ {
+ if (layoutThumbs (s))
+ {
+ ss->state = SCALE_STATE_OUT;
+ scaleMoveFocusWindow (s, 0, 0);
+ damageScreen (s);
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+scaleWindowRemove (CompDisplay *d,
+ Window id)
+{
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, id);
+ if (w)
+ {
+ SCALE_SCREEN (w->screen);
+
+ if (ss->state != SCALE_STATE_NONE && ss->state != SCALE_STATE_IN)
+ {
+ int i;
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ if (ss->windows[i] == w)
+ {
+ if (layoutThumbs (w->screen))
+ {
+ ss->state = SCALE_STATE_OUT;
+ damageScreen (w->screen);
+ break;
+ }
+ else
+ {
+ CompOption o;
+ CompAction *action;
+ int opt;
+
+ SCALE_DISPLAY (d);
+
+ /* terminate scale mode if the recently closed
+ * window was the last scaled window */
+
+ o.type = CompOptionTypeInt;
+ o.name = "root";
+ o.value.i = w->screen->root;
+
+ opt = SCALE_DISPLAY_OPTION_INITIATE_EDGE;
+ action = &sd->opt[opt].value.action;
+ scaleTerminate (d, action,
+ CompActionStateCancel, &o, 1);
+
+ opt = SCALE_DISPLAY_OPTION_INITIATE_KEY;
+ action = &sd->opt[opt].value.action;
+ scaleTerminate (d, action,
+ CompActionStateCancel, &o, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+static Bool
+scaleHoverTimeout (void *closure)
+{
+ CompScreen *s = closure;
+
+ SCALE_SCREEN (s);
+ SCALE_DISPLAY (s->display);
+
+ if (ss->grab && ss->state != SCALE_STATE_IN)
+ {
+ CompWindow *w;
+ CompOption o;
+ int option;
+
+ w = findWindowAtDisplay (s->display, sd->selectedWindow);
+ if (w)
+ {
+ sd->lastActiveNum = w->activeNum;
+ sd->lastActiveWindow = w->id;
+
+ moveInputFocusToWindow (w);
+ }
+
+ o.type = CompOptionTypeInt;
+ o.name = "root";
+ o.value.i = s->root;
+
+ option = SCALE_DISPLAY_OPTION_INITIATE_EDGE;
+ scaleTerminate (s->display, &sd->opt[option].value.action, 0, &o, 1);
+ option = SCALE_DISPLAY_OPTION_INITIATE_KEY;
+ scaleTerminate (s->display, &sd->opt[option].value.action, 0, &o, 1);
+ }
+
+ ss->hoverHandle = 0;
+
+ return FALSE;
+}
+
+static void
+scaleHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+ Bool consumeEvent = FALSE;
+
+ SCALE_DISPLAY (d);
+
+ switch (event->type) {
+ case KeyPress:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ SCALE_SCREEN (s);
+
+ if (ss->grabIndex)
+ {
+ if (event->xkey.keycode == sd->leftKeyCode)
+ {
+ scaleMoveFocusWindow (s, -1, 0);
+ consumeEvent = TRUE;
+ }
+ else if (event->xkey.keycode == sd->rightKeyCode)
+ {
+ scaleMoveFocusWindow (s, 1, 0);
+ consumeEvent = TRUE;
+ }
+ else if (event->xkey.keycode == sd->upKeyCode)
+ {
+ scaleMoveFocusWindow (s, 0, -1);
+ consumeEvent = TRUE;
+ }
+ else if (event->xkey.keycode == sd->downKeyCode)
+ {
+ scaleMoveFocusWindow (s, 0, 1);
+ consumeEvent = TRUE;
+ }
+ }
+ }
+ break;
+ case ButtonPress:
+ if (event->xbutton.button == Button1)
+ {
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+ int option;
+
+ SCALE_SCREEN (s);
+
+ if (ss->grabIndex && ss->state != SCALE_STATE_IN)
+ {
+ CompOption o;
+
+ o.type = CompOptionTypeInt;
+ o.name = "root";
+ o.value.i = s->root;
+
+ if (scaleSelectWindowAt (s,
+ event->xbutton.x_root,
+ event->xbutton.y_root,
+ TRUE))
+ {
+ option = SCALE_DISPLAY_OPTION_INITIATE_EDGE;
+ scaleTerminate (s->display,
+ &sd->opt[option].value.action,
+ 0, &o, 1);
+ option = SCALE_DISPLAY_OPTION_INITIATE_KEY;
+ scaleTerminate (s->display,
+ &sd->opt[option].value.action,
+ 0, &o, 1);
+ }
+ else if (event->xbutton.x_root > s->workArea.x &&
+ event->xbutton.x_root < (s->workArea.x +
+ s->workArea.width) &&
+ event->xbutton.y_root > s->workArea.y &&
+ event->xbutton.y_root < (s->workArea.y +
+ s->workArea.height))
+ {
+ if (sd->opt[SCALE_DISPLAY_OPTION_SHOW_DESKTOP].value.b)
+ {
+ option = SCALE_DISPLAY_OPTION_INITIATE_EDGE;
+ scaleTerminate (s->display,
+ &sd->opt[option].value.action,
+ 0, &o, 1);
+ option = SCALE_DISPLAY_OPTION_INITIATE_KEY;
+ scaleTerminate (s->display,
+ &sd->opt[option].value.action,
+ 0, &o, 1);
+ (*s->enterShowDesktopMode) (s);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ {
+ SCALE_SCREEN (s);
+
+ if (ss->grabIndex && ss->state != SCALE_STATE_IN)
+ {
+ Bool focus;
+
+ focus = !d->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS].value.b;
+
+ scaleSelectWindowAt (s,
+ event->xmotion.x_root,
+ event->xmotion.y_root,
+ focus);
+ }
+ }
+ break;
+ case ClientMessage:
+ if (event->xclient.message_type == d->xdndPositionAtom)
+ {
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, event->xclient.window);
+ if (w)
+ {
+ Bool focus;
+
+ SCALE_SCREEN (w->screen);
+
+ s = w->screen;
+
+ focus = !d->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS].value.b;
+
+ if (w->id == ss->dndTarget)
+ sendDndStatusMessage (w->screen, event->xclient.data.l[0]);
+
+ if (ss->grab &&
+ ss->state != SCALE_STATE_IN &&
+ w->id == ss->dndTarget)
+ {
+ int x = event->xclient.data.l[2] >> 16;
+ int y = event->xclient.data.l[2] & 0xffff;
+
+ w = scaleCheckForWindowAt (s, x, y);
+ if (w && isScaleWin (w))
+ {
+ int time;
+
+ time = ss->opt[SCALE_SCREEN_OPTION_HOVER_TIME].value.i;
+
+ if (ss->hoverHandle)
+ {
+ if (w->id != sd->selectedWindow)
+ {
+ compRemoveTimeout (ss->hoverHandle);
+ ss->hoverHandle = 0;
+ }
+ }
+
+ if (!ss->hoverHandle)
+ ss->hoverHandle =
+ compAddTimeout (time, (float) time * 1.2,
+ scaleHoverTimeout,
+ s);
+
+ scaleSelectWindowAt (s, x, y, focus);
+ }
+ else
+ {
+ if (ss->hoverHandle)
+ compRemoveTimeout (ss->hoverHandle);
+
+ ss->hoverHandle = 0;
+ }
+ }
+ }
+ }
+ else if (event->xclient.message_type == d->xdndDropAtom ||
+ event->xclient.message_type == d->xdndLeaveAtom)
+ {
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, event->xclient.window);
+ if (w)
+ {
+ int option;
+
+ SCALE_SCREEN (w->screen);
+
+ if (ss->grab &&
+ ss->state != SCALE_STATE_IN &&
+ w->id == ss->dndTarget)
+ {
+ CompOption o;
+
+ o.type = CompOptionTypeInt;
+ o.name = "root";
+ o.value.i = w->screen->root;
+
+ option = SCALE_DISPLAY_OPTION_INITIATE_EDGE;
+ scaleTerminate (d, &sd->opt[option].value.action,
+ 0, &o, 1);
+ option = SCALE_DISPLAY_OPTION_INITIATE_KEY;
+ scaleTerminate (d, &sd->opt[option].value.action,
+ 0, &o, 1);
+ }
+ }
+ }
+ default:
+ break;
+ }
+
+ if (!consumeEvent)
+ {
+ UNWRAP (sd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (sd, d, handleEvent, scaleHandleEvent);
+ }
+
+ switch (event->type) {
+ case UnmapNotify:
+ scaleWindowRemove (d, event->xunmap.window);
+ break;
+ case DestroyNotify:
+ scaleWindowRemove (d, event->xdestroywindow.window);
+ break;
+ }
+
+}
+
+static Bool
+scaleDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status = FALSE;
+
+ SCALE_SCREEN (w->screen);
+
+ if (initial)
+ {
+ if (ss->grab && isScaleWin (w))
+ {
+ if (layoutThumbs (w->screen))
+ {
+ ss->state = SCALE_STATE_OUT;
+ damageScreen (w->screen);
+ }
+ }
+ }
+ else if (ss->state == SCALE_STATE_WAIT)
+ {
+ SCALE_WINDOW (w);
+
+ if (sw->slot)
+ {
+ damageTransformedWindowRect (w,
+ sw->scale,
+ sw->scale,
+ sw->tx,
+ sw->ty,
+ rect);
+
+ status = TRUE;
+ }
+ }
+
+ UNWRAP (ss, w->screen, damageWindowRect);
+ status |= (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (ss, w->screen, damageWindowRect, scaleDamageWindowRect);
+
+ return status;
+}
+
+static CompOption *
+scaleGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ SCALE_DISPLAY (display);
+
+ *count = NUM_OPTIONS (sd);
+ return sd->opt;
+}
+
+static Bool
+scaleSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ SCALE_DISPLAY (display);
+
+ o = compFindOption (sd->opt, NUM_OPTIONS (sd), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case SCALE_DISPLAY_OPTION_ABI:
+ case SCALE_DISPLAY_OPTION_INDEX:
+ break;
+ default:
+ return compSetDisplayOption (display, o, value);
+ }
+
+ return FALSE;
+}
+
+static const CompMetadataOptionInfo scaleDisplayOptionInfo[] = {
+ { "abi", "int", 0, 0, 0 },
+ { "index", "int", 0, 0, 0 },
+ { "initiate_edge", "edge", 0, scaleInitiate, scaleTerminate },
+ { "initiate_button", "button", 0, scaleInitiate, scaleTerminate },
+ { "initiate_key", "key", 0, scaleInitiate, scaleTerminate },
+ { "initiate_all_edge", "edge", 0, scaleInitiateAll, scaleTerminate },
+ { "initiate_all_button", "button", 0, scaleInitiateAll, scaleTerminate },
+ { "initiate_all_key", "key", 0, scaleInitiateAll, scaleTerminate },
+ { "initiate_group_edge", "edge", 0, scaleInitiateGroup, scaleTerminate },
+ { "initiate_group_button", "button", 0,
+ scaleInitiateGroup, scaleTerminate },
+ { "initiate_group_key", "key", 0, scaleInitiateGroup, scaleTerminate },
+ { "initiate_output_edge", "edge", 0, scaleInitiateOutput, scaleTerminate },
+ { "initiate_output_button", "button", 0,
+ scaleInitiateOutput, scaleTerminate },
+ { "initiate_output_key", "key", 0, scaleInitiateOutput, scaleTerminate },
+ { "show_desktop", "bool", 0, 0, 0 },
+ { "relayout_slots", "action", 0, scaleRelayoutSlots, 0 },
+ { "key_bindings_toggle", "bool", 0, 0, 0 },
+ { "button_bindings_toggle", "bool", 0, 0, 0 }
+};
+
+static Bool
+scaleInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ScaleDisplay *sd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ sd = malloc (sizeof (ScaleDisplay));
+ if (!sd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &scaleMetadata,
+ scaleDisplayOptionInfo,
+ sd->opt,
+ SCALE_DISPLAY_OPTION_NUM))
+ {
+ free (sd);
+ return FALSE;
+ }
+
+ sd->opt[SCALE_DISPLAY_OPTION_ABI].value.i = SCALE_ABIVERSION;
+ sd->opt[SCALE_DISPLAY_OPTION_INDEX].value.i = scaleDisplayPrivateIndex;
+
+ sd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (sd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, sd->opt, SCALE_DISPLAY_OPTION_NUM);
+ free (sd);
+ return FALSE;
+ }
+
+ sd->lastActiveNum = None;
+ sd->selectedWindow = None;
+ sd->hoveredWindow = None;
+
+ sd->leftKeyCode = XKeysymToKeycode (d->display, XStringToKeysym ("Left"));
+ sd->rightKeyCode = XKeysymToKeycode (d->display, XStringToKeysym ("Right"));
+ sd->upKeyCode = XKeysymToKeycode (d->display, XStringToKeysym ("Up"));
+ sd->downKeyCode = XKeysymToKeycode (d->display, XStringToKeysym ("Down"));
+
+ WRAP (sd, d, handleEvent, scaleHandleEvent);
+
+ d->base.privates[scaleDisplayPrivateIndex].ptr = sd;
+
+ return TRUE;
+}
+
+static void
+scaleFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ SCALE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, sd->screenPrivateIndex);
+
+ UNWRAP (sd, d, handleEvent);
+
+ compFiniDisplayOptions (d, sd->opt, SCALE_DISPLAY_OPTION_NUM);
+
+ free (sd);
+}
+
+static const CompMetadataOptionInfo scaleScreenOptionInfo[] = {
+ { "spacing", "int", "<min>0</min>", 0, 0 },
+ { "speed", "float", "<min>0.1</min>", 0, 0 },
+ { "timestep", "float", "<min>0.1</min>", 0, 0 },
+ { "window_match", "match", 0, 0, 0 },
+ { "darken_back", "bool", 0, 0, 0 },
+ { "opacity", "int", "<min>0</min><max>100</max>", 0, 0 },
+ { "overlay_icon", "int", RESTOSTRING (0, SCALE_ICON_LAST), 0, 0 },
+ { "hover_time", "int", "<min>50</min>", 0, 0 },
+ { "multioutput_mode", "int", RESTOSTRING (0, SCALE_MOMODE_LAST), 0, 0 }
+};
+
+static Bool
+scaleInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ScaleScreen *ss;
+
+ SCALE_DISPLAY (s->display);
+
+ ss = malloc (sizeof (ScaleScreen));
+ if (!ss)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &scaleMetadata,
+ scaleScreenOptionInfo,
+ ss->opt,
+ SCALE_SCREEN_OPTION_NUM))
+ {
+ free (ss);
+ return FALSE;
+ }
+
+ ss->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (ss->windowPrivateIndex < 0)
+ {
+ compFiniScreenOptions (s, ss->opt, SCALE_SCREEN_OPTION_NUM);
+ free (ss);
+ return FALSE;
+ }
+
+ ss->grab = FALSE;
+ ss->grabIndex = 0;
+
+ ss->hoverHandle = 0;
+
+ ss->dndTarget = None;
+
+ ss->state = SCALE_STATE_NONE;
+
+ ss->slots = 0;
+ ss->slotsSize = 0;
+
+ ss->windows = 0;
+ ss->windowsSize = 0;
+
+ ss->opacity =
+ (OPAQUE * ss->opt[SCALE_SCREEN_OPTION_OPACITY].value.i) / 100;
+
+ matchInit (&ss->match);
+
+ ss->layoutSlotsAndAssignWindows = layoutSlotsAndAssignWindows;
+ ss->setScaledPaintAttributes = setScaledPaintAttributes;
+ ss->scalePaintDecoration = scalePaintDecoration;
+ ss->selectWindow = scaleSelectWindow;
+
+ WRAP (ss, s, preparePaintScreen, scalePreparePaintScreen);
+ WRAP (ss, s, donePaintScreen, scaleDonePaintScreen);
+ WRAP (ss, s, paintOutput, scalePaintOutput);
+ WRAP (ss, s, paintWindow, scalePaintWindow);
+ WRAP (ss, s, damageWindowRect, scaleDamageWindowRect);
+
+ ss->cursor = XCreateFontCursor (s->display->display, XC_left_ptr);
+
+ s->base.privates[sd->screenPrivateIndex].ptr = ss;
+
+ return TRUE;
+}
+
+static void
+scaleFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ SCALE_SCREEN (s);
+
+ UNWRAP (ss, s, preparePaintScreen);
+ UNWRAP (ss, s, donePaintScreen);
+ UNWRAP (ss, s, paintOutput);
+ UNWRAP (ss, s, paintWindow);
+ UNWRAP (ss, s, damageWindowRect);
+
+ matchFini (&ss->match);
+
+ if (ss->cursor)
+ XFreeCursor (s->display->display, ss->cursor);
+
+ if (ss->slotsSize)
+ free (ss->slots);
+
+ if (ss->windows)
+ free (ss->windows);
+
+ freeWindowPrivateIndex (s, ss->windowPrivateIndex);
+
+ compFiniScreenOptions (s, ss->opt, SCALE_SCREEN_OPTION_NUM);
+
+ free (ss);
+}
+
+static Bool
+scaleInitWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ ScaleWindow *sw;
+
+ SCALE_SCREEN (w->screen);
+
+ sw = malloc (sizeof (ScaleWindow));
+ if (!sw)
+ return FALSE;
+
+ sw->slot = 0;
+ sw->scale = 1.0f;
+ sw->tx = sw->ty = 0.0f;
+ sw->adjust = FALSE;
+ sw->xVelocity = sw->yVelocity = 0.0f;
+ sw->scaleVelocity = 1.0f;
+ sw->delta = 1.0f;
+ sw->lastThumbOpacity = 0.0f;
+
+ w->base.privates[ss->windowPrivateIndex].ptr = sw;
+
+ return TRUE;
+}
+
+static void
+scaleFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ SCALE_WINDOW (w);
+
+ free (sw);
+}
+
+static CompBool
+scaleInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) scaleInitDisplay,
+ (InitPluginObjectProc) scaleInitScreen,
+ (InitPluginObjectProc) scaleInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+scaleFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) scaleFiniDisplay,
+ (FiniPluginObjectProc) scaleFiniScreen,
+ (FiniPluginObjectProc) scaleFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+scaleGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) scaleGetDisplayOptions,
+ (GetPluginObjectOptionsProc) scaleGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+scaleSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) scaleSetDisplayOption,
+ (SetPluginObjectOptionProc) scaleSetScreenOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+scaleInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&scaleMetadata,
+ p->vTable->name,
+ scaleDisplayOptionInfo,
+ SCALE_DISPLAY_OPTION_NUM,
+ scaleScreenOptionInfo,
+ SCALE_SCREEN_OPTION_NUM))
+ return FALSE;
+
+ scaleDisplayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (scaleDisplayPrivateIndex < 0)
+ {
+ compFiniMetadata (&scaleMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&scaleMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+scaleFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (scaleDisplayPrivateIndex);
+ compFiniMetadata (&scaleMetadata);
+}
+
+static CompMetadata *
+scaleGetMetadata (CompPlugin *plugin)
+{
+ return &scaleMetadata;
+}
+
+CompPluginVTable scaleVTable = {
+ "scale",
+ scaleGetMetadata,
+ scaleInit,
+ scaleFini,
+ scaleInitObject,
+ scaleFiniObject,
+ scaleGetObjectOptions,
+ scaleSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &scaleVTable;
+}
diff --git a/plugins/screenshot.c b/plugins/screenshot.c
new file mode 100644
index 0000000..1bb824b
--- /dev/null
+++ b/plugins/screenshot.c
@@ -0,0 +1,627 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+
+#include <compiz-core.h>
+
+static CompMetadata shotMetadata;
+
+static int displayPrivateIndex;
+
+#define SHOT_DISPLAY_OPTION_INITIATE_BUTTON 0
+#define SHOT_DISPLAY_OPTION_DIR 1
+#define SHOT_DISPLAY_OPTION_LAUNCH_APP 2
+#define SHOT_DISPLAY_OPTION_NUM 3
+
+typedef struct _ShotDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompOption opt[SHOT_DISPLAY_OPTION_NUM];
+} ShotDisplay;
+
+typedef struct _ShotScreen {
+ PaintOutputProc paintOutput;
+ PaintScreenProc paintScreen;
+ int grabIndex;
+
+ int x1, y1, x2, y2;
+ Bool grab;
+} ShotScreen;
+
+#define GET_SHOT_DISPLAY(d) \
+ ((ShotDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define SHOT_DISPLAY(d) \
+ ShotDisplay *sd = GET_SHOT_DISPLAY (d)
+
+#define GET_SHOT_SCREEN(s, sd) \
+ ((ShotScreen *) (s)->base.privates[(sd)->screenPrivateIndex].ptr)
+
+#define SHOT_SCREEN(s) \
+ ShotScreen *ss = GET_SHOT_SCREEN (s, GET_SHOT_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+
+static Bool
+shotInitiate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ SHOT_SCREEN (s);
+
+ if (otherScreenGrabExist (s, "screenshot", 0))
+ return FALSE;
+
+ if (!ss->grabIndex)
+ ss->grabIndex = pushScreenGrab (s, None, "screenshot");
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ /* start selection screenshot rectangle */
+
+ ss->x1 = ss->x2 = pointerX;
+ ss->y1 = ss->y2 = pointerY;
+
+ ss->grab = TRUE;
+ }
+
+ return TRUE;
+}
+
+static Bool
+shotTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ SHOT_SCREEN (s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (ss->grabIndex)
+ {
+ removeScreenGrab (s, ss->grabIndex, NULL);
+ ss->grabIndex = 0;
+
+ if (ss->x1 != ss->x2 && ss->y1 != ss->y2)
+ {
+ REGION reg;
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ reg.extents.x1 = MIN (ss->x1, ss->x2) - 1;
+ reg.extents.y1 = MIN (ss->y1, ss->y2) - 1;
+ reg.extents.x2 = MAX (ss->x1, ss->x2) + 1;
+ reg.extents.y2 = MAX (ss->y1, ss->y2) + 1;
+
+ damageScreenRegion (s, &reg);
+ }
+ }
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+static int
+shotFilter (const struct dirent *d)
+{
+ int number;
+
+ if (sscanf (d->d_name, "screenshot%d.png", &number))
+ return 1;
+
+ return 0;
+}
+
+static int
+shotSort (const void *_a,
+ const void *_b)
+{
+ struct dirent **a = (struct dirent **) _a;
+ struct dirent **b = (struct dirent **) _b;
+ int al = strlen ((*a)->d_name);
+ int bl = strlen ((*b)->d_name);
+
+ if (al == bl)
+ return strcoll ((*a)->d_name, (*b)->d_name);
+ else
+ return al - bl;
+}
+
+static void
+shotPaintScreen (CompScreen *s,
+ CompOutput *outputs,
+ int numOutput,
+ unsigned int mask)
+{
+ SHOT_SCREEN (s);
+
+ UNWRAP (ss, s, paintScreen);
+ (*s->paintScreen) (s, outputs, numOutput, mask);
+ WRAP (ss, s, paintScreen, shotPaintScreen);
+
+ if (ss->grab)
+ {
+ int x1, x2, y1, y2;
+
+ x1 = MIN (ss->x1, ss->x2);
+ y1 = MIN (ss->y1, ss->y2);
+ x2 = MAX (ss->x1, ss->x2);
+ y2 = MAX (ss->y1, ss->y2);
+
+ if (!ss->grabIndex)
+ {
+ int w = x2 - x1;
+ int h = y2 - y1;
+
+ SHOT_DISPLAY (s->display);
+
+ if (w && h)
+ {
+ GLubyte *buffer;
+ char *dir = sd->opt[SHOT_DISPLAY_OPTION_DIR].value.s;
+
+ buffer = malloc (sizeof (GLubyte) * w * h * 4);
+ if (buffer)
+ {
+ struct dirent **namelist;
+ int n;
+
+ glReadPixels (x1, s->height - y2, w, h,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ (GLvoid *) buffer);
+
+ n = scandir (dir, &namelist, shotFilter, shotSort);
+ if (n >= 0)
+ {
+ char name[256];
+ char *app;
+ int number = 0;
+
+ if (n > 0)
+ sscanf (namelist[n - 1]->d_name,
+ "screenshot%d.png",
+ &number);
+
+ number++;
+
+ if (n)
+ free (namelist);
+
+ sprintf (name, "screenshot%d.png", number);
+
+ app = sd->opt[SHOT_DISPLAY_OPTION_LAUNCH_APP].value.s;
+
+ if (!writeImageToFile (s->display, dir, name, "png",
+ w, h, buffer))
+ {
+ compLogMessage ("screenshot", CompLogLevelError,
+ "failed to write screenshot image");
+ }
+ else if (*app != '\0')
+ {
+ char *command;
+
+ command = malloc (strlen (app) +
+ strlen (dir) +
+ strlen (name) + 3);
+ if (command)
+ {
+ sprintf (command, "%s %s/%s", app, dir, name);
+
+ runCommand (s, command);
+
+ free (command);
+ }
+ }
+ }
+ else
+ {
+ perror (dir);
+ }
+
+ free (buffer);
+ }
+ }
+
+ ss->grab = FALSE;
+ }
+ }
+}
+
+static Bool
+shotPaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ Bool status;
+
+ SHOT_SCREEN (s);
+
+ UNWRAP (ss, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+ WRAP (ss, s, paintOutput, shotPaintOutput);
+
+ if (status && ss->grab)
+ {
+ int x1, x2, y1, y2;
+
+ x1 = MIN (ss->x1, ss->x2);
+ y1 = MIN (ss->y1, ss->y2);
+ x2 = MAX (ss->x1, ss->x2);
+ y2 = MAX (ss->y1, ss->y2);
+
+ if (ss->grabIndex)
+ {
+ glPushMatrix ();
+
+ prepareXCoords (s, output, -DEFAULT_Z_CAMERA);
+
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ glEnable (GL_BLEND);
+ glColor4us (0x2fff, 0x2fff, 0x4fff, 0x4fff);
+ glRecti (x1, y2, x2, y1);
+ glColor4us (0x2fff, 0x2fff, 0x4fff, 0x9fff);
+ glBegin (GL_LINE_LOOP);
+ glVertex2i (x1, y1);
+ glVertex2i (x2, y1);
+ glVertex2i (x2, y2);
+ glVertex2i (x1, y2);
+ glEnd ();
+ glColor4usv (defaultColor);
+ glDisable (GL_BLEND);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ glPopMatrix ();
+ }
+ }
+
+ return status;
+}
+
+static void
+shotHandleMotionEvent (CompScreen *s,
+ int xRoot,
+ int yRoot)
+{
+ SHOT_SCREEN (s);
+
+ /* update screenshot rectangle size */
+
+ if (ss->grabIndex)
+ {
+ REGION reg;
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ reg.extents.x1 = MIN (ss->x1, ss->x2) - 1;
+ reg.extents.y1 = MIN (ss->y1, ss->y2) - 1;
+ reg.extents.x2 = MAX (ss->x1, ss->x2) + 1;
+ reg.extents.y2 = MAX (ss->y1, ss->y2) + 1;
+
+ damageScreenRegion (s, &reg);
+
+ ss->x2 = xRoot;
+ ss->y2 = yRoot;
+
+ reg.extents.x1 = MIN (ss->x1, ss->x2) - 1;
+ reg.extents.y1 = MIN (ss->y1, ss->y2) - 1;
+ reg.extents.x2 = MAX (ss->x1, ss->x2) + 1;
+ reg.extents.y2 = MAX (ss->y1, ss->y2) + 1;
+
+ damageScreenRegion (s, &reg);
+
+ damageScreen (s);
+ }
+}
+
+static void
+shotHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ SHOT_DISPLAY (d);
+
+ switch (event->type) {
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ shotHandleMotionEvent (s, pointerX, pointerY);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ s = findScreenAtDisplay (d, event->xcrossing.root);
+ if (s)
+ shotHandleMotionEvent (s, pointerX, pointerY);
+ default:
+ break;
+ }
+
+ UNWRAP (sd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (sd, d, handleEvent, shotHandleEvent);
+}
+
+static CompOption *
+shotGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ SHOT_DISPLAY (display);
+
+ *count = NUM_OPTIONS (sd);
+ return sd->opt;
+}
+
+static Bool
+shotSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ SHOT_DISPLAY (display);
+
+ o = compFindOption (sd->opt, NUM_OPTIONS (sd), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (display, o, value);
+}
+
+static const CompMetadataOptionInfo shotDisplayOptionInfo[] = {
+ { "initiate_button", "button", 0, shotInitiate, shotTerminate },
+ { "directory", "string", 0, 0, 0 },
+ { "launch_app", "string", 0, 0, 0 }
+};
+
+static Bool
+shotInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ShotDisplay *sd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ sd = malloc (sizeof (ShotDisplay));
+ if (!sd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &shotMetadata,
+ shotDisplayOptionInfo,
+ sd->opt,
+ SHOT_DISPLAY_OPTION_NUM))
+ {
+ free (sd);
+ return FALSE;
+ }
+
+ sd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (sd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, sd->opt, SHOT_DISPLAY_OPTION_NUM);
+ free (sd);
+ return FALSE;
+ }
+
+ WRAP (sd, d, handleEvent, shotHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = sd;
+
+ return TRUE;
+}
+
+static void
+shotFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ SHOT_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, sd->screenPrivateIndex);
+
+ UNWRAP (sd, d, handleEvent);
+
+ compFiniDisplayOptions (d, sd->opt, SHOT_DISPLAY_OPTION_NUM);
+
+ free (sd);
+}
+
+static Bool
+shotInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ShotScreen *ss;
+
+ SHOT_DISPLAY (s->display);
+
+ ss = malloc (sizeof (ShotScreen));
+ if (!ss)
+ return FALSE;
+
+ ss->grabIndex = 0;
+ ss->grab = FALSE;
+
+ WRAP (ss, s, paintScreen, shotPaintScreen);
+ WRAP (ss, s, paintOutput, shotPaintOutput);
+
+ s->base.privates[sd->screenPrivateIndex].ptr = ss;
+
+ return TRUE;
+}
+
+static void
+shotFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ SHOT_SCREEN (s);
+
+ UNWRAP (ss, s, paintScreen);
+ UNWRAP (ss, s, paintOutput);
+
+ free (ss);
+}
+
+static CompBool
+shotInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) shotInitDisplay,
+ (InitPluginObjectProc) shotInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+shotFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) shotFiniDisplay,
+ (FiniPluginObjectProc) shotFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+shotGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) shotGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+shotSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) shotSetDisplayOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+shotInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&shotMetadata,
+ p->vTable->name,
+ shotDisplayOptionInfo,
+ SHOT_DISPLAY_OPTION_NUM,
+ 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&shotMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&shotMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+shotFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&shotMetadata);
+}
+
+static CompMetadata *
+shotGetMetadata (CompPlugin *plugin)
+{
+ return &shotMetadata;
+}
+
+static CompPluginVTable shotVTable = {
+ "screenshot",
+ shotGetMetadata,
+ shotInit,
+ shotFini,
+ shotInitObject,
+ shotFiniObject,
+ shotGetObjectOptions,
+ shotSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &shotVTable;
+}
diff --git a/plugins/svg.c b/plugins/svg.c
new file mode 100644
index 0000000..87fe546
--- /dev/null
+++ b/plugins/svg.c
@@ -0,0 +1,1053 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <cairo/cairo-xlib.h>
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+
+#include <compiz-core.h>
+#include <decoration.h>
+
+static CompMetadata svgMetadata;
+
+#define SVG_DISPLAY_OPTION_SET 0
+#define SVG_DISPLAY_OPTION_NUM 1
+
+static int displayPrivateIndex;
+
+typedef struct _SvgDisplay {
+ CompOption opt[SVG_DISPLAY_OPTION_NUM];
+
+ int screenPrivateIndex;
+
+ HandleCompizEventProc handleCompizEvent;
+
+ FileToImageProc fileToImage;
+} SvgDisplay;
+
+typedef struct _SvgScreen {
+ int windowPrivateIndex;
+
+ DrawWindowProc drawWindow;
+
+ WindowMoveNotifyProc windowMoveNotify;
+ WindowResizeNotifyProc windowResizeNotify;
+
+ BoxRec zoom;
+} SvgScreen;
+
+typedef struct _SvgSource {
+ decor_point_t p1;
+ decor_point_t p2;
+
+ RsvgHandle *svg;
+ RsvgDimensionData dimension;
+} SvgSource;
+
+typedef struct _SvgTexture {
+ CompTexture texture;
+ CompMatrix matrix;
+ cairo_t *cr;
+ Pixmap pixmap;
+ int width;
+ int height;
+} SvgTexture;
+
+typedef struct _SvgContext {
+ SvgSource *source;
+ REGION box;
+ SvgTexture texture[2];
+ BoxRec rect;
+ int width, height;
+} SvgContext;
+
+typedef struct _SvgWindow {
+ SvgSource *source;
+ SvgContext *context;
+} SvgWindow;
+
+#define GET_SVG_DISPLAY(d) \
+ ((SvgDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define SVG_DISPLAY(d) \
+ SvgDisplay *sd = GET_SVG_DISPLAY (d)
+
+#define GET_SVG_SCREEN(s, sd) \
+ ((SvgScreen *) (s)->base.privates[(sd)->screenPrivateIndex].ptr)
+
+#define SVG_SCREEN(s) \
+ SvgScreen *ss = GET_SVG_SCREEN (s, GET_SVG_DISPLAY (s->display))
+
+#define GET_SVG_WINDOW(w, ss) \
+ ((SvgWindow *) (w)->base.privates[(ss)->windowPrivateIndex].ptr)
+
+#define SVG_WINDOW(w) \
+ SvgWindow *sw = GET_SVG_WINDOW (w, \
+ GET_SVG_SCREEN (w->screen, \
+ GET_SVG_DISPLAY (w->screen->display)))
+
+#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
+
+static void
+renderSvg (CompScreen *s,
+ SvgSource *source,
+ SvgTexture *texture,
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ int width,
+ int height)
+{
+ float w = x2 - x1;
+ float h = y2 - y1;
+
+ cairo_save (texture->cr);
+
+ cairo_set_operator (texture->cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba (texture->cr, 1.0, 1.0, 1.0, 0.0);
+ cairo_paint (texture->cr);
+ cairo_set_operator (texture->cr, CAIRO_OPERATOR_OVER);
+
+ cairo_scale (texture->cr, 1.0 / w, 1.0 / h);
+
+ cairo_scale (texture->cr,
+ (double) width / source->dimension.width,
+ (double) height / source->dimension.height);
+
+ cairo_translate (texture->cr,
+ -x1 * source->dimension.width,
+ -y1 * source->dimension.height);
+
+ rsvg_handle_render_cairo (source->svg, texture->cr);
+
+ cairo_restore (texture->cr);
+}
+
+static Bool
+initSvgTexture (CompWindow *w,
+ SvgSource *source,
+ SvgTexture *texture,
+ int width,
+ int height)
+{
+ cairo_surface_t *surface;
+ CompScreen *s = w->screen;
+ Visual *visual;
+ int depth;
+
+ initTexture (s, &texture->texture);
+
+ texture->width = width;
+ texture->height = height;
+
+ texture->pixmap = None;
+ texture->cr = NULL;
+
+ if (width && height)
+ {
+ XWindowAttributes attr;
+ XGetWindowAttributes (s->display->display, w->id, &attr);
+
+ depth = attr.depth;
+ texture->pixmap = XCreatePixmap (s->display->display, s->root,
+ width, height, depth);
+
+ if (!bindPixmapToTexture (s,
+ &texture->texture,
+ texture->pixmap,
+ width, height, depth))
+ {
+ fprintf (stderr, "%s: Couldn't bind pixmap 0x%x to "
+ "texture\n", programName, (int) texture->pixmap);
+
+ XFreePixmap (s->display->display, texture->pixmap);
+
+ return FALSE;
+ }
+
+ visual = attr.visual;
+ surface = cairo_xlib_surface_create (s->display->display,
+ texture->pixmap, visual,
+ width, height);
+ texture->cr = cairo_create (surface);
+ cairo_surface_destroy (surface);
+ }
+
+ return TRUE;
+}
+
+static void
+finiSvgTexture (CompScreen *s,
+ SvgTexture *texture)
+{
+ if (texture->cr)
+ cairo_destroy (texture->cr);
+
+ if (texture->pixmap)
+ XFreePixmap (s->display->display, texture->pixmap);
+
+ finiTexture (s, &texture->texture);
+}
+
+static void
+updateWindowSvgMatrix (CompWindow *w)
+{
+ CompMatrix *m;
+ int width, height;
+
+ SVG_WINDOW (w);
+
+ width = sw->context->box.extents.x2 - sw->context->box.extents.x1;
+ height = sw->context->box.extents.y2 - sw->context->box.extents.y1;
+
+ m = &sw->context->texture[0].matrix;
+ *m = sw->context->texture[0].texture.matrix;
+
+ m->xx *= (float) sw->context->texture[0].width / width;
+ m->yy *= (float) sw->context->texture[0].height / height;
+
+ m->x0 -= (sw->context->box.extents.x1 * m->xx);
+ m->y0 -= (sw->context->box.extents.y1 * m->yy);
+
+ m = &sw->context->texture[1].matrix;
+ *m = sw->context->texture[1].texture.matrix;
+
+ width = sw->context->rect.x2 - sw->context->rect.x1;
+ height = sw->context->rect.y2 - sw->context->rect.y1;
+
+ m->xx *= (float) sw->context->texture[1].width / width;
+ m->yy *= (float) sw->context->texture[1].height / height;
+
+ m->x0 -= (sw->context->rect.x1 * m->xx);
+ m->y0 -= (sw->context->rect.y1 * m->yy);
+}
+
+static Bool
+svgDrawWindow (CompWindow *w,
+ const CompTransform *transform,
+ const FragmentAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ SVG_SCREEN (w->screen);
+
+ UNWRAP (ss, w->screen, drawWindow);
+ status = (*w->screen->drawWindow) (w, transform, attrib, region, mask);
+ WRAP (ss, w->screen, drawWindow, svgDrawWindow);
+
+ if (status)
+ {
+ SVG_WINDOW (w);
+
+ if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
+ region = &infiniteRegion;
+
+ if (sw->context && region->numRects)
+ {
+ CompTexture *texture = &sw->context->texture[0].texture;
+ CompMatrix *matrix = &sw->context->texture[0].matrix;
+ REGION r;
+
+ r.rects = &r.extents;
+ r.numRects = 1;
+
+ r.extents = sw->context->box.extents;
+
+ if (r.extents.x1 < ss->zoom.x1)
+ r.extents.x1 = ss->zoom.x1;
+ if (r.extents.y1 < ss->zoom.y1)
+ r.extents.y1 = ss->zoom.y1;
+ if (r.extents.x2 > ss->zoom.x2)
+ r.extents.x2 = ss->zoom.x2;
+ if (r.extents.y2 > ss->zoom.y2)
+ r.extents.y2 = ss->zoom.y2;
+
+ w->vCount = w->indexCount = 0;
+
+ (*w->screen->addWindowGeometry) (w,
+ matrix, 1,
+ &sw->context->box,
+ region);
+
+ if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
+ mask |= PAINT_WINDOW_BLEND_MASK;
+
+ (*w->screen->drawWindowTexture) (w, texture, attrib, mask);
+
+ if (r.extents.x1 < r.extents.x2 && r.extents.y1 < r.extents.y2)
+ {
+ float xScale, yScale;
+ float dx, dy;
+ int width, height;
+ int saveFilter;
+
+ r.extents.x1--;
+ r.extents.y1--;
+ r.extents.x2++;
+ r.extents.y2++;
+
+ xScale = w->screen->width / (float)
+ (ss->zoom.x2 - ss->zoom.x1);
+ yScale = w->screen->height / (float)
+ (ss->zoom.y2 - ss->zoom.y1);
+
+ dx = r.extents.x2 - r.extents.x1;
+ dy = r.extents.y2 - r.extents.y1;
+
+ width = dx * xScale + 0.5f;
+ height = dy * yScale + 0.5f;
+
+ if (r.extents.x1 != sw->context->rect.x1 ||
+ r.extents.y1 != sw->context->rect.y1 ||
+ r.extents.x2 != sw->context->rect.x2 ||
+ r.extents.y2 != sw->context->rect.y2 ||
+ width != sw->context->width ||
+ height != sw->context->height)
+ {
+ float x1, y1, x2, y2;
+
+ sw->context->rect = r.extents;
+
+ sw->context->width = width;
+ sw->context->height = height;
+
+ dx = sw->context->box.extents.x2 -
+ sw->context->box.extents.x1;
+ dy = sw->context->box.extents.y2 -
+ sw->context->box.extents.y1;
+
+ x1 = (r.extents.x1 - sw->context->box.extents.x1) / dx;
+ y1 = (r.extents.y1 - sw->context->box.extents.y1) / dy;
+ x2 = (r.extents.x2 - sw->context->box.extents.x1) / dx;
+ y2 = (r.extents.y2 - sw->context->box.extents.y1) / dy;
+
+ finiSvgTexture (w->screen, &sw->context->texture[1]);
+
+ if (initSvgTexture (w, sw->context->source,
+ &sw->context->texture[1],
+ width, height))
+ {
+ renderSvg (w->screen, sw->context->source,
+ &sw->context->texture[1],
+ x1, y1, x2, y2,
+ width, height);
+
+ updateWindowSvgMatrix (w);
+ }
+ }
+
+ texture = &sw->context->texture[1].texture;
+ matrix = &sw->context->texture[1].matrix;
+
+ w->vCount = w->indexCount = 0;
+
+ saveFilter = w->screen->filter[SCREEN_TRANS_FILTER];
+ w->screen->filter[SCREEN_TRANS_FILTER] =
+ COMP_TEXTURE_FILTER_GOOD;
+
+ (*w->screen->addWindowGeometry) (w, matrix, 1, &r, region);
+ (*w->screen->drawWindowTexture) (w, texture, attrib, mask);
+
+ w->screen->filter[SCREEN_TRANS_FILTER] = saveFilter;
+ }
+ else if (sw->context->texture[1].width)
+ {
+ finiSvgTexture (w->screen, &sw->context->texture[1]);
+ initSvgTexture (w, sw->source, &sw->context->texture[1], 0, 0);
+
+ memset (&sw->context->rect, 0, sizeof (BoxRec));
+
+ sw->context->width = 0;
+ sw->context->height = 0;
+ }
+ }
+ }
+
+ return status;
+}
+
+static void
+updateWindowSvgContext (CompWindow *w,
+ SvgSource *source)
+{
+ int x1, y1, x2, y2;
+
+ SVG_WINDOW (w);
+
+ if (sw->context)
+ {
+ finiSvgTexture (w->screen, &sw->context->texture[0]);
+ finiSvgTexture (w->screen, &sw->context->texture[1]);
+ }
+ else
+ {
+ sw->context = malloc (sizeof (SvgContext));
+ if (!sw->context)
+ return;
+ }
+
+ memset (&sw->context->rect, 0, sizeof (BoxRec));
+
+ sw->context->width = 0;
+ sw->context->height = 0;
+
+ initSvgTexture (w, source, &sw->context->texture[1], 0, 0);
+
+ sw->context->source = source;
+
+ sw->context->box.rects = &sw->context->box.extents;
+ sw->context->box.numRects = 1;
+
+ decor_apply_gravity (source->p1.gravity,
+ source->p1.x, source->p1.y,
+ w->width, w->height,
+ &x1, &y1);
+
+ decor_apply_gravity (source->p2.gravity,
+ source->p2.x, source->p2.y,
+ w->width, w->height,
+ &x2, &y2);
+
+ x1 = MAX (x1, 0);
+ y1 = MAX (y1, 0);
+ x2 = MIN (x2, w->width);
+ y2 = MIN (y2, w->height);
+
+ if (!initSvgTexture (w, source, &sw->context->texture[0],
+ w->width, w->height))
+ {
+ free (sw->context);
+ sw->context = NULL;
+ }
+ else
+ {
+ renderSvg (w->screen, source, &sw->context->texture[0],
+ 0.0f, 0.0f, 1.0f, 1.0f, w->width, w->height);
+
+ initSvgTexture (w, source, &sw->context->texture[1], 0, 0);
+
+ sw->context->box.extents.x1 = x1;
+ sw->context->box.extents.y1 = y1;
+ sw->context->box.extents.x2 = x2;
+ sw->context->box.extents.y2 = y2;
+
+ sw->context->box.extents.x1 += w->attrib.x;
+ sw->context->box.extents.y1 += w->attrib.y;
+ sw->context->box.extents.x2 += w->attrib.x;
+ sw->context->box.extents.y2 += w->attrib.y;
+
+ updateWindowSvgMatrix (w);
+ }
+}
+
+static Bool
+svgSet (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompWindow *w;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "window", 0);
+
+ w = findWindowAtDisplay (d, xid);
+ if (w)
+ {
+ decor_point_t p[2];
+ char *data;
+ RsvgHandle *svg = NULL;
+ GError *error = NULL;
+
+ SVG_WINDOW (w);
+
+ memset (p, 0, sizeof (p));
+
+ p[0].gravity = getIntOptionNamed (option, nOption, "gravity0",
+ GRAVITY_NORTH | GRAVITY_WEST);
+
+ p[0].x = getIntOptionNamed (option, nOption, "x0", 0);
+ p[0].y = getIntOptionNamed (option, nOption, "y0", 0);
+
+ p[1].gravity = getIntOptionNamed (option, nOption, "gravity1",
+ GRAVITY_SOUTH | GRAVITY_EAST);
+
+ p[1].x = getIntOptionNamed (option, nOption, "x1", 0);
+ p[1].y = getIntOptionNamed (option, nOption, "y1", 0);
+
+ data = getStringOptionNamed (option, nOption, "data", 0);
+ if (data)
+ svg = rsvg_handle_new_from_data ((guint8 *) data, strlen (data),
+ &error);
+
+ if (sw->source)
+ {
+ rsvg_handle_free (sw->source->svg);
+ sw->source->svg = svg;
+ }
+ else
+ {
+ sw->source = malloc (sizeof (SvgSource));
+ if (sw->source)
+ sw->source->svg = svg;
+ }
+
+ if (sw->source && sw->source->svg)
+ {
+ sw->source->p1 = p[0];
+ sw->source->p2 = p[1];
+
+ sw->source->svg = svg;
+
+ rsvg_handle_get_dimensions (svg, &sw->source->dimension);
+
+ updateWindowSvgContext (w, sw->source);
+ }
+ else
+ {
+ if (svg)
+ rsvg_handle_free (svg);
+
+ if (sw->source)
+ {
+ free (sw->source);
+ sw->source = NULL;
+ }
+
+ if (sw->context)
+ {
+ finiSvgTexture (w->screen, &sw->context->texture[0]);
+ free (sw->context);
+ sw->context = NULL;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+svgWindowMoveNotify (CompWindow *w,
+ int dx,
+ int dy,
+ Bool immediate)
+{
+ SVG_SCREEN (w->screen);
+ SVG_WINDOW (w);
+
+ if (sw->context)
+ {
+ sw->context->box.extents.x1 += dx;
+ sw->context->box.extents.y1 += dy;
+ sw->context->box.extents.x2 += dx;
+ sw->context->box.extents.y2 += dy;
+
+ updateWindowSvgMatrix (w);
+ }
+
+ UNWRAP (ss, w->screen, windowMoveNotify);
+ (*w->screen->windowMoveNotify) (w, dx, dy, immediate);
+ WRAP (ss, w->screen, windowMoveNotify, svgWindowMoveNotify);
+}
+
+static void
+svgWindowResizeNotify (CompWindow *w,
+ int dx,
+ int dy,
+ int dwidth,
+ int dheight)
+{
+ SVG_SCREEN (w->screen);
+ SVG_WINDOW (w);
+
+ if (sw->source)
+ updateWindowSvgContext (w, sw->source);
+
+ UNWRAP (ss, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
+ WRAP (ss, w->screen, windowResizeNotify, svgWindowResizeNotify);
+}
+
+static void
+svgHandleCompizEvent (CompDisplay *d,
+ const char *pluginName,
+ const char *eventName,
+ CompOption *option,
+ int nOption)
+{
+ SVG_DISPLAY (d);
+
+ UNWRAP (sd, d, handleCompizEvent);
+ (*d->handleCompizEvent) (d, pluginName, eventName, option, nOption);
+ WRAP (sd, d, handleCompizEvent, svgHandleCompizEvent);
+
+ if (strcmp (pluginName, "zoom") == 0)
+ {
+ CompScreen *s;
+ int output = getIntOptionNamed (option, nOption, "output", 0);
+
+ s = findScreenAtDisplay (d, getIntOptionNamed (option, nOption,
+ "root", 0));
+ if (s && output == 0)
+ {
+ SVG_SCREEN (s);
+
+ if (strcmp (eventName, "in") == 0)
+ {
+ ss->zoom.x1 = getIntOptionNamed (option, nOption, "x1", 0);
+ ss->zoom.y1 = getIntOptionNamed (option, nOption, "y1", 0);
+ ss->zoom.x2 = getIntOptionNamed (option, nOption, "x2", 0);
+ ss->zoom.y2 = getIntOptionNamed (option, nOption, "y2", 0);
+ }
+ else if (strcmp (eventName, "out") == 0)
+ {
+ memset (&ss->zoom, 0, sizeof (BoxRec));
+ }
+ }
+ }
+}
+
+static Bool
+readSvgFileToImage (char *file,
+ int *width,
+ int *height,
+ void **data)
+{
+ cairo_surface_t *surface;
+ FILE *fp;
+ GError *error = NULL;
+ RsvgHandle *svgHandle;
+ RsvgDimensionData svgDimension;
+
+ fp = fopen (file, "r");
+ if (!fp)
+ return FALSE;
+
+ fclose (fp);
+
+ svgHandle = rsvg_handle_new_from_file (file, &error);
+ if (!svgHandle)
+ return FALSE;
+
+ rsvg_handle_get_dimensions (svgHandle, &svgDimension);
+
+ *width = svgDimension.width;
+ *height = svgDimension.height;
+
+ *data = malloc (svgDimension.width * svgDimension.height * 4);
+ if (!*data)
+ {
+ rsvg_handle_free (svgHandle);
+ return FALSE;
+ }
+
+ surface = cairo_image_surface_create_for_data (*data,
+ CAIRO_FORMAT_ARGB32,
+ svgDimension.width,
+ svgDimension.height,
+ svgDimension.width * 4);
+ if (surface)
+ {
+ cairo_t *cr;
+
+ cr = cairo_create (surface);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+ rsvg_handle_render_cairo (svgHandle, cr);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+ }
+
+ rsvg_handle_free (svgHandle);
+
+ return TRUE;
+}
+
+static char *
+svgExtension (const char *name)
+{
+
+ if (strlen (name) > 4)
+ {
+ if (strcasecmp (name + (strlen (name) - 4), ".svg") == 0)
+ return "";
+ }
+
+ return ".svg";
+}
+
+static Bool
+svgFileToImage (CompDisplay *d,
+ const char *path,
+ const char *name,
+ int *width,
+ int *height,
+ int *stride,
+ void **data)
+{
+ Bool status = FALSE;
+ char *extension = svgExtension (name);
+ char *file;
+ int len;
+
+ SVG_DISPLAY (d);
+
+ len = (path ? strlen (path) : 0) + strlen (name) + strlen (extension) + 2;
+
+ file = malloc (len);
+ if (file)
+ {
+ if (path)
+ sprintf (file, "%s/%s%s", path, name, extension);
+ else
+ sprintf (file, "%s%s", name, extension);
+
+ status = readSvgFileToImage (file, width, height, data);
+
+ free (file);
+
+ if (status)
+ {
+ *stride = *width * 4;
+ return TRUE;
+ }
+ }
+
+ UNWRAP (sd, d, fileToImage);
+ status = (*d->fileToImage) (d, path, name, width, height, stride, data);
+ WRAP (sd, d, fileToImage, svgFileToImage);
+
+ return status;
+}
+
+static CompOption *
+svgGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ SVG_DISPLAY (display);
+
+ *count = NUM_OPTIONS (sd);
+ return sd->opt;
+}
+
+static Bool
+svgSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ SVG_DISPLAY (display);
+
+ o = compFindOption (sd->opt, NUM_OPTIONS (sd), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (display, o, value);
+}
+
+static const CompMetadataOptionInfo svgDisplayOptionInfo[] = {
+ { "set", "action", 0, svgSet, NULL }
+};
+
+static Bool
+svgInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ SvgDisplay *sd;
+ CompScreen *s;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ sd = malloc (sizeof (SvgDisplay));
+ if (!sd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &svgMetadata,
+ svgDisplayOptionInfo,
+ sd->opt,
+ SVG_DISPLAY_OPTION_NUM))
+ {
+ free (sd);
+ return FALSE;
+ }
+
+ sd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (sd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, sd->opt, SVG_DISPLAY_OPTION_NUM);
+ free (sd);
+ return FALSE;
+ }
+
+ WRAP (sd, d, handleCompizEvent, svgHandleCompizEvent);
+ WRAP (sd, d, fileToImage, svgFileToImage);
+
+ d->base.privates[displayPrivateIndex].ptr = sd;
+
+ for (s = d->screens; s; s = s->next)
+ updateDefaultIcon (s);
+
+ return TRUE;
+}
+
+static void
+svgFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ CompScreen *s;
+
+ SVG_DISPLAY (d);
+
+ UNWRAP (sd, d, handleCompizEvent);
+ UNWRAP (sd, d, fileToImage);
+
+ for (s = d->screens; s; s = s->next)
+ updateDefaultIcon (s);
+
+ freeScreenPrivateIndex (d, sd->screenPrivateIndex);
+
+ compFiniDisplayOptions (d, sd->opt, SVG_DISPLAY_OPTION_NUM);
+
+ free (sd);
+}
+
+static Bool
+svgInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ SvgScreen *ss;
+
+ SVG_DISPLAY (s->display);
+
+ ss = malloc (sizeof (SvgScreen));
+ if (!ss)
+ return FALSE;
+
+ ss->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (ss->windowPrivateIndex < 0)
+ {
+ free (ss);
+ return FALSE;
+ }
+
+ memset (&ss->zoom, 0, sizeof (BoxRec));
+
+ WRAP (ss, s, drawWindow, svgDrawWindow);
+ WRAP (ss, s, windowMoveNotify, svgWindowMoveNotify);
+ WRAP (ss, s, windowResizeNotify, svgWindowResizeNotify);
+
+ s->base.privates[sd->screenPrivateIndex].ptr = ss;
+
+ return TRUE;
+}
+
+static void
+svgFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ SVG_SCREEN (s);
+
+ freeWindowPrivateIndex (s, ss->windowPrivateIndex);
+
+ UNWRAP (ss, s, drawWindow);
+ UNWRAP (ss, s, windowMoveNotify);
+ UNWRAP (ss, s, windowResizeNotify);
+
+ free (ss);
+}
+
+static Bool
+svgInitWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ SvgWindow *sw;
+
+ SVG_SCREEN (w->screen);
+
+ sw = malloc (sizeof (SvgWindow));
+ if (!sw)
+ return FALSE;
+
+ sw->source = NULL;
+ sw->context = NULL;
+
+ w->base.privates[ss->windowPrivateIndex].ptr = sw;
+
+ return TRUE;
+}
+
+static void
+svgFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ SVG_WINDOW (w);
+
+ if (sw->source)
+ {
+ rsvg_handle_free (sw->source->svg);
+ free (sw->source);
+ }
+
+ if (sw->context)
+ {
+ finiSvgTexture (w->screen, &sw->context->texture[0]);
+ free (sw->context);
+ }
+
+ free (sw);
+}
+
+static CompBool
+svgInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) svgInitDisplay,
+ (InitPluginObjectProc) svgInitScreen,
+ (InitPluginObjectProc) svgInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+svgFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) svgFiniDisplay,
+ (FiniPluginObjectProc) svgFiniScreen,
+ (FiniPluginObjectProc) svgFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+svgGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) svgGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+svgSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) svgSetDisplayOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+svgInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&svgMetadata,
+ p->vTable->name,
+ svgDisplayOptionInfo,
+ SVG_DISPLAY_OPTION_NUM,
+ 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&svgMetadata);
+ return FALSE;
+ }
+
+ rsvg_init ();
+
+ compAddMetadataFromFile (&svgMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+svgFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&svgMetadata);
+
+ rsvg_term ();
+}
+
+static CompMetadata *
+svgGetMetadata (CompPlugin *plugin)
+{
+ return &svgMetadata;
+}
+
+CompPluginVTable svgVTable = {
+ "svg",
+ svgGetMetadata,
+ svgInit,
+ svgFini,
+ svgInitObject,
+ svgFiniObject,
+ svgGetObjectOptions,
+ svgSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &svgVTable;
+}
diff --git a/plugins/switcher.c b/plugins/switcher.c
new file mode 100644
index 0000000..116ed91
--- /dev/null
+++ b/plugins/switcher.c
@@ -0,0 +1,2134 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <compiz-core.h>
+#include <decoration.h>
+
+#include <X11/Xatom.h>
+#include <X11/extensions/Xrender.h>
+
+#define ZOOMED_WINDOW_MASK (1 << 0)
+#define NORMAL_WINDOW_MASK (1 << 1)
+
+static CompMetadata switchMetadata;
+
+static int displayPrivateIndex;
+
+#define SWITCH_DISPLAY_OPTION_NEXT_BUTTON 0
+#define SWITCH_DISPLAY_OPTION_NEXT_KEY 1
+#define SWITCH_DISPLAY_OPTION_PREV_BUTTON 2
+#define SWITCH_DISPLAY_OPTION_PREV_KEY 3
+#define SWITCH_DISPLAY_OPTION_NEXT_ALL_BUTTON 4
+#define SWITCH_DISPLAY_OPTION_NEXT_ALL_KEY 5
+#define SWITCH_DISPLAY_OPTION_PREV_ALL_BUTTON 6
+#define SWITCH_DISPLAY_OPTION_PREV_ALL_KEY 7
+#define SWITCH_DISPLAY_OPTION_NEXT_NO_POPUP_BUTTON 8
+#define SWITCH_DISPLAY_OPTION_NEXT_NO_POPUP_KEY 9
+#define SWITCH_DISPLAY_OPTION_PREV_NO_POPUP_BUTTON 10
+#define SWITCH_DISPLAY_OPTION_PREV_NO_POPUP_KEY 11
+#define SWITCH_DISPLAY_OPTION_NEXT_PANEL_BUTTON 12
+#define SWITCH_DISPLAY_OPTION_NEXT_PANEL_KEY 13
+#define SWITCH_DISPLAY_OPTION_PREV_PANEL_BUTTON 14
+#define SWITCH_DISPLAY_OPTION_PREV_PANEL_KEY 15
+#define SWITCH_DISPLAY_OPTION_NUM 16
+
+typedef struct _SwitchDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompOption opt[SWITCH_DISPLAY_OPTION_NUM];
+
+ Atom selectWinAtom;
+ Atom selectFgColorAtom;
+} SwitchDisplay;
+
+#define SWITCH_SCREEN_OPTION_SPEED 0
+#define SWITCH_SCREEN_OPTION_TIMESTEP 1
+#define SWITCH_SCREEN_OPTION_WINDOW_MATCH 2
+#define SWITCH_SCREEN_OPTION_MIPMAP 3
+#define SWITCH_SCREEN_OPTION_SATURATION 4
+#define SWITCH_SCREEN_OPTION_BRIGHTNESS 5
+#define SWITCH_SCREEN_OPTION_OPACITY 6
+#define SWITCH_SCREEN_OPTION_BRINGTOFRONT 7
+#define SWITCH_SCREEN_OPTION_ZOOM 8
+#define SWITCH_SCREEN_OPTION_ICON 9
+#define SWITCH_SCREEN_OPTION_MINIMIZED 10
+#define SWITCH_SCREEN_OPTION_AUTO_ROTATE 11
+#define SWITCH_SCREEN_OPTION_NUM 12
+
+typedef enum {
+ CurrentViewport = 0,
+ AllViewports,
+ Panels
+} SwitchWindowSelection;
+
+typedef struct _SwitchScreen {
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintOutputProc paintOutput;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+
+ CompOption opt[SWITCH_SCREEN_OPTION_NUM];
+
+ Window popupWindow;
+
+ Window selectedWindow;
+ Window zoomedWindow;
+ unsigned int lastActiveNum;
+
+ float zoom;
+
+ int grabIndex;
+
+ Bool switching;
+ Bool zooming;
+ int zoomMask;
+
+ int moreAdjust;
+
+ GLfloat mVelocity;
+ GLfloat tVelocity;
+ GLfloat sVelocity;
+
+ CompWindow **windows;
+ int windowsSize;
+ int nWindows;
+
+ int pos;
+ int move;
+
+ float translate;
+ float sTranslate;
+
+ SwitchWindowSelection selection;
+
+ unsigned int fgColor[4];
+} SwitchScreen;
+
+#define MwmHintsDecorations (1L << 1)
+
+typedef struct {
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+} MwmHints;
+
+#define WIDTH 212
+#define HEIGHT 192
+#define SPACE 10
+
+#define SWITCH_ZOOM 0.1f
+
+#define BOX_WIDTH 3
+
+#define ICON_SIZE 64
+
+static float _boxVertices[] =
+{
+ -(WIDTH >> 1), 0,
+ -(WIDTH >> 1), BOX_WIDTH,
+ (WIDTH >> 1), BOX_WIDTH,
+ (WIDTH >> 1), 0,
+
+ -(WIDTH >> 1), BOX_WIDTH,
+ -(WIDTH >> 1), HEIGHT - BOX_WIDTH,
+ -(WIDTH >> 1) + BOX_WIDTH, HEIGHT - BOX_WIDTH,
+ -(WIDTH >> 1) + BOX_WIDTH, 0,
+
+ (WIDTH >> 1) - BOX_WIDTH, BOX_WIDTH,
+ (WIDTH >> 1) - BOX_WIDTH, HEIGHT - BOX_WIDTH,
+ (WIDTH >> 1), HEIGHT - BOX_WIDTH,
+ (WIDTH >> 1), 0,
+
+ -(WIDTH >> 1), HEIGHT - BOX_WIDTH,
+ -(WIDTH >> 1), HEIGHT,
+ (WIDTH >> 1), HEIGHT,
+ (WIDTH >> 1), HEIGHT - BOX_WIDTH
+};
+
+#define WINDOW_WIDTH(count) (WIDTH * (count) + (SPACE << 1))
+#define WINDOW_HEIGHT (HEIGHT + (SPACE << 1))
+
+#define GET_SWITCH_DISPLAY(d) \
+ ((SwitchDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define SWITCH_DISPLAY(d) \
+ SwitchDisplay *sd = GET_SWITCH_DISPLAY (d)
+
+#define GET_SWITCH_SCREEN(s, sd) \
+ ((SwitchScreen *) (s)->base.privates[(sd)->screenPrivateIndex].ptr)
+
+#define SWITCH_SCREEN(s) \
+ SwitchScreen *ss = GET_SWITCH_SCREEN (s, GET_SWITCH_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static CompOption *
+switchGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+ int *count)
+{
+ SWITCH_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ss);
+ return ss->opt;
+}
+
+static Bool
+switchSetScreenOption (CompPlugin *plugin,
+ CompScreen *screen,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ SWITCH_SCREEN (screen);
+
+ o = compFindOption (ss->opt, NUM_OPTIONS (ss), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case SWITCH_SCREEN_OPTION_ZOOM:
+ if (compSetFloatOption (o, value))
+ {
+ if (o->value.f < 0.05f)
+ {
+ ss->zooming = FALSE;
+ ss->zoom = 0.0f;
+ }
+ else
+ {
+ ss->zooming = TRUE;
+ ss->zoom = o->value.f / 30.0f;
+ }
+
+ return TRUE;
+ }
+ break;
+ default:
+ return compSetScreenOption (screen, o, value);
+ }
+
+ return FALSE;
+}
+
+static void
+setSelectedWindowHint (CompScreen *s)
+{
+ SWITCH_DISPLAY (s->display);
+ SWITCH_SCREEN (s);
+
+ XChangeProperty (s->display->display, ss->popupWindow, sd->selectWinAtom,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &ss->selectedWindow, 1);
+}
+
+static Bool
+isSwitchWin (CompWindow *w)
+{
+ SWITCH_SCREEN (w->screen);
+
+ if (!w->mapNum || w->attrib.map_state != IsViewable)
+ {
+ if (ss->opt[SWITCH_SCREEN_OPTION_MINIMIZED].value.b)
+ {
+ if (!w->minimized && !w->inShowDesktopMode && !w->shaded)
+ return FALSE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ if (!(w->inputHint || (w->protocols & CompWindowProtocolTakeFocusMask)))
+ return FALSE;
+
+ if (w->attrib.override_redirect)
+ return FALSE;
+
+ if (ss->selection == Panels)
+ {
+ if (!(w->type & (CompWindowTypeDockMask | CompWindowTypeDesktopMask)))
+ return FALSE;
+ }
+ else
+ {
+ CompMatch *match;
+
+ if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
+ return FALSE;
+
+ if (w->state & CompWindowStateSkipTaskbarMask)
+ return FALSE;
+
+ match = &ss->opt[SWITCH_SCREEN_OPTION_WINDOW_MATCH].value.match;
+ if (!matchEval (match, w))
+ return FALSE;
+
+ }
+
+ if (ss->selection == CurrentViewport)
+ {
+ if (!w->mapNum || w->attrib.map_state != IsViewable)
+ {
+ if (w->serverX + w->width <= 0 ||
+ w->serverY + w->height <= 0 ||
+ w->serverX >= w->screen->width ||
+ w->serverY >= w->screen->height)
+ return FALSE;
+ }
+ else
+ {
+ if (!(*w->screen->focusWindow) (w))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+switchActivateEvent (CompScreen *s,
+ Bool activating)
+{
+ CompOption o[2];
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "root";
+ o[0].value.i = s->root;
+
+ o[1].type = CompOptionTypeBool;
+ o[1].name = "active";
+ o[1].value.b = activating;
+
+ (*s->display->handleCompizEvent) (s->display, "switcher", "activate", o, 2);
+}
+
+static int
+compareWindows (const void *elem1,
+ const void *elem2)
+{
+ CompWindow *w1 = *((CompWindow **) elem1);
+ CompWindow *w2 = *((CompWindow **) elem2);
+
+ if (w1->mapNum && !w2->mapNum)
+ return -1;
+
+ if (w2->mapNum && !w1->mapNum)
+ return 1;
+
+ return w2->activeNum - w1->activeNum;
+}
+
+static void
+switchAddWindowToList (CompScreen *s,
+ CompWindow *w)
+{
+ SWITCH_SCREEN (s);
+
+ if (ss->windowsSize <= ss->nWindows)
+ {
+ ss->windows = realloc (ss->windows,
+ sizeof (CompWindow *) * (ss->nWindows + 32));
+ if (!ss->windows)
+ return;
+
+ ss->windowsSize = ss->nWindows + 32;
+ }
+
+ ss->windows[ss->nWindows++] = w;
+}
+
+static void
+switchUpdateWindowList (CompScreen *s,
+ int count)
+{
+ int x, y;
+
+ SWITCH_SCREEN (s);
+
+ if (count > 1)
+ {
+ count -= (count + 1) & 1;
+ if (count < 3)
+ count = 3;
+ }
+
+ ss->pos = ((count >> 1) - ss->nWindows) * WIDTH;
+ ss->move = 0;
+
+ ss->selectedWindow = ss->windows[0]->id;
+
+ x = s->outputDev[s->currentOutputDev].region.extents.x1 +
+ s->outputDev[s->currentOutputDev].width / 2;
+ y = s->outputDev[s->currentOutputDev].region.extents.y1 +
+ s->outputDev[s->currentOutputDev].height / 2;
+
+ if (ss->popupWindow)
+ XMoveResizeWindow (s->display->display, ss->popupWindow,
+ x - WINDOW_WIDTH (count) / 2,
+ y - WINDOW_HEIGHT / 2,
+ WINDOW_WIDTH (count),
+ WINDOW_HEIGHT);
+}
+
+static void
+switchCreateWindowList (CompScreen *s,
+ int count)
+{
+ CompWindow *w;
+
+ SWITCH_SCREEN (s);
+
+ ss->nWindows = 0;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (isSwitchWin (w))
+ switchAddWindowToList (s, w);
+ }
+
+ qsort (ss->windows, ss->nWindows, sizeof (CompWindow *), compareWindows);
+
+ if (ss->nWindows == 2)
+ {
+ switchAddWindowToList (s, ss->windows[0]);
+ switchAddWindowToList (s, ss->windows[1]);
+ }
+
+ switchUpdateWindowList (s, count);
+}
+
+static void
+switchToWindow (CompScreen *s,
+ Bool toNext)
+{
+ CompWindow *w;
+ int cur;
+
+ SWITCH_SCREEN (s);
+
+ if (!ss->grabIndex)
+ return;
+
+ for (cur = 0; cur < ss->nWindows; cur++)
+ {
+ if (ss->windows[cur]->id == ss->selectedWindow)
+ break;
+ }
+
+ if (cur == ss->nWindows)
+ return;
+
+ if (toNext)
+ w = ss->windows[(cur + 1) % ss->nWindows];
+ else
+ w = ss->windows[(cur + ss->nWindows - 1) % ss->nWindows];
+
+ if (w)
+ {
+ Window old = ss->selectedWindow;
+
+ if (ss->selection == AllViewports &&
+ ss->opt[SWITCH_SCREEN_OPTION_AUTO_ROTATE].value.b)
+ {
+ XEvent xev;
+ int x, y;
+
+ defaultViewportForWindow (w, &x, &y);
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.display = s->display->display;
+ xev.xclient.format = 32;
+
+ xev.xclient.message_type = s->display->desktopViewportAtom;
+ xev.xclient.window = s->root;
+
+ xev.xclient.data.l[0] = x * s->width;
+ xev.xclient.data.l[1] = y * s->height;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ XSendEvent (s->display->display, s->root, FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &xev);
+ }
+
+ ss->lastActiveNum = w->activeNum;
+ ss->selectedWindow = w->id;
+
+ if (!ss->zoomedWindow)
+ ss->zoomedWindow = ss->selectedWindow;
+
+ if (old != w->id)
+ {
+ if (toNext)
+ ss->move -= WIDTH;
+ else
+ ss->move += WIDTH;
+
+ ss->moreAdjust = 1;
+ }
+
+ if (ss->popupWindow)
+ {
+ CompWindow *popup;
+
+ popup = findWindowAtScreen (s, ss->popupWindow);
+ if (popup)
+ addWindowDamage (popup);
+
+ setSelectedWindowHint (s);
+ }
+
+ addWindowDamage (w);
+
+ if (old)
+ {
+ w = findWindowAtScreen (s, old);
+ if (w)
+ addWindowDamage (w);
+ }
+ }
+}
+
+static int
+switchCountWindows (CompScreen *s)
+{
+ CompWindow *w;
+ int count = 0;
+
+ for (w = s->windows; w && count < 5; w = w->next)
+ if (isSwitchWin (w))
+ count++;
+
+ if (count == 5 && s->width <= WINDOW_WIDTH (5))
+ count = 3;
+
+ return count;
+}
+
+static Visual *
+findArgbVisual (Display *dpy, int scr)
+{
+ XVisualInfo *xvi;
+ XVisualInfo template;
+ int nvi;
+ int i;
+ XRenderPictFormat *format;
+ Visual *visual;
+
+ template.screen = scr;
+ template.depth = 32;
+ template.class = TrueColor;
+
+ xvi = XGetVisualInfo (dpy,
+ VisualScreenMask |
+ VisualDepthMask |
+ VisualClassMask,
+ &template,
+ &nvi);
+ if (!xvi)
+ return 0;
+
+ visual = 0;
+ for (i = 0; i < nvi; i++)
+ {
+ format = XRenderFindVisualFormat (dpy, xvi[i].visual);
+ if (format->type == PictTypeDirect && format->direct.alphaMask)
+ {
+ visual = xvi[i].visual;
+ break;
+ }
+ }
+
+ XFree (xvi);
+
+ return visual;
+}
+
+static void
+switchInitiate (CompScreen *s,
+ SwitchWindowSelection selection,
+ Bool showPopup)
+{
+ int count;
+
+ SWITCH_SCREEN (s);
+
+ if (otherScreenGrabExist (s, "switcher", "scale", "cube", 0))
+ return;
+
+ ss->selection = selection;
+ ss->selectedWindow = None;
+
+ count = switchCountWindows (s);
+ if (count < 1)
+ return;
+
+ if (!ss->popupWindow && showPopup)
+ {
+ Display *dpy = s->display->display;
+ XSizeHints xsh;
+ XWMHints xwmh;
+ XClassHint xch;
+ Atom state[4];
+ int nState = 0;
+ XSetWindowAttributes attr;
+ Visual *visual;
+
+ visual = findArgbVisual (dpy, s->screenNum);
+ if (!visual)
+ return;
+
+ if (count > 1)
+ {
+ count -= (count + 1) & 1;
+ if (count < 3)
+ count = 3;
+ }
+
+ xsh.flags = PSize | PWinGravity;
+ xsh.width = WINDOW_WIDTH (count);
+ xsh.height = WINDOW_HEIGHT;
+ xsh.win_gravity = StaticGravity;
+
+ xwmh.flags = InputHint;
+ xwmh.input = 0;
+
+ xch.res_name = "compiz";
+ xch.res_class = "switcher-window";
+
+ attr.background_pixel = 0;
+ attr.border_pixel = 0;
+ attr.colormap = XCreateColormap (dpy, s->root, visual,
+ AllocNone);
+
+ ss->popupWindow =
+ XCreateWindow (dpy, s->root,
+ s->width / 2 - xsh.width / 2,
+ s->height / 2 - xsh.height / 2,
+ xsh.width, xsh.height, 0,
+ 32, InputOutput, visual,
+ CWBackPixel | CWBorderPixel | CWColormap, &attr);
+
+ XSetWMProperties (dpy, ss->popupWindow, NULL, NULL,
+ programArgv, programArgc,
+ &xsh, &xwmh, &xch);
+
+ state[nState++] = s->display->winStateAboveAtom;
+ state[nState++] = s->display->winStateStickyAtom;
+ state[nState++] = s->display->winStateSkipTaskbarAtom;
+ state[nState++] = s->display->winStateSkipPagerAtom;
+
+ XChangeProperty (dpy, ss->popupWindow,
+ s->display->winStateAtom,
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) state, nState);
+
+ XChangeProperty (dpy, ss->popupWindow,
+ s->display->winTypeAtom,
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) &s->display->winTypeUtilAtom, 1);
+
+ setWindowProp (s->display, ss->popupWindow,
+ s->display->winDesktopAtom,
+ 0xffffffff);
+
+ setSelectedWindowHint (s);
+ }
+
+ if (!ss->grabIndex)
+ ss->grabIndex = pushScreenGrab (s, s->invisibleCursor, "switcher");
+
+ if (ss->grabIndex)
+ {
+ if (!ss->switching)
+ {
+ ss->lastActiveNum = s->activeNum;
+
+ switchCreateWindowList (s, count);
+
+ ss->sTranslate = ss->zoom;
+
+ if (ss->popupWindow && showPopup)
+ {
+ CompWindow *w;
+
+ w = findWindowAtScreen (s, ss->popupWindow);
+ if (w && (w->state & CompWindowStateHiddenMask))
+ {
+ w->hidden = FALSE;
+ showWindow (w);
+ }
+ else
+ {
+ XMapWindow (s->display->display, ss->popupWindow);
+ }
+ }
+
+ switchActivateEvent (s, TRUE);
+ }
+
+ damageScreen (s);
+
+ ss->switching = TRUE;
+ ss->moreAdjust = 1;
+ }
+}
+
+static Bool
+switchTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ SWITCH_SCREEN (s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (ss->grabIndex)
+ {
+ CompWindow *w;
+
+ if (ss->popupWindow)
+ {
+ w = findWindowAtScreen (s, ss->popupWindow);
+ if (w && w->managed && w->mapNum)
+ {
+ w->hidden = TRUE;
+ hideWindow (w);
+ }
+ else
+ {
+ XUnmapWindow (s->display->display, ss->popupWindow);
+ }
+ }
+
+ ss->switching = FALSE;
+
+ if (state & CompActionStateCancel)
+ {
+ ss->selectedWindow = None;
+ ss->zoomedWindow = None;
+ }
+
+ if (state && ss->selectedWindow)
+ {
+ w = findWindowAtScreen (s, ss->selectedWindow);
+ if (w)
+ sendWindowActivationRequest (w->screen, w->id);
+ }
+
+ removeScreenGrab (s, ss->grabIndex, 0);
+ ss->grabIndex = 0;
+
+ if (!ss->zooming)
+ {
+ ss->selectedWindow = None;
+ ss->zoomedWindow = None;
+
+ switchActivateEvent (s, FALSE);
+ }
+ else
+ {
+ ss->moreAdjust = 1;
+ }
+
+ ss->selectedWindow = None;
+ setSelectedWindowHint (s);
+
+ ss->lastActiveNum = 0;
+
+ damageScreen (s);
+ }
+ }
+
+ if (action)
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+static Bool
+switchInitiateCommon (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption,
+ SwitchWindowSelection selection,
+ Bool showPopup,
+ Bool nextWindow)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ SWITCH_SCREEN (s);
+
+ if (!ss->switching)
+ {
+ switchInitiate (s, selection, showPopup);
+
+ if (state & CompActionStateInitKey)
+ action->state |= CompActionStateTermKey;
+
+ if (state & CompActionStateInitEdge)
+ action->state |= CompActionStateTermEdge;
+ else if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+ }
+
+ switchToWindow (s, nextWindow);
+ }
+
+ return FALSE;
+}
+
+static Bool
+switchNext (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ return switchInitiateCommon (d, action, state, option, nOption,
+ CurrentViewport, TRUE, TRUE);
+}
+
+static Bool
+switchPrev (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ return switchInitiateCommon (d, action, state, option, nOption,
+ CurrentViewport, TRUE, FALSE);
+}
+
+static Bool
+switchNextAll (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ return switchInitiateCommon (d, action, state, option, nOption,
+ AllViewports, TRUE, TRUE);
+}
+
+static Bool
+switchPrevAll (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ return switchInitiateCommon (d, action, state, option, nOption,
+ AllViewports, TRUE, FALSE);
+}
+
+static Bool
+switchNextNoPopup (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ return switchInitiateCommon (d, action, state, option, nOption,
+ CurrentViewport, FALSE, TRUE);
+}
+
+static Bool
+switchPrevNoPopup (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ return switchInitiateCommon (d, action, state, option, nOption,
+ CurrentViewport, FALSE, FALSE);
+}
+
+static Bool
+switchNextPanel (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ return switchInitiateCommon (d, action, state, option, nOption,
+ Panels, FALSE, TRUE);
+}
+
+static Bool
+switchPrevPanel (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ return switchInitiateCommon (d, action, state, option, nOption,
+ Panels, FALSE, FALSE);
+}
+
+static void
+switchWindowRemove (CompDisplay *d,
+ Window id)
+{
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, id);
+ if (w)
+ {
+ Bool inList = FALSE;
+ int count, j, i = 0;
+ Window selected, old;
+
+ SWITCH_SCREEN (w->screen);
+
+ if (isSwitchWin (w))
+ return;
+
+ old = selected = ss->selectedWindow;
+
+ while (i < ss->nWindows)
+ {
+ if (ss->windows[i] == w)
+ {
+ inList = TRUE;
+
+ if (w->id == selected)
+ {
+ if (i < ss->nWindows)
+ selected = ss->windows[i + 1]->id;
+ else
+ selected = ss->windows[0]->id;
+ }
+
+ ss->nWindows--;
+ for (j = i; j < ss->nWindows; j++)
+ ss->windows[j] = ss->windows[j + 1];
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ if (!inList)
+ return;
+
+ count = ss->nWindows;
+
+ if (ss->nWindows == 2)
+ {
+ if (ss->windows[0] == ss->windows[1])
+ {
+ ss->nWindows--;
+ count = 1;
+ }
+ else
+ {
+ switchAddWindowToList (w->screen, ss->windows[0]);
+ switchAddWindowToList (w->screen, ss->windows[1]);
+ }
+ }
+
+ if (ss->nWindows == 0)
+ {
+ CompOption o;
+
+ o.type = CompOptionTypeInt;
+ o.name = "root";
+ o.value.i = w->screen->root;
+
+ switchTerminate (d, NULL, 0, &o, 1);
+ return;
+ }
+
+ if (!ss->grabIndex)
+ return;
+
+ switchUpdateWindowList (w->screen, count);
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ ss->selectedWindow = ss->windows[i]->id;
+
+ if (ss->selectedWindow == selected)
+ break;
+
+ ss->pos -= WIDTH;
+ if (ss->pos < -ss->nWindows * WIDTH)
+ ss->pos += ss->nWindows * WIDTH;
+ }
+
+ if (ss->popupWindow)
+ {
+ CompWindow *popup;
+
+ popup = findWindowAtScreen (w->screen, ss->popupWindow);
+ if (popup)
+ addWindowDamage (popup);
+
+ setSelectedWindowHint (w->screen);
+ }
+
+ if (old != ss->selectedWindow)
+ {
+ addWindowDamage (w);
+
+ w = findWindowAtScreen (w->screen, old);
+ if (w)
+ addWindowDamage (w);
+
+ ss->moreAdjust = 1;
+ }
+ }
+}
+
+static void
+updateForegroundColor (CompScreen *s)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *propData;
+
+ SWITCH_SCREEN (s);
+ SWITCH_DISPLAY (s->display);
+
+ if (!ss->popupWindow)
+ return;
+
+
+ result = XGetWindowProperty (s->display->display, ss->popupWindow,
+ sd->selectFgColorAtom, 0L, 4L, FALSE,
+ XA_INTEGER, &actual, &format,
+ &n, &left, &propData);
+
+ if (result == Success && propData)
+ {
+ if (n == 3 || n == 4)
+ {
+ long *data = (long *) propData;
+
+ ss->fgColor[0] = MIN (0xffff, data[0]);
+ ss->fgColor[1] = MIN (0xffff, data[1]);
+ ss->fgColor[2] = MIN (0xffff, data[2]);
+
+ if (n == 4)
+ ss->fgColor[3] = MIN (0xffff, data[3]);
+ }
+
+ XFree (propData);
+ }
+ else
+ {
+ ss->fgColor[0] = 0;
+ ss->fgColor[1] = 0;
+ ss->fgColor[2] = 0;
+ ss->fgColor[3] = 0xffff;
+ }
+}
+
+static void
+switchHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompWindow *w;
+ SWITCH_DISPLAY (d);
+
+ switch (event->type) {
+ w = findWindowAtDisplay (d, event->xmap.window);
+ if (w)
+ {
+ SWITCH_SCREEN (w->screen);
+
+ if (w->id == ss->popupWindow)
+ {
+ w->wmType = getWindowType (d, w->id);
+ recalcWindowType (w);
+ recalcWindowActions (w);
+ updateWindowClassHints (w);
+ }
+ }
+ break;
+ }
+
+ UNWRAP (sd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (sd, d, handleEvent, switchHandleEvent);
+
+ switch (event->type) {
+ case UnmapNotify:
+ switchWindowRemove (d, event->xunmap.window);
+ break;
+ case DestroyNotify:
+ switchWindowRemove (d, event->xdestroywindow.window);
+ break;
+ case PropertyNotify:
+ if (event->xproperty.atom == sd->selectFgColorAtom)
+ {
+ w = findWindowAtDisplay (d, event->xproperty.window);
+ if (w)
+ {
+ SWITCH_SCREEN (w->screen);
+
+ if (event->xproperty.window == ss->popupWindow)
+ updateForegroundColor (w->screen);
+ }
+ }
+
+ default:
+ break;
+ }
+}
+
+static int
+adjustSwitchVelocity (CompScreen *s)
+{
+ float dx, adjust, amount;
+
+ SWITCH_SCREEN (s);
+
+ dx = ss->move;
+
+ adjust = dx * 0.15f;
+ amount = fabs (dx) * 1.5f;
+ if (amount < 0.2f)
+ amount = 0.2f;
+ else if (amount > 2.0f)
+ amount = 2.0f;
+
+ ss->mVelocity = (amount * ss->mVelocity + adjust) / (amount + 1.0f);
+
+ if (ss->zooming)
+ {
+ float dt, ds;
+
+ if (ss->switching)
+ dt = ss->zoom - ss->translate;
+ else
+ dt = 0.0f - ss->translate;
+
+ adjust = dt * 0.15f;
+ amount = fabs (dt) * 1.5f;
+ if (amount < 0.2f)
+ amount = 0.2f;
+ else if (amount > 2.0f)
+ amount = 2.0f;
+
+ ss->tVelocity = (amount * ss->tVelocity + adjust) / (amount + 1.0f);
+
+ if (ss->selectedWindow == ss->zoomedWindow)
+ ds = ss->zoom - ss->sTranslate;
+ else
+ ds = 0.0f - ss->sTranslate;
+
+ adjust = ds * 0.5f;
+ amount = fabs (ds) * 5.0f;
+ if (amount < 1.0f)
+ amount = 1.0f;
+ else if (amount > 6.0f)
+ amount = 6.0f;
+
+ ss->sVelocity = (amount * ss->sVelocity + adjust) / (amount + 1.0f);
+
+ if (ss->selectedWindow == ss->zoomedWindow)
+ {
+ if (fabs (dx) < 0.1f && fabs (ss->mVelocity) < 0.2f &&
+ fabs (dt) < 0.001f && fabs (ss->tVelocity) < 0.001f &&
+ fabs (ds) < 0.001f && fabs (ss->sVelocity) < 0.001f)
+ {
+ ss->mVelocity = ss->tVelocity = ss->sVelocity = 0.0f;
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ if (fabs (dx) < 0.1f && fabs (ss->mVelocity) < 0.2f)
+ {
+ ss->mVelocity = 0.0f;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void
+switchPreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ SWITCH_SCREEN (s);
+
+ if (ss->moreAdjust)
+ {
+ int steps, m;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f *
+ ss->opt[SWITCH_SCREEN_OPTION_SPEED].value.f;
+ steps = amount /
+ (0.5f * ss->opt[SWITCH_SCREEN_OPTION_TIMESTEP].value.f);
+ if (!steps) steps = 1;
+ chunk = amount / (float) steps;
+
+ while (steps--)
+ {
+ ss->moreAdjust = adjustSwitchVelocity (s);
+ if (!ss->moreAdjust)
+ {
+ ss->pos += ss->move;
+ ss->move = 0;
+
+ if (ss->zooming)
+ {
+ if (ss->switching)
+ {
+ ss->translate = ss->zoom;
+ ss->sTranslate = ss->zoom;
+ }
+ else
+ {
+ ss->translate = 0.0f;
+ ss->sTranslate = ss->zoom;
+
+ ss->selectedWindow = None;
+ ss->zoomedWindow = None;
+
+ if (ss->grabIndex)
+ {
+ removeScreenGrab (s, ss->grabIndex, 0);
+ ss->grabIndex = 0;
+ }
+
+ switchActivateEvent (s, FALSE);
+ }
+ }
+ break;
+ }
+
+ m = ss->mVelocity * chunk;
+ if (!m)
+ {
+ if (ss->mVelocity)
+ m = (ss->move > 0) ? 1 : -1;
+ }
+
+ ss->move -= m;
+ ss->pos += m;
+ if (ss->pos < -ss->nWindows * WIDTH)
+ ss->pos += ss->nWindows * WIDTH;
+ else if (ss->pos > 0)
+ ss->pos -= ss->nWindows * WIDTH;
+
+ ss->translate += ss->tVelocity * chunk;
+ ss->sTranslate += ss->sVelocity * chunk;
+
+ if (ss->selectedWindow != ss->zoomedWindow)
+ {
+ if (ss->sTranslate < 0.01f)
+ ss->zoomedWindow = ss->selectedWindow;
+ }
+ }
+ }
+
+ UNWRAP (ss, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (ss, s, preparePaintScreen, switchPreparePaintScreen);
+}
+
+static Bool
+switchPaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ Bool status;
+
+ SWITCH_SCREEN (s);
+
+ ss->zoomMask = ZOOMED_WINDOW_MASK | NORMAL_WINDOW_MASK;
+
+ if (ss->grabIndex || (ss->zooming && ss->translate > 0.001f))
+ {
+ CompTransform sTransform = *transform;
+ CompWindow *zoomed;
+ CompWindow *switcher;
+ Window zoomedAbove = None;
+ Bool saveDestroyed = FALSE;
+
+ if (ss->zooming)
+ {
+ mask &= ~PAINT_SCREEN_REGION_MASK;
+ mask |= PAINT_SCREEN_TRANSFORMED_MASK | PAINT_SCREEN_CLEAR_MASK;
+
+ matrixTranslate (&sTransform, 0.0f, 0.0f, -ss->translate);
+
+ ss->zoomMask = NORMAL_WINDOW_MASK;
+ }
+
+ switcher = findWindowAtScreen (s, ss->popupWindow);
+ if (switcher)
+ {
+ saveDestroyed = switcher->destroyed;
+ switcher->destroyed = TRUE;
+ }
+
+ if (ss->opt[SWITCH_SCREEN_OPTION_BRINGTOFRONT].value.b)
+ {
+ zoomed = findWindowAtScreen (s, ss->zoomedWindow);
+ if (zoomed)
+ {
+ CompWindow *w;
+
+ for (w = zoomed->prev; w && w->id <= 1; w = w->prev);
+ zoomedAbove = (w) ? w->id : None;
+
+ unhookWindowFromScreen (s, zoomed);
+ insertWindowIntoScreen (s, zoomed, s->reverseWindows->id);
+ }
+ }
+ else
+ {
+ zoomed = NULL;
+ }
+
+ UNWRAP (ss, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, &sTransform,
+ region, output, mask);
+ WRAP (ss, s, paintOutput, switchPaintOutput);
+
+ if (ss->zooming)
+ {
+ float zTranslate;
+
+ mask &= ~PAINT_SCREEN_CLEAR_MASK;
+ mask |= PAINT_SCREEN_NO_BACKGROUND_MASK;
+
+ ss->zoomMask = ZOOMED_WINDOW_MASK;
+
+ zTranslate = MIN (ss->sTranslate, ss->translate);
+ matrixTranslate (&sTransform, 0.0f, 0.0f, zTranslate);
+
+ UNWRAP (ss, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, &sTransform, region,
+ output, mask);
+ WRAP (ss, s, paintOutput, switchPaintOutput);
+ }
+
+ if (zoomed)
+ {
+ unhookWindowFromScreen (s, zoomed);
+ insertWindowIntoScreen (s, zoomed, zoomedAbove);
+ }
+
+ if (switcher)
+ {
+ sTransform = *transform;
+
+ switcher->destroyed = saveDestroyed;
+
+ transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform);
+
+ glPushMatrix ();
+ glLoadMatrixf (sTransform.m);
+
+ if (!switcher->destroyed &&
+ switcher->attrib.map_state == IsViewable &&
+ switcher->damaged)
+ {
+ (*s->paintWindow) (switcher, &switcher->paint, &sTransform,
+ &infiniteRegion, 0);
+ }
+
+ glPopMatrix ();
+ }
+ }
+ else
+ {
+ UNWRAP (ss, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output,
+ mask);
+ WRAP (ss, s, paintOutput, switchPaintOutput);
+ }
+
+ return status;
+}
+
+static void
+switchDonePaintScreen (CompScreen *s)
+{
+ SWITCH_SCREEN (s);
+
+ if ((ss->grabIndex || ss->zooming) && ss->moreAdjust)
+ {
+ if (ss->zooming)
+ {
+ damageScreen (s);
+ }
+ else
+ {
+ CompWindow *w;
+
+ w = findWindowAtScreen (s, ss->popupWindow);
+ if (w)
+ addWindowDamage (w);
+ }
+ }
+
+ UNWRAP (ss, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (ss, s, donePaintScreen, switchDonePaintScreen);
+}
+
+static void
+switchPaintThumb (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ unsigned int mask,
+ int x,
+ int y,
+ int x1,
+ int x2)
+{
+ WindowPaintAttrib sAttrib = *attrib;
+ int wx, wy;
+ float width, height;
+ CompIcon *icon = NULL;
+
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+
+ if (w->mapNum)
+ {
+ if (!w->texture->pixmap && !w->bindFailed)
+ bindWindow (w);
+ }
+
+ if (w->texture->pixmap)
+ {
+ AddWindowGeometryProc oldAddWindowGeometry;
+ FragmentAttrib fragment;
+ CompTransform wTransform = *transform;
+ int ww, wh;
+
+ SWITCH_SCREEN (w->screen);
+
+ width = WIDTH - (SPACE << 1);
+ height = HEIGHT - (SPACE << 1);
+
+ ww = w->width + w->input.left + w->input.right;
+ wh = w->height + w->input.top + w->input.bottom;
+
+ if (ww > width)
+ sAttrib.xScale = width / ww;
+ else
+ sAttrib.xScale = 1.0f;
+
+ if (wh > height)
+ sAttrib.yScale = height / wh;
+ else
+ sAttrib.yScale = 1.0f;
+
+ if (sAttrib.xScale < sAttrib.yScale)
+ sAttrib.yScale = sAttrib.xScale;
+ else
+ sAttrib.xScale = sAttrib.yScale;
+
+ width = ww * sAttrib.xScale;
+ height = wh * sAttrib.yScale;
+
+ wx = x + SPACE + ((WIDTH - (SPACE << 1)) - width) / 2;
+ wy = y + SPACE + ((HEIGHT - (SPACE << 1)) - height) / 2;
+
+ sAttrib.xTranslate = wx - w->attrib.x + w->input.left * sAttrib.xScale;
+ sAttrib.yTranslate = wy - w->attrib.y + w->input.top * sAttrib.yScale;
+
+ initFragmentAttrib (&fragment, &sAttrib);
+
+ if (w->alpha || fragment.opacity != OPAQUE)
+ mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
+
+ matrixTranslate (&wTransform, w->attrib.x, w->attrib.y, 0.0f);
+ matrixScale (&wTransform, sAttrib.xScale, sAttrib.yScale, 1.0f);
+ matrixTranslate (&wTransform,
+ sAttrib.xTranslate / sAttrib.xScale - w->attrib.x,
+ sAttrib.yTranslate / sAttrib.yScale - w->attrib.y,
+ 0.0f);
+
+ glPushMatrix ();
+ glLoadMatrixf (wTransform.m);
+
+ /* XXX: replacing the addWindowGeometry function like this is
+ very ugly but necessary until the vertex stage has been made
+ fully pluggable. */
+ oldAddWindowGeometry = w->screen->addWindowGeometry;
+ w->screen->addWindowGeometry = addWindowGeometry;
+ (w->screen->drawWindow) (w, &wTransform, &fragment, &infiniteRegion,
+ mask);
+ w->screen->addWindowGeometry = oldAddWindowGeometry;
+
+ glPopMatrix ();
+
+ if (ss->opt[SWITCH_SCREEN_OPTION_ICON].value.b)
+ {
+ icon = getWindowIcon (w, ICON_SIZE, ICON_SIZE);
+ if (icon)
+ {
+ sAttrib.xScale = sAttrib.yScale = 1.0f;
+
+ wx = x + WIDTH - icon->width - SPACE;
+ wy = y + HEIGHT - icon->height - SPACE;
+ }
+ }
+ }
+ else
+ {
+ width = WIDTH - (WIDTH >> 2);
+ height = HEIGHT - (HEIGHT >> 2);
+
+ icon = getWindowIcon (w, width, height);
+ if (!icon)
+ icon = w->screen->defaultIcon;
+
+ if (icon)
+ {
+ int iw, ih;
+
+ iw = width - SPACE;
+ ih = height - SPACE;
+
+ if (icon->width < (iw >> 1))
+ sAttrib.xScale = (iw / icon->width);
+ else
+ sAttrib.xScale = 1.0f;
+
+ if (icon->height < (ih >> 1))
+ sAttrib.yScale = (ih / icon->height);
+ else
+ sAttrib.yScale = 1.0f;
+
+ if (sAttrib.xScale < sAttrib.yScale)
+ sAttrib.yScale = sAttrib.xScale;
+ else
+ sAttrib.xScale = sAttrib.yScale;
+
+ width = icon->width * sAttrib.xScale;
+ height = icon->height * sAttrib.yScale;
+
+ wx = x + SPACE + ((WIDTH - (SPACE << 1)) - width) / 2;
+ wy = y + SPACE + ((HEIGHT - (SPACE << 1)) - height) / 2;
+ }
+ }
+
+ if (icon && (icon->texture.name || iconToTexture (w->screen, icon)))
+ {
+ REGION iconReg;
+ CompMatrix matrix;
+
+ mask |= PAINT_WINDOW_BLEND_MASK;
+
+ iconReg.rects = &iconReg.extents;
+ iconReg.numRects = 1;
+
+ iconReg.extents.x1 = w->attrib.x;
+ iconReg.extents.y1 = w->attrib.y;
+ iconReg.extents.x2 = w->attrib.x + icon->width;
+ iconReg.extents.y2 = w->attrib.y + icon->height;
+
+ matrix = icon->texture.matrix;
+ matrix.x0 -= (w->attrib.x * icon->texture.matrix.xx);
+ matrix.y0 -= (w->attrib.y * icon->texture.matrix.yy);
+
+ sAttrib.xTranslate = wx - w->attrib.x;
+ sAttrib.yTranslate = wy - w->attrib.y;
+
+ w->vCount = w->indexCount = 0;
+ addWindowGeometry (w, &matrix, 1, &iconReg, &infiniteRegion);
+ if (w->vCount)
+ {
+ FragmentAttrib fragment;
+ CompTransform wTransform = *transform;
+
+ initFragmentAttrib (&fragment, &sAttrib);
+
+ matrixTranslate (&wTransform, w->attrib.x, w->attrib.y, 0.0f);
+ matrixScale (&wTransform, sAttrib.xScale, sAttrib.yScale, 1.0f);
+ matrixTranslate (&wTransform,
+ sAttrib.xTranslate / sAttrib.xScale - w->attrib.x,
+ sAttrib.yTranslate / sAttrib.yScale - w->attrib.y,
+ 0.0f);
+
+ glPushMatrix ();
+ glLoadMatrixf (wTransform.m);
+
+ (*w->screen->drawWindowTexture) (w,
+ &icon->texture, &fragment,
+ mask);
+
+ glPopMatrix ();
+ }
+ }
+}
+
+static Bool
+switchPaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ int zoomType = NORMAL_WINDOW_MASK;
+ Bool status;
+
+ SWITCH_SCREEN (s);
+
+ if (w->id == ss->popupWindow)
+ {
+ GLenum filter;
+ int x, y, x1, x2, cx, i;
+ unsigned short color[4];
+
+ if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)
+ return FALSE;
+
+ UNWRAP (ss, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (ss, s, paintWindow, switchPaintWindow);
+
+ if (!(mask & PAINT_WINDOW_TRANSFORMED_MASK) && region->numRects == 0)
+ return TRUE;
+
+ x1 = w->attrib.x + SPACE;
+ x2 = w->attrib.x + w->width - SPACE;
+
+ x = x1 + ss->pos;
+ y = w->attrib.y + SPACE;
+
+ filter = s->display->textureFilter;
+
+ if (ss->opt[SWITCH_SCREEN_OPTION_MIPMAP].value.b)
+ s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
+
+ glPushAttrib (GL_SCISSOR_BIT);
+
+ glEnable (GL_SCISSOR_TEST);
+ glScissor (x1, 0, x2 - x1, w->screen->height);
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ if (x + WIDTH > x1)
+ switchPaintThumb (ss->windows[i], &w->lastPaint, transform,
+ mask, x, y, x1, x2);
+
+ x += WIDTH;
+ }
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ if (x > x2)
+ break;
+
+ switchPaintThumb (ss->windows[i], &w->lastPaint, transform, mask,
+ x, y, x1, x2);
+
+ x += WIDTH;
+ }
+
+ glPopAttrib ();
+
+ s->display->textureFilter = filter;
+
+ cx = w->attrib.x + (w->width >> 1);
+
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ glEnable (GL_BLEND);
+ for (i = 0; i < 4; i++)
+ color[i] = (unsigned int)ss->fgColor[i] * w->lastPaint.opacity /
+ 0xffff;
+ glColor4usv (color);
+ glPushMatrix ();
+ glTranslatef (cx, y, 0.0f);
+ glVertexPointer (2, GL_FLOAT, 0, _boxVertices);
+ glDrawArrays (GL_QUADS, 0, 16);
+ glPopMatrix ();
+ glColor4usv (defaultColor);
+ glDisable (GL_BLEND);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ }
+ else if (w->id == ss->selectedWindow)
+ {
+ if (ss->opt[SWITCH_SCREEN_OPTION_BRINGTOFRONT].value.b &&
+ ss->selectedWindow == ss->zoomedWindow)
+ zoomType = ZOOMED_WINDOW_MASK;
+
+ if (!(ss->zoomMask & zoomType))
+ return (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) ?
+ FALSE : TRUE;
+
+ UNWRAP (ss, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (ss, s, paintWindow, switchPaintWindow);
+ }
+ else if (ss->switching)
+ {
+ WindowPaintAttrib sAttrib = *attrib;
+ GLuint value;
+
+ value = ss->opt[SWITCH_SCREEN_OPTION_SATURATION].value.i;
+ if (value != 100)
+ sAttrib.saturation = sAttrib.saturation * value / 100;
+
+ value = ss->opt[SWITCH_SCREEN_OPTION_BRIGHTNESS].value.i;
+ if (value != 100)
+ sAttrib.brightness = sAttrib.brightness * value / 100;
+
+ if (w->wmType & ~(CompWindowTypeDockMask | CompWindowTypeDesktopMask))
+ {
+ value = ss->opt[SWITCH_SCREEN_OPTION_OPACITY].value.i;
+ if (value != 100)
+ sAttrib.opacity = sAttrib.opacity * value / 100;
+ }
+
+ if (ss->opt[SWITCH_SCREEN_OPTION_BRINGTOFRONT].value.b &&
+ w->id == ss->zoomedWindow)
+ zoomType = ZOOMED_WINDOW_MASK;
+
+ if (!(ss->zoomMask & zoomType))
+ return (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) ?
+ FALSE : TRUE;
+
+ UNWRAP (ss, s, paintWindow);
+ status = (*s->paintWindow) (w, &sAttrib, transform, region, mask);
+ WRAP (ss, s, paintWindow, switchPaintWindow);
+ }
+ else
+ {
+ if (!(ss->zoomMask & zoomType))
+ return (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) ?
+ FALSE : TRUE;
+
+ UNWRAP (ss, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (ss, s, paintWindow, switchPaintWindow);
+ }
+
+ return status;
+}
+
+static Bool
+switchDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status;
+
+ SWITCH_SCREEN (w->screen);
+
+ if (ss->grabIndex)
+ {
+ CompWindow *popup;
+ int i;
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ if (ss->windows[i] == w)
+ {
+ popup = findWindowAtScreen (w->screen, ss->popupWindow);
+ if (popup)
+ addWindowDamage (popup);
+
+ break;
+ }
+ }
+ }
+
+ UNWRAP (ss, w->screen, damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (ss, w->screen, damageWindowRect, switchDamageWindowRect);
+
+ return status;
+}
+
+static CompOption *
+switchGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ SWITCH_DISPLAY (display);
+
+ *count = NUM_OPTIONS (sd);
+ return sd->opt;
+}
+
+static Bool
+switchSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ SWITCH_DISPLAY (display);
+
+ o = compFindOption (sd->opt, NUM_OPTIONS (sd), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetDisplayOption (display, o, value);
+}
+
+static const CompMetadataOptionInfo switchDisplayOptionInfo[] = {
+ { "next_button", "button", 0, switchNext, switchTerminate },
+ { "next_key", "key", 0, switchNext, switchTerminate },
+ { "prev_button", "button", 0, switchPrev, switchTerminate },
+ { "prev_key", "key", 0, switchPrev, switchTerminate },
+ { "next_all_button", "button", 0, switchNextAll, switchTerminate },
+ { "next_all_key", "key", 0, switchNextAll, switchTerminate },
+ { "prev_all_button", "button", 0, switchPrevAll, switchTerminate },
+ { "prev_all_key", "key", 0, switchPrevAll, switchTerminate },
+ { "next_no_popup_button", "button", 0, switchNextNoPopup,
+ switchTerminate },
+ { "next_no_popup_key", "key", 0, switchNextNoPopup, switchTerminate },
+ { "prev_no_popup_button", "button", 0, switchPrevNoPopup,
+ switchTerminate },
+ { "prev_no_popup_key", "key", 0, switchPrevNoPopup, switchTerminate },
+ { "next_panel_button", "button", 0, switchNextPanel, switchTerminate },
+ { "next_panel_key", "key", 0, switchNextPanel, switchTerminate },
+ { "prev_panel_button", "button", 0, switchPrevPanel, switchTerminate },
+ { "prev_panel_key", "key", 0, switchPrevPanel, switchTerminate }
+};
+
+static Bool
+switchInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ SwitchDisplay *sd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ sd = malloc (sizeof (SwitchDisplay));
+ if (!sd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &switchMetadata,
+ switchDisplayOptionInfo,
+ sd->opt,
+ SWITCH_DISPLAY_OPTION_NUM))
+ {
+ free (sd);
+ return FALSE;
+ }
+
+ sd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (sd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, sd->opt, SWITCH_DISPLAY_OPTION_NUM);
+ free (sd);
+ return FALSE;
+ }
+
+ sd->selectWinAtom = XInternAtom (d->display,
+ DECOR_SWITCH_WINDOW_ATOM_NAME, 0);
+ sd->selectFgColorAtom =
+ XInternAtom (d->display, DECOR_SWITCH_FOREGROUND_COLOR_ATOM_NAME, 0);
+
+ WRAP (sd, d, handleEvent, switchHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = sd;
+
+ return TRUE;
+}
+
+static void
+switchFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ SWITCH_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, sd->screenPrivateIndex);
+
+ UNWRAP (sd, d, handleEvent);
+
+ compFiniDisplayOptions (d, sd->opt, SWITCH_DISPLAY_OPTION_NUM);
+
+ free (sd);
+}
+
+static const CompMetadataOptionInfo switchScreenOptionInfo[] = {
+ { "speed", "float", "<min>0.1</min>", 0, 0 },
+ { "timestep", "float", "<min>0.1</min>", 0, 0 },
+ { "window_match", "match", 0, 0, 0 },
+ { "mipmap", "bool", 0, 0, 0 },
+ { "saturation", "int", "<min>0</min><max>100</max>", 0, 0 },
+ { "brightness", "int", "<min>0</min><max>100</max>", 0, 0 },
+ { "opacity", "int", "<min>0</min><max>100</max>", 0, 0 },
+ { "bring_to_front", "bool", 0, 0, 0 },
+ { "zoom", "float", "<min>0</min>", 0, 0 },
+ { "icon", "bool", 0, 0, 0 },
+ { "minimized", "bool", 0, 0, 0 },
+ { "auto_rotate", "bool", 0, 0, 0 }
+};
+
+static Bool
+switchInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ SwitchScreen *ss;
+
+ SWITCH_DISPLAY (s->display);
+
+ ss = malloc (sizeof (SwitchScreen));
+ if (!ss)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &switchMetadata,
+ switchScreenOptionInfo,
+ ss->opt,
+ SWITCH_SCREEN_OPTION_NUM))
+ {
+ free (ss);
+ return FALSE;
+ }
+
+ ss->popupWindow = None;
+
+ ss->selectedWindow = None;
+ ss->zoomedWindow = None;
+
+ ss->lastActiveNum = 0;
+
+ ss->windows = 0;
+ ss->nWindows = 0;
+ ss->windowsSize = 0;
+
+ ss->pos = ss->move = 0;
+
+ ss->switching = FALSE;
+
+ ss->grabIndex = 0;
+
+ ss->zoom = ss->opt[SWITCH_SCREEN_OPTION_ZOOM].value.f / 30.0f;
+
+ ss->zooming = (ss->opt[SWITCH_SCREEN_OPTION_ZOOM].value.f > 0.05f);
+
+ ss->zoomMask = ~0;
+
+ ss->moreAdjust = 0;
+
+ ss->mVelocity = 0.0f;
+ ss->tVelocity = 0.0f;
+ ss->sVelocity = 0.0f;
+
+ ss->translate = 0.0f;
+ ss->sTranslate = 0.0f;
+
+ ss->selection = CurrentViewport;
+
+ ss->fgColor[0] = 0;
+ ss->fgColor[1] = 0;
+ ss->fgColor[2] = 0;
+ ss->fgColor[3] = 0xffff;
+
+ WRAP (ss, s, preparePaintScreen, switchPreparePaintScreen);
+ WRAP (ss, s, donePaintScreen, switchDonePaintScreen);
+ WRAP (ss, s, paintOutput, switchPaintOutput);
+ WRAP (ss, s, paintWindow, switchPaintWindow);
+ WRAP (ss, s, damageWindowRect, switchDamageWindowRect);
+
+ s->base.privates[sd->screenPrivateIndex].ptr = ss;
+
+ return TRUE;
+}
+
+static void
+switchFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ SWITCH_SCREEN (s);
+
+ UNWRAP (ss, s, preparePaintScreen);
+ UNWRAP (ss, s, donePaintScreen);
+ UNWRAP (ss, s, paintOutput);
+ UNWRAP (ss, s, paintWindow);
+ UNWRAP (ss, s, damageWindowRect);
+
+ if (ss->popupWindow)
+ XDestroyWindow (s->display->display, ss->popupWindow);
+
+ if (ss->windows)
+ free (ss->windows);
+
+ compFiniScreenOptions (s, ss->opt, SWITCH_SCREEN_OPTION_NUM);
+
+ free (ss);
+}
+
+static CompBool
+switchInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) switchInitDisplay,
+ (InitPluginObjectProc) switchInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+switchFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) switchFiniDisplay,
+ (FiniPluginObjectProc) switchFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+switchGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) switchGetDisplayOptions,
+ (GetPluginObjectOptionsProc) switchGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+switchSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) switchSetDisplayOption,
+ (SetPluginObjectOptionProc) switchSetScreenOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+switchInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&switchMetadata,
+ p->vTable->name,
+ switchDisplayOptionInfo,
+ SWITCH_DISPLAY_OPTION_NUM,
+ switchScreenOptionInfo,
+ SWITCH_SCREEN_OPTION_NUM))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&switchMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&switchMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+switchFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&switchMetadata);
+}
+
+static CompMetadata *
+switchGetMetadata (CompPlugin *plugin)
+{
+ return &switchMetadata;
+}
+
+CompPluginVTable switchVTable = {
+ "switcher",
+ switchGetMetadata,
+ switchInit,
+ switchFini,
+ switchInitObject,
+ switchFiniObject,
+ switchGetObjectOptions,
+ switchSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &switchVTable;
+}
diff --git a/plugins/video.c b/plugins/video.c
new file mode 100644
index 0000000..1f035c8
--- /dev/null
+++ b/plugins/video.c
@@ -0,0 +1,1329 @@
+/*
+ * Copyright © 2007 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+
+#include <compiz-core.h>
+#include <decoration.h>
+
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+
+/*
+ * compiz composited video
+ *
+ * supported image formats:
+ *
+ * RGB - packed RGB colorspace
+ *
+ * +---------------+
+ * | | width = image-width
+ * | | height = image-height
+ * | RGB |
+ * | | any pixmap depth with a matching
+ * | | fb-config can be used.
+ * +---------------+
+
+ * YV12 - planar YV12 colorspace
+ *
+ * +---------------+
+ * | | width = image-width
+ * | | height = image-height + image-height / 2
+ * | Y | depth = 8
+ * | |
+ * | | alpha only fb-config with pixmap support
+ * +-------+-------+ must be available.
+ * | | |
+ * | V | U |
+ * | | |
+ * +---------------+
+ *
+ */
+
+static CompMetadata videoMetadata;
+
+typedef struct _VideoTexture {
+ struct _VideoTexture *next;
+ int refCount;
+ Pixmap pixmap;
+ int width;
+ int height;
+ Damage damage;
+ CompTexture texture;
+} VideoTexture;
+
+typedef struct _VideoFunction {
+ struct _VideoFunction *next;
+
+ int handle;
+ int target;
+ int param;
+} VideoFunction;
+
+#define IMAGE_FORMAT_RGB 0
+#define IMAGE_FORMAT_YV12 1
+#define IMAGE_FORMAT_NUM 2
+
+static int displayPrivateIndex;
+
+#define VIDEO_DISPLAY_OPTION_YV12 0
+#define VIDEO_DISPLAY_OPTION_NUM 1
+
+typedef struct _VideoDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+ VideoTexture *textures;
+ Atom videoAtom;
+ Atom videoSupportedAtom;
+ Atom videoImageFormatAtom[IMAGE_FORMAT_NUM];
+
+ CompOption opt[VIDEO_DISPLAY_OPTION_NUM];
+} VideoDisplay;
+
+typedef struct _VideoScreen {
+ int windowPrivateIndex;
+
+ DrawWindowProc drawWindow;
+ DrawWindowTextureProc drawWindowTexture;
+ DamageWindowRectProc damageWindowRect;
+
+ WindowMoveNotifyProc windowMoveNotify;
+ WindowResizeNotifyProc windowResizeNotify;
+
+ VideoFunction *yv12Functions;
+
+ Bool imageFormat[IMAGE_FORMAT_NUM];
+} VideoScreen;
+
+typedef struct _VideoSource {
+ VideoTexture *texture;
+ int format;
+ decor_point_t p1;
+ decor_point_t p2;
+ Bool aspect;
+ float aspectRatio;
+ float panScan;
+ int width;
+ int height;
+} VideoSource;
+
+typedef struct _VideoContext {
+ VideoSource *source;
+ int width;
+ int height;
+ REGION box;
+ CompMatrix matrix;
+ Bool scaled;
+ float panX;
+ float panY;
+ Bool full;
+} VideoContext;
+
+typedef struct _VideoWindow {
+ VideoSource *source;
+ VideoContext *context;
+} VideoWindow;
+
+#define GET_VIDEO_DISPLAY(d) \
+ ((VideoDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define VIDEO_DISPLAY(d) \
+ VideoDisplay *vd = GET_VIDEO_DISPLAY (d)
+
+#define GET_VIDEO_SCREEN(s, vd) \
+ ((VideoScreen *) (s)->base.privates[(vd)->screenPrivateIndex].ptr)
+
+#define VIDEO_SCREEN(s) \
+ VideoScreen *vs = GET_VIDEO_SCREEN (s, GET_VIDEO_DISPLAY (s->display))
+
+#define GET_VIDEO_WINDOW(w, vs) \
+ ((VideoWindow *) (w)->base.privates[(vs)->windowPrivateIndex].ptr)
+
+#define VIDEO_WINDOW(w) \
+ VideoWindow *vw = GET_VIDEO_WINDOW (w, \
+ GET_VIDEO_SCREEN (w->screen, \
+ GET_VIDEO_DISPLAY (w->screen->display)))
+
+#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
+
+static void
+videoSetSupportedHint (CompScreen *s)
+{
+ Atom data[16];
+ int i, n = 0;
+
+ VIDEO_DISPLAY (s->display);
+ VIDEO_SCREEN (s);
+
+ for (i = 0; i < IMAGE_FORMAT_NUM; i++)
+ {
+ if (!vs->imageFormat[i])
+ continue;
+
+ if (i == 0 || vd->opt[i - 1].value.b)
+ data[n++] = vd->videoImageFormatAtom[i];
+ }
+
+ XChangeProperty (s->display->display, s->root,
+ vd->videoSupportedAtom, XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) data, n);
+}
+
+static CompOption *
+videoGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ VIDEO_DISPLAY (display);
+
+ *count = NUM_OPTIONS (vd);
+ return vd->opt;
+}
+
+static Bool
+videoSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ VIDEO_DISPLAY (display);
+
+ o = compFindOption (vd->opt, NUM_OPTIONS (vd), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case VIDEO_DISPLAY_OPTION_YV12:
+ if (compSetBoolOption (o, value))
+ {
+ CompScreen *s;
+
+ for (s = display->screens; s; s = s->next)
+ videoSetSupportedHint (s);
+ }
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static int
+getYV12FragmentFunction (CompScreen *s,
+ CompTexture *texture,
+ int param)
+{
+ VideoFunction *function;
+ CompFunctionData *data;
+ int target;
+
+ VIDEO_SCREEN (s);
+
+ if (texture->target == GL_TEXTURE_2D)
+ target = COMP_FETCH_TARGET_2D;
+ else
+ target = COMP_FETCH_TARGET_RECT;
+
+ for (function = vs->yv12Functions; function; function = function->next)
+ if (function->param == param && function->target == target)
+ return function->handle;
+
+ data = createFunctionData ();
+ if (data)
+ {
+ static char *temp[] = { "uv", "tmp", "position" };
+ int i, handle = 0;
+ char str[1024];
+ Bool ok = TRUE;
+
+ for (i = 0; i < sizeof (temp) / sizeof (temp[0]); i++)
+ ok &= addTempHeaderOpToFunctionData (data, temp[i]);
+
+ snprintf (str, 1024,
+ "MOV position, fragment.texcoord[0];"
+ "MAX position, position, program.env[%d];"
+ "MIN position, position, program.env[%d].zwww;",
+ param, param);
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ if (target == COMP_FETCH_TARGET_RECT)
+ {
+ snprintf (str, 1024,
+ "TEX output, position, texture[0], RECT;"
+ "MOV output, output.a;");
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ if (s->glxPixmapFBConfigs[8].yInverted)
+ {
+ snprintf (str, 1024,
+ "MAD position, position, 0.5, program.env[%d].xy;",
+ param + 1);
+ }
+ else
+ {
+ snprintf (str, 1024,
+ "ADD position, position, program.env[%d].xy;"
+ "MUL position, position, 0.5;",
+ param + 1);
+ }
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ snprintf (str, 1024,
+ "TEX tmp, position, texture[0], RECT;"
+ "MOV uv, tmp.a;"
+ "MAD output, output, 1.164, -0.073;"
+ "ADD position.x, position.x, program.env[%d].z;"
+ "TEX tmp, position, texture[0], RECT;"
+ "MOV uv.y, tmp.a;",
+ param + 1);
+ }
+ else
+ {
+ snprintf (str, 1024,
+ "TEX output, position, texture[0], 2D;"
+ "MOV output, output.a;");
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ if (s->glxPixmapFBConfigs[8].yInverted)
+ {
+ snprintf (str, 1024,
+ "MAD position, position, 0.5, { 0.0, %f };",
+ 2.0f / 3.0f);
+ }
+ else
+ {
+ snprintf (str, 1024,
+ "SUB position, position, { 0.0, %f };"
+ "MUL position, position, 0.5;",
+ 1.0f / 3.0f);
+ }
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ snprintf (str, 1024,
+ "TEX tmp, position, texture[0], 2D;"
+ "MOV uv, tmp.a;"
+ "MAD output, output, 1.164, -0.073;"
+ "ADD position.x, position.x, 0.5;"
+ "TEX tmp, position, texture[0], 2D;"
+ "MOV uv.y, tmp.a;");
+ }
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ snprintf (str, 1024,
+ "SUB uv, uv, { 0.5, 0.5 };"
+ "MAD output.xyz, { 1.596, -0.813, 0.0 }, uv.xxxw, output;"
+ "MAD output.xyz, { 0.0, -0.391, 2.018 }, uv.yyyw, output;"
+ "MOV output.a, 1.0;");
+
+ ok &= addDataOpToFunctionData (data, str);
+
+ if (!ok)
+ {
+ destroyFunctionData (data);
+ return 0;
+ }
+
+ function = malloc (sizeof (VideoFunction));
+ if (function)
+ {
+ handle = createFragmentFunction (s, "video", data);
+
+ function->handle = handle;
+ function->target = target;
+ function->param = param;
+
+ function->next = vs->yv12Functions;
+ vs->yv12Functions = function;
+ }
+
+ destroyFunctionData (data);
+
+ return handle;
+ }
+
+ return 0;
+}
+
+static void
+videoDestroyFragmentFunctions (CompScreen *s,
+ VideoFunction **videoFunctions)
+{
+ VideoFunction *function, *next;
+
+ function = *videoFunctions;
+ while (function)
+ {
+ destroyFragmentFunction (s, function->handle);
+
+ next = function->next;
+ free (function);
+ function = next;
+ }
+
+ *videoFunctions = NULL;
+}
+
+static void
+videoDrawWindowTexture (CompWindow *w,
+ CompTexture *texture,
+ const FragmentAttrib *attrib,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+
+ VIDEO_SCREEN (s);
+ VIDEO_WINDOW (w);
+
+ if (vw->context)
+ {
+ VideoSource *src = vw->context->source;
+
+ if (src->format == IMAGE_FORMAT_YV12 &&
+ &src->texture->texture == texture)
+ {
+ FragmentAttrib fa = *attrib;
+ int param, function;
+
+ param = allocFragmentParameters (&fa, 2);
+
+ function = getYV12FragmentFunction (s, texture, param);
+ if (function)
+ {
+ float minX, minY, maxX, maxY, y1, y2;
+
+ addFragmentFunction (&fa, function);
+
+ minX = COMP_TEX_COORD_X (&texture->matrix, 1.0f);
+ maxX = COMP_TEX_COORD_X (&texture->matrix, src->width - 1.0f);
+
+ y1 = COMP_TEX_COORD_Y (&texture->matrix, 1.0f);
+ y2 = COMP_TEX_COORD_Y (&texture->matrix, src->height - 1.0f);
+
+ minY = MIN (y1, y2);
+ maxY = MAX (y1, y2);
+
+ (*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, param,
+ minX, minY, maxX, maxY);
+
+ /* need to provide plane offsets when texture coordinates
+ are not normalized */
+ if (texture->target != GL_TEXTURE_2D)
+ {
+ float offsetX, offsetY;
+
+ offsetX = COMP_TEX_COORD_X (&texture->matrix,
+ src->width / 2);
+
+ if (s->glxPixmapFBConfigs[8].yInverted)
+ offsetY = COMP_TEX_COORD_Y (&texture->matrix,
+ src->height);
+ else
+ offsetY = COMP_TEX_COORD_Y (&texture->matrix,
+ -src->height / 2);
+
+ (*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
+ param + 1,
+ 0.0f, offsetY, offsetX, 0.0f);
+ }
+ }
+
+ UNWRAP (vs, s, drawWindowTexture);
+ (*s->drawWindowTexture) (w, texture, &fa, mask);
+ WRAP (vs, s, drawWindowTexture, videoDrawWindowTexture);
+ }
+ else
+ {
+ if (!(mask & PAINT_WINDOW_BLEND_MASK))
+ {
+ /* we don't have to draw client window texture when
+ video cover the full window and blending isn't used */
+ if (vw->context->full && texture == w->texture)
+ return;
+ }
+
+ UNWRAP (vs, s, drawWindowTexture);
+ (*s->drawWindowTexture) (w, texture, attrib, mask);
+ WRAP (vs, s, drawWindowTexture, videoDrawWindowTexture);
+ }
+ }
+ else
+ {
+ UNWRAP (vs, s, drawWindowTexture);
+ (*s->drawWindowTexture) (w, texture, attrib, mask);
+ WRAP (vs, s, drawWindowTexture, videoDrawWindowTexture);
+ }
+}
+
+static Bool
+videoDrawWindow (CompWindow *w,
+ const CompTransform *transform,
+ const FragmentAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ VIDEO_SCREEN (w->screen);
+
+ UNWRAP (vs, w->screen, drawWindow);
+ status = (*w->screen->drawWindow) (w, transform, attrib, region, mask);
+ WRAP (vs, w->screen, drawWindow, videoDrawWindow);
+
+ if (status)
+ {
+ VIDEO_WINDOW (w);
+
+ if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
+ region = &infiniteRegion;
+
+ if (vw->context && region->numRects)
+ {
+ CompTexture *texture = &vw->context->source->texture->texture;
+ int saveFilter;
+
+ w->vCount = w->indexCount = 0;
+
+ if (vw->context->box.extents.x1 < vw->context->box.extents.x2 &&
+ vw->context->box.extents.y1 < vw->context->box.extents.y2)
+ {
+ (*w->screen->addWindowGeometry) (w,
+ &vw->context->matrix, 1,
+ &vw->context->box,
+ region);
+ }
+
+ if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
+ mask |= PAINT_WINDOW_BLEND_MASK;
+
+ saveFilter = w->screen->filter[NOTHING_TRANS_FILTER];
+
+ if (vw->context->scaled)
+ w->screen->filter[NOTHING_TRANS_FILTER] =
+ COMP_TEXTURE_FILTER_GOOD;
+
+ if (w->vCount)
+ (*w->screen->drawWindowTexture) (w, texture, attrib, mask);
+
+ w->screen->filter[NOTHING_TRANS_FILTER] = saveFilter;
+ }
+ }
+
+ return status;
+}
+
+static VideoTexture *
+videoGetTexture (CompScreen *screen,
+ Pixmap pixmap)
+{
+ VideoTexture *texture;
+ unsigned int width, height, depth, ui;
+ Window root;
+ int i;
+
+ VIDEO_DISPLAY (screen->display);
+
+ for (texture = vd->textures; texture; texture = texture->next)
+ {
+ if (texture->pixmap == pixmap)
+ {
+ texture->refCount++;
+ return texture;
+ }
+ }
+
+ texture = malloc (sizeof (VideoTexture));
+ if (!texture)
+ return NULL;
+
+ initTexture (screen, &texture->texture);
+
+ if (!XGetGeometry (screen->display->display, pixmap, &root,
+ &i, &i, &width, &height, &ui, &depth))
+ {
+ finiTexture (screen, &texture->texture);
+ free (texture);
+ return NULL;
+ }
+
+ if (!bindPixmapToTexture (screen, &texture->texture, pixmap,
+ width, height, depth))
+ {
+ finiTexture (screen, &texture->texture);
+ free (texture);
+ return NULL;
+ }
+
+ texture->damage = XDamageCreate (screen->display->display, pixmap,
+ XDamageReportRawRectangles);
+
+ texture->refCount = 1;
+ texture->pixmap = pixmap;
+ texture->width = width;
+ texture->height = height;
+ texture->next = vd->textures;
+
+ vd->textures = texture;
+
+ return texture;
+}
+
+static void
+videoReleaseTexture (CompScreen *screen,
+ VideoTexture *texture)
+{
+ VIDEO_DISPLAY (screen->display);
+
+ texture->refCount--;
+ if (texture->refCount)
+ return;
+
+ if (texture == vd->textures)
+ {
+ vd->textures = texture->next;
+ }
+ else
+ {
+ VideoTexture *t;
+
+ for (t = vd->textures; t; t = t->next)
+ {
+ if (t->next == texture)
+ {
+ t->next = texture->next;
+ break;
+ }
+ }
+ }
+
+ finiTexture (screen, &texture->texture);
+ free (texture);
+}
+
+static void
+updateWindowVideoMatrix (CompWindow *w)
+{
+ VIDEO_WINDOW (w);
+
+ if (!vw->context)
+ return;
+
+ vw->context->matrix = vw->context->source->texture->texture.matrix;
+
+ vw->context->matrix.yy /= (float)
+ vw->context->height / vw->context->source->height;
+
+ if (vw->context->width != vw->context->source->width ||
+ vw->context->height != vw->context->source->height)
+ {
+ vw->context->matrix.xx /= (float)
+ vw->context->width / vw->context->source->width;
+
+ vw->context->scaled = TRUE;
+ }
+ else
+ {
+ vw->context->scaled = FALSE;
+ }
+
+ vw->context->matrix.x0 -=
+ (vw->context->box.extents.x1 * vw->context->matrix.xx);
+ vw->context->matrix.y0 -=
+ (vw->context->box.extents.y1 * vw->context->matrix.yy);
+
+ vw->context->matrix.x0 += (vw->context->panX * vw->context->matrix.xx);
+ vw->context->matrix.y0 += (vw->context->panY * vw->context->matrix.yy);
+}
+
+static void
+updateWindowVideoContext (CompWindow *w,
+ VideoSource *source)
+{
+ int x1, y1, x2, y2;
+
+ VIDEO_WINDOW (w);
+
+ if (!vw->context)
+ {
+ vw->context = malloc (sizeof (VideoContext));
+ if (!vw->context)
+ return;
+ }
+
+ vw->context->source = source;
+
+ vw->context->box.rects = &vw->context->box.extents;
+ vw->context->box.numRects = 1;
+
+ decor_apply_gravity (source->p1.gravity,
+ source->p1.x, source->p1.y,
+ w->width, w->height,
+ &x1, &y1);
+
+ decor_apply_gravity (source->p2.gravity,
+ source->p2.x, source->p2.y,
+ w->width, w->height,
+ &x2, &y2);
+
+ x1 = MAX (x1, 0);
+ y1 = MAX (y1, 0);
+ x2 = MIN (x2, w->width);
+ y2 = MIN (y2, w->height);
+
+ vw->context->width = x2 - x1;
+ vw->context->height = y2 - y1;
+
+ vw->context->panX = 0.0f;
+ vw->context->panY = 0.0f;
+
+ if (source->aspect)
+ {
+ float aspect, width, height, v;
+
+ width = vw->context->width;
+ height = vw->context->height;
+
+ aspect = width / height;
+
+ if (aspect < source->aspectRatio)
+ {
+ v = (width + width * source->panScan) / source->aspectRatio;
+ height = MIN (vw->context->height, v);
+ width = height * source->aspectRatio;
+ }
+ else
+ {
+ v = (height + height * source->panScan) * source->aspectRatio;
+ width = MIN (vw->context->width, v);
+ height = width / source->aspectRatio;
+ }
+
+ x1 = (vw->context->width / 2.0f) - (width / 2.0f);
+ y1 = (vw->context->height / 2.0f) - (height / 2.0f);
+ x2 = ((vw->context->width / 2.0f) + (width / 2.0f) + 0.5f);
+ y2 = ((vw->context->height / 2.0f) + (height / 2.0f) + 0.5f);
+
+ vw->context->width = x2 - x1;
+ vw->context->height = y2 - y1;
+
+ if (x1 < 0)
+ vw->context->panX = -x1;
+
+ if (y1 < 0)
+ vw->context->panY = -y1;
+
+ x1 = MAX (x1, 0);
+ y1 = MAX (y1, 0);
+ x2 = MIN (x2, w->width);
+ y2 = MIN (y2, w->height);
+ }
+
+ if (x1 == 0 &&
+ y1 == 0 &&
+ x2 == w->width &&
+ y2 == w->height)
+ {
+ vw->context->full = TRUE;
+ }
+ else
+ {
+ vw->context->full = FALSE;
+ }
+
+ vw->context->box.extents.x1 = x1;
+ vw->context->box.extents.y1 = y1;
+ vw->context->box.extents.x2 = x2;
+ vw->context->box.extents.y2 = y2;
+
+ vw->context->box.extents.x1 += w->attrib.x;
+ vw->context->box.extents.y1 += w->attrib.y;
+ vw->context->box.extents.x2 += w->attrib.x;
+ vw->context->box.extents.y2 += w->attrib.y;
+
+ updateWindowVideoMatrix (w);
+}
+
+static void
+videoWindowUpdate (CompWindow *w)
+{
+ Atom actual;
+ int result, format, i;
+ unsigned long n, left;
+ unsigned char *propData;
+ VideoTexture *texture = NULL;
+ Pixmap pixmap = None;
+ Atom imageFormat = 0;
+ decor_point_t p[2];
+ int aspectX = 0;
+ int aspectY = 0;
+ int panScan = 0;
+ int width = 0;
+ int height = 0;
+
+ VIDEO_DISPLAY (w->screen->display);
+ VIDEO_SCREEN (w->screen);
+ VIDEO_WINDOW (w);
+
+ memset (p, 0, sizeof (p));
+
+ result = XGetWindowProperty (w->screen->display->display, w->id,
+ vd->videoAtom, 0L, 13L, FALSE,
+ XA_INTEGER, &actual, &format,
+ &n, &left, &propData);
+
+ if (result == Success && propData)
+ {
+ if (n == 13)
+ {
+ long *data = (long *) propData;
+
+ pixmap = *data++;
+ imageFormat = *data++;
+
+ width = *data++;
+ height = *data++;
+
+ aspectX = *data++;
+ aspectY = *data++;
+ panScan = *data++;
+
+ p[0].gravity = *data++;
+ p[0].x = *data++;
+ p[0].y = *data++;
+ p[1].gravity = *data++;
+ p[1].x = *data++;
+ p[1].y = *data++;
+ }
+
+ XFree (propData);
+ }
+
+ for (i = 0; i < IMAGE_FORMAT_NUM; i++)
+ if (vd->videoImageFormatAtom[i] == imageFormat)
+ break;
+
+ if (i < IMAGE_FORMAT_NUM)
+ {
+ if (!vs->imageFormat[i])
+ {
+ compLogMessage ("video", CompLogLevelWarn,
+ "Image format not supported");
+ i = IMAGE_FORMAT_NUM;
+ }
+ }
+
+ if (i < IMAGE_FORMAT_NUM)
+ {
+ texture = videoGetTexture (w->screen, pixmap);
+ if (!texture)
+ {
+ compLogMessage ("video", CompLogLevelWarn,
+ "Bad pixmap 0x%x", (int) pixmap);
+ }
+ }
+
+ if (vw->source)
+ {
+ videoReleaseTexture (w->screen, vw->source->texture);
+ }
+ else
+ {
+ vw->source = malloc (sizeof (VideoSource));
+ }
+
+ if (texture && vw->source)
+ {
+ vw->source->texture = texture;
+ vw->source->format = i;
+ vw->source->p1 = p[0];
+ vw->source->p2 = p[1];
+ vw->source->width = width;
+ vw->source->height = height;
+ vw->source->aspect = aspectX && aspectY;
+ vw->source->panScan = panScan / 65536.0f;
+
+ if (vw->source->aspect)
+ vw->source->aspectRatio = (float) aspectX / aspectY;
+
+ updateWindowVideoContext (w, vw->source);
+ }
+ else
+ {
+ if (texture)
+ videoReleaseTexture (w->screen, texture);
+
+ if (vw->source)
+ {
+ free (vw->source);
+ vw->source = NULL;
+ }
+
+ if (vw->context)
+ {
+ free (vw->context);
+ vw->context = NULL;
+ }
+ }
+}
+
+static void
+videoHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompWindow *w;
+
+ VIDEO_DISPLAY (d);
+
+ switch (event->type) {
+ case PropertyNotify:
+ if (event->xproperty.atom == vd->videoAtom)
+ {
+ w = findWindowAtDisplay (d, event->xproperty.window);
+ if (w)
+ videoWindowUpdate (w);
+ }
+ break;
+ default:
+ if (event->type == d->damageEvent + XDamageNotify)
+ {
+ XDamageNotifyEvent *de = (XDamageNotifyEvent *) event;
+ VideoTexture *t;
+
+ for (t = vd->textures; t; t = t->next)
+ {
+ if (t->pixmap == de->drawable)
+ {
+ VideoWindow *vw;
+ VideoScreen *vs;
+ CompScreen *s;
+ BoxRec box;
+ int bw;
+
+ t->texture.oldMipmaps = TRUE;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ vs = GET_VIDEO_SCREEN (s, vd);
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->shaded || w->mapNum)
+ {
+ vw = GET_VIDEO_WINDOW (w, vs);
+
+ if (vw->context &&
+ vw->context->source->texture == t)
+ {
+ box = vw->context->box.extents;
+
+ bw = w->attrib.border_width;
+
+ box.x1 -= w->attrib.x + bw;
+ box.y1 -= w->attrib.y + bw;
+ box.x2 -= w->attrib.x + bw;
+ box.y2 -= w->attrib.y + bw;
+
+ addWindowDamageRect (w, &box);
+ }
+ }
+ }
+ }
+ return;
+ }
+ }
+ }
+ break;
+ }
+
+ UNWRAP (vd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (vd, d, handleEvent, videoHandleEvent);
+}
+
+static Bool
+videoDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status;
+
+ VIDEO_SCREEN (w->screen);
+
+ if (initial)
+ videoWindowUpdate (w);
+
+ UNWRAP (vs, w->screen, damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (vs, w->screen, damageWindowRect, videoDamageWindowRect);
+
+ return status;
+}
+
+static void
+videoWindowMoveNotify (CompWindow *w,
+ int dx,
+ int dy,
+ Bool immediate)
+{
+ VIDEO_SCREEN (w->screen);
+ VIDEO_WINDOW (w);
+
+ if (vw->context)
+ {
+ vw->context->box.extents.x1 += dx;
+ vw->context->box.extents.y1 += dy;
+ vw->context->box.extents.x2 += dx;
+ vw->context->box.extents.y2 += dy;
+
+ updateWindowVideoMatrix (w);
+ }
+
+ UNWRAP (vs, w->screen, windowMoveNotify);
+ (*w->screen->windowMoveNotify) (w, dx, dy, immediate);
+ WRAP (vs, w->screen, windowMoveNotify, videoWindowMoveNotify);
+}
+
+static void
+videoWindowResizeNotify (CompWindow *w,
+ int dx,
+ int dy,
+ int dwidth,
+ int dheight)
+{
+ VIDEO_SCREEN (w->screen);
+ VIDEO_WINDOW (w);
+
+ if (vw->source)
+ updateWindowVideoContext (w, vw->source);
+
+ UNWRAP (vs, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
+ WRAP (vs, w->screen, windowResizeNotify, videoWindowResizeNotify);
+}
+
+static const CompMetadataOptionInfo videoDisplayOptionInfo[] = {
+ { "yv12", "bool", 0, 0, 0 }
+};
+
+static Bool
+videoInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ VideoDisplay *vd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ vd = malloc (sizeof (VideoDisplay));
+ if (!vd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &videoMetadata,
+ videoDisplayOptionInfo,
+ vd->opt,
+ VIDEO_DISPLAY_OPTION_NUM))
+ {
+ free (vd);
+ return FALSE;
+ }
+
+ vd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (vd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, vd->opt, VIDEO_DISPLAY_OPTION_NUM);
+ free (vd);
+ return FALSE;
+ }
+
+ vd->textures = 0;
+
+ vd->videoAtom =
+ XInternAtom (d->display, "_COMPIZ_VIDEO", 0);
+ vd->videoSupportedAtom =
+ XInternAtom (d->display, "_COMPIZ_VIDEO_SUPPORTED", 0);
+
+ vd->videoImageFormatAtom[IMAGE_FORMAT_RGB] =
+ XInternAtom (d->display, "_COMPIZ_VIDEO_IMAGE_FORMAT_RGB", 0);
+ vd->videoImageFormatAtom[IMAGE_FORMAT_YV12] =
+ XInternAtom (d->display, "_COMPIZ_VIDEO_IMAGE_FORMAT_YV12", 0);
+
+ WRAP (vd, d, handleEvent, videoHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = vd;
+
+ return TRUE;
+}
+
+static void
+videoFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ VIDEO_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, vd->screenPrivateIndex);
+
+ UNWRAP (vd, d, handleEvent);
+
+ compFiniDisplayOptions (d, vd->opt, VIDEO_DISPLAY_OPTION_NUM);
+
+ free (vd);
+}
+
+static Bool
+videoInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ VideoScreen *vs;
+
+ VIDEO_DISPLAY (s->display);
+
+ vs = malloc (sizeof (VideoScreen));
+ if (!vs)
+ return FALSE;
+
+ vs->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (vs->windowPrivateIndex < 0)
+ {
+ free (vs);
+ return FALSE;
+ }
+
+ vs->yv12Functions = NULL;
+
+ memset (vs->imageFormat, 0, sizeof (vs->imageFormat));
+
+ vs->imageFormat[IMAGE_FORMAT_RGB] = TRUE;
+ if (s->fragmentProgram)
+ {
+ if (s->glxPixmapFBConfigs[8].fbConfig)
+ {
+ vs->imageFormat[IMAGE_FORMAT_YV12] = TRUE;
+ }
+ else
+ {
+ compLogMessage ("video", CompLogLevelWarn,
+ "No 8 bit GLX pixmap format, "
+ "disabling YV12 image format");
+ }
+ }
+
+ WRAP (vs, s, drawWindow, videoDrawWindow);
+ WRAP (vs, s, drawWindowTexture, videoDrawWindowTexture);
+ WRAP (vs, s, damageWindowRect, videoDamageWindowRect);
+ WRAP (vs, s, windowMoveNotify, videoWindowMoveNotify);
+ WRAP (vs, s, windowResizeNotify, videoWindowResizeNotify);
+
+ s->base.privates[vd->screenPrivateIndex].ptr = vs;
+
+ videoSetSupportedHint (s);
+
+ return TRUE;
+}
+
+static void
+videoFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ VIDEO_DISPLAY (s->display);
+ VIDEO_SCREEN (s);
+
+ freeWindowPrivateIndex (s, vs->windowPrivateIndex);
+
+ XDeleteProperty (s->display->display, s->root, vd->videoSupportedAtom);
+
+ videoDestroyFragmentFunctions (s, &vs->yv12Functions);
+
+ UNWRAP (vs, s, drawWindow);
+ UNWRAP (vs, s, drawWindowTexture);
+ UNWRAP (vs, s, damageWindowRect);
+ UNWRAP (vs, s, windowMoveNotify);
+ UNWRAP (vs, s, windowResizeNotify);
+
+ free (vs);
+}
+
+static Bool
+videoInitWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ VideoWindow *vw;
+
+ VIDEO_SCREEN (w->screen);
+
+ vw = malloc (sizeof (VideoWindow));
+ if (!vw)
+ return FALSE;
+
+ vw->source = NULL;
+ vw->context = NULL;
+
+ w->base.privates[vs->windowPrivateIndex].ptr = vw;
+
+ if (w->shaded || w->attrib.map_state == IsViewable)
+ videoWindowUpdate (w);
+
+ return TRUE;
+}
+
+static void
+videoFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ VIDEO_WINDOW (w);
+
+ if (vw->source)
+ {
+ videoReleaseTexture (w->screen, vw->source->texture);
+ free (vw->source);
+ }
+
+ if (vw->context)
+ free (vw->context);
+
+ free (vw);
+}
+
+static CompBool
+videoInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) videoInitDisplay,
+ (InitPluginObjectProc) videoInitScreen,
+ (InitPluginObjectProc) videoInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+videoFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) videoFiniDisplay,
+ (FiniPluginObjectProc) videoFiniScreen,
+ (FiniPluginObjectProc) videoFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+videoGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) videoGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+videoSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) videoSetDisplayOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+videoInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&videoMetadata,
+ p->vTable->name,
+ videoDisplayOptionInfo,
+ VIDEO_DISPLAY_OPTION_NUM,
+ 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&videoMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&videoMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+videoFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&videoMetadata);
+}
+
+static CompMetadata *
+videoGetMetadata (CompPlugin *plugin)
+{
+ return &videoMetadata;
+}
+
+static CompPluginVTable videoVTable = {
+ "video",
+ videoGetMetadata,
+ videoInit,
+ videoFini,
+ videoInitObject,
+ videoFiniObject,
+ videoGetObjectOptions,
+ videoSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &videoVTable;
+}
diff --git a/plugins/water.c b/plugins/water.c
new file mode 100644
index 0000000..6b39d70
--- /dev/null
+++ b/plugins/water.c
@@ -0,0 +1,1824 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "../config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include <compiz-core.h>
+
+#define TEXTURE_SIZE 256
+
+#define K 0.1964f
+
+#define TEXTURE_NUM 3
+
+typedef struct _WaterFunction {
+ struct _WaterFunction *next;
+
+ int handle;
+ int target;
+ int param;
+ int unit;
+} WaterFunction;
+
+#define TINDEX(ws, i) (((ws)->tIndex + (i)) % TEXTURE_NUM)
+
+#define CLAMP(v, min, max) \
+ if ((v) > (max)) \
+ (v) = (max); \
+ else if ((v) < (min)) \
+ (v) = (min)
+
+#define WATER_INITIATE_MODIFIERS_DEFAULT (ControlMask | CompSuperMask)
+
+static CompMetadata waterMetadata;
+
+static int displayPrivateIndex;
+
+static int waterLastPointerX = 0;
+static int waterLastPointerY = 0;
+
+#define WATER_DISPLAY_OPTION_INITIATE_KEY 0
+#define WATER_DISPLAY_OPTION_TOGGLE_RAIN_KEY 1
+#define WATER_DISPLAY_OPTION_TOGGLE_WIPER_KEY 2
+#define WATER_DISPLAY_OPTION_OFFSET_SCALE 3
+#define WATER_DISPLAY_OPTION_RAIN_DELAY 4
+#define WATER_DISPLAY_OPTION_TITLE_WAVE 5
+#define WATER_DISPLAY_OPTION_POINT 6
+#define WATER_DISPLAY_OPTION_LINE 7
+#define WATER_DISPLAY_OPTION_NUM 8
+
+typedef struct _WaterDisplay {
+ int screenPrivateIndex;
+
+ CompOption opt[WATER_DISPLAY_OPTION_NUM];
+
+ HandleEventProc handleEvent;
+
+ float offsetScale;
+} WaterDisplay;
+
+typedef struct _WaterScreen {
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ DrawWindowTextureProc drawWindowTexture;
+
+ int grabIndex;
+ int width, height;
+
+ GLuint program;
+ GLuint texture[TEXTURE_NUM];
+
+ int tIndex;
+ GLenum target;
+ GLfloat tx, ty;
+
+ int count;
+
+ GLuint fbo;
+ GLint fboStatus;
+
+ void *data;
+ float *d0;
+ float *d1;
+ unsigned char *t0;
+
+ CompTimeoutHandle rainHandle;
+ CompTimeoutHandle wiperHandle;
+
+ float wiperAngle;
+ float wiperSpeed;
+
+ WaterFunction *bumpMapFunctions;
+} WaterScreen;
+
+#define GET_WATER_DISPLAY(d) \
+ ((WaterDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define WATER_DISPLAY(d) \
+ WaterDisplay *wd = GET_WATER_DISPLAY (d)
+
+#define GET_WATER_SCREEN(s, wd) \
+ ((WaterScreen *) (s)->base.privates[(wd)->screenPrivateIndex].ptr)
+
+#define WATER_SCREEN(s) \
+ WaterScreen *ws = GET_WATER_SCREEN (s, GET_WATER_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static Bool
+waterRainTimeout (void *closure);
+
+static Bool
+waterWiperTimeout (void *closure);
+
+static const char *waterFpString =
+ "!!ARBfp1.0"
+
+ "PARAM param = program.local[0];"
+ "ATTRIB t11 = fragment.texcoord[0];"
+
+ "TEMP t01, t21, t10, t12;"
+ "TEMP c11, c01, c21, c10, c12;"
+ "TEMP prev, v, temp, accel;"
+
+ "TEX prev, t11, texture[0], %s;"
+ "TEX c11, t11, texture[1], %s;"
+
+ /* sample offsets */
+ "ADD t01, t11, { - %f, 0.0, 0.0, 0.0 };"
+ "ADD t21, t11, { %f, 0.0, 0.0, 0.0 };"
+ "ADD t10, t11, { 0.0, - %f, 0.0, 0.0 };"
+ "ADD t12, t11, { 0.0, %f, 0.0, 0.0 };"
+
+ /* fetch nesseccary samples */
+ "TEX c01, t01, texture[1], %s;"
+ "TEX c21, t21, texture[1], %s;"
+ "TEX c10, t10, texture[1], %s;"
+ "TEX c12, t12, texture[1], %s;"
+
+ /* x/y normals from height */
+ "MOV v, { 0.0, 0.0, 0.75, 0.0 };"
+ "SUB v.x, c12.w, c10.w;"
+ "SUB v.y, c01.w, c21.w;"
+
+ /* bumpiness */
+ "MUL v, v, 1.5;"
+
+ /* normalize */
+ "MAD temp, v.x, v.x, 1.0;"
+ "MAD temp, v.y, v.y, temp;"
+ "RSQ temp, temp.x;"
+ "MUL v, v, temp;"
+
+ /* add scale and bias to normal */
+ "MAD v, v, 0.5, 0.5;"
+
+ /* done with computing the normal, continue with computing the next
+ height value */
+ "ADD accel, c10, c12;"
+ "ADD accel, c01, accel;"
+ "ADD accel, c21, accel;"
+ "MAD accel, -4.0, c11, accel;"
+
+ /* store new height in alpha component */
+ "MAD v.w, 2.0, c11, -prev.w;"
+ "MAD v.w, accel, param.x, v.w;"
+
+ /* fade out height */
+ "MUL v.w, v.w, param.y;"
+
+ "MOV result.color, v;"
+
+ "END";
+
+static int
+loadFragmentProgram (CompScreen *s,
+ GLuint *program,
+ const char *string)
+{
+ GLint errorPos;
+
+ /* clear errors */
+ glGetError ();
+
+ if (!*program)
+ (*s->genPrograms) (1, program);
+
+ (*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, *program);
+ (*s->programString) (GL_FRAGMENT_PROGRAM_ARB,
+ GL_PROGRAM_FORMAT_ASCII_ARB,
+ strlen (string), string);
+
+ glGetIntegerv (GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
+ if (glGetError () != GL_NO_ERROR || errorPos != -1)
+ {
+ compLogMessage ("water", CompLogLevelError,
+ "failed to load bump map program");
+
+ (*s->deletePrograms) (1, program);
+ *program = 0;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+loadWaterProgram (CompScreen *s)
+{
+ char buffer[1024];
+
+ WATER_SCREEN (s);
+
+ if (ws->target == GL_TEXTURE_2D)
+ sprintf (buffer, waterFpString,
+ "2D", "2D",
+ 1.0f / ws->width, 1.0f / ws->width,
+ 1.0f / ws->height, 1.0f / ws->height,
+ "2D", "2D", "2D", "2D");
+ else
+ sprintf (buffer, waterFpString,
+ "RECT", "RECT",
+ 1.0f, 1.0f, 1.0f, 1.0f,
+ "RECT", "RECT", "RECT", "RECT");
+
+ return loadFragmentProgram (s, &ws->program, buffer);
+}
+
+static int
+getBumpMapFragmentFunction (CompScreen *s,
+ CompTexture *texture,
+ int unit,
+ int param)
+{
+ WaterFunction *function;
+ CompFunctionData *data;
+ int target;
+
+ WATER_SCREEN (s);
+
+ if (texture->target == GL_TEXTURE_2D)
+ target = COMP_FETCH_TARGET_2D;
+ else
+ target = COMP_FETCH_TARGET_RECT;
+
+ for (function = ws->bumpMapFunctions; function; function = function->next)
+ {
+ if (function->param == param &&
+ function->unit == unit &&
+ function->target == target)
+ return function->handle;
+ }
+
+ data = createFunctionData ();
+ if (data)
+ {
+ static char *temp[] = { "normal", "temp", "total", "bump", "offset" };
+ int i, handle = 0;
+ char str[1024];
+
+ for (i = 0; i < sizeof (temp) / sizeof (temp[0]); i++)
+ {
+ if (!addTempHeaderOpToFunctionData (data, temp[i]))
+ {
+ destroyFunctionData (data);
+ return 0;
+ }
+ }
+
+ snprintf (str, 1024,
+
+ /* get normal from normal map */
+ "TEX normal, fragment.texcoord[%d], texture[%d], %s;"
+
+ /* save height */
+ "MOV offset, normal;"
+
+ /* remove scale and bias from normal */
+ "MAD normal, normal, 2.0, -1.0;"
+
+ /* normalize the normal map */
+ "DP3 temp, normal, normal;"
+ "RSQ temp, temp.x;"
+ "MUL normal, normal, temp;"
+
+ /* scale down normal by height and constant and use as
+ offset in texture */
+ "MUL offset, normal, offset.w;"
+ "MUL offset, offset, program.env[%d];",
+
+ unit, unit,
+ (ws->target == GL_TEXTURE_2D) ? "2D" : "RECT",
+ param);
+
+ if (!addDataOpToFunctionData (data, str))
+ {
+ destroyFunctionData (data);
+ return 0;
+ }
+
+ if (!addFetchOpToFunctionData (data, "output", "offset.yxzz", target))
+ {
+ destroyFunctionData (data);
+ return 0;
+ }
+
+ snprintf (str, 1024,
+
+ /* normal dot lightdir, this should eventually be
+ changed to a real light vector */
+ "DP3 bump, normal, { 0.707, 0.707, 0.0, 0.0 };"
+ "MUL bump, bump, state.light[0].diffuse;");
+
+ if (!addDataOpToFunctionData (data, str))
+ {
+ destroyFunctionData (data);
+ return 0;
+ }
+
+ if (!addColorOpToFunctionData (data, "output", "output"))
+ {
+ destroyFunctionData (data);
+ return 0;
+ }
+
+ snprintf (str, 1024,
+
+ /* diffuse per-vertex lighting, opacity and brightness
+ and add lightsource bump color */
+ "ADD output, output, bump;");
+
+ if (!addDataOpToFunctionData (data, str))
+ {
+ destroyFunctionData (data);
+ return 0;
+ }
+
+ function = malloc (sizeof (WaterFunction));
+ if (function)
+ {
+ handle = createFragmentFunction (s, "water", data);
+
+ function->handle = handle;
+ function->target = target;
+ function->param = param;
+ function->unit = unit;
+
+ function->next = ws->bumpMapFunctions;
+ ws->bumpMapFunctions = function;
+ }
+
+ destroyFunctionData (data);
+
+ return handle;
+ }
+
+ return 0;
+}
+
+static void
+allocTexture (CompScreen *s,
+ int index)
+{
+ WATER_SCREEN (s);
+
+ glGenTextures (1, &ws->texture[index]);
+ glBindTexture (ws->target, ws->texture[index]);
+
+ glTexParameteri (ws->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (ws->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri (ws->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri (ws->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D (ws->target,
+ 0,
+ GL_RGBA,
+ ws->width,
+ ws->height,
+ 0,
+ GL_BGRA,
+
+#if IMAGE_BYTE_ORDER == MSBFirst
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+#else
+ GL_UNSIGNED_BYTE,
+#endif
+
+ ws->t0);
+
+ glBindTexture (ws->target, 0);
+}
+
+static int
+fboPrologue (CompScreen *s,
+ int tIndex)
+{
+ WATER_SCREEN (s);
+
+ if (!ws->fbo)
+ return 0;
+
+ if (!ws->texture[tIndex])
+ allocTexture (s, tIndex);
+
+ (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, ws->fbo);
+
+ (*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT,
+ ws->target, ws->texture[tIndex],
+ 0);
+
+ glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
+ glReadBuffer (GL_COLOR_ATTACHMENT0_EXT);
+
+ /* check status the first time */
+ if (!ws->fboStatus)
+ {
+ ws->fboStatus = (*s->checkFramebufferStatus) (GL_FRAMEBUFFER_EXT);
+ if (ws->fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ compLogMessage ("water", CompLogLevelError,
+ "framebuffer incomplete");
+
+ (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0);
+ (*s->deleteFramebuffers) (1, &ws->fbo);
+
+ glDrawBuffer (GL_BACK);
+ glReadBuffer (GL_BACK);
+
+ ws->fbo = 0;
+
+ return 0;
+ }
+ }
+
+ glViewport (0, 0, ws->width, ws->height);
+ glMatrixMode (GL_PROJECTION);
+ glPushMatrix ();
+ glLoadIdentity ();
+ glOrtho (0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
+ glMatrixMode (GL_MODELVIEW);
+ glPushMatrix ();
+ glLoadIdentity ();
+
+ return 1;
+}
+
+static void
+fboEpilogue (CompScreen *s)
+{
+ (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0);
+
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ glDepthRange (0, 1);
+ glViewport (-1, -1, 2, 2);
+ glRasterPos2f (0, 0);
+
+ s->rasterX = s->rasterY = 0;
+
+ setDefaultViewport (s);
+
+ glMatrixMode (GL_PROJECTION);
+ glPopMatrix ();
+ glMatrixMode (GL_MODELVIEW);
+ glPopMatrix ();
+
+ glDrawBuffer (GL_BACK);
+ glReadBuffer (GL_BACK);
+}
+
+static int
+fboUpdate (CompScreen *s,
+ float dt,
+ float fade)
+{
+ WATER_SCREEN (s);
+
+ if (!fboPrologue (s, TINDEX (ws, 1)))
+ return 0;
+
+ if (!ws->texture[TINDEX (ws, 2)])
+ allocTexture (s, TINDEX (ws, 2));
+
+ if (!ws->texture[TINDEX (ws, 0)])
+ allocTexture (s, TINDEX (ws, 0));
+
+ glEnable (ws->target);
+
+ (*s->activeTexture) (GL_TEXTURE0_ARB);
+ glBindTexture (ws->target, ws->texture[TINDEX (ws, 2)]);
+
+ glTexParameteri (ws->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (ws->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ (*s->activeTexture) (GL_TEXTURE1_ARB);
+ glBindTexture (ws->target, ws->texture[TINDEX (ws, 0)]);
+ glTexParameteri (ws->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (ws->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glEnable (GL_FRAGMENT_PROGRAM_ARB);
+ (*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, ws->program);
+
+ (*s->programLocalParameter4f) (GL_FRAGMENT_PROGRAM_ARB, 0,
+ dt * K, fade, 1.0f, 1.0f);
+
+ glBegin (GL_QUADS);
+
+ glTexCoord2f (0.0f, 0.0f);
+ glVertex2f (0.0f, 0.0f);
+ glTexCoord2f (ws->tx, 0.0f);
+ glVertex2f (1.0f, 0.0f);
+ glTexCoord2f (ws->tx, ws->ty);
+ glVertex2f (1.0f, 1.0f);
+ glTexCoord2f (0.0f, ws->ty);
+ glVertex2f (0.0f, 1.0f);
+
+ glEnd ();
+
+ glDisable (GL_FRAGMENT_PROGRAM_ARB);
+
+ glTexParameteri (ws->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (ws->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glBindTexture (ws->target, 0);
+ (*s->activeTexture) (GL_TEXTURE0_ARB);
+ glTexParameteri (ws->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (ws->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glBindTexture (ws->target, 0);
+
+ glDisable (ws->target);
+
+ fboEpilogue (s);
+
+ /* increment texture index */
+ ws->tIndex = TINDEX (ws, 1);
+
+ return 1;
+}
+
+static int
+fboVertices (CompScreen *s,
+ GLenum type,
+ XPoint *p,
+ int n,
+ float v)
+{
+ WATER_SCREEN (s);
+
+ if (!fboPrologue (s, TINDEX (ws, 0)))
+ return 0;
+
+ glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ glColor4f (0.0f, 0.0f, 0.0f, v);
+
+ glPointSize (3.0f);
+ glLineWidth (1.0f);
+
+ glScalef (1.0f / ws->width, 1.0f / ws->height, 1.0);
+ glTranslatef (0.5f, 0.5f, 0.0f);
+
+ glBegin (type);
+
+ while (n--)
+ {
+ glVertex2i (p->x, p->y);
+ p++;
+ }
+
+ glEnd ();
+
+ glColor4usv (defaultColor);
+ glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+ fboEpilogue (s);
+
+ return 1;
+}
+
+static void
+softwareUpdate (CompScreen *s,
+ float dt,
+ float fade)
+{
+ float *dTmp;
+ int i, j;
+ float v0, v1, inv;
+ float accel, value;
+ unsigned char *t0, *t;
+ int dWidth, dHeight;
+ float *d01, *d10, *d11, *d12;
+
+ WATER_SCREEN (s);
+
+ if (!ws->texture[TINDEX (ws, 0)])
+ allocTexture (s, TINDEX (ws, 0));
+
+ dt *= K * 2.0f;
+ fade *= 0.99f;
+
+ dWidth = ws->width + 2;
+ dHeight = ws->height + 2;
+
+#define D(d, j) (*((d) + (j)))
+
+ d01 = ws->d0 + dWidth;
+ d10 = ws->d1;
+ d11 = d10 + dWidth;
+ d12 = d11 + dWidth;
+
+ for (i = 1; i < dHeight - 1; i++)
+ {
+ for (j = 1; j < dWidth - 1; j++)
+ {
+ accel = dt * (D (d10, j) +
+ D (d12, j) +
+ D (d11, j - 1) +
+ D (d11, j + 1) - 4.0f * D (d11, j));
+
+ value = (2.0f * D (d11, j) - D (d01, j) + accel) * fade;
+
+ CLAMP (value, 0.0f, 1.0f);
+
+ D (d01, j) = value;
+ }
+
+ d01 += dWidth;
+ d10 += dWidth;
+ d11 += dWidth;
+ d12 += dWidth;
+ }
+
+ /* update border */
+ memcpy (ws->d0, ws->d0 + dWidth, dWidth * sizeof (GLfloat));
+ memcpy (ws->d0 + dWidth * (dHeight - 1),
+ ws->d0 + dWidth * (dHeight - 2),
+ dWidth * sizeof (GLfloat));
+
+ d01 = ws->d0 + dWidth;
+
+ for (i = 1; i < dHeight - 1; i++)
+ {
+ D (d01, 0) = D (d01, 1);
+ D (d01, dWidth - 1) = D (d01, dWidth - 2);
+
+ d01 += dWidth;
+ }
+
+ d10 = ws->d1;
+ d11 = d10 + dWidth;
+ d12 = d11 + dWidth;
+
+ t0 = ws->t0;
+
+ /* update texture */
+ for (i = 0; i < ws->height; i++)
+ {
+ for (j = 0; j < ws->width; j++)
+ {
+ v0 = (D (d12, j) - D (d10, j)) * 1.5f;
+ v1 = (D (d11, j - 1) - D (d11, j + 1)) * 1.5f;
+
+ /* 0.5 for scale */
+ inv = 0.5f / sqrtf (v0 * v0 + v1 * v1 + 1.0f);
+
+ /* add scale and bias to normal */
+ v0 = v0 * inv + 0.5f;
+ v1 = v1 * inv + 0.5f;
+
+ /* store normal map in RGB components */
+ t = t0 + (j * 4);
+ t[0] = (unsigned char) ((inv + 0.5f) * 255.0f);
+ t[1] = (unsigned char) (v1 * 255.0f);
+ t[2] = (unsigned char) (v0 * 255.0f);
+
+ /* store height in A component */
+ t[3] = (unsigned char) (D (d11, j) * 255.0f);
+ }
+
+ d10 += dWidth;
+ d11 += dWidth;
+ d12 += dWidth;
+
+ t0 += ws->width * 4;
+ }
+
+#undef D
+
+ /* swap height maps */
+ dTmp = ws->d0;
+ ws->d0 = ws->d1;
+ ws->d1 = dTmp;
+
+ if (ws->texture[TINDEX (ws, 0)])
+ {
+ glBindTexture (ws->target, ws->texture[TINDEX (ws, 0)]);
+ glTexImage2D (ws->target,
+ 0,
+ GL_RGBA,
+ ws->width,
+ ws->height,
+ 0,
+ GL_BGRA,
+
+#if IMAGE_BYTE_ORDER == MSBFirst
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+#else
+ GL_UNSIGNED_BYTE,
+#endif
+
+ ws->t0);
+ }
+}
+
+
+#define SET(x, y, v) *((ws->d1) + (ws->width + 2) * (y + 1) + (x + 1)) = (v)
+
+static void
+softwarePoints (CompScreen *s,
+ XPoint *p,
+ int n,
+ float add)
+{
+ WATER_SCREEN (s);
+
+ while (n--)
+ {
+ SET (p->x - 1, p->y - 1, add);
+ SET (p->x, p->y - 1, add);
+ SET (p->x + 1, p->y - 1, add);
+
+ SET (p->x - 1, p->y, add);
+ SET (p->x, p->y, add);
+ SET (p->x + 1, p->y, add);
+
+ SET (p->x - 1, p->y + 1, add);
+ SET (p->x, p->y + 1, add);
+ SET (p->x + 1, p->y + 1, add);
+
+ p++;
+ }
+}
+
+/* bresenham */
+static void
+softwareLines (CompScreen *s,
+ XPoint *p,
+ int n,
+ float v)
+{
+ int x1, y1, x2, y2;
+ Bool steep;
+ int tmp;
+ int deltaX, deltaY;
+ int error = 0;
+ int yStep;
+ int x, y;
+
+ WATER_SCREEN (s);
+
+#define SWAP(v0, v1) \
+ tmp = v0; \
+ v0 = v1; \
+ v1 = tmp
+
+ while (n > 1)
+ {
+ x1 = p->x;
+ y1 = p->y;
+
+ p++;
+ n--;
+
+ x2 = p->x;
+ y2 = p->y;
+
+ p++;
+ n--;
+
+ steep = abs (y2 - y1) > abs (x2 - x1);
+ if (steep)
+ {
+ SWAP (x1, y1);
+ SWAP (x2, y2);
+ }
+
+ if (x1 > x2)
+ {
+ SWAP (x1, x2);
+ SWAP (y1, y2);
+ }
+
+#undef SWAP
+
+ deltaX = x2 - x1;
+ deltaY = abs (y2 - y1);
+
+ y = y1;
+ if (y1 < y2)
+ yStep = 1;
+ else
+ yStep = -1;
+
+ for (x = x1; x <= x2; x++)
+ {
+ if (steep)
+ {
+ SET (y, x, v);
+ }
+ else
+ {
+ SET (x, y, v);
+ }
+
+ error += deltaY;
+ if (2 * error >= deltaX)
+ {
+ y += yStep;
+ error -= deltaX;
+ }
+ }
+ }
+}
+
+#undef SET
+
+static void
+softwareVertices (CompScreen *s,
+ GLenum type,
+ XPoint *p,
+ int n,
+ float v)
+{
+ switch (type) {
+ case GL_POINTS:
+ softwarePoints (s, p, n, v);
+ break;
+ case GL_LINES:
+ softwareLines (s, p, n, v);
+ break;
+ }
+}
+
+static void
+waterUpdate (CompScreen *s,
+ float dt)
+{
+ GLfloat fade = 1.0f;
+
+ WATER_SCREEN (s);
+
+ if (ws->count < 1000)
+ {
+ if (ws->count > 1)
+ fade = 0.90f + ws->count / 10000.0f;
+ else
+ fade = 0.0f;
+ }
+
+ if (!fboUpdate (s, dt, fade))
+ softwareUpdate (s, dt, fade);
+}
+
+static void
+scaleVertices (CompScreen *s,
+ XPoint *p,
+ int n)
+{
+ WATER_SCREEN (s);
+
+ while (n--)
+ {
+ p[n].x = (ws->width * p[n].x) / s->width;
+ p[n].y = (ws->height * p[n].y) / s->height;
+ }
+}
+
+static void
+waterVertices (CompScreen *s,
+ GLenum type,
+ XPoint *p,
+ int n,
+ float v)
+{
+ WATER_SCREEN (s);
+
+ if (!s->fragmentProgram)
+ return;
+
+ scaleVertices (s, p, n);
+
+ if (!fboVertices (s, type, p, n, v))
+ softwareVertices (s, type, p, n, v);
+
+ if (ws->count < 3000)
+ ws->count = 3000;
+}
+
+static Bool
+waterRainTimeout (void *closure)
+{
+ CompScreen *s = closure;
+ XPoint p;
+
+ p.x = (int) (s->width * (rand () / (float) RAND_MAX));
+ p.y = (int) (s->height * (rand () / (float) RAND_MAX));
+
+ waterVertices (s, GL_POINTS, &p, 1, 0.8f * (rand () / (float) RAND_MAX));
+
+ damageScreen (s);
+
+ return TRUE;
+}
+
+static Bool
+waterWiperTimeout (void *closure)
+{
+ CompScreen *s = closure;
+
+ WATER_SCREEN (s);
+
+ if (ws->count)
+ {
+ if (ws->wiperAngle == 0.0f)
+ ws->wiperSpeed = 2.5f;
+ else if (ws->wiperAngle == 180.0f)
+ ws->wiperSpeed = -2.5f;
+ }
+
+ return TRUE;
+}
+
+static void
+waterReset (CompScreen *s)
+{
+ int size, i, j;
+
+ WATER_SCREEN (s);
+
+ ws->height = TEXTURE_SIZE;
+ ws->width = (ws->height * s->width) / s->height;
+
+ if (s->textureNonPowerOfTwo ||
+ (POWER_OF_TWO (ws->width) && POWER_OF_TWO (ws->height)))
+ {
+ ws->target = GL_TEXTURE_2D;
+ ws->tx = ws->ty = 1.0f;
+ }
+ else
+ {
+ ws->target = GL_TEXTURE_RECTANGLE_NV;
+ ws->tx = ws->width;
+ ws->ty = ws->height;
+ }
+
+ if (!s->fragmentProgram)
+ return;
+
+ if (s->fbo)
+ {
+ loadWaterProgram (s);
+ if (!ws->fbo)
+ (*s->genFramebuffers) (1, &ws->fbo);
+ }
+
+ ws->fboStatus = 0;
+
+ for (i = 0; i < TEXTURE_NUM; i++)
+ {
+ if (ws->texture[i])
+ {
+ glDeleteTextures (1, &ws->texture[i]);
+ ws->texture[i] = 0;
+ }
+ }
+
+ if (ws->data)
+ free (ws->data);
+
+ size = (ws->width + 2) * (ws->height + 2);
+
+ ws->data = calloc (1, (sizeof (float) * size * 2) +
+ (sizeof (GLubyte) * ws->width * ws->height * 4));
+ if (!ws->data)
+ return;
+
+ ws->d0 = ws->data;
+ ws->d1 = (ws->d0 + (size));
+ ws->t0 = (unsigned char *) (ws->d1 + (size));
+
+ for (i = 0; i < ws->height; i++)
+ {
+ for (j = 0; j < ws->width; j++)
+ {
+ (ws->t0 + (ws->width * 4 * i + j * 4))[0] = 0xff;
+ }
+ }
+}
+
+static void
+waterDrawWindowTexture (CompWindow *w,
+ CompTexture *texture,
+ const FragmentAttrib *attrib,
+ unsigned int mask)
+{
+ WATER_SCREEN (w->screen);
+
+ if (ws->count)
+ {
+ FragmentAttrib fa = *attrib;
+ Bool lighting = w->screen->lighting;
+ int param, function, unit;
+ GLfloat plane[4];
+
+ WATER_DISPLAY (w->screen->display);
+
+ param = allocFragmentParameters (&fa, 1);
+ unit = allocFragmentTextureUnits (&fa, 1);
+
+ function = getBumpMapFragmentFunction (w->screen, texture, unit, param);
+ if (function)
+ {
+ addFragmentFunction (&fa, function);
+
+ screenLighting (w->screen, TRUE);
+
+ (*w->screen->activeTexture) (GL_TEXTURE0_ARB + unit);
+
+ glBindTexture (ws->target, ws->texture[TINDEX (ws, 0)]);
+
+ plane[1] = plane[2] = 0.0f;
+ plane[0] = ws->tx / (GLfloat) w->screen->width;
+ plane[3] = 0.0f;
+
+ glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glTexGenfv (GL_S, GL_EYE_PLANE, plane);
+ glEnable (GL_TEXTURE_GEN_S);
+
+ plane[0] = plane[2] = 0.0f;
+ plane[1] = ws->ty / (GLfloat) w->screen->height;
+ plane[3] = 0.0f;
+
+ glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glTexGenfv (GL_T, GL_EYE_PLANE, plane);
+ glEnable (GL_TEXTURE_GEN_T);
+
+ (*w->screen->activeTexture) (GL_TEXTURE0_ARB);
+
+ (*w->screen->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, param,
+ texture->matrix.yy *
+ wd->offsetScale,
+ -texture->matrix.xx *
+ wd->offsetScale,
+ 0.0f, 0.0f);
+ }
+
+ /* to get appropriate filtering of texture */
+ mask |= PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK;
+
+ UNWRAP (ws, w->screen, drawWindowTexture);
+ (*w->screen->drawWindowTexture) (w, texture, &fa, mask);
+ WRAP (ws, w->screen, drawWindowTexture, waterDrawWindowTexture);
+
+ if (function)
+ {
+ (*w->screen->activeTexture) (GL_TEXTURE0_ARB + unit);
+ glDisable (GL_TEXTURE_GEN_T);
+ glDisable (GL_TEXTURE_GEN_S);
+ glBindTexture (ws->target, 0);
+ (*w->screen->activeTexture) (GL_TEXTURE0_ARB);
+
+ screenLighting (w->screen, lighting);
+ }
+ }
+ else
+ {
+ UNWRAP (ws, w->screen, drawWindowTexture);
+ (*w->screen->drawWindowTexture) (w, texture, attrib, mask);
+ WRAP (ws, w->screen, drawWindowTexture, waterDrawWindowTexture);
+ }
+}
+
+/* TODO: a way to control the speed */
+static void
+waterPreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ WATER_SCREEN (s);
+
+ if (ws->count)
+ {
+ ws->count -= 10;
+ if (ws->count < 0)
+ ws->count = 0;
+
+ if (ws->wiperHandle)
+ {
+ float step, angle0, angle1;
+ Bool wipe = FALSE;
+ XPoint p[3];
+
+ p[1].x = s->width / 2;
+ p[1].y = s->height;
+
+ step = ws->wiperSpeed * msSinceLastPaint / 20.0f;
+
+ if (ws->wiperSpeed > 0.0f)
+ {
+ if (ws->wiperAngle < 180.0f)
+ {
+ angle0 = ws->wiperAngle;
+
+ ws->wiperAngle += step;
+ ws->wiperAngle = MIN (ws->wiperAngle, 180.0f);
+
+ angle1 = ws->wiperAngle;
+
+ wipe = TRUE;
+ }
+ }
+ else
+ {
+ if (ws->wiperAngle > 0.0f)
+ {
+ angle1 = ws->wiperAngle;
+
+ ws->wiperAngle += step;
+ ws->wiperAngle = MAX (ws->wiperAngle, 0.0f);
+
+ angle0 = ws->wiperAngle;
+
+ wipe = TRUE;
+ }
+ }
+
+#define TAN(a) (tanf ((a) * (M_PI / 180.0f)))
+
+ if (wipe)
+ {
+ if (angle0 > 0.0f)
+ {
+ p[2].x = s->width / 2 - s->height / TAN (angle0);
+ p[2].y = 0;
+ }
+ else
+ {
+ p[2].x = 0;
+ p[2].y = s->height;
+ }
+
+ if (angle1 < 180.0f)
+ {
+ p[0].x = s->width / 2 - s->height / TAN (angle1);
+ p[0].y = 0;
+ }
+ else
+ {
+ p[0].x = s->width;
+ p[0].y = s->height;
+ }
+
+ /* software rasterizer doesn't support triangles yet so wiper
+ effect will only work with FBOs right now */
+ waterVertices (s, GL_TRIANGLES, p, 3, 0.0f);
+ }
+
+#undef TAN
+
+ }
+
+ waterUpdate (s, 0.8f);
+ }
+
+ UNWRAP (ws, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (ws, s, preparePaintScreen, waterPreparePaintScreen);
+}
+
+static void
+waterDonePaintScreen (CompScreen *s)
+{
+ WATER_SCREEN (s);
+
+ if (ws->count)
+ damageScreen (s);
+
+ UNWRAP (ws, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (ws, s, donePaintScreen, waterDonePaintScreen);
+}
+
+static void
+waterHandleMotionEvent (CompDisplay *d,
+ Window root)
+{
+ CompScreen *s;
+
+ s = findScreenAtDisplay (d, root);
+ if (s)
+ {
+ WATER_SCREEN (s);
+
+ if (ws->grabIndex)
+ {
+ XPoint p[2];
+
+ p[0].x = waterLastPointerX;
+ p[0].y = waterLastPointerY;
+
+ p[1].x = waterLastPointerX = pointerX;
+ p[1].y = waterLastPointerY = pointerY;
+
+ waterVertices (s, GL_LINES, p, 2, 0.2f);
+
+ damageScreen (s);
+ }
+ }
+}
+
+static Bool
+waterInitiate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ unsigned int ui;
+ Window root, child;
+ int xRoot, yRoot, i;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ WATER_SCREEN (s);
+
+ if (otherScreenGrabExist (s, "water", 0))
+ continue;
+
+ if (!ws->grabIndex)
+ ws->grabIndex = pushScreenGrab (s, None, "water");
+
+ if (XQueryPointer (d->display, s->root, &root, &child, &xRoot, &yRoot,
+ &i, &i, &ui))
+ {
+ XPoint p;
+
+ p.x = waterLastPointerX = xRoot;
+ p.y = waterLastPointerY = yRoot;
+
+ waterVertices (s, GL_POINTS, &p, 1, 0.8f);
+
+ damageScreen (s);
+ }
+ }
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ if (state & CompActionStateInitKey)
+ action->state |= CompActionStateTermKey;
+
+ return FALSE;
+}
+
+static Bool
+waterTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ WATER_SCREEN (s);
+
+ if (ws->grabIndex)
+ {
+ removeScreenGrab (s, ws->grabIndex, 0);
+ ws->grabIndex = 0;
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+waterToggleRain (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+
+ WATER_DISPLAY (d);
+
+ s = findScreenAtDisplay (d, getIntOptionNamed (option, nOption, "root", 0));
+ if (s)
+ {
+ WATER_SCREEN (s);
+
+ if (!ws->rainHandle)
+ {
+ int delay;
+
+ delay = wd->opt[WATER_DISPLAY_OPTION_RAIN_DELAY].value.i;
+ ws->rainHandle = compAddTimeout (delay, (float) delay * 1.2,
+ waterRainTimeout, s);
+ }
+ else
+ {
+ compRemoveTimeout (ws->rainHandle);
+ ws->rainHandle = 0;
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+waterToggleWiper (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+
+ s = findScreenAtDisplay (d, getIntOptionNamed (option, nOption, "root", 0));
+ if (s)
+ {
+ WATER_SCREEN (s);
+
+ if (!ws->wiperHandle)
+ {
+ ws->wiperHandle = compAddTimeout (2000, 2400, waterWiperTimeout, s);
+ }
+ else
+ {
+ compRemoveTimeout (ws->wiperHandle);
+ ws->wiperHandle = 0;
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+waterTitleWave (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompWindow *w;
+ int xid;
+
+ xid = getIntOptionNamed (option, nOption, "window", d->activeWindow);
+
+ w = findWindowAtDisplay (d, xid);
+ if (w)
+ {
+ XPoint p[2];
+
+ p[0].x = w->attrib.x - w->input.left;
+ p[0].y = w->attrib.y - w->input.top / 2;
+
+ p[1].x = w->attrib.x + w->width + w->input.right;
+ p[1].y = p[0].y;
+
+ waterVertices (w->screen, GL_LINES, p, 2, 0.15f);
+
+ damageScreen (w->screen);
+ }
+
+ return FALSE;
+}
+
+static Bool
+waterPoint (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ int xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ XPoint p;
+ float amp;
+
+ p.x = getIntOptionNamed (option, nOption, "x", s->width / 2);
+ p.y = getIntOptionNamed (option, nOption, "y", s->height / 2);
+
+ amp = getFloatOptionNamed (option, nOption, "amplitude", 0.5f);
+
+ waterVertices (s, GL_POINTS, &p, 1, amp);
+
+ damageScreen (s);
+ }
+
+ return FALSE;
+}
+
+static Bool
+waterLine (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ int xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ XPoint p[2];
+ float amp;
+
+ p[0].x = getIntOptionNamed (option, nOption, "x0", s->width / 4);
+ p[0].y = getIntOptionNamed (option, nOption, "y0", s->height / 2);
+
+ p[1].x = getIntOptionNamed (option, nOption, "x1",
+ s->width - s->width / 4);
+ p[1].y = getIntOptionNamed (option, nOption, "y1", s->height / 2);
+
+
+ amp = getFloatOptionNamed (option, nOption, "amplitude", 0.25f);
+
+ waterVertices (s, GL_LINES, p, 2, amp);
+
+ damageScreen (s);
+ }
+
+ return FALSE;
+}
+
+static void
+waterHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ WATER_DISPLAY (d);
+
+ switch (event->type) {
+ case ButtonPress:
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+ WATER_SCREEN (s);
+
+ if (ws->grabIndex)
+ {
+ XPoint p;
+
+ p.x = pointerX;
+ p.y = pointerY;
+
+ waterVertices (s, GL_POINTS, &p, 1, 0.8f);
+ damageScreen (s);
+ }
+ }
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ waterHandleMotionEvent (d, event->xcrossing.root);
+ break;
+ case MotionNotify:
+ waterHandleMotionEvent (d, event->xmotion.root);
+ default:
+ break;
+ }
+
+ UNWRAP (wd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (wd, d, handleEvent, waterHandleEvent);
+}
+
+static CompOption *
+waterGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ WATER_DISPLAY (display);
+
+ *count = NUM_OPTIONS (wd);
+ return wd->opt;
+}
+
+static Bool
+waterSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ WATER_DISPLAY (display);
+
+ o = compFindOption (wd->opt, NUM_OPTIONS (wd), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case WATER_DISPLAY_OPTION_OFFSET_SCALE:
+ if (compSetFloatOption (o, value))
+ {
+ wd->offsetScale = o->value.f * 50.0f;
+ return TRUE;
+ }
+ break;
+ case WATER_DISPLAY_OPTION_RAIN_DELAY:
+ if (compSetIntOption (o, value))
+ {
+ CompScreen *s;
+
+ for (s = display->screens; s; s = s->next)
+ {
+ WATER_SCREEN (s);
+
+ if (!ws->rainHandle)
+ continue;
+
+ compRemoveTimeout (ws->rainHandle);
+ ws->rainHandle = compAddTimeout (value->i,
+ (float)value->i * 1.2,
+ waterRainTimeout, s);
+ }
+ return TRUE;
+ }
+ break;
+ default:
+ return compSetDisplayOption (display, o, value);
+ }
+
+ return FALSE;
+}
+
+static const CompMetadataOptionInfo waterDisplayOptionInfo[] = {
+ { "initiate_key", "key", 0, waterInitiate, waterTerminate },
+ { "toggle_rain_key", "key", 0, waterToggleRain, 0 },
+ { "toggle_wiper_key", "key", 0, waterToggleWiper, 0 },
+ { "offset_scale", "float", "<min>0</min>", 0, 0 },
+ { "rain_delay", "int", "<min>1</min>", 0, 0 },
+ { "title_wave", "bell", 0, waterTitleWave, 0 },
+ { "point", "action", 0, waterPoint, 0 },
+ { "line", "action", 0, waterLine, 0 }
+};
+
+static Bool
+waterInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ WaterDisplay *wd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ wd = malloc (sizeof (WaterDisplay));
+ if (!wd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &waterMetadata,
+ waterDisplayOptionInfo,
+ wd->opt,
+ WATER_DISPLAY_OPTION_NUM))
+ {
+ free (wd);
+ return FALSE;
+ }
+
+ wd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (wd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, wd->opt, WATER_DISPLAY_OPTION_NUM);
+ free (wd);
+ return FALSE;
+ }
+
+ wd->offsetScale = wd->opt[WATER_DISPLAY_OPTION_OFFSET_SCALE].value.f * 50.0f;
+
+ WRAP (wd, d, handleEvent, waterHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = wd;
+
+ return TRUE;
+}
+
+static void
+waterFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ WATER_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, wd->screenPrivateIndex);
+
+ UNWRAP (wd, d, handleEvent);
+
+ compFiniDisplayOptions (d, wd->opt, WATER_DISPLAY_OPTION_NUM);
+
+ free (wd);
+}
+
+static Bool
+waterInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ WaterScreen *ws;
+
+ WATER_DISPLAY (s->display);
+
+ ws = calloc (1, sizeof (WaterScreen));
+ if (!ws)
+ return FALSE;
+
+ ws->grabIndex = 0;
+
+ WRAP (ws, s, preparePaintScreen, waterPreparePaintScreen);
+ WRAP (ws, s, donePaintScreen, waterDonePaintScreen);
+ WRAP (ws, s, drawWindowTexture, waterDrawWindowTexture);
+
+ s->base.privates[wd->screenPrivateIndex].ptr = ws;
+
+ waterReset (s);
+
+ return TRUE;
+}
+
+static void
+waterFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ WaterFunction *function, *next;
+ int i;
+
+ WATER_SCREEN (s);
+
+ if (ws->rainHandle)
+ compRemoveTimeout (ws->rainHandle);
+
+ if (ws->wiperHandle)
+ compRemoveTimeout (ws->wiperHandle);
+
+ if (ws->fbo)
+ (*s->deleteFramebuffers) (1, &ws->fbo);
+
+ for (i = 0; i < TEXTURE_NUM; i++)
+ {
+ if (ws->texture[i])
+ glDeleteTextures (1, &ws->texture[i]);
+ }
+
+ if (ws->program)
+ (*s->deletePrograms) (1, &ws->program);
+
+ if (ws->data)
+ free (ws->data);
+
+ function = ws->bumpMapFunctions;
+ while (function)
+ {
+ destroyFragmentFunction (s, function->handle);
+
+ next = function->next;
+ free (function);
+ function = next;
+ }
+
+ UNWRAP (ws, s, preparePaintScreen);
+ UNWRAP (ws, s, donePaintScreen);
+ UNWRAP (ws, s, drawWindowTexture);
+
+ free (ws);
+}
+
+static CompBool
+waterInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) waterInitDisplay,
+ (InitPluginObjectProc) waterInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+waterFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) waterFiniDisplay,
+ (FiniPluginObjectProc) waterFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+waterGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) waterGetDisplayOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+waterSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) waterSetDisplayOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+waterInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&waterMetadata,
+ p->vTable->name,
+ waterDisplayOptionInfo,
+ WATER_DISPLAY_OPTION_NUM,
+ 0, 0))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&waterMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&waterMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+waterFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&waterMetadata);
+}
+
+static CompMetadata *
+waterGetMetadata (CompPlugin *plugin)
+{
+ return &waterMetadata;
+}
+
+static CompPluginVTable waterVTable = {
+ "water",
+ waterGetMetadata,
+ waterInit,
+ waterFini,
+ waterInitObject,
+ waterFiniObject,
+ waterGetObjectOptions,
+ waterSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &waterVTable;
+}
diff --git a/plugins/wobbly.c b/plugins/wobbly.c
new file mode 100644
index 0000000..4773e9d
--- /dev/null
+++ b/plugins/wobbly.c
@@ -0,0 +1,2967 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+/*
+ * Spring model implemented by Kristian Hogsberg.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <compiz-core.h>
+
+#define WIN_X(w) ((w)->attrib.x - (w)->output.left)
+#define WIN_Y(w) ((w)->attrib.y - (w)->output.top)
+#define WIN_W(w) ((w)->width + (w)->output.left + (w)->output.right)
+#define WIN_H(w) ((w)->height + (w)->output.top + (w)->output.bottom)
+
+#define GRID_WIDTH 4
+#define GRID_HEIGHT 4
+
+#define MODEL_MAX_SPRINGS (GRID_WIDTH * GRID_HEIGHT * 2)
+
+#define MASS 15.0f
+
+typedef struct _xy_pair {
+ float x, y;
+} Point, Vector;
+
+#define NorthEdgeMask (1L << 0)
+#define SouthEdgeMask (1L << 1)
+#define WestEdgeMask (1L << 2)
+#define EastEdgeMask (1L << 3)
+
+#define EDGE_DISTANCE 25.0f
+#define EDGE_VELOCITY 13.0f
+
+typedef struct _Edge {
+ float next, prev;
+
+ float start;
+ float end;
+
+ float attract;
+ float velocity;
+
+ Bool snapped;
+} Edge;
+
+typedef struct _Object {
+ Vector force;
+ Point position;
+ Vector velocity;
+ float theta;
+ Bool immobile;
+ unsigned int edgeMask;
+ Edge vertEdge;
+ Edge horzEdge;
+} Object;
+
+typedef struct _Spring {
+ Object *a;
+ Object *b;
+ Vector offset;
+} Spring;
+
+#define NORTH 0
+#define SOUTH 1
+#define WEST 2
+#define EAST 3
+
+typedef struct _Model {
+ Object *objects;
+ int numObjects;
+ Spring springs[MODEL_MAX_SPRINGS];
+ int numSprings;
+ Object *anchorObject;
+ float steps;
+ Point topLeft;
+ Point bottomRight;
+ unsigned int edgeMask;
+ unsigned int snapCnt[4];
+} Model;
+
+#define WOBBLY_EFFECT_NONE 0
+#define WOBBLY_EFFECT_SHIVER 1
+#define WOBBLY_EFFECT_LAST WOBBLY_EFFECT_SHIVER
+
+static CompMetadata wobblyMetadata;
+
+static int displayPrivateIndex;
+
+#define WOBBLY_DISPLAY_OPTION_SNAP_KEY 0
+#define WOBBLY_DISPLAY_OPTION_SNAP_INVERTED 1
+#define WOBBLY_DISPLAY_OPTION_SHIVER 2
+#define WOBBLY_DISPLAY_OPTION_NUM 3
+
+typedef struct _WobblyDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompOption opt[WOBBLY_DISPLAY_OPTION_NUM];
+
+ Bool snapping;
+} WobblyDisplay;
+
+#define WOBBLY_SCREEN_OPTION_FRICTION 0
+#define WOBBLY_SCREEN_OPTION_SPRING_K 1
+#define WOBBLY_SCREEN_OPTION_GRID_RESOLUTION 2
+#define WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE 3
+#define WOBBLY_SCREEN_OPTION_MAP_EFFECT 4
+#define WOBBLY_SCREEN_OPTION_FOCUS_EFFECT 5
+#define WOBBLY_SCREEN_OPTION_MAP_WINDOW_MATCH 6
+#define WOBBLY_SCREEN_OPTION_FOCUS_WINDOW_MATCH 7
+#define WOBBLY_SCREEN_OPTION_GRAB_WINDOW_MATCH 8
+#define WOBBLY_SCREEN_OPTION_MOVE_WINDOW_MATCH 9
+#define WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT 10
+#define WOBBLY_SCREEN_OPTION_NUM 11
+
+typedef struct _WobblyScreen {
+ int windowPrivateIndex;
+
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintOutputProc paintOutput;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+ AddWindowGeometryProc addWindowGeometry;
+
+ WindowResizeNotifyProc windowResizeNotify;
+ WindowMoveNotifyProc windowMoveNotify;
+ WindowGrabNotifyProc windowGrabNotify;
+ WindowUngrabNotifyProc windowUngrabNotify;
+
+ CompOption opt[WOBBLY_SCREEN_OPTION_NUM];
+
+ Bool wobblyWindows;
+
+ unsigned int grabMask;
+ CompWindow *grabWindow;
+ Bool moveWindow;
+} WobblyScreen;
+
+#define WobblyInitial (1L << 0)
+#define WobblyForce (1L << 1)
+#define WobblyVelocity (1L << 2)
+
+typedef struct _WobblyWindow {
+ Model *model;
+ int wobbly;
+ Bool grabbed;
+ Bool velocity;
+ unsigned int state;
+} WobblyWindow;
+
+#define GET_WOBBLY_DISPLAY(d) \
+ ((WobblyDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define WOBBLY_DISPLAY(d) \
+ WobblyDisplay *wd = GET_WOBBLY_DISPLAY (d)
+
+#define GET_WOBBLY_SCREEN(s, wd) \
+ ((WobblyScreen *) (s)->base.privates[(wd)->screenPrivateIndex].ptr)
+
+#define WOBBLY_SCREEN(s) \
+ WobblyScreen *ws = GET_WOBBLY_SCREEN (s, GET_WOBBLY_DISPLAY (s->display))
+
+#define GET_WOBBLY_WINDOW(w, ws) \
+ ((WobblyWindow *) (w)->base.privates[(ws)->windowPrivateIndex].ptr)
+
+#define WOBBLY_WINDOW(w) \
+ WobblyWindow *ww = GET_WOBBLY_WINDOW (w, \
+ GET_WOBBLY_SCREEN (w->screen, \
+ GET_WOBBLY_DISPLAY (w->screen->display)))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static CompOption *
+wobblyGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+ int *count)
+{
+ WOBBLY_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ws);
+ return ws->opt;
+}
+
+static Bool
+wobblySetScreenOption (CompPlugin *plugin,
+ CompScreen *screen,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ WOBBLY_SCREEN (screen);
+
+ o = compFindOption (ws->opt, NUM_OPTIONS (ws), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetScreenOption (screen, o, value);
+}
+
+static const CompMetadataOptionInfo wobblyScreenOptionInfo[] = {
+ { "friction", "float", "<min>0.1</min>", 0, 0 },
+ { "spring_k", "float", "<min>0.1</min>", 0, 0 },
+ { "grid_resolution", "int", "<min>1</min><max>64</max>", 0, 0 },
+ { "min_grid_size", "int", "<min>4</min><max>128</max>", 0, 0 },
+ { "map_effect", "int", RESTOSTRING (0, WOBBLY_EFFECT_LAST), 0, 0 },
+ { "focus_effect", "int", RESTOSTRING (0, WOBBLY_EFFECT_LAST), 0, 0 },
+ { "map_window_match", "match", 0, 0, 0 },
+ { "focus_window_match", "match", 0, 0, 0 },
+ { "grab_window_match", "match", 0, 0, 0 },
+ { "move_window_match", "match", 0, 0, 0 },
+ { "maximize_effect", "bool", 0, 0, 0 }
+};
+
+#define SNAP_WINDOW_TYPE (CompWindowTypeNormalMask | \
+ CompWindowTypeToolbarMask | \
+ CompWindowTypeMenuMask | \
+ CompWindowTypeUtilMask)
+
+static void
+findNextWestEdge (CompWindow *w,
+ Object *object)
+{
+ int v, v1, v2;
+ int s, start;
+ int e, end;
+ int x;
+ int output;
+
+ start = -65535.0f;
+ end = 65535.0f;
+
+ v1 = -65535.0f;
+ v2 = 65535.0f;
+
+ x = object->position.x + w->output.left - w->input.left;
+
+ output = outputDeviceForPoint (w->screen, x, object->position.y);
+
+ if (x >= w->screen->outputDev[output].region.extents.x1)
+ {
+ CompWindow *p;
+
+ v1 = w->screen->outputDev[output].region.extents.x1;
+
+ for (p = w->screen->windows; p; p = p->next)
+ {
+ if (w == p)
+ continue;
+
+ if (p->mapNum && p->struts)
+ {
+ s = p->struts->left.y - w->output.top;
+ e = p->struts->left.y + p->struts->left.height +
+ w->output.bottom;
+ }
+ else if (!p->invisible && (p->type & SNAP_WINDOW_TYPE))
+ {
+ s = p->attrib.y - p->input.top - w->output.top;
+ e = p->attrib.y + p->height + p->input.bottom +
+ w->output.bottom;
+ }
+ else
+ {
+ continue;
+ }
+
+ if (s > object->position.y)
+ {
+ if (s < end)
+ end = s;
+ }
+ else if (e < object->position.y)
+ {
+ if (e > start)
+ start = e;
+ }
+ else
+ {
+ if (s > start)
+ start = s;
+
+ if (e < end)
+ end = e;
+
+ if (p->mapNum && p->struts)
+ v = p->struts->left.x + p->struts->left.width;
+ else
+ v = p->attrib.x + p->width + p->input.right;
+
+ if (v <= x)
+ {
+ if (v > v1)
+ v1 = v;
+ }
+ else
+ {
+ if (v < v2)
+ v2 = v;
+ }
+ }
+ }
+ }
+ else
+ {
+ v2 = w->screen->outputDev[output].region.extents.x1;
+ }
+
+ v1 = v1 - w->output.left + w->input.left;
+ v2 = v2 - w->output.left + w->input.left;
+
+ if (v1 != (int) object->vertEdge.next)
+ object->vertEdge.snapped = FALSE;
+
+ object->vertEdge.start = start;
+ object->vertEdge.end = end;
+
+ object->vertEdge.next = v1;
+ object->vertEdge.prev = v2;
+
+ object->vertEdge.attract = v1 + EDGE_DISTANCE;
+ object->vertEdge.velocity = EDGE_VELOCITY;
+}
+
+static void
+findNextEastEdge (CompWindow *w,
+ Object *object)
+{
+ int v, v1, v2;
+ int s, start;
+ int e, end;
+ int x;
+ int output;
+
+ start = -65535.0f;
+ end = 65535.0f;
+
+ v1 = 65535.0f;
+ v2 = -65535.0f;
+
+ x = object->position.x - w->output.right + w->input.right;
+
+ output = outputDeviceForPoint (w->screen, x, object->position.y);
+
+ if (x <= w->screen->outputDev[output].region.extents.x2)
+ {
+ CompWindow *p;
+
+ v1 = w->screen->outputDev[output].region.extents.x2;
+
+ for (p = w->screen->windows; p; p = p->next)
+ {
+ if (w == p)
+ continue;
+
+ if (p->mapNum && p->struts)
+ {
+ s = p->struts->right.y - w->output.top;
+ e = p->struts->right.y + p->struts->right.height +
+ w->output.bottom;
+ }
+ else if (!p->invisible && (p->type & SNAP_WINDOW_TYPE))
+ {
+ s = p->attrib.y - p->input.top - w->output.top;
+ e = p->attrib.y + p->height + p->input.bottom +
+ w->output.bottom;
+ }
+ else
+ {
+ continue;
+ }
+
+ if (s > object->position.y)
+ {
+ if (s < end)
+ end = s;
+ }
+ else if (e < object->position.y)
+ {
+ if (e > start)
+ start = e;
+ }
+ else
+ {
+ if (s > start)
+ start = s;
+
+ if (e < end)
+ end = e;
+
+ if (p->mapNum && p->struts)
+ v = p->struts->right.x;
+ else
+ v = p->attrib.x - p->input.left;
+
+ if (v >= x)
+ {
+ if (v < v1)
+ v1 = v;
+ }
+ else
+ {
+ if (v > v2)
+ v2 = v;
+ }
+ }
+ }
+ }
+ else
+ {
+ v2 = w->screen->outputDev[output].region.extents.x2;
+ }
+
+ v1 = v1 + w->output.right - w->input.right;
+ v2 = v2 + w->output.right - w->input.right;
+
+ if (v1 != (int) object->vertEdge.next)
+ object->vertEdge.snapped = FALSE;
+
+ object->vertEdge.start = start;
+ object->vertEdge.end = end;
+
+ object->vertEdge.next = v1;
+ object->vertEdge.prev = v2;
+
+ object->vertEdge.attract = v1 - EDGE_DISTANCE;
+ object->vertEdge.velocity = EDGE_VELOCITY;
+}
+
+static void
+findNextNorthEdge (CompWindow *w,
+ Object *object)
+{
+ int v, v1, v2;
+ int s, start;
+ int e, end;
+ int y;
+ int output;
+
+ start = -65535.0f;
+ end = 65535.0f;
+
+ v1 = -65535.0f;
+ v2 = 65535.0f;
+
+ y = object->position.y + w->output.top - w->input.top;
+
+ output = outputDeviceForPoint (w->screen, object->position.x, y);
+
+ if (y >= w->screen->outputDev[output].region.extents.y1)
+ {
+ CompWindow *p;
+
+ v1 = w->screen->outputDev[output].region.extents.y1;
+
+ for (p = w->screen->windows; p; p = p->next)
+ {
+ if (w == p)
+ continue;
+
+ if (p->mapNum && p->struts)
+ {
+ s = p->struts->top.x - w->output.left;
+ e = p->struts->top.x + p->struts->top.width + w->output.right;
+ }
+ else if (!p->invisible && (p->type & SNAP_WINDOW_TYPE))
+ {
+ s = p->attrib.x - p->input.left - w->output.left;
+ e = p->attrib.x + p->width + p->input.right + w->output.right;
+ }
+ else
+ {
+ continue;
+ }
+
+ if (s > object->position.x)
+ {
+ if (s < end)
+ end = s;
+ }
+ else if (e < object->position.x)
+ {
+ if (e > start)
+ start = e;
+ }
+ else
+ {
+ if (s > start)
+ start = s;
+
+ if (e < end)
+ end = e;
+
+ if (p->mapNum && p->struts)
+ v = p->struts->top.y + p->struts->top.height;
+ else
+ v = p->attrib.y + p->height + p->input.bottom;
+
+ if (v <= y)
+ {
+ if (v > v1)
+ v1 = v;
+ }
+ else
+ {
+ if (v < v2)
+ v2 = v;
+ }
+ }
+ }
+ }
+ else
+ {
+ v2 = w->screen->outputDev[output].region.extents.y1;
+ }
+
+ v1 = v1 - w->output.top + w->input.top;
+ v2 = v2 - w->output.top + w->input.top;
+
+ if (v1 != (int) object->horzEdge.next)
+ object->horzEdge.snapped = FALSE;
+
+ object->horzEdge.start = start;
+ object->horzEdge.end = end;
+
+ object->horzEdge.next = v1;
+ object->horzEdge.prev = v2;
+
+ object->horzEdge.attract = v1 + EDGE_DISTANCE;
+ object->horzEdge.velocity = EDGE_VELOCITY;
+}
+
+static void
+findNextSouthEdge (CompWindow *w,
+ Object *object)
+{
+ int v, v1, v2;
+ int s, start;
+ int e, end;
+ int y;
+ int output;
+
+ start = -65535.0f;
+ end = 65535.0f;
+
+ v1 = 65535.0f;
+ v2 = -65535.0f;
+
+ y = object->position.y - w->output.bottom + w->input.bottom;
+
+ output = outputDeviceForPoint (w->screen, object->position.x, y);
+
+ if (y <= w->screen->outputDev[output].region.extents.y2)
+ {
+ CompWindow *p;
+
+ v1 = w->screen->outputDev[output].region.extents.y2;
+
+ for (p = w->screen->windows; p; p = p->next)
+ {
+ if (w == p)
+ continue;
+
+ if (p->mapNum && p->struts)
+ {
+ s = p->struts->bottom.x - w->output.left;
+ e = p->struts->bottom.x + p->struts->bottom.width +
+ w->output.right;
+ }
+ else if (!p->invisible && (p->type & SNAP_WINDOW_TYPE))
+ {
+ s = p->attrib.x - p->input.left - w->output.left;
+ e = p->attrib.x + p->width + p->input.right + w->output.right;
+ }
+ else
+ {
+ continue;
+ }
+
+ if (s > object->position.x)
+ {
+ if (s < end)
+ end = s;
+ }
+ else if (e < object->position.x)
+ {
+ if (e > start)
+ start = e;
+ }
+ else
+ {
+ if (s > start)
+ start = s;
+
+ if (e < end)
+ end = e;
+
+ if (p->mapNum && p->struts)
+ v = p->struts->bottom.y;
+ else
+ v = p->attrib.y - p->input.top;
+
+ if (v >= y)
+ {
+ if (v < v1)
+ v1 = v;
+ }
+ else
+ {
+ if (v > v2)
+ v2 = v;
+ }
+ }
+ }
+ }
+ else
+ {
+ v2 = w->screen->outputDev[output].region.extents.y2;
+ }
+
+ v1 = v1 + w->output.bottom - w->input.bottom;
+ v2 = v2 + w->output.bottom - w->input.bottom;
+
+ if (v1 != (int) object->horzEdge.next)
+ object->horzEdge.snapped = FALSE;
+
+ object->horzEdge.start = start;
+ object->horzEdge.end = end;
+
+ object->horzEdge.next = v1;
+ object->horzEdge.prev = v2;
+
+ object->horzEdge.attract = v1 - EDGE_DISTANCE;
+ object->horzEdge.velocity = EDGE_VELOCITY;
+}
+
+static void
+objectInit (Object *object,
+ float positionX,
+ float positionY,
+ float velocityX,
+ float velocityY)
+{
+ object->force.x = 0;
+ object->force.y = 0;
+
+ object->position.x = positionX;
+ object->position.y = positionY;
+
+ object->velocity.x = velocityX;
+ object->velocity.y = velocityY;
+
+ object->theta = 0;
+ object->immobile = FALSE;
+
+ object->edgeMask = 0;
+
+ object->vertEdge.snapped = FALSE;
+ object->horzEdge.snapped = FALSE;
+
+ object->vertEdge.next = 0.0f;
+ object->horzEdge.next = 0.0f;
+}
+
+static void
+springInit (Spring *spring,
+ Object *a,
+ Object *b,
+ float offsetX,
+ float offsetY)
+{
+ spring->a = a;
+ spring->b = b;
+ spring->offset.x = offsetX;
+ spring->offset.y = offsetY;
+}
+
+static void
+modelCalcBounds (Model *model)
+{
+ int i;
+
+ model->topLeft.x = MAXSHORT;
+ model->topLeft.y = MAXSHORT;
+ model->bottomRight.x = MINSHORT;
+ model->bottomRight.y = MINSHORT;
+
+ for (i = 0; i < model->numObjects; i++)
+ {
+ if (model->objects[i].position.x < model->topLeft.x)
+ model->topLeft.x = model->objects[i].position.x;
+ else if (model->objects[i].position.x > model->bottomRight.x)
+ model->bottomRight.x = model->objects[i].position.x;
+
+ if (model->objects[i].position.y < model->topLeft.y)
+ model->topLeft.y = model->objects[i].position.y;
+ else if (model->objects[i].position.y > model->bottomRight.y)
+ model->bottomRight.y = model->objects[i].position.y;
+ }
+}
+
+static void
+modelAddSpring (Model *model,
+ Object *a,
+ Object *b,
+ float offsetX,
+ float offsetY)
+{
+ Spring *spring;
+
+ spring = &model->springs[model->numSprings];
+ model->numSprings++;
+
+ springInit (spring, a, b, offsetX, offsetY);
+}
+
+static void
+modelSetMiddleAnchor (Model *model,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ float gx, gy;
+
+ gx = ((GRID_WIDTH - 1) / 2 * width) / (float) (GRID_WIDTH - 1);
+ gy = ((GRID_HEIGHT - 1) / 2 * height) / (float) (GRID_HEIGHT - 1);
+
+ if (model->anchorObject)
+ model->anchorObject->immobile = FALSE;
+
+ model->anchorObject = &model->objects[GRID_WIDTH *
+ ((GRID_HEIGHT - 1) / 2) +
+ (GRID_WIDTH - 1) / 2];
+ model->anchorObject->position.x = x + gx;
+ model->anchorObject->position.y = y + gy;
+
+ model->anchorObject->immobile = TRUE;
+}
+
+static void
+modelSetTopAnchor (Model *model,
+ int x,
+ int y,
+ int width)
+{
+ float gx;
+
+ gx = ((GRID_WIDTH - 1) / 2 * width) / (float) (GRID_WIDTH - 1);
+
+ if (model->anchorObject)
+ model->anchorObject->immobile = FALSE;
+
+ model->anchorObject = &model->objects[(GRID_WIDTH - 1) / 2];
+ model->anchorObject->position.x = x + gx;
+ model->anchorObject->position.y = y;
+
+ model->anchorObject->immobile = TRUE;
+}
+
+static void
+modelAddEdgeAnchors (Model *model,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ Object *o;
+
+ o = &model->objects[0];
+ o->position.x = x;
+ o->position.y = y;
+ o->immobile = TRUE;
+
+ o = &model->objects[GRID_WIDTH - 1];
+ o->position.x = x + width;
+ o->position.y = y;
+ o->immobile = TRUE;
+
+ o = &model->objects[GRID_WIDTH * (GRID_HEIGHT - 1)];
+ o->position.x = x;
+ o->position.y = y + height;
+ o->immobile = TRUE;
+
+ o = &model->objects[model->numObjects - 1];
+ o->position.x = x + width;
+ o->position.y = y + height;
+ o->immobile = TRUE;
+
+ if (!model->anchorObject)
+ model->anchorObject = &model->objects[0];
+}
+
+static void
+modelRemoveEdgeAnchors (Model *model,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ Object *o;
+
+ o = &model->objects[0];
+ o->position.x = x;
+ o->position.y = y;
+ if (o != model->anchorObject)
+ o->immobile = FALSE;
+
+ o = &model->objects[GRID_WIDTH - 1];
+ o->position.x = x + width;
+ o->position.y = y;
+ if (o != model->anchorObject)
+ o->immobile = FALSE;
+
+ o = &model->objects[GRID_WIDTH * (GRID_HEIGHT - 1)];
+ o->position.x = x;
+ o->position.y = y + height;
+ if (o != model->anchorObject)
+ o->immobile = FALSE;
+
+ o = &model->objects[model->numObjects - 1];
+ o->position.x = x + width;
+ o->position.y = y + height;
+ if (o != model->anchorObject)
+ o->immobile = FALSE;
+}
+
+static void
+modelAdjustObjectPosition (Model *model,
+ Object *object,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ Object *o;
+ int gridX, gridY, i = 0;
+
+ for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
+ {
+ for (gridX = 0; gridX < GRID_WIDTH; gridX++)
+ {
+ o = &model->objects[i];
+ if (o == object)
+ {
+ o->position.x = x + (gridX * width) / (GRID_WIDTH - 1);
+ o->position.y = y + (gridY * height) / (GRID_HEIGHT - 1);
+
+ return;
+ }
+
+ i++;
+ }
+ }
+}
+
+static void
+modelInitObjects (Model *model,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ int gridX, gridY, i = 0;
+ float gw, gh;
+
+ gw = GRID_WIDTH - 1;
+ gh = GRID_HEIGHT - 1;
+
+ for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
+ {
+ for (gridX = 0; gridX < GRID_WIDTH; gridX++)
+ {
+ objectInit (&model->objects[i],
+ x + (gridX * width) / gw,
+ y + (gridY * height) / gh,
+ 0, 0);
+ i++;
+ }
+ }
+
+ modelSetMiddleAnchor (model, x, y, width, height);
+}
+
+static void
+modelUpdateSnapping (CompWindow *window,
+ Model *model)
+{
+ unsigned int edgeMask, gridMask, mask;
+ int gridX, gridY, i = 0;
+
+ edgeMask = model->edgeMask;
+
+ if (model->snapCnt[NORTH])
+ edgeMask &= ~SouthEdgeMask;
+ else if (model->snapCnt[SOUTH])
+ edgeMask &= ~NorthEdgeMask;
+
+ if (model->snapCnt[WEST])
+ edgeMask &= ~EastEdgeMask;
+ else if (model->snapCnt[EAST])
+ edgeMask &= ~WestEdgeMask;
+
+ for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
+ {
+ if (gridY == 0)
+ gridMask = edgeMask & NorthEdgeMask;
+ else if (gridY == GRID_HEIGHT - 1)
+ gridMask = edgeMask & SouthEdgeMask;
+ else
+ gridMask = 0;
+
+ for (gridX = 0; gridX < GRID_WIDTH; gridX++)
+ {
+ mask = gridMask;
+
+ if (gridX == 0)
+ mask |= edgeMask & WestEdgeMask;
+ else if (gridX == GRID_WIDTH - 1)
+ mask |= edgeMask & EastEdgeMask;
+
+ if (mask != model->objects[i].edgeMask)
+ {
+ model->objects[i].edgeMask = mask;
+
+ if (mask & WestEdgeMask)
+ {
+ if (!model->objects[i].vertEdge.snapped)
+ findNextWestEdge (window, &model->objects[i]);
+ }
+ else if (mask & EastEdgeMask)
+ {
+ if (!model->objects[i].vertEdge.snapped)
+ findNextEastEdge (window, &model->objects[i]);
+ }
+ else
+ model->objects[i].vertEdge.snapped = FALSE;
+
+ if (mask & NorthEdgeMask)
+ {
+ if (!model->objects[i].horzEdge.snapped)
+ findNextNorthEdge (window, &model->objects[i]);
+ }
+ else if (mask & SouthEdgeMask)
+ {
+ if (!model->objects[i].horzEdge.snapped)
+ findNextSouthEdge (window, &model->objects[i]);
+ }
+ else
+ model->objects[i].horzEdge.snapped = FALSE;
+ }
+
+ i++;
+ }
+ }
+}
+
+static void
+modelReduceEdgeEscapeVelocity (Model *model)
+{
+ int gridX, gridY, i = 0;
+
+ for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
+ {
+ for (gridX = 0; gridX < GRID_WIDTH; gridX++)
+ {
+ if (model->objects[i].vertEdge.snapped)
+ model->objects[i].vertEdge.velocity *= drand48 () * 0.25f;
+
+ if (model->objects[i].horzEdge.snapped)
+ model->objects[i].horzEdge.velocity *= drand48 () * 0.25f;
+
+ i++;
+ }
+ }
+}
+
+static Bool
+modelDisableSnapping (CompWindow *window,
+ Model *model)
+{
+ int gridX, gridY, i = 0;
+ Bool snapped = FALSE;
+
+ for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
+ {
+ for (gridX = 0; gridX < GRID_WIDTH; gridX++)
+ {
+ if (model->objects[i].vertEdge.snapped ||
+ model->objects[i].horzEdge.snapped)
+ snapped = TRUE;
+
+ model->objects[i].vertEdge.snapped = FALSE;
+ model->objects[i].horzEdge.snapped = FALSE;
+
+ model->objects[i].edgeMask = 0;
+
+ i++;
+ }
+ }
+
+ memset (model->snapCnt, 0, sizeof (model->snapCnt));
+
+ return snapped;
+}
+
+static void
+modelAdjustObjectsForShiver (Model *model,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ int gridX, gridY, i = 0;
+ float vX, vY;
+ float w, h;
+ float scale;
+
+ w = width;
+ h = height;
+
+ for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
+ {
+ for (gridX = 0; gridX < GRID_WIDTH; gridX++)
+ {
+ if (!model->objects[i].immobile)
+ {
+ vX = model->objects[i].position.x - (x + w / 2);
+ vY = model->objects[i].position.y - (y + h / 2);
+
+ vX /= w;
+ vY /= h;
+
+ scale = ((float) rand () * 7.5f) / RAND_MAX;
+
+ model->objects[i].velocity.x += vX * scale;
+ model->objects[i].velocity.y += vY * scale;
+ }
+
+ i++;
+ }
+ }
+}
+
+static void
+modelInitSprings (Model *model,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ int gridX, gridY, i = 0;
+ float hpad, vpad;
+
+ model->numSprings = 0;
+
+ hpad = ((float) width) / (GRID_WIDTH - 1);
+ vpad = ((float) height) / (GRID_HEIGHT - 1);
+
+ for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
+ {
+ for (gridX = 0; gridX < GRID_WIDTH; gridX++)
+ {
+ if (gridX > 0)
+ modelAddSpring (model,
+ &model->objects[i - 1],
+ &model->objects[i],
+ hpad, 0);
+
+ if (gridY > 0)
+ modelAddSpring (model,
+ &model->objects[i - GRID_WIDTH],
+ &model->objects[i],
+ 0, vpad);
+
+ i++;
+ }
+ }
+}
+
+static void
+modelMove (Model *model,
+ float tx,
+ float ty)
+{
+ int i;
+
+ for (i = 0; i < model->numObjects; i++)
+ {
+ model->objects[i].position.x += tx;
+ model->objects[i].position.y += ty;
+ }
+}
+
+static Model *
+createModel (int x,
+ int y,
+ int width,
+ int height,
+ unsigned int edgeMask)
+{
+ Model *model;
+
+ model = malloc (sizeof (Model));
+ if (!model)
+ return 0;
+
+ model->numObjects = GRID_WIDTH * GRID_HEIGHT;
+ model->objects = malloc (sizeof (Object) * model->numObjects);
+ if (!model->objects)
+ {
+ free (model);
+ return 0;
+ }
+
+ model->anchorObject = 0;
+ model->numSprings = 0;
+
+ model->steps = 0;
+
+ memset (model->snapCnt, 0, sizeof (model->snapCnt));
+
+ model->edgeMask = edgeMask;
+
+ modelInitObjects (model, x, y, width, height);
+ modelInitSprings (model, x, y, width, height);
+
+ modelCalcBounds (model);
+
+ return model;
+}
+
+static void
+objectApplyForce (Object *object,
+ float fx,
+ float fy)
+{
+ object->force.x += fx;
+ object->force.y += fy;
+}
+
+static void
+springExertForces (Spring *spring,
+ float k)
+{
+ Vector da, db;
+ Vector a, b;
+
+ a = spring->a->position;
+ b = spring->b->position;
+
+ da.x = 0.5f * (b.x - a.x - spring->offset.x);
+ da.y = 0.5f * (b.y - a.y - spring->offset.y);
+
+ db.x = 0.5f * (a.x - b.x + spring->offset.x);
+ db.y = 0.5f * (a.y - b.y + spring->offset.y);
+
+ objectApplyForce (spring->a, k * da.x, k * da.y);
+ objectApplyForce (spring->b, k * db.x, k * db.y);
+}
+
+static Bool
+objectReleaseWestEdge (CompWindow *w,
+ Model *model,
+ Object *object)
+{
+ if (fabs (object->velocity.x) > object->vertEdge.velocity)
+ {
+ object->position.x += object->velocity.x * 2.0f;
+
+ model->snapCnt[WEST]--;
+
+ object->vertEdge.snapped = FALSE;
+ object->edgeMask = 0;
+
+ modelUpdateSnapping (w, model);
+
+ return TRUE;
+ }
+
+ object->velocity.x = 0.0f;
+
+ return FALSE;
+}
+
+static Bool
+objectReleaseEastEdge (CompWindow *w,
+ Model *model,
+ Object *object)
+{
+ if (fabs (object->velocity.x) > object->vertEdge.velocity)
+ {
+ object->position.x += object->velocity.x * 2.0f;
+
+ model->snapCnt[EAST]--;
+
+ object->vertEdge.snapped = FALSE;
+ object->edgeMask = 0;
+
+ modelUpdateSnapping (w, model);
+
+ return TRUE;
+ }
+
+ object->velocity.x = 0.0f;
+
+ return FALSE;
+}
+
+static Bool
+objectReleaseNorthEdge (CompWindow *w,
+ Model *model,
+ Object *object)
+{
+ if (fabs (object->velocity.y) > object->horzEdge.velocity)
+ {
+ object->position.y += object->velocity.y * 2.0f;
+
+ model->snapCnt[NORTH]--;
+
+ object->horzEdge.snapped = FALSE;
+ object->edgeMask = 0;
+
+ modelUpdateSnapping (w, model);
+
+ return TRUE;
+ }
+
+ object->velocity.y = 0.0f;
+
+ return FALSE;
+}
+
+static Bool
+objectReleaseSouthEdge (CompWindow *w,
+ Model *model,
+ Object *object)
+{
+ if (fabs (object->velocity.y) > object->horzEdge.velocity)
+ {
+ object->position.y += object->velocity.y * 2.0f;
+
+ model->snapCnt[SOUTH]--;
+
+ object->horzEdge.snapped = FALSE;
+ object->edgeMask = 0;
+
+ modelUpdateSnapping (w, model);
+
+ return TRUE;
+ }
+
+ object->velocity.y = 0.0f;
+
+ return FALSE;
+}
+
+static float
+modelStepObject (CompWindow *window,
+ Model *model,
+ Object *object,
+ float friction,
+ float *force)
+{
+ object->theta += 0.05f;
+
+ if (object->immobile)
+ {
+ object->velocity.x = 0.0f;
+ object->velocity.y = 0.0f;
+
+ object->force.x = 0.0f;
+ object->force.y = 0.0f;
+
+ *force = 0.0f;
+
+ return 0.0f;
+ }
+ else
+ {
+ object->force.x -= friction * object->velocity.x;
+ object->force.y -= friction * object->velocity.y;
+
+ object->velocity.x += object->force.x / MASS;
+ object->velocity.y += object->force.y / MASS;
+
+ if (object->edgeMask)
+ {
+ if (object->edgeMask & WestEdgeMask)
+ {
+ if (object->position.y < object->vertEdge.start ||
+ object->position.y > object->vertEdge.end)
+ findNextWestEdge (window, object);
+
+ if (!object->vertEdge.snapped ||
+ objectReleaseWestEdge (window, model, object))
+ {
+ object->position.x += object->velocity.x;
+
+ if (object->velocity.x < 0.0f &&
+ object->position.x < object->vertEdge.attract)
+ {
+ if (object->position.x < object->vertEdge.next)
+ {
+ object->vertEdge.snapped = TRUE;
+ object->position.x = object->vertEdge.next;
+ object->velocity.x = 0.0f;
+
+ model->snapCnt[WEST]++;
+
+ modelUpdateSnapping (window, model);
+ }
+ else
+ {
+ object->velocity.x -=
+ object->vertEdge.attract - object->position.x;
+ }
+ }
+
+ if (object->position.x > object->vertEdge.prev)
+ findNextWestEdge (window, object);
+ }
+ }
+ else if (object->edgeMask & EastEdgeMask)
+ {
+ if (object->position.y < object->vertEdge.start ||
+ object->position.y > object->vertEdge.end)
+ findNextEastEdge (window, object);
+
+ if (!object->vertEdge.snapped ||
+ objectReleaseEastEdge (window, model, object))
+ {
+ object->position.x += object->velocity.x;
+
+ if (object->velocity.x > 0.0f &&
+ object->position.x > object->vertEdge.attract)
+ {
+ if (object->position.x > object->vertEdge.next)
+ {
+ object->vertEdge.snapped = TRUE;
+ object->position.x = object->vertEdge.next;
+ object->velocity.x = 0.0f;
+
+ model->snapCnt[EAST]++;
+
+ modelUpdateSnapping (window, model);
+ }
+ else
+ {
+ object->velocity.x =
+ object->position.x - object->vertEdge.attract;
+ }
+ }
+
+ if (object->position.x < object->vertEdge.prev)
+ findNextEastEdge (window, object);
+ }
+ }
+ else
+ object->position.x += object->velocity.x;
+
+ if (object->edgeMask & NorthEdgeMask)
+ {
+ if (object->position.x < object->horzEdge.start ||
+ object->position.x > object->horzEdge.end)
+ findNextNorthEdge (window, object);
+
+ if (!object->horzEdge.snapped ||
+ objectReleaseNorthEdge (window, model, object))
+ {
+ object->position.y += object->velocity.y;
+
+ if (object->velocity.y < 0.0f &&
+ object->position.y < object->horzEdge.attract)
+ {
+ if (object->position.y < object->horzEdge.next)
+ {
+ object->horzEdge.snapped = TRUE;
+ object->position.y = object->horzEdge.next;
+ object->velocity.y = 0.0f;
+
+ model->snapCnt[NORTH]++;
+
+ modelUpdateSnapping (window, model);
+ }
+ else
+ {
+ object->velocity.y -=
+ object->horzEdge.attract - object->position.y;
+ }
+ }
+
+ if (object->position.y > object->horzEdge.prev)
+ findNextNorthEdge (window, object);
+ }
+ }
+ else if (object->edgeMask & SouthEdgeMask)
+ {
+ if (object->position.x < object->horzEdge.start ||
+ object->position.x > object->horzEdge.end)
+ findNextSouthEdge (window, object);
+
+ if (!object->horzEdge.snapped ||
+ objectReleaseSouthEdge (window, model, object))
+ {
+ object->position.y += object->velocity.y;
+
+ if (object->velocity.y > 0.0f &&
+ object->position.y > object->horzEdge.attract)
+ {
+ if (object->position.y > object->horzEdge.next)
+ {
+ object->horzEdge.snapped = TRUE;
+ object->position.y = object->horzEdge.next;
+ object->velocity.y = 0.0f;
+
+ model->snapCnt[SOUTH]++;
+
+ modelUpdateSnapping (window, model);
+ }
+ else
+ {
+ object->velocity.y =
+ object->position.y - object->horzEdge.attract;
+ }
+ }
+
+ if (object->position.y < object->horzEdge.prev)
+ findNextSouthEdge (window, object);
+ }
+ }
+ else
+ object->position.y += object->velocity.y;
+ }
+ else
+ {
+ object->position.x += object->velocity.x;
+ object->position.y += object->velocity.y;
+ }
+
+ *force = fabs (object->force.x) + fabs (object->force.y);
+
+ object->force.x = 0.0f;
+ object->force.y = 0.0f;
+
+ return fabs (object->velocity.x) + fabs (object->velocity.y);
+ }
+}
+
+static int
+modelStep (CompWindow *window,
+ Model *model,
+ float friction,
+ float k,
+ float time)
+{
+ int i, j, steps, wobbly = 0;
+ float velocitySum = 0.0f;
+ float force, forceSum = 0.0f;
+
+ model->steps += time / 15.0f;
+ steps = floor (model->steps);
+ model->steps -= steps;
+
+ if (!steps)
+ return TRUE;
+
+ for (j = 0; j < steps; j++)
+ {
+ for (i = 0; i < model->numSprings; i++)
+ springExertForces (&model->springs[i], k);
+
+ for (i = 0; i < model->numObjects; i++)
+ {
+ velocitySum += modelStepObject (window,
+ model,
+ &model->objects[i],
+ friction,
+ &force);
+ forceSum += force;
+ }
+ }
+
+ modelCalcBounds (model);
+
+ if (velocitySum > 0.5f)
+ wobbly |= WobblyVelocity;
+
+ if (forceSum > 20.0f)
+ wobbly |= WobblyForce;
+
+ return wobbly;
+}
+
+static void
+bezierPatchEvaluate (Model *model,
+ float u,
+ float v,
+ float *patchX,
+ float *patchY)
+{
+ float coeffsU[4], coeffsV[4];
+ float x, y;
+ int i, j;
+
+ coeffsU[0] = (1 - u) * (1 - u) * (1 - u);
+ coeffsU[1] = 3 * u * (1 - u) * (1 - u);
+ coeffsU[2] = 3 * u * u * (1 - u);
+ coeffsU[3] = u * u * u;
+
+ coeffsV[0] = (1 - v) * (1 - v) * (1 - v);
+ coeffsV[1] = 3 * v * (1 - v) * (1 - v);
+ coeffsV[2] = 3 * v * v * (1 - v);
+ coeffsV[3] = v * v * v;
+
+ x = y = 0.0f;
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ x += coeffsU[i] * coeffsV[j] *
+ model->objects[j * GRID_WIDTH + i].position.x;
+ y += coeffsU[i] * coeffsV[j] *
+ model->objects[j * GRID_WIDTH + i].position.y;
+ }
+ }
+
+ *patchX = x;
+ *patchY = y;
+}
+
+static Bool
+wobblyEnsureModel (CompWindow *w)
+{
+ WOBBLY_WINDOW (w);
+
+ if (!ww->model)
+ {
+ unsigned int edgeMask = 0;
+
+ if (w->type & CompWindowTypeNormalMask)
+ edgeMask = WestEdgeMask | EastEdgeMask | NorthEdgeMask |
+ SouthEdgeMask;
+
+ ww->model = createModel (WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w),
+ edgeMask);
+ if (!ww->model)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static float
+objectDistance (Object *object,
+ float x,
+ float y)
+{
+ float dx, dy;
+
+ dx = object->position.x - x;
+ dy = object->position.y - y;
+
+ return sqrt (dx * dx + dy * dy);
+}
+
+static Object *
+modelFindNearestObject (Model *model,
+ float x,
+ float y)
+{
+ Object *object = &model->objects[0];
+ float distance, minDistance = 0.0;
+ int i;
+
+ for (i = 0; i < model->numObjects; i++)
+ {
+ distance = objectDistance (&model->objects[i], x, y);
+ if (i == 0 || distance < minDistance)
+ {
+ minDistance = distance;
+ object = &model->objects[i];
+ }
+ }
+
+ return object;
+}
+
+static Bool
+isWobblyWin (CompWindow *w)
+{
+ WOBBLY_WINDOW (w);
+
+ if (ww->model)
+ return TRUE;
+
+ /* avoid tiny windows */
+ if (w->width == 1 && w->height == 1)
+ return FALSE;
+
+ /* avoid fullscreen windows */
+ if (w->attrib.x <= 0 &&
+ w->attrib.y <= 0 &&
+ w->attrib.x + w->width >= w->screen->width &&
+ w->attrib.y + w->height >= w->screen->height)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+wobblyPreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ WobblyWindow *ww;
+ CompWindow *w;
+
+ WOBBLY_SCREEN (s);
+
+ if (ws->wobblyWindows & (WobblyInitial | WobblyVelocity))
+ {
+ BoxRec box;
+ Point topLeft, bottomRight;
+ float friction, springK;
+ Model *model;
+
+ friction = ws->opt[WOBBLY_SCREEN_OPTION_FRICTION].value.f;
+ springK = ws->opt[WOBBLY_SCREEN_OPTION_SPRING_K].value.f;
+
+ ws->wobblyWindows = 0;
+ for (w = s->windows; w; w = w->next)
+ {
+ ww = GET_WOBBLY_WINDOW (w, ws);
+
+ if (ww->wobbly)
+ {
+ if (ww->wobbly & (WobblyInitial | WobblyVelocity))
+ {
+ model = ww->model;
+
+ topLeft = model->topLeft;
+ bottomRight = model->bottomRight;
+
+ ww->wobbly = modelStep (w, model, friction, springK,
+ (ww->wobbly & WobblyVelocity) ?
+ msSinceLastPaint :
+ s->redrawTime);
+
+ if ((ww->state & MAXIMIZE_STATE) && ww->grabbed)
+ ww->wobbly |= WobblyForce;
+
+ if (ww->wobbly)
+ {
+ /* snapped to more than one edge, we have to reduce
+ edge escape velocity until only one edge is snapped */
+ if (ww->wobbly == WobblyForce && !ww->grabbed)
+ {
+ modelReduceEdgeEscapeVelocity (ww->model);
+ ww->wobbly |= WobblyInitial;
+ }
+ }
+ else
+ {
+ ww->model = 0;
+
+ if (w->attrib.x == w->serverX &&
+ w->attrib.y == w->serverY)
+ {
+ moveWindow (w,
+ model->topLeft.x + w->output.left -
+ w->attrib.x,
+ model->topLeft.y + w->output.top -
+ w->attrib.y,
+ TRUE, TRUE);
+ syncWindowPosition (w);
+ }
+
+ ww->model = model;
+ }
+
+ if (!(s->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK))
+ {
+ if (ww->wobbly)
+ {
+ if (ww->model->topLeft.x < topLeft.x)
+ topLeft.x = ww->model->topLeft.x;
+ if (ww->model->topLeft.y < topLeft.y)
+ topLeft.y = ww->model->topLeft.y;
+ if (ww->model->bottomRight.x > bottomRight.x)
+ bottomRight.x = ww->model->bottomRight.x;
+ if (ww->model->bottomRight.y > bottomRight.y)
+ bottomRight.y = ww->model->bottomRight.y;
+ }
+ else
+ addWindowDamage (w);
+
+ box.x1 = topLeft.x;
+ box.y1 = topLeft.y;
+ box.x2 = bottomRight.x + 0.5f;
+ box.y2 = bottomRight.y + 0.5f;
+
+ box.x1 -= w->attrib.x + w->attrib.border_width;
+ box.y1 -= w->attrib.y + w->attrib.border_width;
+ box.x2 -= w->attrib.x + w->attrib.border_width;
+ box.y2 -= w->attrib.y + w->attrib.border_width;
+
+ addWindowDamageRect (w, &box);
+ }
+ }
+
+ ws->wobblyWindows |= ww->wobbly;
+ }
+ }
+ }
+
+ UNWRAP (ws, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (ws, s, preparePaintScreen, wobblyPreparePaintScreen);
+}
+
+static void
+wobblyDonePaintScreen (CompScreen *s)
+{
+ WOBBLY_SCREEN (s);
+
+ if (ws->wobblyWindows & (WobblyVelocity | WobblyInitial))
+ damagePendingOnScreen (s);
+
+ UNWRAP (ws, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (ws, s, donePaintScreen, wobblyDonePaintScreen);
+}
+
+static void
+wobblyDrawWindowGeometry (CompWindow *w)
+{
+ int texUnit = w->texUnits;
+ int currentTexUnit = 0;
+ int stride = w->vertexStride;
+ GLfloat *vertices = w->vertices + (stride - 3);
+
+ stride *= sizeof (GLfloat);
+
+ glVertexPointer (3, GL_FLOAT, stride, vertices);
+
+ while (texUnit--)
+ {
+ if (texUnit != currentTexUnit)
+ {
+ w->screen->clientActiveTexture (GL_TEXTURE0_ARB + texUnit);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ currentTexUnit = texUnit;
+ }
+ vertices -= w->texCoordSize;
+ glTexCoordPointer (w->texCoordSize, GL_FLOAT, stride, vertices);
+ }
+
+ glDrawElements (GL_QUADS, w->indexCount, GL_UNSIGNED_SHORT, w->indices);
+
+ /* disable all texture coordinate arrays except 0 */
+ texUnit = w->texUnits;
+ if (texUnit > 1)
+ {
+ while (--texUnit)
+ {
+ (*w->screen->clientActiveTexture) (GL_TEXTURE0_ARB + texUnit);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ }
+
+ (*w->screen->clientActiveTexture) (GL_TEXTURE0_ARB);
+ }
+}
+
+static void
+wobblyAddWindowGeometry (CompWindow *w,
+ CompMatrix *matrix,
+ int nMatrix,
+ Region region,
+ Region clip)
+{
+ WOBBLY_WINDOW (w);
+ WOBBLY_SCREEN (w->screen);
+
+ if (ww->wobbly)
+ {
+ BoxPtr pClip;
+ int nClip, nVertices, nIndices;
+ GLushort *i;
+ GLfloat *v;
+ int x1, y1, x2, y2;
+ float width, height;
+ float deformedX, deformedY;
+ int x, y, iw, ih, wx, wy;
+ int vSize, it;
+ int gridW, gridH;
+ Bool rect = TRUE;
+
+ for (it = 0; it < nMatrix; it++)
+ {
+ if (matrix[it].xy != 0.0f || matrix[it].yx != 0.0f)
+ {
+ rect = FALSE;
+ break;
+ }
+ }
+
+ wx = WIN_X (w);
+ wy = WIN_Y (w);
+ width = WIN_W (w);
+ height = WIN_H (w);
+
+ gridW = width / ws->opt[WOBBLY_SCREEN_OPTION_GRID_RESOLUTION].value.i;
+ if (gridW < ws->opt[WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE].value.i)
+ gridW = ws->opt[WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE].value.i;
+
+ gridH = height / ws->opt[WOBBLY_SCREEN_OPTION_GRID_RESOLUTION].value.i;
+ if (gridH < ws->opt[WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE].value.i)
+ gridH = ws->opt[WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE].value.i;
+
+ nClip = region->numRects;
+ pClip = region->rects;
+
+ w->texUnits = nMatrix;
+
+ vSize = 3 + nMatrix * 2;
+
+ nVertices = w->vCount;
+ nIndices = w->indexCount;
+
+ v = w->vertices + (nVertices * vSize);
+ i = w->indices + nIndices;
+
+ while (nClip--)
+ {
+ x1 = pClip->x1;
+ y1 = pClip->y1;
+ x2 = pClip->x2;
+ y2 = pClip->y2;
+
+ iw = ((x2 - x1 - 1) / gridW) + 1;
+ ih = ((y2 - y1 - 1) / gridH) + 1;
+
+ if (nIndices + (iw * ih * 4) > w->indexSize)
+ {
+ if (!moreWindowIndices (w, nIndices + (iw * ih * 4)))
+ return;
+
+ i = w->indices + nIndices;
+ }
+
+ iw++;
+ ih++;
+
+ for (y = 0; y < ih - 1; y++)
+ {
+ for (x = 0; x < iw - 1; x++)
+ {
+ *i++ = nVertices + iw * (y + 1) + x;
+ *i++ = nVertices + iw * (y + 1) + x + 1;
+ *i++ = nVertices + iw * y + x + 1;
+ *i++ = nVertices + iw * y + x;
+
+ nIndices += 4;
+ }
+ }
+
+ if (((nVertices + iw * ih) * vSize) > w->vertexSize)
+ {
+ if (!moreWindowVertices (w, (nVertices + iw * ih) * vSize))
+ return;
+
+ v = w->vertices + (nVertices * vSize);
+ }
+
+ for (y = y1;; y += gridH)
+ {
+ if (y > y2)
+ y = y2;
+
+ for (x = x1;; x += gridW)
+ {
+ if (x > x2)
+ x = x2;
+
+ bezierPatchEvaluate (ww->model,
+ (x - wx) / width,
+ (y - wy) / height,
+ &deformedX,
+ &deformedY);
+
+ if (rect)
+ {
+ for (it = 0; it < nMatrix; it++)
+ {
+ *v++ = COMP_TEX_COORD_X (&matrix[it], x);
+ *v++ = COMP_TEX_COORD_Y (&matrix[it], y);
+ }
+ }
+ else
+ {
+ for (it = 0; it < nMatrix; it++)
+ {
+ *v++ = COMP_TEX_COORD_XY (&matrix[it], x, y);
+ *v++ = COMP_TEX_COORD_YX (&matrix[it], x, y);
+ }
+ }
+
+ *v++ = deformedX;
+ *v++ = deformedY;
+ *v++ = 0.0;
+
+ nVertices++;
+
+ if (x == x2)
+ break;
+ }
+
+ if (y == y2)
+ break;
+ }
+
+ pClip++;
+ }
+
+ w->vCount = nVertices;
+ w->vertexStride = vSize;
+ w->texCoordSize = 2;
+ w->indexCount = nIndices;
+ w->drawWindowGeometry = wobblyDrawWindowGeometry;
+ }
+ else
+ {
+ UNWRAP (ws, w->screen, addWindowGeometry);
+ (*w->screen->addWindowGeometry) (w, matrix, nMatrix, region, clip);
+ WRAP (ws, w->screen, addWindowGeometry, wobblyAddWindowGeometry);
+ }
+}
+
+static Bool
+wobblyPaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ WOBBLY_SCREEN (w->screen);
+ WOBBLY_WINDOW (w);
+
+ if (ww->wobbly)
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+
+ UNWRAP (ws, w->screen, paintWindow);
+ status = (*w->screen->paintWindow) (w, attrib, transform, region, mask);
+ WRAP (ws, w->screen, paintWindow, wobblyPaintWindow);
+
+ return status;
+}
+
+static Bool
+wobblyEnableSnapping (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ CompWindow *w;
+
+ WOBBLY_DISPLAY (d);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ for (w = s->windows; w; w = w->next)
+ {
+ WOBBLY_WINDOW (w);
+
+ if (ww->grabbed && ww->model)
+ modelUpdateSnapping (w, ww->model);
+ }
+ }
+
+ wd->snapping = TRUE;
+
+ return FALSE;
+}
+
+static Bool
+wobblyDisableSnapping (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ CompWindow *w;
+
+ WOBBLY_DISPLAY (d);
+
+ if (!wd->snapping)
+ return FALSE;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ for (w = s->windows; w; w = w->next)
+ {
+ WOBBLY_WINDOW (w);
+
+ if (ww->grabbed && ww->model)
+ {
+ if (modelDisableSnapping (w, ww->model))
+ {
+ WOBBLY_SCREEN (w->screen);
+
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+
+ damagePendingOnScreen (w->screen);
+ }
+ }
+ }
+ }
+
+ wd->snapping = FALSE;
+
+ return FALSE;
+}
+
+static Bool
+wobblyShiver (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompWindow *w;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "window", 0);
+
+ w = findWindowAtDisplay (d, xid);
+ if (w && isWobblyWin (w) && wobblyEnsureModel (w))
+ {
+ WOBBLY_SCREEN (w->screen);
+ WOBBLY_WINDOW (w);
+
+ modelSetMiddleAnchor (ww->model,
+ WIN_X (w), WIN_Y (w),
+ WIN_W (w), WIN_H (w));
+ modelAdjustObjectsForShiver (ww->model,
+ WIN_X (w), WIN_Y (w),
+ WIN_W (w), WIN_H (w));
+
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+
+ damagePendingOnScreen (w->screen);
+ }
+
+ return FALSE;
+}
+
+static void
+wobblyHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ Window activeWindow = d->activeWindow;
+ CompWindow *w;
+ CompScreen *s;
+
+ WOBBLY_DISPLAY (d);
+
+ switch (event->type) {
+ case MapNotify:
+ w = findWindowAtDisplay (d, event->xmap.window);
+ if (w)
+ {
+ WOBBLY_WINDOW (w);
+
+ if (ww->model)
+ {
+ modelInitObjects (ww->model,
+ WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w));
+
+ modelInitSprings (ww->model,
+ WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w));
+ }
+ }
+ break;
+ default:
+ if (event->type == d->xkbEvent)
+ {
+ XkbAnyEvent *xkbEvent = (XkbAnyEvent *) event;
+
+ if (xkbEvent->xkb_type == XkbStateNotify)
+ {
+ XkbStateNotifyEvent *stateEvent = (XkbStateNotifyEvent *) event;
+ CompAction *action;
+ Bool inverted;
+ unsigned int mods = 0xffffffff;
+
+ action =
+ &wd->opt[WOBBLY_DISPLAY_OPTION_SNAP_KEY].value.action;
+ inverted = wd->opt[WOBBLY_DISPLAY_OPTION_SNAP_INVERTED].value.b;
+
+ if (action->type & CompBindingTypeKey)
+ mods = action->key.modifiers;
+
+ if ((stateEvent->mods & mods) == mods)
+ {
+ if (inverted)
+ wobblyDisableSnapping (d, NULL, 0, NULL, 0);
+ else
+ wobblyEnableSnapping (d, NULL, 0, NULL, 0);
+ }
+ else
+ {
+ if (inverted)
+ wobblyEnableSnapping (d, NULL, 0, NULL, 0);
+ else
+ wobblyDisableSnapping (d, NULL, 0, NULL, 0);
+ }
+ }
+ }
+ break;
+ }
+
+ UNWRAP (wd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (wd, d, handleEvent, wobblyHandleEvent);
+
+ switch (event->type) {
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ {
+ WOBBLY_SCREEN (s);
+
+ if (ws->grabWindow &&
+ ws->moveWindow &&
+ ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
+ {
+ WOBBLY_WINDOW (ws->grabWindow);
+
+ if (ww->state & MAXIMIZE_STATE)
+ {
+ if (ww->model && ww->grabbed)
+ {
+ int dx, dy;
+
+ if (ww->state & CompWindowStateMaximizedHorzMask)
+ dx = pointerX - lastPointerX;
+ else
+ dx = 0;
+
+ if (ww->state & CompWindowStateMaximizedVertMask)
+ dy = pointerY - lastPointerY;
+ else
+ dy = 0;
+
+ ww->model->anchorObject->position.x += dx;
+ ww->model->anchorObject->position.y += dy;
+
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+
+ damagePendingOnScreen (s);
+ }
+ }
+ }
+ }
+ default:
+ break;
+ }
+
+ if (d->activeWindow != activeWindow)
+ {
+ w = findWindowAtDisplay (d, d->activeWindow);
+ if (w && isWobblyWin (w))
+ {
+ int mIndex;
+ int focusEffect;
+
+ WOBBLY_WINDOW (w);
+ WOBBLY_SCREEN (w->screen);
+
+ mIndex = WOBBLY_SCREEN_OPTION_FOCUS_WINDOW_MATCH;
+ focusEffect = ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_EFFECT].value.i;
+
+ if ((focusEffect != WOBBLY_EFFECT_NONE) &&
+ matchEval (&ws->opt[mIndex].value.match, w) &&
+ wobblyEnsureModel (w))
+ {
+ switch (focusEffect) {
+ case WOBBLY_EFFECT_SHIVER:
+ modelAdjustObjectsForShiver (ww->model,
+ WIN_X (w),
+ WIN_Y (w),
+ WIN_W (w),
+ WIN_H (w));
+ default:
+ break;
+ }
+
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+
+ damagePendingOnScreen (w->screen);
+ }
+ }
+ }
+}
+
+static Bool
+wobblyDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status;
+
+ WOBBLY_SCREEN (w->screen);
+
+ if (!initial)
+ {
+ WOBBLY_WINDOW (w);
+
+ if (ww->wobbly == WobblyForce)
+ {
+ REGION region;
+
+ region.rects = &region.extents;
+ region.numRects = region.size = 1;
+
+ region.extents.x1 = ww->model->topLeft.x;
+ region.extents.y1 = ww->model->topLeft.y;
+ region.extents.x2 = ww->model->bottomRight.x + 0.5f;
+ region.extents.y2 = ww->model->bottomRight.y + 0.5f;
+
+ damageScreenRegion (w->screen, &region);
+
+ return TRUE;
+ }
+ }
+
+ UNWRAP (ws, w->screen, damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (ws, w->screen, damageWindowRect, wobblyDamageWindowRect);
+
+ if (initial)
+ {
+ if (isWobblyWin (w))
+ {
+ int mIndex;
+ int mapEffect;
+
+ WOBBLY_WINDOW (w);
+ WOBBLY_SCREEN (w->screen);
+
+ mIndex = WOBBLY_SCREEN_OPTION_MAP_WINDOW_MATCH;
+ mapEffect = ws->opt[WOBBLY_SCREEN_OPTION_MAP_EFFECT].value.i;
+
+ if (ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
+ wobblyEnsureModel (w);
+
+ if ((mapEffect != WOBBLY_EFFECT_NONE) &&
+ matchEval (&ws->opt[mIndex].value.match, w) &&
+ wobblyEnsureModel (w))
+ {
+ switch (mapEffect) {
+ case WOBBLY_EFFECT_SHIVER:
+ modelAdjustObjectsForShiver (ww->model,
+ WIN_X (w), WIN_Y (w),
+ WIN_W (w), WIN_H (w));
+ default:
+ break;
+ }
+
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+
+ damagePendingOnScreen (w->screen);
+ }
+ }
+ }
+
+ return status;
+}
+
+static void
+wobblyWindowResizeNotify (CompWindow *w,
+ int dx,
+ int dy,
+ int dwidth,
+ int dheight)
+{
+ WOBBLY_SCREEN (w->screen);
+ WOBBLY_WINDOW (w);
+
+ if (ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b &&
+ isWobblyWin (w) &&
+ /* prevent wobbling when shading maximized windows - assuming that
+ the height difference shaded - non-shaded will hardly be -1 and
+ a lack of wobbly animation in that corner case is tolerable */
+ (dheight != -1) &&
+ ((w->state | ww->state) & MAXIMIZE_STATE))
+ {
+ ww->state &= ~MAXIMIZE_STATE;
+ ww->state |= w->state & MAXIMIZE_STATE;
+
+ if (wobblyEnsureModel (w))
+ {
+ if (w->state & MAXIMIZE_STATE)
+ {
+ if (!ww->grabbed && ww->model->anchorObject)
+ {
+ ww->model->anchorObject->immobile = FALSE;
+ ww->model->anchorObject = NULL;
+ }
+
+ modelAddEdgeAnchors (ww->model,
+ WIN_X (w), WIN_Y (w),
+ WIN_W (w), WIN_H (w));
+ }
+ else
+ {
+ modelRemoveEdgeAnchors (ww->model,
+ WIN_X (w), WIN_Y (w),
+ WIN_W (w), WIN_H (w));
+ modelSetMiddleAnchor (ww->model,
+ WIN_X (w), WIN_Y (w),
+ WIN_W (w), WIN_H (w));
+ }
+
+ modelInitSprings (ww->model,
+ WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w));
+
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+
+ damagePendingOnScreen (w->screen);
+ }
+ }
+ else if (ww->model)
+ {
+ if (ww->wobbly)
+ {
+ if (!(ww->state & MAXIMIZE_STATE))
+ modelSetTopAnchor (ww->model, WIN_X (w), WIN_Y (w), WIN_W (w));
+ }
+ else
+ {
+ modelInitObjects (ww->model,
+ WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w));
+ }
+
+ modelInitSprings (ww->model,
+ WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w));
+ }
+
+ /* update grab */
+ if (ww->model && ww->grabbed)
+ {
+ if (ww->model->anchorObject)
+ ww->model->anchorObject->immobile = FALSE;
+
+ ww->model->anchorObject = modelFindNearestObject (ww->model,
+ pointerX,
+ pointerY);
+ ww->model->anchorObject->immobile = TRUE;
+
+ modelAdjustObjectPosition (ww->model,
+ ww->model->anchorObject,
+ WIN_X (w), WIN_Y (w),
+ WIN_W (w), WIN_H (w));
+ }
+
+ UNWRAP (ws, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
+ WRAP (ws, w->screen, windowResizeNotify, wobblyWindowResizeNotify);
+}
+
+static void
+wobblyWindowMoveNotify (CompWindow *w,
+ int dx,
+ int dy,
+ Bool immediate)
+{
+ WOBBLY_SCREEN (w->screen);
+ WOBBLY_WINDOW (w);
+
+ if (ww->model)
+ {
+ if (ww->grabbed && !immediate)
+ {
+ if (ww->state & MAXIMIZE_STATE)
+ {
+ int i;
+
+ for (i = 0; i < ww->model->numObjects; i++)
+ {
+ if (ww->model->objects[i].immobile)
+ {
+ ww->model->objects[i].position.x += dx;
+ ww->model->objects[i].position.y += dy;
+ }
+ }
+ }
+ else
+ {
+ ww->model->anchorObject->position.x += dx;
+ ww->model->anchorObject->position.y += dy;
+ }
+
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+
+ damagePendingOnScreen (w->screen);
+ }
+ else
+ modelMove (ww->model, dx, dy);
+ }
+
+ UNWRAP (ws, w->screen, windowMoveNotify);
+ (*w->screen->windowMoveNotify) (w, dx, dy, immediate);
+ WRAP (ws, w->screen, windowMoveNotify, wobblyWindowMoveNotify);
+}
+
+static void
+wobblyWindowGrabNotify (CompWindow *w,
+ int x,
+ int y,
+ unsigned int state,
+ unsigned int mask)
+{
+ int mIndex;
+
+ WOBBLY_SCREEN (w->screen);
+
+ mIndex = WOBBLY_SCREEN_OPTION_MOVE_WINDOW_MATCH;
+
+ if (!ws->grabWindow)
+ {
+ ws->grabMask = mask;
+ ws->grabWindow = w;
+ }
+ ws->moveWindow = FALSE;
+
+ if ((mask & CompWindowGrabButtonMask) &&
+ matchEval (&ws->opt[mIndex].value.match, w) &&
+ isWobblyWin (w))
+ {
+ WOBBLY_WINDOW (w);
+
+ ws->moveWindow = TRUE;
+
+ if (wobblyEnsureModel (w))
+ {
+ Spring *s;
+ int i;
+
+ if (ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
+ {
+ if (w->state & MAXIMIZE_STATE)
+ {
+ modelAddEdgeAnchors (ww->model,
+ WIN_X (w), WIN_Y (w),
+ WIN_W (w), WIN_H (w));
+ }
+ else
+ {
+ modelRemoveEdgeAnchors (ww->model,
+ WIN_X (w), WIN_Y (w),
+ WIN_W (w), WIN_H (w));
+
+ if (ww->model->anchorObject)
+ ww->model->anchorObject->immobile = FALSE;
+ }
+ }
+ else
+ {
+ if (ww->model->anchorObject)
+ ww->model->anchorObject->immobile = FALSE;
+ }
+
+ ww->model->anchorObject = modelFindNearestObject (ww->model, x, y);
+ ww->model->anchorObject->immobile = TRUE;
+
+ ww->grabbed = TRUE;
+
+ if (mask & CompWindowGrabMoveMask)
+ {
+ WOBBLY_DISPLAY (w->screen->display);
+
+ modelDisableSnapping (w, ww->model);
+ if (wd->snapping)
+ modelUpdateSnapping (w, ww->model);
+ }
+
+ mIndex = WOBBLY_SCREEN_OPTION_GRAB_WINDOW_MATCH;
+
+ if (matchEval (&ws->opt[mIndex].value.match, w))
+ {
+ for (i = 0; i < ww->model->numSprings; i++)
+ {
+ s = &ww->model->springs[i];
+
+ if (s->a == ww->model->anchorObject)
+ {
+ s->b->velocity.x -= s->offset.x * 0.05f;
+ s->b->velocity.y -= s->offset.y * 0.05f;
+ }
+ else if (s->b == ww->model->anchorObject)
+ {
+ s->a->velocity.x += s->offset.x * 0.05f;
+ s->a->velocity.y += s->offset.y * 0.05f;
+ }
+ }
+
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+
+ damagePendingOnScreen (w->screen);
+ }
+ }
+ }
+
+ UNWRAP (ws, w->screen, windowGrabNotify);
+ (*w->screen->windowGrabNotify) (w, x, y, state, mask);
+ WRAP (ws, w->screen, windowGrabNotify, wobblyWindowGrabNotify);
+}
+
+static void
+wobblyWindowUngrabNotify (CompWindow *w)
+{
+ WOBBLY_SCREEN (w->screen);
+ WOBBLY_WINDOW (w);
+
+ if (w == ws->grabWindow)
+ {
+ ws->grabMask = 0;
+ ws->grabWindow = NULL;
+ }
+
+ if (ww->grabbed)
+ {
+ if (ww->model)
+ {
+ if (ww->model->anchorObject)
+ ww->model->anchorObject->immobile = FALSE;
+
+ ww->model->anchorObject = NULL;
+
+ if (ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
+ {
+ if (ww->state & MAXIMIZE_STATE)
+ modelAddEdgeAnchors (ww->model,
+ WIN_X (w), WIN_Y (w),
+ WIN_W (w), WIN_H (w));
+ }
+
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+
+ damagePendingOnScreen (w->screen);
+ }
+
+ ww->grabbed = FALSE;
+ }
+
+ UNWRAP (ws, w->screen, windowUngrabNotify);
+ (*w->screen->windowUngrabNotify) (w);
+ WRAP (ws, w->screen, windowUngrabNotify, wobblyWindowUngrabNotify);
+}
+
+
+static Bool
+wobblyPaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ Bool status;
+
+ WOBBLY_SCREEN (s);
+
+ if (ws->wobblyWindows)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+
+ UNWRAP (ws, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+ WRAP (ws, s, paintOutput, wobblyPaintOutput);
+
+ return status;
+}
+
+static CompOption *
+wobblyGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ WOBBLY_DISPLAY (display);
+
+ *count = NUM_OPTIONS (wd);
+ return wd->opt;
+}
+
+static Bool
+wobblySetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ WOBBLY_DISPLAY (display);
+
+ o = compFindOption (wd->opt, NUM_OPTIONS (wd), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case WOBBLY_DISPLAY_OPTION_SNAP_KEY:
+ /* ignore the key */
+ value->action.key.keycode = 0;
+
+ if (compSetActionOption (o, value))
+ return TRUE;
+ break;
+ default:
+ return compSetDisplayOption (display, o, value);
+ }
+
+ return FALSE;
+}
+
+static const CompMetadataOptionInfo wobblyDisplayOptionInfo[] = {
+ { "snap_key", "key", "<passive_grab>false</passive_grab>",
+ wobblyEnableSnapping, wobblyDisableSnapping },
+ { "snap_inverted", "bool", 0, 0, 0 },
+ { "shiver", "bell", 0, wobblyShiver, 0 }
+};
+
+static Bool
+wobblyInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ WobblyDisplay *wd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ wd = malloc (sizeof (WobblyDisplay));
+ if (!wd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &wobblyMetadata,
+ wobblyDisplayOptionInfo,
+ wd->opt,
+ WOBBLY_DISPLAY_OPTION_NUM))
+ {
+ free (wd);
+ return FALSE;
+ }
+
+ wd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (wd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, wd->opt, WOBBLY_DISPLAY_OPTION_NUM);
+ free (wd);
+ return FALSE;
+ }
+
+ WRAP (wd, d, handleEvent, wobblyHandleEvent);
+
+ wd->snapping = FALSE;
+
+ d->base.privates[displayPrivateIndex].ptr = wd;
+
+ return TRUE;
+}
+
+static void
+wobblyFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ WOBBLY_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, wd->screenPrivateIndex);
+
+ UNWRAP (wd, d, handleEvent);
+
+ compFiniDisplayOptions (d, wd->opt, WOBBLY_DISPLAY_OPTION_NUM);
+
+ free (wd);
+}
+
+static Bool
+wobblyInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ WobblyScreen *ws;
+
+ WOBBLY_DISPLAY (s->display);
+
+ ws = malloc (sizeof (WobblyScreen));
+ if (!ws)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &wobblyMetadata,
+ wobblyScreenOptionInfo,
+ ws->opt,
+ WOBBLY_SCREEN_OPTION_NUM))
+ {
+ free (ws);
+ return FALSE;
+ }
+
+ ws->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (ws->windowPrivateIndex < 0)
+ {
+ compFiniScreenOptions (s, ws->opt, WOBBLY_SCREEN_OPTION_NUM);
+ free (ws);
+ return FALSE;
+ }
+
+ ws->wobblyWindows = FALSE;
+
+ ws->grabMask = 0;
+ ws->grabWindow = NULL;
+ ws->moveWindow = FALSE;
+
+ WRAP (ws, s, preparePaintScreen, wobblyPreparePaintScreen);
+ WRAP (ws, s, donePaintScreen, wobblyDonePaintScreen);
+ WRAP (ws, s, paintOutput, wobblyPaintOutput);
+ WRAP (ws, s, paintWindow, wobblyPaintWindow);
+ WRAP (ws, s, damageWindowRect, wobblyDamageWindowRect);
+ WRAP (ws, s, addWindowGeometry, wobblyAddWindowGeometry);
+ WRAP (ws, s, windowResizeNotify, wobblyWindowResizeNotify);
+ WRAP (ws, s, windowMoveNotify, wobblyWindowMoveNotify);
+ WRAP (ws, s, windowGrabNotify, wobblyWindowGrabNotify);
+ WRAP (ws, s, windowUngrabNotify, wobblyWindowUngrabNotify);
+
+ s->base.privates[wd->screenPrivateIndex].ptr = ws;
+
+ return TRUE;
+}
+
+static void
+wobblyFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ WOBBLY_SCREEN (s);
+
+ freeWindowPrivateIndex (s, ws->windowPrivateIndex);
+
+ UNWRAP (ws, s, preparePaintScreen);
+ UNWRAP (ws, s, donePaintScreen);
+ UNWRAP (ws, s, paintOutput);
+ UNWRAP (ws, s, paintWindow);
+ UNWRAP (ws, s, damageWindowRect);
+ UNWRAP (ws, s, addWindowGeometry);
+ UNWRAP (ws, s, windowResizeNotify);
+ UNWRAP (ws, s, windowMoveNotify);
+ UNWRAP (ws, s, windowGrabNotify);
+ UNWRAP (ws, s, windowUngrabNotify);
+
+ compFiniScreenOptions (s, ws->opt, WOBBLY_SCREEN_OPTION_NUM);
+
+ free (ws);
+}
+
+static Bool
+wobblyInitWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ WobblyWindow *ww;
+
+ WOBBLY_SCREEN (w->screen);
+
+ ww = malloc (sizeof (WobblyWindow));
+ if (!ww)
+ return FALSE;
+
+ ww->model = 0;
+ ww->wobbly = 0;
+ ww->grabbed = FALSE;
+ ww->state = w->state;
+
+ w->base.privates[ws->windowPrivateIndex].ptr = ww;
+
+ if (w->mapNum && ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
+ {
+ if (isWobblyWin (w))
+ wobblyEnsureModel (w);
+ }
+
+ return TRUE;
+}
+
+static void
+wobblyFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ WOBBLY_WINDOW (w);
+ WOBBLY_SCREEN (w->screen);
+
+ if (ws->grabWindow == w)
+ {
+ ws->grabWindow = NULL;
+ ws->grabMask = 0;
+ }
+
+ if (ww->model)
+ {
+ free (ww->model->objects);
+ free (ww->model);
+ }
+
+ free (ww);
+}
+
+static CompBool
+wobblyInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) wobblyInitDisplay,
+ (InitPluginObjectProc) wobblyInitScreen,
+ (InitPluginObjectProc) wobblyInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+wobblyFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) wobblyFiniDisplay,
+ (FiniPluginObjectProc) wobblyFiniScreen,
+ (FiniPluginObjectProc) wobblyFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+wobblyGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) wobblyGetDisplayOptions,
+ (GetPluginObjectOptionsProc) wobblyGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+wobblySetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) wobblySetDisplayOption,
+ (SetPluginObjectOptionProc) wobblySetScreenOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+wobblyInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&wobblyMetadata,
+ p->vTable->name,
+ wobblyDisplayOptionInfo,
+ WOBBLY_DISPLAY_OPTION_NUM,
+ wobblyScreenOptionInfo,
+ WOBBLY_SCREEN_OPTION_NUM))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&wobblyMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&wobblyMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+wobblyFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&wobblyMetadata);
+}
+
+static CompMetadata *
+wobblyGetMetadata (CompPlugin *plugin)
+{
+ return &wobblyMetadata;
+}
+
+CompPluginVTable wobblyVTable = {
+ "wobbly",
+ wobblyGetMetadata,
+ wobblyInit,
+ wobblyFini,
+ wobblyInitObject,
+ wobblyFiniObject,
+ wobblyGetObjectOptions,
+ wobblySetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &wobblyVTable;
+}
diff --git a/plugins/zoom.c b/plugins/zoom.c
new file mode 100644
index 0000000..3726a47
--- /dev/null
+++ b/plugins/zoom.c
@@ -0,0 +1,1203 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * 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, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sys/time.h>
+
+#include <X11/cursorfont.h>
+
+#include <compiz-core.h>
+
+static CompMetadata zoomMetadata;
+
+static int displayPrivateIndex;
+
+#define ZOOM_DISPLAY_OPTION_INITIATE_BUTTON 0
+#define ZOOM_DISPLAY_OPTION_IN_BUTTON 1
+#define ZOOM_DISPLAY_OPTION_OUT_BUTTON 2
+#define ZOOM_DISPLAY_OPTION_PAN_BUTTON 3
+#define ZOOM_DISPLAY_OPTION_NUM 4
+
+typedef struct _ZoomDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompOption opt[ZOOM_DISPLAY_OPTION_NUM];
+} ZoomDisplay;
+
+typedef struct _ZoomBox {
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+} ZoomBox;
+
+#define ZOOM_SCREEN_OPTION_SPEED 0
+#define ZOOM_SCREEN_OPTION_TIMESTEP 1
+#define ZOOM_SCREEN_OPTION_ZOOM_FACTOR 2
+#define ZOOM_SCREEN_OPTION_FILTER_LINEAR 3
+#define ZOOM_SCREEN_OPTION_NUM 4
+
+typedef struct _ZoomScreen {
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintOutputProc paintOutput;
+
+ CompOption opt[ZOOM_SCREEN_OPTION_NUM];
+
+ float pointerSensitivity;
+
+ int grabIndex;
+ Bool grab;
+
+ int zoomed;
+
+ Bool adjust;
+
+ int panGrabIndex;
+ Cursor panCursor;
+
+ GLfloat velocity;
+ GLfloat scale;
+
+ ZoomBox current[16];
+ ZoomBox last[16];
+
+ int x1, y1, x2, y2;
+
+ int zoomOutput;
+} ZoomScreen;
+
+#define GET_ZOOM_DISPLAY(d) \
+ ((ZoomDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define ZOOM_DISPLAY(d) \
+ ZoomDisplay *zd = GET_ZOOM_DISPLAY (d)
+
+#define GET_ZOOM_SCREEN(s, zd) \
+ ((ZoomScreen *) (s)->base.privates[(zd)->screenPrivateIndex].ptr)
+
+#define ZOOM_SCREEN(s) \
+ ZoomScreen *zs = GET_ZOOM_SCREEN (s, GET_ZOOM_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static CompOption *
+zoomGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+ int *count)
+{
+ ZOOM_SCREEN (screen);
+
+ *count = NUM_OPTIONS (zs);
+ return zs->opt;
+}
+
+static Bool
+zoomSetScreenOption (CompPlugin *plugin,
+ CompScreen *screen,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+
+ ZOOM_SCREEN (screen);
+
+ o = compFindOption (zs->opt, NUM_OPTIONS (zs), name, NULL);
+ if (!o)
+ return FALSE;
+
+ return compSetScreenOption (screen, o, value);
+}
+
+static int
+adjustZoomVelocity (ZoomScreen *zs)
+{
+ float d, adjust, amount;
+
+ d = (1.0f - zs->scale) * 10.0f;
+
+ adjust = d * 0.002f;
+ amount = fabs (d);
+ if (amount < 1.0f)
+ amount = 1.0f;
+ else if (amount > 5.0f)
+ amount = 5.0f;
+
+ zs->velocity = (amount * zs->velocity + adjust) / (amount + 1.0f);
+
+ return (fabs (d) < 0.02f && fabs (zs->velocity) < 0.005f);
+}
+
+static void
+zoomInEvent (CompScreen *s)
+{
+ CompOption o[6];
+
+ ZOOM_SCREEN (s);
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "root";
+ o[0].value.i = s->root;
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "output";
+ o[1].value.i = zs->zoomOutput;
+
+ o[2].type = CompOptionTypeInt;
+ o[2].name = "x1";
+ o[2].value.i = zs->current[zs->zoomOutput].x1;
+
+ o[3].type = CompOptionTypeInt;
+ o[3].name = "y1";
+ o[3].value.i = zs->current[zs->zoomOutput].y1;
+
+ o[4].type = CompOptionTypeInt;
+ o[4].name = "x2";
+ o[4].value.i = zs->current[zs->zoomOutput].x2;
+
+ o[5].type = CompOptionTypeInt;
+ o[5].name = "y2";
+ o[5].value.i = zs->current[zs->zoomOutput].y2;
+
+ (*s->display->handleCompizEvent) (s->display, "zoom", "in", o, 6);
+}
+
+static void
+zoomOutEvent (CompScreen *s)
+{
+ CompOption o[2];
+
+ ZOOM_SCREEN (s);
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "root";
+ o[0].value.i = s->root;
+
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "output";
+ o[1].value.i = zs->zoomOutput;
+
+ (*s->display->handleCompizEvent) (s->display, "zoom", "out", o, 2);
+}
+
+static void
+zoomPreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ ZOOM_SCREEN (s);
+
+ if (zs->adjust)
+ {
+ int steps;
+ float amount;
+
+ amount = msSinceLastPaint * 0.35f *
+ zs->opt[ZOOM_SCREEN_OPTION_SPEED].value.f;
+ steps = amount / (0.5f * zs->opt[ZOOM_SCREEN_OPTION_TIMESTEP].value.f);
+ if (!steps) steps = 1;
+
+ while (steps--)
+ {
+ if (adjustZoomVelocity (zs))
+ {
+ BoxPtr pBox = &s->outputDev[zs->zoomOutput].region.extents;
+
+ zs->scale = 1.0f;
+ zs->velocity = 0.0f;
+ zs->adjust = FALSE;
+
+ if (zs->current[zs->zoomOutput].x1 == pBox->x1 &&
+ zs->current[zs->zoomOutput].y1 == pBox->y1 &&
+ zs->current[zs->zoomOutput].x2 == pBox->x2 &&
+ zs->current[zs->zoomOutput].y2 == pBox->y2)
+ {
+ zs->zoomed &= ~(1 << zs->zoomOutput);
+ zoomOutEvent (s);
+ }
+ else
+ {
+ zoomInEvent (s);
+ }
+
+ break;
+ }
+ else
+ {
+ zs->scale += (zs->velocity * msSinceLastPaint) / s->redrawTime;
+ }
+ }
+ }
+
+ UNWRAP (zs, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (zs, s, preparePaintScreen, zoomPreparePaintScreen);
+}
+
+static void
+zoomGetCurrentZoom (CompScreen *s,
+ int output,
+ ZoomBox *pBox)
+{
+ ZOOM_SCREEN (s);
+
+ if (output == zs->zoomOutput)
+ {
+ float inverse;
+
+ inverse = 1.0f - zs->scale;
+
+ pBox->x1 = zs->scale * zs->current[output].x1 +
+ inverse * zs->last[output].x1;
+ pBox->y1 = zs->scale * zs->current[output].y1 +
+ inverse * zs->last[output].y1;
+ pBox->x2 = zs->scale * zs->current[output].x2 +
+ inverse * zs->last[output].x2;
+ pBox->y2 = zs->scale * zs->current[output].y2 +
+ inverse * zs->last[output].y2;
+ }
+ else
+ {
+ pBox->x1 = zs->current[output].x1;
+ pBox->y1 = zs->current[output].y1;
+ pBox->x2 = zs->current[output].x2;
+ pBox->y2 = zs->current[output].y2;
+ }
+}
+
+static void
+zoomDonePaintScreen (CompScreen *s)
+{
+ ZOOM_SCREEN (s);
+
+ if (zs->adjust)
+ damageScreen (s);
+
+ UNWRAP (zs, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (zs, s, donePaintScreen, zoomDonePaintScreen);
+}
+
+static Bool
+zoomPaintOutput (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ CompTransform zTransform = *transform;
+ Bool status;
+
+ ZOOM_SCREEN (s);
+
+ if (output->id != ~0 && (zs->zoomed & (1 << output->id)))
+ {
+ int saveFilter;
+ ZoomBox box;
+ float scale, x, y, x1, y1;
+ float oWidth = output->width;
+ float oHeight = output->height;
+
+ mask &= ~PAINT_SCREEN_REGION_MASK;
+
+ zoomGetCurrentZoom (s, output->id, &box);
+
+ x1 = box.x1 - output->region.extents.x1;
+ y1 = box.y1 - output->region.extents.y1;
+
+ scale = oWidth / (box.x2 - box.x1);
+
+ x = ((oWidth / 2.0f) - x1) / oWidth;
+ y = ((oHeight / 2.0f) - y1) / oHeight;
+
+ x = 0.5f - x * scale;
+ y = 0.5f - y * scale;
+
+ matrixTranslate (&zTransform, -x, y, 0.0f);
+ matrixScale (&zTransform, scale, scale, 1.0f);
+
+ mask |= PAINT_SCREEN_TRANSFORMED_MASK;
+
+ saveFilter = s->filter[SCREEN_TRANS_FILTER];
+
+ if ((zs->zoomOutput != output->id || !zs->adjust) && scale > 3.9f &&
+ !zs->opt[ZOOM_SCREEN_OPTION_FILTER_LINEAR].value.b)
+ s->filter[SCREEN_TRANS_FILTER] = COMP_TEXTURE_FILTER_FAST;
+
+ UNWRAP (zs, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, &zTransform, region, output,
+ mask);
+ WRAP (zs, s, paintOutput, zoomPaintOutput);
+
+ s->filter[SCREEN_TRANS_FILTER] = saveFilter;
+ }
+ else
+ {
+ UNWRAP (zs, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output,
+ mask);
+ WRAP (zs, s, paintOutput, zoomPaintOutput);
+ }
+
+ if (status && zs->grab)
+ {
+ int x1, x2, y1, y2;
+
+ x1 = MIN (zs->x1, zs->x2);
+ y1 = MIN (zs->y1, zs->y2);
+ x2 = MAX (zs->x1, zs->x2);
+ y2 = MAX (zs->y1, zs->y2);
+
+ if (zs->grabIndex)
+ {
+ transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &zTransform);
+
+ glPushMatrix ();
+ glLoadMatrixf (zTransform.m);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ glEnable (GL_BLEND);
+ glColor4us (0x2fff, 0x2fff, 0x4fff, 0x4fff);
+ glRecti (x1, y2, x2, y1);
+ glColor4us (0x2fff, 0x2fff, 0x4fff, 0x9fff);
+ glBegin (GL_LINE_LOOP);
+ glVertex2i (x1, y1);
+ glVertex2i (x2, y1);
+ glVertex2i (x2, y2);
+ glVertex2i (x1, y2);
+ glEnd ();
+ glColor4usv (defaultColor);
+ glDisable (GL_BLEND);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ glPopMatrix ();
+ }
+ }
+
+ return status;
+}
+
+static void
+zoomInitiateForSelection (CompScreen *s,
+ int output)
+{
+ int tmp;
+
+ ZOOM_SCREEN (s);
+
+ if (zs->x1 > zs->x2)
+ {
+ tmp = zs->x1;
+ zs->x1 = zs->x2;
+ zs->x2 = tmp;
+ }
+
+ if (zs->y1 > zs->y2)
+ {
+ tmp = zs->y1;
+ zs->y1 = zs->y2;
+ zs->y2 = tmp;
+ }
+
+ if (zs->x1 < zs->x2 && zs->y1 < zs->y2)
+ {
+ float oWidth, oHeight;
+ float xScale, yScale, scale;
+ BoxRec box;
+ int cx, cy;
+ int width, height;
+
+ oWidth = s->outputDev[output].width;
+ oHeight = s->outputDev[output].height;
+
+ cx = (int) ((zs->x1 + zs->x2) / 2.0f + 0.5f);
+ cy = (int) ((zs->y1 + zs->y2) / 2.0f + 0.5f);
+
+ width = zs->x2 - zs->x1;
+ height = zs->y2 - zs->y1;
+
+ xScale = oWidth / width;
+ yScale = oHeight / height;
+
+ scale = MAX (MIN (xScale, yScale), 1.0f);
+
+ box.x1 = cx - (oWidth / scale) / 2.0f;
+ box.y1 = cy - (oHeight / scale) / 2.0f;
+ box.x2 = cx + (oWidth / scale) / 2.0f;
+ box.y2 = cy + (oHeight / scale) / 2.0f;
+
+ if (box.x1 < s->outputDev[output].region.extents.x1)
+ {
+ box.x2 += s->outputDev[output].region.extents.x1 - box.x1;
+ box.x1 = s->outputDev[output].region.extents.x1;
+ }
+ else if (box.x2 > s->outputDev[output].region.extents.x2)
+ {
+ box.x1 -= box.x2 - s->outputDev[output].region.extents.x2;
+ box.x2 = s->outputDev[output].region.extents.x2;
+ }
+
+ if (box.y1 < s->outputDev[output].region.extents.y1)
+ {
+ box.y2 += s->outputDev[output].region.extents.y1 - box.y1;
+ box.y1 = s->outputDev[output].region.extents.y1;
+ }
+ else if (box.y2 > s->outputDev[output].region.extents.y2)
+ {
+ box.y1 -= box.y2 - s->outputDev[output].region.extents.y2;
+ box.y2 = s->outputDev[output].region.extents.y2;
+ }
+
+ if (zs->zoomed & (1 << output))
+ {
+ zoomGetCurrentZoom (s, output, &zs->last[output]);
+ }
+ else
+ {
+ zs->last[output].x1 = s->outputDev[output].region.extents.x1;
+ zs->last[output].y1 = s->outputDev[output].region.extents.y1;
+ zs->last[output].x2 = s->outputDev[output].region.extents.x2;
+ zs->last[output].y2 = s->outputDev[output].region.extents.y2;
+ }
+
+ zs->current[output].x1 = box.x1;
+ zs->current[output].y1 = box.y1;
+ zs->current[output].x2 = box.x2;
+ zs->current[output].y2 = box.y2;
+
+ zs->scale = 0.0f;
+ zs->adjust = TRUE;
+ zs->zoomOutput = output;
+ zs->zoomed |= (1 << output);
+
+ damageScreen (s);
+ }
+}
+
+static Bool
+zoomIn (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ float w, h, x0, y0;
+ int output;
+ ZoomBox box;
+
+ ZOOM_SCREEN (s);
+
+ output = outputDeviceForPoint (s, pointerX, pointerY);
+
+ if (!zs->grabIndex)
+ zs->grabIndex = pushScreenGrab (s, None, "zoom");
+
+ if (zs->zoomed & (1 << output))
+ {
+ zoomGetCurrentZoom (s, output, &box);
+ }
+ else
+ {
+ box.x1 = s->outputDev[output].region.extents.x1;
+ box.y1 = s->outputDev[output].region.extents.y1;
+ box.x2 = s->outputDev[output].region.extents.x2;
+ box.y2 = s->outputDev[output].region.extents.y2;
+ }
+
+ w = (box.x2 - box.x1) /
+ zs->opt[ZOOM_SCREEN_OPTION_ZOOM_FACTOR].value.f;
+ h = (box.y2 - box.y1) /
+ zs->opt[ZOOM_SCREEN_OPTION_ZOOM_FACTOR].value.f;
+
+ x0 = (pointerX - s->outputDev[output].region.extents.x1) / (float)
+ s->outputDev[output].width;
+ y0 = (pointerY - s->outputDev[output].region.extents.y1) / (float)
+ s->outputDev[output].height;
+
+ zs->x1 = box.x1 + (x0 * (box.x2 - box.x1) - x0 * w + 0.5f);
+ zs->y1 = box.y1 + (y0 * (box.y2 - box.y1) - y0 * h + 0.5f);
+ zs->x2 = zs->x1 + w;
+ zs->y2 = zs->y1 + h;
+
+ zoomInitiateForSelection (s, output);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static Bool
+zoomInitiate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ int output, x1, y1;
+ float scale;
+
+ ZOOM_SCREEN (s);
+
+ if (otherScreenGrabExist (s, "zoom", 0))
+ return FALSE;
+
+ if (!zs->grabIndex)
+ zs->grabIndex = pushScreenGrab (s, None, "zoom");
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ /* start selection zoom rectangle */
+
+ output = outputDeviceForPoint (s, pointerX, pointerY);
+
+ if (zs->zoomed & (1 << output))
+ {
+ ZoomBox box;
+ float oWidth;
+
+ zoomGetCurrentZoom (s, output, &box);
+
+ oWidth = s->outputDev[output].width;
+ scale = oWidth / (box.x2 - box.x1);
+
+ x1 = box.x1;
+ y1 = box.y1;
+ }
+ else
+ {
+ scale = 1.0f;
+ x1 = s->outputDev[output].region.extents.x1;
+ y1 = s->outputDev[output].region.extents.y1;
+ }
+
+ zs->x1 = zs->x2 = x1 +
+ ((pointerX - s->outputDev[output].region.extents.x1) /
+ scale + 0.5f);
+ zs->y1 = zs->y2 = y1 +
+ ((pointerY - s->outputDev[output].region.extents.y1) /
+ scale + 0.5f);
+
+ zs->zoomOutput = output;
+
+ zs->grab = TRUE;
+
+ damageScreen (s);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static Bool
+zoomOut (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ int output;
+
+ ZOOM_SCREEN (s);
+
+ output = outputDeviceForPoint (s, pointerX, pointerY);
+
+ zoomGetCurrentZoom (s, output, &zs->last[output]);
+
+ zs->current[output].x1 = s->outputDev[output].region.extents.x1;
+ zs->current[output].y1 = s->outputDev[output].region.extents.y1;
+ zs->current[output].x2 = s->outputDev[output].region.extents.x2;
+ zs->current[output].y2 = s->outputDev[output].region.extents.y2;
+
+ zs->zoomOutput = output;
+ zs->scale = 0.0f;
+ zs->adjust = TRUE;
+ zs->grab = FALSE;
+
+ if (zs->grabIndex)
+ {
+ removeScreenGrab (s, zs->grabIndex, NULL);
+ zs->grabIndex = 0;
+ }
+
+ damageScreen (s);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static Bool
+zoomTerminate (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ ZOOM_SCREEN (s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (zs->grab)
+ {
+ int output;
+
+ output = outputDeviceForPoint (s, zs->x1, zs->y1);
+
+ if (zs->x2 > s->outputDev[output].region.extents.x2)
+ zs->x2 = s->outputDev[output].region.extents.x2;
+
+ if (zs->y2 > s->outputDev[output].region.extents.y2)
+ zs->y2 = s->outputDev[output].region.extents.y2;
+
+ zoomInitiateForSelection (s, output);
+
+ zs->grab = FALSE;
+ }
+ else
+ {
+ CompOption o;
+
+ o.type = CompOptionTypeInt;
+ o.name = "root";
+ o.value.i = s->root;
+
+ zoomOut (d, action, state, &o, 1);
+ }
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+static Bool
+zoomInitiatePan (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+ int output;
+
+ ZOOM_SCREEN (s);
+
+ output = outputDeviceForPoint (s, pointerX, pointerY);
+
+ if (!(zs->zoomed & (1 << output)))
+ return FALSE;
+
+ if (otherScreenGrabExist (s, "zoom", 0))
+ return FALSE;
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ if (!zs->panGrabIndex)
+ zs->panGrabIndex = pushScreenGrab (s, zs->panCursor, "zoom-pan");
+
+ zs->zoomOutput = output;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static Bool
+zoomTerminatePan (CompDisplay *d,
+ CompAction *action,
+ CompActionState state,
+ CompOption *option,
+ int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next)
+ {
+ ZOOM_SCREEN (s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (zs->panGrabIndex)
+ {
+ removeScreenGrab (s, zs->panGrabIndex, NULL);
+ zs->panGrabIndex = 0;
+
+ zoomInEvent (s);
+ }
+
+ return TRUE;
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+static void
+zoomHandleMotionEvent (CompScreen *s,
+ int xRoot,
+ int yRoot)
+{
+ ZOOM_SCREEN (s);
+
+ if (zs->grabIndex)
+ {
+ int output = zs->zoomOutput;
+ ZoomBox box;
+ float scale, oWidth = s->outputDev[output].width;
+
+ zoomGetCurrentZoom (s, output, &box);
+
+ if (zs->zoomed & (1 << output))
+ scale = oWidth / (box.x2 - box.x1);
+ else
+ scale = 1.0f;
+
+ if (zs->panGrabIndex)
+ {
+ float dx, dy;
+
+ dx = (xRoot - lastPointerX) / scale;
+ dy = (yRoot - lastPointerY) / scale;
+
+ box.x1 -= dx;
+ box.y1 -= dy;
+ box.x2 -= dx;
+ box.y2 -= dy;
+
+ if (box.x1 < s->outputDev[output].region.extents.x1)
+ {
+ box.x2 += s->outputDev[output].region.extents.x1 - box.x1;
+ box.x1 = s->outputDev[output].region.extents.x1;
+ }
+ else if (box.x2 > s->outputDev[output].region.extents.x2)
+ {
+ box.x1 -= box.x2 - s->outputDev[output].region.extents.x2;
+ box.x2 = s->outputDev[output].region.extents.x2;
+ }
+
+ if (box.y1 < s->outputDev[output].region.extents.y1)
+ {
+ box.y2 += s->outputDev[output].region.extents.y1 - box.y1;
+ box.y1 = s->outputDev[output].region.extents.y1;
+ }
+ else if (box.y2 > s->outputDev[output].region.extents.y2)
+ {
+ box.y1 -= box.y2 - s->outputDev[output].region.extents.y2;
+ box.y2 = s->outputDev[output].region.extents.y2;
+ }
+
+ zs->current[output] = box;
+
+ damageScreen (s);
+ }
+ else
+ {
+ int x1, y1;
+
+ if (zs->zoomed & (1 << output))
+ {
+ x1 = box.x1;
+ y1 = box.y1;
+ }
+ else
+ {
+ x1 = s->outputDev[output].region.extents.x1;
+ y1 = s->outputDev[output].region.extents.y1;
+ }
+
+ zs->x2 = x1 +
+ ((xRoot - s->outputDev[output].region.extents.x1) /
+ scale + 0.5f);
+ zs->y2 = y1 +
+ ((yRoot - s->outputDev[output].region.extents.y1) /
+ scale + 0.5f);
+
+ damageScreen (s);
+ }
+ }
+}
+
+static void
+zoomHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ ZOOM_DISPLAY (d);
+
+ switch (event->type) {
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ zoomHandleMotionEvent (s, pointerX, pointerY);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ s = findScreenAtDisplay (d, event->xcrossing.root);
+ if (s)
+ zoomHandleMotionEvent (s, pointerX, pointerY);
+ default:
+ break;
+ }
+
+ UNWRAP (zd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (zd, d, handleEvent, zoomHandleEvent);
+}
+
+static CompOption *
+zoomGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+ int *count)
+{
+ ZOOM_DISPLAY (display);
+
+ *count = NUM_OPTIONS (zd);
+ return zd->opt;
+}
+
+static Bool
+zoomSetDisplayOption (CompPlugin *plugin,
+ CompDisplay *display,
+ const char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ ZOOM_DISPLAY (display);
+
+ o = compFindOption (zd->opt, NUM_OPTIONS (zd), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case ZOOM_DISPLAY_OPTION_OUT_BUTTON:
+ if (compSetActionOption (o, value))
+ return TRUE;
+ break;
+ default:
+ return compSetDisplayOption (display, o, value);
+ }
+
+ return FALSE;
+}
+
+static const CompMetadataOptionInfo zoomDisplayOptionInfo[] = {
+ { "initiate_button", "button", 0, zoomInitiate, zoomTerminate },
+ { "zoom_in_button", "button", 0, zoomIn, 0 },
+ { "zoom_out_button", "button", 0, zoomOut, 0 },
+ { "zoom_pan_button", "button", 0, zoomInitiatePan, zoomTerminatePan }
+};
+
+static Bool
+zoomInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ZoomDisplay *zd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ zd = malloc (sizeof (ZoomDisplay));
+ if (!zd)
+ return FALSE;
+
+ if (!compInitDisplayOptionsFromMetadata (d,
+ &zoomMetadata,
+ zoomDisplayOptionInfo,
+ zd->opt,
+ ZOOM_DISPLAY_OPTION_NUM))
+ {
+ free (zd);
+ return FALSE;
+ }
+
+ zd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (zd->screenPrivateIndex < 0)
+ {
+ compFiniDisplayOptions (d, zd->opt, ZOOM_DISPLAY_OPTION_NUM);
+ free (zd);
+ return FALSE;
+ }
+
+ WRAP (zd, d, handleEvent, zoomHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = zd;
+
+ return TRUE;
+}
+
+static void
+zoomFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ZOOM_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, zd->screenPrivateIndex);
+
+ UNWRAP (zd, d, handleEvent);
+
+ compFiniDisplayOptions (d, zd->opt, ZOOM_DISPLAY_OPTION_NUM);
+
+ free (zd);
+}
+
+static const CompMetadataOptionInfo zoomScreenOptionInfo[] = {
+ { "speed", "float", "<min>0.1</min>", 0, 0 },
+ { "timestep", "float", "<min>0.1</min>", 0, 0 },
+ { "zoom_factor", "float", "<min>1.01</min>", 0, 0 },
+ { "filter_linear", "bool", 0, 0, 0 }
+};
+
+static Bool
+zoomInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ZoomScreen *zs;
+
+ ZOOM_DISPLAY (s->display);
+
+ zs = malloc (sizeof (ZoomScreen));
+ if (!zs)
+ return FALSE;
+
+ if (!compInitScreenOptionsFromMetadata (s,
+ &zoomMetadata,
+ zoomScreenOptionInfo,
+ zs->opt,
+ ZOOM_SCREEN_OPTION_NUM))
+ {
+ free (zs);
+ return FALSE;
+ }
+
+ zs->grabIndex = 0;
+ zs->grab = FALSE;
+
+ zs->velocity = 0.0f;
+
+ zs->zoomOutput = 0;
+
+ zs->zoomed = 0;
+ zs->adjust = FALSE;
+
+ zs->panGrabIndex = 0;
+ zs->panCursor = XCreateFontCursor (s->display->display, XC_fleur);
+
+ zs->scale = 0.0f;
+
+ memset (&zs->current, 0, sizeof (zs->current));
+ memset (&zs->last, 0, sizeof (zs->last));
+
+ WRAP (zs, s, preparePaintScreen, zoomPreparePaintScreen);
+ WRAP (zs, s, donePaintScreen, zoomDonePaintScreen);
+ WRAP (zs, s, paintOutput, zoomPaintOutput);
+
+ s->base.privates[zd->screenPrivateIndex].ptr = zs;
+
+ return TRUE;
+}
+
+static void
+zoomFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ZOOM_SCREEN (s);
+
+ if (zs->panCursor)
+ XFreeCursor (s->display->display, zs->panCursor);
+
+ UNWRAP (zs, s, preparePaintScreen);
+ UNWRAP (zs, s, donePaintScreen);
+ UNWRAP (zs, s, paintOutput);
+
+ compFiniScreenOptions (s, zs->opt, ZOOM_SCREEN_OPTION_NUM);
+
+ free (zs);
+}
+
+static CompBool
+zoomInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) zoomInitDisplay,
+ (InitPluginObjectProc) zoomInitScreen
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+zoomFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) zoomFiniDisplay,
+ (FiniPluginObjectProc) zoomFiniScreen
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static CompOption *
+zoomGetObjectOptions (CompPlugin *plugin,
+ CompObject *object,
+ int *count)
+{
+ static GetPluginObjectOptionsProc dispTab[] = {
+ (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
+ (GetPluginObjectOptionsProc) zoomGetDisplayOptions,
+ (GetPluginObjectOptionsProc) zoomGetScreenOptions
+ };
+
+ *count = 0;
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
+ (void *) count, (plugin, object, count));
+}
+
+static CompBool
+zoomSetObjectOption (CompPlugin *plugin,
+ CompObject *object,
+ const char *name,
+ CompOptionValue *value)
+{
+ static SetPluginObjectOptionProc dispTab[] = {
+ (SetPluginObjectOptionProc) 0, /* SetCoreOption */
+ (SetPluginObjectOptionProc) zoomSetDisplayOption,
+ (SetPluginObjectOptionProc) zoomSetScreenOption
+ };
+
+ RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
+ (plugin, object, name, value));
+}
+
+static Bool
+zoomInit (CompPlugin *p)
+{
+ if (!compInitPluginMetadataFromInfo (&zoomMetadata,
+ p->vTable->name,
+ zoomDisplayOptionInfo,
+ ZOOM_DISPLAY_OPTION_NUM,
+ zoomScreenOptionInfo,
+ ZOOM_SCREEN_OPTION_NUM))
+ return FALSE;
+
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ {
+ compFiniMetadata (&zoomMetadata);
+ return FALSE;
+ }
+
+ compAddMetadataFromFile (&zoomMetadata, p->vTable->name);
+
+ return TRUE;
+}
+
+static void
+zoomFini (CompPlugin *p)
+{
+ freeDisplayPrivateIndex (displayPrivateIndex);
+ compFiniMetadata (&zoomMetadata);
+}
+
+static CompMetadata *
+zoomGetMetadata (CompPlugin *plugin)
+{
+ return &zoomMetadata;
+}
+
+CompPluginVTable zoomVTable = {
+ "zoom",
+ zoomGetMetadata,
+ zoomInit,
+ zoomFini,
+ zoomInitObject,
+ zoomFiniObject,
+ zoomGetObjectOptions,
+ zoomSetObjectOption
+};
+
+CompPluginVTable *
+getCompPluginInfo20070830 (void)
+{
+ return &zoomVTable;
+}