diff options
author | Eamon Walsh <ewalsh@tycho.nsa.gov> | 2009-09-17 19:05:06 -0400 |
---|---|---|
committer | Eamon Walsh <ewalsh@tycho.nsa.gov> | 2009-09-17 19:05:06 -0400 |
commit | 72d98f6a2088ccacbe913d0523b08cd868f16470 (patch) | |
tree | c083e0fce6ee0f49ed4f0d313415563da8c26754 /plugins |
Initial commit.
Diffstat (limited to 'plugins')
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 = ®.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, ®); + } +} + +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 = ®.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, ®); + } +} + +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 = ®.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, ®); + } +} + +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 = ®.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, ®); + } +} + +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 = ®ion.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 (®ion, 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 = ®ion.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, ®ion, 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, ®ion, 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, ®ion, 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, ®ion, 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, ®ion, 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 (®exMetadata, p->vTable->name, + 0, 0, 0, 0)) + return FALSE; + + displayPrivateIndex = allocateDisplayPrivateIndex (); + if (displayPrivateIndex < 0) + { + compFiniMetadata (®exMetadata); + return FALSE; + } + + compAddMetadataFromFile (®exMetadata, p->vTable->name); + + return TRUE; +} + +static void +regexFini (CompPlugin *p) +{ + freeDisplayPrivateIndex (displayPrivateIndex); + compFiniMetadata (®exMetadata); +} + +static CompMetadata * +regexGetMetadata (CompPlugin *plugin) +{ + return ®exMetadata; +} + +static CompPluginVTable regexVTable = { + "regex", + regexGetMetadata, + regexInit, + regexFini, + regexInitObject, + regexFiniObject, + 0, /* GetObjectOptions */ + 0 /* SetObjectOption */ +}; + +CompPluginVTable * +getCompPluginInfo20070830 (void) +{ + return ®exVTable; +} 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 = ®.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, ®); +} + +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 = ®.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, ®); + } + } + } + + 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 = ®.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, ®); + + 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, ®); + + 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 = ®ion.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, ®ion); + + 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; +} |